Browse Source

Flags-column funnel always opens popup; header on top

Two related fixes for the flags-filter UX.

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
d26d5a171e
  1. 44
      ILSpy/Controls/FlagsFilterPopup.cs
  2. 41
      ILSpy/Metadata/MetadataColumnBuilder.cs

44
ILSpy/Controls/FlagsFilterPopup.cs

@ -53,6 +53,31 @@ namespace ILSpy.Views.Filters @@ -53,6 +53,31 @@ namespace ILSpy.Views.Filters
ApplyAccentPalette(Styles);
var stack = new StackPanel { Orientation = Orientation.Vertical, Spacing = 4 };
// Header row at the top: hint on the left, Clear button on the right. Same
// content the popup used to put at the bottom — moved to the top so the
// "how to use this" text and the explicit clear affordance are both visible
// without scrolling past the chip groups.
var clearButton = new Button {
Content = "Clear",
MinHeight = 0,
Padding = new Thickness(6, 1),
VerticalAlignment = VerticalAlignment.Top,
};
clearButton.Click += (_, _) => Clear();
var chipHint = new TextBlock {
Text = "Click a chip to select only that value · Shift+Click to add to / remove from the selection · Click <Any> to clear",
FontStyle = FontStyle.Italic,
Foreground = Brushes.Gray,
TextWrapping = TextWrapping.Wrap,
VerticalAlignment = VerticalAlignment.Center,
};
var headerRow = new DockPanel { LastChildFill = true, Margin = new Thickness(0, 0, 0, 4) };
DockPanel.SetDock(clearButton, global::Avalonia.Controls.Dock.Right);
headerRow.Children.Add(clearButton);
headerRow.Children.Add(chipHint);
stack.Children.Add(headerRow);
foreach (var g in state.Schema.MutexGroups)
{
var chips = new MutexChipGroup(state, g);
@ -65,17 +90,6 @@ namespace ILSpy.Views.Filters @@ -65,17 +90,6 @@ namespace ILSpy.Views.Filters
stack.Children.Add(independentGroup);
}
// Hint text in the same style as the live filter summary below — just a quiet
// italic line explaining the chip click semantics so users don't have to guess.
var chipHint = new TextBlock {
Text = "Click a chip to select only that value · Shift+Click to add to / remove from the selection · Click <Any> to clear",
FontStyle = FontStyle.Italic,
Foreground = Brushes.Gray,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0, 6, 0, 0),
};
stack.Children.Add(chipHint);
summary = new TextBlock {
FontStyle = FontStyle.Italic,
Foreground = Brushes.Gray,
@ -84,14 +98,6 @@ namespace ILSpy.Views.Filters @@ -84,14 +98,6 @@ namespace ILSpy.Views.Filters
};
stack.Children.Add(summary);
var clearButton = new Button {
Content = "Clear",
HorizontalAlignment = HorizontalAlignment.Right,
Margin = new Thickness(0, 4, 0, 0),
};
clearButton.Click += (_, _) => Clear();
stack.Children.Add(clearButton);
Content = new Border {
Padding = new Thickness(8),
MinWidth = 220,

41
ILSpy/Metadata/MetadataColumnBuilder.cs

@ -197,23 +197,25 @@ namespace ILSpy.Metadata @@ -197,23 +197,25 @@ namespace ILSpy.Metadata
headerRow.Children.Add(headerContent);
filterIconHost.PointerPressed += (_, e) => {
bool isActive = !string.IsNullOrWhiteSpace(filter.Text)
|| (filter.FlagsState != null && !filter.FlagsState.IsEmpty);
if (isActive)
{
// Icon is an X right now — click clears the filter. Both branches raise
// the appropriate PropertyChanged so Update() swaps the icon back to the
// funnel and the row is re-evaluated.
filter.Text = string.Empty;
filter.FlagsState?.Clear();
}
else if (popup != null)
if (popup != null)
{
// Flag columns: the funnel always opens the popup. Modifying the filter
// requires the chip UI inside the popup, and clearing is owned by the
// popup's own Clear button — calling FlagsState.Clear() from out here
// would leave the chip IsChecked state stale because we'd skip the
// SyncFromState() pass that FlagsFilterPopup.Clear() does.
popup.IsOpen = true;
}
else if (textBox != null)
else
{
textBox.Focus();
// Text columns: the icon doubles as a clear button when a filter is active
// (icon visually morphs into an X via Update()). Click while active wipes
// the TextBox; click while inactive focuses it.
bool isActive = !string.IsNullOrWhiteSpace(filter.Text);
if (isActive)
filter.Text = string.Empty;
else if (textBox != null)
textBox.Focus();
}
e.Handled = true;
};
@ -242,10 +244,13 @@ namespace ILSpy.Metadata @@ -242,10 +244,13 @@ namespace ILSpy.Metadata
}
// Flag columns leave label IsVisible alone — it stays on for sort clicks while
// the funnel icon owns the popup-open gesture.
// Active state swaps the funnel out for an X — icon becomes the "clear filter"
// affordance. Funnel uses Fill (closed shape); X is two crossing strokes, so
// it has no Fill and instead paints with Stroke. Toggle both modes in lockstep.
if (active)
// Icon affordance: the X form indicates "click to clear". That only matches
// the click behaviour on text columns (where clicking when active does clear);
// for flag columns the click always opens the popup, so we keep the funnel
// shape and just tint it SteelBlue to signal the filter is active.
bool useXForm = active && popup is null;
if (useXForm)
{
filterIcon.Data = xGeometry;
filterIcon.Fill = null;
@ -255,7 +260,7 @@ namespace ILSpy.Metadata @@ -255,7 +260,7 @@ namespace ILSpy.Metadata
else
{
filterIcon.Data = funnelGeometry;
filterIcon.Fill = Brushes.Gray;
filterIcon.Fill = active ? Brushes.SteelBlue : Brushes.Gray;
filterIcon.Stroke = null;
}
}

Loading…
Cancel
Save