diff --git a/windows/src/main/cpp/Decorations.cpp b/windows/src/main/cpp/Decorations.cpp index 5cf093dc..620eae97 100644 --- a/windows/src/main/cpp/Decorations.cpp +++ b/windows/src/main/cpp/Decorations.cpp @@ -37,7 +37,7 @@ static bool is_windows_11 = false; std::map wrapper_map = std::map(); -static bool Maximized(HWND hwnd) { +[[nodiscard]] static bool Maximized(HWND hwnd) { WINDOWPLACEMENT placement; if (!GetWindowPlacement(hwnd, &placement)) return false; return placement.showCmd == SW_MAXIMIZE; @@ -52,11 +52,11 @@ static bool IsLeftMousePressed(WindowWrapper *wrapper) { } } -static inline int GetFrameSize() { +[[nodiscard]] static inline int GetFrameSize() { return GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER); } -static LRESULT HandleHitTest(WindowWrapper *wrapper, int x, int y) { +[[nodiscard]] static LRESULT HandleHitTest(WindowWrapper *wrapper, int x, int y) { if (wrapper->popup_menu) return HTCLIENT; POINT ptMouse = { x, y }; @@ -69,6 +69,9 @@ static LRESULT HandleHitTest(WindowWrapper *wrapper, int x, int y) { USHORT uRow = 1; USHORT uCol = 1; + bool x_in_button_area = ptMouse.x > rcWindow.right - wrapper->right; + bool y_in_title_area = ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + wrapper->title_height; + if (!Maximized(wrapper->window)) { /* * The horizontal frame should be the same size as the vertical frame, @@ -80,9 +83,8 @@ static LRESULT HandleHitTest(WindowWrapper *wrapper, int x, int y) { // Make the top resize area smaller for the window buttons area. bool top = - ptMouse.x <= rcWindow.right - wrapper->right ? - ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + frame_size : - ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + 1; + !x_in_button_area ? ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + frame_size : + ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + 1; bool bottom = !top && (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - frame_size); @@ -127,6 +129,15 @@ static LRESULT HandleHitTest(WindowWrapper *wrapper, int x, int y) { && ptMouse.x <= rcWindow.right - wrapper->right) { return HTCAPTION; } + if (x_in_button_area && y_in_title_area) { + if (ptMouse.x < rcWindow.right - 2 * wrapper->button_width) { + return HTCLIENT; + } else if (ptMouse.x < rcWindow.right - wrapper->button_width) { + return HTMAXBUTTON; + } else { + return HTCLIENT; + } + } return HTCLIENT; } else { return hit; @@ -171,7 +182,7 @@ static void UpdateRegion(WindowWrapper *wrapper) { else SetWindowRgn(wrapper->window, CreateRectRgnIndirect(&wrapper->rgn), TRUE); } -static bool AutoHideTaskbar(UINT edge, RECT mon) { +[[nodiscard]] static bool AutoHideTaskbar(UINT edge, RECT mon) { APPBARDATA data; data.cbSize = sizeof(APPBARDATA); data.uEdge = edge; @@ -179,6 +190,30 @@ static bool AutoHideTaskbar(UINT edge, RECT mon) { return SHAppBarMessage(ABM_GETAUTOHIDEBAREX, &data); } +[[nodiscard]] static inline bool IsWindowSnappedTop(RECT rcWork, RECT rcWindow) { + return rcWindow.left == rcWork.left && rcWindow.right == rcWork.right && rcWindow.top == rcWork.top; +} + +[[nodiscard]] static inline bool IsWindowSnappedBottom(RECT rcWork, RECT rcWindow) { + return rcWindow.left == rcWork.left && rcWindow.right == rcWork.right && rcWindow.bottom == rcWork.bottom; +} + +[[nodiscard]] static inline bool IsWindowSnappedLeft(RECT rcWork, RECT rcWindow) { + return rcWindow.left == rcWork.left && rcWindow.bottom == rcWork.bottom && rcWindow.top == rcWork.top; +} + +[[nodiscard]] static inline bool IsWindowSnappedRight(RECT rcWork, RECT rcWindow) { + return rcWindow.right == rcWork.right && rcWindow.bottom == rcWork.bottom && rcWindow.top == rcWork.top; +} + +[[nodiscard]] static inline MONITORINFO GetMonitorInfo(WindowWrapper &wrapper) { + HMONITOR mon = MonitorFromWindow(wrapper.window, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfoW(mon, &mi); + return mi; +} + /** * Adjust the maximized frame size to respect auto hiding taskbars. */ @@ -212,10 +247,7 @@ static void HandleNCCalcSize(WindowWrapper *wrapper, WPARAM wparam, LPARAM lpara (*params.rect).right = client.right; (*params.rect).bottom = client.bottom; - HMONITOR mon = MonitorFromWindow(wrapper->window, MONITOR_DEFAULTTOPRIMARY); - MONITORINFO mi; - mi.cbSize = sizeof(mi); - GetMonitorInfoW(mon, &mi); + MONITORINFO mi = GetMonitorInfo(*wrapper); /* * If the client rectangle is the same as the monitor's rectangle, @@ -240,9 +272,22 @@ static void HandleNCCalcSize(WindowWrapper *wrapper, WPARAM wparam, LPARAM lpara */ if (wrapper->resizable) { int frame_size = GetFrameSize(); - nonclient.left += frame_size; - nonclient.right -= frame_size; - nonclient.bottom -= frame_size; + if (is_windows_11) { + MONITORINFO mi = GetMonitorInfo(*wrapper); + + bool top_equals = mi.rcMonitor.top == nonclient.top; + bool bottom_equals = mi.rcMonitor.bottom == nonclient.bottom; + bool left_equals = mi.rcMonitor.left == nonclient.left; + bool right_equals = mi.rcMonitor.right == nonclient.right; + + if (!(left_equals && (top_equals || bottom_equals))) nonclient.left += frame_size; + if (!(right_equals && (top_equals || bottom_equals))) nonclient.right -= frame_size; + if (!(bottom_equals && (left_equals || right_equals))) nonclient.bottom -= frame_size; + } else { + nonclient.left += frame_size; + nonclient.right -= frame_size; + nonclient.bottom -= frame_size; + } } *params.rect = nonclient; } @@ -281,17 +326,32 @@ static void ExtendClientFrame(HWND handle) { */ static void SetupWindowStyle(HWND handle) { auto style = GetWindowLongPtr(handle, GWL_STYLE); - style |= WS_THICKFRAME; + style |= WS_THICKFRAME | WS_MAXIMIZEBOX; SetWindowLongPtr(handle, GWL_STYLE, style); } +enum M_DWMWINDOWATTRIBUTE { + DWMWA_WINDOW_CORNER_PREFERENCE = 33 +}; + +enum DWM_WINDOW_CORNER_PREFERENCE { + DWMWCP_DEFAULT = 0, DWMWCP_DONOTROUND = 1, DWMWCP_ROUND = 2, DWMWCP_ROUNDSMALL = 3 +}; + static bool InstallDecorations(HWND handle, bool is_popup) { // Prevent multiple installations overriding the real window procedure. auto it = wrapper_map.find(handle); if (it != wrapper_map.end()) return false; SetupWindowStyle(handle); - ExtendClientFrame(handle); + if (is_popup || !is_windows_11) { + ExtendClientFrame(handle); + if (is_popup) { + auto attribute = M_DWMWINDOWATTRIBUTE::DWMWA_WINDOW_CORNER_PREFERENCE; + auto preference = DWM_WINDOW_CORNER_PREFERENCE::DWMWCP_ROUNDSMALL; + DwmSetWindowAttribute(handle, attribute, &preference, sizeof(preference)); + } + } WNDPROC proc = reinterpret_cast(GetWindowLongPtr(handle, GWLP_WNDPROC)); @@ -391,7 +451,38 @@ LRESULT CALLBACK WindowWrapper::WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ default: break; } - return CallWindowProc(wrapper->prev_proc, hwnd, uMsg, wParam, lParam); + if (is_windows_11) { + return WindowProc_Windows11(wrapper, uMsg, wParam, lParam); + } else { + return CallWindowProc(wrapper->prev_proc, hwnd, uMsg, wParam, lParam); + } +} + +[[maybe_unused]] LRESULT WindowWrapper::WindowProc_Windows11(WindowWrapper* wrapper, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) { + switch (uMsg) { + case WM_NCLBUTTONDOWN: + if (wParam == HTMAXBUTTON) { + wParam = 0; + uMsg = WM_LBUTTONDOWN; + } + break; + case WM_NCLBUTTONUP: + if (wParam == HTMAXBUTTON) { + wParam = 0; + uMsg = WM_LBUTTONUP; + } + break; + case WM_NCMOUSEMOVE: + if (wParam == HTMAXBUTTON) { + wParam = 0; + uMsg = WM_MOUSEMOVE; + } + break; + default: + break; + } + if (uMsg >= WM_NCXBUTTONUP && uMsg <= WM_NCXBUTTONUP) return TRUE; + return CallWindowProc(wrapper->prev_proc, wrapper->window, uMsg, wParam, lParam); } // @formatter:on JNIEXPORT void JNICALL @@ -404,13 +495,14 @@ Java_com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows_setResizabl } JNIEXPORT void JNICALL -Java_com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows_updateValues(JNIEnv*, jclass, jlong hwnd, jint l, jint r, jint h) { +Java_com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows_updateValues(JNIEnv*, jclass, jlong hwnd, jint l, jint r, jint h, jint bw) { HWND handle = reinterpret_cast(hwnd); auto wrap = wrapper_map[handle]; if (wrap) { wrap->left = l; wrap->right = r; wrap->title_height = h; + wrap->button_width = bw; } } diff --git a/windows/src/main/cpp/Decorations.h b/windows/src/main/cpp/Decorations.h index 5575eaeb..bc63fc1b 100644 --- a/windows/src/main/cpp/Decorations.h +++ b/windows/src/main/cpp/Decorations.h @@ -50,6 +50,7 @@ class WindowWrapper { HWND window; int width; int height; + int button_width; // The window region. RECT rgn; @@ -59,5 +60,5 @@ class WindowWrapper { int right = 0; int title_height = 0; - static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); + static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);static LRESULT WindowProc_Windows11(WindowWrapper*, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); }; diff --git a/windows/src/main/cpp/Registry.cpp b/windows/src/main/cpp/Registry.cpp new file mode 100644 index 00000000..909ce8f5 --- /dev/null +++ b/windows/src/main/cpp/Registry.cpp @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2021 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include "Registry.h" + +DWORD RegGetDword(HKEY hKey, const LPCSTR subKey, const LPCSTR value) { + DWORD data {}; + DWORD dataSize = sizeof(data); + DWORD flags = RRF_RT_REG_DWORD; + ModifyFlags(flags); + LONG retCode = ::RegGetValueA(hKey, subKey, value, flags, nullptr, &data, &dataSize); + if (retCode != ERROR_SUCCESS) throw retCode; + + return data; +} + +std::string RegGetString(HKEY hKey, const LPCSTR subKey, const LPCSTR value) { + DWORD dataSize {}; + DWORD flags = RRF_RT_REG_SZ; + ModifyFlags(flags); + LONG retCode = ::RegGetValueA(hKey, subKey, value, flags, nullptr, nullptr, &dataSize); + if (retCode != ERROR_SUCCESS) throw retCode; + + std::string data; + DWORD stringLengthInChars = dataSize / sizeof(char); + data.resize(stringLengthInChars); + retCode = ::RegGetValueA(hKey, subKey, value, flags, nullptr, &data[0], &dataSize); + if (retCode != ERROR_SUCCESS) throw retCode; + + return data; +} diff --git a/windows/src/main/cpp/Registry.h b/windows/src/main/cpp/Registry.h new file mode 100644 index 00000000..090f0745 --- /dev/null +++ b/windows/src/main/cpp/Registry.h @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2021 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#pragma once + +#include +#include +#include + +inline void ModifyFlags(DWORD &flags) { +#ifdef _WIN64 + flags |= RRF_SUBKEY_WOW6464KEY; +#else + flags |= RRF_SUBKEY_WOW6432KEY; +#endif +} + +DWORD RegGetDword(HKEY hKey, const LPCSTR subKey, const LPCSTR value); + +std::string RegGetString(HKEY hKey, const LPCSTR subKey, const LPCSTR value); diff --git a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/JNIDecorationsWindows.java b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/JNIDecorationsWindows.java index af99df46..a89b5f9c 100644 --- a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/JNIDecorationsWindows.java +++ b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/JNIDecorationsWindows.java @@ -32,7 +32,8 @@ public final class JNIDecorationsWindows { public static native long getWindowHWND(final Window window, final String javaLibPath); - public static native void updateValues(final long hwnd, final int left, final int right, final int height); + public static native void updateValues(final long hwnd, final int left, final int right, final int height, + final int buttonWidth); public static native void setResizable(final long hwnd, final boolean resizable);