diff --git a/README.md b/README.md
index 2d137c8..dbd655c 100644
--- a/README.md
+++ b/README.md
@@ -102,9 +102,14 @@ to F12 while running, and twelve more give the same keys while searching, with
the ones left out keeping their running message. The buttons say what the file
says, and they change as you move between running and searching.
+The lines are coloured as N1MM colours them: grey for a comment, blue for the
+keys while running, green for the keys while searching, and red for anything
+past the twenty-four lines that are read, so a file with a line too many says so
+on the screen.
+
This is N1MM's `.mc` format. **Import…** reads a file somebody published for
N1MM, **Export…** writes one out, and **Back to the defaults** puts the built-in
-messages back.
+messages back. Escape closes the editor and keeps what was there before.
The macros are N1MM's, spelled the way N1MM's function-key documentation spells
them, so a `.mc` file written for either program says the same thing. The text
diff --git a/src/Nonemm.App/Dialogs/MessagesDialog.axaml b/src/Nonemm.App/Dialogs/MessagesDialog.axaml
index a05702b..8cf0923 100644
--- a/src/Nonemm.App/Dialogs/MessagesDialog.axaml
+++ b/src/Nonemm.App/Dialogs/MessagesDialog.axaml
@@ -2,7 +2,15 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Nonemm.App.Dialogs.MessagesDialog"
Title="Function key messages" Width="620" Height="520"
- WindowStartupLocation="CenterOwner">
+ WindowStartupLocation="CenterOwner" KeyDown="OnKeyDown">
+
+
+
+
@@ -17,8 +25,18 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/src/Nonemm.App/Dialogs/MessagesDialog.axaml.cs b/src/Nonemm.App/Dialogs/MessagesDialog.axaml.cs
index 3e8cc4f..2faea9f 100644
--- a/src/Nonemm.App/Dialogs/MessagesDialog.axaml.cs
+++ b/src/Nonemm.App/Dialogs/MessagesDialog.axaml.cs
@@ -1,7 +1,10 @@
using Avalonia.Controls;
+using Avalonia.Input;
using Avalonia.Interactivity;
+using Avalonia.Media;
using Avalonia.Platform.Storage;
using Nonemm.Core;
+using Nonemm.Session;
namespace Nonemm.App.Dialogs;
@@ -25,6 +28,62 @@ public sealed partial class MessagesDialog : Window
InitializeComponent();
Title = $"{what} — {(mode == ModeCategory.Phone ? "phone" : "CW")}";
TextArea.Text = text.Trim().Length > 0 ? text : Messages.DefaultFor(mode);
+ TextArea.CaretBrush = Foreground;
+ TextArea.TextChanged += (_, _) => Paint();
+ Paint();
+ }
+
+ /// The same colours the rest of the program uses for a verdict: grey for a
+ /// comment, blue for the keys while running, green for the keys while
+ /// searching, red for the lines past the twenty-four N1MM reads.
+ /// The colours are the ones the rest of the program uses for a verdict:
+ /// grey for a comment, blue for the keys while running, green for the keys
+ /// while searching, red for anything past the twenty-four N1MM reads.
+ ///
+ /// The lines are kept and rewritten rather than rebuilt, because a control
+ /// that has been shown once cannot be put back into a fresh list.
+ private void Paint()
+ {
+ string[] lines = (TextArea.Text ?? "").Split('\n');
+ while (Highlight.Children.Count < lines.Length)
+ {
+ Highlight.Children.Add(new TextBlock
+ {
+ FontFamily = TextArea.FontFamily,
+ FontSize = TextArea.FontSize,
+ TextWrapping = TextWrapping.NoWrap,
+ });
+ }
+ while (Highlight.Children.Count > lines.Length)
+ {
+ Highlight.Children.RemoveAt(Highlight.Children.Count - 1);
+ }
+
+ int key = 0;
+ for (int at = 0; at < lines.Length; at++)
+ {
+ IBrush colour = Verdicts.Nothing;
+ if (!lines[at].StartsWith('#'))
+ {
+ colour = key < MessageFile.KeyCount ? Verdicts.Worth
+ : key < 2 * MessageFile.KeyCount ? Verdicts.NewMultiplier
+ : Verdicts.Dupe;
+ key++;
+ }
+ TextBlock line = (TextBlock)Highlight.Children[at];
+ // a line with nothing in it still has to take up its height
+ line.Text = lines[at].Length > 0 ? lines[at] : " ";
+ line.Foreground = colour;
+ }
+ }
+
+ private void OnKeyDown(object? sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Escape)
+ {
+ e.Handled = true;
+ Close(null);
+ }
}
private async void OnImport(object? sender, RoutedEventArgs e)