Browse Source

Merge 7b4eda133a into 3068850405

pull/20108/merge
Max Katz 3 days ago
committed by GitHub
parent
commit
679da74eb9
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 3
      samples/ControlCatalog/MainView.xaml
  2. 142
      samples/ControlCatalog/Pages/PlatformSettingsPage.xaml
  3. 15
      samples/ControlCatalog/Pages/PlatformSettingsPage.xaml.cs
  4. 77
      samples/ControlCatalog/ViewModels/PlatformSettingsViewModel.cs
  5. 11
      src/Avalonia.Base/Platform/DefaultPlatformSettings.cs
  6. 14
      src/Avalonia.Base/Platform/IPlatformSettings.cs
  7. 45
      src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs
  8. 14
      src/Browser/Avalonia.Browser/Interop/DomHelper.cs
  9. 8
      src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts
  10. 3
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  11. 6
      src/Windows/Avalonia.Win32/Win32Platform.cs
  12. 34
      src/Windows/Avalonia.Win32/Win32PlatformSettings.cs

3
samples/ControlCatalog/MainView.xaml

@ -174,6 +174,9 @@
<TabItem Header="Platform Information">
<pages:PlatformInfoPage />
</TabItem>
<TabItem Header="Platform Settings">
<pages:PlatformSettingsPage />
</TabItem>
<TabItem Header="Pointers">
<pages:PointersPage />
</TabItem>

142
samples/ControlCatalog/Pages/PlatformSettingsPage.xaml

@ -0,0 +1,142 @@
<UserControl x:Class="ControlCatalog.Pages.PlatformSettingsPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ControlCatalog.ViewModels"
d:DesignHeight="800"
d:DesignWidth="600"
mc:Ignorable="d"
x:DataType="viewModels:PlatformSettingsViewModel">
<ScrollViewer>
<StackPanel Spacing="20" Margin="20">
<TextBlock Text="Platform Settings Information"
FontSize="20"
FontWeight="Bold" />
<TextBlock Text="This page displays IPlatformSettings values which may change at runtime based on system settings."
TextWrapping="Wrap"
Opacity="0.7" />
<GroupBox Header="System Color Values">
<StackPanel Spacing="15">
<Grid ColumnDefinitions="180,*" RowDefinitions="Auto,Auto,Auto">
<TextBlock Grid.Column="0" Grid.Row="0" Text="Theme Variant:" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding ThemeVariant}" FontWeight="Bold" VerticalAlignment="Center" />
<TextBlock Grid.Column="0" Grid.Row="1" Text="Contrast Preference:" VerticalAlignment="Center" Margin="0,10,0,0" />
<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding ContrastPreference}" FontWeight="Bold" VerticalAlignment="Center" Margin="0,10,0,0" />
</Grid>
<StackPanel Spacing="10">
<Grid ColumnDefinitions="180,120,*">
<TextBlock Grid.Column="0" Text="Accent Color 1:" VerticalAlignment="Center" />
<Border Grid.Column="1"
Width="100"
Height="40"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="4"
HorizontalAlignment="Left">
<Border.Background>
<SolidColorBrush Color="{Binding AccentColor1}" />
</Border.Background>
</Border>
<TextBlock Grid.Column="2"
Text="{Binding AccentColor1}"
VerticalAlignment="Center"
Margin="10,0,0,0" />
</Grid>
<Grid ColumnDefinitions="180,120,*">
<TextBlock Grid.Column="0" Text="Accent Color 2:" VerticalAlignment="Center" />
<Border Grid.Column="1"
Width="100"
Height="40"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="4"
HorizontalAlignment="Left">
<Border.Background>
<SolidColorBrush Color="{Binding AccentColor2}" />
</Border.Background>
</Border>
<TextBlock Grid.Column="2"
Text="{Binding AccentColor2}"
VerticalAlignment="Center"
Margin="10,0,0,0" />
</Grid>
<Grid ColumnDefinitions="180,120,*">
<TextBlock Grid.Column="0" Text="Accent Color 3:" VerticalAlignment="Center" />
<Border Grid.Column="1"
Width="100"
Height="40"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="4"
HorizontalAlignment="Left">
<Border.Background>
<SolidColorBrush Color="{Binding AccentColor3}" />
</Border.Background>
</Border>
<TextBlock Grid.Column="2"
Text="{Binding AccentColor3}"
VerticalAlignment="Center"
Margin="10,0,0,0" />
</Grid>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="Language Settings">
<StackPanel Spacing="10">
<TextBlock Text="This reflects the preferred application language as specified in OS settings."
TextWrapping="Wrap"
Opacity="0.7"
FontSize="12" />
<Grid ColumnDefinitions="180,*">
<TextBlock Grid.Column="0" Text="Preferred Language:" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding PreferredLanguage}" FontWeight="Bold" VerticalAlignment="Center" />
</Grid>
</StackPanel>
</GroupBox>
<GroupBox Header="Other Platform Settings">
<StackPanel Spacing="10">
<TextBlock Text="These settings are less commonly changed and are platform-specific."
TextWrapping="Wrap"
Opacity="0.7"
FontSize="12" />
<Grid ColumnDefinitions="180,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock Grid.Column="0" Grid.Row="0" Text="Hold Wait Duration:" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding HoldWaitDuration}" VerticalAlignment="Center" />
<TextBlock Grid.Column="0" Grid.Row="1" Text="Tap Size (Touch):" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding TapSizeTouch}" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="0" Grid.Row="2" Text="Tap Size (Mouse):" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding TapSizeMouse}" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="0" Grid.Row="3" Text="Double-Tap Size (Touch):" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="1" Grid.Row="3" Text="{Binding DoubleTapSizeTouch}" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="0" Grid.Row="4" Text="Double-Tap Size (Mouse):" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="1" Grid.Row="4" Text="{Binding DoubleTapSizeMouse}" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="0" Grid.Row="5" Text="Double-Tap Time (Touch):" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="1" Grid.Row="5" Text="{Binding DoubleTapTimeTouch}" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="0" Grid.Row="6" Text="Double-Tap Time (Mouse):" VerticalAlignment="Center" Margin="0,5,0,0" />
<TextBlock Grid.Column="1" Grid.Row="6" Text="{Binding DoubleTapTimeMouse}" VerticalAlignment="Center" Margin="0,5,0,0" />
</Grid>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</UserControl>

15
samples/ControlCatalog/Pages/PlatformSettingsPage.xaml.cs

@ -0,0 +1,15 @@
using Avalonia.Controls;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public partial class PlatformSettingsPage : UserControl
{
public PlatformSettingsPage()
{
InitializeComponent();
DataContext = new PlatformSettingsViewModel();
}
}
}

77
samples/ControlCatalog/ViewModels/PlatformSettingsViewModel.cs

@ -0,0 +1,77 @@
using System;
using Avalonia;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Platform;
using MiniMvvm;
namespace ControlCatalog.ViewModels;
public class PlatformSettingsViewModel : ViewModelBase
{
private readonly IPlatformSettings? _platformSettings;
private PlatformColorValues? _colorValues;
private string? _preferredLanguage;
public PlatformSettingsViewModel()
{
_platformSettings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
if (_platformSettings != null)
{
_colorValues = _platformSettings.GetColorValues();
_preferredLanguage = _platformSettings.PreferredApplicationLanguage;
_platformSettings.ColorValuesChanged += OnColorValuesChanged;
_platformSettings.PreferredApplicationLanguageChanged += OnPreferredLanguageChanged;
}
}
private void OnColorValuesChanged(object? sender, PlatformColorValues e)
{
_colorValues = e;
RaisePropertyChanged(nameof(ThemeVariant));
RaisePropertyChanged(nameof(ContrastPreference));
RaisePropertyChanged(nameof(AccentColor1));
RaisePropertyChanged(nameof(AccentColor2));
RaisePropertyChanged(nameof(AccentColor3));
}
private void OnPreferredLanguageChanged(object? sender, EventArgs e)
{
if (_platformSettings != null)
{
_preferredLanguage = _platformSettings.PreferredApplicationLanguage;
RaisePropertyChanged(nameof(PreferredLanguage));
}
}
public bool IsAvailable => _platformSettings != null;
public string PreferredLanguage => _preferredLanguage ?? "Not available";
public string ThemeVariant => _colorValues?.ThemeVariant.ToString() ?? "Not available";
public string ContrastPreference => _colorValues?.ContrastPreference.ToString() ?? "Not available";
public Color AccentColor1 => _colorValues?.AccentColor1 ?? Colors.Gray;
public Color AccentColor2 => _colorValues?.AccentColor2 ?? Colors.Gray;
public Color AccentColor3 => _colorValues?.AccentColor3 ?? Colors.Gray;
public string HoldWaitDuration => _platformSettings?.HoldWaitDuration.ToString() ?? "Not available";
public string TapSizeTouch => _platformSettings?.GetTapSize(PointerType.Touch).ToString() ?? "Not available";
public string TapSizeMouse => _platformSettings?.GetTapSize(PointerType.Mouse).ToString() ?? "Not available";
public string DoubleTapSizeTouch => _platformSettings?.GetDoubleTapSize(PointerType.Touch).ToString() ?? "Not available";
public string DoubleTapSizeMouse => _platformSettings?.GetDoubleTapSize(PointerType.Mouse).ToString() ?? "Not available";
public string DoubleTapTimeTouch => _platformSettings?.GetDoubleTapTime(PointerType.Touch).ToString() ?? "Not available";
public string DoubleTapTimeMouse => _platformSettings?.GetDoubleTapTime(PointerType.Mouse).ToString() ?? "Not available";
}

11
src/Avalonia.Base/Platform/DefaultPlatformSettings.cs

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Media;
@ -39,6 +40,9 @@ namespace Avalonia.Platform
public PlatformHotkeyConfiguration HotkeyConfiguration =>
AvaloniaLocator.Current.GetRequiredService<PlatformHotkeyConfiguration>();
public virtual string PreferredApplicationLanguage =>
CultureInfo.CurrentUICulture.Name;
public virtual PlatformColorValues GetColorValues()
{
return new PlatformColorValues
@ -48,11 +52,18 @@ namespace Avalonia.Platform
}
public virtual event EventHandler<PlatformColorValues>? ColorValuesChanged;
public virtual event EventHandler? PreferredApplicationLanguageChanged;
protected void OnColorValuesChanged(PlatformColorValues colorValues)
{
Dispatcher.UIThread.Send(
_ => ColorValuesChanged?.Invoke(this, colorValues));
}
protected void OnPreferredApplicationLanguageChanged()
{
Dispatcher.UIThread.Send(
_ => PreferredApplicationLanguageChanged?.Invoke(this, EventArgs.Empty));
}
}
}

14
src/Avalonia.Base/Platform/IPlatformSettings.cs

@ -37,12 +37,17 @@ namespace Avalonia.Platform
/// Holding duration between pointer press and when event is fired.
/// </summary>
TimeSpan HoldWaitDuration { get; }
/// <summary>
/// Get a configuration for platform-specific hotkeys in an Avalonia application.
/// </summary>
PlatformHotkeyConfiguration HotkeyConfiguration { get; }
/// <summary>
/// Gets the preferred application language as specified in the operating system settings.
/// </summary>
string PreferredApplicationLanguage { get; }
/// <summary>
/// Gets current system color values including dark mode and accent colors.
/// </summary>
@ -52,5 +57,10 @@ namespace Avalonia.Platform
/// Raises when current system color values are changed. Including changing of a dark mode and accent colors.
/// </summary>
event EventHandler<PlatformColorValues>? ColorValuesChanged;
/// <summary>
/// Raises when the preferred application language is changed in the operating system settings.
/// </summary>
event EventHandler? PreferredApplicationLanguageChanged;
}
}

45
src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Browser.Interop;
using Avalonia.Platform;
@ -9,20 +10,41 @@ internal class BrowserPlatformSettings : DefaultPlatformSettings
private bool _isDarkMode;
private bool _isHighContrast;
private bool _isInitialized;
private string? _lastLanguage;
public override event EventHandler<PlatformColorValues>? ColorValuesChanged
{
add
{
EnsureBackend();
EnsureSettings();
base.ColorValuesChanged += value;
}
remove => base.ColorValuesChanged -= value;
}
public override event EventHandler? PreferredApplicationLanguageChanged
{
add
{
EnsureSettings();
base.PreferredApplicationLanguageChanged += value;
}
remove => base.PreferredApplicationLanguageChanged -= value;
}
public override string PreferredApplicationLanguage
{
get
{
EnsureSettings();
return _lastLanguage ?? base.PreferredApplicationLanguage;
}
}
public override PlatformColorValues GetColorValues()
{
EnsureBackend();
EnsureSettings();
return base.GetColorValues() with
{
@ -31,18 +53,27 @@ internal class BrowserPlatformSettings : DefaultPlatformSettings
};
}
public void OnValuesChanged(bool isDarkMode, bool isHighContrast)
public void OnColorValuesChanged(bool isDarkMode, bool isHighContrast)
{
_isDarkMode = isDarkMode;
_isHighContrast = isHighContrast;
OnColorValuesChanged(GetColorValues());
}
private void EnsureBackend()
public void OnPreferredLanguageChanged(string? language)
{
if (language is not null && _lastLanguage != language)
{
_lastLanguage = language;
OnPreferredApplicationLanguageChanged();
}
}
private void EnsureSettings()
{
if (!_isInitialized)
{
// WASM module has async nature of initialization. We can't native code right away during components registration.
// WASM module has async nature of initialization. We can't call platform code right away during components registration.
_isInitialized = true;
var values = DomHelper.GetDarkMode(BrowserWindowingPlatform.GlobalThis);
if (values.Length == 2)
@ -50,6 +81,8 @@ internal class BrowserPlatformSettings : DefaultPlatformSettings
_isDarkMode = values[0] > 0;
_isHighContrast = values[1] > 0;
}
_lastLanguage = DomHelper.GetNavigatorLanguage(BrowserWindowingPlatform.GlobalThis);
}
}
}

14
src/Browser/Avalonia.Browser/Interop/DomHelper.cs

@ -1,4 +1,4 @@
using System.Runtime.InteropServices.JavaScript;
using System.Runtime.InteropServices.JavaScript;
using System.Threading.Tasks;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform;
@ -31,6 +31,9 @@ internal static partial class DomHelper
[JSImport("AvaloniaDOM.getDarkMode", AvaloniaModule.MainModuleName)]
public static partial int[] GetDarkMode(JSObject globalThis);
[JSImport("AvaloniaDOM.getNavigatorLanguage", AvaloniaModule.MainModuleName)]
public static partial string? GetNavigatorLanguage(JSObject globalThis);
[JSImport("AvaloniaDOM.addClass", AvaloniaModule.MainModuleName)]
public static partial void AddCssClass(JSObject element, string className);
@ -40,7 +43,7 @@ internal static partial class DomHelper
[JSExport]
public static Task DarkModeChanged(bool isDarkMode, bool isHighContrast)
{
(AvaloniaLocator.Current.GetService<IPlatformSettings>() as BrowserPlatformSettings)?.OnValuesChanged(isDarkMode, isHighContrast);
(AvaloniaLocator.Current.GetService<IPlatformSettings>() as BrowserPlatformSettings)?.OnColorValuesChanged(isDarkMode, isHighContrast);
return Task.CompletedTask;
}
@ -51,6 +54,13 @@ internal static partial class DomHelper
return Task.CompletedTask;
}
[JSExport]
public static Task LanguageChanged(string language)
{
(AvaloniaLocator.Current.GetService<IPlatformSettings>() as BrowserPlatformSettings)?.OnPreferredLanguageChanged(language);
return Task.CompletedTask;
}
[JSExport]
public static Task ScreensChanged()
{

8
src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts

@ -136,6 +136,10 @@ export class AvaloniaDOM {
});
}
globalThis.addEventListener("languagechange", () => {
JsExports.DomHelper.LanguageChanged(globalThis.navigator.language);
});
globalThis.document.addEventListener("visibilitychange", () => {
JsExports.DomHelper.DocumentVisibilityChanged(globalThis.document.visibilityState);
});
@ -167,4 +171,8 @@ export class AvaloniaDOM {
prefersContrastMedia.matches ? 1 : 0
];
}
public static getNavigatorLanguage(globalThis: Window): string | null {
return globalThis.navigator?.language ?? null;
}
}

3
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1334,6 +1334,9 @@ namespace Avalonia.Win32.Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetModuleHandleW", ExactSpelling = true)]
public static extern IntPtr GetModuleHandle(string? lpModuleName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetUserDefaultLocaleName(char* lpLocaleName, int cchLocaleName);
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(SystemMetric smIndex);

6
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@ -183,6 +183,10 @@ namespace Avalonia.Win32
{
win32PlatformSettings.OnColorValuesChanged();
}
else if (changedSetting == "intl") // language/locale change
{
win32PlatformSettings.OnLanguageChanged();
}
}
if (msg == (uint)WindowsMessage.WM_TIMER)

34
src/Windows/Avalonia.Win32/Win32PlatformSettings.cs

@ -13,6 +13,7 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
&& WinRTApiInformation.IsTypePresent("Windows.UI.ViewManagement.AccessibilitySettings"));
private PlatformColorValues? _lastColorValues;
private string? _lastLanguage;
public override Size GetTapSize(PointerType type)
{
@ -33,6 +34,27 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
}
public override TimeSpan GetDoubleTapTime(PointerType type) => TimeSpan.FromMilliseconds(GetDoubleClickTime());
public override string PreferredApplicationLanguage
{
get
{
if (_lastLanguage is null)
{
unsafe
{
const int LOCALE_NAME_MAX_LENGTH = 85;
var buffer = stackalloc char[LOCALE_NAME_MAX_LENGTH];
var length = GetUserDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH);
_lastLanguage = length > 0
? new string(buffer, 0, length - 1)
: base.PreferredApplicationLanguage;
}
}
return _lastLanguage;
}
}
public override PlatformColorValues GetColorValues()
{
@ -88,4 +110,16 @@ internal class Win32PlatformSettings : DefaultPlatformSettings
OnColorValuesChanged(colorValues);
}
}
internal void OnLanguageChanged()
{
var oldLanguage = _lastLanguage;
_lastLanguage = null;
var newLanguage = PreferredApplicationLanguage;
if (oldLanguage != newLanguage)
{
OnPreferredApplicationLanguageChanged();
}
}
}

Loading…
Cancel
Save