Mark the picked line in the log only while the window is in front

A marked line in a window behind says nothing about where the keyboard
is. The log takes the marks off as another window is activated and puts
them back when it comes forward; the contacts stay picked in between, so
"Change Operator…" and Delete still act on what the operator picked.

The frame around the cell the keyboard is on goes with them. The theme
draws that frame with a rectangle inside the cell, not with the cell's
own border, so it takes a style on the template.

Quick edit moves the picked line: Ctrl+Q and Ctrl+A step through the log
from the entry window, and the contact in the entry boxes is the picked
line here and is scrolled to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
This commit is contained in:
2026-09-10 17:22:54 +00:00
co-authored by Claude Opus 5
parent 606c43e4a6
commit 00bbb0e6b3
2 changed files with 91 additions and 0 deletions
+7
View File
@@ -29,6 +29,13 @@
<Style Selector="DataGridColumnHeader"> <Style Selector="DataGridColumnHeader">
<Setter Property="MinHeight" Value="0" /> <Setter Property="MinHeight" Value="0" />
</Style> </Style>
<!-- the frame around the cell the keyboard is on goes with the picked
lines while another window is in front. The theme draws it with a
rectangle inside the cell rather than with the cell's own border,
so setting the border brush does nothing -->
<Style Selector="DataGrid.away DataGridCell:current /template/ Rectangle">
<Setter Property="IsVisible" Value="False" />
</Style>
<!-- the editing box takes the same height as the text it covers, <!-- the editing box takes the same height as the text it covers,
otherwise it grows the row and the whole grid shifts as editing otherwise it grows the row and the whole grid shifts as editing
starts. Its border is what would make it two pixels taller, so it starts. Its border is what would make it two pixels taller, so it
+84
View File
@@ -24,6 +24,16 @@ public sealed partial class LogWindow : RefreshableWindow
/// when only the values changed, so that an edit does not rebuild the list /// when only the values changed, so that an edit does not rebuild the list
/// and move the view. /// and move the view.
private List<LogRow> shownRows = []; private List<LogRow> shownRows = [];
/// The contacts the operator picked, by id. The grid is told to show them
/// as picked only while this window is in front: a marked line in a window
/// behind says nothing about where the keyboard is.
private List<string> picked = [];
/// True while this window is setting the selection itself, so that its own
/// changes are not read back as the operator's.
private bool picking;
/// The contact quick edit is on, so that the picked line follows Ctrl+Q
/// and Ctrl+A rather than being put back on every refresh.
private string quickEditId = "";
private string finding = ""; private string finding = "";
public LogWindow(AppSession session) public LogWindow(AppSession session)
@@ -39,11 +49,79 @@ public sealed partial class LogWindow : RefreshableWindow
Rows.PreparingCellForEdit += SelectCellText; Rows.PreparingCellForEdit += SelectCellText;
Rows.CellEditEnding += TakeCellEdit; Rows.CellEditEnding += TakeCellEdit;
Rows.AddHandler(ContextRequestedEvent, OnContextRequested, RoutingStrategies.Bubble); Rows.AddHandler(ContextRequestedEvent, OnContextRequested, RoutingStrategies.Bubble);
Rows.SelectionChanged += RememberPicked;
Activated += (_, _) =>
{
Rows.Classes.Remove("away");
ShowPicked();
};
Deactivated += (_, _) =>
{
Rows.Classes.Add("away");
HidePicked();
};
Colour(Rows); Colour(Rows);
Colour(Earlier); Colour(Earlier);
Refresh(); Refresh();
} }
/// What the operator picked. The grid raises this for the picks this window
/// makes as well, and those are already known.
private void RememberPicked(object? sender, SelectionChangedEventArgs e)
{
if (picking)
{
return;
}
picked = [.. Rows.SelectedItems.OfType<LogRow>().Select(r => r.Qso.Id)];
}
/// Marks the picked lines again. Only a window in front marks them, so the
/// callers that are not the window coming to the front ask whether it is in
/// front first.
private void ShowPicked()
{
picking = true;
Rows.SelectedItems.Clear();
foreach (LogRow row in shownRows.Where(r => picked.Contains(r.Qso.Id)))
{
Rows.SelectedItems.Add(row);
}
picking = false;
}
/// Takes the marks off while another window has the keyboard. The contacts
/// stay picked and are marked again when the window comes back.
private void HidePicked()
{
picking = true;
Rows.SelectedItems.Clear();
picking = false;
}
/// Quick edit steps through the log from the entry window with Ctrl+Q and
/// Ctrl+A. The contact in the entry boxes is the picked line here, and it
/// is scrolled to whether or not this window is in front.
private void FollowQuickEdit()
{
string id = session.Position?.Editing?.Id ?? "";
if (id == quickEditId || id.Length == 0)
{
quickEditId = id;
return;
}
quickEditId = id;
picked = [id];
if (IsActive)
{
ShowPicked();
}
if (shownRows.FirstOrDefault(r => r.Qso.Id == id) is { } row)
{
Rows.ScrollIntoView(row, null);
}
}
/// A cell that has just opened for editing has all of its text selected, so /// A cell that has just opened for editing has all of its text selected, so
/// typing replaces the value rather than adding to it. /// typing replaces the value rather than adding to it.
private static void SelectCellText(object? sender, DataGridPreparingCellForEditEventArgs e) private static void SelectCellText(object? sender, DataGridPreparingCellForEditEventArgs e)
@@ -139,7 +217,13 @@ public sealed partial class LogWindow : RefreshableWindow
{ {
ShowAgain(bottom, shownRows); ShowAgain(bottom, shownRows);
} }
// the new list comes with nothing picked
if (IsActive)
{
ShowPicked();
}
} }
FollowQuickEdit();
ShowEarlier(); ShowEarlier();
SummaryText.Text = message.Length > 0 ? message : Summary(session.Logging); SummaryText.Text = message.Length > 0 ? message : Summary(session.Logging);
message = ""; message = "";