From 83450aef7b6e11d4f8da1e1d7fd62bfd2ab6d00a Mon Sep 17 00:00:00 2001 From: "Mr.k" Date: Fri, 6 Dec 2024 06:22:33 +0300 Subject: [PATCH] [UI/UX] Update the look & feel of the GUI (#2884) * Add @CodingWonders to the About page for his contribution and determination to make WinUtil (and MicroWin) the best software it can be! * Remove the 'LogoSize' Parameter for About page - Make every sponsor in the sponsors list a link to ChrisTitusTech's Sponsors web link * Change Theme Option name from 'ConfigTabButtonFontSize' to 'ConfigUpdateButtonFontSize' - Lower its value from 16 to 14 * Change Theme Option name from 'FontSizeHeading' to 'HeadingFontSize' for consistency * Update 'LinkForegroundColor' for Light Theme to be lighter which'll make link hover color more noticeable * Update 'Show-CustomDialog' Private Function - Made a lot of theming/styling choices for Custom Dialogs exposed as parameters. - Update the documentation for these new parameters in 'Show-CustomDialog' Function. - Add a Link Hover Effect using 'Add_EVENT' methods (This can be tweaks/tuned-down if needed). - Made use of 'Title' Parameter for 'Show-CustomDialog' Function inside 'scripts/main.ps1' script, to change Dialog Window Title. - Now you can change the Logo Size of WinUtil through the 'LogoSize' Theming Option (the option was there, but not used in implementation). * Preprocessing result * Fix Settings Popup not closing after losing focus - Add trivial null checks for better debugging of UI code * Remove unnecessary whitespace indentation in 'main.ps1' script * Add a new 'Invoke-WPFPopup' Function to Better Handle Showing/Hiding/Toggling of Popups * Improve 'Invoke-WPFPopup' by @MyDrift-user Thanks for the improvements :) --------- Co-authored-by: MyDrift Co-authored-by: Chris Titus --- config/themes.json | 8 +- functions/private/Show-CustomDialog.ps1 | 111 ++++++++++++++------ functions/public/Invoke-WPFPopup.ps1 | 54 ++++++++++ functions/public/Invoke-WPFUIElements.ps1 | 2 +- scripts/main.ps1 | 121 ++++++++-------------- 5 files changed, 182 insertions(+), 114 deletions(-) create mode 100644 functions/public/Invoke-WPFPopup.ps1 diff --git a/config/themes.json b/config/themes.json index 18f55b5649..dd2fb93af6 100644 --- a/config/themes.json +++ b/config/themes.json @@ -2,12 +2,12 @@ "shared":{ "CustomDialogFontSize": "12", "CustomDialogFontSizeHeader": "14", - "CustomDialogIconSize": "25", + "CustomDialogLogoSize": "25", "CustomDialogWidth": "400", "CustomDialogHeight": "200", "FontSize": "12", "FontFamily": "Arial", - "FontSizeHeading": "16", + "HeadingFontSize": "16", "HeaderFontFamily": "Consolas, Monaco", "CheckBoxBulletDecoratorSize": "14", "CheckBoxMargin": "15,0,0,2", @@ -27,7 +27,7 @@ "ButtonFontFamily": "Arial", "ButtonWidth": "200", "ButtonHeight": "25", - "ConfigTabButtonFontSize": "16", + "ConfigUpdateButtonFontSize": "14", "SearchBarWidth": "200", "SearchBarHeight": "26", "SearchBarTextBoxFontSize": "12", @@ -44,7 +44,7 @@ "MainForegroundColor": "#232629", "MainBackgroundColor": "#F7F7F7", "LabelBackgroundColor": "#F7F7F7", - "LinkForegroundColor": "#232629", + "LinkForegroundColor": "#484848", "LinkHoverForegroundColor": "#232629", "ScrollBarBackgroundColor": "#4A4D52", "ScrollBarHoverColor": "#5A5D62", diff --git a/functions/private/Show-CustomDialog.ps1 b/functions/private/Show-CustomDialog.ps1 index 654cb41ec5..e4fa3a00f4 100644 --- a/functions/private/Show-CustomDialog.ps1 +++ b/functions/private/Show-CustomDialog.ps1 @@ -6,6 +6,9 @@ function Show-CustomDialog { .DESCRIPTION This function creates a custom dialog box with the specified message and additional elements such as an image, heading, and an OK button. The dialog box is designed with a green border, rounded corners, and a black background. + .PARAMETER Title + The Title to use for the dialog window's Title Bar, this will not be visible by the user, as window styling is set to None. + .PARAMETER Message The message to be displayed in the dialog box. @@ -16,60 +19,102 @@ function Show-CustomDialog { The height of the custom dialog window. .PARAMETER FontSize - The Font Size for text shown inside the custom dialog window. + The Font Size of message shown inside custom dialog window. .PARAMETER HeaderFontSize - The Font Size for the Header of the custom dialog window. + The Font Size for the Header of custom dialog window. + + .PARAMETER LogoSize + The Size of the Logo used inside the custom dialog window. + + .PARAMETER ForegroundColor + The Foreground Color of dialog window title & message. + + .PARAMETER BackgroundColor + The Background Color of dialog window. + + .PARAMETER BorderColor + The Color for dialog window border. + + .PARAMETER ButtonBackgroundColor + The Background Color for Buttons in dialog window. + + .PARAMETER ButtonForegroundColor + The Foreground Color for Buttons in dialog window. + + .PARAMETER ShadowColor + The Color used when creating the Drop-down Shadow effect for dialog window. + + .PARAMETER LogoColor + The Color of WinUtil Text found next to WinUtil's Logo inside dialog window. + + .PARAMETER LinkForegroundColor + The Foreground Color for Links inside dialog window. - .PARAMETER IconSize - The Size to use for Icon inside the custom dialog window. + .PARAMETER LinkHoverForegroundColor + The Foreground Color for Links when the mouse pointer hovers over them inside dialog window. .PARAMETER EnableScroll A flag indicating whether to enable scrolling if the content exceeds the window size. .EXAMPLE - Show-CustomDialog -Message "This is a custom dialog with a message and an image above." -Width 300 -Height 200 + Show-CustomDialog -Title "My Custom Dialog" -Message "This is a custom dialog with a message and an image above." -Width 300 -Height 200 + + Makes a new Custom Dialog with the title 'My Custom Dialog' and a message 'This is a custom dialog with a message and an image above.', with dimensions of 300 by 200 pixels. + Other styling options are grabbed from '$sync.Form.Resources' global variable. + + .EXAMPLE + $foregroundColor = New-Object System.Windows.Media.SolidColorBrush("#0088e5") + $backgroundColor = New-Object System.Windows.Media.SolidColorBrush("#1e1e1e") + $linkForegroundColor = New-Object System.Windows.Media.SolidColorBrush("#0088e5") + $linkHoverForegroundColor = New-Object System.Windows.Media.SolidColorBrush("#005289") + Show-CustomDialog -Title "My Custom Dialog" -Message "This is a custom dialog with a message and an image above." -Width 300 -Height 200 -ForegroundColor $foregroundColor -BackgroundColor $backgroundColor -LinkForegroundColor $linkForegroundColor -LinkHoverForegroundColor $linkHoverForegroundColor + + Makes a new Custom Dialog with the title 'My Custom Dialog' and a message 'This is a custom dialog with a message and an image above.', with dimensions of 300 by 200 pixels, with a link foreground (and general foreground) colors of '#0088e5', background color of '#1e1e1e', and Link Color on Hover of '005289', all of which are in Hexadecimal (the '#' Symbol is required by SolidColorBrush Constructor). + Other styling options are grabbed from '$sync.Form.Resources' global variable. #> param( + [string]$Title, [string]$Message, [int]$Width = $sync.Form.Resources.CustomDialogWidth, [int]$Height = $sync.Form.Resources.CustomDialogHeight, + + [System.Windows.Media.FontFamily]$FontFamily = $sync.Form.Resources.FontFamily, [int]$FontSize = $sync.Form.Resources.CustomDialogFontSize, [int]$HeaderFontSize = $sync.Form.Resources.CustomDialogFontSizeHeader, - [int]$IconSize = $sync.Form.Resources.CustomDialogLogoSize, + [int]$LogoSize = $sync.Form.Resources.CustomDialogLogoSize, + + [System.Windows.Media.Color]$ShadowColor = "#AAAAAAAA", + [System.Windows.Media.SolidColorBrush]$LogoColor = $sync.Form.Resources.LabelboxForegroundColor, + [System.Windows.Media.SolidColorBrush]$BorderColor = $sync.Form.Resources.BorderColor, + [System.Windows.Media.SolidColorBrush]$ForegroundColor = $sync.Form.Resources.MainForegroundColor, + [System.Windows.Media.SolidColorBrush]$BackgroundColor = $sync.Form.Resources.MainBackgroundColor, + [System.Windows.Media.SolidColorBrush]$ButtonForegroundColor = $sync.Form.Resources.ButtonInstallForegroundColor, + [System.Windows.Media.SolidColorBrush]$ButtonBackgroundColor = $sync.Form.Resources.ButtonInstallBackgroundColor, + [System.Windows.Media.SolidColorBrush]$LinkForegroundColor = $sync.Form.Resources.LinkForegroundColor, + [System.Windows.Media.SolidColorBrush]$LinkHoverForegroundColor = $sync.Form.Resources.LinkHoverForegroundColor, + [bool]$EnableScroll = $false ) - Add-Type -AssemblyName PresentationFramework - - # Define theme colors - $foregroundColor = $sync.Form.Resources.MainForegroundColor - $backgroundColor = $sync.Form.Resources.MainBackgroundColor - $font = New-Object Windows.Media.FontFamily("Consolas") - $borderColor = $sync.Form.Resources.BorderColor # ButtonInstallBackgroundColor - $buttonBackgroundColor = $sync.Form.Resources.ButtonInstallBackgroundColor - $buttonForegroundColor = $sync.Form.Resources.ButtonInstallForegroundColor - $shadowColor = [Windows.Media.ColorConverter]::ConvertFromString("#AAAAAAAA") - $logocolor = $sync.Form.Resources.LabelboxForegroundColor - # Create a custom dialog window $dialog = New-Object Windows.Window - $dialog.Title = "About" + $dialog.Title = $Title $dialog.Height = $Height $dialog.Width = $Width $dialog.Margin = New-Object Windows.Thickness(10) # Add margin to the entire dialog box $dialog.WindowStyle = [Windows.WindowStyle]::None # Remove title bar and window controls $dialog.ResizeMode = [Windows.ResizeMode]::NoResize # Disable resizing $dialog.WindowStartupLocation = [Windows.WindowStartupLocation]::CenterScreen # Center the window - $dialog.Foreground = $foregroundColor - $dialog.Background = $backgroundColor - $dialog.FontFamily = $font + $dialog.Foreground = $ForegroundColor + $dialog.Background = $BackgroundColor + $dialog.FontFamily = $FontFamily $dialog.FontSize = $FontSize # Create a Border for the green edge with rounded corners $border = New-Object Windows.Controls.Border - $border.BorderBrush = $borderColor + $border.BorderBrush = $BorderColor $border.BorderThickness = New-Object Windows.Thickness(1) # Adjust border thickness as needed $border.CornerRadius = New-Object Windows.CornerRadius(10) # Adjust the radius for rounded corners @@ -89,7 +134,7 @@ function Show-CustomDialog { $grid = New-Object Windows.Controls.Grid $border.Child = $grid - # Add the following line to show gridlines + # Uncomment the following line to show gridlines #$grid.ShowGridLines = $true # Add the following line to set the background color of the grid @@ -102,7 +147,6 @@ function Show-CustomDialog { $border.HorizontalAlignment = [Windows.HorizontalAlignment]::Stretch $border.VerticalAlignment = [Windows.VerticalAlignment]::Stretch - # Set up Row Definitions $row0 = New-Object Windows.Controls.RowDefinition $row0.Height = [Windows.GridLength]::Auto @@ -129,17 +173,18 @@ function Show-CustomDialog { [Windows.Controls.Grid]::SetRow($stackPanel, 0) # Set the row to the second row (0-based index) # Add SVG path to the stack panel - $stackPanel.Children.Add((Invoke-WinUtilAssets -Type "logo" -Size 25)) + $stackPanel.Children.Add((Invoke-WinUtilAssets -Type "logo" -Size $LogoSize)) # Add "Winutil" text $winutilTextBlock = New-Object Windows.Controls.TextBlock $winutilTextBlock.Text = "Winutil" $winutilTextBlock.FontSize = $HeaderFontSize - $winutilTextBlock.Foreground = $logocolor + $winutilTextBlock.Foreground = $LogoColor $winutilTextBlock.Margin = New-Object Windows.Thickness(10, 10, 10, 5) # Add margins around the text block $stackPanel.Children.Add($winutilTextBlock) # Add TextBlock for information with text wrapping and margins $messageTextBlock = New-Object Windows.Controls.TextBlock + $messageTextBlock.FontSize = $FontSize $messageTextBlock.TextWrapping = [Windows.TextWrapping]::Wrap # Enable text wrapping $messageTextBlock.HorizontalAlignment = [Windows.HorizontalAlignment]::Left $messageTextBlock.VerticalAlignment = [Windows.VerticalAlignment]::Top @@ -162,7 +207,7 @@ function Show-CustomDialog { $hyperlink.NavigateUri = New-Object System.Uri($match.Groups[1].Value) $hyperlink.Inlines.Add($match.Groups[2].Value) $hyperlink.TextDecorations = [Windows.TextDecorations]::None # Remove underline - $hyperlink.Foreground = $sync.Form.Resources.LinkForegroundColor + $hyperlink.Foreground = $LinkForegroundColor $hyperlink.Add_Click({ param($sender, $args) @@ -170,11 +215,15 @@ function Show-CustomDialog { }) $hyperlink.Add_MouseEnter({ param($sender, $args) - $sender.Foreground = $sync.Form.Resources.LinkHoverForegroundColor + $sender.Foreground = $LinkHoverForegroundColor + $sender.FontSize = ($FontSize + ($FontSize / 4)) + $sender.FontWeight = "SemiBold" }) $hyperlink.Add_MouseLeave({ param($sender, $args) - $sender.Foreground = $sync.Form.Resources.LinkForegroundColor + $sender.Foreground = $LinkForegroundColor + $sender.FontSize = $FontSize + $sender.FontWeight = "Normal" }) $messageTextBlock.Inlines.Add($hyperlink) @@ -218,7 +267,7 @@ function Show-CustomDialog { $okButton.Margin = New-Object Windows.Thickness(0, 0, 0, 10) $okButton.Background = $buttonBackgroundColor $okButton.Foreground = $buttonForegroundColor - $okButton.BorderBrush = $borderColor + $okButton.BorderBrush = $BorderColor $okButton.Add_Click({ $dialog.Close() }) diff --git a/functions/public/Invoke-WPFPopup.ps1 b/functions/public/Invoke-WPFPopup.ps1 new file mode 100644 index 0000000000..46cdc2159c --- /dev/null +++ b/functions/public/Invoke-WPFPopup.ps1 @@ -0,0 +1,54 @@ +function Invoke-WPFPopup { + param ( + [ValidateSet("Show", "Hide", "Toggle")] + [string]$Action = "", + + [string[]]$Popups = @(), + + [ValidateScript({ + $invalid = $_.GetEnumerator() | Where-Object { $_.Value -notin @("Show", "Hide", "Toggle") } + if ($invalid) { + throw "Found invalid Popup-Action pair(s): " + ($invalid | ForEach-Object { "$($_.Key) = $($_.Value)" } -join "; ") + } + $true + })] + [hashtable]$PopupActionTable = @{} + ) + + if (-not $PopupActionTable.Count -and (-not $Action -or -not $Popups.Count)) { + throw "Provide either 'PopupActionTable' or both 'Action' and 'Popups'." + } + + if ($PopupActionTable.Count -and ($Action -or $Popups.Count)) { + throw "Use 'PopupActionTable' on its own, or 'Action' with 'Popups'." + } + + # Collect popups and actions + $PopupsToProcess = if ($PopupActionTable.Count) { + $PopupActionTable.GetEnumerator() | ForEach-Object { [PSCustomObject]@{ Name = "$($_.Key)Popup"; Action = $_.Value } } + } else { + $Popups | ForEach-Object { [PSCustomObject]@{ Name = "$_`Popup"; Action = $Action } } + } + + $PopupsNotFound = @() + + # Apply actions + foreach ($popupEntry in $PopupsToProcess) { + $popupName = $popupEntry.Name + + if (-not $sync.$popupName) { + $PopupsNotFound += $popupName + continue + } + + $sync.$popupName.IsOpen = switch ($popupEntry.Action) { + "Show" { $true } + "Hide" { $false } + "Toggle" { -not $sync.$popupName.IsOpen } + } + } + + if ($PopupsNotFound.Count -gt 0) { + throw "Could not find the following popups: $($PopupsNotFound -join ', ')" + } +} diff --git a/functions/public/Invoke-WPFUIElements.ps1 b/functions/public/Invoke-WPFUIElements.ps1 index cdcf7142af..1eee508dcd 100644 --- a/functions/public/Invoke-WPFUIElements.ps1 +++ b/functions/public/Invoke-WPFUIElements.ps1 @@ -153,7 +153,7 @@ function Invoke-WPFUIElements { $label = New-Object Windows.Controls.Label $label.Content = $category -replace ".*__", "" - $label.FontSize = $theme.FontSizeHeading + $label.FontSize = $theme.HeadingFontSize $label.FontFamily = $theme.HeaderFontFamily $stackPanel.Children.Add($label) | Out-Null diff --git a/scripts/main.ps1 b/scripts/main.ps1 index a2f351cdf2..785f55e746 100644 --- a/scripts/main.ps1 +++ b/scripts/main.ps1 @@ -31,23 +31,20 @@ $sync.runspace.Open() # Create classes for different exceptions - class WingetFailedInstall : Exception { - [string]$additionalData - - WingetFailedInstall($Message) : base($Message) {} - } - - class ChocoFailedInstall : Exception { - [string]$additionalData - - ChocoFailedInstall($Message) : base($Message) {} - } +class WingetFailedInstall : Exception { + [string]$additionalData + WingetFailedInstall($Message) : base($Message) {} +} - class GenericException : Exception { - [string]$additionalData +class ChocoFailedInstall : Exception { + [string]$additionalData + ChocoFailedInstall($Message) : base($Message) {} +} - GenericException($Message) : base($Message) {} - } +class GenericException : Exception { + [string]$additionalData + GenericException($Message) : base($Message) {} +} $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^@christitustech Runspace : @DeveloperDurp -MicroWin : @KonTy +MicroWin : @KonTy, @CodingWonders GitHub : ChrisTitusTech/winutil Version : $($sync.version) "@ - - Show-CustomDialog -Message $authorInfo -LogoSize $LogoSize + Show-CustomDialog -Title "About" -Message $authorInfo }) - $sync["SponsorMenuItem"].Add_Click({ - # Handle Export menu item click Write-Debug "Sponsors clicked" - $sync["SettingsPopup"].IsOpen = $false + Invoke-WPFPopup -Action "Hide" -Popups @("Settings") + $authorInfo = @" Current sponsors for ChrisTitusTech: "@ $authorInfo += "`n" try { - # Call the function to get the sponsors $sponsors = Invoke-WinUtilSponsors - - # Append the sponsors to the authorInfo - $sponsors | ForEach-Object { $authorInfo += "$_`n" } + foreach ($sponsor in $sponsors) { + $authorInfo += "$sponsor`n" + } } catch { $authorInfo += "An error occurred while fetching or processing the sponsors: $_`n" } - - Show-CustomDialog -Message $authorInfo -EnableScroll $true + Show-CustomDialog -Title "Sponsors" -Message $authorInfo -EnableScroll $true }) $sync["Form"].ShowDialog() | out-null