Browse Source

Search pane UX — dropdown icons, DSL hint, clear-X

Three visible gaps from WPF parity, fixed together:
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
478371c4f9
  1. 1
      ILSpy/Assets/Icons/Literal.svg
  2. 1
      ILSpy/Images.cs
  3. 32
      ILSpy/Search/SearchPane.axaml
  4. 12
      ILSpy/Search/SearchPane.axaml.cs
  5. 30
      ILSpy/Search/SearchPaneModel.cs

1
ILSpy/Assets/Icons/Literal.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.st0{opacity:0}.st0,.st1{fill:#f6f6f6}.st2{fill:#424242}.st3{fill:#f0eff1}</style><g id="outline"><path class="st0" d="M0 0h16v16H0z"/><path class="st1" d="M16 0H1v6H0v10h9v-2h7z"/></g><g id="icon_x5F_bg"><path class="st2" d="M2 1v6H1v8h7v-2h7V1H2zm5 13H2V8h5v6zm7-2H8V7H3V2h11v10z"/><path class="st2" d="M5.527 9.379a1.073 1.073 0 0 0-.423-.295 1.649 1.649 0 0 0-.609-.102c-.119 0-.241.012-.368.033a3.194 3.194 0 0 0-.672.196 1.375 1.375 0 0 0-.213.113v.768c.158-.131.341-.235.544-.313.204-.078.413-.117.627-.117.213 0 .377.063.494.186.116.125.174.324.174.6l-1.03.154a1.631 1.631 0 0 0-.525.152 1.08 1.08 0 0 0-.562.66 1.557 1.557 0 0 0-.065.461c0 .17.025.322.074.463s.121.26.216.361c.093.1.21.178.352.234.14.057.298.084.479.084.229 0 .431-.053.604-.16a1.3 1.3 0 0 0 .439-.463l-.054.067v.529l.85.01v-2.514c0-.238-.027-.451-.083-.637a1.26 1.26 0 0 0-.249-.47zm-.925 2.879c-.089.067-.213.072-.337.072a.682.682 0 0 1-.242-.041.489.489 0 0 1-.302-.285.588.588 0 0 1-.041-.219c0-.086.01-.164.027-.233a.435.435 0 0 1 .098-.18A.553.553 0 0 1 4 11.24c.083-.033.189-.061.32-.078l.698-.109v.346c.001-.001-.096.617-.416.859z"/></g><path class="st3" d="M3 2v5h5v5h6V2z" id="icon_x5F_fg"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

1
ILSpy/Images.cs

@ -107,6 +107,7 @@ namespace ILSpy.Images @@ -107,6 +107,7 @@ namespace ILSpy.Images
public static readonly IImage Field = LoadSvg(nameof(Field));
public static readonly IImage Property = LoadSvg(nameof(Property));
public static readonly IImage Event = LoadSvg(nameof(Event));
public static readonly IImage Literal = LoadSvg(nameof(Literal));
// Reference overlays (small badge layered onto a base icon for TypeRef/MemberRef nodes).
internal static readonly IImage ReferenceOverlay = LoadSvg(nameof(ReferenceOverlay));

32
ILSpy/Search/SearchPane.axaml

@ -9,15 +9,39 @@ @@ -9,15 +9,39 @@
<Grid RowDefinitions="Auto,Auto,*" Margin="4">
<Grid Grid.Row="0" ColumnDefinitions="*,8,Auto" Margin="0,0,0,4">
<TextBox Grid.Column="0" Name="SearchInput"
PlaceholderText="Search…"
Text="{Binding SearchTerm, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
PlaceholderText="Search for t:TypeName, m:Member or c:Constant; use exact match (=term), 'should not contain' (-term) or 'must contain' (+term); use /reg(ular)?Ex(pressions)?/ or both - t:/Type(Name)?/..."
ToolTip.Tip="Search"
Text="{Binding SearchTerm, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InnerRightContent>
<!-- Visibility comes from string.Length via the StringConverters.IsNotNullOrEmpty
static — Avalonia's built-in null/empty converter doesn't ship with this
specific name, so the IsVisible binds to SearchTerm directly using a length
check via x:Static is overkill; the button hides itself when SearchTerm == "".
Click handler in the code-behind sets SearchTerm = "" via the view-model. -->
<Button Name="ClearSearchButton"
IsVisible="{Binding SearchTerm, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Background="Transparent" BorderThickness="0"
Padding="4,0" Margin="0"
ToolTip.Tip="Clear (Esc)"
Click="OnClearSearchClicked">
<TextBlock Text="✕" FontSize="11" VerticalAlignment="Center" />
</Button>
</TextBox.InnerRightContent>
</TextBox>
<ComboBox Grid.Column="2" Name="SearchModePicker"
ItemsSource="{Binding SearchModes}"
SelectedItem="{Binding SelectedSearchMode, Mode=TwoWay}"
MinWidth="150">
MinWidth="180">
<ComboBox.ItemTemplate>
<DataTemplate DataType="search:SearchModeEntry">
<TextBlock Text="{Binding Name}" />
<!-- Icon + label, matching the result-row template — Grid (not StackPanel)
keeps the icon column un-compressed even when the dropdown is narrow. -->
<Grid ColumnDefinitions="Auto,4,*">
<Image Grid.Column="0" Source="{Binding Image}" Width="16" Height="16"
VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding Name}"
VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

12
ILSpy/Search/SearchPane.axaml.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Threading;
using ICSharpCode.ILSpyX.Search;
@ -37,6 +38,17 @@ namespace ILSpy.Search @@ -37,6 +38,17 @@ namespace ILSpy.Search
SearchResults.KeyDown += OnResultKeyDown;
}
void OnClearSearchClicked(object? sender, RoutedEventArgs e)
{
// Empty the query and return focus to the input so the user can start typing
// straight away. Setting SearchTerm = "" triggers the same cancel-and-restart
// path as deleting the text by hand.
if (DataContext is SearchPaneModel vm)
vm.SearchTerm = string.Empty;
SearchInput.Focus();
e.Handled = true;
}
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);

30
ILSpy/Search/SearchPaneModel.cs

@ -20,6 +20,8 @@ using System; @@ -20,6 +20,8 @@ using System;
using System.Collections.ObjectModel;
using System.Composition;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using ICSharpCode.ILSpyX;
@ -36,12 +38,14 @@ namespace ILSpy.Search @@ -36,12 +38,14 @@ namespace ILSpy.Search
/// <summary>
/// One entry in the search-mode picker. The <see cref="Mode"/> determines which
/// <c>ICSharpCode.ILSpyX.Search</c> strategy runs when the user types a query;
/// <see cref="Name"/> is the label rendered in the ComboBox.
/// <see cref="Name"/> is the label rendered in the ComboBox and <see cref="Image"/>
/// is the icon shown alongside it (both in the dropdown and in the closed selector).
/// </summary>
public sealed class SearchModeEntry
{
public required SearchMode Mode { get; init; }
public required string Name { get; init; }
public required IImage Image { get; init; }
}
[Export]
@ -71,18 +75,18 @@ namespace ILSpy.Search @@ -71,18 +75,18 @@ namespace ILSpy.Search
/// strings so users moving between builds see the same labels.
/// </summary>
public SearchModeEntry[] SearchModes { get; } = new[] {
new SearchModeEntry { Mode = SearchMode.TypeAndMember, Name = "Types and Members" },
new SearchModeEntry { Mode = SearchMode.Type, Name = "Type" },
new SearchModeEntry { Mode = SearchMode.Member, Name = "Member" },
new SearchModeEntry { Mode = SearchMode.Method, Name = "Method" },
new SearchModeEntry { Mode = SearchMode.Field, Name = "Field" },
new SearchModeEntry { Mode = SearchMode.Property, Name = "Property" },
new SearchModeEntry { Mode = SearchMode.Event, Name = "Event" },
new SearchModeEntry { Mode = SearchMode.Literal, Name = "Constant" },
new SearchModeEntry { Mode = SearchMode.Token, Name = "Metadata Token" },
new SearchModeEntry { Mode = SearchMode.Resource, Name = "Resource" },
new SearchModeEntry { Mode = SearchMode.Assembly, Name = "Assembly" },
new SearchModeEntry { Mode = SearchMode.Namespace, Name = "Namespace" },
new SearchModeEntry { Mode = SearchMode.TypeAndMember, Name = "Types and Members", Image = Images.Images.Library },
new SearchModeEntry { Mode = SearchMode.Type, Name = "Type", Image = Images.Images.Class },
new SearchModeEntry { Mode = SearchMode.Member, Name = "Member", Image = Images.Images.Property },
new SearchModeEntry { Mode = SearchMode.Method, Name = "Method", Image = Images.Images.Method },
new SearchModeEntry { Mode = SearchMode.Field, Name = "Field", Image = Images.Images.Field },
new SearchModeEntry { Mode = SearchMode.Property, Name = "Property", Image = Images.Images.Property },
new SearchModeEntry { Mode = SearchMode.Event, Name = "Event", Image = Images.Images.Event },
new SearchModeEntry { Mode = SearchMode.Literal, Name = "Constant", Image = Images.Images.Literal },
new SearchModeEntry { Mode = SearchMode.Token, Name = "Metadata Token", Image = Images.Images.Library },
new SearchModeEntry { Mode = SearchMode.Resource, Name = "Resource", Image = Images.Images.Resource },
new SearchModeEntry { Mode = SearchMode.Assembly, Name = "Assembly", Image = Images.Images.Assembly },
new SearchModeEntry { Mode = SearchMode.Namespace, Name = "Namespace", Image = Images.Images.Namespace },
};
/// <summary>

Loading…
Cancel
Save