diff --git a/src/Nonemm.App/Configuration/Settings.cs b/src/Nonemm.App/Configuration/Settings.cs
index 8950731..52b2404 100644
--- a/src/Nonemm.App/Configuration/Settings.cs
+++ b/src/Nonemm.App/Configuration/Settings.cs
@@ -16,6 +16,12 @@ public sealed record Settings
/// dark screen. Config > Manage Skins, Colors and Fonts picks one.
public string Theme { get; init; } = "light";
+ /// Minimize and restore the other windows with the entry window.
+ public bool MinimizeWindowsTogether { get; init; } = true;
+
+ /// Raise the other windows that are not minimized when one is activated.
+ public bool RaiseWindowsTogether { get; init; } = true;
+
public int ContestNumber { get; init; }
public StoredStation Station { get; init; } = new();
diff --git a/src/Nonemm.App/Dialogs/ThemeDialog.axaml b/src/Nonemm.App/Dialogs/ThemeDialog.axaml
index fa0ec62..9def726 100644
--- a/src/Nonemm.App/Dialogs/ThemeDialog.axaml
+++ b/src/Nonemm.App/Dialogs/ThemeDialog.axaml
@@ -22,6 +22,8 @@
+
+
diff --git a/src/Nonemm.App/Dialogs/ThemeDialog.axaml.cs b/src/Nonemm.App/Dialogs/ThemeDialog.axaml.cs
index b358ab2..4cf93a8 100644
--- a/src/Nonemm.App/Dialogs/ThemeDialog.axaml.cs
+++ b/src/Nonemm.App/Dialogs/ThemeDialog.axaml.cs
@@ -2,6 +2,7 @@ using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
+using Nonemm.App.Configuration;
using Nonemm.App.Theming;
namespace Nonemm.App.Dialogs;
@@ -11,9 +12,15 @@ namespace Nonemm.App.Dialogs;
/// shows what each one paints a station.
public sealed partial class ThemeDialog : Window
{
- public ThemeDialog(string current)
+ private readonly Settings settings;
+
+ public ThemeDialog(Settings settings)
{
+ this.settings = settings;
+ string current = settings.Theme;
InitializeComponent();
+ MinimizeBox.IsChecked = settings.MinimizeWindowsTogether;
+ RaiseBox.IsChecked = settings.RaiseWindowsTogether;
ThemeBox.ItemsSource = Themes.All.Select(Label).ToList();
ThemeBox.SelectedIndex = Math.Max(0, Themes.All.ToList().FindIndex(t => t.Name == Themes.Find(current).Name));
}
@@ -61,7 +68,12 @@ public sealed partial class ThemeDialog : Window
},
};
- private void OnOk(object? sender, RoutedEventArgs e) => Close(Picked.Name);
+ private void OnOk(object? sender, RoutedEventArgs e) => Close(settings with
+ {
+ Theme = Picked.Name,
+ MinimizeWindowsTogether = MinimizeBox.IsChecked == true,
+ RaiseWindowsTogether = RaiseBox.IsChecked == true,
+ });
private void OnCancel(object? sender, RoutedEventArgs e) => Close(null);
}
diff --git a/src/Nonemm.App/Windows/EntryWindow.Menu.cs b/src/Nonemm.App/Windows/EntryWindow.Menu.cs
index bb31722..21e7d93 100644
--- a/src/Nonemm.App/Windows/EntryWindow.Menu.cs
+++ b/src/Nonemm.App/Windows/EntryWindow.Menu.cs
@@ -610,14 +610,14 @@ public sealed partial class EntryWindow
private async void OnThemeSettings(object? sender, RoutedEventArgs e)
{
- ThemeDialog dialog = new(session.Settings.Theme);
- if (await dialog.ShowDialog(this) is not { } picked)
+ ThemeDialog dialog = new(session.Settings);
+ if (await dialog.ShowDialog(this) is not { } updated)
{
return;
}
- session.Save(session.Settings with { Theme = picked });
- Themes.Use(picked);
- Status($"{picked} colours");
+ session.Save(updated);
+ Themes.Use(updated.Theme);
+ Status($"{updated.Theme} colours");
}
private async void OnQtcSetup(object? sender, RoutedEventArgs e)
@@ -752,6 +752,7 @@ public sealed partial class EntryWindow
// No owner: an owned window is transient-for the entry window, so the
// window manager keeps it above the entry window and it cannot be
// lowered. Closing the entry window closes these (see the Closed handler in the constructor).
+ window.Activated += (_, _) => RaiseTogether(window);
window.Show();
return window;
}
diff --git a/src/Nonemm.App/Windows/EntryWindow.axaml.cs b/src/Nonemm.App/Windows/EntryWindow.axaml.cs
index e647215..ae9e612 100644
--- a/src/Nonemm.App/Windows/EntryWindow.axaml.cs
+++ b/src/Nonemm.App/Windows/EntryWindow.axaml.cs
@@ -84,11 +84,13 @@ public sealed partial class EntryWindow : Window
PropertyChanged += (_, e) =>
{
if (e.Property == WindowStateProperty
+ && session.Settings.MinimizeWindowsTogether
&& WindowState is WindowState.Minimized or WindowState.Normal)
{
FollowWindowState(WindowState);
}
};
+ Activated += (_, _) => RaiseTogether(this);
Closed += (_, _) =>
{
foreach (Window window in openWindows.Values.ToList())
@@ -192,6 +194,27 @@ public sealed partial class EntryWindow : Window
}
}
+ private long lastRaise;
+
+ /// Brings the other windows that are not minimized above whatever else is
+ /// on screen, then puts `front` back on top. Activating them fires their
+ /// own Activated events, so raises within half a second are ignored.
+ private void RaiseTogether(Window front)
+ {
+ if (!session.Settings.RaiseWindowsTogether || Environment.TickCount64 - lastRaise < 500)
+ {
+ return;
+ }
+ lastRaise = Environment.TickCount64;
+ IEnumerable others = Placed().Concat(secondRadio is null ? [] : [secondRadio])
+ .Where(w => w != front && w.WindowState != WindowState.Minimized);
+ foreach (Window window in others)
+ {
+ window.Activate();
+ }
+ front.Activate();
+ }
+
/// The keyboard follows the operator to the other radio.
private void FollowActiveRadio(int number)
{