mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2026-08-09 09:31:15 +10:00
Add category search chips to Install tab for improved filtering (#4826)
* Add Quick Category Search Chips to Install tab Add category shortcut chips on the Install tab that insert the category name into the existing search box. Append each app's category as a description tag so the existing description search filters by category. Reuses the standard button style and existing search; no search, install, or preset logic is changed. * Fix install category chip filtering * Fix category filter refresh behavior --------- Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
@@ -10,6 +10,9 @@ function Find-AppsByNameOrDescription {
|
|||||||
.PARAMETER SearchString
|
.PARAMETER SearchString
|
||||||
The string to be searched for. Wildcards are treated as literal characters.
|
The string to be searched for. Wildcards are treated as literal characters.
|
||||||
|
|
||||||
|
.PARAMETER Category
|
||||||
|
When provided, only applications in this exact category are shown.
|
||||||
|
|
||||||
.NOTES
|
.NOTES
|
||||||
- Uses module-scope $sync (no parameter needed; inherits from caller's scope)
|
- Uses module-scope $sync (no parameter needed; inherits from caller's scope)
|
||||||
- Performs literal matching (no wildcard expansion)
|
- Performs literal matching (no wildcard expansion)
|
||||||
@@ -18,7 +21,10 @@ function Find-AppsByNameOrDescription {
|
|||||||
#>
|
#>
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $false)]
|
[Parameter(Mandatory = $false)]
|
||||||
[string]$SearchString = ""
|
[string]$SearchString = "",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Category = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
# Validate that $sync exists and has required structure
|
# Validate that $sync exists and has required structure
|
||||||
@@ -39,7 +45,7 @@ function Find-AppsByNameOrDescription {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
# Reset the visibility if the search string is empty or the search is cleared
|
# Reset the visibility if the search string is empty or the search is cleared
|
||||||
if ([string]::IsNullOrWhiteSpace($SearchString)) {
|
if ([string]::IsNullOrWhiteSpace($SearchString) -and [string]::IsNullOrWhiteSpace($Category)) {
|
||||||
$sync.ItemsControl.Items | ForEach-Object {
|
$sync.ItemsControl.Items | ForEach-Object {
|
||||||
# Each item is a StackPanel container
|
# Each item is a StackPanel container
|
||||||
$_.Visibility = [Windows.Visibility]::Visible
|
$_.Visibility = [Windows.Visibility]::Visible
|
||||||
@@ -94,10 +100,11 @@ function Find-AppsByNameOrDescription {
|
|||||||
|
|
||||||
# Check if app matches search criteria
|
# Check if app matches search criteria
|
||||||
if ($null -ne $appEntry) {
|
if ($null -ne $appEntry) {
|
||||||
$contentMatch = $appEntry.Content -like "*$escapedSearchString*"
|
$categoryMatch = -not [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Category -eq $Category
|
||||||
$descriptionMatch = $appEntry.Description -like "*$escapedSearchString*"
|
$contentMatch = [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Content -like "*$escapedSearchString*"
|
||||||
|
$descriptionMatch = [string]::IsNullOrWhiteSpace($Category) -and $appEntry.Description -like "*$escapedSearchString*"
|
||||||
|
|
||||||
if ($contentMatch -or $descriptionMatch) {
|
if ($categoryMatch -or $contentMatch -or $descriptionMatch) {
|
||||||
# Show the App and mark that this category has a match
|
# Show the App and mark that this category has a match
|
||||||
$appControl.Visibility = [Windows.Visibility]::Visible
|
$appControl.Visibility = [Windows.Visibility]::Visible
|
||||||
$categoryHasMatch = $true
|
$categoryHasMatch = $true
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
function Set-WinUtilAppCategoryFilter {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Applies an exact application category filter from an Install tab search chip.
|
||||||
|
|
||||||
|
.PARAMETER Category
|
||||||
|
The application category to show. An empty value clears the filter.
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Category = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
$sync.SearchBar.Tag = $Category
|
||||||
|
$sync.SearchBar.Text = $Category
|
||||||
|
Find-AppsByNameOrDescription -SearchString $Category -Category $Category
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ function Invoke-WinUtilInstallAppRenderBatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($sync.currentTab -eq "Install" -and $sync.SearchBar -and -not [string]::IsNullOrWhiteSpace($sync.SearchBar.Text)) {
|
if ($sync.currentTab -eq "Install" -and $sync.SearchBar -and -not [string]::IsNullOrWhiteSpace($sync.SearchBar.Text)) {
|
||||||
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text
|
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Category $sync.SearchBar.Tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ Describe "Install app rendering startup contract" {
|
|||||||
$renderScript | Should -Match 'Dispatcher\.BeginInvoke'
|
$renderScript | Should -Match 'Dispatcher\.BeginInvoke'
|
||||||
$renderScript | Should -Match 'Invoke-WinUtilInstallAppRenderNextBatch'
|
$renderScript | Should -Match 'Invoke-WinUtilInstallAppRenderNextBatch'
|
||||||
$renderScript | Should -Match 'Initialize-InstallAppEntry'
|
$renderScript | Should -Match 'Initialize-InstallAppEntry'
|
||||||
|
$renderScript | Should -Match 'Find-AppsByNameOrDescription -SearchString \$sync\.SearchBar\.Text -Category \$sync\.SearchBar\.Tag'
|
||||||
$renderScript | Should -Match '\$sync\.InstallAppEntriesRendered = \$true'
|
$renderScript | Should -Match '\$sync\.InstallAppEntriesRendered = \$true'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ Describe "Install app rendering startup contract" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function global:Find-AppsByNameOrDescription {
|
function global:Find-AppsByNameOrDescription {
|
||||||
param($SearchString)
|
param($SearchString, $Category)
|
||||||
throw "Search should not run for an empty search box in this test."
|
throw "Search should not run for an empty search box in this test."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,18 +172,27 @@ namespace Windows.Controls
|
|||||||
WPFInstallBrowser = [pscustomobject]@{
|
WPFInstallBrowser = [pscustomobject]@{
|
||||||
Content = "Firefox"
|
Content = "Firefox"
|
||||||
Description = "Fast private browser"
|
Description = "Fast private browser"
|
||||||
|
Category = "Browsers"
|
||||||
}
|
}
|
||||||
WPFInstallMedia = [pscustomobject]@{
|
WPFInstallMedia = [pscustomobject]@{
|
||||||
Content = "VLC"
|
Content = "VLC"
|
||||||
Description = "Media player"
|
Description = "Media player"
|
||||||
|
Category = "Multimedia Tools"
|
||||||
}
|
}
|
||||||
WPFInstallLiteral = [pscustomobject]@{
|
WPFInstallLiteral = [pscustomobject]@{
|
||||||
Content = "Tool [abc]"
|
Content = "Tool [abc]"
|
||||||
Description = "Literal wildcard sample"
|
Description = "Literal wildcard sample"
|
||||||
|
Category = "Utilities"
|
||||||
}
|
}
|
||||||
WPFInstallEditor = [pscustomobject]@{
|
WPFInstallEditor = [pscustomobject]@{
|
||||||
Content = "Code Editor"
|
Content = "Code Editor"
|
||||||
Description = "Text editing"
|
Description = "Text editing"
|
||||||
|
Category = "Development"
|
||||||
|
}
|
||||||
|
WPFInstallPowerToys = [pscustomobject]@{
|
||||||
|
Content = "PowerToys"
|
||||||
|
Description = "A collection of system utilities"
|
||||||
|
Category = "Microsoft Tools"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,6 +372,19 @@ Describe "Find-AppsByNameOrDescription" {
|
|||||||
$mediaItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
|
$mediaItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
|
||||||
$category.Visibility | Should -Be ([Windows.Visibility]::Visible)
|
$category.Visibility | Should -Be ([Windows.Visibility]::Visible)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It "filters category chips by exact application category" {
|
||||||
|
$utilityItem = New-WinUtilAppSearchItem -Tag "WPFInstallLiteral"
|
||||||
|
$powerToysItem = New-WinUtilAppSearchItem -Tag "WPFInstallPowerToys"
|
||||||
|
$category = New-WinUtilAppCategory -Label "- Tools" -Items @($utilityItem, $powerToysItem)
|
||||||
|
New-WinUtilAppSearchContext -Categories @($category)
|
||||||
|
|
||||||
|
Find-AppsByNameOrDescription -SearchString "Utilities" -Category "Utilities"
|
||||||
|
|
||||||
|
$utilityItem.Visibility | Should -Be ([Windows.Visibility]::Visible)
|
||||||
|
$powerToysItem.Visibility | Should -Be ([Windows.Visibility]::Collapsed)
|
||||||
|
$category.Visibility | Should -Be ([Windows.Visibility]::Visible)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Describe "Find-TweaksByNameOrDescription" {
|
Describe "Find-TweaksByNameOrDescription" {
|
||||||
|
|||||||
@@ -137,6 +137,17 @@ Describe "XAML document" {
|
|||||||
"SearchBar",
|
"SearchBar",
|
||||||
"SearchBarIcon",
|
"SearchBarIcon",
|
||||||
"SearchBarClearButton",
|
"SearchBarClearButton",
|
||||||
|
"WPFSearchChips",
|
||||||
|
"WPFSearchChipAll",
|
||||||
|
"WPFSearchChipBrowsers",
|
||||||
|
"WPFSearchChipCommunications",
|
||||||
|
"WPFSearchChipDevelopment",
|
||||||
|
"WPFSearchChipGames",
|
||||||
|
"WPFSearchChipMicrosoftTools",
|
||||||
|
"WPFSearchChipMultimediaTools",
|
||||||
|
"WPFSearchChipProTools",
|
||||||
|
"WPFSearchChipSelfhostedTools",
|
||||||
|
"WPFSearchChipUtilities",
|
||||||
"appscategory",
|
"appscategory",
|
||||||
"appspanel",
|
"appspanel",
|
||||||
"tweakspanel",
|
"tweakspanel",
|
||||||
|
|||||||
+23
-1
@@ -341,7 +341,7 @@ $searchBarTimer.add_Tick({
|
|||||||
$searchBarTimer.Stop()
|
$searchBarTimer.Stop()
|
||||||
switch ($sync.currentTab) {
|
switch ($sync.currentTab) {
|
||||||
"Install" {
|
"Install" {
|
||||||
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text
|
Find-AppsByNameOrDescription -SearchString $sync.SearchBar.Text -Category $sync.SearchBar.Tag
|
||||||
}
|
}
|
||||||
"Tweaks" {
|
"Tweaks" {
|
||||||
Find-TweaksByNameOrDescription -SearchString $sync.SearchBar.Text
|
Find-TweaksByNameOrDescription -SearchString $sync.SearchBar.Text
|
||||||
@@ -352,6 +352,10 @@ $searchBarTimer.add_Tick({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
$sync["SearchBar"].Add_TextChanged({
|
$sync["SearchBar"].Add_TextChanged({
|
||||||
|
if ($sync.SearchBar.Tag -ne $sync.SearchBar.Text) {
|
||||||
|
$sync.SearchBar.Tag = $null
|
||||||
|
}
|
||||||
|
|
||||||
if ($sync.SearchBar.Text -ne "") {
|
if ($sync.SearchBar.Text -ne "") {
|
||||||
$sync.SearchBarClearButton.Visibility = "Visible"
|
$sync.SearchBarClearButton.Visibility = "Visible"
|
||||||
$sync.SearchBarIcon.Visibility = "Collapsed"
|
$sync.SearchBarIcon.Visibility = "Collapsed"
|
||||||
@@ -359,12 +363,30 @@ $sync["SearchBar"].Add_TextChanged({
|
|||||||
$sync.SearchBarClearButton.Visibility = "Collapsed"
|
$sync.SearchBarClearButton.Visibility = "Collapsed"
|
||||||
$sync.SearchBarIcon.Visibility = "Visible"
|
$sync.SearchBarIcon.Visibility = "Visible"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Category chip handlers apply their filter immediately.
|
||||||
|
if ($sync.SearchBar.Tag -eq $sync.SearchBar.Text) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if ($searchBarTimer.IsEnabled) {
|
if ($searchBarTimer.IsEnabled) {
|
||||||
$searchBarTimer.Stop()
|
$searchBarTimer.Stop()
|
||||||
}
|
}
|
||||||
$searchBarTimer.Start()
|
$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["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" })
|
||||||
|
|
||||||
$sync["Form"].Add_Loaded({
|
$sync["Form"].Add_Loaded({
|
||||||
param($e)
|
param($e)
|
||||||
$null = $e
|
$null = $e
|
||||||
|
|||||||
+19
-1
@@ -1275,8 +1275,26 @@
|
|||||||
<TabControl Name="WPFTabNav" Background="Transparent" Width="Auto" Height="Auto" BorderBrush="Transparent" BorderThickness="0" Grid.Row="2" Grid.Column="0" Padding="-1">
|
<TabControl Name="WPFTabNav" Background="Transparent" Width="Auto" Height="Auto" BorderBrush="Transparent" BorderThickness="0" Grid.Row="2" Grid.Column="0" Padding="-1">
|
||||||
<TabItem Header="Install" Visibility="Collapsed" Name="WPFTab1">
|
<TabItem Header="Install" Visibility="Collapsed" Name="WPFTab1">
|
||||||
<Grid Background="Transparent" >
|
<Grid Background="Transparent" >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Grid Grid.Row="0" Grid.Column="0" Margin="{DynamicResource TabContentMargin}">
|
<!-- Quick Category Search Chips -->
|
||||||
|
<WrapPanel Grid.Row="0" Orientation="Horizontal" Margin="5,5,5,5" Name="WPFSearchChips">
|
||||||
|
<Button Name="WPFSearchChipAll" Content="All" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipBrowsers" Content="Browsers" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipCommunications" Content="Communications" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipDevelopment" Content="Development" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipGames" Content="Games" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipMicrosoftTools" Content="Microsoft Tools" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipMultimediaTools" Content="Multimedia Tools" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipProTools" Content="Pro Tools" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipSelfhostedTools" Content="Selfhosted Tools" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
<Button Name="WPFSearchChipUtilities" Content="Utilities" Width="Auto" Height="Auto" Margin="2"/>
|
||||||
|
</WrapPanel>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Margin="{DynamicResource TabContentMargin}">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
|
|||||||
Reference in New Issue
Block a user