Show what has gone out in the transmit pane
The transmit pane is now the type-ahead buffer. What has gone to the engine is in red beside the box and cannot be reached; what is still to go is in the box and can be typed over, added to or deleted while the engine works through it. A macro fills the pane rather than the engine, so the operator can correct a call the message is still sending. The cursor holds the pump: nothing behind it goes out, so an engine that catches up with the operator idles instead of transmitting half a word. With the box out of focus there is no cursor and everything goes. Escape and the RX button drop what has not gone out and stop the engine where it is. Enter puts in the carriage return the engine takes as a new line. The pane starts empty for the next message. The baud rate the pump counts by is in the digital setup window, beside the alignment frequency. It has to match the engine: 45.45 unless the contest says otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
This commit is contained in:
@@ -36,6 +36,9 @@ public sealed partial class DigitalWindow : RefreshableWindow
|
||||
/// What N1MM marks a call its resource files do not hold with.
|
||||
private static readonly IBrush Unknown = Brushes.Gold;
|
||||
|
||||
/// What is drawn over the text that has already gone to the engine.
|
||||
private static readonly IBrush Transmitted = new SolidColorBrush(Color.FromRgb(0xC0, 0x39, 0x2B));
|
||||
|
||||
private readonly AppSession session;
|
||||
private readonly EntryWindow entry;
|
||||
private readonly int radioNumber;
|
||||
@@ -52,6 +55,13 @@ public sealed partial class DigitalWindow : RefreshableWindow
|
||||
private bool paused;
|
||||
private bool lastWasReturn;
|
||||
|
||||
/// True while the transmit box is being redrawn from the buffer, so the
|
||||
/// redraw is not read back as an edit by the operator.
|
||||
private bool showingBuffer;
|
||||
|
||||
/// The buffer this window is drawing, or null while no engine is running.
|
||||
private TypeAhead? buffer;
|
||||
|
||||
/// Where the next line goes in a non-scrolling pane. It walks down the
|
||||
/// window and starts again at the top; -1 is before the first line.
|
||||
private int lineAt = -1;
|
||||
@@ -72,6 +82,9 @@ public sealed partial class DigitalWindow : RefreshableWindow
|
||||
entry.Activated += WhenEntryActivated;
|
||||
BuildMacros();
|
||||
ApplySettings();
|
||||
// the cursor holds the pump back, so it has to follow the caret as it
|
||||
// moves, not only as the text changes
|
||||
TransmitBox.GetObservable(TextBox.CaretIndexProperty).Subscribe(new Watcher(ShowCursor));
|
||||
Attach(session.Digital);
|
||||
Refresh();
|
||||
Closed += (_, _) =>
|
||||
@@ -91,6 +104,13 @@ public sealed partial class DigitalWindow : RefreshableWindow
|
||||
{
|
||||
ReceiveScroller.Background = Themes.Brush(Themes.Current.FieldBackground);
|
||||
GrabScroller.Background = Themes.Brush(Themes.Current.FieldBackground);
|
||||
SentText.Foreground = Transmitted;
|
||||
SentText.FontSize = Settings.DigitalFontSize;
|
||||
TransmitBox.FontSize = Settings.DigitalFontSize;
|
||||
if (buffer is not null)
|
||||
{
|
||||
buffer.Baud = Settings.DigitalBaud;
|
||||
}
|
||||
FollowRunAndFrequency();
|
||||
ShowState();
|
||||
ShowGrabList();
|
||||
@@ -127,6 +147,12 @@ public sealed partial class DigitalWindow : RefreshableWindow
|
||||
}
|
||||
Detach();
|
||||
engine = started;
|
||||
buffer = session.DigitalKeyer?.TypeAhead;
|
||||
if (buffer is not null)
|
||||
{
|
||||
buffer.Baud = Settings.DigitalBaud;
|
||||
buffer.Changed += WhenBufferChanged;
|
||||
}
|
||||
started.Received += WhenReceived;
|
||||
started.TransmitChanged += WhenTransmitChanged;
|
||||
started.ConnectionChanged += WhenConnectionChanged;
|
||||
@@ -139,6 +165,11 @@ public sealed partial class DigitalWindow : RefreshableWindow
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (buffer is not null)
|
||||
{
|
||||
buffer.Changed -= WhenBufferChanged;
|
||||
buffer = null;
|
||||
}
|
||||
engine.Received -= WhenReceived;
|
||||
engine.TransmitChanged -= WhenTransmitChanged;
|
||||
engine.ConnectionChanged -= WhenConnectionChanged;
|
||||
@@ -154,12 +185,24 @@ public sealed partial class DigitalWindow : RefreshableWindow
|
||||
Print(text);
|
||||
});
|
||||
|
||||
private void WhenBufferChanged(object? sender, EventArgs e) =>
|
||||
Dispatcher.UIThread.Post(ShowBuffer);
|
||||
|
||||
/// The pane starts empty for the next message: the transmitter dropping
|
||||
/// with nothing left to send is the end of this one. An engine that keys
|
||||
/// itself drops between two characters as well, and that text is left
|
||||
/// where it is.
|
||||
private void WhenTransmitChanged(object? sender, bool transmitting) =>
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
TransmitDot.Background = transmitting
|
||||
? Themes.Brush(Themes.Current.TransmitLight)
|
||||
: Brushes.Transparent;
|
||||
if (!transmitting && buffer is { IsSending: false })
|
||||
{
|
||||
buffer.Clear();
|
||||
ShowBuffer();
|
||||
}
|
||||
});
|
||||
|
||||
private void WhenConnectionChanged(object? sender, bool connected) =>
|
||||
@@ -525,3 +568,18 @@ public sealed partial class DigitalWindow : RefreshableWindow
|
||||
_ => StackOrder.Disabled,
|
||||
};
|
||||
}
|
||||
|
||||
/// Watches one property. Avalonia hands out observables and this program has no
|
||||
/// other use for Rx, so a handler that takes no value is enough.
|
||||
internal sealed class Watcher(Action changed) : IObserver<int>
|
||||
{
|
||||
public void OnCompleted()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnNext(int value) => changed();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user