Prechádzať zdrojové kódy

Fixed window management for linux

flabbet 5 mesiacov pred
rodič
commit
626416344c

+ 1 - 1
src/PixiDocks

@@ -1 +1 @@
-Subproject commit 47107d7dc284e04ed92e4c470a6ed2f972e5d9cd
+Subproject commit 17c222b262301f0f38687dfbb7545e58d880b9f3

+ 12 - 2
src/PixiEditor/Styles/PixiEditorPopupTemplate.axaml

@@ -12,7 +12,16 @@
     <Style Selector="controls|PixiEditorPopup">
         <Setter Property="WindowStartupLocation" Value="CenterOwner" />
         <Setter Property="TransparencyLevelHint" Value="Transparent" />
-        <Setter Property="SystemDecorations" Value="Full" />
+        <Setter Property="SystemDecorations">
+            <OnPlatform>
+                <OnPlatform.Default>
+                    <SystemDecorations>Full</SystemDecorations>
+                </OnPlatform.Default>
+                <OnPlatform.Linux>
+                    <SystemDecorations>None</SystemDecorations>
+                </OnPlatform.Linux>
+            </OnPlatform>
+        </Setter>
         <Setter Property="ExtendClientAreaChromeHints">
             <Setter.Value>
                 <OnPlatform>
@@ -36,9 +45,10 @@
         <Setter Property="Template">
             <ControlTemplate>
                 <VisualLayerManager>
-                    <Panel>
+                    <Panel Name="PART_ResizePanel">
                         <DockPanel>
                             <controls:DialogTitleBar
+                                Name="PART_TitleBar"
                                 DockPanel.Dock="Top" 
                                 CloseCommand="{TemplateBinding CloseCommand}"
                                 CanMinimize="{TemplateBinding CanMinimize}"

+ 1 - 1
src/PixiEditor/Views/Dialogs/DialogTitleBar.axaml

@@ -23,7 +23,7 @@
         <DockPanel IsHitTestVisible="True">
             <CaptionButtons Name="captionButtons" DockPanel.Dock="Right" IsVisible="{OnPlatform macOS=false, Default=true}"/>
             <ContentPresenter DockPanel.Dock="Right" IsVisible="{Binding !!AdditionalElement}" Content="{Binding Path=AdditionalElement}"/>
-            <Control /><!-- dummy control to occupy dockpanel center -->
+            <Panel Background="Transparent" IsHitTestVisible="True" /><!-- dummy control to occupy dockpanel center -->
         </DockPanel>
     </Grid>
 </UserControl>

+ 1 - 0
src/PixiEditor/Views/Dialogs/DialogTitleBar.axaml.cs

@@ -3,6 +3,7 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.Chrome;
 using Avalonia.Controls.Primitives;
+using Avalonia.Input;
 using Avalonia.Interactivity;
 using PixiEditor.Extensions.UI;
 

+ 64 - 4
src/PixiEditor/Views/Dialogs/PixiEditorPopup.cs

@@ -2,8 +2,14 @@
 using System.Windows.Input;
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+using Avalonia.Interactivity;
 using Avalonia.Styling;
+using Avalonia.Threading;
 using CommunityToolkit.Mvvm.Input;
+using PixiDocks.Avalonia.Helpers;
 using PixiEditor.Extensions.CommonApi;
 using PixiEditor.Extensions.CommonApi.Async;
 using PixiEditor.Extensions.CommonApi.Windowing;
@@ -11,6 +17,8 @@ using PixiEditor.Extensions.UI;
 
 namespace PixiEditor.Views.Dialogs;
 
+[TemplatePart("PART_ResizePanel", typeof(Panel))]
+[TemplatePart("Part_TitleBar", typeof(DialogTitleBar))]
 public partial class PixiEditorPopup : Window, IPopupWindow
 {
     public string UniqueId => "PixiEditor.Popup";
@@ -21,8 +29,9 @@ public partial class PixiEditorPopup : Window, IPopupWindow
     public static readonly StyledProperty<bool> CloseIsHideProperty = AvaloniaProperty.Register<PixiEditorPopup, bool>(
         nameof(CloseIsHide), defaultValue: false);
 
-    public static readonly StyledProperty<ICommand> CloseCommandProperty = AvaloniaProperty.Register<PixiEditorPopup, ICommand>(
-        nameof(CloseCommand));
+    public static readonly StyledProperty<ICommand> CloseCommandProperty =
+        AvaloniaProperty.Register<PixiEditorPopup, ICommand>(
+            nameof(CloseCommand));
 
     public ICommand CloseCommand
     {
@@ -42,6 +51,8 @@ public partial class PixiEditorPopup : Window, IPopupWindow
         set => SetValue(CanMinimizeProperty, value);
     }
 
+    private Panel resizePanel;
+
     protected override Type StyleKeyOverride => typeof(PixiEditorPopup);
 
     public PixiEditorPopup()
@@ -52,6 +63,55 @@ public partial class PixiEditorPopup : Window, IPopupWindow
 #endif
     }
 
+    protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+    {
+        base.OnApplyTemplate(e);
+        if (System.OperatingSystem.IsLinux())
+        {
+            var titleBar = e.NameScope.Find<DialogTitleBar>("PART_TitleBar");
+            titleBar.PointerPressed += OnTitleBarPressed;
+
+            resizePanel = e.NameScope.Find<Panel>("PART_ResizePanel");
+            resizePanel.AddHandler(PointerPressedEvent, OnResizePanelPressed,
+                RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
+            resizePanel.PointerMoved += OnResizePanelMoved;
+        }
+    }
+
+    private void OnTitleBarPressed(object? sender, PointerPressedEventArgs e)
+    {
+        if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+        {
+            if (e.ClickCount == 2)
+            {
+                WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
+            }
+            else
+            {
+                BeginMoveDrag(e);
+                e.Handled = true;
+            }
+        }
+    }
+
+    private void OnResizePanelPressed(object? sender, PointerPressedEventArgs e)
+    {
+        if (WindowState == WindowState.Normal && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && CanResize)
+        {
+            var dir = WindowUtility.GetResizeDirection(e.GetPosition(resizePanel), resizePanel, new Thickness(8));
+            if (dir == null) return;
+
+            BeginResizeDrag(dir.Value, e);
+            e.Handled = true;
+        }
+    }
+
+    private void OnResizePanelMoved(object? sender, PointerEventArgs e)
+    {
+        if (!CanResize || WindowState != WindowState.Normal) return;
+        Cursor = new Cursor(WindowUtility.SetResizeCursor(e, resizePanel, new Thickness(8)));
+    }
+
     public override void Show()
     {
         Show(MainWindow.Current);
@@ -65,7 +125,7 @@ public partial class PixiEditorPopup : Window, IPopupWindow
     [RelayCommand]
     public void SetResultAndCloseCommand()
     {
-        if(CloseIsHide)
+        if (CloseIsHide)
             Hide();
         else
             Close(true);
@@ -73,7 +133,7 @@ public partial class PixiEditorPopup : Window, IPopupWindow
 
     public void ClosePopup()
     {
-        if(CloseIsHide)
+        if (CloseIsHide)
             Hide();
         else
             Close(false);

+ 1 - 0
src/PixiEditor/Views/Main/MainTitleBar.axaml.cs

@@ -1,5 +1,6 @@
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Input;
 using Avalonia.Markup.Xaml;
 using PixiEditor.OperatingSystem;
 using PixiEditor.ViewModels.Menu;

+ 10 - 0
src/PixiEditor/Views/MainWindow.axaml

@@ -17,5 +17,15 @@
         Initialized="MainWindow_Initialized"
         ui:Translator.UseLanguageFlowDirection="True"
         Title="PixiEditor">
+    <Window.SystemDecorations>
+        <OnPlatform>
+            <OnPlatform.Default>
+                <SystemDecorations>Full</SystemDecorations>
+            </OnPlatform.Default>
+            <OnPlatform.Linux>
+                <SystemDecorations>None</SystemDecorations>
+            </OnPlatform.Linux>
+        </OnPlatform> 
+    </Window.SystemDecorations>
     <views1:MainView />
 </Window>

+ 57 - 3
src/PixiEditor/Views/MainWindow.axaml.cs

@@ -2,13 +2,17 @@ using AsyncImageLoader.Loaders;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.OpenGL;
 using Avalonia.Platform;
 using Avalonia.Rendering.Composition;
 using Avalonia.Threading;
+using Avalonia.VisualTree;
 using Microsoft.Extensions.DependencyInjection;
 using Drawie.Backend.Core.Bridge;
+using PixiDocks.Avalonia.Helpers;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Helpers;
@@ -18,6 +22,7 @@ using PixiEditor.Models.ExceptionHandling;
 using PixiEditor.Models.IO;
 using PixiEditor.Platform;
 using PixiEditor.ViewModels.SubViewModels;
+using PixiEditor.Views.Main;
 using PixiEditor.Views.Rendering;
 using ViewModels_ViewModelMain = PixiEditor.ViewModels.ViewModelMain;
 
@@ -31,7 +36,7 @@ internal partial class MainWindow : Window
     private static ExtensionLoader extLoader;
 
     public StartupPerformance StartupPerformance { get; } = new();
-    
+
     public new ViewModels_ViewModelMain DataContext
     {
         get => (ViewModels_ViewModelMain)base.DataContext;
@@ -53,7 +58,7 @@ internal partial class MainWindow : Window
     public MainWindow(ExtensionLoader extensionLoader, Guid? analyticsSessionId = null)
     {
         StartupPerformance.ReportToMainWindow();
-        
+
         (Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).MainWindow = this;
         extLoader = extensionLoader;
 
@@ -63,7 +68,8 @@ internal partial class MainWindow : Window
             .AddExtensionServices(extensionLoader)
             .BuildServiceProvider();
 
-        AsyncImageLoader.ImageLoader.AsyncImageLoader = new DiskCachedWebImageLoader(Path.Combine(Paths.TempFilesPath, "ImageCache"));
+        AsyncImageLoader.ImageLoader.AsyncImageLoader =
+            new DiskCachedWebImageLoader(Path.Combine(Paths.TempFilesPath, "ImageCache"));
 
         preferences = services.GetRequiredService<IPreferences>();
         platform = services.GetRequiredService<IPlatform>();
@@ -127,12 +133,60 @@ internal partial class MainWindow : Window
     protected override void OnLoaded(RoutedEventArgs e)
     {
         base.OnLoaded(e);
+        
+        if (System.OperatingSystem.IsLinux())
+        {
+            MainTitleBar titleBar = this.FindDescendantOfType<MainTitleBar>(true);
+            titleBar.PointerPressed += OnTitleBarPressed;
+            
+            PointerMoved += UpdateResizeCursor;
+            AddHandler(PointerPressedEvent, Pressed, RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
+        }
+        
+
         LoadingWindow.Instance?.SafeClose();
         Activate();
         StartupPerformance.ReportToInteractivity();
         Analytics.SendStartup(StartupPerformance);
     }
 
+    private void UpdateResizeCursor(object? sender, PointerEventArgs e)
+    {
+        if(WindowState != WindowState.Normal)
+        {
+            return;
+        }
+        
+        Cursor = new Cursor(WindowUtility.SetResizeCursor(e, this, new Thickness(8)));
+    }
+
+    private void Pressed(object? sender, PointerPressedEventArgs e)
+    {
+        if (WindowState == WindowState.Normal && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+        {
+            var direction = WindowUtility.GetResizeDirection(e.GetPosition(this), this, new Thickness(8));
+            if(direction == null) return;
+            
+            BeginResizeDrag(direction.Value, e);
+        }
+    }
+
+    private void OnTitleBarPressed(object? sender, PointerPressedEventArgs e)
+    {
+        if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+        {
+            if(e.ClickCount == 2)
+            {
+                WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
+            }
+            else
+            {
+                BeginMoveDrag(e);
+                e.Handled = true;
+            }
+        }
+    }
+
     protected override void OnClosing(WindowClosingEventArgs e)
     {
         if (!DataContext.UserWantsToClose)