mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 10:55:38 +02:00
IntuneManagement 4.0.0-beta1
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# Experimental-platform notice - the DECISION half.
|
||||
#
|
||||
# macOS and Linux run the same engine as Windows through the Avalonia backend, but
|
||||
# the macOS path has never been executed on a Mac. Users get told that once, with a
|
||||
# "do not show again" opt-out, rather than discovering it from a stack trace.
|
||||
#
|
||||
# Why here and not in UI/<backend>/: UI/Classes/ loads for BOTH backends, so the
|
||||
# gate is testable headlessly on Windows (Tests/ExperimentalPlatformNotice.Tests.ps1)
|
||||
# even though the dialog itself only ever renders under Avalonia. The rendering half
|
||||
# lives in UI/Avalonia/Extensions/ExperimentalPlatformNoticeAvalonia.ps1.
|
||||
#
|
||||
# Why AppInitialized: UI/Classes/ dot-sources before Internal/, so the settings
|
||||
# sections do not exist at load time. See the header of UICommonSettings.ps1.
|
||||
|
||||
$script:ExperimentalPlatformNoticeKey = "HideExperimentalPlatformNotice"
|
||||
|
||||
function Add-ExperimentalPlatformSettings
|
||||
{
|
||||
# Idempotent: the notice calls this defensively if it runs before AppInitialized
|
||||
# has fired, and a second Add-SettingsObject would put a duplicate row in the
|
||||
# Settings dialog.
|
||||
foreach($section in (Get-SettingsSections)) {
|
||||
if($section.Values | Where-Object Key -eq $script:ExperimentalPlatformNoticeKey) { return }
|
||||
}
|
||||
|
||||
Add-SettingsObject -Title "Hide experimental platform notice" -Key $script:ExperimentalPlatformNoticeKey -Type "Boolean" `
|
||||
-Description "Stop showing the startup notice that macOS and Linux support is experimental. Clear this to see it again." `
|
||||
-DefaultValue $false -Section "General"
|
||||
}
|
||||
Add-AppEventHandler "AppInitialized" "Add-ExperimentalPlatformSettings"
|
||||
|
||||
function Test-ShouldShowExperimentalPlatformNotice
|
||||
{
|
||||
# The opt-out wins everywhere, including under the forced override - otherwise a
|
||||
# developer who ticks the box on Windows cannot get the dialog back without
|
||||
# editing the settings store by hand.
|
||||
if((Get-SettingValue $script:ExperimentalPlatformNoticeKey) -eq $true) { return $false }
|
||||
|
||||
# IM_EXPERIMENTAL_NOTICE=1 forces the dialog on Windows so it can be reviewed on
|
||||
# the machine it is developed on; it never renders there otherwise.
|
||||
if($script:IsWindowsOS -and $env:IM_EXPERIMENTAL_NOTICE -ne '1') { return $false }
|
||||
|
||||
return $true
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
# Shared UI settings registration - single source for settings that are consumed
|
||||
# ONLY by UI code but apply to BOTH backends (WPF + Avalonia).
|
||||
#
|
||||
# Why here: these keys used to be registered twice (once per backend, kept in sync by
|
||||
# hand) or in engine files despite having no engine consumer. UI/Classes/ loads for
|
||||
# every backend (and headless - harmless: the Settings dialog never renders there,
|
||||
# and registration keeps Get-SettingValue resolving the right SubPath everywhere).
|
||||
#
|
||||
# Why AppInitialized: UI/Classes/ dot-sources BEFORE Internal/, so the non-General
|
||||
# sections (IntuneManager / GraphGeneral / IntuneTools / MSAL) do not exist yet at
|
||||
# load time. All sections exist by AppInitialized, and both Settings dialogs read
|
||||
# the registry at render time, long after that. (Same pattern as the IntuneManagerUI
|
||||
# extensions' registrations.)
|
||||
#
|
||||
# Rule of thumb: a setting consumed by Internal/Classes/Public code must be
|
||||
# registered in an engine file instead (see GraphPageSize in Internal/IntuneManager.ps1
|
||||
# for the cautionary tale) - this file is only for UI-consumed settings.
|
||||
|
||||
function Add-CommonUISettings
|
||||
{
|
||||
# ---- General section (UI appearance / behavior) ----
|
||||
|
||||
# Default is not a palette of its own - it resolves to Light or Dark from the
|
||||
# Windows app theme at apply time (see UI/Classes/UIThemeCommon.ps1).
|
||||
$themeList = @(
|
||||
[PSCustomObject]@{ Name = "Default (follow Windows)"; Value = "Default" },
|
||||
[PSCustomObject]@{ Name = "Light"; Value = "Light" },
|
||||
[PSCustomObject]@{ Name = "Dark"; Value = "Dark" }
|
||||
)
|
||||
|
||||
Add-SettingsObject -Title "Theme" -Key "AppTheme" -Type "List" `
|
||||
-ItemsSource $themeList `
|
||||
-Description "Application color theme. Default follows the Windows app theme." `
|
||||
-DefaultValue "Default" -Section "General"
|
||||
|
||||
Add-SettingsObject -Title "Hide No-access items" -Key "HideNoAccess" -Type "Boolean" `
|
||||
-Description "Remove items from the menu if object permissions is missing. Default is to mark them with red" `
|
||||
-DefaultValue $false -Section "General"
|
||||
|
||||
Add-SettingsObject -Title "Environment name" -Key "EnvironmentText" -Type "Text" `
|
||||
-Description "Label shown as a badge in the toolbar (e.g. Production, Lab). Leave empty to hide." `
|
||||
-Section "General"
|
||||
|
||||
# Guarded: System.Drawing may be unavailable in a headless PS7 session; the badge
|
||||
# color picker then just offers the empty entry (the dialog never renders anyway).
|
||||
$colorsList = @([PSCustomObject]@{ Name = ""; Value = "" })
|
||||
if("System.Drawing.Color" -as [type]) {
|
||||
foreach($color in ([System.Drawing.Color].GetProperties() | Where-Object { $_.PropertyType -eq [System.Drawing.Color] } | Sort-Object -Property Name | Select-Object Name).Name) {
|
||||
$colorsList += [PSCustomObject]@{ Name = $color; Value = $color }
|
||||
}
|
||||
}
|
||||
|
||||
Add-SettingsObject -Title "Environment color" -Key "EnvironmentColor" -Type "List" -ItemsSource $colorsList `
|
||||
-Description "Background color of the environment badge. Text color is auto-calculated for contrast." `
|
||||
-Section "General"
|
||||
|
||||
Add-SettingsObject -Title "Show tenant name" -Key "MenuShowOrganizationName" -Type "Boolean" -DefaultValue $true `
|
||||
-Description "Adds the organization name next to the login info on the menu bar" `
|
||||
-Section "General"
|
||||
|
||||
# ---- Intune section (list behavior) ----
|
||||
|
||||
Add-SettingsObject -Title "Get all pages" -Key "GetAllPages" -Type "Boolean" `
|
||||
-Description "Get all pages when getting items in the UI. Note: This can take long time in environments with lots of policies and apps." `
|
||||
-DefaultValue $true -SubPath "IntuneManager" -Section "IntuneManager"
|
||||
|
||||
Add-SettingsObject -Title "Single-line values in object list" -Key "ObjectListFirstLineOnly" -Type "Boolean" `
|
||||
-Description "Show only the first line of multi-line values (e.g. store app descriptions) in the object list, so every row is one line high. Hover a cell for the full text. Takes effect when the list is next loaded." `
|
||||
-DefaultValue $true -SubPath "IntuneManager" -Section "IntuneManager"
|
||||
|
||||
Add-SettingsObject -Title "Max characters per cell" -Key "ObjectListMaxCellLength" -Type "Int" `
|
||||
-Description "With single-line values on, cut a cell's text at this many characters so one long description cannot push the other columns out of view. Hover the cell for the full text; sorting and the filter still use the whole value. 0 = no limit." `
|
||||
-DefaultValue 50 -SubPath "IntuneManager" -Section "IntuneManager"
|
||||
|
||||
$viewMenuTypes = @(
|
||||
[PSCustomObject]@{ Name = "Group"; Value = "Group" },
|
||||
[PSCustomObject]@{ Name = "Type (API)"; Value = "Type" }
|
||||
)
|
||||
|
||||
Add-SettingsObject -Title "Menu Object Type" -Key "ObjectViewType" -Type "List" -ItemsSource $viewMenuTypes `
|
||||
-Description "Specify object type for the menu. Group: Groups items together like the portal. Type - Single item based on API. Some APIs are split into multiple menu items." `
|
||||
-DefaultValue "Group" -SubPath "IntuneManager" -Section "IntuneManager"
|
||||
|
||||
# ---- MS Graph General section (UI action gating) ----
|
||||
|
||||
Add-SettingsObject -Title "Refresh Objects after copy" -Key "RefreshObjectsAfterCopy" -Type "Boolean" `
|
||||
-Description "Reload the object list after copying an object" -DefaultValue $true `
|
||||
-SubPath "IntuneManager" -Section "GraphGeneral"
|
||||
|
||||
Add-SettingsObject -Title "Show Delete button" -Key "AllowDelete" -Type "Boolean" `
|
||||
-Description "Allow deleting individual objectes" -DefaultValue $false `
|
||||
-SubPath "IntuneManager" -Section "GraphGeneral"
|
||||
|
||||
Add-SettingsObject -Title "Show Bulk Delete" -Key "AllowBulkDelete" -Type "Boolean" `
|
||||
-Description "Allow using bulk delete to delete all objects of selected types" -DefaultValue $true `
|
||||
-SubPath "IntuneManager" -Section "GraphGeneral"
|
||||
|
||||
# ---- Intune Tools section ----
|
||||
|
||||
Add-SettingsObject -Title "Format OMA-URI Settings" -Key "FormatOMAURI" -Type "Boolean" `
|
||||
-Description "Automatically clean up XML formatting in OMA-URI and ADMX registry policies for consistent output" -DefaultValue $false `
|
||||
-SubPath "IntuneManager" -Section "IntuneTools"
|
||||
|
||||
# ---- MSAL section (picker sort order) ----
|
||||
|
||||
Add-SettingsObject -Title "Sort Account List" -Key "SortAccountList" -Type "Boolean" -DefaultValue $false `
|
||||
-Description "Sort the list of cached accounts based on user name. Updated at restart or account change" `
|
||||
-Section "MSAL"
|
||||
|
||||
Add-SettingsObject -Title "Sort Tenant List" -Key "SortTenantList" -Type "Boolean" -DefaultValue $false `
|
||||
-Description "Sort the list of available tenants based on Tenant name. Updated at restart or account change" `
|
||||
-Section "MSAL"
|
||||
}
|
||||
|
||||
Add-AppEventHandler "AppInitialized" "Add-CommonUISettings"
|
||||
@@ -0,0 +1,188 @@
|
||||
#ImportOrder 30
|
||||
#
|
||||
# Abstract base class for the per-backend UI provider. Concrete subclasses
|
||||
# live in UI/WPF/Classes/WPFUIProvider.ps1 and UI/Avalonia/Classes/AvaloniaUIProvider.ps1.
|
||||
# The loader instantiates exactly one of them into $script:UIProvider after
|
||||
# all classes are loaded but before AppInitialized fires (see
|
||||
# IntuneManagement.psm1).
|
||||
#
|
||||
# Methods are virtual-by-throw: every method on this base raises
|
||||
# "<name> not implemented by <typename>" so a missing override surfaces at
|
||||
# the call site rather than silently no-op'ing. Subclasses override every
|
||||
# method they support; cross-boundary callers (Internal/Public) only invoke
|
||||
# methods that both backends implement.
|
||||
#
|
||||
# Parameter types are deliberately loose ([object]) for surfaces where the
|
||||
# concrete shape differs between backends — e.g. ShowModalForm receives a
|
||||
# WPF FrameworkElement under WPF and an Avalonia Control under Avalonia.
|
||||
|
||||
class UIProvider {
|
||||
|
||||
#region Modal / dialog
|
||||
|
||||
[void] ShowMessageBox([string]$Text) {
|
||||
throw "ShowMessageBox(text) not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] ShowMessageBox([string]$Text, [string]$Caption) {
|
||||
throw "ShowMessageBox not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[object] ShowMessageBox([string]$Text, [string]$Caption, [string]$Button, [string]$Icon) {
|
||||
throw "ShowMessageBox(button,icon) not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] ShowModalForm([string]$FormTitle, [object]$FormObject) {
|
||||
throw "ShowModalForm not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] ShowModalForm([string]$FormTitle, [object]$FormObject, [bool]$HideButtons) {
|
||||
throw "ShowModalForm(hideButtons) not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] ShowModalObject() {
|
||||
throw "ShowModalObject() not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] ShowModalObject([object]$Obj) {
|
||||
throw "ShowModalObject not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] CloseTopModalObject() {
|
||||
throw "CloseTopModalObject not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[string] ShowInputDialog([string]$FormTitle, [string]$FormText, [string]$DefaultValue) {
|
||||
throw "ShowInputDialog not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] ShowAboutDialog() {
|
||||
throw "ShowAboutDialog not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[bool] RequestUIConfirmation([string]$Message, [string]$Caption) {
|
||||
throw "RequestUIConfirmation not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] ShowPopup([object]$Popup) {
|
||||
throw "ShowPopup not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] HidePopup() {
|
||||
throw "HidePopup not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File / folder pickers
|
||||
|
||||
[string] ShowFolderPicker([string]$Description) {
|
||||
throw "ShowFolderPicker not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[string] ShowFolderPicker([string]$Path, [string]$Description) {
|
||||
throw "ShowFolderPicker(path,description) not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region XAML helpers
|
||||
|
||||
[object] GetXamlObject([string]$FileName) {
|
||||
throw "GetXamlObject not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[object] GetXamlObject([string]$FileName, [bool]$AddVariables) {
|
||||
throw "GetXamlObject(addVariables) not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[object] GetXamlObject([string]$FileName, [bool]$AddVariables, [bool]$AddStyles) {
|
||||
throw "GetXamlObject(addVariables,addStyles) not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] AddXamlEvent([object]$XamlObj, [string]$ControlName, [string]$EventName, [scriptblock]$Handler) {
|
||||
throw "AddXamlEvent not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] AddXamlVariables([object]$XamlObj, [object]$Scope) {
|
||||
throw "AddXamlVariables not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] SetXamlProperty([object]$XamlObj, [string]$ControlName, [string]$PropertyName, [object]$Value) {
|
||||
throw "SetXamlProperty not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[object] GetXamlProperty([object]$XamlObj, [string]$ControlName, [string]$PropertyName) {
|
||||
throw "GetXamlProperty not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
# Show or hide a named control. $Visible is the LOGICAL intent (shown/hidden);
|
||||
# each backend maps it to its own framework property: WPF sets Visibility to
|
||||
# "Visible"/"Collapsed", Avalonia sets the IsVisible bool. Callers must use this
|
||||
# method (not the underlying property) so code stays backend-agnostic.
|
||||
[void] SetControlVisible([object]$XamlObj, [string]$ControlName, [bool]$Visible) {
|
||||
throw "SetControlVisible not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Main window / chrome
|
||||
|
||||
[void] ShowMainWindow([object]$View) {
|
||||
throw "ShowMainWindow not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] SetMainTitle() {
|
||||
throw "SetMainTitle not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[object] GetMainWindow() {
|
||||
throw "GetMainWindow not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] SetAppTheme([string]$ThemeName) {
|
||||
throw "SetAppTheme not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Threading / status
|
||||
|
||||
[void] UpdateUIStatus([string]$Text) {
|
||||
throw "UpdateUIStatus not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[bool] UpdateUIStatus([System.Collections.IDictionary]$Params) {
|
||||
throw "UpdateUIStatus(IDictionary) not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] InvokeUIMessagePump() {
|
||||
throw "InvokeUIMessagePump not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
# Copy text to the system clipboard. Backends implement this over their own
|
||||
# framework clipboard (WPF System.Windows.Clipboard / Avalonia TopLevel.Clipboard)
|
||||
# rather than PowerShell's Set-Clipboard, which is a silent no-op on a bare
|
||||
# Linux box without xclip/xsel/wl-copy.
|
||||
[void] SetClipboardText([string]$Text) {
|
||||
throw "SetClipboardText not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Auth chrome
|
||||
|
||||
[void] ShowAuthenticationInfo() {
|
||||
throw "ShowAuthenticationInfo not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] SetEnvironmentInfo() {
|
||||
throw "SetEnvironmentInfo() not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
[void] SetEnvironmentInfo([string]$TenantName) {
|
||||
throw "SetEnvironmentInfo not implemented by $($this.GetType().Name)"
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
# Theme resolution shared by both UI backends.
|
||||
#
|
||||
# The AppTheme setting has three values:
|
||||
# Default - follow the OS app theme, and keep following it
|
||||
# Light - always light
|
||||
# Dark - always dark
|
||||
#
|
||||
# "Default" is not a theme of its own: it resolves to Light or Dark at the point
|
||||
# of use. Both backends call Resolve-AppTheme and then apply the concrete result
|
||||
# their own way - WPF merges Themes/<name>.xaml, Avalonia sets
|
||||
# Application.RequestedThemeVariant - so this file stays free of toolkit types
|
||||
# (R12) and neither backend reimplements the lookup (R10).
|
||||
|
||||
# Platform flags, mirroring $script:IsWindowsOS in IntuneManagement.psm1.
|
||||
# $IsMacOS / $IsLinux exist only on PowerShell Core; on 5.1 they read as $null,
|
||||
# which is correct because 5.1 is Windows-only. Captured once at load so tests
|
||||
# can override them in module scope to exercise a branch off-platform.
|
||||
$script:IsMacOSPlatform = [bool]$IsMacOS
|
||||
$script:IsLinuxPlatform = [bool]$IsLinux
|
||||
|
||||
# The two external theme readers sit behind thin wrappers on purpose: Pester
|
||||
# cannot mock a native command that does not exist on the machine running the
|
||||
# test, so `defaults` is unmockable on Linux/Windows CI and `gsettings` on
|
||||
# Windows/macOS. Wrapping them makes both branches of Get-SystemAppTheme
|
||||
# testable everywhere. They are the only lines here that touch the OS.
|
||||
function Get-MacOSInterfaceStyle {
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
& defaults read -g AppleInterfaceStyle 2>$null
|
||||
}
|
||||
|
||||
function Get-LinuxDesktopSetting {
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory = $true)][string]$Key)
|
||||
& gsettings get org.gnome.desktop.interface $Key 2>$null
|
||||
}
|
||||
|
||||
# The OS app theme, as 'Light' or 'Dark'.
|
||||
#
|
||||
# Every platform falls back to Light when its theme cannot be read. Light is the
|
||||
# historical default for this app, so an unreadable setting changes nothing.
|
||||
function Get-SystemAppTheme {
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
if ($script:IsWindowsOS) {
|
||||
# Personalize\AppsUseLightTheme is the *app* theme; SystemUsesLightTheme
|
||||
# is the separate taskbar/Start setting and is deliberately not used - a
|
||||
# user who runs light apps on a dark taskbar wants light apps.
|
||||
try {
|
||||
$key = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize'
|
||||
$value = (Get-ItemProperty -Path $key -Name 'AppsUseLightTheme' -ErrorAction Stop).AppsUseLightTheme
|
||||
if ($null -ne $value -and [int]$value -eq 0) { return 'Dark' }
|
||||
return 'Light'
|
||||
}
|
||||
catch {
|
||||
Write-LogDebug "Windows app theme not readable, assuming Light: $($_.Exception.Message)"
|
||||
return 'Light'
|
||||
}
|
||||
}
|
||||
|
||||
if ($script:IsMacOSPlatform) {
|
||||
# 'defaults read -g AppleInterfaceStyle' prints 'Dark' in dark mode. In
|
||||
# light mode the key does not exist at all, so the command exits non-zero
|
||||
# and prints "does not exist" to stderr. That failure IS the light answer,
|
||||
# not an error - hence the catch returning Light rather than logging loudly.
|
||||
# The try/catch is required: on PS 7.4+ a non-zero native exit throws when
|
||||
# $ErrorActionPreference is Stop.
|
||||
try {
|
||||
$style = Get-MacOSInterfaceStyle
|
||||
if ($style -and (($style -join '').Trim() -eq 'Dark')) { return 'Dark' }
|
||||
}
|
||||
catch {
|
||||
Write-LogDebug "macOS AppleInterfaceStyle not set, assuming Light"
|
||||
}
|
||||
return 'Light'
|
||||
}
|
||||
|
||||
if ($script:IsLinuxPlatform) {
|
||||
# There is no cross-desktop standard. GNOME 42+ exposes color-scheme
|
||||
# ('prefer-dark' / 'prefer-light' / 'default'); 'default' means the user
|
||||
# expressed no preference, so it is Light here. Older GNOME and several
|
||||
# derivatives only carry gtk-theme, whose name ends in '-dark' by
|
||||
# convention (Adwaita-dark, Yaru-dark). Anything else stays Light.
|
||||
# gsettings values come back single-quoted, hence the Trim.
|
||||
try {
|
||||
$scheme = Get-LinuxDesktopSetting 'color-scheme'
|
||||
if ($scheme) {
|
||||
$scheme = ($scheme -join '').Trim().Trim("'")
|
||||
if ($scheme -eq 'prefer-dark') { return 'Dark' }
|
||||
if ($scheme -eq 'prefer-light') { return 'Light' }
|
||||
}
|
||||
|
||||
$gtkTheme = Get-LinuxDesktopSetting 'gtk-theme'
|
||||
if ($gtkTheme) {
|
||||
$gtkTheme = ($gtkTheme -join '').Trim().Trim("'")
|
||||
if ($gtkTheme -match '-dark$') { return 'Dark' }
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-LogDebug "Linux desktop theme not readable (no gsettings?), assuming Light"
|
||||
}
|
||||
return 'Light'
|
||||
}
|
||||
|
||||
return 'Light'
|
||||
}
|
||||
|
||||
# Turn an AppTheme setting value into the concrete theme to apply.
|
||||
#
|
||||
# Pass nothing to read the current setting. Unknown values resolve as Default
|
||||
# rather than throwing, so a hand-edited settings file cannot leave the app
|
||||
# unthemed.
|
||||
function Resolve-AppTheme {
|
||||
[CmdletBinding()]
|
||||
param([string]$ThemeName)
|
||||
|
||||
if (-not $PSBoundParameters.ContainsKey('ThemeName') -or [string]::IsNullOrWhiteSpace($ThemeName)) {
|
||||
$ThemeName = Get-SettingValue 'AppTheme'
|
||||
}
|
||||
|
||||
switch ($ThemeName) {
|
||||
'Light' { return 'Light' }
|
||||
'Dark' { return 'Dark' }
|
||||
default { return (Get-SystemAppTheme) }
|
||||
}
|
||||
}
|
||||
|
||||
# True when the setting says "follow the OS" - the backends use this to decide
|
||||
# whether an OS theme change should be reacted to.
|
||||
function Test-AppThemeFollowsSystem {
|
||||
[CmdletBinding()]
|
||||
param([string]$ThemeName)
|
||||
|
||||
if (-not $PSBoundParameters.ContainsKey('ThemeName') -or [string]::IsNullOrWhiteSpace($ThemeName)) {
|
||||
$ThemeName = Get-SettingValue 'AppTheme'
|
||||
}
|
||||
return ($ThemeName -notin @('Light', 'Dark'))
|
||||
}
|
||||
Reference in New Issue
Block a user