mirror of
https://github.com/ChrisTitusTech/winutil.git
synced 2025-07-01 18:42:35 -05:00
Merge branch 'main' into berlintay-patch-1
This commit is contained in:
50
functions/private/Add-SelectedAppsMenuItem.ps1
Normal file
50
functions/private/Add-SelectedAppsMenuItem.ps1
Normal file
@ -0,0 +1,50 @@
|
||||
function Add-SelectedAppsMenuItem {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This is a helper function that generates and adds the Menu Items to the Selected Apps Popup.
|
||||
|
||||
.Parameter name
|
||||
The actual Name of an App like "Chrome" or "Brave"
|
||||
This name is contained in the "Content" property inside the applications.json
|
||||
.PARAMETER key
|
||||
The key which identifies an app object in applications.json
|
||||
For Chrome this would be "WPFInstallchrome" because "WPFInstall" is prepended automatically for each key in applications.json
|
||||
#>
|
||||
|
||||
param ([string]$name, [string]$key)
|
||||
|
||||
$selectedAppGrid = New-Object Windows.Controls.Grid
|
||||
|
||||
$selectedAppGrid.ColumnDefinitions.Add((New-Object System.Windows.Controls.ColumnDefinition -Property @{Width = "*"}))
|
||||
$selectedAppGrid.ColumnDefinitions.Add((New-Object System.Windows.Controls.ColumnDefinition -Property @{Width = "30"}))
|
||||
|
||||
# Sets the name to the Content as well as the Tooltip, because the parent Popup Border has a fixed width and text could "overflow".
|
||||
# With the tooltip, you can still read the whole entry on hover
|
||||
$selectedAppLabel = New-Object Windows.Controls.Label
|
||||
$selectedAppLabel.Content = $name
|
||||
$selectedAppLabel.ToolTip = $name
|
||||
$selectedAppLabel.HorizontalAlignment = "Left"
|
||||
$selectedAppLabel.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor")
|
||||
[System.Windows.Controls.Grid]::SetColumn($selectedAppLabel, 0)
|
||||
$selectedAppGrid.Children.Add($selectedAppLabel)
|
||||
|
||||
$selectedAppRemoveButton = New-Object Windows.Controls.Button
|
||||
$selectedAppRemoveButton.FontFamily = "Segoe MDL2 Assets"
|
||||
$selectedAppRemoveButton.Content = [string]([char]0xE711)
|
||||
$selectedAppRemoveButton.HorizontalAlignment = "Center"
|
||||
$selectedAppRemoveButton.Tag = $key
|
||||
$selectedAppRemoveButton.ToolTip = "Remove the App from Selection"
|
||||
$selectedAppRemoveButton.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor")
|
||||
$selectedAppRemoveButton.SetResourceReference([Windows.Controls.Control]::StyleProperty, "HoverButtonStyle")
|
||||
|
||||
# Highlight the Remove icon on Hover
|
||||
$selectedAppRemoveButton.Add_MouseEnter({ $this.Foreground = "Red" })
|
||||
$selectedAppRemoveButton.Add_MouseLeave({ $this.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor") })
|
||||
$selectedAppRemoveButton.Add_Click({
|
||||
$sync.($this.Tag).isChecked = $false # On click of the remove button, we only have to uncheck the corresponding checkbox. This will kick of all neccessary changes to update the UI
|
||||
})
|
||||
[System.Windows.Controls.Grid]::SetColumn($selectedAppRemoveButton, 1)
|
||||
$selectedAppGrid.Children.Add($selectedAppRemoveButton)
|
||||
# Add new Element to Popup
|
||||
$sync.selectedAppsstackPanel.Children.Add($selectedAppGrid)
|
||||
}
|
35
functions/private/Find-AppsByNameOrDescription.ps1
Normal file
35
functions/private/Find-AppsByNameOrDescription.ps1
Normal file
@ -0,0 +1,35 @@
|
||||
function Find-AppsByNameOrDescription {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Searches through the Apps on the Install Tab and hides all entries that do not match the string
|
||||
|
||||
.PARAMETER SearchString
|
||||
The string to be searched for
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$SearchString = ""
|
||||
)
|
||||
# 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 {
|
||||
# Hide all CategoryWrapPanel and ToggleButton
|
||||
$_.Visibility = [Windows.Visibility]::Collapsed
|
||||
if ($_.Tag -like "CategoryWrapPanel_*") {
|
||||
# Search for Apps that match the search string
|
||||
$_.Children | Foreach-Object {
|
||||
if ($sync.configs.applicationsHashtable.$($_.Tag).Content -like "*$SearchString*") {
|
||||
# Show the App and the parent CategoryWrapPanel if the string is found
|
||||
$_.Visibility = [Windows.Visibility]::Visible
|
||||
$_.parent.Visibility = [Windows.Visibility]::Visible
|
||||
}
|
||||
else {
|
||||
$_.Visibility = [Windows.Visibility]::Collapsed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
function Get-WinUtilWingetLatest {
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Uses GitHub API to check for the latest release of Winget.
|
||||
.DESCRIPTION
|
||||
This function first attempts to update WinGet using winget itself, then falls back to manual installation if needed.
|
||||
#>
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
$InformationPreference = 'Continue'
|
||||
|
||||
try {
|
||||
$wingetCmd = Get-Command winget -ErrorAction Stop
|
||||
Write-Information "Attempting to update WinGet using WinGet..."
|
||||
$result = Start-Process -FilePath "`"$($wingetCmd.Source)`"" -ArgumentList "install -e --accept-source-agreements --accept-package-agreements Microsoft.AppInstaller" -Wait -NoNewWindow -PassThru
|
||||
if ($result.ExitCode -ne 0) {
|
||||
throw "WinGet update failed with exit code: $($result.ExitCode)"
|
||||
}
|
||||
return $true
|
||||
}
|
||||
catch {
|
||||
Write-Information "WinGet not found or update failed. Attempting to install from Microsoft Store..."
|
||||
try {
|
||||
# Try to close any running WinGet processes
|
||||
Get-Process -Name "DesktopAppInstaller", "winget" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Write-Information "Stopping running WinGet process..."
|
||||
$_.Kill()
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
|
||||
# Try to load Windows Runtime assemblies more reliably
|
||||
$null = [System.Runtime.WindowsRuntime.WindowsRuntimeSystemExtensions]
|
||||
Add-Type -AssemblyName System.Runtime.WindowsRuntime
|
||||
|
||||
# Load required assemblies from Windows SDK
|
||||
$null = @(
|
||||
[Windows.Management.Deployment.PackageManager, Windows.Management.Deployment, ContentType = WindowsRuntime]
|
||||
[Windows.Foundation.Uri, Windows.Foundation, ContentType = WindowsRuntime]
|
||||
[Windows.Management.Deployment.DeploymentOptions, Windows.Management.Deployment, ContentType = WindowsRuntime]
|
||||
)
|
||||
|
||||
# Initialize PackageManager
|
||||
$packageManager = New-Object Windows.Management.Deployment.PackageManager
|
||||
|
||||
# Rest of the Microsoft Store installation logic
|
||||
$appxPackage = "https://aka.ms/getwinget"
|
||||
$uri = New-Object Windows.Foundation.Uri($appxPackage)
|
||||
$deploymentOperation = $packageManager.AddPackageAsync($uri, $null, "Add")
|
||||
|
||||
# Add timeout check for deployment operation
|
||||
$timeout = 300
|
||||
$timer = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
|
||||
while ($deploymentOperation.Status -eq 0) {
|
||||
if ($timer.Elapsed.TotalSeconds -gt $timeout) {
|
||||
throw "Installation timed out after $timeout seconds"
|
||||
}
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
|
||||
if ($deploymentOperation.Status -eq 1) {
|
||||
Write-Information "Successfully installed WinGet from Microsoft Store"
|
||||
return $true
|
||||
} else {
|
||||
throw "Installation failed with status: $($deploymentOperation.Status)"
|
||||
}
|
||||
}
|
||||
catch [System.Management.Automation.RuntimeException] {
|
||||
Write-Information "Windows Runtime components not available. Attempting manual download..."
|
||||
try {
|
||||
# Try to close any running WinGet processes
|
||||
Get-Process -Name "DesktopAppInstaller", "winget" -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Write-Information "Stopping running WinGet process..."
|
||||
$_.Kill()
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
|
||||
# Fallback to direct download from GitHub
|
||||
$apiUrl = "https://api.github.com/repos/microsoft/winget-cli/releases/latest"
|
||||
$release = Invoke-RestMethod -Uri $apiUrl
|
||||
$msixBundleUrl = ($release.assets | Where-Object { $_.name -like "*.msixbundle" }).browser_download_url
|
||||
|
||||
$tempFile = Join-Path $env:TEMP "Microsoft.DesktopAppInstaller.msixbundle"
|
||||
Invoke-WebRequest -Uri $msixBundleUrl -OutFile $tempFile
|
||||
|
||||
Add-AppxPackage -Path $tempFile -ErrorAction Stop
|
||||
Remove-Item $tempFile -Force
|
||||
|
||||
Write-Information "Successfully installed WinGet from GitHub release"
|
||||
return $true
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to install WinGet: $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to install WinGet: $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
}
|
197
functions/private/Initalize-InstallAppEntry.ps1
Normal file
197
functions/private/Initalize-InstallAppEntry.ps1
Normal file
@ -0,0 +1,197 @@
|
||||
function Initialize-InstallAppEntry {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates the app entry to be placed on the isntall tab for a given app
|
||||
Used to as part of the Install Tab UI generation
|
||||
.PARAMETER TargetElement
|
||||
The Element into which the Apps should be placed
|
||||
.PARAMETER AppKey
|
||||
The Key of the app inside the $sync.configs.applicationsHashtable
|
||||
#>
|
||||
param(
|
||||
[Windows.Controls.WrapPanel]$TargetElement,
|
||||
$AppKey
|
||||
)
|
||||
$App = $sync.configs.applicationsHashtable.$AppKey
|
||||
# Create the outer Border for the application type
|
||||
$border = New-Object Windows.Controls.Border
|
||||
$border.BorderBrush = [Windows.Media.Brushes]::Gray
|
||||
$border.SetResourceReference([Windows.Controls.Control]::BorderThicknessProperty, "AppTileBorderThickness")
|
||||
$border.CornerRadius = 5
|
||||
$border.SetResourceReference([Windows.Controls.Control]::PaddingProperty, "AppTileMargins")
|
||||
$border.SetResourceReference([Windows.Controls.Control]::WidthProperty, "AppTileWidth")
|
||||
$border.VerticalAlignment = "Top"
|
||||
$border.SetResourceReference([Windows.Controls.Control]::MarginProperty, "AppTileMargins")
|
||||
$border.Cursor = [System.Windows.Input.Cursors]::Hand
|
||||
$border.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor")
|
||||
$border.Tag = $Appkey
|
||||
$border.ToolTip = $App.description
|
||||
$border.Add_MouseUp({
|
||||
$childCheckbox = ($this.Child.Children | Where-Object {$_.Template.TargetType -eq [System.Windows.Controls.Checkbox]})[0]
|
||||
$childCheckBox.isChecked = -not $childCheckbox.IsChecked
|
||||
})
|
||||
$border.Add_MouseEnter({
|
||||
if (($sync.$($this.Tag).IsChecked) -eq $false) {
|
||||
$this.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallHighlightedColor")
|
||||
}
|
||||
})
|
||||
$border.Add_MouseLeave({
|
||||
if (($sync.$($this.Tag).IsChecked) -eq $false) {
|
||||
$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.Background = "Transparent"
|
||||
$checkBox.HorizontalAlignment = "Left"
|
||||
$checkBox.VerticalAlignment = "Center"
|
||||
$checkBox.SetResourceReference([Windows.Controls.Control]::MarginProperty, "AppTileMargins")
|
||||
$checkBox.SetResourceReference([Windows.Controls.Control]::StyleProperty, "CollapsedCheckBoxStyle")
|
||||
$checkbox.Add_Checked({
|
||||
Invoke-WPFSelectedAppsUpdate -type "Add" -checkbox $this
|
||||
$borderElement = $this.Parent.Parent
|
||||
$borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallSelectedColor")
|
||||
})
|
||||
|
||||
$checkbox.Add_Unchecked({
|
||||
Invoke-WPFSelectedAppsUpdate -type "Remove" -checkbox $this
|
||||
$borderElement = $this.Parent.Parent
|
||||
$borderElement.SetResourceReference([Windows.Controls.Control]::BackgroundProperty, "AppInstallUnselectedColor")
|
||||
})
|
||||
$sync.$($checkBox.Name) = $checkBox
|
||||
# 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.Width = 40
|
||||
$image.Height = 40
|
||||
$image.Margin = New-Object Windows.Thickness(0, 0, 10, 0)
|
||||
$image.Source = $noimage # Ensure $noimage is defined in your script
|
||||
|
||||
# Clip the image corners
|
||||
$image.Clip = New-Object Windows.Media.RectangleGeometry
|
||||
$image.Clip.Rect = New-Object Windows.Rect(0, 0, $image.Width, $image.Height)
|
||||
$image.Clip.RadiusX = 5
|
||||
$image.Clip.RadiusY = 5
|
||||
$image.SetResourceReference([Windows.Controls.Control]::VisibilityProperty, "AppTileCompactVisibility")
|
||||
|
||||
$imageAndNamePanel.Children.Add($image) | Out-Null
|
||||
|
||||
# Create the TextBlock for the application name
|
||||
$appName = New-Object Windows.Controls.TextBlock
|
||||
$appName.Text = $App.Content
|
||||
$appName.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "AppTileFontSize")
|
||||
$appName.FontWeight = [Windows.FontWeights]::Bold
|
||||
$appName.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor")
|
||||
$appName.VerticalAlignment = "Center"
|
||||
$appName.SetResourceReference([Windows.Controls.Control]::MarginProperty, "AppTileMargins")
|
||||
$appName.Background = "Transparent"
|
||||
$imageAndNamePanel.Children.Add($appName) | Out-Null
|
||||
|
||||
# Add the image and name panel to the Checkbox
|
||||
$checkBox.Content = $imageAndNamePanel
|
||||
|
||||
# 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.Orientation = "Horizontal"
|
||||
$buttonPanel.HorizontalAlignment = "Right"
|
||||
$buttonPanel.VerticalAlignment = "Center"
|
||||
$buttonPanel.SetResourceReference([Windows.Controls.Control]::MarginProperty, "AppTileMargins")
|
||||
$buttonPanel.SetResourceReference([Windows.Controls.Control]::VisibilityProperty, "AppTileCompactVisibility")
|
||||
[Windows.Controls.DockPanel]::SetDock($buttonPanel, [Windows.Controls.Dock]::Right)
|
||||
|
||||
# Create the "Install" button
|
||||
$installButton = New-Object Windows.Controls.Button
|
||||
$installButton.Width = 45
|
||||
$installButton.Height = 35
|
||||
$installButton.Margin = New-Object Windows.Thickness(0, 0, 10, 0)
|
||||
|
||||
$installIcon = New-Object Windows.Controls.TextBlock
|
||||
$installIcon.Text = [char]0xE118 # Install Icon
|
||||
$installIcon.FontFamily = "Segoe MDL2 Assets"
|
||||
$installIcon.FontSize = 20
|
||||
$installIcon.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor")
|
||||
$installIcon.Background = "Transparent"
|
||||
$installIcon.HorizontalAlignment = "Center"
|
||||
$installIcon.VerticalAlignment = "Center"
|
||||
|
||||
$installButton.Content = $installIcon
|
||||
$installButton.ToolTip = "Install or Upgrade the application"
|
||||
$buttonPanel.Children.Add($installButton) | Out-Null
|
||||
|
||||
# Add Click event for the "Install" button
|
||||
$installButton.Add_Click({
|
||||
$appKey = $this.Parent.Parent.Parent.Tag
|
||||
$appObject = $sync.configs.applicationsHashtable.$appKey
|
||||
Invoke-WPFInstall -PackagesToInstall $appObject
|
||||
})
|
||||
|
||||
# Create the "Uninstall" button
|
||||
$uninstallButton = New-Object Windows.Controls.Button
|
||||
$uninstallButton.Width = 45
|
||||
$uninstallButton.Height = 35
|
||||
|
||||
$uninstallIcon = New-Object Windows.Controls.TextBlock
|
||||
$uninstallIcon.Text = [char]0xE74D # Uninstall Icon
|
||||
$uninstallIcon.FontFamily = "Segoe MDL2 Assets"
|
||||
$uninstallIcon.FontSize = 20
|
||||
$uninstallIcon.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor")
|
||||
$uninstallIcon.Background = "Transparent"
|
||||
$uninstallIcon.HorizontalAlignment = "Center"
|
||||
$uninstallIcon.VerticalAlignment = "Center"
|
||||
|
||||
$uninstallButton.Content = $uninstallIcon
|
||||
$buttonPanel.Children.Add($uninstallButton) | Out-Null
|
||||
|
||||
$uninstallButton.ToolTip = "Uninstall the application"
|
||||
$uninstallButton.Add_Click({
|
||||
$appKey = $this.Parent.Parent.Parent.Tag
|
||||
$appObject = $sync.configs.applicationsHashtable.$appKey
|
||||
Invoke-WPFUnInstall -PackagesToUninstall $appObject
|
||||
})
|
||||
|
||||
# Create the "Info" button
|
||||
$infoButton = New-Object Windows.Controls.Button
|
||||
$infoButton.Width = 45
|
||||
$infoButton.Height = 35
|
||||
$infoButton.Margin = New-Object Windows.Thickness(10, 0, 0, 0)
|
||||
|
||||
$infoIcon = New-Object Windows.Controls.TextBlock
|
||||
$infoIcon.Text = [char]0xE946 # Info Icon
|
||||
$infoIcon.FontFamily = "Segoe MDL2 Assets"
|
||||
$infoIcon.FontSize = 20
|
||||
$infoIcon.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor")
|
||||
$infoIcon.Background = "Transparent"
|
||||
$infoIcon.HorizontalAlignment = "Center"
|
||||
$infoIcon.VerticalAlignment = "Center"
|
||||
|
||||
$infoButton.Content = $infoIcon
|
||||
$infoButton.ToolTip = "Open the application's website in your default browser"
|
||||
$buttonPanel.Children.Add($infoButton) | Out-Null
|
||||
|
||||
$infoButton.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 the border to the corresponding Category
|
||||
$TargetElement.Children.Add($border) | Out-Null
|
||||
}
|
36
functions/private/Initialize-InstallAppArea.ps1
Normal file
36
functions/private/Initialize-InstallAppArea.ps1
Normal file
@ -0,0 +1,36 @@
|
||||
function Initialize-InstallAppArea {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates a [Windows.Controls.ScrollViewer] containing a [Windows.Controls.ItemsControl] which is setup to use Virtualization to only load the visible elements for performance reasons.
|
||||
This is used as the parent object for all category and app entries on the install tab
|
||||
Used to as part of the Install Tab UI generation
|
||||
|
||||
.PARAMETER TargetElement
|
||||
The element to which the AppArea shoud be added
|
||||
|
||||
#>
|
||||
param($TargetElement)
|
||||
$scrollViewer = New-Object Windows.Controls.ScrollViewer
|
||||
$scrollViewer.VerticalScrollBarVisibility = 'Auto'
|
||||
$scrollViewer.HorizontalAlignment = 'Stretch'
|
||||
$scrollViewer.VerticalAlignment = 'Stretch'
|
||||
$scrollViewer.CanContentScroll = $true
|
||||
|
||||
$itemsControl = New-Object Windows.Controls.ItemsControl
|
||||
$itemsControl.HorizontalAlignment = 'Stretch'
|
||||
$itemsControl.VerticalAlignment = 'Stretch'
|
||||
|
||||
$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)
|
||||
return $itemsControl
|
||||
}
|
28
functions/private/Initialize-InstallAppsMainElement.ps1
Normal file
28
functions/private/Initialize-InstallAppsMainElement.ps1
Normal file
@ -0,0 +1,28 @@
|
||||
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
|
||||
}
|
72
functions/private/Initialize-InstallCategoryAppList.ps1
Normal file
72
functions/private/Initialize-InstallCategoryAppList.ps1
Normal file
@ -0,0 +1,72 @@
|
||||
function Initialize-InstallCategoryAppList {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Clears the Target Element and sets up a "Loading" message. This is done, because loading of all apps can take a bit of time in some scenarios
|
||||
Iterates through all Categories and Apps and adds them to the UI
|
||||
Used to as part of the Install Tab UI generation
|
||||
.PARAMETER TargetElement
|
||||
The Element into which the Categories and Apps should be placed
|
||||
.PARAMETER Apps
|
||||
The Hashtable of Apps to be added to the UI
|
||||
The Categories are also extracted from the Apps Hashtable
|
||||
|
||||
#>
|
||||
param(
|
||||
$TargetElement,
|
||||
$Apps
|
||||
)
|
||||
function Add-Category {
|
||||
param(
|
||||
[string]$Category,
|
||||
[Windows.Controls.ItemsControl]$TargetElement
|
||||
)
|
||||
|
||||
$toggleButton = New-Object Windows.Controls.Primitives.ToggleButton
|
||||
$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
|
||||
})
|
||||
$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, "FontSizeHeading")
|
||||
$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
|
||||
$categories = $Apps.Values | Select-Object -ExpandProperty category -Unique | Sort-Object
|
||||
foreach ($category in $categories) {
|
||||
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)
|
||||
$Apps.Keys | Where-Object { $Apps.$_.Category -eq $category } | Sort-Object | ForEach-Object {
|
||||
Initialize-InstallAppEntry -TargetElement $wrapPanel -AppKey $_
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
89
functions/private/Initialize-InstallHeader.ps1
Normal file
89
functions/private/Initialize-InstallHeader.ps1
Normal file
@ -0,0 +1,89 @@
|
||||
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"}
|
||||
)
|
||||
|
||||
foreach ($config in $buttonConfigs) {
|
||||
$button = New-WPFButton -Name $config.Name -Content $config.Content
|
||||
$null = $wrapPanelTop.Children.Add($button)
|
||||
$sync[$config.Name] = $button
|
||||
}
|
||||
|
||||
$selectedAppsButton = New-Object Windows.Controls.Button
|
||||
$selectedAppsButton.Name = "WPFselectedAppsButton"
|
||||
$selectedAppsButton.Content = "Selected Apps: 0"
|
||||
$selectedAppsButton.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "FontSizeHeading")
|
||||
$selectedAppsButton.SetResourceReference([Windows.Controls.Control]::MarginProperty, "TabContentMargin")
|
||||
$selectedAppsButton.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor")
|
||||
$selectedAppsButton.HorizontalAlignment = "Center"
|
||||
$selectedAppsButton.VerticalAlignment = "Center"
|
||||
|
||||
$selectedAppsPopup = New-Object Windows.Controls.Primitives.Popup
|
||||
$selectedAppsPopup.IsOpen = $false
|
||||
$selectedAppsPopup.PlacementTarget = $selectedAppsButton
|
||||
$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
|
||||
$selectedAppsButton.Add_Click({
|
||||
$sync.selectedAppsPopup.IsOpen = -not $sync.selectedAppsPopup.IsOpen
|
||||
})
|
||||
# Close selectedAppsPopup when mouse leaves both button and selectedAppsPopup
|
||||
$selectedAppsButton.Add_MouseLeave({
|
||||
if (-not $sync.selectedAppsPopup.IsMouseOver) {
|
||||
$sync.selectedAppsPopup.IsOpen = $false
|
||||
}
|
||||
})
|
||||
$selectedAppsPopup.Add_MouseLeave({
|
||||
if (-not $selectedAppsButton.IsMouseOver) {
|
||||
$sync.selectedAppsPopup.IsOpen = $false
|
||||
}
|
||||
})
|
||||
|
||||
$null = $wrapPanelTop.Children.Add($selectedAppsButton)
|
||||
$sync.$($selectedAppsButton.Name) = $selectedAppsButton
|
||||
|
||||
[Windows.Controls.DockPanel]::SetDock($wrapPanelTop, [Windows.Controls.Dock]::Top)
|
||||
$null = $TargetElement.Children.Add($wrapPanelTop)
|
||||
}
|
@ -33,18 +33,44 @@ function Install-WinUtilWinget {
|
||||
return
|
||||
}
|
||||
|
||||
# Install Winget via GitHub method.
|
||||
# Used part of my own script with some modification: ruxunderscore/windows-initialization
|
||||
Write-Host "Downloading Winget and License File`r"
|
||||
Get-WinUtilWingetLatest
|
||||
Write-Host "Enabling NuGet and Module..."
|
||||
Install-PackageProvider -Name NuGet -Force
|
||||
Install-Module -Name Microsoft.WinGet.Client -Force
|
||||
# Winget only needs a refresh of the environment variables to be used.
|
||||
Write-Output "Refreshing Environment Variables...`n"
|
||||
$ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||
} catch {
|
||||
Write-Error "Failed to install Winget: $($_.Exception.Message)"
|
||||
}
|
||||
Write-Host "Attempting to install/update Winget`r"
|
||||
try {
|
||||
$wingetCmd = Get-Command winget -ErrorAction Stop
|
||||
Write-Information "Attempting to update WinGet using WinGet..."
|
||||
$result = Start-Process -FilePath "`"$($wingetCmd.Source)`"" -ArgumentList "install -e --accept-source-agreements --accept-package-agreements Microsoft.AppInstaller" -Wait -NoNewWindow -PassThru
|
||||
if ($result.ExitCode -ne 0) {
|
||||
throw "WinGet update failed with exit code: $($result.ExitCode)"
|
||||
}
|
||||
Write-Output "Refreshing Environment Variables...`n"
|
||||
$ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||
return
|
||||
} catch {
|
||||
Write-Information "WinGet not found or update failed. Attempting to install from Microsoft Store..."
|
||||
}
|
||||
try {
|
||||
Write-Host "Attempting to repair WinGet using Repair-WinGetPackageManager..." -ForegroundColor Yellow
|
||||
|
||||
# Check if Windows version supports Repair-WinGetPackageManager (24H2 and above)
|
||||
if ([System.Environment]::OSVersion.Version.Build -ge 26100) {
|
||||
Repair-WinGetPackageManager -Force -Latest -Verbose
|
||||
# Verify if repair was successful
|
||||
$wingetCmd = Get-Command winget -ErrorAction Stop
|
||||
Write-Host "WinGet repair successful!" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "Repair-WinGetPackageManager is only available on Windows 24H2 and above. Your version doesn't support this method." -ForegroundColor Yellow
|
||||
throw "Windows version not supported for repair method"
|
||||
}
|
||||
|
||||
Write-Output "Refreshing Environment Variables...`n"
|
||||
$ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||
return
|
||||
|
||||
} catch {
|
||||
Write-Error "All installation methods failed. Unable to install WinGet."
|
||||
throw
|
||||
}
|
||||
} catch {
|
||||
Write-Error "An error occurred during WinGet installation: $_"
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
44
functions/private/Set-CategoryVisibility.ps1
Normal file
44
functions/private/Set-CategoryVisibility.ps1
Normal file
@ -0,0 +1,44 @@
|
||||
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
|
||||
}
|
||||
}
|
54
functions/private/Show-OnlyCheckedApps.ps1
Normal file
54
functions/private/Show-OnlyCheckedApps.ps1
Normal file
@ -0,0 +1,54 @@
|
||||
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 "*"
|
||||
}
|
||||
}
|
@ -23,14 +23,22 @@ function Test-WinUtilPackageManager {
|
||||
# Check if Winget is available while getting it's Version if it's available
|
||||
$wingetExists = $true
|
||||
try {
|
||||
$wingetVersionFull = winget --version
|
||||
$wingetInfo = winget --info
|
||||
# Extract the package version from the output
|
||||
$wingetVersionFull = ($wingetInfo | Select-String -Pattern 'Microsoft\.DesktopAppInstaller v\d+\.\d+\.\d+\.\d+').Matches.Value
|
||||
if ($wingetVersionFull) {
|
||||
$wingetVersionFull = $wingetVersionFull.Split(' ')[-1].TrimStart('v')
|
||||
} else {
|
||||
# Fallback in case the pattern isn't found
|
||||
$wingetVersionFull = ($wingetInfo | Select-String -Pattern 'Package Manager v\d+\.\d+\.\d+').Matches.Value.Split(' ')[-1]
|
||||
}
|
||||
} catch [System.Management.Automation.CommandNotFoundException], [System.Management.Automation.ApplicationFailedException] {
|
||||
Write-Warning "Winget was not found due to un-availablity reasons"
|
||||
$wingetExists = $false
|
||||
} catch {
|
||||
Write-Warning "Winget was not found due to un-known reasons, The Stack Trace is:`n$($psitem.Exception.StackTrace)"
|
||||
$wingetExists = $false
|
||||
}
|
||||
}
|
||||
|
||||
# If Winget is available, Parse it's Version and give proper information to Terminal Output.
|
||||
# If it isn't available, the return of this funtion will be "not-installed", indicating that
|
||||
@ -48,13 +56,14 @@ function Test-WinUtilPackageManager {
|
||||
# Check if Winget's Version is too old.
|
||||
$wingetCurrentVersion = [System.Version]::Parse($wingetVersion.Trim('v'))
|
||||
# Grabs the latest release of Winget from the Github API for version check process.
|
||||
$response = Invoke-RestMethod -Uri "https://api.github.com/repos/microsoft/Winget-cli/releases/latest" -Method Get -ErrorAction Stop
|
||||
$wingetLatestVersion = [System.Version]::Parse(($response.tag_name).Trim('v')) #Stores version number of latest release.
|
||||
$wingetOutdated = $wingetCurrentVersion -lt $wingetLatestVersion
|
||||
$response = winget search -e Microsoft.AppInstaller --accept-source-agreements
|
||||
$wingetLatestVersion = ($response | Select-String -Pattern '\d+\.\d+\.\d+\.\d+').Matches.Value
|
||||
Write-Host "Latest Search Version: $wingetLatestVersion" -ForegroundColor White
|
||||
Write-Host "Current Installed Version: $wingetCurrentVersion" -ForegroundColor White
|
||||
$wingetOutdated = $wingetCurrentVersion -lt [System.Version]::Parse($wingetLatestVersion)
|
||||
Write-Host "===========================================" -ForegroundColor Green
|
||||
Write-Host "--- Winget is installed ---" -ForegroundColor Green
|
||||
Write-Host "===========================================" -ForegroundColor Green
|
||||
Write-Host "Version: $wingetVersionFull" -ForegroundColor White
|
||||
|
||||
if (!$wingetPreview) {
|
||||
Write-Host " - Winget is a release version." -ForegroundColor Green
|
||||
|
Reference in New Issue
Block a user