Two themes, and one place that decides what colour a station is
Colours came from static brushes scattered over the windows. They now come from a Theme, and everything that paints a station, a spot or a log row asks Themes.Current as it paints, so changing the theme changes every window without reopening it. light is N1MM's own palette, read out of ApplicationStyles: the pale blue behind a window, ivory on a button, white boxes, and the four colours a station is painted in. dark is the same states in colours for a dark screen, which is roughly what the program looked like before. Config > Manage Skins, Colors and Fonts picks one, under N1MM's name for the same dialog, and shows what each theme paints a callsign. The choice is kept in settings.json as Theme, and light is the default. The window chrome follows through resources the Fluent controls read, so a text box and a button take the theme's colours in every state. Fonts and skins are not part of it. Looked at under Xvfb in both themes: the entry window, the theme dialog, and the log window with its rows still coloured by what each contact was worth. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Nonemm.App.Configuration;
|
||||
using Nonemm.App.Dialogs;
|
||||
using Nonemm.App.Theming;
|
||||
using Nonemm.Contests;
|
||||
using Nonemm.Core;
|
||||
using Nonemm.Core.Country;
|
||||
@@ -469,6 +470,18 @@ public sealed partial class EntryWindow
|
||||
|
||||
private void OnEditPhoneMessages(object? sender, RoutedEventArgs e) => _ = EditMessages(ModeCategory.Phone);
|
||||
|
||||
private async void OnThemeSettings(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
ThemeDialog dialog = new(session.Settings.Theme);
|
||||
if (await dialog.ShowDialog<string?>(this) is not { } picked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
session.Save(session.Settings with { Theme = picked });
|
||||
Themes.Use(picked);
|
||||
Status($"{picked} colours");
|
||||
}
|
||||
|
||||
private async void OnQtcSetup(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
QtcSetupDialog dialog = new(session.Settings);
|
||||
|
||||
@@ -118,6 +118,7 @@
|
||||
<MenuItem Header="Change _SSB Function Key Definitions" Click="OnEditPhoneMessages" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="Change Band Plan…" Click="OnSubBandSettings" />
|
||||
<MenuItem Header="Manage Skins, Colors and Fonts…" Click="OnThemeSettings" />
|
||||
<MenuItem Header="Edit Networked-Computer Names…" Click="OnNetworkSettings" />
|
||||
<MenuItem Header="WAE">
|
||||
<MenuItem Header="Open QTC Window setup area" Click="OnQtcSetup" />
|
||||
|
||||
@@ -7,6 +7,7 @@ using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using Nonemm.Contests;
|
||||
using Nonemm.Core;
|
||||
using Nonemm.App.Theming;
|
||||
using Nonemm.Session;
|
||||
|
||||
namespace Nonemm.App.Windows;
|
||||
@@ -24,13 +25,6 @@ public sealed partial class EntryWindow : Window
|
||||
|
||||
private static readonly IBrush Dark = new SolidColorBrush(Color.FromArgb(0x33, 0x80, 0x80, 0x80));
|
||||
|
||||
private static readonly IBrush GreenLight = new SolidColorBrush(Color.FromRgb(0x0D, 0x9D, 0x0D));
|
||||
|
||||
private static readonly IBrush RedLight = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0x00));
|
||||
|
||||
/// The pale yellow N1MM paints the entry boxes while quick editing.
|
||||
private static readonly IBrush QuickEditBackground = new SolidColorBrush(Color.FromRgb(0xFF, 0xFF, 0xC0));
|
||||
|
||||
private bool sending;
|
||||
private TextBlock? callMark;
|
||||
private TextBox? nameBox;
|
||||
@@ -52,6 +46,8 @@ public sealed partial class EntryWindow : Window
|
||||
BuildEntryBoxes();
|
||||
ShowSecondRadio();
|
||||
});
|
||||
Themes.Changed += OnThemeChanged;
|
||||
Closed += (_, _) => Themes.Changed -= OnThemeChanged;
|
||||
clock.Tick += (_, _) => UpdateClock();
|
||||
clock.Start();
|
||||
session.ActiveRadioChanged += (_, number) =>
|
||||
@@ -67,6 +63,8 @@ public sealed partial class EntryWindow : Window
|
||||
}
|
||||
|
||||
|
||||
private void OnThemeChanged(object? sender, EventArgs e) => Dispatcher.UIThread.Post(Refresh);
|
||||
|
||||
/// Resolved every time rather than held, because opening a contest builds
|
||||
/// new positions.
|
||||
private OperatingPosition? Logging =>
|
||||
@@ -697,8 +695,8 @@ public sealed partial class EntryWindow : Window
|
||||
}
|
||||
else
|
||||
{
|
||||
box.Background = QuickEditBackground;
|
||||
box.Foreground = Brushes.Black;
|
||||
box.Background = Themes.Brush(Themes.Current.QuickEdit);
|
||||
box.Foreground = Themes.Brush(Themes.Current.FormForeground);
|
||||
}
|
||||
}
|
||||
FrequencyText.Text = Logging.TransmitFrequency.Hertz > 0
|
||||
@@ -719,7 +717,7 @@ public sealed partial class EntryWindow : Window
|
||||
ShowCallColour(verdict);
|
||||
VerdictBorder.Background = Verdicts.Background(verdict);
|
||||
VerdictText.Text = VerdictLine(verdict);
|
||||
VerdictText.Foreground = Brushes.Black;
|
||||
VerdictText.Foreground = Themes.Brush(Themes.Current.FormForeground);
|
||||
foreach (Window window in openWindows.Values)
|
||||
{
|
||||
(window as RefreshableWindow)?.Refresh();
|
||||
@@ -781,8 +779,8 @@ public sealed partial class EntryWindow : Window
|
||||
private void ShowLights()
|
||||
{
|
||||
bool radio = session.Radios.Any(r => r.Number == radioNumber && r.IsConnected);
|
||||
RadioLight.Fill = radio ? GreenLight : Dark;
|
||||
TransmitLight.Fill = sending ? RedLight : Dark;
|
||||
RadioLight.Fill = radio ? Themes.Brush(Themes.Current.RadioLight) : Dark;
|
||||
TransmitLight.Fill = sending ? Themes.Brush(Themes.Current.TransmitLight) : Dark;
|
||||
}
|
||||
|
||||
/// What N1MM writes in its title bar: the frequency, the mode, whether the
|
||||
|
||||
@@ -5,6 +5,7 @@ using Avalonia.Media;
|
||||
using Nonemm.App.Configuration;
|
||||
using Nonemm.App.Dialogs;
|
||||
using Nonemm.Contests.Rules;
|
||||
using Nonemm.App.Theming;
|
||||
using Nonemm.Core;
|
||||
using Nonemm.Session;
|
||||
|
||||
@@ -20,9 +21,11 @@ namespace Nonemm.App.Windows;
|
||||
/// SSB by voice, on RTTY from the digital window there is not yet.
|
||||
public sealed partial class QtcWindow : Window
|
||||
{
|
||||
private static readonly IBrush Saved = new SolidColorBrush(Color.FromRgb(0xD4, 0xF5, 0xD4));
|
||||
private static readonly IBrush Unsaved = new SolidColorBrush(Color.FromRgb(0xFF, 0xE4, 0xE1));
|
||||
private static readonly IBrush Malformed = new SolidColorBrush(Color.FromRgb(0xFF, 0xF8, 0xC4));
|
||||
private static IBrush Saved => Themes.Brush(Themes.Current.GoodBackground);
|
||||
|
||||
private static IBrush Unsaved => Themes.Brush(Themes.Current.EmptyBackground);
|
||||
|
||||
private static IBrush Malformed => Themes.Brush(Themes.Current.BadBackground);
|
||||
|
||||
private readonly AppSession session;
|
||||
private readonly OperatingPosition position;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using Nonemm.App.Configuration;
|
||||
using Nonemm.App.Theming;
|
||||
using Nonemm.Core;
|
||||
using Nonemm.Session;
|
||||
using Nonemm.Spotting;
|
||||
@@ -18,9 +19,11 @@ public sealed partial class TelnetWindow : RefreshableWindow
|
||||
{
|
||||
private const int LinesKept = 500;
|
||||
|
||||
private static readonly IBrush SpotColour = new SolidColorBrush(Color.FromRgb(0x27, 0xAE, 0x60));
|
||||
private static readonly IBrush SentColour = new SolidColorBrush(Color.FromRgb(0x29, 0x80, 0xB9));
|
||||
private static readonly IBrush NoticeColour = new SolidColorBrush(Color.FromRgb(0x7F, 0x8C, 0x8D));
|
||||
private static IBrush SpotColour => Themes.Brush(Themes.Current.Multipliers);
|
||||
|
||||
private static IBrush SentColour => Themes.Brush(Themes.Current.Worth);
|
||||
|
||||
private static IBrush NoticeColour => Themes.Brush(Themes.Current.Dupe);
|
||||
|
||||
private readonly AppSession session;
|
||||
private readonly Action<Frequency, string> tune;
|
||||
|
||||
Reference in New Issue
Block a user