Put N1MM's status bar along the bottom of the entry window
Three parts, as N1MM has them: what just happened on the left, the contacts and multipliers in the middle, the score on the right. The left says where the call being typed is from, in N1MM's own shape: OM: EU -> Slovak Republic, Zn 15. The zone is the one the contest counts in, so Contest.UsesItuZones answers that the way N1MM's ZoneType does, and IARU overrides it. A message from something the operator just did stands there until the next keystroke. The middle is N1MM's breakdown: the contacts and then one number per multiplier the contest counts, separated by slashes, and No Score before the first contact. Points are not in it, because they are not in N1MM's; the score window has them. Those numbers were in the line at the top of the window, which now carries the contest name alone, and the country was in the bar under the callsign box, which now says only what working the call would bring. Neither is said twice. Looked at under Xvfb: the country line for OM3KFF and then G4AB, 10/7/8 and 300 beside them, and a Mark message standing in the left until the next keystroke. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
This commit is contained in:
@@ -123,6 +123,20 @@
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<!-- N1MM's status strip: what just happened on the left, then the contacts
|
||||
and multipliers, then the score -->
|
||||
<Border DockPanel.Dock="Bottom" Padding="6,3" BorderThickness="0,1,0,0"
|
||||
BorderBrush="#33808080">
|
||||
<Grid ColumnDefinitions="*,Auto,Auto">
|
||||
<TextBlock Name="StatusText" FontSize="11" Opacity="0.8" Text=""
|
||||
TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Column="1" Name="BreakdownText" FontSize="11" Opacity="0.8" Text=""
|
||||
Margin="12,0" VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Column="2" Name="ScoreText" FontSize="11" FontWeight="Bold" Text=""
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border DockPanel.Dock="Left" Padding="6,6,4,6" BorderThickness="0,0,1,0"
|
||||
BorderBrush="#33808080">
|
||||
<Grid Name="BandButtons" ColumnDefinitions="Auto,Auto" />
|
||||
@@ -183,7 +197,6 @@
|
||||
<TextBlock Name="UserTextText" FontSize="11" Margin="2,2,0,0" Text=""
|
||||
Foreground="#2980B9" TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<TextBlock Name="StatusText" FontSize="11" Opacity="0.7" Margin="2,4,0,0" Text="" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
||||
@@ -37,6 +37,11 @@ public sealed partial class EntryWindow : Window
|
||||
private readonly Dictionary<Type, Window> openWindows = [];
|
||||
private bool updating;
|
||||
|
||||
/// What the last thing the operator did had to say. It stands in the status
|
||||
/// bar until the next keystroke, and then the country of the call being
|
||||
/// typed takes the place of it.
|
||||
private string message = "";
|
||||
|
||||
public EntryWindow(AppSession session, int radioNumber = 1)
|
||||
{
|
||||
this.session = session;
|
||||
@@ -315,6 +320,8 @@ public sealed partial class EntryWindow : Window
|
||||
{
|
||||
ResetEsm();
|
||||
}
|
||||
// typing answers whatever the status bar was reporting
|
||||
message = "";
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -737,6 +744,9 @@ public sealed partial class EntryWindow : Window
|
||||
UserTextText.Text = Logging.Session.History.Find(Logging.Entry.Call)?.UserText ?? "";
|
||||
ShowLights();
|
||||
|
||||
ShowScore();
|
||||
StatusText.Text = message.Length > 0 ? message : CountryLine();
|
||||
|
||||
Verdict? verdict = Logging.Verdict();
|
||||
ShowCallColour(verdict);
|
||||
VerdictBorder.Background = Verdicts.Background(verdict);
|
||||
@@ -783,19 +793,57 @@ public sealed partial class EntryWindow : Window
|
||||
&& !call.All(char.IsAsciiDigit);
|
||||
}
|
||||
|
||||
/// The contacts and multipliers so far, and the score. N1MM keeps them in
|
||||
/// the status bar in this shape: the contacts, then one number per
|
||||
/// multiplier the contest counts, separated by slashes.
|
||||
private void ShowScore()
|
||||
{
|
||||
if (Logging is null)
|
||||
{
|
||||
BreakdownText.Text = "";
|
||||
ScoreText.Text = "";
|
||||
return;
|
||||
}
|
||||
if (Logging.Log.Qsos.Count == 0)
|
||||
{
|
||||
BreakdownText.Text = "No Score";
|
||||
ScoreText.Text = "";
|
||||
return;
|
||||
}
|
||||
BreakdownText.Text = string.Join(
|
||||
"/",
|
||||
new[] { Logging.Log.Tally.Qsos }.Concat(
|
||||
Logging.Contest.MultiplierNames.Select(
|
||||
(_, at) => Logging.Log.Tally.MultiplierCount(at + 1))));
|
||||
ScoreText.Text = $"{Logging.Log.TotalScore:N0}";
|
||||
}
|
||||
|
||||
private string ContestLine()
|
||||
{
|
||||
if (Logging is null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
string mults = string.Join(
|
||||
" ",
|
||||
Logging.Contest.MultiplierNames.Select(
|
||||
(name, at) => $"{name} {Logging.Log.Tally.MultiplierCount(at + 1)}"));
|
||||
string radio = session.Radios.Count > 1 ? $" · radio {session.ActiveRadioNumber}" : "";
|
||||
return $"{Logging.Contest.DisplayName} · {Logging.Log.Tally.Qsos} Q · " +
|
||||
$"{Logging.Log.Tally.Points} pts · {mults} · {Logging.Log.TotalScore:N0}{radio}";
|
||||
return $"{Logging.Contest.DisplayName}{radio}";
|
||||
}
|
||||
|
||||
/// N1MM's status bar says where the call being typed is from, in this
|
||||
/// shape: `OM: EU -> Slovak Republic, Zn 15`. The zone is the one the
|
||||
/// contest counts in. A message from something the operator just did takes
|
||||
/// the place of it until the next keystroke.
|
||||
private string CountryLine()
|
||||
{
|
||||
if (Logging is null || Logging.Entry.Call.Trim().Length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
if (Logging.Country() is not { } found)
|
||||
{
|
||||
return "unknown country";
|
||||
}
|
||||
int zone = Logging.Contest.UsesItuZones ? found.ItuZone : found.CqZone;
|
||||
return $"{found.Entity.PrimaryPrefix}: {found.Continent} -> {found.Entity.Name}, Zn {zone}";
|
||||
}
|
||||
|
||||
/// The two lights N1MM has beside the run switch: the left one says a radio
|
||||
@@ -857,21 +905,21 @@ public sealed partial class EntryWindow : Window
|
||||
: "no bearing — the country file does not place this callsign";
|
||||
}
|
||||
|
||||
private string VerdictLine(Verdict? verdict)
|
||||
{
|
||||
if (Logging is null || Logging.Entry.Call.Trim().Length == 0)
|
||||
{
|
||||
return $"sending {Logging?.SentNumber}";
|
||||
}
|
||||
string country = Logging.Country() is { } found
|
||||
? $"{found.Entity.Name} · {found.Continent} · CQ {found.CqZone} · ITU {found.ItuZone}"
|
||||
: "unknown country";
|
||||
return $"{country} — {Verdicts.Describe(verdict)}";
|
||||
}
|
||||
/// What working the call being typed would bring. Where it is from is in
|
||||
/// the status bar rather than here, so neither says it twice.
|
||||
private string VerdictLine(Verdict? verdict) =>
|
||||
Logging is null || Logging.Entry.Call.Trim().Length == 0
|
||||
? $"sending {Logging?.SentNumber}"
|
||||
: Verdicts.Describe(verdict);
|
||||
|
||||
private void UpdateClock() => ClockText.Text = DateTime.UtcNow.ToString("HH:mm:ss") + "Z";
|
||||
|
||||
private void Status(string text) => StatusText.Text = text;
|
||||
/// What just happened, in the left of the status bar.
|
||||
private void Status(string text)
|
||||
{
|
||||
message = text;
|
||||
StatusText.Text = text;
|
||||
}
|
||||
|
||||
/// The labels come from the message file, so what the buttons say is what
|
||||
/// the operator wrote. Right-clicking one opens the editor, as it does on
|
||||
|
||||
Reference in New Issue
Block a user