123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- using Avalonia;
- using Avalonia.Controls;
- using Avalonia.Controls.Primitives;
- using Avalonia.Input;
- using Avalonia.Media;
- using System;
- using Avalonia.Collections;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Interactivity;
- using SukiUI.Enums;
- namespace SukiUI.Controls;
- public class SukiWindow : Window
- {
- protected override Type StyleKeyOverride => typeof(SukiWindow);
- public static readonly StyledProperty<double> TitleFontSizeProperty =
- AvaloniaProperty.Register<SukiWindow, double>(nameof(TitleFontSize), defaultValue: 13);
- public double TitleFontSize
- {
- get => GetValue(TitleFontSizeProperty);
- set => SetValue(TitleFontSizeProperty, value);
- }
- public static readonly StyledProperty<FontWeight> TitleFontWeightProperty =
- AvaloniaProperty.Register<SukiWindow, FontWeight>(nameof(TitleFontWeight), defaultValue: FontWeight.Bold);
- public FontWeight TitleFontWeight
- {
- get => GetValue(TitleFontWeightProperty);
- set => SetValue(TitleFontWeightProperty, value);
- }
- public static readonly StyledProperty<Control?> LogoContentProperty =
- AvaloniaProperty.Register<SukiWindow, Control?>(nameof(LogoContent));
- public Control? LogoContent
- {
- get => GetValue(LogoContentProperty);
- set => SetValue(LogoContentProperty, value);
- }
- public static readonly StyledProperty<bool> ShowBottomBorderProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(ShowBottomBorder), defaultValue: true);
- public bool ShowBottomBorder
- {
- get => GetValue(ShowBottomBorderProperty);
- set => SetValue(ShowBottomBorderProperty, value);
- }
- public static readonly StyledProperty<bool> IsTitleBarVisibleProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(IsTitleBarVisible), defaultValue: true);
- public bool IsTitleBarVisible
- {
- get => GetValue(IsTitleBarVisibleProperty);
- set => SetValue(IsTitleBarVisibleProperty, value);
- }
- public static readonly StyledProperty<bool> TitleBarAnimationEnabledProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(TitleBarAnimationEnabled), defaultValue: true);
- public bool TitleBarAnimationEnabled
- {
- get => GetValue(TitleBarAnimationEnabledProperty);
- set => SetValue(TitleBarAnimationEnabledProperty, value);
- }
- public static readonly StyledProperty<bool> IsMenuVisibleProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(IsMenuVisible), defaultValue: false);
- public bool IsMenuVisible
- {
- get => GetValue(IsMenuVisibleProperty);
- set => SetValue(IsMenuVisibleProperty, value);
- }
- public static readonly StyledProperty<AvaloniaList<MenuItem>?> MenuItemsProperty =
- AvaloniaProperty.Register<SukiWindow, AvaloniaList<MenuItem>?>(nameof(MenuItems));
- public AvaloniaList<MenuItem>? MenuItems
- {
- get => GetValue(MenuItemsProperty);
- set => SetValue(MenuItemsProperty, value);
- }
- public static readonly StyledProperty<bool> CanMinimizeProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(CanMinimize), defaultValue: true);
- public bool CanMinimize
- {
- get => GetValue(CanMinimizeProperty);
- set => SetValue(CanMinimizeProperty, value);
- }
- public static readonly StyledProperty<bool> CanMaximizeProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(CanMaximize), defaultValue: true);
- public bool CanMaximize
- {
- get => GetValue(CanMaximizeProperty);
- set => SetValue(CanMaximizeProperty, value);
- }
- public static readonly StyledProperty<bool> CanMoveProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(CanMove), defaultValue: true);
- public bool CanMove
- {
- get => GetValue(CanMoveProperty);
- set => SetValue(CanMoveProperty, value);
- }
- // Background properties
- public static readonly StyledProperty<bool> BackgroundAnimationEnabledProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(BackgroundAnimationEnabled), defaultValue: false);
- /// <inheritdoc cref="SukiBackground.AnimationEnabled"/>
- public bool BackgroundAnimationEnabled
- {
- get => GetValue(BackgroundAnimationEnabledProperty);
- set => SetValue(BackgroundAnimationEnabledProperty, value);
- }
- public static readonly StyledProperty<SukiBackgroundStyle> BackgroundStyleProperty =
- AvaloniaProperty.Register<SukiWindow, SukiBackgroundStyle>(nameof(BackgroundStyle),
- defaultValue: SukiBackgroundStyle.GradientSoft);
- /// <inheritdoc cref="SukiBackground.Style"/>
- public SukiBackgroundStyle BackgroundStyle
- {
- get => GetValue(BackgroundStyleProperty);
- set => SetValue(BackgroundStyleProperty, value);
- }
- public static readonly StyledProperty<string?> BackgroundShaderFileProperty =
- AvaloniaProperty.Register<SukiWindow, string?>(nameof(BackgroundShaderFile));
- /// <inheritdoc cref="SukiBackground.ShaderFile"/>
- public string? BackgroundShaderFile
- {
- get => GetValue(BackgroundShaderFileProperty);
- set => SetValue(BackgroundShaderFileProperty, value);
- }
- public static readonly StyledProperty<string?> BackgroundShaderCodeProperty =
- AvaloniaProperty.Register<SukiWindow, string?>(nameof(BackgroundShaderCode));
- /// <inheritdoc cref="SukiBackground.ShaderCode"/>
- public string? BackgroundShaderCode
- {
- get => GetValue(BackgroundShaderCodeProperty);
- set => SetValue(BackgroundShaderCodeProperty, value);
- }
- public static readonly StyledProperty<bool> BackgroundTransitionsEnabledProperty =
- AvaloniaProperty.Register<SukiBackground, bool>(nameof(BackgroundTransitionsEnabled), defaultValue: false);
- /// <inheritdoc cref="SukiBackground.TransitionsEnabled"/>
- public bool BackgroundTransitionsEnabled
- {
- get => GetValue(BackgroundTransitionsEnabledProperty);
- set => SetValue(BackgroundTransitionsEnabledProperty, value);
- }
- public static readonly StyledProperty<double> BackgroundTransitionTimeProperty =
- AvaloniaProperty.Register<SukiBackground, double>(nameof(BackgroundTransitionTime), defaultValue: 1.0);
- /// <inheritdoc cref="SukiBackground.TransitionTime"/>
- public double BackgroundTransitionTime
- {
- get => GetValue(BackgroundTransitionTimeProperty);
- set => SetValue(BackgroundTransitionTimeProperty, value);
- }
- public static readonly StyledProperty<Avalonia.Controls.Controls> RightWindowTitleBarControlsProperty =
- AvaloniaProperty.Register<SukiWindow, Avalonia.Controls.Controls>(nameof(RightWindowTitleBarControls),
- defaultValue: new Avalonia.Controls.Controls());
- public static readonly StyledProperty<bool> BackgroundForceSoftwareRenderingProperty =
- AvaloniaProperty.Register<SukiWindow, bool>(nameof(BackgroundForceSoftwareRendering));
- /// <summary>
- /// Forces the background of the window to utilise software rendering.
- /// This prevents use of any advanced effects or animations and provides only a flat background colour that changes with the theme.
- /// </summary>
- public bool BackgroundForceSoftwareRendering
- {
- get => GetValue(BackgroundForceSoftwareRenderingProperty);
- set => SetValue(BackgroundForceSoftwareRenderingProperty, value);
- }
- /// <summary>
- /// Controls that are displayed on the right side of the title bar,
- /// to the left of the normal window control buttons. (Displays provided controls right-to-left)
- /// </summary>
- public Avalonia.Controls.Controls RightWindowTitleBarControls
- {
- get => GetValue(RightWindowTitleBarControlsProperty);
- set => SetValue(RightWindowTitleBarControlsProperty, value);
- }
- public static readonly StyledProperty<Avalonia.Controls.Controls> HostsProperty =
- AvaloniaProperty.Register<SukiWindow, Avalonia.Controls.Controls>(nameof(Hosts),
- defaultValue: new Avalonia.Controls.Controls());
- /// <summary>
- /// These controls are displayed above all others and fill the entire window.
- /// You can include <see cref="SukiDialogHost"/> and <see cref="SukiToastHost"/> or create your own custom implementations.
- /// </summary>
- public Avalonia.Controls.Controls Hosts
- {
- get => GetValue(HostsProperty);
- set => SetValue(HostsProperty, value);
- }
- public SukiWindow()
- {
- MenuItems = new AvaloniaList<MenuItem>();
- RightWindowTitleBarControls = new Avalonia.Controls.Controls();
- Hosts = new Avalonia.Controls.Controls();
- }
- protected override void OnLoaded(RoutedEventArgs e)
- {
- base.OnLoaded(e);
- if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
- return;
- if (desktop.MainWindow is SukiWindow window && window != this)
- {
- Icon ??= window.Icon;
- // This would be nice to do, but obviously LogoContent is a control and you can't attach it twice.
- // if (LogoContent is null) LogoContent = s.LogoContent;
- }
- }
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
- {
- base.OnPropertyChanged(change);
- if (change.Property == WindowStateProperty && change.NewValue is WindowState windowState)
- OnWindowStateChanged(windowState);
- }
- protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
- {
- base.OnApplyTemplate(e);
- OnWindowStateChanged(WindowState);
- try
- {
- // Create handlers for buttons
- if (e.NameScope.Get<Button>("PART_MaximizeButton") is { } maximize)
- {
- maximize.Click += OnMaximizeButtonClicked;
- EnableWindowsSnapLayout(maximize);
- }
- if (e.NameScope.Get<Button>("PART_MinimizeButton") is { } minimize)
- minimize.Click += (_, _) => WindowState = WindowState.Minimized;
- if (e.NameScope.Get<Button>("PART_CloseButton") is { } close)
- close.Click += (_, _) => Close();
- if (e.NameScope.Get<GlassCard>("PART_TitleBarBackground") is { } titleBar)
- {
- titleBar.PointerPressed += OnTitleBarPointerPressed;
- titleBar.DoubleTapped += OnMaximizeButtonClicked;
- }
- }
- catch
- {
- // ignored
- }
- }
- private void OnMaximizeButtonClicked(object? sender, RoutedEventArgs args)
- {
- if (!CanMaximize) return;
- WindowState = WindowState == WindowState.Maximized
- ? WindowState.Normal
- : WindowState.Maximized;
- }
- private void EnableWindowsSnapLayout(Button maximize)
- {
- var pointerOnMaxButton = false;
- var setter = typeof(Button).GetProperty("IsPointerOver");
- var proc = (IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool handled) =>
- {
- switch (msg)
- {
- case 533:
- if (!pointerOnMaxButton) break;
- if (!CanMaximize) break;
- WindowState = WindowState == WindowState.Maximized
- ? WindowState.Normal
- : WindowState.Maximized;
- break;
- case 0x0084:
- var point = new PixelPoint(
- (short)(ToInt32(lParam) & 0xffff),
- (short)(ToInt32(lParam) >> 16));
- var desiredSize = maximize.DesiredSize;
- var buttonLeftTop = maximize.PointToScreen(FlowDirection == FlowDirection.LeftToRight
- ? new Point(desiredSize.Width, 0)
- : new Point(0, 0));
- var x = (buttonLeftTop.X - point.X) / RenderScaling;
- var y = (point.Y - buttonLeftTop.Y) / RenderScaling;
- if (new Rect(0, 0,
- desiredSize.Width,
- desiredSize.Height)
- .Contains(new Point(x, y)))
- {
- setter?.SetValue(maximize, true);
- pointerOnMaxButton = true;
- handled = true;
- return (IntPtr)9;
- }
- pointerOnMaxButton = false;
- setter?.SetValue(maximize, false);
- break;
- }
- return IntPtr.Zero;
- static int ToInt32(IntPtr ptr) => IntPtr.Size == 4
- ? ptr.ToInt32()
- : (int)(ptr.ToInt64() & 0xffffffff);
- };
- Win32Properties.AddWndProcHookCallback(this, new Win32Properties.CustomWndProcHookCallback(proc));
- }
- private void OnWindowStateChanged(WindowState state)
- {
- if (state == WindowState.FullScreen)
- CanMaximize = CanResize = CanMove = false;
- if (state == WindowState.Maximized)
- Margin = new Thickness(7);
- else
- Margin = new Thickness(0);
- }
- private void OnTitleBarPointerPressed(object? sender, PointerPressedEventArgs e)
- {
- base.OnPointerPressed(e);
- BeginMoveDrag(e);
- }
- }
|