diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index 0309a6562..73ead0e66 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -171,4 +171,58 @@ private string FindExecutable(string filename) return File.Exists(local) ? local : string.Empty; } } + + [SupportedOSPlatform("linux")] + public static class LinuxUtilities + { + public const string TiledFrameClass = "window_frame_tiled"; + + private const int EdgeTolerance = 2; + + public static void UpdateCustomWindowFrameStyle(Window window) + { + if (OS.UseSystemWindowFrame || !window.Classes.Contains("custom_window_frame")) + return; + + var tiled = IsWindowTiledOrSnapped(window); + if (tiled) + { + if (!window.Classes.Contains(TiledFrameClass)) + window.Classes.Add(TiledFrameClass); + } + else if (window.Classes.Contains(TiledFrameClass)) + { + window.Classes.Remove(TiledFrameClass); + } + } + + private static bool IsWindowTiledOrSnapped(Window window) + { + if (window.WindowState is WindowState.Maximized or WindowState.FullScreen) + return false; + + if (window.Screens is not { } screens) + return false; + + var screen = screens.ScreenFromWindow(window); + if (screen == null) + return false; + + var workArea = screen.WorkingArea; + var size = PixelSize.FromSize(new Size(window.Width, window.Height), window.DesktopScaling); + var frame = new PixelRect(window.Position, size); + + var edgeCount = 0; + if (Math.Abs(frame.X - workArea.X) <= EdgeTolerance) + edgeCount++; + if (Math.Abs(frame.Right - workArea.Right) <= EdgeTolerance) + edgeCount++; + if (Math.Abs(frame.Y - workArea.Y) <= EdgeTolerance) + edgeCount++; + if (Math.Abs(frame.Bottom - workArea.Bottom) <= EdgeTolerance) + edgeCount++; + + return edgeCount >= 2; + } + } } diff --git a/src/Resources/Styles.axaml b/src/Resources/Styles.axaml index 4aaf3b23c..8f99811d8 100644 --- a/src/Resources/Styles.axaml +++ b/src/Resources/Styles.axaml @@ -126,7 +126,7 @@ Cursor="BottomRightCorner" Tag="{x:Static WindowEdge.SouthEast}"/> - + - + + + + diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs index ffc744598..945461524 100644 --- a/src/Views/ChromelessWindow.cs +++ b/src/Views/ChromelessWindow.cs @@ -26,6 +26,11 @@ public ChromelessWindow() { Focusable = true; Native.OS.SetupForWindow(this); + + if (OperatingSystem.IsLinux() && !UseSystemWindowFrame) + { + PositionChanged += (_, _) => Native.LinuxUtilities.UpdateCustomWindowFrameStyle(this); + } } public void BeginMoveWindow(object _, PointerPressedEventArgs e) @@ -81,6 +86,16 @@ protected override void OnLoaded(RoutedEventArgs e) if (OperatingSystem.IsWindows()) Native.Win64Utilities.FixWindowFrame(this); + else if (OperatingSystem.IsLinux()) + Native.LinuxUtilities.UpdateCustomWindowFrameStyle(this); + } + + protected override void OnSizeChanged(SizeChangedEventArgs e) + { + base.OnSizeChanged(e); + + if (OperatingSystem.IsLinux()) + Native.LinuxUtilities.UpdateCustomWindowFrameStyle(this); } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) @@ -89,6 +104,15 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (OperatingSystem.IsWindows() && change.Property == WindowStateProperty) Native.Win64Utilities.FixWindowFrame(this); + else if (OperatingSystem.IsLinux() && + Classes.Contains("custom_window_frame") && + (change.Property == WindowStateProperty || + change.Property == BoundsProperty || + change.Property == WidthProperty || + change.Property == HeightProperty)) + { + Native.LinuxUtilities.UpdateCustomWindowFrameStyle(this); + } } protected override void OnKeyDown(KeyEventArgs e)