The generic name only resolves through fontconfig, so on Windows the windows fell back to the proportional default. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
124 lines
4.0 KiB
C#
124 lines
4.0 KiB
C#
using Nonemm.App.Theming;
|
|
using Avalonia.Controls;
|
|
using Nonemm.Contests;
|
|
using Nonemm.Core;
|
|
|
|
namespace Nonemm.App.Windows;
|
|
|
|
/// Contacts, points and multipliers band by band.
|
|
public sealed partial class ScoreWindow : RefreshableWindow
|
|
{
|
|
private readonly AppSession session;
|
|
private int columnCount = 4;
|
|
|
|
public ScoreWindow(AppSession session)
|
|
{
|
|
this.session = session;
|
|
InitializeComponent();
|
|
Refresh();
|
|
}
|
|
|
|
|
|
public override void Refresh()
|
|
{
|
|
Table.Children.Clear();
|
|
Table.ColumnDefinitions.Clear();
|
|
Table.RowDefinitions.Clear();
|
|
if (session.Logging is null)
|
|
{
|
|
TotalText.Text = "no contest is open";
|
|
return;
|
|
}
|
|
|
|
// the log keeps the same split N1MM's score window shows, so the band
|
|
// rows carry the weighted multipliers and leave out what is not in the
|
|
// claimed score
|
|
List<KeyValuePair<ScoreKey, ScoreTally>> lines = session.Logging.Log.Rows
|
|
.OrderBy(r => Bands.Named(r.Key.Band)?.MegahertzLabel ?? double.MaxValue)
|
|
.ThenBy(r => r.Key.Kind, StringComparer.Ordinal)
|
|
.ToList();
|
|
bool hasKinds = session.Logging.Contest.HasQtcTraffic;
|
|
|
|
columnCount = hasKinds ? 5 : 4;
|
|
for (int column = 0; column < columnCount; column++)
|
|
{
|
|
Table.ColumnDefinitions.Add(new ColumnDefinition(GridLength.Star));
|
|
}
|
|
AddRow(0, "Band", "Q/QTC", "QSOs", "Points", "Mults", header: true);
|
|
|
|
int row = 1;
|
|
foreach ((ScoreKey key, ScoreTally line) in lines)
|
|
{
|
|
AddRow(
|
|
row++,
|
|
key.Band,
|
|
key.Kind,
|
|
line.Qsos.ToString(),
|
|
line.Points.ToString(),
|
|
line.TotalMultipliers.ToString());
|
|
}
|
|
AddRow(
|
|
row,
|
|
"Total",
|
|
hasKinds ? "All" : "",
|
|
session.Logging.Log.Tally.Qsos.ToString(),
|
|
session.Logging.Log.Tally.Points.ToString(),
|
|
session.Logging.Log.Tally.TotalMultipliers.ToString(),
|
|
header: true);
|
|
|
|
TimeSpan on = OperatingTime.On(
|
|
session.Logging.Log.Qsos,
|
|
session.Logging.Contest.MinimumOffTime);
|
|
TimeSpan off = OperatingTime.Off(
|
|
session.Logging.Log.Qsos,
|
|
session.Logging.Contest.MinimumOffTime);
|
|
TotalText.Text =
|
|
$"Claimed score {session.Logging.Log.TotalScore:N0}"
|
|
+ $" on {Hours(on)} off {Hours(off)}"
|
|
+ BandChanges();
|
|
}
|
|
|
|
/// The band changes made and the allowance, for a contest that limits
|
|
/// them, and nothing at all for the contests that do not.
|
|
private string BandChanges()
|
|
{
|
|
BandChangeRules rules = session.Logging!.Contest.BandChangesFor(session.Logging.Log.Entry);
|
|
return rules.IsCounted
|
|
? $" band changes {rules.CountedIn(session.Logging.Log.Qsos, DateTime.UtcNow)}/{rules.Max}"
|
|
: "";
|
|
}
|
|
|
|
/// Hours and minutes, which is how a sponsor asks for operating time.
|
|
private static string Hours(TimeSpan time) =>
|
|
$"{(int)time.TotalHours}:{time.Minutes:00}";
|
|
|
|
private void AddRow(
|
|
int row,
|
|
string band,
|
|
string kind,
|
|
string qsos,
|
|
string points,
|
|
string mults,
|
|
bool header = false)
|
|
{
|
|
Table.RowDefinitions.Add(new RowDefinition(GridLength.Auto));
|
|
string[] cells = columnCount == 5
|
|
? [band, kind, qsos, points, mults]
|
|
: [band, qsos, points, mults];
|
|
for (int column = 0; column < cells.Length; column++)
|
|
{
|
|
TextBlock text = new()
|
|
{
|
|
Text = cells[column],
|
|
FontFamily = Fonts.Mono,
|
|
FontSize = 13,
|
|
Opacity = header ? 1.0 : 0.85,
|
|
Margin = new Avalonia.Thickness(0, 2, 8, 2),
|
|
};
|
|
Grid.SetRow(text, row);
|
|
Grid.SetColumn(text, column);
|
|
Table.Children.Add(text);
|
|
}
|
|
}
|
|
}
|