Add the Avalonia application and the station network

The entry window types contacts and colours them as they are typed; the log,
check, bandmap, score and packet windows read the same session. Contacts are
shared with the other stations of a multi-operator entry in N1MM's contact
message, so an N1MM station on the same network sees them too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik
2026-08-27 10:56:35 +00:00
parent ffeeb2cdc1
commit 2834f63c8e
45 changed files with 2667 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using Nonemm.Session;
namespace Nonemm.App.Windows;
/// What the callsign being typed could be, a column per source. The columns
/// stay apart because they answer different questions, and merged into one list
/// they would all read as equally reliable.
public sealed partial class CheckWindow : RefreshableWindow
{
private readonly AppSession session;
private readonly Func<string> typed;
public CheckWindow(AppSession session, Func<string> typed)
{
this.session = session;
this.typed = typed;
InitializeComponent();
Refresh();
}
public override void Refresh()
{
Columns.Children.Clear();
if (session.Check is null)
{
return;
}
int at = 0;
foreach (CheckColumn column in session.Check.Columns(typed()))
{
Control panel = BuildColumn(column);
Grid.SetColumn(panel, at++);
Columns.Children.Add(panel);
}
}
private static Control BuildColumn(CheckColumn column)
{
StackPanel panel = new() { Margin = new Avalonia.Thickness(0, 0, 8, 0) };
panel.Children.Add(new TextBlock
{
Text = $"{Heading(column.Source)} {Count(column)}",
FontSize = 11,
Opacity = 0.7,
Margin = new Avalonia.Thickness(0, 0, 0, 4),
});
foreach (CheckCandidate candidate in column.Candidates)
{
panel.Children.Add(new TextBlock
{
Text = candidate.Call,
FontFamily = new FontFamily("monospace"),
FontSize = 14,
Foreground = Verdicts.Colour(candidate.Verdict),
HorizontalAlignment = HorizontalAlignment.Left,
});
}
return new ScrollViewer { Content = panel };
}
/// Before anything is typed the heading says how many calls the source
/// holds; once it is offering candidates it says how many.
private static string Count(CheckColumn column) =>
column.Candidates.Count > 0 ? column.Candidates.Count.ToString() : $"({column.Held})";
private static string Heading(CheckSource source) => source switch
{
CheckSource.Log => "Log",
CheckSource.Database => "Master",
_ => "Bandmap",
};
}