diff --git a/src/Nonemm.App/Windows/CheckWindow.axaml.cs b/src/Nonemm.App/Windows/CheckWindow.axaml.cs index b137e36..de8a092 100644 --- a/src/Nonemm.App/Windows/CheckWindow.axaml.cs +++ b/src/Nonemm.App/Windows/CheckWindow.axaml.cs @@ -27,6 +27,8 @@ public sealed partial class CheckWindow : RefreshableWindow } + public override void RefreshForEntry() => Refresh(); + public override void Refresh() { Columns.Children.Clear(); diff --git a/src/Nonemm.App/Windows/EntryWindow.axaml.cs b/src/Nonemm.App/Windows/EntryWindow.axaml.cs index 0a435af..bdde515 100644 --- a/src/Nonemm.App/Windows/EntryWindow.axaml.cs +++ b/src/Nonemm.App/Windows/EntryWindow.axaml.cs @@ -83,8 +83,16 @@ public sealed partial class EntryWindow : Window Closing += (_, _) => session.Save(WithLayout(session.Settings)); PropertyChanged += (_, e) => { - if (e.Property == WindowStateProperty - && session.Settings.MinimizeWindowsTogether + if (e.Property != WindowStateProperty) + { + return; + } + lastStateChange = Environment.TickCount64; + if (WindowState == WindowState.Normal) + { + Dispatcher.UIThread.Post(() => boxes.ElementAtOrDefault(0)?.Focus(), DispatcherPriority.Background); + } + if (session.Settings.MinimizeWindowsTogether && WindowState is WindowState.Minimized or WindowState.Normal) { FollowWindowState(WindowState); @@ -195,13 +203,19 @@ public sealed partial class EntryWindow : Window } private long lastRaise; + private long lastStateChange; /// 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) + // The window manager activates another window when the entry window + // minimizes. Raising then would restore the entry window. + if (!session.Settings.RaiseWindowsTogether + || Environment.TickCount64 - lastRaise < 500 + || Environment.TickCount64 - lastStateChange < 1000 + || WindowState == WindowState.Minimized) { return; } @@ -380,7 +394,8 @@ public sealed partial class EntryWindow : Window private bool typingRefreshPosted; /// Refresh redraws this window and every open tool window, which is too - /// slow to run between two key presses. It runs once the key presses + /// slow to run between two key presses. Typing refreshes this window and + /// the tool windows that show the call, and it runs once the key presses /// waiting in the queue have been handled, so fast typing costs one /// refresh instead of one per letter. private void RefreshAfterTyping() @@ -394,7 +409,7 @@ public sealed partial class EntryWindow : Window () => { typingRefreshPosted = false; - Refresh(); + RefreshForTyping(); }, DispatcherPriority.Background); } @@ -919,6 +934,26 @@ public sealed partial class EntryWindow : Window } internal void Refresh() + { + RefreshSelf(); + foreach (Window window in openWindows.Values) + { + (window as RefreshableWindow)?.Refresh(); + } + } + + /// What a typed letter changes: this window, and the tool windows that + /// show something about the call being typed. + private void RefreshForTyping() + { + RefreshSelf(); + foreach (Window window in openWindows.Values) + { + (window as RefreshableWindow)?.RefreshForEntry(); + } + } + + private void RefreshSelf() { ShowFunctionKeys(); ShowBandButtons(); @@ -967,10 +1002,6 @@ public sealed partial class EntryWindow : Window VerdictBorder.Background = Verdicts.Background(verdict); VerdictText.Text = VerdictLine(verdict); VerdictText.Foreground = Themes.Brush(Themes.Current.FormForeground); - foreach (Window window in openWindows.Values) - { - (window as RefreshableWindow)?.Refresh(); - } } /// N1MM colours the callsign itself rather than the box: blue for a station diff --git a/src/Nonemm.App/Windows/GraylineWindow.axaml.cs b/src/Nonemm.App/Windows/GraylineWindow.axaml.cs index b99e679..0d3259f 100644 --- a/src/Nonemm.App/Windows/GraylineWindow.axaml.cs +++ b/src/Nonemm.App/Windows/GraylineWindow.axaml.cs @@ -36,6 +36,8 @@ public sealed partial class GraylineWindow : RefreshableWindow private OperatingPosition? Radio => session.Position; + public override void RefreshForEntry() => Refresh(); + public override void Refresh() { Map.Moment = DateTime.UtcNow; diff --git a/src/Nonemm.App/Windows/LogWindow.axaml.cs b/src/Nonemm.App/Windows/LogWindow.axaml.cs index fbcafd8..a5e3aca 100644 --- a/src/Nonemm.App/Windows/LogWindow.axaml.cs +++ b/src/Nonemm.App/Windows/LogWindow.axaml.cs @@ -204,6 +204,8 @@ public sealed partial class LogWindow : RefreshableWindow return new ContextMenu { ItemsSource = new List { edit, operators, delete } }; } + public override void RefreshForEntry() => ShowEarlier(); + public override void Refresh() { if (session.Logging is null) diff --git a/src/Nonemm.App/Windows/RefreshableWindow.cs b/src/Nonemm.App/Windows/RefreshableWindow.cs index b4da26b..07df951 100644 --- a/src/Nonemm.App/Windows/RefreshableWindow.cs +++ b/src/Nonemm.App/Windows/RefreshableWindow.cs @@ -6,4 +6,10 @@ namespace Nonemm.App.Windows; public abstract class RefreshableWindow : Window { public abstract void Refresh(); + + /// Called instead of `Refresh` when the entry window's call or exchange + /// changes. A window that shows nothing about them does nothing. + public virtual void RefreshForEntry() + { + } } diff --git a/src/Nonemm.Core/Calls/PartialMatching.cs b/src/Nonemm.Core/Calls/PartialMatching.cs index 6226cd7..ef8faa9 100644 --- a/src/Nonemm.Core/Calls/PartialMatching.cs +++ b/src/Nonemm.Core/Calls/PartialMatching.cs @@ -14,20 +14,20 @@ public static class PartialMatching { return null; } - if (Same(query, candidate)) + if (Same(query, candidate.AsSpan())) { return PartialMatchQuality.Exact; } if (query.Length < candidate.Length) { - if (Same(query, candidate[..query.Length]) || - Same(query, candidate[^query.Length..])) + if (Same(query, candidate.AsSpan(0, query.Length)) || + Same(query, candidate.AsSpan(candidate.Length - query.Length))) { return PartialMatchQuality.StartsOrEnds; } for (int at = 1; at + query.Length < candidate.Length; at++) { - if (Same(query, candidate.Substring(at, query.Length))) + if (Same(query, candidate.AsSpan(at, query.Length))) { return PartialMatchQuality.Contains; } @@ -69,7 +69,7 @@ public static class PartialMatching SameCharacter(query[firstDifference + 1], candidate[firstDifference]); } - private static bool Same(string query, string candidate) + private static bool Same(string query, ReadOnlySpan candidate) { if (query.Length != candidate.Length) {