From dd19ad4e66d5d5f8210745327f810b3710ddc91c Mon Sep 17 00:00:00 2001 From: weisj Date: Sat, 25 Apr 2020 16:46:56 +0200 Subject: [PATCH] Fixed popups not having a shadow [windows]. Fixed content being misplaced after window is restored into maximized position [windows]. --- .../darklaf/platform/DecorationsHandler.java | 10 +- .../platform/DefaultDecorationsProvider.java | 5 - .../weisj/darklaf/ui/DarkPopupFactory.java | 1 - .../ui/progressbar/DarkProgressBarUI.java | 4 +- .../ui/rootpane/DarkSubstanceRootLayout.java | 40 +-- .../github/weisj/darklaf/util/DarkUIUtil.java | 26 +- core/src/test/java/ui/ComponentDemo.java | 1 + .../platform/macos/ui/MacOSTitlePane.java | 5 - .../platform/decorations/CustomTitlePane.java | 2 - .../decorations/DecorationsProvider.java | 18 +- windows/build.gradle.kts | 4 +- windows/src/main/cpp/Decorations.cpp | 322 +++++++++++++----- windows/src/main/cpp/Decorations.h | 14 +- .../windows/WindowsDecorationsProvider.java | 34 +- .../platform/windows/ui/WindowsTitlePane.java | 25 +- 15 files changed, 358 insertions(+), 153 deletions(-) diff --git a/core/src/main/java/com/github/weisj/darklaf/platform/DecorationsHandler.java b/core/src/main/java/com/github/weisj/darklaf/platform/DecorationsHandler.java index 1929d8d8..ae73cd6b 100644 --- a/core/src/main/java/com/github/weisj/darklaf/platform/DecorationsHandler.java +++ b/core/src/main/java/com/github/weisj/darklaf/platform/DecorationsHandler.java @@ -79,7 +79,7 @@ public class DecorationsHandler { } public void installPopupWindow(final Window window) { - decorationsProvider.installPopupMenu(window); + decorationsProvider.installPopupWindow(window); } public void uninstallPopupWindow(final Window window) { @@ -108,4 +108,12 @@ public class DecorationsHandler { public void setDecorationsEnabled(final boolean enabled) { decorationsEnabled = enabled; } + + public void adjustContentArea(final JRootPane root, final Rectangle rect) { + decorationsProvider.adjustContentArea(root, rect); + } + + public void adjustWindowInsets(final Window window, final Insets i) { + decorationsProvider.adjustWindowInsets(window, i); + } } diff --git a/core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java b/core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java index 3b7f74b9..b13bd506 100644 --- a/core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java +++ b/core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java @@ -41,11 +41,6 @@ public class DefaultDecorationsProvider implements DecorationsProvider { @Override public void uninstall() {} - - @Override - public Insets getWindowSizeAdjustment() { - return new Insets(0, 0, 0, 0); - } }; } diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/DarkPopupFactory.java b/core/src/main/java/com/github/weisj/darklaf/ui/DarkPopupFactory.java index 0776ac4d..3ec17fe9 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/DarkPopupFactory.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/DarkPopupFactory.java @@ -81,7 +81,6 @@ public class DarkPopupFactory extends PopupFactory { && Boolean.TRUE.equals(((JComponent) contents).getClientProperty(KEY_NO_DECORATION))); boolean opaque = isJComponent && Boolean.TRUE.equals(((JComponent) contents).getClientProperty(KEY_OPAQUE)); - window.setBackground(PaintUtil.TRANSPARENT_COLOR); if (window instanceof RootPaneContainer) { JRootPane rootPane = ((RootPaneContainer) window).getRootPane(); if (opaque) { diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/progressbar/DarkProgressBarUI.java b/core/src/main/java/com/github/weisj/darklaf/ui/progressbar/DarkProgressBarUI.java index ec6ef477..85b8c9d3 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/progressbar/DarkProgressBarUI.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/progressbar/DarkProgressBarUI.java @@ -104,7 +104,7 @@ public class DarkProgressBarUI extends BasicProgressBarUI implements PropertyCha } Insets i = progressBar.getInsets(); - DarkUIUtil.removeInsets(r, i); + DarkUIUtil.applyInsets(r, i); int orientation = progressBar.getOrientation(); Color startColor, endColor; @@ -248,7 +248,7 @@ public class DarkProgressBarUI extends BasicProgressBarUI implements PropertyCha } Insets i = progressBar.getInsets(); - DarkUIUtil.removeInsets(r, i); + DarkUIUtil.applyInsets(r, i); int amountFull = getAmountFull(i, r.width, r.height); Shape fullShape, coloredShape; diff --git a/core/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkSubstanceRootLayout.java b/core/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkSubstanceRootLayout.java index d3461df6..b16662df 100644 --- a/core/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkSubstanceRootLayout.java +++ b/core/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkSubstanceRootLayout.java @@ -28,7 +28,9 @@ import java.awt.*; import javax.swing.*; +import com.github.weisj.darklaf.platform.DecorationsHandler; import com.github.weisj.darklaf.platform.decorations.CustomTitlePane; +import com.github.weisj.darklaf.util.DarkUIUtil; /** * @author Konstantin Bulenkov @@ -53,17 +55,10 @@ class DarkSubstanceRootLayout implements LayoutManager2 { int tpWidth = 0; int tpHeight = 0; Insets i = parent.getInsets(); - - Insets sizeAdjustment = new Insets(0, 0, 0, 0); - CustomTitlePane titlePane = getTitlePane(SwingUtilities.getRootPane(parent)); - if (titlePane != null) { - sizeAdjustment = titlePane.getWindowSizeAdjustment(); - } - - i.set(i.top + sizeAdjustment.top, i.left + sizeAdjustment.left, - i.bottom + sizeAdjustment.bottom, i.right + sizeAdjustment.right); + DecorationsHandler.getSharedInstance().adjustWindowInsets(DarkUIUtil.getWindow(parent), i); JRootPane root = (JRootPane) parent; + CustomTitlePane titlePane = getTitlePane(root); if (root.getContentPane() != null) { cpd = root.getContentPane().getPreferredSize(); @@ -125,8 +120,7 @@ class DarkSubstanceRootLayout implements LayoutManager2 { } if ((root.getWindowDecorationStyle() != JRootPane.NONE) && (root.getUI() instanceof DarkRootPaneUI)) { - JComponent titlePane = ((DarkRootPaneUI) root.getUI()) - .getTitlePane(); + JComponent titlePane = ((DarkRootPaneUI) root.getUI()).getTitlePane(); if (titlePane != null) { tpd = titlePane.getMinimumSize(); if (tpd != null) { @@ -152,34 +146,42 @@ class DarkSubstanceRootLayout implements LayoutManager2 { public void layoutContainer(final Container parent) { JRootPane root = (JRootPane) parent; Rectangle b = root.getBounds(); + Insets i = root.getInsets(); - int nextY = 0; - int w = b.width - i.right - i.left; - int h = b.height - i.top - i.bottom; + + b.setLocation(0, 0); + DarkUIUtil.applyInsets(b, i); if (root.getLayeredPane() != null) { - root.getLayeredPane().setBounds(i.left, i.top, w, h); + root.getLayeredPane().setBounds(b.x, b.y, b.width, b.height); } if (root.getGlassPane() != null) { - root.getGlassPane().setBounds(i.left, i.top, w, h); + root.getGlassPane().setBounds(b.x, b.y, b.width, b.height); } + DecorationsHandler.getSharedInstance().adjustContentArea(root, b); + + layoutContent(root, b.x, b.y, b.width, b.height); + } + + public void layoutContent(final JRootPane root, final int x, final int y, final int w, final int h) { + int nextY = y; JComponent titlePane = getTitlePane(root); if (titlePane != null) { Dimension tpd = titlePane.getPreferredSize(); if (tpd != null) { int tpHeight = tpd.height; - titlePane.setBounds(0, 0, w, tpHeight); + titlePane.setBounds(x, y, w, tpHeight); nextY += tpHeight; } } if (root.getJMenuBar() != null) { Dimension mbd = root.getJMenuBar().getPreferredSize(); - root.getJMenuBar().setBounds(0, nextY, w, mbd.height); + root.getJMenuBar().setBounds(x, nextY, w, mbd.height); nextY += mbd.height; } if (root.getContentPane() != null) { - root.getContentPane().setBounds(0, nextY, w, h < nextY ? 0 : h - nextY); + root.getContentPane().setBounds(x, nextY, w, h < nextY ? 0 : h - nextY); } } diff --git a/core/src/main/java/com/github/weisj/darklaf/util/DarkUIUtil.java b/core/src/main/java/com/github/weisj/darklaf/util/DarkUIUtil.java index 7e416944..84a6cc2e 100644 --- a/core/src/main/java/com/github/weisj/darklaf/util/DarkUIUtil.java +++ b/core/src/main/java/com/github/weisj/darklaf/util/DarkUIUtil.java @@ -61,17 +61,29 @@ public final class DarkUIUtil { if (insets != null && rect != null) { rect.x += insets.left; rect.y += insets.top; - rect.width -= (insets.right + rect.x); - rect.height -= (insets.bottom + rect.y); + rect.width -= (insets.right + insets.left); + rect.height -= (insets.bottom + insets.top); } } + public static Insets addInsets(final Insets ins1, final Insets ins2) { + if (ins2 == null) return ins1; + if (ins1 != null) { + ins1.left += ins2.left; + ins1.right += ins2.right; + ins1.top += ins2.top; + ins1.bottom += ins2.bottom; + return ins1; + } + return null; + } + public static void removeInsets(final Rectangle rectangle, final Insets insets) { - if (insets != null) { - rectangle.x += insets.left; - rectangle.y += insets.top; - rectangle.width -= insets.left + insets.right; - rectangle.height -= insets.top + insets.bottom; + if (insets != null && rectangle != null) { + rectangle.x -= insets.left; + rectangle.y -= insets.top; + rectangle.width += insets.left + insets.right; + rectangle.height += insets.top + insets.bottom; } } diff --git a/core/src/test/java/ui/ComponentDemo.java b/core/src/test/java/ui/ComponentDemo.java index b67cf108..3087be0d 100644 --- a/core/src/test/java/ui/ComponentDemo.java +++ b/core/src/test/java/ui/ComponentDemo.java @@ -57,6 +57,7 @@ public interface ComponentDemo { static void showDemo(final ComponentDemo demo, final Dimension dimension) { SwingUtilities.invokeLater(() -> { + LafManager.setDecorationsEnabled(true); LafManager.install(demo.createTheme()); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); diff --git a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java index 47010e75..0177e427 100644 --- a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java +++ b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java @@ -144,11 +144,6 @@ public class MacOSTitlePane extends CustomTitlePane { decorationInformation = null; } - @Override - public Insets getWindowSizeAdjustment() { - return new Insets(0, 0, 0, 0); - } - private void installListeners() { if (window != null && useCustomTitle()) { windowListener = new WindowHandler(); diff --git a/platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/CustomTitlePane.java b/platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/CustomTitlePane.java index 3d786af6..dd3874a6 100644 --- a/platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/CustomTitlePane.java +++ b/platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/CustomTitlePane.java @@ -39,8 +39,6 @@ public abstract class CustomTitlePane extends JComponent { */ protected abstract void install(); - public abstract Insets getWindowSizeAdjustment(); - @Override public void addNotify() { super.addNotify(); diff --git a/platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/DecorationsProvider.java b/platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/DecorationsProvider.java index 82e4db91..bae629e5 100644 --- a/platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/DecorationsProvider.java +++ b/platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/DecorationsProvider.java @@ -65,10 +65,26 @@ public interface DecorationsProvider { /** * Initialize the window of a popup menu. */ - default void installPopupMenu(final Window window) {} + default void installPopupWindow(final Window window) {} /** * Uninstall the window of a popup menu. */ default void uninstallPopupWindow(final Window window) {} + + /** + * Adjusts the content area. + * + * @param root the corresponding root pane. + * @param rect the proposed content area. + */ + default void adjustContentArea(final JRootPane root, final Rectangle rect) {} + + /** + * Adjust the window insets. + * + * @param window the corresponding window. + * @param i the insets to adjust. + */ + default void adjustWindowInsets(final Window window, final Insets i) {} } diff --git a/windows/build.gradle.kts b/windows/build.gradle.kts index 4ac1253d..4c3a2a39 100644 --- a/windows/build.gradle.kts +++ b/windows/build.gradle.kts @@ -32,8 +32,8 @@ library { binaries.whenElementFinalized(CppSharedLibrary::class) { linkTask.get().linkerArgs.addAll( when (toolChain) { - is Gcc, is Clang -> listOf("-ldwmapi", "-lGdi32", "-luser32", "-ladvapi32") - is VisualCpp -> listOf("dwmapi.lib", "user32.lib", "Gdi32.lib", "Advapi32.lib") + is Gcc, is Clang -> listOf("-ldwmapi", "-lGdi32", "-luser32", "-ladvapi32", "-Shell32") + is VisualCpp -> listOf("dwmapi.lib", "user32.lib", "Gdi32.lib", "Advapi32.lib", "Shell32.lib") else -> emptyList() } ) diff --git a/windows/src/main/cpp/Decorations.cpp b/windows/src/main/cpp/Decorations.cpp index bc9ec01e..77b4f615 100644 --- a/windows/src/main/cpp/Decorations.cpp +++ b/windows/src/main/cpp/Decorations.cpp @@ -24,47 +24,87 @@ */ #include "Decorations.h" #include "com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows.h" -#include -#include -#include -#include + +#ifndef WM_NCUAHDRAWCAPTION +#define WM_NCUAHDRAWCAPTION (0x00AE) +#endif +#ifndef WM_NCUAHDRAWFRAME +#define WM_NCUAHDRAWFRAME (0x00AF) +#endif std::map wrapper_map = std::map(); -LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam, WindowWrapper *wrapper) +bool Maximized(HWND hwnd) +{ + WINDOWPLACEMENT placement; + if (!GetWindowPlacement(hwnd, &placement)) + return false; + return placement.showCmd == SW_MAXIMIZE; +} + +LRESULT HandleHitTest(WindowWrapper *wrapper, int x, int y) { if (wrapper->popup_menu) return HTCLIENT; - // Get the point coordinates for the hit test. - POINT ptMouse = { GET_X_LPARAM(lParam), - GET_Y_LPARAM(lParam) }; + + POINT ptMouse = { x, + y }; // Get the window rectangle. RECT rcWindow; - GetWindowRect(hWnd, &rcWindow); + GetWindowRect(wrapper->window, &rcWindow); // Determine if the hit test is for resizing. Default middle (1,1). USHORT uRow = 1; USHORT uCol = 1; - // Determine if the point is at the top or bottom of the window. - if (ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + 5) + if (!Maximized(wrapper->window)) { - uRow = 0; - } - else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - 5) - { - uRow = 2; - } + /* The horizontal frame should be the same size as the vertical frame, + since the NONCLIENTMETRICS structure does not distinguish between them */ + int frame_size = GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER); + /* The diagonal size handles are wider than the frame */ + int diagonal_width = frame_size * 2 + GetSystemMetrics(SM_CXBORDER); - // Determine if the point is at the left or right of the window. - if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + 5) - { - uCol = 0; // left side - } - else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - 5) - { - uCol = 2; // right side + bool top = ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + frame_size; + bool bottom = !top && (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - frame_size); + + bool left = ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + frame_size; + bool right = !left && (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - frame_size); + + bool diag_top = ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + diagonal_width; + bool diag_bottom = !diag_top && (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - diagonal_width); + + bool diag_left = ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + frame_size; + bool diag_right = !diag_left && (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - diagonal_width); + + if (top) + uRow = 0; + if (bottom) + uRow = 2; + + if (top || bottom) + { + if (diag_left) + uCol = 0; + else if (diag_right) + uCol = 2; + } + else + { + if (left) + uCol = 0; + if (right) + uCol = 2; + + if (left || right) + { + if (diag_top) + uRow = 0; + else if (diag_bottom) + uRow = 2; + } + } } // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT) @@ -76,12 +116,12 @@ LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam, WindowWrapper *wrapp HTRIGHT }, { HTBOTTOMLEFT, HTBOTTOM, - HTBOTTOMRIGHT }, }; + HTBOTTOMRIGHT } }; LRESULT hit = hitTests[uRow][uCol]; if (hit == HTNOWHERE || !wrapper->resizable) { //Handle window drag. - if (ptMouse.y < rcWindow.top + wrapper->height && ptMouse.x >= rcWindow.left + wrapper->left + if (ptMouse.y < rcWindow.top + wrapper->title_height && ptMouse.x >= rcWindow.left + wrapper->left && ptMouse.x <= rcWindow.right - wrapper->right) { return HTCAPTION; @@ -94,12 +134,54 @@ LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam, WindowWrapper *wrapp } } -bool Maximized(HWND hwnd) +void UpdateRegion(WindowWrapper *wrapper) { - WINDOWPLACEMENT placement; - if (!GetWindowPlacement(hwnd, &placement)) - return false; - return placement.showCmd == SW_MAXIMIZE; + if (wrapper->popup_menu) + return; + RECT old_rgn = wrapper->rgn; + + if (Maximized(wrapper->window)) + { + WINDOWINFO wi; + wi.cbSize = sizeof(WINDOWINFO); + GetWindowInfo(wrapper->window, &wi); + + /* For maximized windows, a region is needed to cut off the non-client + borders that hang over the edge of the screen */ + wrapper->rgn.left = wi.rcClient.left - wi.rcWindow.left; + wrapper->rgn.top = wi.rcClient.top - wi.rcWindow.top; + wrapper->rgn.right = wi.rcClient.right - wi.rcWindow.left; + wrapper->rgn.bottom = wi.rcClient.bottom - wi.rcWindow.top; + + } + else + { + /* Don't mess with the region when composition is enabled and the + window is not maximized, otherwise it will lose its shadow */ + wrapper->rgn.left = 0; + wrapper->rgn.top = 0; + wrapper->rgn.right = 0; + wrapper->rgn.bottom = 0; + } + + /* Avoid unnecessarily updating the region to avoid unnecessary redraws */ + if (EqualRect(&wrapper->rgn, &old_rgn)) + return; + /* Treat empty regions as NULL regions */ + RECT empty = { 0 }; + if (EqualRect(&wrapper->rgn, &empty)) + SetWindowRgn(wrapper->window, NULL, TRUE); + else + SetWindowRgn(wrapper->window, CreateRectRgnIndirect(&wrapper->rgn), TRUE); +} + +bool AutoHideTaskbar(UINT edge, RECT mon) +{ + APPBARDATA data; + data.cbSize = sizeof(APPBARDATA); + data.uEdge = edge; + data.rc = mon; + return SHAppBarMessage(ABM_GETAUTOHIDEBAREX, &data); } void AdjustMaximizedClientArea(HWND window, RECT &rect) @@ -107,36 +189,94 @@ void AdjustMaximizedClientArea(HWND window, RECT &rect) if (!Maximized(window)) return; - auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); if (!monitor) return; MONITORINFO monitor_info {}; - monitor_info.cbSize = sizeof(monitor_info); - if (!GetMonitorInfoW(monitor, &monitor_info)) + monitor_info.cbSize = sizeof(MONITORINFO); + if (!GetMonitorInfo(monitor, &monitor_info)) return; rect = monitor_info.rcWork; } -void AdjustMinMaxInfo(HWND hwnd, LPARAM lParam) +void HandleNCCalcSize(WindowWrapper *wrapper, WPARAM wparam, LPARAM lparam) +{ + union + { + LPARAM lparam; + RECT *rect; + } params; + params.lparam = lparam; + + /* DefWindowProc must be called in both the maximized and non-maximized + cases, otherwise tile/cascade windows won't work */ + RECT nonclient = *params.rect; + DefWindowProc(wrapper->window, WM_NCCALCSIZE, wparam, lparam); + RECT client = *params.rect; + + if (Maximized(wrapper->window)) + { + WINDOWINFO wi; + wi.cbSize = sizeof(wi); + GetWindowInfo(wrapper->window, &wi); + + /* Maximized windows always have a non-client border that hangs over + the edge of the screen, so the size proposed by WM_NCCALCSIZE is + fine. Just adjust the top border to remove the window title. */ + (*params.rect).left = client.left; + (*params.rect).top = nonclient.top + wi.cyWindowBorders; + (*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); + + /* + * If the client rectangle is the same as the monitor's rectangle, + * the shell assumes that the window has gone fullscreen, so it removes + * the topmost attribute from any auto-hide appbars, making them + * inaccessible. To avoid this, reduce the size of the client area by + * one pixel on a certain edge. The edge is chosen based on which side + * of the monitor is likely to contain an auto-hide appbar, so the + * missing client area is covered by it. + */ + if (EqualRect(params.rect, &mi.rcMonitor)) + { + if (AutoHideTaskbar(ABE_BOTTOM, mi.rcMonitor)) + params.rect->bottom--; + else if (AutoHideTaskbar(ABE_LEFT, mi.rcMonitor)) + params.rect->left++; + else if (AutoHideTaskbar(ABE_TOP, mi.rcMonitor)) + params.rect->top++; + else if (AutoHideTaskbar(ABE_RIGHT, mi.rcMonitor)) + params.rect->right--; + } + } + else + { + /* For the non-maximized case, set the output RECT to what it was + before WM_NCCALCSIZE modified it. This will make the client size the + same as the non-client size. */ + *params.rect = nonclient; + } +} + +void HandleWindowPosChanged(WindowWrapper *wrapper, const WINDOWPOS *pos) { - HMONITOR hPrimaryMonitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY); - HMONITOR hTargetMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - - MONITORINFO primaryMonitorInfo { sizeof(MONITORINFO) }; - MONITORINFO targetMonitorInfo { sizeof(MONITORINFO) }; - - GetMonitorInfo(hPrimaryMonitor, &primaryMonitorInfo); - GetMonitorInfo(hTargetMonitor, &targetMonitorInfo); - - MINMAXINFO *min_max_info = reinterpret_cast(lParam); - RECT max_rect = primaryMonitorInfo.rcWork; - RECT target_rect = targetMonitorInfo.rcWork; - min_max_info->ptMaxSize.x = target_rect.right - target_rect.left; - min_max_info->ptMaxSize.y = target_rect.bottom - target_rect.top; - min_max_info->ptMaxPosition.x = max_rect.left; - min_max_info->ptMaxPosition.y = max_rect.top; + RECT client; + GetClientRect(wrapper->window, &client); + LONG old_width = wrapper->width; + LONG old_height = wrapper->height; + wrapper->width = client.right; + wrapper->height = client.bottom; + bool client_changed = wrapper->width != old_width || wrapper->height != old_height; + + if (client_changed || (pos->flags & SWP_FRAMECHANGED)) + UpdateRegion(wrapper); } void PaintBackground(HWND hwnd, WPARAM wParam, WindowWrapper *wrapper) @@ -152,45 +292,49 @@ LRESULT CALLBACK WindowWrapper::WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ HWND handle = reinterpret_cast(hwnd); auto wrapper = wrapper_map[handle]; - if (uMsg == WM_NCACTIVATE) + switch(uMsg) { + case WM_NCACTIVATE: return TRUE; - } - else if (uMsg == WM_NCCALCSIZE) - { + case WM_NCCALCSIZE: if (wParam == TRUE) { - NCCALCSIZE_PARAMS& params = *reinterpret_cast(lParam); - AdjustMaximizedClientArea(handle, params.rgrc[0]); - return TRUE; + HandleNCCalcSize(wrapper, wParam, lParam); + UpdateRegion(wrapper); + return 0; } - } - else if (uMsg == WM_GETMINMAXINFO) - { - AdjustMinMaxInfo(hwnd, lParam); - return FALSE; - } - else if (uMsg == WM_NCHITTEST) - { - return HitTestNCA(hwnd, wParam, lParam, wrapper); - } - else if (uMsg == WM_MOVE) - { + break; + case WM_NCHITTEST: + return HandleHitTest(wrapper, GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)); + case WM_MOVE: wrapper->moving = wrapper->move_mode; - } - else if (uMsg == WM_ENTERSIZEMOVE) - { + break; + case WM_ENTERSIZEMOVE: wrapper->move_mode = TRUE; - } - else if (uMsg == WM_EXITSIZEMOVE) - { + break; + case WM_EXITSIZEMOVE: wrapper->moving = FALSE; wrapper->move_mode = FALSE; - } - else if ((uMsg == WM_PAINT || uMsg == WM_ERASEBKGND) && wrapper->bgBrush) - { - if (!wrapper->moving) PaintBackground(hwnd, wParam, wrapper); - if (uMsg == WM_ERASEBKGND) return TRUE; + break; + case WM_ERASEBKGND: + case WM_PAINT: + if (!wrapper->bgBrush) + break; + if (!wrapper->moving) + PaintBackground(hwnd, wParam, wrapper); + if (uMsg == WM_ERASEBKGND) + return TRUE; + break; + case WM_NCUAHDRAWCAPTION: + case WM_NCUAHDRAWFRAME: + /* These undocumented messages are sent to draw themed window borders. + Block them to prevent drawing borders over the client area. */ + return 0; + case WM_WINDOWPOSCHANGED: + HandleWindowPosChanged(wrapper, (WINDOWPOS*)lParam); + break; + default: + break; } return CallWindowProc(wrapper->prev_proc, hwnd, uMsg, wParam, lParam); @@ -217,7 +361,7 @@ Java_com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows_updateValue { wrap->left = l; wrap->right = r; - wrap->height = h; + wrap->title_height = h; } } @@ -236,15 +380,16 @@ void ExtendClientFrame(HWND handle) { MARGINS margins = { 0, 0, - 0, - 1 }; + 1, + 0 }; DwmExtendFrameIntoClientArea(handle, &margins); } -void SetupWindowStyle(HWND handle) +void SetupWindowStyle(HWND handle, bool is_popup) { auto style = GetWindowLongPtr(handle, GWL_STYLE); - SetWindowLongPtr(handle, GWL_STYLE, (style | WS_THICKFRAME)); + style |= WS_THICKFRAME; + SetWindowLongPtr(handle, GWL_STYLE, style); } bool InstallDecorations(HWND handle, bool is_popup) @@ -254,12 +399,13 @@ bool InstallDecorations(HWND handle, bool is_popup) if (it != wrapper_map.end()) return false; - SetupWindowStyle(handle); + SetupWindowStyle(handle, is_popup); ExtendClientFrame(handle); WNDPROC proc = reinterpret_cast(GetWindowLongPtr(handle, GWLP_WNDPROC)); WindowWrapper *wrapper = new WindowWrapper(); + wrapper->window = handle; wrapper->prev_proc = proc; wrapper->popup_menu = is_popup; wrapper_map[handle] = wrapper; diff --git a/windows/src/main/cpp/Decorations.h b/windows/src/main/cpp/Decorations.h index 6cb20e9a..f19774cf 100644 --- a/windows/src/main/cpp/Decorations.h +++ b/windows/src/main/cpp/Decorations.h @@ -25,6 +25,11 @@ #include #include #include +#include +#include +#include +#include +#include class WindowWrapper { @@ -33,12 +38,19 @@ class WindowWrapper bool popup_menu = false; bool moving = false; bool move_mode = false; + bool maximized = false; WNDPROC prev_proc; HBRUSH bgBrush; + HWND window; + int width; + int height; + + RECT rgn; + int left = 0; int right = 0; - int height = 0; + int title_height = 0; static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); }; diff --git a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java index 31dd8603..1122fade 100644 --- a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java +++ b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java @@ -28,6 +28,7 @@ import java.awt.*; import java.util.Properties; import javax.swing.*; +import javax.swing.border.Border; import com.github.weisj.darklaf.PropertyLoader; import com.github.weisj.darklaf.icons.IconLoader; @@ -53,15 +54,18 @@ public class WindowsDecorationsProvider implements DecorationsProvider { } @Override - public void installPopupMenu(final Window window) { + public void installPopupWindow(final Window window) { if (!window.isDisplayable()) { window.addNotify(); } long hwnd = PointerUtil.getHWND(window); if (hwnd != 0) { - if (JNIDecorationsWindows.installPopupMenuDecorations(hwnd)) { - Color bg = window.getBackground(); - JNIDecorationsWindows.setBackground(hwnd, bg.getRed(), bg.getGreen(), bg.getBlue()); + if (JNIDecorationsWindows.installPopupMenuDecorations(hwnd) && window instanceof RootPaneContainer) { + JRootPane rootPane = ((RootPaneContainer) window).getRootPane(); + Color bg = rootPane != null ? rootPane.getBackground() : null; + if (bg != null) { + JNIDecorationsWindows.setBackground(hwnd, bg.getRed(), bg.getGreen(), bg.getBlue()); + } } } } @@ -77,6 +81,28 @@ public class WindowsDecorationsProvider implements DecorationsProvider { } } + @Override + public void adjustContentArea(final JRootPane root, final Rectangle rect) { + Border border = root.getBorder(); + if (border != null) { + Insets ins = border.getBorderInsets(root); + rect.x -= ins.left; + rect.y -= ins.top; + } + } + + @Override + public void adjustWindowInsets(final Window window, final Insets i) { + // Compensate for the insets of the native window peer that include the decorations. + if (window != null) { + Insets insets = window.getInsets(); + i.left -= insets.left; + i.right -= insets.right; + i.top -= insets.top; + i.bottom -= insets.bottom; + } + } + @Override public void loadDecorationProperties(final Properties properties, final UIDefaults currentDefaults) { IconLoader iconLoader = IconLoader.get(WindowsDecorationsProvider.class); diff --git a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java index 81a25b43..944306ab 100644 --- a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java +++ b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java @@ -32,6 +32,7 @@ import java.util.List; import javax.accessibility.AccessibleContext; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.plaf.UIResource; import sun.awt.SunToolkit; @@ -115,6 +116,7 @@ public class WindowsTitlePane extends CustomTitlePane { private Color activeBackground; private Color activeForeground; private Color border; + private Border oldBorder; public WindowsTitlePane(final JRootPane root, final int decorationStyle, final Window window) { this.rootPane = root; @@ -169,16 +171,6 @@ public class WindowsTitlePane extends CustomTitlePane { } } - @Override - public Insets getWindowSizeAdjustment() { - // Compensate for the insets of the native window peer that include the decorations. - Insets insets = window != null && windowHandle != 0 - ? window.getInsets() - : new Insets(0, 0, 0, 0); - insets.set(-insets.top, -insets.left, -insets.bottom, -insets.right); - return insets; - } - private void uninstallListeners() { if (window != null) { window.removeWindowListener(windowListener); @@ -507,11 +499,14 @@ public class WindowsTitlePane extends CustomTitlePane { if (frame != null) { JRootPane rootPane = getRootPane(); - if (((state & Frame.MAXIMIZED_BOTH) != 0) - && (rootPane.getBorder() == null - || (rootPane.getBorder() instanceof UIResource)) - && frame.isShowing()) { - rootPane.setBorder(null); + if (((state & Frame.MAXIMIZED_BOTH) != 0)) { + oldBorder = rootPane.getBorder(); + LookAndFeel.uninstallBorder(rootPane); + } else { + Border border = rootPane.getBorder(); + if (oldBorder != null && border == null || border instanceof UIResource) { + rootPane.setBorder(oldBorder); + } } if (frame.isResizable()) {