Browse Source

Improved decorations setup on windows.

pull/130/head
weisj 5 years ago
parent
commit
279892d952
  1. 5
      core/src/main/java/com/github/weisj/darklaf/DarkLaf.java
  2. 41
      core/src/main/java/com/github/weisj/darklaf/ui/DarkPopupFactory.java
  3. 64
      core/src/test/java/PreferenceChangeDemo.java
  4. 6
      core/src/test/java/ui/ComponentDemo.java
  5. 84
      windows/src/main/cpp/Decorations.cpp
  6. 2
      windows/src/main/cpp/Decorations.h
  7. 4
      windows/src/main/java/com/github/weisj/darklaf/platform/windows/JNIDecorationsWindows.java
  8. 13
      windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java
  9. 18
      windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java

5
core/src/main/java/com/github/weisj/darklaf/DarkLaf.java

@ -113,8 +113,9 @@ public class DarkLaf extends BasicLookAndFeel {
private void setupDecorations() {
DecorationsHandler.getSharedInstance().initialize();
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
boolean decorated = getSupportsWindowDecorations();
JFrame.setDefaultLookAndFeelDecorated(decorated);
JDialog.setDefaultLookAndFeelDecorated(decorated);
}
@Override

41
core/src/main/java/com/github/weisj/darklaf/ui/DarkPopupFactory.java

@ -26,6 +26,7 @@ package com.github.weisj.darklaf.ui;
import com.github.weisj.darklaf.platform.DecorationsHandler;
import com.github.weisj.darklaf.ui.popupmenu.DarkPopupMenuUI;
import com.github.weisj.darklaf.ui.rootpane.DarkRootPaneUI;
import com.github.weisj.darklaf.util.DarkUIUtil;
import javax.swing.*;
import java.awt.*;
@ -37,9 +38,16 @@ public class DarkPopupFactory extends PopupFactory {
public static final String KEY_FORCE_HEAVYWEIGHT = "JPopupFactory.forceHeavyweight";
public static final String KEY_START_HIDDEN = "JPopupFactory.startHidden";
private HeavyWeightParent heavyWeightParent;
@Override
public Popup getPopup(final Component owner, final Component contents,
final int x, final int y) throws IllegalArgumentException {
if (heavyWeightParent == null) {
JComponent box = Box.createHorizontalBox();
super.getPopup(null, box, 0, 0);
heavyWeightParent = new HeavyWeightParent(DarkUIUtil.getWindow(box));
}
Popup popup = super.getPopup(owner, contents, x, y);
boolean isMediumWeight = popup.getClass().getSimpleName().endsWith("MediumWeightPopup");
boolean isLightWeight = popup.getClass().getSimpleName().endsWith("LightWeightPopup");
@ -51,7 +59,7 @@ public class DarkPopupFactory extends PopupFactory {
&& Boolean.TRUE.equals(((JComponent) contents).getClientProperty(KEY_START_HIDDEN));
if (forceHeavy && (isMediumWeight || isLightWeight)) {
// null owner forces a heavyweight popup.
popup = super.getPopup(null, contents, x, y);
popup = super.getPopup(heavyWeightParent, contents, x, y);
}
if (isMediumWeight) {
JRootPane rootPane = SwingUtilities.getRootPane(contents);
@ -64,13 +72,20 @@ public class DarkPopupFactory extends PopupFactory {
// That is why we set window background explicitly.
Window window = SwingUtilities.getWindowAncestor(contents);
if (window != null) {
window.setBackground(UIManager.getColor("PopupMenu.translucentBackground"));
boolean install = true;
window.setBackground(UIManager.getColor("PopupMenu.translucentBackground"));
if (window instanceof RootPaneContainer) {
JRootPane rootPane = ((RootPaneContainer) window).getRootPane();
rootPane.putClientProperty(DarkRootPaneUI.KEY_NO_DECORATIONS, true);
window.setBackground(rootPane.getBackground());
install = !Boolean.TRUE.equals(rootPane.getClientProperty(KEY_NO_DECORATION));
}
if (isFocusable && !window.getFocusableWindowState()) {
window.dispose();
window.setFocusableWindowState(true);
}
if (install) {
DecorationsHandler.getSharedInstance().installPopupWindow(window);
} else {
@ -80,11 +95,25 @@ public class DarkPopupFactory extends PopupFactory {
((JComponent) contents).putClientProperty(DarkPopupMenuUI.KEY_MAKE_VISIBLE, true);
window.setOpacity(0.0f);
}
if (isFocusable && !window.getFocusableWindowState()) {
window.dispose();
window.setFocusableWindowState(true);
}
}
return popup;
}
private static class HeavyWeightParent extends JComponent {
private final Window window;
private HeavyWeightParent(final Window window) {
this.window = window;
}
@Override
public Container getParent() {
return window;
}
public Window getWindow() {
return window;
}
}
}

64
core/src/test/java/PreferenceChangeDemo.java

@ -0,0 +1,64 @@
import com.github.weisj.darklaf.LafManager;
import com.github.weisj.darklaf.theme.Theme;
import ui.ComponentDemo;
import javax.swing.*;
import java.awt.*;
/*
* MIT License
*
* Copyright (c) 2020 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.
*/
public class PreferenceChangeDemo implements ComponentDemo {
public static void main(final String[] args) {
ComponentDemo.showDemo(new PreferenceChangeDemo());
}
@Override
public JComponent createComponent() {
LafManager.addThemePreferenceChangeListener(e -> LafManager.installTheme(e.getPreferredThemeStyle()));
return new JPanel(new GridBagLayout()) {{
add(new JToggleButton("Start") {{
addActionListener(e -> {
setText(isSelected() ? "Stop" : "Start");
LafManager.enabledPreferenceChangeReporting(isSelected());
});
}});
}};
}
@Override
public String getTitle() {
return "Preference Change Demo";
}
@Override
public Theme createTheme() {
return LafManager.themeForPreferredStyle(LafManager.getPreferredThemeStyle());
}
@Override
public JMenuBar createMenuBar() {
return null;
}
}

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

@ -48,7 +48,7 @@ public interface ComponentDemo {
static void showDemo(final ComponentDemo demo, final Dimension dimension) {
SwingUtilities.invokeLater(() -> {
LafManager.install(getTheme());
LafManager.install(demo.createTheme());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setTitle(demo.getTitle());
@ -100,5 +100,9 @@ public interface ComponentDemo {
return menuBar;
}
default Theme createTheme() {
return getTheme();
}
String getTitle();
}

84
windows/src/main/cpp/Decorations.cpp

@ -90,43 +90,50 @@ LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam, WindowWrapper *wrapp
}
}
bool Maximized(HWND hwnd)
{
WINDOWPLACEMENT placement;
if (!GetWindowPlacement(hwnd, &placement)) return false;
return placement.showCmd == SW_MAXIMIZE;
}
void AdjustMaximizedClientArea(HWND window, RECT& rect)
{
if (!Maximized(window)) return;
auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
if (!monitor) return;
MONITORINFO monitor_info{};
monitor_info.cbSize = sizeof(monitor_info);
if (!GetMonitorInfoW(monitor, &monitor_info)) return;
rect = monitor_info.rcWork;
}
LRESULT CALLBACK WindowWrapper::WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
auto wrapper = wrapper_map[handle];
if (uMsg == WM_NCCALCSIZE)
{
if (wParam == TRUE)
{
SetWindowLong(hwnd, 0, 0);
if (wParam == TRUE) {
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
AdjustMaximizedClientArea(handle, params.rgrc[0]);
return TRUE;
}
return FALSE;
}
else if (uMsg == WM_NCHITTEST)
{
return HitTestNCA(hwnd, wParam, lParam, wrapper);
}
else if (uMsg == WM_GETMINMAXINFO)
else if ((uMsg == WM_PAINT || uMsg == WM_ERASEBKGND) && wrapper->bgBrush)
{
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<MINMAXINFO *>(lParam);
RECT max_rect = primaryMonitorInfo.rcWork;
RECT target_rect = targetMonitorInfo.rcWork;
int indent = 2;
min_max_info->ptMaxSize.x = target_rect.right - target_rect.left - 2 * indent;
min_max_info->ptMaxSize.y = target_rect.bottom - target_rect.top - 2 + indent;
min_max_info->ptMaxPosition.x = max_rect.left + indent;
min_max_info->ptMaxPosition.y = max_rect.top + indent;
return 0;
HDC hdc = reinterpret_cast<HDC>(wParam);
RECT clientRect;
GetClientRect(hwnd, &clientRect);
FillRect(hdc, &clientRect, wrapper->bgBrush);
if (uMsg == WM_ERASEBKGND) return TRUE;
}
return CallWindowProc(wrapper->prev_proc, hwnd, uMsg, wParam, lParam);
@ -164,47 +171,48 @@ Java_com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows_setBackgrou
auto wrap = wrapper_map[handle];
if (wrap)
{
wrap->background = RGB(r, g, b);
wrap->bgBrush = CreateSolidBrush(RGB(r, g, b));
}
}
void extend_client_frame(HWND handle)
void ExtendClientFrame(HWND handle)
{
MARGINS margins = {0, 0, 0, 1};
MARGINS margins = {1, 1, 1, 1};
DwmExtendFrameIntoClientArea(handle, &margins);
}
void setup_window_style(HWND handle)
void SetupWindowStyle(HWND handle)
{
auto style = GetWindowLongPtr(handle, GWL_STYLE);
SetWindowLongPtr(handle, GWL_STYLE, style | WS_THICKFRAME);
SetWindowLongPtr(handle, GWL_STYLE, (style | WS_THICKFRAME));
}
void install_decorations(HWND handle, bool is_popup)
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;
if (it != wrapper_map.end()) return false;
extend_client_frame(handle);
setup_window_style(handle);
SetupWindowStyle(handle);
ExtendClientFrame(handle);
SetWindowPos(handle, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
WNDPROC proc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(handle, GWL_WNDPROC));
WindowWrapper *wrapper = new WindowWrapper();
wrapper->prev_proc = proc;
wrapper->popup_menu = is_popup;
wrapper_map[handle] = wrapper;
SetWindowLongPtr(handle, GWL_WNDPROC, (LONG_PTR)WindowWrapper::WindowProc);
UINT flags = SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED;
SetWindowPos(handle, NULL, 0, 0, 0, 0, flags);
return true;
}
JNIEXPORT void JNICALL
JNIEXPORT jboolean JNICALL
Java_com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows_installDecorations(JNIEnv *env, jclass obj, jlong hwnd)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
install_decorations(handle, false);
return (jboolean) InstallDecorations(handle, false);
}
JNIEXPORT void JNICALL
@ -220,11 +228,11 @@ Java_com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows_uninstallDe
}
}
JNIEXPORT void JNICALL
JNIEXPORT jboolean JNICALL
Java_com_github_weisj_darklaf_platform_windows_JNIDecorationsWindows_installPopupMenuDecorations(JNIEnv *env, jclass obj, jlong hwnd)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
install_decorations(handle, true);
return (jboolean) InstallDecorations(handle, true);
}
//Window functions.

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

@ -31,7 +31,7 @@ public:
bool resizable = true;
bool popup_menu = false;
WNDPROC prev_proc;
COLORREF background = RGB(255, 255, 255);
HBRUSH bgBrush;
int left = 0;
int right = 0;

4
windows/src/main/java/com/github/weisj/darklaf/platform/windows/JNIDecorationsWindows.java

@ -40,9 +40,9 @@ public class JNIDecorationsWindows {
public static native void restore(final long hwnd);
public static native void installDecorations(final long hwnd);
public static native boolean installDecorations(final long hwnd);
public static native void uninstallDecorations(final long hwnd);
public static native void installPopupMenuDecorations(final long hwnd);
public static native boolean installPopupMenuDecorations(final long hwnd);
}

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

@ -32,8 +32,6 @@ import com.github.weisj.darklaf.platform.windows.ui.WindowsTitlePane;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Properties;
public class WindowsDecorationsProvider implements DecorationsProvider {
@ -60,13 +58,10 @@ public class WindowsDecorationsProvider implements DecorationsProvider {
}
long hwnd = PointerUtil.getHWND(window);
if (hwnd != 0) {
JNIDecorationsWindows.installPopupMenuDecorations(hwnd);
window.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(final WindowEvent e) {
JNIDecorationsWindows.uninstallDecorations(hwnd);
}
});
if (JNIDecorationsWindows.installPopupMenuDecorations(hwnd)) {
Color bg = window.getBackground();
JNIDecorationsWindows.setBackground(hwnd, bg.getRed(), bg.getGreen(), bg.getBlue());
}
}
}

18
windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java

@ -230,7 +230,6 @@ public class WindowsTitlePane extends CustomTitlePane {
updateResizeBehaviour();
Color color = window.getBackground();
JNIDecorationsWindows.setBackground(windowHandle, color.getRed(), color.getGreen(), color.getBlue());
forceNativeResize();
} else {
uninstall();
return false;
@ -239,23 +238,6 @@ public class WindowsTitlePane extends CustomTitlePane {
return true;
}
private void forceNativeResize() {
Rectangle bounds = window.getBounds();
Dimension size = bounds.getSize();
Point p = bounds.getLocation();
if (window.isPreferredSizeSet()) {
size = window.getPreferredSize();
} else {
p.x += size.width / 2;
p.y += size.height / 2;
}
//Resizing triggers #reshapeNativePeer
window.setSize(size.width, size.height + 1);
window.setSize(size.width, size.height);
window.setLocation(p.x - size.width / 2,
p.y - size.height / 2);
}
private void installListeners() {
if (window != null) {
windowListener = createWindowListener();

Loading…
Cancel
Save