Compare commits

...

4 Commits

Author SHA1 Message Date
Chris Titus Tech
b7d15569cd Fix search 2026-02-05 15:59:04 -06:00
Chris Titus Tech
53f837f1e9 finish new install GUI 2026-02-05 15:50:15 -06:00
Chris Titus Tech
17f92df25a Make collapsable categories 2026-02-05 15:03:36 -06:00
Chris Titus Tech
0b78970dc7 cleanup and checkbox addition 2026-02-05 13:52:27 -06:00
15 changed files with 223 additions and 66 deletions

View File

@@ -38,25 +38,39 @@
"Order": "2", "Order": "2",
"Description": "Use Chocolatey for package management" "Description": "Use Chocolatey for package management"
}, },
"WPFCollapseAllCategories": {
"Content": "Collapse All Categories",
"Category": "__Selection",
"Type": "Button",
"Order": "1",
"Description": "Collapse all application categories"
},
"WPFExpandAllCategories": {
"Content": "Expand All Categories",
"Category": "__Selection",
"Type": "Button",
"Order": "2",
"Description": "Expand all application categories"
},
"WPFClearInstallSelection": { "WPFClearInstallSelection": {
"Content": "Clear Selection", "Content": "Clear Selection",
"Category": "__Selection", "Category": "__Selection",
"Type": "Button", "Type": "Button",
"Order": "1", "Order": "3",
"Description": "Clear the selection of applications" "Description": "Clear the selection of applications"
}, },
"WPFGetInstalled": { "WPFGetInstalled": {
"Content": "Get Installed", "Content": "Get Installed",
"Category": "__Selection", "Category": "__Selection",
"Type": "Button", "Type": "Button",
"Order": "2", "Order": "4",
"Description": "Show installed applications" "Description": "Show installed applications"
}, },
"WPFselectedAppsButton": { "WPFselectedAppsButton": {
"Content": "Selected Apps: 0", "Content": "Selected Apps: 0",
"Category": "__Selection", "Category": "__Selection",
"Type": "Button", "Type": "Button",
"Order": "3", "Order": "5",
"Description": "Show the selected applications" "Description": "Show the selected applications"
} }
} }

View File

@@ -1,8 +1,8 @@
{ {
"shared":{ "shared":{
"AppEntryWidth": "130", "AppEntryWidth": "200",
"AppEntryFontSize": "11", "AppEntryFontSize": "11",
"AppEntryMargin": "1,1,1,1", "AppEntryMargin": "1,0,1,0",
"AppEntryBorderThickness": "0", "AppEntryBorderThickness": "0",
"CustomDialogFontSize": "12", "CustomDialogFontSize": "12",
"CustomDialogFontSizeHeader": "14", "CustomDialogFontSizeHeader": "14",
@@ -91,7 +91,7 @@
"AppInstallOverlayBackgroundColor":"#2E3135", "AppInstallOverlayBackgroundColor":"#2E3135",
"ComboBoxForegroundColor": "#F7F7F7", "ComboBoxForegroundColor": "#F7F7F7",
"ComboBoxBackgroundColor": "#1E3747", "ComboBoxBackgroundColor": "#1E3747",
"LabelboxForegroundColor": "#0567ff", "LabelboxForegroundColor": "#5bdcff",
"MainForegroundColor": "#F7F7F7", "MainForegroundColor": "#F7F7F7",
"MainBackgroundColor": "#232629", "MainBackgroundColor": "#232629",
"LabelBackgroundColor": "#232629", "LabelBackgroundColor": "#232629",

View File

@@ -13,37 +13,68 @@ function Find-AppsByNameOrDescription {
# 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)) {
$sync.ItemsControl.Items | ForEach-Object { $sync.ItemsControl.Items | ForEach-Object {
# Each item is a StackPanel container
$_.Visibility = [Windows.Visibility]::Visible $_.Visibility = [Windows.Visibility]::Visible
$_.Children | ForEach-Object {
if ($null -ne $_) { if ($_.Children.Count -ge 2) {
$_.Visibility = [Windows.Visibility]::Visible $categoryLabel = $_.Children[0]
$wrapPanel = $_.Children[1]
# Keep category label visible
$categoryLabel.Visibility = [Windows.Visibility]::Visible
# Respect the collapsed state of categories (indicated by + prefix)
if ($categoryLabel.Content -like "+*") {
$wrapPanel.Visibility = [Windows.Visibility]::Collapsed
} else {
$wrapPanel.Visibility = [Windows.Visibility]::Visible
} }
# Show all apps within the category
$wrapPanel.Children | ForEach-Object {
$_.Visibility = [Windows.Visibility]::Visible
}
} }
} }
return return
} }
# Perform search
$sync.ItemsControl.Items | ForEach-Object { $sync.ItemsControl.Items | ForEach-Object {
# Ensure ToggleButtons remain visible # Each item is a StackPanel container with Children[0] = label, Children[1] = WrapPanel
if ($_.Tag -like "CategoryToggleButton") { if ($_.Children.Count -ge 2) {
$_.Visibility = [Windows.Visibility]::Visible $categoryLabel = $_.Children[0]
return $wrapPanel = $_.Children[1]
} $categoryHasMatch = $false
# Hide all CategoryWrapPanel and ToggleButton
$_.Visibility = [Windows.Visibility]::Collapsed # Keep category label visible
if ($_.Tag -like "CategoryWrapPanel_*") { $categoryLabel.Visibility = [Windows.Visibility]::Visible
# Search for Apps that match the search string
$_.Children | Foreach-Object { # Search through apps in this category
$wrapPanel.Children | ForEach-Object {
$appEntry = $sync.configs.applicationsHashtable.$($_.Tag) $appEntry = $sync.configs.applicationsHashtable.$($_.Tag)
if ($appEntry.Content -like "*$SearchString*" -or $appEntry.Description -like "*$SearchString*") { if ($appEntry.Content -like "*$SearchString*" -or $appEntry.Description -like "*$SearchString*") {
# Show the App and the parent CategoryWrapPanel if the string is found # Show the App and mark that this category has a match
$_.Visibility = [Windows.Visibility]::Visible $_.Visibility = [Windows.Visibility]::Visible
$_.parent.Visibility = [Windows.Visibility]::Visible $categoryHasMatch = $true
} }
else { else {
$_.Visibility = [Windows.Visibility]::Collapsed $_.Visibility = [Windows.Visibility]::Collapsed
} }
} }
# 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 (-)
if ($categoryLabel.Content -like "+*") {
$categoryLabel.Content = $categoryLabel.Content -replace "^\+ ", "- "
}
} else {
# Hide the entire category container if no matches
$_.Visibility = [Windows.Visibility]::Collapsed
}
} }
} }
} }

View File

@@ -41,13 +41,13 @@
$itemsControl.VerticalAlignment = 'Stretch' $itemsControl.VerticalAlignment = 'Stretch'
$scrollViewer.Content = $itemsControl $scrollViewer.Content = $itemsControl
# Enable virtualization for the ItemsControl to improve performance (It's hard to test if this is actually working, so if you know what you're doing, please check this) # Use WrapPanel to create dynamic columns based on AppEntryWidth and window width
$itemsPanelTemplate = New-Object Windows.Controls.ItemsPanelTemplate $itemsPanelTemplate = New-Object Windows.Controls.ItemsPanelTemplate
$factory = New-Object Windows.FrameworkElementFactory ([Windows.Controls.VirtualizingStackPanel]) $factory = New-Object Windows.FrameworkElementFactory ([Windows.Controls.WrapPanel])
$factory.SetValue([Windows.Controls.WrapPanel]::OrientationProperty, [Windows.Controls.Orientation]::Horizontal)
$factory.SetValue([Windows.Controls.WrapPanel]::HorizontalAlignmentProperty, [Windows.HorizontalAlignment]::Left)
$itemsPanelTemplate.VisualTree = $factory $itemsPanelTemplate.VisualTree = $factory
$itemsControl.ItemsPanel = $itemsPanelTemplate $itemsControl.ItemsPanel = $itemsPanelTemplate
$itemsControl.SetValue([Windows.Controls.VirtualizingStackPanel]::IsVirtualizingProperty, $true)
$itemsControl.SetValue([Windows.Controls.VirtualizingStackPanel]::VirtualizationModeProperty, [Windows.Controls.VirtualizationMode]::Recycling)
# Add the Border containing the App Area to the target Grid # Add the Border containing the App Area to the target Grid
$targetGrid.Children.Add($Border) | Out-Null $targetGrid.Children.Add($Border) | Out-Null

View File

@@ -15,22 +15,6 @@ function Initialize-InstallCategoryAppList {
$TargetElement, $TargetElement,
$Apps $Apps
) )
function Add-Category {
param(
[string]$Category,
[Windows.Controls.ItemsControl]$TargetElement
)
$toggleButton = New-Object Windows.Controls.Label
$toggleButton.Content = "$Category"
$toggleButton.Tag = "CategoryToggleButton"
$toggleButton.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "HeaderFontSize")
$toggleButton.SetResourceReference([Windows.Controls.Control]::FontFamilyProperty, "HeaderFontFamily")
$sync.$Category = $toggleButton
$null = $TargetElement.Items.Add($toggleButton)
}
# Pre-group apps by category # Pre-group apps by category
$appsByCategory = @{} $appsByCategory = @{}
@@ -42,17 +26,71 @@ function Initialize-InstallCategoryAppList {
$appsByCategory[$category] += $appKey $appsByCategory[$category] += $appKey
} }
foreach ($category in $($appsByCategory.Keys | Sort-Object)) { foreach ($category in $($appsByCategory.Keys | Sort-Object)) {
Add-Category -Category $category -TargetElement $TargetElement # Create a container for category label + apps
$categoryContainer = New-Object Windows.Controls.StackPanel
$categoryContainer.Orientation = "Vertical"
$categoryContainer.Margin = New-Object Windows.Thickness(0, 0, 0, 0)
$categoryContainer.HorizontalAlignment = [Windows.HorizontalAlignment]::Stretch
# Bind Width to the ItemsControl's ActualWidth to force full-row layout in WrapPanel
$binding = New-Object Windows.Data.Binding
$binding.Path = New-Object Windows.PropertyPath("ActualWidth")
$binding.RelativeSource = New-Object Windows.Data.RelativeSource([Windows.Data.RelativeSourceMode]::FindAncestor, [Windows.Controls.ItemsControl], 1)
[void][Windows.Data.BindingOperations]::SetBinding($categoryContainer, [Windows.FrameworkElement]::WidthProperty, $binding)
# Add category label to container
$toggleButton = New-Object Windows.Controls.Label
$toggleButton.Content = "- $Category"
$toggleButton.Tag = "CategoryToggleButton"
$toggleButton.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "HeaderFontSize")
$toggleButton.SetResourceReference([Windows.Controls.Control]::FontFamilyProperty, "HeaderFontFamily")
$toggleButton.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "LabelboxForegroundColor")
$toggleButton.Cursor = [System.Windows.Input.Cursors]::Hand
$toggleButton.HorizontalAlignment = [Windows.HorizontalAlignment]::Stretch
$sync.$Category = $toggleButton
# Add click handler to toggle category visibility
$toggleButton.Add_MouseLeftButtonUp({
param($sender, $e)
# Find the parent StackPanel (categoryContainer)
$categoryContainer = $sender.Parent
if ($categoryContainer -and $categoryContainer.Children.Count -ge 2) {
# The WrapPanel is the second child
$wrapPanel = $categoryContainer.Children[1]
# Toggle visibility
if ($wrapPanel.Visibility -eq [Windows.Visibility]::Visible) {
$wrapPanel.Visibility = [Windows.Visibility]::Collapsed
# Change - to +
$sender.Content = $sender.Content -replace "^- ", "+ "
} else {
$wrapPanel.Visibility = [Windows.Visibility]::Visible
# Change + to -
$sender.Content = $sender.Content -replace "^\+ ", "- "
}
}
})
$null = $categoryContainer.Children.Add($toggleButton)
# Add wrap panel for apps to container
$wrapPanel = New-Object Windows.Controls.WrapPanel $wrapPanel = New-Object Windows.Controls.WrapPanel
$wrapPanel.Orientation = "Horizontal" $wrapPanel.Orientation = "Horizontal"
$wrapPanel.HorizontalAlignment = "Stretch" $wrapPanel.HorizontalAlignment = "Left"
$wrapPanel.VerticalAlignment = "Center" $wrapPanel.VerticalAlignment = "Top"
$wrapPanel.Margin = New-Object Windows.Thickness(0, 0, 0, 20) $wrapPanel.Margin = New-Object Windows.Thickness(0, 0, 0, 0)
$wrapPanel.Visibility = [Windows.Visibility]::Visible $wrapPanel.Visibility = [Windows.Visibility]::Visible
$wrapPanel.Tag = "CategoryWrapPanel_$category" $wrapPanel.Tag = "CategoryWrapPanel_$category"
$null = $TargetElement.Items.Add($wrapPanel)
$appsByCategory[$category] |Sort-Object | ForEach-Object { $null = $categoryContainer.Children.Add($wrapPanel)
$sync.$_ = $(Initialize-InstallAppEntry -TargetElement $wrapPanel -AppKey $_)
# Add the entire category container to the target element
$null = $TargetElement.Items.Add($categoryContainer)
# Add apps to the wrap panel
$appsByCategory[$category] | Sort-Object | ForEach-Object {
$sync.$_ = $(Initialize-InstallAppEntry -TargetElement $wrapPanel -AppKey $_)
} }
} }
} }

View File

@@ -23,6 +23,8 @@ function Invoke-WPFButton {
"WPFInstall" {Invoke-WPFInstall} "WPFInstall" {Invoke-WPFInstall}
"WPFUninstall" {Invoke-WPFUnInstall} "WPFUninstall" {Invoke-WPFUnInstall}
"WPFInstallUpgrade" {Invoke-WPFInstallUpgrade} "WPFInstallUpgrade" {Invoke-WPFInstallUpgrade}
"WPFCollapseAllCategories" {Invoke-WPFToggleAllCategories -Action "Collapse"}
"WPFExpandAllCategories" {Invoke-WPFToggleAllCategories -Action "Expand"}
"WPFStandard" {Invoke-WPFPresets "Standard" -checkboxfilterpattern "WPFTweak*"} "WPFStandard" {Invoke-WPFPresets "Standard" -checkboxfilterpattern "WPFTweak*"}
"WPFMinimal" {Invoke-WPFPresets "Minimal" -checkboxfilterpattern "WPFTweak*"} "WPFMinimal" {Invoke-WPFPresets "Minimal" -checkboxfilterpattern "WPFTweak*"}
"WPFClearTweaksSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFTweak*"} "WPFClearTweaksSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFTweak*"}

View File

@@ -0,0 +1,51 @@
function Invoke-WPFToggleAllCategories {
<#
.SYNOPSIS
Expands or collapses all categories in the Install tab
.PARAMETER Action
The action to perform: "Expand" or "Collapse"
.DESCRIPTION
This function iterates through all category containers in the Install tab
and expands or collapses their WrapPanels while updating the toggle button labels
#>
param(
[Parameter(Mandatory=$true)]
[ValidateSet("Expand", "Collapse")]
[string]$Action
)
try {
if ($null -eq $sync.ItemsControl) {
Write-Warning "ItemsControl not initialized"
return
}
$targetVisibility = if ($Action -eq "Expand") { [Windows.Visibility]::Visible } else { [Windows.Visibility]::Collapsed }
$targetPrefix = if ($Action -eq "Expand") { "-" } else { "+" }
$sourcePrefix = if ($Action -eq "Expand") { "+" } else { "-" }
# Iterate through all items in the ItemsControl
$sync.ItemsControl.Items | ForEach-Object {
$categoryContainer = $_
# Check if this is a category container (StackPanel with children)
if ($categoryContainer -is [System.Windows.Controls.StackPanel] -and $categoryContainer.Children.Count -ge 2) {
# Get the WrapPanel (second child)
$wrapPanel = $categoryContainer.Children[1]
$wrapPanel.Visibility = $targetVisibility
# Update the label to show the correct state
$categoryLabel = $categoryContainer.Children[0]
if ($categoryLabel.Content -like "$sourcePrefix*") {
$categoryLabel.Content = $categoryLabel.Content -replace "^$sourcePrefix ", "$targetPrefix "
}
}
}
}
catch {
Write-Error "Error toggling categories: $_"
}
}

View File

@@ -23,7 +23,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource BorderColor}"/> <Setter Property="BorderBrush" Value="{DynamicResource BorderColor}"/>
<Setter Property="MaxWidth" Value="{DynamicResource ToolTipWidth}"/> <Setter Property="MaxWidth" Value="{DynamicResource ToolTipWidth}"/>
<Setter Property="BorderThickness" Value="1"/> <Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="5"/> <Setter Property="Padding" Value="2"/>
<Setter Property="FontSize" Value="{DynamicResource FontSize}"/> <Setter Property="FontSize" Value="{DynamicResource FontSize}"/>
<Setter Property="FontFamily" Value="{DynamicResource FontFamily}"/> <Setter Property="FontFamily" Value="{DynamicResource FontFamily}"/>
<!-- This ContentTemplate ensures that the content of the ToolTip wraps text properly for better readability --> <!-- This ContentTemplate ensures that the content of the ToolTip wraps text properly for better readability -->
@@ -85,7 +85,7 @@
<Style x:Key="AppEntryBorderStyle" TargetType="Border"> <Style x:Key="AppEntryBorderStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="Gray"/> <Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="{DynamicResource AppEntryBorderThickness}"/> <Setter Property="BorderThickness" Value="{DynamicResource AppEntryBorderThickness}"/>
<Setter Property="CornerRadius" Value="5"/> <Setter Property="CornerRadius" Value="2"/>
<Setter Property="Padding" Value="{DynamicResource AppEntryMargin}"/> <Setter Property="Padding" Value="{DynamicResource AppEntryMargin}"/>
<Setter Property="Width" Value="{DynamicResource AppEntryWidth}"/> <Setter Property="Width" Value="{DynamicResource AppEntryWidth}"/>
<Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="VerticalAlignment" Value="Top"/>
@@ -101,10 +101,30 @@
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="CheckBox"> <ControlTemplate TargetType="CheckBox">
<ContentPresenter Content="{TemplateBinding Content}" <StackPanel Orientation="Horizontal">
VerticalAlignment="Center" <Grid Width="16" Height="16" Margin="0,0,8,0">
HorizontalAlignment="Left" <Border x:Name="CheckBoxBorder"
Margin="{TemplateBinding Padding}"/> BorderBrush="{DynamicResource MainForegroundColor}"
Background="{DynamicResource ButtonBackgroundColor}"
BorderThickness="1"
Width="12"
Height="12"
CornerRadius="2"/>
<Path x:Name="CheckMark"
Stroke="{DynamicResource ToggleButtonOnColor}"
StrokeThickness="2"
Data="M 2 8 L 6 12 L 14 4"
Visibility="Collapsed"/>
</Grid>
<ContentPresenter Content="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Left"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> </ControlTemplate>
</Setter.Value> </Setter.Value>
</Setter> </Setter>
@@ -164,6 +184,7 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<Style TargetType="Button" x:Key="HoverButtonStyle"> <Style TargetType="Button" x:Key="HoverButtonStyle">
<Setter Property="Foreground" Value="{DynamicResource MainForegroundColor}" /> <Setter Property="Foreground" Value="{DynamicResource MainForegroundColor}" />
@@ -532,7 +553,7 @@
BorderThickness="1" BorderThickness="1"
Width="{DynamicResource CheckBoxBulletDecoratorSize *0.85}" Width="{DynamicResource CheckBoxBulletDecoratorSize *0.85}"
Height="{DynamicResource CheckBoxBulletDecoratorSize *0.85}" Height="{DynamicResource CheckBoxBulletDecoratorSize *0.85}"
Margin="2" Margin="1"
SnapsToDevicePixels="True"/> SnapsToDevicePixels="True"/>
<Viewbox x:Name="CheckMarkContainer" <Viewbox x:Name="CheckMarkContainer"
Width="{DynamicResource CheckBoxBulletDecoratorSize}" Width="{DynamicResource CheckBoxBulletDecoratorSize}"