Browse Source

Fixed popups not having a shadow [windows].

Fixed content being misplaced after window is restored into maximized position [windows].
pull/154/head
weisj 5 years ago
parent
commit
dd19ad4e66
  1. 10
      core/src/main/java/com/github/weisj/darklaf/platform/DecorationsHandler.java
  2. 5
      core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java
  3. 1
      core/src/main/java/com/github/weisj/darklaf/ui/DarkPopupFactory.java
  4. 4
      core/src/main/java/com/github/weisj/darklaf/ui/progressbar/DarkProgressBarUI.java
  5. 40
      core/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkSubstanceRootLayout.java
  6. 26
      core/src/main/java/com/github/weisj/darklaf/util/DarkUIUtil.java
  7. 1
      core/src/test/java/ui/ComponentDemo.java
  8. 5
      macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java
  9. 2
      platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/CustomTitlePane.java
  10. 18
      platform-base/src/main/java/com/github/weisj/darklaf/platform/decorations/DecorationsProvider.java
  11. 4
      windows/build.gradle.kts
  12. 322
      windows/src/main/cpp/Decorations.cpp
  13. 14
      windows/src/main/cpp/Decorations.h
  14. 34
      windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java
  15. 25
      windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java

10
core/src/main/java/com/github/weisj/darklaf/platform/DecorationsHandler.java

@ -79,7 +79,7 @@ public class DecorationsHandler {
} }
public void installPopupWindow(final Window window) { public void installPopupWindow(final Window window) {
decorationsProvider.installPopupMenu(window); decorationsProvider.installPopupWindow(window);
} }
public void uninstallPopupWindow(final Window window) { public void uninstallPopupWindow(final Window window) {
@ -108,4 +108,12 @@ public class DecorationsHandler {
public void setDecorationsEnabled(final boolean enabled) { public void setDecorationsEnabled(final boolean enabled) {
decorationsEnabled = 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);
}
} }

5
core/src/main/java/com/github/weisj/darklaf/platform/DefaultDecorationsProvider.java

@ -41,11 +41,6 @@ public class DefaultDecorationsProvider implements DecorationsProvider {
@Override @Override
public void uninstall() {} public void uninstall() {}
@Override
public Insets getWindowSizeAdjustment() {
return new Insets(0, 0, 0, 0);
}
}; };
} }

1
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.TRUE.equals(((JComponent) contents).getClientProperty(KEY_NO_DECORATION)));
boolean opaque = isJComponent boolean opaque = isJComponent
&& Boolean.TRUE.equals(((JComponent) contents).getClientProperty(KEY_OPAQUE)); && Boolean.TRUE.equals(((JComponent) contents).getClientProperty(KEY_OPAQUE));
window.setBackground(PaintUtil.TRANSPARENT_COLOR);
if (window instanceof RootPaneContainer) { if (window instanceof RootPaneContainer) {
JRootPane rootPane = ((RootPaneContainer) window).getRootPane(); JRootPane rootPane = ((RootPaneContainer) window).getRootPane();
if (opaque) { if (opaque) {

4
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(); Insets i = progressBar.getInsets();
DarkUIUtil.removeInsets(r, i); DarkUIUtil.applyInsets(r, i);
int orientation = progressBar.getOrientation(); int orientation = progressBar.getOrientation();
Color startColor, endColor; Color startColor, endColor;
@ -248,7 +248,7 @@ public class DarkProgressBarUI extends BasicProgressBarUI implements PropertyCha
} }
Insets i = progressBar.getInsets(); Insets i = progressBar.getInsets();
DarkUIUtil.removeInsets(r, i); DarkUIUtil.applyInsets(r, i);
int amountFull = getAmountFull(i, r.width, r.height); int amountFull = getAmountFull(i, r.width, r.height);
Shape fullShape, coloredShape; Shape fullShape, coloredShape;

40
core/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkSubstanceRootLayout.java

@ -28,7 +28,9 @@ import java.awt.*;
import javax.swing.*; import javax.swing.*;
import com.github.weisj.darklaf.platform.DecorationsHandler;
import com.github.weisj.darklaf.platform.decorations.CustomTitlePane; import com.github.weisj.darklaf.platform.decorations.CustomTitlePane;
import com.github.weisj.darklaf.util.DarkUIUtil;
/** /**
* @author Konstantin Bulenkov * @author Konstantin Bulenkov
@ -53,17 +55,10 @@ class DarkSubstanceRootLayout implements LayoutManager2 {
int tpWidth = 0; int tpWidth = 0;
int tpHeight = 0; int tpHeight = 0;
Insets i = parent.getInsets(); Insets i = parent.getInsets();
DecorationsHandler.getSharedInstance().adjustWindowInsets(DarkUIUtil.getWindow(parent), i);
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);
JRootPane root = (JRootPane) parent; JRootPane root = (JRootPane) parent;
CustomTitlePane titlePane = getTitlePane(root);
if (root.getContentPane() != null) { if (root.getContentPane() != null) {
cpd = root.getContentPane().getPreferredSize(); cpd = root.getContentPane().getPreferredSize();
@ -125,8 +120,7 @@ class DarkSubstanceRootLayout implements LayoutManager2 {
} }
if ((root.getWindowDecorationStyle() != JRootPane.NONE) if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) { && (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI()) JComponent titlePane = ((DarkRootPaneUI) root.getUI()).getTitlePane();
.getTitlePane();
if (titlePane != null) { if (titlePane != null) {
tpd = titlePane.getMinimumSize(); tpd = titlePane.getMinimumSize();
if (tpd != null) { if (tpd != null) {
@ -152,34 +146,42 @@ class DarkSubstanceRootLayout implements LayoutManager2 {
public void layoutContainer(final Container parent) { public void layoutContainer(final Container parent) {
JRootPane root = (JRootPane) parent; JRootPane root = (JRootPane) parent;
Rectangle b = root.getBounds(); Rectangle b = root.getBounds();
Insets i = root.getInsets(); Insets i = root.getInsets();
int nextY = 0;
int w = b.width - i.right - i.left; b.setLocation(0, 0);
int h = b.height - i.top - i.bottom; DarkUIUtil.applyInsets(b, i);
if (root.getLayeredPane() != null) { 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) { 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); JComponent titlePane = getTitlePane(root);
if (titlePane != null) { if (titlePane != null) {
Dimension tpd = titlePane.getPreferredSize(); Dimension tpd = titlePane.getPreferredSize();
if (tpd != null) { if (tpd != null) {
int tpHeight = tpd.height; int tpHeight = tpd.height;
titlePane.setBounds(0, 0, w, tpHeight); titlePane.setBounds(x, y, w, tpHeight);
nextY += tpHeight; nextY += tpHeight;
} }
} }
if (root.getJMenuBar() != null) { if (root.getJMenuBar() != null) {
Dimension mbd = root.getJMenuBar().getPreferredSize(); Dimension mbd = root.getJMenuBar().getPreferredSize();
root.getJMenuBar().setBounds(0, nextY, w, mbd.height); root.getJMenuBar().setBounds(x, nextY, w, mbd.height);
nextY += mbd.height; nextY += mbd.height;
} }
if (root.getContentPane() != null) { 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);
} }
} }

26
core/src/main/java/com/github/weisj/darklaf/util/DarkUIUtil.java

@ -61,17 +61,29 @@ public final class DarkUIUtil {
if (insets != null && rect != null) { if (insets != null && rect != null) {
rect.x += insets.left; rect.x += insets.left;
rect.y += insets.top; rect.y += insets.top;
rect.width -= (insets.right + rect.x); rect.width -= (insets.right + insets.left);
rect.height -= (insets.bottom + rect.y); 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) { public static void removeInsets(final Rectangle rectangle, final Insets insets) {
if (insets != null) { if (insets != null && rectangle != null) {
rectangle.x += insets.left; rectangle.x -= insets.left;
rectangle.y += insets.top; rectangle.y -= insets.top;
rectangle.width -= insets.left + insets.right; rectangle.width += insets.left + insets.right;
rectangle.height -= insets.top + insets.bottom; rectangle.height += insets.top + insets.bottom;
} }
} }

1
core/src/test/java/ui/ComponentDemo.java

@ -57,6 +57,7 @@ public interface ComponentDemo {
static void showDemo(final ComponentDemo demo, final Dimension dimension) { static void showDemo(final ComponentDemo demo, final Dimension dimension) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
LafManager.setDecorationsEnabled(true);
LafManager.install(demo.createTheme()); LafManager.install(demo.createTheme());
JFrame frame = new JFrame(); JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

5
macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java

@ -144,11 +144,6 @@ public class MacOSTitlePane extends CustomTitlePane {
decorationInformation = null; decorationInformation = null;
} }
@Override
public Insets getWindowSizeAdjustment() {
return new Insets(0, 0, 0, 0);
}
private void installListeners() { private void installListeners() {
if (window != null && useCustomTitle()) { if (window != null && useCustomTitle()) {
windowListener = new WindowHandler(); windowListener = new WindowHandler();

2
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(); protected abstract void install();
public abstract Insets getWindowSizeAdjustment();
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();

18
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. * 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. * Uninstall the window of a popup menu.
*/ */
default void uninstallPopupWindow(final Window window) {} 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) {}
} }

4
windows/build.gradle.kts

@ -32,8 +32,8 @@ library {
binaries.whenElementFinalized(CppSharedLibrary::class) { binaries.whenElementFinalized(CppSharedLibrary::class) {
linkTask.get().linkerArgs.addAll( linkTask.get().linkerArgs.addAll(
when (toolChain) { when (toolChain) {
is Gcc, is Clang -> listOf("-ldwmapi", "-lGdi32", "-luser32", "-ladvapi32") is Gcc, is Clang -> listOf("-ldwmapi", "-lGdi32", "-luser32", "-ladvapi32", "-Shell32")
is VisualCpp -> listOf("dwmapi.lib", "user32.lib", "Gdi32.lib", "Advapi32.lib") is VisualCpp -> listOf("dwmapi.lib", "user32.lib", "Gdi32.lib", "Advapi32.lib", "Shell32.lib")
else -> emptyList() else -> emptyList()
} }
) )

322
windows/src/main/cpp/Decorations.cpp

@ -24,47 +24,87 @@
*/ */
#include "Decorations.h" #include "Decorations.h"
#include "com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows.h" #include "com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows.h"
#include <dwmapi.h>
#include <map> #ifndef WM_NCUAHDRAWCAPTION
#include <iostream> #define WM_NCUAHDRAWCAPTION (0x00AE)
#include <winuser.h> #endif
#ifndef WM_NCUAHDRAWFRAME
#define WM_NCUAHDRAWFRAME (0x00AF)
#endif
std::map<HWND, WindowWrapper*> wrapper_map = std::map<HWND, WindowWrapper*>(); std::map<HWND, WindowWrapper*> wrapper_map = std::map<HWND, WindowWrapper*>();
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) if (wrapper->popup_menu)
return HTCLIENT; return HTCLIENT;
// Get the point coordinates for the hit test.
POINT ptMouse = { GET_X_LPARAM(lParam), POINT ptMouse = { x,
GET_Y_LPARAM(lParam) }; y };
// Get the window rectangle. // Get the window rectangle.
RECT rcWindow; RECT rcWindow;
GetWindowRect(hWnd, &rcWindow); GetWindowRect(wrapper->window, &rcWindow);
// Determine if the hit test is for resizing. Default middle (1,1). // Determine if the hit test is for resizing. Default middle (1,1).
USHORT uRow = 1; USHORT uRow = 1;
USHORT uCol = 1; USHORT uCol = 1;
// Determine if the point is at the top or bottom of the window. if (!Maximized(wrapper->window))
if (ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + 5)
{ {
uRow = 0; /* The horizontal frame should be the same size as the vertical frame,
} since the NONCLIENTMETRICS structure does not distinguish between them */
else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - 5) int frame_size = GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
{ /* The diagonal size handles are wider than the frame */
uRow = 2; int diagonal_width = frame_size * 2 + GetSystemMetrics(SM_CXBORDER);
}
// Determine if the point is at the left or right of the window. bool top = ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + frame_size;
if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + 5) bool bottom = !top && (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - frame_size);
{
uCol = 0; // left side 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);
else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - 5)
{ bool diag_top = ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + diagonal_width;
uCol = 2; // right side 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) // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
@ -76,12 +116,12 @@ LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam, WindowWrapper *wrapp
HTRIGHT }, HTRIGHT },
{ HTBOTTOMLEFT, { HTBOTTOMLEFT,
HTBOTTOM, HTBOTTOM,
HTBOTTOMRIGHT }, }; HTBOTTOMRIGHT } };
LRESULT hit = hitTests[uRow][uCol]; LRESULT hit = hitTests[uRow][uCol];
if (hit == HTNOWHERE || !wrapper->resizable) if (hit == HTNOWHERE || !wrapper->resizable)
{ {
//Handle window drag. //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) && ptMouse.x <= rcWindow.right - wrapper->right)
{ {
return HTCAPTION; 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 (wrapper->popup_menu)
if (!GetWindowPlacement(hwnd, &placement)) return;
return false; RECT old_rgn = wrapper->rgn;
return placement.showCmd == SW_MAXIMIZE;
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) void AdjustMaximizedClientArea(HWND window, RECT &rect)
@ -107,36 +189,94 @@ void AdjustMaximizedClientArea(HWND window, RECT &rect)
if (!Maximized(window)) if (!Maximized(window))
return; return;
auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);
if (!monitor) if (!monitor)
return; return;
MONITORINFO monitor_info {}; MONITORINFO monitor_info {};
monitor_info.cbSize = sizeof(monitor_info); monitor_info.cbSize = sizeof(MONITORINFO);
if (!GetMonitorInfoW(monitor, &monitor_info)) if (!GetMonitorInfo(monitor, &monitor_info))
return; return;
rect = monitor_info.rcWork; 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); RECT client;
HMONITOR hTargetMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); GetClientRect(wrapper->window, &client);
LONG old_width = wrapper->width;
MONITORINFO primaryMonitorInfo { sizeof(MONITORINFO) }; LONG old_height = wrapper->height;
MONITORINFO targetMonitorInfo { sizeof(MONITORINFO) }; wrapper->width = client.right;
wrapper->height = client.bottom;
GetMonitorInfo(hPrimaryMonitor, &primaryMonitorInfo); bool client_changed = wrapper->width != old_width || wrapper->height != old_height;
GetMonitorInfo(hTargetMonitor, &targetMonitorInfo);
if (client_changed || (pos->flags & SWP_FRAMECHANGED))
MINMAXINFO *min_max_info = reinterpret_cast<MINMAXINFO*>(lParam); UpdateRegion(wrapper);
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;
} }
void PaintBackground(HWND hwnd, WPARAM wParam, WindowWrapper *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>(hwnd); HWND handle = reinterpret_cast<HWND>(hwnd);
auto wrapper = wrapper_map[handle]; auto wrapper = wrapper_map[handle];
if (uMsg == WM_NCACTIVATE) switch(uMsg)
{ {
case WM_NCACTIVATE:
return TRUE; return TRUE;
} case WM_NCCALCSIZE:
else if (uMsg == WM_NCCALCSIZE)
{
if (wParam == TRUE) if (wParam == TRUE)
{ {
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam); HandleNCCalcSize(wrapper, wParam, lParam);
AdjustMaximizedClientArea(handle, params.rgrc[0]); UpdateRegion(wrapper);
return TRUE; return 0;
} }
} break;
else if (uMsg == WM_GETMINMAXINFO) case WM_NCHITTEST:
{ return HandleHitTest(wrapper, GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
AdjustMinMaxInfo(hwnd, lParam); case WM_MOVE:
return FALSE;
}
else if (uMsg == WM_NCHITTEST)
{
return HitTestNCA(hwnd, wParam, lParam, wrapper);
}
else if (uMsg == WM_MOVE)
{
wrapper->moving = wrapper->move_mode; wrapper->moving = wrapper->move_mode;
} break;
else if (uMsg == WM_ENTERSIZEMOVE) case WM_ENTERSIZEMOVE:
{
wrapper->move_mode = TRUE; wrapper->move_mode = TRUE;
} break;
else if (uMsg == WM_EXITSIZEMOVE) case WM_EXITSIZEMOVE:
{
wrapper->moving = FALSE; wrapper->moving = FALSE;
wrapper->move_mode = FALSE; wrapper->move_mode = FALSE;
} break;
else if ((uMsg == WM_PAINT || uMsg == WM_ERASEBKGND) && wrapper->bgBrush) case WM_ERASEBKGND:
{ case WM_PAINT:
if (!wrapper->moving) PaintBackground(hwnd, wParam, wrapper); if (!wrapper->bgBrush)
if (uMsg == WM_ERASEBKGND) return TRUE; 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); 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->left = l;
wrap->right = r; wrap->right = r;
wrap->height = h; wrap->title_height = h;
} }
} }
@ -236,15 +380,16 @@ void ExtendClientFrame(HWND handle)
{ {
MARGINS margins = { 0, MARGINS margins = { 0,
0, 0,
0, 1,
1 }; 0 };
DwmExtendFrameIntoClientArea(handle, &margins); DwmExtendFrameIntoClientArea(handle, &margins);
} }
void SetupWindowStyle(HWND handle) void SetupWindowStyle(HWND handle, bool is_popup)
{ {
auto style = GetWindowLongPtr(handle, GWL_STYLE); 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) bool InstallDecorations(HWND handle, bool is_popup)
@ -254,12 +399,13 @@ bool InstallDecorations(HWND handle, bool is_popup)
if (it != wrapper_map.end()) if (it != wrapper_map.end())
return false; return false;
SetupWindowStyle(handle); SetupWindowStyle(handle, is_popup);
ExtendClientFrame(handle); ExtendClientFrame(handle);
WNDPROC proc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(handle, GWLP_WNDPROC)); WNDPROC proc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(handle, GWLP_WNDPROC));
WindowWrapper *wrapper = new WindowWrapper(); WindowWrapper *wrapper = new WindowWrapper();
wrapper->window = handle;
wrapper->prev_proc = proc; wrapper->prev_proc = proc;
wrapper->popup_menu = is_popup; wrapper->popup_menu = is_popup;
wrapper_map[handle] = wrapper; wrapper_map[handle] = wrapper;

14
windows/src/main/cpp/Decorations.h

@ -25,6 +25,11 @@
#include <stdio.h> #include <stdio.h>
#include <windows.h> #include <windows.h>
#include <windowsx.h> #include <windowsx.h>
#include <dwmapi.h>
#include <map>
#include <iostream>
#include <shellapi.h>
#include <winuser.h>
class WindowWrapper class WindowWrapper
{ {
@ -33,12 +38,19 @@ class WindowWrapper
bool popup_menu = false; bool popup_menu = false;
bool moving = false; bool moving = false;
bool move_mode = false; bool move_mode = false;
bool maximized = false;
WNDPROC prev_proc; WNDPROC prev_proc;
HBRUSH bgBrush; HBRUSH bgBrush;
HWND window;
int width;
int height;
RECT rgn;
int left = 0; int left = 0;
int right = 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); static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
}; };

34
windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java

@ -28,6 +28,7 @@ import java.awt.*;
import java.util.Properties; import java.util.Properties;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border;
import com.github.weisj.darklaf.PropertyLoader; import com.github.weisj.darklaf.PropertyLoader;
import com.github.weisj.darklaf.icons.IconLoader; import com.github.weisj.darklaf.icons.IconLoader;
@ -53,15 +54,18 @@ public class WindowsDecorationsProvider implements DecorationsProvider {
} }
@Override @Override
public void installPopupMenu(final Window window) { public void installPopupWindow(final Window window) {
if (!window.isDisplayable()) { if (!window.isDisplayable()) {
window.addNotify(); window.addNotify();
} }
long hwnd = PointerUtil.getHWND(window); long hwnd = PointerUtil.getHWND(window);
if (hwnd != 0) { if (hwnd != 0) {
if (JNIDecorationsWindows.installPopupMenuDecorations(hwnd)) { if (JNIDecorationsWindows.installPopupMenuDecorations(hwnd) && window instanceof RootPaneContainer) {
Color bg = window.getBackground(); JRootPane rootPane = ((RootPaneContainer) window).getRootPane();
JNIDecorationsWindows.setBackground(hwnd, bg.getRed(), bg.getGreen(), bg.getBlue()); 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 @Override
public void loadDecorationProperties(final Properties properties, final UIDefaults currentDefaults) { public void loadDecorationProperties(final Properties properties, final UIDefaults currentDefaults) {
IconLoader iconLoader = IconLoader.get(WindowsDecorationsProvider.class); IconLoader iconLoader = IconLoader.get(WindowsDecorationsProvider.class);

25
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.accessibility.AccessibleContext;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
import sun.awt.SunToolkit; import sun.awt.SunToolkit;
@ -115,6 +116,7 @@ public class WindowsTitlePane extends CustomTitlePane {
private Color activeBackground; private Color activeBackground;
private Color activeForeground; private Color activeForeground;
private Color border; private Color border;
private Border oldBorder;
public WindowsTitlePane(final JRootPane root, final int decorationStyle, final Window window) { public WindowsTitlePane(final JRootPane root, final int decorationStyle, final Window window) {
this.rootPane = root; 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() { private void uninstallListeners() {
if (window != null) { if (window != null) {
window.removeWindowListener(windowListener); window.removeWindowListener(windowListener);
@ -507,11 +499,14 @@ public class WindowsTitlePane extends CustomTitlePane {
if (frame != null) { if (frame != null) {
JRootPane rootPane = getRootPane(); JRootPane rootPane = getRootPane();
if (((state & Frame.MAXIMIZED_BOTH) != 0) if (((state & Frame.MAXIMIZED_BOTH) != 0)) {
&& (rootPane.getBorder() == null oldBorder = rootPane.getBorder();
|| (rootPane.getBorder() instanceof UIResource)) LookAndFeel.uninstallBorder(rootPane);
&& frame.isShowing()) { } else {
rootPane.setBorder(null); Border border = rootPane.getBorder();
if (oldBorder != null && border == null || border instanceof UIResource) {
rootPane.setBorder(oldBorder);
}
} }
if (frame.isResizable()) { if (frame.isResizable()) {

Loading…
Cancel
Save