From 962b18e8fabdb755d78a804ffbaec2af122c55b4 Mon Sep 17 00:00:00 2001 From: Martin Wiethan <47688561+Marterich@users.noreply.github.com> Date: Mon, 12 May 2025 22:45:57 +0200 Subject: [PATCH] Re-Redesign of the Install Tab (#3350) * Initial Remove Expanded View * more cleanup * Add word wrapping for Tooltips * Update tooltip colors in themes and XAML styles * Rename Properties for consistency * More Cleanup, and simplification. Also added support for screenreaders * Remove unused variables and shorten window naming * Rename Invoke-WPFUIApps to Initialize-WPFUI and update function calls for consistency * Rename Invoke-WPFUIApps.ps1 to Initialize-WPFUI.ps1 * Add TODO comments for sidebar UI generation in Initialize-WPFUI function --- config/appnavigation.json | 50 +++++--- config/themes.json | 12 +- .../private/Find-AppsByNameOrDescription.ps1 | 17 ++- .../Find-TweaksByNameOrDescription.ps1 | 4 - .../private/Initalize-InstallAppEntry.ps1 | 93 ++------------ .../private/Initialize-InstallAppArea.ps1 | 17 ++- .../Initialize-InstallAppsMainElement.ps1 | 28 ---- .../Initialize-InstallCategoryAppList.ps1 | 71 ++++------ .../private/Initialize-InstallHeader.ps1 | 78 ----------- functions/private/Set-CategoryVisibility.ps1 | 44 ------- functions/private/Show-OnlyCheckedApps.ps1 | 54 -------- functions/public/Initialize-WPFUI.ps1 | 57 +++++++++ functions/public/Invoke-WPFButton.ps1 | 4 +- functions/public/Invoke-WPFGetInstalled.ps1 | 16 +-- functions/public/Invoke-WPFInstall.ps1 | 1 - .../public/Invoke-WPFSelectedAppsUpdate.ps1 | 2 +- functions/public/Invoke-WPFUIApps.ps1 | 22 ---- functions/public/Invoke-WPFUIElements.ps1 | 6 - functions/public/Invoke-WPFtweaksbutton.ps1 | 2 +- scripts/main.ps1 | 49 +------ scripts/start.ps1 | 5 +- xaml/inputXML.xaml | 121 ++++-------------- 22 files changed, 200 insertions(+), 553 deletions(-) delete mode 100644 functions/private/Initialize-InstallAppsMainElement.ps1 delete mode 100644 functions/private/Initialize-InstallHeader.ps1 delete mode 100644 functions/private/Set-CategoryVisibility.ps1 delete mode 100644 functions/private/Show-OnlyCheckedApps.ps1 create mode 100644 functions/public/Initialize-WPFUI.ps1 delete mode 100644 functions/public/Invoke-WPFUIApps.ps1 diff --git a/config/appnavigation.json b/config/appnavigation.json index c0a408a9..d7782162 100644 --- a/config/appnavigation.json +++ b/config/appnavigation.json @@ -1,31 +1,24 @@ { - "WPFToggleView": { - "Content": ["Expanded View", "Compact View"], + "WPFInstall": { + "Content": "Install/Upgrade Applications", "Category": "____Actions", - "Type": "ToggleButton", + "Type": "Button", "Order": "1", - "Description": "Toggle between a list and a compact grid like view" + "Description": "Install or upgrade the selected applications" }, - "WPFSelectedFilter": { - "Content": [ "Show All", "Show Selected"], + "WPFUninstall": { + "Content": "Uninstall Applications", "Category": "____Actions", - "Type": "ToggleButton", + "Type": "Button", "Order": "2", - "Description": "Toggle between showing all or only the selected applications" + "Description": "Uninstall the selected applications" }, - "WPFClearInstallSelection": { - "Content": "Clear Selection", + "WPFInstallUpgrade": { + "Content": "Upgrade all Applications", "Category": "____Actions", "Type": "Button", "Order": "3", - "Description": "Clear the selection of applications" - }, - "WPFGetInstalled": { - "Content": "Get Installed", - "Category": "____Actions", - "Type": "Button", - "Order": "4", - "Description": "Show installed applications" + "Description": "Upgrade all applications to the latest version" }, "WingetRadioButton": { "Content": "Winget", @@ -44,5 +37,26 @@ "Checked": false, "Order": "2", "Description": "Use Chocolatey for package management" + }, + "WPFClearInstallSelection": { + "Content": "Clear Selection", + "Category": "__Selection", + "Type": "Button", + "Order": "1", + "Description": "Clear the selection of applications" + }, + "WPFGetInstalled": { + "Content": "Get Installed", + "Category": "__Selection", + "Type": "Button", + "Order": "2", + "Description": "Show installed applications" + }, + "WPFselectedAppsButton": { + "Content": "Selected Apps: 0", + "Category": "__Selection", + "Type": "Button", + "Order": "3", + "Description": "Show the selected applications" } } diff --git a/config/themes.json b/config/themes.json index 724cb6af..b657c770 100644 --- a/config/themes.json +++ b/config/themes.json @@ -1,5 +1,9 @@ { "shared":{ + "AppEntryWidth": "130", + "AppEntryFontSize": "11", + "AppEntryMargin": "1,1,1,1", + "AppEntryBorderTickness": "0", "CustomDialogFontSize": "12", "CustomDialogFontSizeHeader": "14", "CustomDialogLogoSize": "25", @@ -16,6 +20,7 @@ "TabButtonWidth": "110", "TabButtonHeight": "26", "TabRowHeightInPixels": "50", + "ToolTipWidth": "300", "IconFontSize": "14", "IconButtonSize": "35", "SettingsIconFontSize": "18", @@ -35,11 +40,10 @@ "CheckboxMouseOverColor": "#999999", "ButtonBorderThickness": "1", "ButtonMargin": "1", - "ButtonCornerRadius": "2", - "AppTileImageSize": "40" + "ButtonCornerRadius": "2" }, "Light": { - "AppInstallUnselectedColor": "#F0F0F0", + "AppInstallUnselectedColor": "#F7F7F7", "AppInstallHighlightedColor": "#CFCFCF", "AppInstallSelectedColor": "#C2C2C2", "ComboBoxForegroundColor": "#232629", @@ -72,6 +76,7 @@ "ButtonForegroundColor": "#232629", "ToggleButtonOnColor": "#2e77ff", "ToggleButtonOffColor": "#707070", + "ToolTipBackgroundColor": "#F7F7F7", "BorderColor": "#232629", "BorderOpacity": "0.2" @@ -110,6 +115,7 @@ "ButtonForegroundColor": "#F7F7F7", "ToggleButtonOnColor": "#2e77ff", "ToggleButtonOffColor": "#707070", + "ToolTipBackgroundColor": "#2F373D", "BorderColor": "#2F373D", "BorderOpacity": "0.2" } diff --git a/functions/private/Find-AppsByNameOrDescription.ps1 b/functions/private/Find-AppsByNameOrDescription.ps1 index 4271835f..44cf48a4 100644 --- a/functions/private/Find-AppsByNameOrDescription.ps1 +++ b/functions/private/Find-AppsByNameOrDescription.ps1 @@ -12,10 +12,23 @@ function Find-AppsByNameOrDescription { ) # Reset the visibility if the search string is empty or the search is cleared if ([string]::IsNullOrWhiteSpace($SearchString)) { - Set-CategoryVisibility -Category "*" - return + $sync.ItemsControl.Items | ForEach-Object { + $_.Visibility = [Windows.Visibility]::Visible + $_.Children | ForEach-Object { + if ($null -ne $_) { + $_.Visibility = [Windows.Visibility]::Visible + } + + } + } + return } $sync.ItemsControl.Items | ForEach-Object { + # Ensure ToggleButtons remain visible + if ($_.Tag -like "CategoryToggleButton") { + $_.Visibility = [Windows.Visibility]::Visible + return + } # Hide all CategoryWrapPanel and ToggleButton $_.Visibility = [Windows.Visibility]::Collapsed if ($_.Tag -like "CategoryWrapPanel_*") { diff --git a/functions/private/Find-TweaksByNameOrDescription.ps1 b/functions/private/Find-TweaksByNameOrDescription.ps1 index 59928d04..81e9cb39 100644 --- a/functions/private/Find-TweaksByNameOrDescription.ps1 +++ b/functions/private/Find-TweaksByNameOrDescription.ps1 @@ -45,7 +45,6 @@ function Find-TweaksByNameOrDescription { # Search for matching tweaks when search string is not null $tweakspanel = $sync.Form.FindName("tweakspanel") - $matchFound = $false $tweakspanel.Children | ForEach-Object { $categoryBorder = $_ @@ -96,9 +95,6 @@ function Find-TweaksByNameOrDescription { # Set the visibility based on if any item matched $categoryBorder.Visibility = if ($categoryVisible) { [Windows.Visibility]::Visible } else { [Windows.Visibility]::Collapsed } - if ($categoryVisible) { - $matchFound = $true - } } } } diff --git a/functions/private/Initalize-InstallAppEntry.ps1 b/functions/private/Initalize-InstallAppEntry.ps1 index a81adf07..eef4f8ba 100644 --- a/functions/private/Initalize-InstallAppEntry.ps1 +++ b/functions/private/Initalize-InstallAppEntry.ps1 @@ -15,14 +15,11 @@ function Initialize-InstallAppEntry { # Create the outer Border for the application type $border = New-Object Windows.Controls.Border - $border.Style = $sync.Form.Resources.AppTileBorderStyle + $border.Style = $sync.Form.Resources.AppEntryBorderStyle $border.Tag = $appKey $border.ToolTip = $Apps.$appKey.description $border.Add_MouseUp({ - if ($_.ChangedButton -eq [System.Windows.Input.MouseButton]::Right) { - Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFInstall*"; - } - $childCheckbox = ($this.Child.Children | Where-Object {$_.Template.TargetType -eq [System.Windows.Controls.Checkbox]})[0] + $childCheckbox = ($this.Child | Where-Object {$_.Template.TargetType -eq [System.Windows.Controls.Checkbox]})[0] $childCheckBox.isChecked = -not $childCheckbox.IsChecked }) $border.Add_MouseEnter({ @@ -35,101 +32,35 @@ function Initialize-InstallAppEntry { $this.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor") } }) - # Create a DockPanel inside the Border - $dockPanel = New-Object Windows.Controls.DockPanel - $dockPanel.LastChildFill = $true - $border.Child = $dockPanel - # Create the CheckBox, vertically centered $checkBox = New-Object Windows.Controls.CheckBox $checkBox.Name = $appKey - $checkbox.Style = $sync.Form.Resources.AppTileCheckboxStyle + $checkbox.Style = $sync.Form.Resources.AppEntryCheckboxStyle $checkbox.Add_Checked({ Invoke-WPFSelectedAppsUpdate -type "Add" -checkbox $this - $borderElement = $this.Parent.Parent + $borderElement = $this.Parent $borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallSelectedColor") }) $checkbox.Add_Unchecked({ Invoke-WPFSelectedAppsUpdate -type "Remove" -checkbox $this - $borderElement = $this.Parent.Parent + $borderElement = $this.Parent $borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor") }) - # Create a StackPanel for the image and name - $imageAndNamePanel = New-Object Windows.Controls.StackPanel - $imageAndNamePanel.Orientation = "Horizontal" - $imageAndNamePanel.VerticalAlignment = "Center" - - # Create the Image and set a placeholder - $image = New-Object Windows.Controls.Image - # $image.Name = "wpfapplogo" + $App.Name - $image.Style = $sync.Form.Resources.AppTileImageStyle - $image.Source = $noimage # Ensure $noimage is defined in your script - - $imageAndNamePanel.Children.Add($image) | Out-Null - # Create the TextBlock for the application name $appName = New-Object Windows.Controls.TextBlock - $appName.Style = $sync.Form.Resources.AppTileNameStyle + $appName.Style = $sync.Form.Resources.AppEntryNameStyle $appName.Text = $Apps.$appKey.content - $imageAndNamePanel.Children.Add($appName) | Out-Null - # Add the image and name panel to the Checkbox - $checkBox.Content = $imageAndNamePanel + # Add the name to the Checkbox + $checkBox.Content = $appName - # Add the checkbox to the DockPanel - [Windows.Controls.DockPanel]::SetDock($checkBox, [Windows.Controls.Dock]::Left) - $dockPanel.Children.Add($checkBox) | Out-Null - - # Create the StackPanel for the buttons and dock it to the right - $buttonPanel = New-Object Windows.Controls.StackPanel - $buttonPanel.Style = $sync.Form.Resources.AppTileButtonPanelStyle - [Windows.Controls.DockPanel]::SetDock($buttonPanel, [Windows.Controls.Dock]::Right) - - # Define the button properties - $buttons = @( - [PSCustomObject]@{ Name = "Install"; Description = "Install or Upgrade the application"; Tooltip = "Install or Upgrade the application"; Icon = [char]0xE118 }, - [PSCustomObject]@{ Name = "Uninstall"; Description = "Uninstall the application"; Tooltip = "Uninstall the application"; Icon = [char]0xE74D }, - [PSCustomObject]@{ Name = "Info"; Description = "Open the application's website in your default browser"; Tooltip = "Open the application's website in your default browser"; Icon = [char]0xE946 } - ) - - # Iterate over each button and create it - foreach ($button in $buttons) { - $newButton = New-Object Windows.Controls.Button - $newButton.Style = $sync.Form.Resources.AppTileButtonStyle - $newButton.Content = $button.Icon - $newButton.ToolTip = $button.Tooltip - $buttonPanel.Children.Add($newButton) | Out-Null - - switch ($button.Name) { - "Install" { - $newButton.Add_Click({ - $appKey = $this.Parent.Parent.Parent.Tag - $appObject = $sync.configs.applicationsHashtable.$appKey - Invoke-WPFInstall -PackagesToInstall $appObject - }) - } - "Uninstall" { - $newButton.Add_Click({ - $appKey = $this.Parent.Parent.Parent.Tag - $appObject = $sync.configs.applicationsHashtable.$appKey - Invoke-WPFUnInstall -PackagesToUninstall $appObject - }) - } - "Info" { - $newButton.Add_Click({ - $appKey = $this.Parent.Parent.Parent.Tag - $appObject = $sync.configs.applicationsHashtable.$appKey - Start-Process $appObject.link - }) - } - } - } - - # Add the button panel to the DockPanel - $dockPanel.Children.Add($buttonPanel) | Out-Null + # Add accessibility properties to make the elements screen reader friendly + $checkBox.SetValue([Windows.Automation.AutomationProperties]::NameProperty, $Apps.$appKey.content) + $border.SetValue([Windows.Automation.AutomationProperties]::NameProperty, $Apps.$appKey.content) + $border.Child = $checkBox # Add the border to the corresponding Category $TargetElement.Children.Add($border) | Out-Null return $checkbox diff --git a/functions/private/Initialize-InstallAppArea.ps1 b/functions/private/Initialize-InstallAppArea.ps1 index 3e79d56d..ea2f7ee0 100644 --- a/functions/private/Initialize-InstallAppArea.ps1 +++ b/functions/private/Initialize-InstallAppArea.ps1 @@ -10,27 +10,36 @@ #> param($TargetElement) + $targetGrid = $sync.Form.FindName($TargetElement) + $null = $targetGrid.Children.Clear() + + # Create the outer Border for the aren where the apps will be placed + $Border = New-Object Windows.Controls.Border + $Border.VerticalAlignment = "Stretch" + $Border.SetResourceReference([Windows.Controls.Control]::StyleProperty, "BorderStyle") + + # Add a ScrollViewer, because the ItemsControl does not support scrolling by itself $scrollViewer = New-Object Windows.Controls.ScrollViewer $scrollViewer.VerticalScrollBarVisibility = 'Auto' $scrollViewer.HorizontalAlignment = 'Stretch' $scrollViewer.VerticalAlignment = 'Stretch' $scrollViewer.CanContentScroll = $true + ## Create the ItemsControl, which will be the parent of all the app entries $itemsControl = New-Object Windows.Controls.ItemsControl $itemsControl.HorizontalAlignment = 'Stretch' $itemsControl.VerticalAlignment = 'Stretch' + # 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) $itemsPanelTemplate = New-Object Windows.Controls.ItemsPanelTemplate $factory = New-Object Windows.FrameworkElementFactory ([Windows.Controls.VirtualizingStackPanel]) $itemsPanelTemplate.VisualTree = $factory $itemsControl.ItemsPanel = $itemsPanelTemplate - $itemsControl.SetValue([Windows.Controls.VirtualizingStackPanel]::IsVirtualizingProperty, $true) $itemsControl.SetValue([Windows.Controls.VirtualizingStackPanel]::VirtualizationModeProperty, [Windows.Controls.VirtualizationMode]::Recycling) $scrollViewer.Content = $itemsControl - - [Windows.Controls.DockPanel]::SetDock($scrollViewer, [Windows.Controls.Dock]::Bottom) - $null = $TargetElement.Children.Add($scrollViewer) + $Border.Child = $scrollViewer + $null = $targetGrid.Children.Add($Border) return $itemsControl } diff --git a/functions/private/Initialize-InstallAppsMainElement.ps1 b/functions/private/Initialize-InstallAppsMainElement.ps1 deleted file mode 100644 index 818a6343..00000000 --- a/functions/private/Initialize-InstallAppsMainElement.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -function Initialize-InstallAppsMainElement { - <# - .SYNOPSIS - Clears the given WPF Grid and creates a [Windows.Controls.Border] containing a [Windows.Controls.StackPanel] - Used to as part of the Install Tab UI generation - .PARAMETER TargetGridName - The WPF Grid name - .OUTPUTS - Returns the created [Windows.Controls.StackPanel] element - #> - param( - [Parameter(Mandatory)] - [string]$TargetGridName - ) - $targetGrid = $sync.Form.FindName($TargetGridName) - $null = $targetGrid.Children.Clear() - - $Border = New-Object Windows.Controls.Border - $Border.VerticalAlignment = "Stretch" - $Border.SetResourceReference([Windows.Controls.Control]::StyleProperty, "BorderStyle") - - $dockPanel = New-Object Windows.Controls.DockPanel - $Border.Child = $dockPanel - - $null = $targetGrid.Children.Add($Border) - - return $dockPanel -} diff --git a/functions/private/Initialize-InstallCategoryAppList.ps1 b/functions/private/Initialize-InstallCategoryAppList.ps1 index 5e69c42b..1272316a 100644 --- a/functions/private/Initialize-InstallCategoryAppList.ps1 +++ b/functions/private/Initialize-InstallCategoryAppList.ps1 @@ -21,61 +21,36 @@ function Initialize-InstallCategoryAppList { [Windows.Controls.ItemsControl]$TargetElement ) - $toggleButton = New-Object Windows.Controls.Primitives.ToggleButton + $toggleButton = New-Object Windows.Controls.Label $toggleButton.Content = "$Category" $toggleButton.Tag = "CategoryToggleButton" - $toggleButton.Cursor = [System.Windows.Input.Cursors]::Hand - $toggleButton.SetResourceReference([Windows.Controls.Control]::StyleProperty, "CategoryToggleButtonStyle") - $sync.Buttons.Add($toggleButton) - $toggleButton.Add_Checked({ - # Clear the search bar when a category is clicked - $sync.SearchBar.Text = "" - Set-CategoryVisibility -Category $this.Content -overrideState Expand - }) - $toggleButton.Add_Unchecked({ - Set-CategoryVisibility -Category $this.Content -overrideState Collapse - }) + $sync.$Category = $Category + $null = $TargetElement.Items.Add($toggleButton) } - $loadingLabel = New-Object Windows.Controls.Label - $loadingLabel.Content = "Loading, please wait..." - $loadingLabel.HorizontalAlignment = "Center" - $loadingLabel.VerticalAlignment = "Center" - $loadingLabel.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "HeaderFontSize") - $loadingLabel.FontWeight = [Windows.FontWeights]::Bold - $loadingLabel.Foreground = [Windows.Media.Brushes]::Gray - $sync.LoadingLabel = $loadingLabel - $TargetElement.Items.Clear() - $null = $TargetElement.Items.Add($sync.LoadingLabel) - # Use the Dispatcher to make sure the Loading message is shown before the logic loading the apps starts, and only is removed when the loading is complete and the apps are added to the UI - $TargetElement.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ - - $TargetElement.Items.Clear() # Remove the loading message - - # Pre-group apps by category - $appsByCategory = @{} - foreach ($appKey in $Apps.Keys) { - $category = $Apps.$appKey.Category - if (-not $appsByCategory.ContainsKey($category)) { - $appsByCategory[$category] = @() - } - $appsByCategory[$category] += $appKey + # Pre-group apps by category + $appsByCategory = @{} + foreach ($appKey in $Apps.Keys) { + $category = $Apps.$appKey.Category + if (-not $appsByCategory.ContainsKey($category)) { + $appsByCategory[$category] = @() } - foreach ($category in $($appsByCategory.Keys | Sort-Object)) { - Add-Category -Category $category -TargetElement $TargetElement - $wrapPanel = New-Object Windows.Controls.WrapPanel - $wrapPanel.Orientation = "Horizontal" - $wrapPanel.HorizontalAlignment = "Stretch" - $wrapPanel.VerticalAlignment = "Center" - $wrapPanel.Margin = New-Object Windows.Thickness(0, 0, 0, 20) - $wrapPanel.Visibility = [Windows.Visibility]::Collapsed - $wrapPanel.Tag = "CategoryWrapPanel_$category" - $null = $TargetElement.Items.Add($wrapPanel) - $appsByCategory[$category] |Sort-Object | ForEach-Object { - $sync.$_ = $(Initialize-InstallAppEntry -TargetElement $wrapPanel -AppKey $_) + $appsByCategory[$category] += $appKey + } + foreach ($category in $($appsByCategory.Keys | Sort-Object)) { + Add-Category -Category $category -TargetElement $TargetElement + $wrapPanel = New-Object Windows.Controls.WrapPanel + $wrapPanel.Orientation = "Horizontal" + $wrapPanel.HorizontalAlignment = "Stretch" + $wrapPanel.VerticalAlignment = "Center" + $wrapPanel.Margin = New-Object Windows.Thickness(0, 0, 0, 20) + $wrapPanel.Visibility = [Windows.Visibility]::Visible + $wrapPanel.Tag = "CategoryWrapPanel_$category" + $null = $TargetElement.Items.Add($wrapPanel) + $appsByCategory[$category] |Sort-Object | ForEach-Object { + $sync.$_ = $(Initialize-InstallAppEntry -TargetElement $wrapPanel -AppKey $_) } } - }) } diff --git a/functions/private/Initialize-InstallHeader.ps1 b/functions/private/Initialize-InstallHeader.ps1 deleted file mode 100644 index c218dab3..00000000 --- a/functions/private/Initialize-InstallHeader.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -function Initialize-InstallHeader { - <# - .SYNOPSIS - Creates the Multi Selection Header Elements on the Install Tab - Used to as part of the Install Tab UI generation - .PARAMETER TargetElement - The Parent Element into which the Header should be placed - #> - param($TargetElement) - function New-WPFButton { - param ( - [string]$Name, - [string]$Content - ) - $button = New-Object Windows.Controls.Button - $button.Name = $Name - $button.Content = $Content - $button.Margin = New-Object Windows.Thickness(2) - $button.HorizontalAlignment = "Stretch" - return $button - } - - $wrapPanelTop = New-Object Windows.Controls.WrapPanel - $wrapPanelTop.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "MainBackgroundColor") - $wrapPanelTop.HorizontalAlignment = "Left" - $wrapPanelTop.VerticalAlignment = "Top" - $wrapPanelTop.Orientation = "Horizontal" - $wrapPanelTop.SetResourceReference([Windows.Controls.Control]::MarginProperty, "TabContentMargin") - $buttonConfigs = @( - @{Name="WPFInstall"; Content="Install/Upgrade Selected"}, - @{Name="WPFInstallUpgrade"; Content="Upgrade All"}, - @{Name="WPFUninstall"; Content="Uninstall Selected"}, - @{Name="WPFselectedAppsButton"; Content="Selected Apps: 0"} - ) - - foreach ($config in $buttonConfigs) { - $button = New-WPFButton -Name $config.Name -Content $config.Content - $null = $wrapPanelTop.Children.Add($button) - $sync[$config.Name] = $button - } - - $selectedAppsPopup = New-Object Windows.Controls.Primitives.Popup - $selectedAppsPopup.IsOpen = $false - $selectedAppsPopup.PlacementTarget = $sync.WPFselectedAppsButton - $selectedAppsPopup.Placement = [System.Windows.Controls.Primitives.PlacementMode]::Bottom - $selectedAppsPopup.AllowsTransparency = $true - - $selectedAppsBorder = New-Object Windows.Controls.Border - $selectedAppsBorder.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "MainBackgroundColor") - $selectedAppsBorder.SetResourceReference([Windows.Controls.Control]::BorderBrushProperty, "MainForegroundColor") - $selectedAppsBorder.SetResourceReference([Windows.Controls.Control]::BorderThicknessProperty, "ButtonBorderThickness") - $selectedAppsBorder.Width = 200 - $selectedAppsBorder.Padding = 5 - $selectedAppsPopup.Child = $selectedAppsBorder - $sync.selectedAppsPopup = $selectedAppsPopup - - $sync.selectedAppsstackPanel = New-Object Windows.Controls.StackPanel - $selectedAppsBorder.Child = $sync.selectedAppsstackPanel - - # Toggle selectedAppsPopup open/close with button - $sync.WPFselectedAppsButton.Add_Click({ - $sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen - }) - # Close selectedAppsPopup when mouse leaves both button and selectedAppsPopup - $sync.WPFselectedAppsButton.Add_MouseLeave({ - if (-not $sync.selectedAppsPopup.IsMouseOver) { - $sync.selectedAppsPopup.IsOpen = $false - } - }) - $selectedAppsPopup.Add_MouseLeave({ - if (-not $sync.WPFselectedAppsButton.IsMouseOver) { - $sync.selectedAppsPopup.IsOpen = $false - } - }) - - [Windows.Controls.DockPanel]::SetDock($wrapPanelTop, [Windows.Controls.Dock]::Top) - $null = $TargetElement.Children.Add($wrapPanelTop) -} diff --git a/functions/private/Set-CategoryVisibility.ps1 b/functions/private/Set-CategoryVisibility.ps1 deleted file mode 100644 index 4d2c48d0..00000000 --- a/functions/private/Set-CategoryVisibility.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -function Set-CategoryVisibility { - <# - .SYNOPSIS - Used to expand or collapse categories and corresponding apps on the install tab - - .PARAMETER Category - Can eigther be a specific category name like "Browsers" OR "*" to affect all categories at once - - .PARAMETER overrideState - "Expand" => expands the corresponding elements - "Collapse" => collapses the corresponding elements - N/A => if compactView is active expand, otherwise collapse elements - #> - param( - [Parameter(Mandatory=$true)] - [string]$Category, - [ValidateSet("Expand", "Collapse")] - [string]$overrideState - ) - - switch ($overrideState) { - "Expand" {$state = $true} - "Collapse" {$state = $false} - default {$state = $sync.CompactView} - } - - # If all the Categories are affected, update the Checked state of the ToggleButtons. - # Otherwise, the state is not synced when toggling between the display modes - if ($category -eq "*") { - $items = $sync.ItemsControl.Items | Where-Object {($_.Tag -like "CategoryWrapPanel_*")} - $sync.ItemsControl.Items | Where-Object {($_.Tag -eq "CategoryToggleButton")} | Foreach-Object { $_.Visibility = [Windows.Visibility]::Visible; $_.IsChecked = $state } - - } else { - $items = $sync.ItemsControl.Items | Where-Object {($_.Tag -eq "CategoryWrapPanel_$Category")} - } - - $elementVisibility = if ($state -eq $true) {[Windows.Visibility]::Visible} else {[Windows.Visibility]::Collapsed} - $items | ForEach-Object { - $_.Visibility = $elementVisibility - } - $items.Children | ForEach-Object { - $_.Visibility = $elementVisibility - } -} diff --git a/functions/private/Show-OnlyCheckedApps.ps1 b/functions/private/Show-OnlyCheckedApps.ps1 deleted file mode 100644 index fc581cb0..00000000 --- a/functions/private/Show-OnlyCheckedApps.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -function Show-OnlyCheckedApps { - <# - .SYNOPSIS - Toggle between showing only the actively selected apps on the Install Tab and hiding everything else and displaying every app. - If no apps are selected, dont do anything - .PARAMETER appKeys - Expects a List of appKeys that are selected at the moment - If not provided, or empty, the function exits without any visual change to the ui - .EXAMPLE - Show-OnlyCheckedApps -appKeys $sync.SelectedApps - Show-OnlyCheckedApps -appKeys ("WPFInstallChrome", "WPFInstall7zip") - #> - param ( - [Parameter(Mandatory=$false)] - [String[]]$appKeys - ) - # If no apps are selected, do not allow switching to show only selected - if (($false -eq $sync.ShowOnlySelected) -and ($appKeys.Length -eq 0)) { - Write-Host "No apps selected" - $sync.wpfselectedfilter.IsChecked = $false - return - } - $sync.ShowOnlySelected = -not $sync.ShowOnlySelected - if ($sync.ShowOnlySelected) { - $sync.Buttons | Where-Object {$_.Name -like "ShowSelectedAppsButton"} | ForEach-Object { - $_.Content = "Show All" - } - - $sync.ItemsControl.Items | Foreach-Object { - # Search for App Container and set them to visible - if ($_.Tag -like "CategoryWrapPanel_*") { - $_.Visibility = [Windows.Visibility]::Visible - # Iterate through all the apps in the container and set them to visible if they are in the appKeys array - $_.Children | ForEach-Object { - if ($appKeys -contains $_.Tag) { - $_.Visibility = [Windows.Visibility]::Visible - } - else { - $_.Visibility = [Windows.Visibility]::Collapsed - } - } - } - else { - # Set all other items to collapsed - $_.Visibility = [Windows.Visibility]::Collapsed - } - } - } else { - $sync.Buttons | Where-Object {$_.Name -like "ShowSelectedAppsButton"} | ForEach-Object { - $_.Content = "Show Selected" - } - Set-CategoryVisibility -Category "*" - } -} diff --git a/functions/public/Initialize-WPFUI.ps1 b/functions/public/Initialize-WPFUI.ps1 new file mode 100644 index 00000000..4786a8e9 --- /dev/null +++ b/functions/public/Initialize-WPFUI.ps1 @@ -0,0 +1,57 @@ +function Initialize-WPFUI { + [OutputType([void])] + param( + [Parameter(Mandatory)] + [string]$TargetGridName + ) + + switch ($TargetGridName) { + "appscategory"{ + # TODO + # Switch UI generation of the sidebar to this function + # $sync.ItemsControl = Initialize-InstallAppArea -TargetElement $TargetGridName + # ... + + # Create and configure a popup for displaying selected apps + $selectedAppsPopup = New-Object Windows.Controls.Primitives.Popup + $selectedAppsPopup.IsOpen = $false + $selectedAppsPopup.PlacementTarget = $sync.WPFselectedAppsButton + $selectedAppsPopup.Placement = [System.Windows.Controls.Primitives.PlacementMode]::Bottom + $selectedAppsPopup.AllowsTransparency = $true + + # Style the popup with a border and background + $selectedAppsBorder = New-Object Windows.Controls.Border + $selectedAppsBorder.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "MainBackgroundColor") + $selectedAppsBorder.SetResourceReference([Windows.Controls.Control]::BorderBrushProperty, "MainForegroundColor") + $selectedAppsBorder.SetResourceReference([Windows.Controls.Control]::BorderThicknessProperty, "ButtonBorderThickness") + $selectedAppsBorder.Width = 200 + $selectedAppsBorder.Padding = 5 + $selectedAppsPopup.Child = $selectedAppsBorder + $sync.selectedAppsPopup = $selectedAppsPopup + + # Add a stack panel inside the popup's border to organize its child elements + $sync.selectedAppsstackPanel = New-Object Windows.Controls.StackPanel + $selectedAppsBorder.Child = $sync.selectedAppsstackPanel + + # Close selectedAppsPopup when mouse leaves both button and selectedAppsPopup + $sync.WPFselectedAppsButton.Add_MouseLeave({ + if (-not $sync.selectedAppsPopup.IsMouseOver) { + $sync.selectedAppsPopup.IsOpen = $false + } + }) + $selectedAppsPopup.Add_MouseLeave({ + if (-not $sync.WPFselectedAppsButton.IsMouseOver) { + $sync.selectedAppsPopup.IsOpen = $false + } + }) + } + "appspanel" { + $sync.ItemsControl = Initialize-InstallAppArea -TargetElement $TargetGridName + Initialize-InstallCategoryAppList -TargetElement $sync.ItemsControl -Apps $sync.configs.applicationsHashtable + } + default { + Write-Output "$TargetGridName not yet implemented" + } + } +} + diff --git a/functions/public/Invoke-WPFButton.ps1 b/functions/public/Invoke-WPFButton.ps1 index 32903468..bad13090 100644 --- a/functions/public/Invoke-WPFButton.ps1 +++ b/functions/public/Invoke-WPFButton.ps1 @@ -26,8 +26,7 @@ function Invoke-WPFButton { "WPFStandard" {Invoke-WPFPresets "Standard" -checkboxfilterpattern "WPFTweak*"} "WPFMinimal" {Invoke-WPFPresets "Minimal" -checkboxfilterpattern "WPFTweak*"} "WPFClearTweaksSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFTweak*"} - "WPFClearInstallSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFInstall*"; Show-OnlyCheckedApps; $sync.wpfselectedfilter.IsChecked = $false} - "WPFSelectedFilter" {Show-OnlyCheckedApps -appKeys $sync.SelectedApps} + "WPFClearInstallSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFInstall*"} "WPFtweaksbutton" {Invoke-WPFtweaksbutton} "WPFOOSUbutton" {Invoke-WPFOOSU} "WPFAddUltPerf" {Invoke-WPFUltimatePerformance -State "Enable"} @@ -62,5 +61,6 @@ function Invoke-WPFButton { "WPFWinUtilInstallPSProfile" {Invoke-WinUtilInstallPSProfile} "WPFWinUtilUninstallPSProfile" {Invoke-WinUtilUninstallPSProfile} "WPFWinUtilSSHServer" {Invoke-WPFSSHServer} + "WPFselectedAppsButton" {$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen} } } diff --git a/functions/public/Invoke-WPFGetInstalled.ps1 b/functions/public/Invoke-WPFGetInstalled.ps1 index 8bb245ec..772315df 100644 --- a/functions/public/Invoke-WPFGetInstalled.ps1 +++ b/functions/public/Invoke-WPFGetInstalled.ps1 @@ -19,15 +19,11 @@ function Invoke-WPFGetInstalled { return } $managerPreference = $sync["ManagerPreference"] - $sync.ItemsControl.Dispatcher.Invoke([action] { - $sync.ItemsControl.Items | ForEach-Object { $_.Visibility = [Windows.Visibility]::Collapsed } - $null = $sync.itemsControl.Items.Add($sync.LoadingLabel) - }) - Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox),("ShowOnlyCheckedApps", ${function:Show-OnlyCheckedApps})) -DebugPreference $DebugPreference -ScriptBlock { + + Invoke-WPFRunspace -ParameterList @(("managerPreference", $managerPreference),("checkbox", $checkbox)) -DebugPreference $DebugPreference -ScriptBlock { param ( [string]$checkbox, - [PackageManagers]$managerPreference, - [scriptblock]$ShowOnlyCheckedApps + [PackageManagers]$managerPreference ) $sync.ProcessRunning = $true $sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state "Indeterminate" }) @@ -49,11 +45,7 @@ function Invoke-WPFGetInstalled { $sync.$checkbox.ischecked = $True } }) - $sync.ItemsControl.Dispatcher.Invoke([action] { - $ShowOnlyCheckedApps.Invoke($sync.SelectedApps) - $sync["WPFSelectedFilter"].IsChecked = $true - $sync.ItemsControl.Items.Remove($sync.LoadingLabel) - }) + Write-Host "Done..." $sync.ProcessRunning = $false $sync.form.Dispatcher.Invoke([action] { Set-WinUtilTaskbaritem -state "None" }) diff --git a/functions/public/Invoke-WPFInstall.ps1 b/functions/public/Invoke-WPFInstall.ps1 index c4187034..70b710f5 100644 --- a/functions/public/Invoke-WPFInstall.ps1 +++ b/functions/public/Invoke-WPFInstall.ps1 @@ -34,7 +34,6 @@ function Invoke-WPFInstall { try { $sync.ProcessRunning = $true - $errorPackages = @() if($packagesWinget.Count -gt 0) { Install-WinUtilWinget Install-WinUtilProgramWinget -Action Install -Programs $packagesWinget diff --git a/functions/public/Invoke-WPFSelectedAppsUpdate.ps1 b/functions/public/Invoke-WPFSelectedAppsUpdate.ps1 index 19e39a4a..7ac63856 100644 --- a/functions/public/Invoke-WPFSelectedAppsUpdate.ps1 +++ b/functions/public/Invoke-WPFSelectedAppsUpdate.ps1 @@ -20,7 +20,7 @@ function Invoke-WPFSelectedAppsUpdate { $selectedAppsButton = $sync.WPFselectedAppsButton # Get the actual Name from the selectedAppLabel inside the Checkbox - $appKey = $checkbox.Parent.Parent.Tag + $appKey = $checkbox.Parent.Tag if ($type -eq "Add") { $sync.selectedApps.Add($appKey) # The List type needs to be specified again, because otherwise Sort-Object will convert the list to a string if there is only a single entry diff --git a/functions/public/Invoke-WPFUIApps.ps1 b/functions/public/Invoke-WPFUIApps.ps1 deleted file mode 100644 index e339277f..00000000 --- a/functions/public/Invoke-WPFUIApps.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -function Invoke-WPFUIApps { - [OutputType([void])] - param( - [Parameter(Mandatory, Position = 0)] - [PSCustomObject[]]$Apps, - [Parameter(Mandatory, Position = 1)] - [string]$TargetGridName - ) - - switch ($TargetGridName) { - "appspanel" { - $dockPanel = Initialize-InstallAppsMainElement -TargetGridName $TargetGridName - $null = Initialize-InstallHeader -TargetElement $dockPanel - $sync.ItemsControl = Initialize-InstallAppArea -TargetElement $dockPanel - Initialize-InstallCategoryAppList -TargetElement $sync.ItemsControl -Apps $Apps - } - default { - Write-Output "$TargetGridName not yet implemented" - } - } -} - diff --git a/functions/public/Invoke-WPFUIElements.ps1 b/functions/public/Invoke-WPFUIElements.ps1 index cfbf662b..09764404 100644 --- a/functions/public/Invoke-WPFUIElements.ps1 +++ b/functions/public/Invoke-WPFUIElements.ps1 @@ -93,12 +93,6 @@ function Invoke-WPFUIElements { # Store application data in an array under the category $organizedData[$entryObject.Panel][$entryObject.Category] += $entryObject - # Only apply the logic for distributing entries across columns if the targetGridName is "appspanel" - if ($targetGridName -eq "appspanel") { - $panelcount = 0 - $entrycount = $configHashtable.Keys.Count + $organizedData["0"].Keys.Count - } - } # Initialize panel count diff --git a/functions/public/Invoke-WPFtweaksbutton.ps1 b/functions/public/Invoke-WPFtweaksbutton.ps1 index d0a53190..ed879b28 100644 --- a/functions/public/Invoke-WPFtweaksbutton.ps1 +++ b/functions/public/Invoke-WPFtweaksbutton.ps1 @@ -25,7 +25,7 @@ function Invoke-WPFtweaksbutton { Write-Debug "Number of tweaks to process: $($Tweaks.Count)" # The leading "," in the ParameterList is nessecary because we only provide one argument and powershell cannot be convinced that we want a nested loop with only one argument otherwise - $tweaksHandle = Invoke-WPFRunspace -ParameterList @(,("tweaks",$tweaks)) -DebugPreference $DebugPreference -ScriptBlock { + Invoke-WPFRunspace -ParameterList @(,("tweaks",$tweaks)) -DebugPreference $DebugPreference -ScriptBlock { param( $tweaks, $DebugPreference diff --git a/scripts/main.ps1 b/scripts/main.ps1 index 337701dd..57ad15b0 100644 --- a/scripts/main.ps1 +++ b/scripts/main.ps1 @@ -117,9 +117,6 @@ $sync.Form.Add_Loaded({ Invoke-WinutilThemeChange -init $true # Load the configuration files -$noimage = "https://images.emojiterra.com/google/noto-emoji/unicode-15/color/512px/1f4e6.png" -$noimage = [Windows.Media.Imaging.BitmapImage]::new([Uri]::new($noimage)) - $sync.configs.applicationsHashtable = @{} $sync.configs.applications.PSObject.Properties | ForEach-Object { $sync.configs.applicationsHashtable[$_.Name] = $_.Value @@ -127,13 +124,9 @@ $sync.configs.applications.PSObject.Properties | ForEach-Object { # Now call the function with the final merged config Invoke-WPFUIElements -configVariable $sync.configs.appnavigation -targetGridName "appscategory" -columncount 1 +Initialize-WPFUI -targetGridName "appscategory" -# Add logic to handle click to the ToggleView Button on the Install Tab -$sync.WPFToggleView.Add_Click({ - $sync.CompactView = -not $sync.CompactView - Update-AppTileProperties -}) -Invoke-WPFUIApps -Apps $sync.configs.applicationsHashtable -targetGridName "appspanel" +Initialize-WPFUI -targetGridName "appspanel" Invoke-WPFUIElements -configVariable $sync.configs.tweaks -targetGridName "tweakspanel" -columncount 2 @@ -211,44 +204,6 @@ Invoke-WPFRunspace -ScriptBlock { # Print the logo Show-CTTLogo -$sync.CompactView = $true -$sync.Form.Resources.AppTileWidth = [double]::NaN -$sync.Form.Resources.AppTileCompactVisibility = [Windows.Visibility]::Visible -$sync.Form.Resources.AppTileFontSize = [double]16 -$sync.Form.Resources.AppTileMargins = [Windows.Thickness]5 -$sync.Form.Resources.AppTileBorderThickness = [Windows.Thickness]0 - -function Update-AppTileProperties { - if ($sync.CompactView -eq $true) { - $sync.Form.Resources.AppTileWidth = [double]::NaN - $sync.Form.Resources.AppTileCompactVisibility = [Windows.Visibility]::Collapsed - $sync.Form.Resources.AppTileFontSize = [double]12 - $sync.Form.Resources.AppTileMargins = [Windows.Thickness]2 - $sync.Form.Resources.AppTileBorderThickness = [Windows.Thickness]0 - } - else { - # On first load, set the AppTileWidth to NaN because the Window dosnt exist yet and there is no ActuaWidth - if ($sync.ItemsControl.ActualWidth -gt 0) { - $sync.Form.Resources.AppTileWidth = $sync.ItemsControl.ActualWidth -20} - else { - $sync.Form.Resources.AppTileWidth = [double]::NaN - } - $sync.Form.Resources.AppTileCompactVisibility = [Windows.Visibility]::Visible - $sync.Form.Resources.AppTileFontSize = [double]16 - $sync.Form.Resources.AppTileMargins = [Windows.Thickness]5 - $sync.Form.Resources.AppTileBorderThickness = [Windows.Thickness]1 - } - if ($sync.SearchBar.Text -eq "") { - Set-CategoryVisibility -Category "*" - } -} -# initialize AppTile properties -Update-AppTileProperties - -# We need to update the app tile properties when the form is resized because to fill a WrapPanel update the width of the elemenmt manually (afaik) -$sync.Form.Add_SizeChanged({ - Update-AppTileProperties -}) # Progress bar in taskbaritem > Set-WinUtilProgressbar $sync["Form"].TaskbarItemInfo = New-Object System.Windows.Shell.TaskbarItemInfo diff --git a/scripts/start.ps1 b/scripts/start.ps1 index e9290cac..37b7978c 100644 --- a/scripts/start.ps1 +++ b/scripts/start.ps1 @@ -40,10 +40,7 @@ $sync.configs = @{} $sync.Buttons = [System.Collections.Generic.List[PSObject]]::new() $sync.ProcessRunning = $false $sync.selectedApps = [System.Collections.Generic.List[string]]::new() -$sync.ShowOnlySeleced = $false $sync.currentTab = "Install" -$sync.CompactView = $true -$sync.ShowOnlySelected = $false $sync.selectedAppsStackPanel $sync.selectedAppsPopup @@ -87,5 +84,5 @@ $logdir = "$env:localappdata\winutil\logs" Start-Transcript -Path "$logdir\winutil_$dateTime.log" -Append -NoClobber | Out-Null # Set PowerShell window title -$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Admin)" +$Host.UI.RawUI.WindowTitle = "WinUtil (Admin)" clear-host diff --git a/xaml/inputXML.xaml b/xaml/inputXML.xaml index 3d525991..d0cb9dbb 100644 --- a/xaml/inputXML.xaml +++ b/xaml/inputXML.xaml @@ -12,19 +12,34 @@ Height="Auto" MaxWidth="1380" MaxHeight="800" - Title="Chris Titus Tech's Windows Utility"> + Title="WinUtil"> + + + + + - - - - - - - -