Turn the Install category filters into toggle chips (#4926)

* Turn the Install category filters into toggle chips

- Replace the filter buttons with toggles that show which ones are active
- Add ctrl click to select more than one category
- Keep the search box and the category filter independent of each other
- Expand matching categories while a filter is active

* Fix the filter interactions the category chips missed

- Pass the selected categories to the lazy rendering batches, the old call
  used a parameter that no longer exists and threw while typing
- Keep the category filter when switching tabs, the chips stayed checked
  while the filter itself was dropped
- Put a category back to collapsed once the filter that expanded it is gone

* Document the Install category filters

- Add a guide section for the chips, ctrl click and clearing the filter
- Note that search and the category chips apply together

* Correct the category filter guide wording

- Clearing by clicking the chip only applies when it is the only one selected
- Only categories with matches expand, and only auto expanded ones re-collapse
This commit is contained in:
MyDrift
2026-08-09 13:13:21 -05:00
committed by GitHub
parent 9fdadd1c8f
commit 32ab9f0ab0
13 changed files with 311 additions and 80 deletions
+7 -1
View File
@@ -42,6 +42,12 @@ Use the Applications tab to install, upgrade, uninstall, and review supported ap
![Collapse or expand all categories](../../../assets/screenshots/install-pics/Collapse&ExpandCat.png)
</TabItem>
<TabItem label="Category Filters">
* Click a category chip at the top of the tab to show only that category. The chip stays highlighted while its filter is active.
* Hold `Ctrl` and click to add more categories to the filter, or to remove one again.
* Click `All`, or click the highlighted category again while it is the only one selected, to clear the filter.
* Categories with matching results open while a filter is active. Ones that filtering opened for you go back to collapsed when you clear it, ones you opened yourself stay open.
</TabItem>
<TabItem label="Selected Apps Counter">
* The `Selected Apps` counter in the sidebar shows how many applications are currently selected.
* Use it to keep track of your selection as you browse categories.
@@ -57,7 +63,7 @@ Use the Applications tab to install, upgrade, uninstall, and review supported ap
</Tabs>
:::tip
If you have trouble finding an application, press `Ctrl + F` and search for its name. The list filters as you type.
If you have trouble finding an application, press `Ctrl + F` and search for its name. The list filters as you type. The search and the category chips work together, so you can search inside the categories you picked.
:::
:::note
@@ -1,21 +1,26 @@
function Find-AppsByNameOrDescription {
<#
.SYNOPSIS
Searches through the Apps on the Install Tab and hides all entries that do not match the string
Filters the Install tab entries by search text and by category
.DESCRIPTION
Filters application entries by name or description using literal string matching.
Respects collapsed category state and handles null $sync gracefully.
Search text and categories are independent filters that both have to pass. An entry is
shown when its name or description matches the search text, and when its category is in
the selected set. An empty search matches everything, and an empty category set matches
every category.
While either filter is active the matching categories are expanded, since a collapsed
category would otherwise hide the very results that were asked for. With no filter at
all the collapsed state the user set is restored.
.PARAMETER SearchString
The string to be searched for. Wildcards are treated as literal characters.
The string to search for. Wildcards are treated as literal characters.
.PARAMETER Category
When provided, only applications in this exact category are shown.
.PARAMETER Categories
The categories to show. An empty or missing array shows all of them.
.NOTES
- Uses module-scope $sync (no parameter needed; inherits from caller's scope)
- Performs literal matching (no wildcard expansion)
- Safely handles missing hashtable keys and null UI elements
- Protected by try/catch to prevent UI thread crashes
#>
@@ -24,7 +29,7 @@ function Find-AppsByNameOrDescription {
[string]$SearchString = "",
[Parameter(Mandatory = $false)]
[string]$Category = ""
[string[]]$Categories = @()
)
# Validate that $sync exists and has required structure
@@ -43,21 +48,34 @@ function Find-AppsByNameOrDescription {
return
}
# Categories that filtering expanded on the user's behalf, so clearing the filter can undo it
if ($null -eq $sync.AppCategoryAutoExpanded) {
$sync.AppCategoryAutoExpanded = @{}
}
try {
# Reset the visibility if the search string is empty or the search is cleared
if ([string]::IsNullOrWhiteSpace($SearchString) -and [string]::IsNullOrWhiteSpace($Category)) {
$activeCategories = @($Categories | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
$hasSearch = -not [string]::IsNullOrWhiteSpace($SearchString)
$hasCategories = $activeCategories.Count -gt 0
# Nothing is filtered, so put every entry back and leave the collapsed categories collapsed
if (-not $hasSearch -and -not $hasCategories) {
$sync.ItemsControl.Items | ForEach-Object {
# Each item is a StackPanel container
$_.Visibility = [Windows.Visibility]::Visible
if ($_.Children.Count -ge 2) {
$categoryLabel = $_.Children[0]
$wrapPanel = $_.Children[1]
# Keep category label visible
$categoryLabel.Visibility = [Windows.Visibility]::Visible
# Respect the collapsed state of categories (indicated by + prefix)
# A category that filtering expanded goes back to how the user left it
$categoryName = $categoryLabel.Content -replace '^[+-] ', ''
if ($sync.AppCategoryAutoExpanded.ContainsKey($categoryName)) {
$categoryLabel.Content = $categoryLabel.Content -replace "^- ", "+ "
$sync.AppCategoryAutoExpanded.Remove($categoryName)
}
if ($categoryLabel.Content -like "+*") {
$wrapPanel.Visibility = [Windows.Visibility]::Collapsed
}
@@ -65,7 +83,6 @@ function Find-AppsByNameOrDescription {
$wrapPanel.Visibility = [Windows.Visibility]::Visible
}
# Show all apps within the category
$wrapPanel.Children | ForEach-Object {
$_.Visibility = [Windows.Visibility]::Visible
}
@@ -77,7 +94,6 @@ function Find-AppsByNameOrDescription {
# Escape wildcard characters for literal matching
$escapedSearchString = [System.Management.Automation.WildcardPattern]::Escape($SearchString)
# Perform search
$sync.ItemsControl.Items | ForEach-Object {
# Each item is a StackPanel container with Children[0] = label, Children[1] = WrapPanel
if ($_.Children.Count -ge 2) {
@@ -85,12 +101,9 @@ function Find-AppsByNameOrDescription {
$wrapPanel = $_.Children[1]
$categoryHasMatch = $false
# Keep category label visible
$categoryLabel.Visibility = [Windows.Visibility]::Visible
# Search through apps in this category
foreach ($appControl in $wrapPanel.Children) {
# Safely retrieve app entry from hashtable
$appTag = $appControl.Tag
$appEntry = $null
@@ -98,14 +111,13 @@ function Find-AppsByNameOrDescription {
$appEntry = $sync.configs.applicationsHashtable[$appTag]
}
# Check if app matches search criteria
if ($null -ne $appEntry) {
$categoryMatch = -not [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Category -eq $Category
$contentMatch = [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Content -like "*$escapedSearchString*"
$descriptionMatch = [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Description -like "*$escapedSearchString*"
$categoryMatch = -not $hasCategories -or $activeCategories -contains $appEntry.Category
$textMatch = -not $hasSearch -or
$appEntry.Content -like "*$escapedSearchString*" -or
$appEntry.Description -like "*$escapedSearchString*"
if ($categoryMatch -or $contentMatch -or $descriptionMatch) {
# Show the App and mark that this category has a match
if ($categoryMatch -and $textMatch) {
$appControl.Visibility = [Windows.Visibility]::Visible
$categoryHasMatch = $true
}
@@ -119,17 +131,17 @@ function Find-AppsByNameOrDescription {
}
}
# If category has matches, show the WrapPanel and update the category label to expanded state
if ($categoryHasMatch) {
$wrapPanel.Visibility = [Windows.Visibility]::Visible
$_.Visibility = [Windows.Visibility]::Visible
# Update category label to show expanded state (-)
# Expand it, otherwise the matches stay hidden behind a collapsed header.
# Remember that it was collapsed so clearing the filter can put it back.
if ($categoryLabel.Content -like "+*") {
$categoryLabel.Content = $categoryLabel.Content -replace "^\+ ", "- "
$sync.AppCategoryAutoExpanded[($categoryLabel.Content -replace '^- ', '')] = $true
}
}
else {
# Hide the entire category container if no matches
$_.Visibility = [Windows.Visibility]::Collapsed
}
}
@@ -62,6 +62,11 @@ function Initialize-InstallCategoryAppList {
# The WrapPanel is the second child
$wrapPanel = $categoryContainer.Children[1]
# An explicit click wins over anything filtering expanded automatically
if ($sync.AppCategoryAutoExpanded) {
$sync.AppCategoryAutoExpanded.Remove(($categoryToggle.Content -replace '^[+-] ', ''))
}
# Toggle visibility
if ($wrapPanel.Visibility -eq [Windows.Visibility]::Visible) {
$wrapPanel.Visibility = [Windows.Visibility]::Collapsed
@@ -0,0 +1,20 @@
function Invoke-WinUtilAppCategoryChip {
<#
.SYNOPSIS
Handles a click on an Install tab category chip
.DESCRIPTION
The chip carries its category in Tag, so every chip shares this handler. Holding ctrl
adds the category to the current selection instead of replacing it.
.PARAMETER Chip
The chip that was clicked
#>
param(
[Parameter(Mandatory)]
$Chip
)
$ctrlDown = [bool]([System.Windows.Input.Keyboard]::Modifiers -band [System.Windows.Input.ModifierKeys]::Control)
Set-WinUtilAppCategoryFilter -Category $Chip.Tag -Additive:$ctrlDown
}
@@ -1,17 +1,50 @@
function Set-WinUtilAppCategoryFilter {
<#
.SYNOPSIS
Applies an exact application category filter from an Install tab search chip.
Applies the Install tab category filter and syncs the chip states to it
.DESCRIPTION
The selection lives in $sync.SelectedAppCategories. An empty selection means every
category is shown, which is what the All chip represents. The category filter and the
search box are independent: this only touches categories, and the current search text
is reapplied on top.
.PARAMETER Category
The application category to show. An empty value clears the filter.
The category to act on. An empty value clears the filter back to All.
.PARAMETER Additive
Toggles this category in or out of the current selection instead of replacing it.
Bound to ctrl click.
#>
param(
[Parameter(Mandatory = $false)]
[string]$Category = ""
[string]$Category = "",
[Parameter(Mandatory = $false)]
[switch]$Additive
)
$sync.SearchBar.Tag = $Category
$sync.SearchBar.Text = $Category
Find-AppsByNameOrDescription -SearchString $Category -Category $Category
if ($null -eq $sync.SelectedAppCategories) {
$sync.SelectedAppCategories = [System.Collections.Generic.List[string]]::new()
}
$selected = $sync.SelectedAppCategories
if ([string]::IsNullOrWhiteSpace($Category)) {
$selected.Clear()
} elseif ($Additive) {
if ($selected.Contains($Category)) {
[void]$selected.Remove($Category)
} else {
$selected.Add($Category)
}
} elseif ($selected.Count -eq 1 -and $selected.Contains($Category)) {
# Clicking the only active category again clears the filter
$selected.Clear()
} else {
$selected.Clear()
$selected.Add($Category)
}
Update-WinUtilAppCategoryChip
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Categories $selected.ToArray()
}
@@ -8,8 +8,14 @@ function Invoke-WinUtilInstallAppRenderBatch {
$sync.$appKey = Initialize-InstallAppEntry -TargetElement $CategoryBatch.TargetElement -AppKey $appKey
}
if ($sync.currentTab -eq "Install" -and $sync.SearchBar -and -not [string]::IsNullOrWhiteSpace($sync.SearchBar.Text)) {
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Category $sync.SearchBar.Tag
# Entries render in batches, so a filter that is already active has to be applied to each new
# batch. Categories count as an active filter just like search text does.
if ($sync.currentTab -eq "Install" -and $sync.SearchBar) {
$selectedCategories = if ($sync.SelectedAppCategories) { $sync.SelectedAppCategories.ToArray() } else { @() }
if (-not [string]::IsNullOrWhiteSpace($sync.SearchBar.Text) -or $selectedCategories.Count -gt 0) {
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Categories $selectedCategories
}
}
}
@@ -0,0 +1,19 @@
function Update-WinUtilAppCategoryChip {
<#
.SYNOPSIS
Pushes the current category selection onto the Install tab filter chips
.DESCRIPTION
The chips are toggle buttons, so their checked state has to follow the selection
rather than whatever the last click did to them. The All chip is checked when no
category is selected.
#>
$selected = $sync.SelectedAppCategories
if ($null -eq $selected) { return }
foreach ($chip in $sync.AppCategoryChips) {
$control = $sync[$chip.Name]
if ($null -eq $control) { continue }
$control.IsChecked = if ($chip.Category) { $selected.Contains($chip.Category) } else { $selected.Count -eq 0 }
}
}
+3 -2
View File
@@ -32,8 +32,9 @@ function Invoke-WPFTab {
# Always reset the filter for the current tab
if ($sync.currentTab -eq "Install") {
# Reset Install tab filter
Find-AppsByNameOrDescription -SearchString ""
# Reset the search text, but keep the categories the chips are still showing as selected
$selectedCategories = if ($sync.SelectedAppCategories) { $sync.SelectedAppCategories.ToArray() } else { @() }
Find-AppsByNameOrDescription -SearchString "" -Categories $selectedCategories
} elseif ($sync.currentTab -eq "Tweaks") {
# Reset Tweaks tab filter
Find-TweaksByNameOrDescription -SearchString ""
+3 -1
View File
@@ -21,7 +21,9 @@ Describe "Install app rendering startup contract" {
$renderScript | Should -Match 'Dispatcher\.BeginInvoke'
$renderScript | Should -Match 'Invoke-WinUtilInstallAppRenderNextBatch'
$renderScript | Should -Match 'Initialize-InstallAppEntry'
$renderScript | Should -Match 'Find-AppsByNameOrDescription -SearchString \$sync\.SearchBar\.Text -Category \$sync\.SearchBar\.Tag'
$renderScript | Should -Match 'Find-AppsByNameOrDescription -SearchString \$sync\.SearchBar\.Text -Categories \$selectedCategories'
# A batch has to be filtered when either filter is on, not only when there is search text
$renderScript | Should -Match '\$selectedCategories\.Count -gt 0'
$renderScript | Should -Match '\$sync\.InstallAppEntriesRendered = \$true'
}
+75 -1
View File
@@ -379,12 +379,86 @@ Describe "Find-AppsByNameOrDescription" {
$category = New-WinUtilAppCategory -Label "- Tools" -Items @($utilityItem, $powerToysItem)
New-WinUtilAppSearchContext -Categories @($category)
Find-AppsByNameOrDescription -SearchString "Utilities" -Category "Utilities"
Find-AppsByNameOrDescription -Categories @("Utilities")
$utilityItem.Visibility | Should -Be ([Windows.Visibility]::Visible)
$powerToysItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
$category.Visibility | Should -Be ([Windows.Visibility]::Visible)
}
It "shows apps from every selected category when several chips are active" {
$utilityItem = New-WinUtilAppSearchItem -Tag "WPFInstallLiteral"
$powerToysItem = New-WinUtilAppSearchItem -Tag "WPFInstallPowerToys"
$browserItem = New-WinUtilAppSearchItem -Tag "WPFInstallBrowser"
$category = New-WinUtilAppCategory -Label "- Tools" -Items @($utilityItem, $powerToysItem, $browserItem)
New-WinUtilAppSearchContext -Categories @($category)
Find-AppsByNameOrDescription -Categories @("Utilities", "Microsoft Tools")
$utilityItem.Visibility | Should -Be ([Windows.Visibility]::Visible)
$powerToysItem.Visibility | Should -Be ([Windows.Visibility]::Visible)
$browserItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
}
It "applies the search text and the category filter together" {
$powerToysItem = New-WinUtilAppSearchItem -Tag "WPFInstallPowerToys"
$literalItem = New-WinUtilAppSearchItem -Tag "WPFInstallLiteral"
$category = New-WinUtilAppCategory -Label "- Tools" -Items @($powerToysItem, $literalItem)
New-WinUtilAppSearchContext -Categories @($category)
Find-AppsByNameOrDescription -SearchString "PowerToys" -Categories @("Microsoft Tools")
$powerToysItem.Visibility | Should -Be ([Windows.Visibility]::Visible)
$literalItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
}
It "hides a category when the search text matches nothing inside the selected categories" {
$powerToysItem = New-WinUtilAppSearchItem -Tag "WPFInstallPowerToys"
$category = New-WinUtilAppCategory -Label "- Tools" -Items @($powerToysItem)
New-WinUtilAppSearchContext -Categories @($category)
Find-AppsByNameOrDescription -SearchString "Firefox" -Categories @("Microsoft Tools")
$powerToysItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
$category.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
}
It "expands a collapsed category that has matches for the selected filter" {
$powerToysItem = New-WinUtilAppSearchItem -Tag "WPFInstallPowerToys"
$category = New-WinUtilAppCategory -Label "+ Tools" -Items @($powerToysItem)
New-WinUtilAppSearchContext -Categories @($category)
Find-AppsByNameOrDescription -Categories @("Microsoft Tools")
$category.Children[0].Content | Should -Be "- Tools"
$category.Children[1].Visibility | Should -Be ([Windows.Visibility]::Visible)
}
It "re-collapses a category it expanded once the filter is cleared" {
$powerToysItem = New-WinUtilAppSearchItem -Tag "WPFInstallPowerToys"
$category = New-WinUtilAppCategory -Label "+ Tools" -Items @($powerToysItem)
New-WinUtilAppSearchContext -Categories @($category)
Find-AppsByNameOrDescription -Categories @("Microsoft Tools")
$category.Children[0].Content | Should -Be "- Tools"
Find-AppsByNameOrDescription -SearchString ""
$category.Children[0].Content | Should -Be "+ Tools"
$category.Children[1].Visibility | Should -Be ([Windows.Visibility]::Collapsed)
}
It "leaves a category the user had expanded alone when the filter is cleared" {
$powerToysItem = New-WinUtilAppSearchItem -Tag "WPFInstallPowerToys"
$category = New-WinUtilAppCategory -Label "- Tools" -Items @($powerToysItem)
New-WinUtilAppSearchContext -Categories @($category)
Find-AppsByNameOrDescription -Categories @("Microsoft Tools")
Find-AppsByNameOrDescription -SearchString ""
$category.Children[0].Content | Should -Be "- Tools"
$category.Children[1].Visibility | Should -Be ([Windows.Visibility]::Visible)
}
}
Describe "Find-TweaksByNameOrDescription" {
+6 -2
View File
@@ -182,7 +182,8 @@ Describe "XAML document" {
It "wires the Document search chip to an existing Document category" {
$mainScript = Get-Content -Path $script:mainScriptPath -Raw
$mainScript | Should -Match '\$sync\["WPFSearchChipDocument"\]\.Add_Click\(\{ Set-WinUtilAppCategoryFilter -Category "Document" \}\)'
$mainScript | Should -Match '@\{ Name = "WPFSearchChipDocument";\s+Category = "Document" \}'
$mainScript | Should -Match '\$sync\["WPFSearchChipDocument"\]\.Add_Click\(\{ Invoke-WinUtilAppCategoryChip -Chip \$this \}\)'
$applications = Get-WinUtilConfigObject -Name "applications"
$categories = @($applications.PSObject.Properties | ForEach-Object { $_.Value.category } | Sort-Object -Unique)
@@ -468,7 +469,10 @@ Describe "XAML and sync wiring" {
"Win11ISOProcessRunning",
"Win11ISOWorkDir",
"Win11ISOContentsDir",
"Win11ISOUSBDisks"
"Win11ISOUSBDisks",
"AppCategoryChips",
"SelectedAppCategories",
"AppCategoryAutoExpanded"
)
$allowedNames = @($xamlNames + $generatedNames + $dynamicStateNames) | Sort-Object -Unique
$bracketReferences = @(
+32 -22
View File
@@ -329,7 +329,7 @@ $searchBarTimer.add_Tick({
$searchBarTimer.Stop()
switch ($sync.currentTab) {
"Install" {
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Category $sync.SearchBar.Tag
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Categories $sync.SelectedAppCategories.ToArray()
}
"Tweaks" {
Find-TweaksByNameOrDescription -SearchString $sync.SearchBar.Text
@@ -340,10 +340,6 @@ $searchBarTimer.add_Tick({
}
})
$sync["SearchBar"].Add_TextChanged({
if ($sync.SearchBar.Tag -ne $sync.SearchBar.Text) {
$sync.SearchBar.Tag = $null
}
if ($sync.SearchBar.Text -ne "") {
$sync.SearchBarClearButton.Visibility = "Visible"
$sync.SearchBarIcon.Visibility = "Collapsed"
@@ -352,29 +348,43 @@ $sync["SearchBar"].Add_TextChanged({
$sync.SearchBarIcon.Visibility = "Visible"
}
# Category chip handlers apply their filter immediately.
if ($sync.SearchBar.Tag -eq $sync.SearchBar.Text) {
return
}
if ($searchBarTimer.IsEnabled) {
$searchBarTimer.Stop()
}
$searchBarTimer.Start()
})
# Quick Category Search Chips
$sync["WPFSearchChipAll"].Add_Click({ Set-WinUtilAppCategoryFilter })
$sync["WPFSearchChipBrowsers"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Browsers" })
$sync["WPFSearchChipCommunications"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Communications" })
$sync["WPFSearchChipDevelopment"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Development" })
$sync["WPFSearchChipDocument"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Document" })
$sync["WPFSearchChipGames"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Games" })
$sync["WPFSearchChipMicrosoftTools"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Microsoft Tools" })
$sync["WPFSearchChipMultimediaTools"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Multimedia Tools" })
$sync["WPFSearchChipProTools"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Pro Tools" })
$sync["WPFSearchChipSelfhostedTools"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Selfhosted Tools" })
$sync["WPFSearchChipUtilities"].Add_Click({ Set-WinUtilAppCategoryFilter -Category "Utilities" })
# Category filter chips. The chip carries its category in Tag, so one handler covers all of them.
$sync.AppCategoryChips = @(
@{ Name = "WPFSearchChipAll"; Category = "" }
@{ Name = "WPFSearchChipBrowsers"; Category = "Browsers" }
@{ Name = "WPFSearchChipCommunications"; Category = "Communications" }
@{ Name = "WPFSearchChipDevelopment"; Category = "Development" }
@{ Name = "WPFSearchChipDocument"; Category = "Document" }
@{ Name = "WPFSearchChipGames"; Category = "Games" }
@{ Name = "WPFSearchChipMicrosoftTools"; Category = "Microsoft Tools" }
@{ Name = "WPFSearchChipMultimediaTools"; Category = "Multimedia Tools" }
@{ Name = "WPFSearchChipProTools"; Category = "Pro Tools" }
@{ Name = "WPFSearchChipSelfhostedTools"; Category = "Selfhosted Tools" }
@{ Name = "WPFSearchChipUtilities"; Category = "Utilities" }
)
$sync.SelectedAppCategories = [System.Collections.Generic.List[string]]::new()
foreach ($appCategoryChip in $sync.AppCategoryChips) {
$sync[$appCategoryChip.Name].Tag = $appCategoryChip.Category
}
$sync["WPFSearchChipAll"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipBrowsers"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipCommunications"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipDevelopment"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipDocument"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipGames"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipMicrosoftTools"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipMultimediaTools"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipProTools"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipSelfhostedTools"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["WPFSearchChipUtilities"].Add_Click({ Invoke-WinUtilAppCategoryChip -Chip $this })
$sync["Form"].Add_Loaded({
param($e)
+55 -16
View File
@@ -997,6 +997,44 @@
</Setter.Value>
</Setter>
</Style>
<!-- Category filter chips. A toggle rather than a button, so the active filter is visible
on the chip itself instead of only in the results below. -->
<Style x:Key="FilterChipToggleStyle" TargetType="ToggleButton">
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="12,4,12,4"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FontSize" Value="{DynamicResource ButtonFontSize}"/>
<Setter Property="FontFamily" Value="{DynamicResource ButtonFontFamily}"/>
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundColor}"/>
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundColor}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Name="ChipBorder"
Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource BorderColor}"
BorderThickness="1"
CornerRadius="{DynamicResource ButtonCornerRadius}"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
TextBlock.Foreground="{TemplateBinding Foreground}"
TextBlock.FontSize="{TemplateBinding FontSize}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ChipBorder" Property="Background" Value="{DynamicResource ButtonBackgroundMouseoverColor}"/>
</Trigger>
<!-- Only colours change on check. Anything affecting text width, bold for
instance, would resize the chip and shift every chip after it. -->
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="ChipBorder" Property="Background" Value="{DynamicResource ButtonBackgroundSelectedColor}"/>
<Setter TargetName="ChipBorder" Property="BorderBrush" Value="{DynamicResource LabelboxForegroundColor}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Background="{DynamicResource MainBackgroundColor}" ShowGridLines="False" Name="WPFMainGrid" Width="Auto" Height="Auto" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
@@ -1313,26 +1351,27 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Quick Category Search Chips -->
<!-- Category filters. Click one to filter, ctrl click to combine several. -->
<WrapPanel Grid.Row="0" Orientation="Horizontal" Margin="5,5,5,5" Name="WPFSearchChips">
<TextBlock Text="Filters"
FontSize="{DynamicResource HeaderFontSize}"
FontFamily="{DynamicResource HeaderFontFamily}"
<TextBlock Text="&#xE71C;"
FontFamily="Segoe MDL2 Assets"
FontSize="{DynamicResource IconFontSize}"
Foreground="{DynamicResource LabelboxForegroundColor}"
Background="Transparent"
VerticalAlignment="Center"
Margin="15,0,8,0"/>
<Button Name="WPFSearchChipAll" Content="All" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipBrowsers" Content="Browsers" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipCommunications" Content="Communications" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipDevelopment" Content="Development" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipDocument" Content="Document" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipGames" Content="Games" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipMicrosoftTools" Content="Microsoft Tools" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipMultimediaTools" Content="Multimedia Tools" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipProTools" Content="Pro Tools" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipSelfhostedTools" Content="Selfhosted Tools" Style="{StaticResource FilterChipStyle}"/>
<Button Name="WPFSearchChipUtilities" Content="Utilities" Style="{StaticResource FilterChipStyle}"/>
Margin="10,0,10,0"
ToolTip="Filter by category. Ctrl click to select more than one."/>
<ToggleButton Name="WPFSearchChipAll" Content="All" Style="{StaticResource FilterChipToggleStyle}" IsChecked="True"/>
<ToggleButton Name="WPFSearchChipBrowsers" Content="Browsers" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipCommunications" Content="Communications" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipDevelopment" Content="Development" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipDocument" Content="Document" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipGames" Content="Games" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipMicrosoftTools" Content="Microsoft Tools" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipMultimediaTools" Content="Multimedia Tools" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipProTools" Content="Pro Tools" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipSelfhostedTools" Content="Selfhosted Tools" Style="{StaticResource FilterChipToggleStyle}"/>
<ToggleButton Name="WPFSearchChipUtilities" Content="Utilities" Style="{StaticResource FilterChipToggleStyle}"/>
</WrapPanel>
<Grid Grid.Row="1" Margin="{DynamicResource TabContentMargin}">