diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index bfb98500..a7202ff2 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -5,6 +5,7 @@ using System.IO; using System.Runtime.Versioning; using Avalonia; +using Avalonia.Controls; namespace SourceGit.Native { @@ -16,6 +17,11 @@ namespace SourceGit.Native builder.With(new X11PlatformOptions() { EnableIme = true }); } + public void SetupWindow(Window window) + { + // Do Nothing. + } + public string FindGitExecutable() { return FindExecutable("git"); diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index 0966233f..fa3b1d2c 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -5,6 +5,7 @@ using System.IO; using System.Runtime.Versioning; using Avalonia; +using Avalonia.Controls; namespace SourceGit.Native { @@ -36,6 +37,11 @@ namespace SourceGit.Native Environment.SetEnvironmentVariable("PATH", path); } + public void SetupWindow(Window window) + { + // Do Nothing. + } + public string FindGitExecutable() { var gitPathVariants = new List() { diff --git a/src/Native/OS.cs b/src/Native/OS.cs index 320b5208..fb211cf2 100644 --- a/src/Native/OS.cs +++ b/src/Native/OS.cs @@ -6,6 +6,7 @@ using System.Text; using System.Text.RegularExpressions; using Avalonia; +using Avalonia.Controls; namespace SourceGit.Native { @@ -14,6 +15,7 @@ namespace SourceGit.Native public interface IBackend { void SetupApp(AppBuilder builder); + void SetupWindow(Window window); string FindGitExecutable(); string FindTerminal(Models.ShellOrTerminal shell); @@ -121,6 +123,11 @@ namespace SourceGit.Native ExternalTools = _backend.FindExternalTools(); } + public static void SetupForWindow(Window window) + { + _backend.SetupWindow(window); + } + public static string FindGitExecutable() { return _backend.FindGitExecutable(); diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index eb354f10..c6459656 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -27,6 +27,42 @@ namespace SourceGit.Native internal string szCSDVersion; } + internal struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + } + + internal enum HitTest + { + HTERROR = -2, + HTTRANSPARENT = -1, + HTNOWHERE = 0, + HTCLIENT = 1, + HTCAPTION = 2, + HTSYSMENU = 3, + HTGROWBOX = 4, + HTMENU = 5, + HTHSCROLL = 6, + HTVSCROLL = 7, + HTMINBUTTON = 8, + HTMAXBUTTON = 9, + HTLEFT = 10, + HTRIGHT = 11, + HTTOP = 12, + HTTOPLEFT = 13, + HTTOPRIGHT = 14, + HTBOTTOM = 15, + HTBOTTOMLEFT = 16, + HTBOTTOMRIGHT = 17, + HTBORDER = 18, + HTOBJECT = 19, + HTCLOSE = 20, + HTHELP = 21 + } + [StructLayout(LayoutKind.Sequential)] internal struct MARGINS { @@ -54,6 +90,9 @@ namespace SourceGit.Native [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)] private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags); + [DllImport("user32.dll")] + private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); + public void SetupApp(AppBuilder builder) { // Fix drop shadow issue on Windows 10 @@ -66,6 +105,44 @@ namespace SourceGit.Native } } + public void SetupWindow(Window window) + { + Win32Properties.AddWndProcHookCallback(window, (IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool handled) => + { + // Custom WM_NCHITTEST + if (msg == 0x0084) + { + var p = IntPtrToPixelPoint(lParam); + GetWindowRect(hWnd, out var rcWindow); + + var borderThinkness = (int)(4 * window.RenderScaling); + int row = 1; + int col = 1; + if (p.X >= rcWindow.left && p.X < rcWindow.left + borderThinkness) + col = 0; + else if (p.X < rcWindow.right && p.X >= rcWindow.right - borderThinkness) + col = 2; + + if (p.Y >= rcWindow.top && p.Y < rcWindow.top + borderThinkness) + row = 0; + else if (p.Y < rcWindow.bottom && p.Y >= rcWindow.bottom - borderThinkness) + row = 2; + + ReadOnlySpan zones = stackalloc HitTest[] + { + HitTest.HTTOPLEFT, HitTest.HTTOP, HitTest.HTTOPRIGHT, + HitTest.HTLEFT, HitTest.HTCLIENT, HitTest.HTRIGHT, + HitTest.HTBOTTOMLEFT, HitTest.HTBOTTOM, HitTest.HTBOTTOMRIGHT + }; + + handled = true; + return (IntPtr)(zones[row * 3 + col]); + } + + return IntPtr.Zero; + }); + } + public string FindGitExecutable() { var reg = Microsoft.Win32.RegistryKey.OpenBaseKey( @@ -228,6 +305,12 @@ namespace SourceGit.Native }, DispatcherPriority.Render); } + private PixelPoint IntPtrToPixelPoint(IntPtr param) + { + var v = IntPtr.Size == 4 ? param.ToInt32() : (int)(param.ToInt64() & 0xFFFFFFFF); + return new PixelPoint((short)(v & 0xffff), (short)(v >> 16)); + } + #region EXTERNAL_EDITOR_FINDER private string FindVSCode() { diff --git a/src/Views/ChromelessWindow.cs b/src/Views/ChromelessWindow.cs index dd2485d4..557909fb 100644 --- a/src/Views/ChromelessWindow.cs +++ b/src/Views/ChromelessWindow.cs @@ -45,6 +45,8 @@ namespace SourceGit.Views ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.SystemChrome; ExtendClientAreaToDecorationsHint = true; } + + Native.OS.SetupForWindow(this); } public void BeginMoveWindow(object _, PointerPressedEventArgs e)