Browse Source

General functioning support for window decorations on windows.

Resize cursors still show up if resizing is disabled.
pull/1/head
weisj 5 years ago
parent
commit
bc3c3e1952
  1. 4
      .gitignore
  2. 111
      build.gradle
  3. 12
      darklaf.iml
  4. BIN
      gradle/wrapper/gradle-wrapper.jar
  5. 6
      gradle/wrapper/gradle-wrapper.properties
  6. 188
      gradlew
  7. 100
      gradlew.bat
  8. 5
      settings.gradle
  9. 184
      src/jniplatform/cpp/JNIDecorations.cpp
  10. 17
      src/jniplatform/cpp/JNIDecorations.h
  11. 77
      src/jniplatform/cpp/com_weis_darklaf_platform_windows_JNIDecorations.h
  12. 4
      src/main/java/com/weis/darklaf/components/ColorPipetteBase.java
  13. 2
      src/main/java/com/weis/darklaf/components/border/TextBubbleBorder.java
  14. 36
      src/main/java/com/weis/darklaf/platform/windows/JNIDecorations.java
  15. 82
      src/main/java/com/weis/darklaf/platform/windows/WindowsFrameUtil.java
  16. 27
      src/main/java/com/weis/darklaf/ui/colorchooser/DarkColorChooserUI.java
  17. 842
      src/main/java/com/weis/darklaf/ui/rootpane/DarkRootPaneUI.java
  18. 141
      src/main/java/com/weis/darklaf/ui/rootpane/DarkTitlePane.java
  19. 213
      src/main/java/com/weis/darklaf/ui/rootpane/SubstanceRootLayout.java
  20. 1
      src/main/java/com/weis/darklaf/util/DarkUIUtil.java
  21. 2
      src/main/resources/com/weis/darklaf/darcula.properties
  22. 1
      src/test/java/ColorChooserDemo.java

4
.gitignore vendored

@ -49,6 +49,10 @@ bin/
# maven
target/
# gradle
/build
/.gradle
# Python
venv/

111
build.gradle

@ -0,0 +1,111 @@
import org.gradle.internal.jvm.Jvm
plugins {
id 'java'
id 'maven-publish'
id 'idea'
id 'cpp'
}
repositories {
mavenLocal()
mavenCentral()
maven {
url = 'http://repo.maven.apache.org/maven2'
}
}
dependencies {
compile 'darcula:darcula.laf:1.0.0'
compile 'com.metsci.ext.com.kitfox.svg:svg-salamander:[0.1.19,)'
compile 'org.jetbrains:annotations:16.0.1'
compile 'org.jetbrains.kotlin:kotlin-compiler:1.3.21'
compile 'org.swinglabs:swingx:1.6.1'
testCompileOnly 'org.swinglabs:swingx:1.6.1'
testCompile 'junit:junit:4.12'
}
group = 'com.weis'
version = '1.0-SNAPSHOT'
compileJava {
sourceCompatibility = 11
targetCompatibility = 11
}
test {
dependsOn 'jniplatformSharedLibrary'
systemProperty "java.library.path", file("${buildDir}/libs/jniplatform/shared").absolutePath + '\\'
}
println "Building on OS: " + System.properties['os.name']
println "Using JDK: " + System.properties['java.home']
model {
platforms {
x86 {
architecture 'x86'
}
x64 {
architecture 'x86_64'
}
}
}
model {
components {
jniplatform(NativeLibrarySpec) {
// targetPlatform "x86"
targetPlatform "x64"
binaries.all {
if (targetPlatform.operatingSystem.macOsX) {
cppCompiler.args '-I', "${Jvm.current().javaHome}/include"
cppCompiler.args '-I', "${Jvm.current().javaHome}/include/darwin"
cppCompiler.args '-mmacosx-version-min=10.4'
linker.args '-mmacosx-version-min=10.4'
} else if (targetPlatform.operatingSystem.linux) {
cppCompiler.args '-I', "${Jvm.current().javaHome}/include"
cppCompiler.args '-I', "${Jvm.current().javaHome}/include/linux"
cppCompiler.args '-D_FILE_OFFSET_BITS=64'
} else if (targetPlatform.operatingSystem.windows) {
cppCompiler.args "-I${Jvm.current().javaHome}/include"
cppCompiler.args "-I${Jvm.current().javaHome}/include/win32"
cppCompiler.args "-std=c++11"
linker.args "-ldwmapi"
linker.args "-lGdi32"
} else if (targetPlatform.operatingSystem.freeBSD) {
cppCompiler.args '-I', "${Jvm.current().javaHome}/include"
cppCompiler.args '-I', "${Jvm.current().javaHome}/include/freebsd"
}
}
}
}
}
publishing {
publications {
maven(MavenPublication) {
from(components.java)
}
}
}
tasks.withType(JavaCompile) {
configure(options) {
options.encoding = 'UTF-8'
options.compilerArgs += [
'-h', file("${projectDir}/src/jniplatform/cpp"),
]
options.compilerArgs += [
'--add-exports=java.desktop/sun.awt=ALL-UNNAMED',
'--add-exports=java.desktop/com.sun.java.swing=ALL-UNNAMED',
'--add-exports=java.desktop/sun.swing=ALL-UNNAMED'
]
}
}
tasks.withType(JavaExec) {
dependsOn 'jniplatformSharedLibrary'
systemProperty "java.library.path", file("${buildDir}/libs/jniplatform/shared").absolutePath + '\\'
}

12
darklaf.iml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="darklaf" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="com.weis" external.system.module.version="1.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11" inherit-compiler-output="true">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

BIN
gradle/wrapper/gradle-wrapper.jar vendored

Binary file not shown.

6
gradle/wrapper/gradle-wrapper.properties vendored

@ -0,0 +1,6 @@
#Tue Oct 01 16:12:24 CEST 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

188
gradlew vendored

@ -0,0 +1,188 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

100
gradlew.bat vendored

@ -0,0 +1,100 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

5
settings.gradle

@ -0,0 +1,5 @@
/*
* This file was generated by the Gradle 'init' task.
*/
rootProject.name = 'darklaf'

184
src/jniplatform/cpp/JNIDecorations.cpp

@ -0,0 +1,184 @@
#include "JNIDecorations.h";
#include "com_weis_darklaf_platform_windows_JNIDecorations.h"
#include <dwmapi.h>
#include <iostream>
#include <map>
#include <winuser.h>
#define GWL_WNDPROC -4
std::map<HWND, WindowWrapper *> wrapper_map = std::map<HWND, WindowWrapper *>();
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam, WindowWrapper *wrapper)
{
// Get the point coordinates for the hit test.
POINT ptMouse = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
// Get the window rectangle.
RECT rcWindow;
GetWindowRect(hWnd, &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)
{
uRow = 0;
}
else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - 5)
{
uRow = 2;
}
// 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
}
// Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
LRESULT hitTests[3][3] =
{
{HTTOPLEFT, HTTOP, HTTOPRIGHT},
{HTLEFT, HTNOWHERE, HTRIGHT},
{HTBOTTOMLEFT, HTBOTTOM, 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 && ptMouse.x <= rcWindow.right - wrapper->right)
{
return HTCAPTION;
}
return HTCLIENT;
}
else
{
return hit;
}
}
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);
return TRUE;
}
return FALSE;
}
else if (uMsg == WM_NCHITTEST)
{
return HitTestNCA(hwnd, wParam, lParam, wrapper);
}
else if (uMsg == WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
FillRect(hdc, &rc, CreateSolidBrush(wrapper->background));
EndPaint(hwnd, &ps);
}
return CallWindowProc(wrapper->prev_proc, hwnd, uMsg, wParam, lParam);
}
JNIEXPORT void JNICALL
Java_com_weis_darklaf_platform_windows_JNIDecorations_setResizable(JNIEnv *env, jclass obj, jlong hwnd, jboolean res)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
auto wrap = wrapper_map[handle];
if (wrap)
{
wrap->resizable = res;
}
}
JNIEXPORT void JNICALL
Java_com_weis_darklaf_platform_windows_JNIDecorations_updateValues(JNIEnv *env, jclass obj, jlong hwnd,
jint l, jint r, jint h)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
auto wrap = wrapper_map[handle];
if (wrap)
{
wrap->left = l;
wrap->right = r;
wrap->height = h;
}
}
JNIEXPORT void JNICALL
Java_com_weis_darklaf_platform_windows_JNIDecorations_setBackground(JNIEnv *env, jclass obj, jlong hwnd, jint r, jint g, jint b)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
auto wrap = wrapper_map[handle];
if (wrap)
{
wrap->background = RGB(r, g, b);
}
}
JNIEXPORT void JNICALL
Java_com_weis_darklaf_platform_windows_JNIDecorations_installDecorations(JNIEnv *env, jclass obj, jlong hwnd)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
MARGINS margins = {0, 0, 0, 1};
DwmExtendFrameIntoClientArea(handle, &margins);
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_map[handle] = wrapper;
SetWindowLongPtr((HWND)hwnd, GWL_WNDPROC, (LONG_PTR)WindowWrapper::WindowProc);
}
JNIEXPORT void JNICALL
Java_com_weis_darklaf_platform_windows_JNIDecorations_uninstallDecorations(JNIEnv *env, jclass obj, jlong hwnd)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
auto wrap = wrapper_map[handle];
if (wrap)
{
SetWindowLongPtr((HWND)hwnd, GWL_WNDPROC, reinterpret_cast<LONG_PTR>(wrap->prev_proc));
wrapper_map.erase(handle);
delete (wrap);
}
}
//Window functions.
JNIEXPORT void JNICALL
Java_com_weis_darklaf_platform_windows_JNIDecorations_minimize(JNIEnv *env, jclass obj, jlong hwnd)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
ShowWindow(handle, SW_MINIMIZE);
}
JNIEXPORT void JNICALL
Java_com_weis_darklaf_platform_windows_JNIDecorations_maximize(JNIEnv *env, jclass obj, jlong hwnd)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
ShowWindow(handle, SW_MAXIMIZE);
}
JNIEXPORT void JNICALL
Java_com_weis_darklaf_platform_windows_JNIDecorations_restore(JNIEnv *env, jclass obj, jlong hwnd)
{
HWND handle = reinterpret_cast<HWND>(hwnd);
ShowWindow(handle, SW_RESTORE);
}

17
src/jniplatform/cpp/JNIDecorations.h

@ -0,0 +1,17 @@
#include <stdio.h>
#include <windows.h>
#include <windowsx.h>
class WindowWrapper
{
public:
bool resizable = true;
WNDPROC prev_proc;
COLORREF background = RGB(255, 255, 255);
int left = 0;
int right = 0;
int height = 0;
static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
};

77
src/jniplatform/cpp/com_weis_darklaf_platform_windows_JNIDecorations.h

@ -0,0 +1,77 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_weis_darklaf_platform_windows_JNIDecorations */
#ifndef _Included_com_weis_darklaf_platform_windows_JNIDecorations
#define _Included_com_weis_darklaf_platform_windows_JNIDecorations
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_weis_darklaf_platform_windows_JNIDecorations
* Method: updateValues
* Signature: (JIII)V
*/
JNIEXPORT void JNICALL Java_com_weis_darklaf_platform_windows_JNIDecorations_updateValues
(JNIEnv *, jclass, jlong, jint, jint, jint);
/*
* Class: com_weis_darklaf_platform_windows_JNIDecorations
* Method: setResizable
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL Java_com_weis_darklaf_platform_windows_JNIDecorations_setResizable
(JNIEnv *, jclass, jlong, jboolean);
/*
* Class: com_weis_darklaf_platform_windows_JNIDecorations
* Method: setBackground
* Signature: (JIII)V
*/
JNIEXPORT void JNICALL Java_com_weis_darklaf_platform_windows_JNIDecorations_setBackground
(JNIEnv *, jclass, jlong, jint, jint, jint);
/*
* Class: com_weis_darklaf_platform_windows_JNIDecorations
* Method: minimize
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_weis_darklaf_platform_windows_JNIDecorations_minimize
(JNIEnv *, jclass, jlong);
/*
* Class: com_weis_darklaf_platform_windows_JNIDecorations
* Method: maximize
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_weis_darklaf_platform_windows_JNIDecorations_maximize
(JNIEnv *, jclass, jlong);
/*
* Class: com_weis_darklaf_platform_windows_JNIDecorations
* Method: restore
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_weis_darklaf_platform_windows_JNIDecorations_restore
(JNIEnv *, jclass, jlong);
/*
* Class: com_weis_darklaf_platform_windows_JNIDecorations
* Method: installDecorations
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_weis_darklaf_platform_windows_JNIDecorations_installDecorations
(JNIEnv *, jclass, jlong);
/*
* Class: com_weis_darklaf_platform_windows_JNIDecorations
* Method: uninstallDecorations
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_weis_darklaf_platform_windows_JNIDecorations_uninstallDecorations
(JNIEnv *, jclass, jlong);
#ifdef __cplusplus
}
#endif
#endif

4
src/main/java/com/weis/darklaf/components/ColorPipetteBase.java

@ -1,7 +1,6 @@
package com.weis.darklaf.components;
import com.bulenkov.iconloader.util.SystemInfo;
import com.weis.darklaf.platform.windows.WindowsFrameUtil;
import com.weis.darklaf.ui.colorchooser.ColorListener;
import com.weis.darklaf.ui.colorchooser.ColorPipette;
import com.weis.darklaf.util.DarkUIUtil;
@ -202,9 +201,6 @@ public abstract class ColorPipetteBase implements ColorPipette, AWTEventListener
closeAction.run();
}
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
if (SystemInfo.isWindows) {
WindowsFrameUtil.User32dll.INSTANCE.ShowCursor(true);
}
}
@Override

2
src/main/java/com/weis/darklaf/components/border/TextBubbleBorder.java

@ -304,7 +304,7 @@ public class TextBubbleBorder extends AbstractBorder {
}
@Contract("_, _, _, _ -> new")
public RoundRectangle2D.@NotNull Double calculateBubbleRect(final int x, final int y,
public RoundRectangle2D.Double calculateBubbleRect(final int x, final int y,
final int width, final int height) {
return new RoundRectangle2D.Double(x + insets.left, y + insets.top, width - insets.left - insets.right,
height - insets.top - insets.bottom, radius, radius);

36
src/main/java/com/weis/darklaf/platform/windows/JNIDecorations.java

@ -0,0 +1,36 @@
package com.weis.darklaf.platform.windows;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef;
import java.awt.*;
public class JNIDecorations {
public static native void updateValues(final long hwnd, final int left, final int right, final int height);
public static native void setResizable(final long hwnd, final boolean resizable);
public static native void setBackground(final long hwnd, final int r, final int g, final int b);
public static native void minimize(final long hwnd);
public static native void maximize(final long hwnd);
public static native void restore(final long hwnd);
public static native void installDecorations(final long hwnd);
public static native void uninstallDecorations(final long hwnd);
public static long getHWND(final Component component) {
var hwnd = new WinDef.HWND();
hwnd.setPointer(Native.getComponentPointer(component));
return Pointer.nativeValue(hwnd.getPointer());
}
static {
System.loadLibrary("jniplatform");
}
}

82
src/main/java/com/weis/darklaf/platform/windows/WindowsFrameUtil.java

@ -1,82 +0,0 @@
package com.weis.darklaf.platform.windows;
import com.sun.jna.LastErrorException;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public final class WindowsFrameUtil {
public static void enableTitleBar(final Window window, final boolean enabled, final boolean resizable) {
if (window == null) return;
final HWND hwnd = getHWND(window);
int style = User32.INSTANCE.GetWindowLong(hwnd, User32dll.GWL_STYLE);
if (enabled) {
style |= User32.WS_CAPTION;
} else {
style = User32.WS_THICKFRAME;
}
if (!resizable) {
style &= ~User32.WS_SIZEBOX;
} else {
style |= User32.WS_SIZEBOX;
}
//ResizeMode="CanResizeWithGrip" AllowsTransparency="True"
User32.INSTANCE.SetWindowLong(hwnd, User32dll.GWL_STYLE, style);
User32.INSTANCE.SetWindowPos(hwnd, new HWND(new Pointer(0L)),
0, 0, 0, 0, 0x27);
}
public static void setWindowCallback(final Window window, final WindowProc callback) {
if (window == null) return;
final HWND hwnd = getHWND(window);
callback.parent = User32dll.INSTANCE.GetWindowLongPtr(hwnd, (long) User32dll.GWL_WNDPROC);
User32dll.INSTANCE.SetWindowLongPtr(hwnd, User32dll.GWL_WNDPROC, callback);
}
@NotNull
@Contract("_ -> new")
public static HWND getHWND(final Component component) {
var hwnd = new HWND();
hwnd.setPointer(Native.getComponentPointer(component));
return hwnd;
}
public interface User32dll extends User32 {
User32dll INSTANCE = Native.loadLibrary("user32", User32dll.class,
W32APIOptions.UNICODE_OPTIONS);
WNDPROC GetWindowLongPtr(HWND hWnd, long nIndex) throws LastErrorException;
LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, WNDPROC wndProc) throws LastErrorException;
int ShowCursor(boolean bShow);
void DisableProcessWindowsGhosting() throws LastErrorException;
INT_PTR GetWindowDC(HWND hwnd);
int ReleaseDC(HWND hwnd, INT_PTR hdc);
}
public interface WNDPROC extends StdCallLibrary.StdCallCallback {
WinDef.LRESULT callback(HWND hWnd, int uMsg, WinDef.WPARAM uParam, WinDef.LPARAM lParam) throws LastErrorException;
}
public abstract static class WindowProc implements WNDPROC {
protected WNDPROC parent;
}
}

27
src/main/java/com/weis/darklaf/ui/colorchooser/DarkColorChooserUI.java

@ -8,9 +8,14 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.colorchooser.AbstractColorChooserPanel;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicColorChooserUI;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.beans.PropertyChangeListener;
public class DarkColorChooserUI extends BasicColorChooserUI {
@ -33,6 +38,26 @@ public class DarkColorChooserUI extends BasicColorChooserUI {
}
}
};
private final AncestorListener ancestorListener = new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent event) {
var win = SwingUtilities.getWindowAncestor(chooser);
if (win instanceof Dialog) {
((Dialog)win).setResizable(false);
chooser.removeAncestorListener(ancestorListener);
}
}
@Override
public void ancestorRemoved(AncestorEvent event) {
}
@Override
public void ancestorMoved(AncestorEvent event) {
}
};
@NotNull
@Contract("_ -> new")
@ -54,12 +79,14 @@ public class DarkColorChooserUI extends BasicColorChooserUI {
protected void installListeners() {
super.installListeners();
chooser.addPropertyChangeListener(propertyChangeListener);
chooser.addAncestorListener(ancestorListener);
}
@Override
protected void uninstallListeners() {
super.uninstallListeners();
chooser.removePropertyChangeListener(propertyChangeListener);
chooser.removeAncestorListener(ancestorListener);
}
@Override

842
src/main/java/com/weis/darklaf/ui/rootpane/DarkRootPaneUI.java

@ -16,55 +16,31 @@ package com.weis.darklaf.ui.rootpane;
* limitations under the License.
*/
import com.sun.jna.platform.win32.WinDef;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicRootPaneUI;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
/**
* @author Konstantin Bulenkov
*/
public class DarkRootPaneUI extends BasicRootPaneUI {
private Cursor lastCursor;
private enum CursorState {EXITED, ENTERED, NIL}
private static final int CORNER_DRAG_WIDTH = 16;
private static final int BORDER_DRAG_THICKNESS = 5;
public class DarkRootPaneUI extends BasicRootPaneUI implements HierarchyListener {
private Window window;
private DarkTitlePane titlePane;
private MouseInputListener mouseInputListener;
private MouseInputListener titleMouseInputListener;
private LayoutManager layoutManager;
private LayoutManager oldLayout;
protected JRootPane rootPane;
protected WindowListener windowListener;
protected Window currentWindow;
protected HierarchyListener hierarchyListener;
protected ComponentListener windowComponentListener;
protected GraphicsConfiguration currentRootPaneGC;
protected PropertyChangeListener propertyChangeListener;
private JRootPane rootPane;
private HierarchyListener hierarchyListener;
private PropertyChangeListener propertyChangeListener;
@NotNull
@ -83,16 +59,12 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
public void uninstallUI(final JComponent c) {
super.uninstallUI(c);
uninstallClientDecorations(rootPane);
layoutManager = null;
mouseInputListener = null;
rootPane = null;
}
public void installBorder(@NotNull final JRootPane root) {
int style = root.getWindowDecorationStyle();
if (style == JRootPane.NONE) {
LookAndFeel.uninstallBorder(root);
} else {
@ -105,40 +77,12 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
}
private void installWindowListeners(final JRootPane root, final Component parent) {
private void updateWindow(final Component parent) {
if (parent instanceof Window) {
window = (Window) parent;
} else {
window = SwingUtilities.getWindowAncestor(parent);
}
if (window != null) {
if (mouseInputListener == null) {
mouseInputListener = createWindowMouseInputListener(root);
}
window.addMouseListener(mouseInputListener);
window.addMouseMotionListener(mouseInputListener);
if (titlePane != null) {
if (titleMouseInputListener == null) {
titleMouseInputListener = new TitleMouseInputHandler();
}
titlePane.addMouseMotionListener(titleMouseInputListener);
titlePane.addMouseListener(titleMouseInputListener);
}
setMaximized();
}
}
private void uninstallWindowListeners(final JRootPane root) {
if (window != null) {
window.removeMouseListener(mouseInputListener);
window.removeMouseMotionListener(mouseInputListener);
}
if (titlePane != null) {
titlePane.removeMouseListener(titleMouseInputListener);
titlePane.removeMouseMotionListener(titleMouseInputListener);
}
}
private void installLayout(final JRootPane root) {
@ -152,130 +96,12 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
@Override
protected void installListeners(final JRootPane root) {
super.installListeners(root);
hierarchyListener = e -> {
Component parent = root.getParent();
if (parent == null) {
return;
}
if (parent.getClass().getName().startsWith("org.jdesktop.jdic.tray")
|| (parent.getClass().getName().compareTo("javax.swing.Popup$HeavyWeightWindow") == 0)) {
//noinspection SSBasedInspection
SwingUtilities.invokeLater(() -> {
root.removeHierarchyListener(hierarchyListener);
hierarchyListener = null;
});
}
Window currWindow;
if (parent instanceof Window) {
currWindow = (Window) parent;
} else {
currWindow = SwingUtilities.getWindowAncestor(parent);
}
if (windowListener != null) {
currentWindow.removeWindowListener(windowListener);
windowListener = null;
}
if (windowComponentListener != null) {
currentWindow.removeComponentListener(windowComponentListener);
windowComponentListener = null;
}
if (currWindow != null) {
windowListener = new WindowAdapter() {
@Override
public void windowClosed(final WindowEvent e) {
//noinspection SSBasedInspection
SwingUtilities.invokeLater(() -> {
Frame[] frames = Frame.getFrames();
for (Frame frame : frames) {
if (frame.isDisplayable()) {
return;
}
}
});
}
};
if (!(parent instanceof JInternalFrame)) {
currWindow.addWindowListener(windowListener);
}
windowComponentListener = new ComponentAdapter() {
@Override
public void componentMoved(final ComponentEvent e) {
processNewPosition();
}
@Override
public void componentResized(final ComponentEvent e) {
processNewPosition();
}
private void processNewPosition() {
//noinspection SSBasedInspection
SwingUtilities.invokeLater(() -> {
if (window == null) {
return;
}
if (!window.isShowing()
|| !window.isDisplayable()) {
currentRootPaneGC = null;
return;
}
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
GraphicsDevice[] gds = ge
.getScreenDevices();
if (gds.length == 1) {
return;
}
Point midLoc = new Point(window.getLocationOnScreen().x + window.getWidth() / 2,
window.getLocationOnScreen().y
+ window.getHeight() / 2);
for (GraphicsDevice gd : gds) {
GraphicsConfiguration gc = gd.getDefaultConfiguration();
Rectangle bounds = gc.getBounds();
if (bounds.contains(midLoc)) {
if (gc != currentRootPaneGC) {
currentRootPaneGC = gc;
setMaximized();
}
break;
}
}
});
}
};
if (parent instanceof JFrame) {
currWindow.addComponentListener(windowComponentListener);
}
if (currWindow != window) {
installClientDecorations(rootPane);
}
window = currWindow;
}
currentWindow = currWindow;
};
root.addHierarchyListener(hierarchyListener);
root.addHierarchyListener(this);
root.addPropertyChangeListener(propertyChangeListener);
}
@Override
protected void uninstallListeners(final JRootPane root) {
if (window != null) {
window.removeWindowListener(windowListener);
windowListener = null;
window
.removeComponentListener(windowComponentListener);
windowComponentListener = null;
}
root.removeHierarchyListener(hierarchyListener);
hierarchyListener = null;
@ -285,11 +111,6 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
super.uninstallListeners(root);
}
/**
* Uninstalls the previously installed <code>LayoutManager</code>.
*
* @param root Root pane.
*/
private void uninstallLayout(final JRootPane root) {
if (oldLayout != null) {
root.setLayout(oldLayout);
@ -297,18 +118,11 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
}
}
/**
* Installs the necessary state onto the JRootPane to render client
* decorations. This is ONLY invoked if the <code>JRootPane</code> has a
* decoration style other than <code>JRootPane.NONE</code>.
*
* @param root Root pane.
*/
private void installClientDecorations(final JRootPane root) {
installBorder(root);
DarkTitlePane titlePane = createTitlePane(root);
setTitlePane(root, titlePane);
installWindowListeners(root, root.getParent());
updateWindow(root.getParent());
installLayout(root);
if (window != null) {
if (window instanceof Frame) {
@ -323,7 +137,6 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
private void uninstallClientDecorations(final JRootPane root) {
uninstallBorder(root);
uninstallWindowListeners(root);
setTitlePane(root, null);
uninstallLayout(root);
int style = root.getWindowDecorationStyle();
@ -335,7 +148,6 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
titlePane.uninstall();
titlePane = null;
}
if (window != null) {
window.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
@ -343,13 +155,7 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
}
protected DarkTitlePane createTitlePane(final JRootPane root) {
return new DarkTitlePane(root, this);
}
@NotNull
@Contract(value = "_ -> new", pure = true)
private MouseInputListener createWindowMouseInputListener(final JRootPane root) {
return new DarkRootPaneUI.MouseInputHandler();
return new DarkTitlePane(root);
}
protected LayoutManager createLayoutManager() {
@ -370,38 +176,16 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
this.titlePane = titlePane;
}
public void setMaximized() {
Component tla = rootPane.getTopLevelAncestor();
GraphicsConfiguration gc = (currentRootPaneGC != null) ? currentRootPaneGC
: tla.getGraphicsConfiguration();
var mode = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
Rectangle maxBounds = new Rectangle(screenInsets.left, screenInsets.top,
mode.getWidth() - ((screenInsets.left + screenInsets.right)),
mode.getHeight() - ((screenInsets.top + screenInsets.bottom)));
if (tla instanceof JFrame) {
((JFrame) tla).setMaximizedBounds(maxBounds);
}
}
public JComponent getTitlePane() {
@Contract(pure = true)
protected JComponent getTitlePane() {
return titlePane;
}
protected JRootPane getRootPane() {
return rootPane;
}
@Override
public void propertyChange(final PropertyChangeEvent e) {
super.propertyChange(e);
String propertyName = e.getPropertyName();
if (propertyName == null) {
return;
}
if (propertyName.equals("windowDecorationStyle")) {
if ("windowDecorationStyle".equals(propertyName)) {
JRootPane root = (JRootPane) e.getSource();
int style = root.getWindowDecorationStyle();
@ -409,594 +193,40 @@ public class DarkRootPaneUI extends BasicRootPaneUI {
if (style != JRootPane.NONE) {
installClientDecorations(root);
}
}
if (propertyName.equals("ancestor")) {
uninstallWindowListeners(rootPane);
} else if ("ancestor".equals(propertyName)) {
if (((JRootPane) e.getSource()).getWindowDecorationStyle() != JRootPane.NONE) {
installWindowListeners(rootPane, rootPane.getParent());
updateWindow(rootPane.getParent());
}
}
}
protected static class SubstanceRootLayout implements LayoutManager2 {
public Dimension preferredLayoutSize(@NotNull final Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
int cpHeight = 0;
int mbWidth = 0;
int mbHeight = 0;
int tpWidth = 0;
int tpHeight = 0;
Insets i = parent.getInsets();
JRootPane root = (JRootPane) parent;
if (root.getContentPane() != null) {
cpd = root.getContentPane().getPreferredSize();
} else {
cpd = root.getSize();
}
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
if (root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getPreferredSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI()).getTitlePane();
if (titlePane != null) {
tpd = titlePane.getPreferredSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth)
+ i.left + i.right, cpHeight + mbHeight + tpHeight + i.top + i.bottom);
}
public Dimension minimumLayoutSize(@NotNull final Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
int cpHeight = 0;
int mbWidth = 0;
int mbHeight = 0;
int tpWidth = 0;
int tpHeight = 0;
Insets i = parent.getInsets();
JRootPane root = (JRootPane) parent;
if (root.getContentPane() != null) {
cpd = root.getContentPane().getMinimumSize();
} else {
cpd = root.getSize();
}
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
if (root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getMinimumSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI())
.getTitlePane();
if (titlePane != null) {
tpd = titlePane.getMinimumSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth)
+ i.left + i.right, cpHeight + mbHeight + tpHeight + i.top
+ i.bottom);
}
public Dimension maximumLayoutSize(@NotNull final Container target) {
Dimension cpd, mbd, tpd;
int cpWidth = Integer.MAX_VALUE;
int cpHeight = Integer.MAX_VALUE;
int mbWidth = Integer.MAX_VALUE;
int mbHeight = Integer.MAX_VALUE;
int tpWidth = Integer.MAX_VALUE;
int tpHeight = Integer.MAX_VALUE;
Insets i = target.getInsets();
JRootPane root = (JRootPane) target;
if (root.getContentPane() != null) {
cpd = root.getContentPane().getMaximumSize();
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
}
if (root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getMaximumSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI())
.getTitlePane();
if (titlePane != null) {
tpd = titlePane.getMaximumSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
int maxHeight = Math.max(Math.max(cpHeight, mbHeight), tpHeight);
if (maxHeight != Integer.MAX_VALUE) {
maxHeight = cpHeight + mbHeight + tpHeight + i.top + i.bottom;
}
int maxWidth = Math.max(Math.max(cpWidth, mbWidth), tpWidth);
if (maxWidth != Integer.MAX_VALUE) {
maxWidth += i.left + i.right;
}
return new Dimension(maxWidth, maxHeight);
}
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;
if (root.getLayeredPane() != null) {
root.getLayeredPane().setBounds(i.left, i.top, w, h);
}
if (root.getGlassPane() != null) {
root.getGlassPane().setBounds(i.left, i.top, w, h);
}
if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI())
.getTitlePane();
if (titlePane != null) {
Dimension tpd = titlePane.getPreferredSize();
if (tpd != null) {
int tpHeight = tpd.height;
titlePane.setBounds(0, 0, w, tpHeight);
nextY += tpHeight;
}
}
}
if (root.getJMenuBar() != null) {
Dimension mbd = root.getJMenuBar().getPreferredSize();
root.getJMenuBar().setBounds(0, nextY, w, mbd.height);
nextY += mbd.height;
}
if (root.getContentPane() != null) {
root.getContentPane().setBounds(0, nextY, w, h < nextY ? 0 : h - nextY);
}
}
public void addLayoutComponent(final String name, final Component comp) {
}
public void removeLayoutComponent(final Component comp) {
}
public void addLayoutComponent(final Component comp, final Object constraints) {
}
public float getLayoutAlignmentX(final Container target) {
return 0.0f;
}
public float getLayoutAlignmentY(final Container target) {
return 0.0f;
}
public void invalidateLayout(final Container target) {
}
}
private static final int[] cursorMapping = new int[]{
Cursor.NW_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR,
Cursor.N_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR,
Cursor.NE_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, 0, 0, 0,
Cursor.NE_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR, 0, 0, 0,
Cursor.E_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR, 0, 0, 0,
Cursor.SE_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR,
Cursor.SW_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR,
Cursor.SE_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR};
private class MouseInputHandler implements MouseInputListener {
private boolean isMovingWindow;
private int dragCursor;
private int dragOffsetX;
private int dragOffsetY;
private int dragWidth;
private int dragHeight;
private final PrivilegedExceptionAction<?> getLocationAction = () -> MouseInfo.getPointerInfo().getLocation();
public void mousePressed(final MouseEvent ev) {
JRootPane rootPane = getRootPane();
if (rootPane.getWindowDecorationStyle() == JRootPane.NONE) {
return;
}
Point dragWindowOffset = ev.getPoint();
Window w = (Window) ev.getSource();
if (w != null) {
w.toFront();
}
Point convertedDragWindowOffset = SwingUtilities.convertPoint(w, dragWindowOffset, getTitlePane());
Frame f = null;
Dialog d = null;
if (w instanceof Frame) {
f = (Frame) w;
} else if (w instanceof Dialog) {
d = (Dialog) w;
}
int frameState = (f != null) ? f.getExtendedState() : 0;
if ((getTitlePane() != null)
&& getTitlePane().contains(
convertedDragWindowOffset)) {
if ((((f != null) && ((frameState & Frame.MAXIMIZED_BOTH) == 0)) || (d != null))
&& (dragWindowOffset.y >= BORDER_DRAG_THICKNESS)
&& (dragWindowOffset.x >= BORDER_DRAG_THICKNESS)
&& (dragWindowOffset.x < w.getWidth() - BORDER_DRAG_THICKNESS)) {
isMovingWindow = true;
dragOffsetX = dragWindowOffset.x;
dragOffsetY = dragWindowOffset.y;
}
} else if (((f != null) && f.isResizable() && ((frameState & Frame.MAXIMIZED_BOTH) == 0))
|| ((d != null) && d.isResizable())) {
dragOffsetX = dragWindowOffset.x;
dragOffsetY = dragWindowOffset.y;
dragWidth = w.getWidth();
dragHeight = w.getHeight();
dragCursor = getCursor(calculateCorner(w, dragWindowOffset.x, dragWindowOffset.y));
}
}
public void mouseReleased(final MouseEvent ev) {
if ((dragCursor != 0)
&& (window != null)
&& !window.isValid()) {
window.validate();
getRootPane().repaint();
}
isMovingWindow = false;
dragCursor = 0;
}
public void mouseMoved(final MouseEvent ev) {
JRootPane root = getRootPane();
if (root.getWindowDecorationStyle() == JRootPane.NONE) {
return;
}
Window w = (Window) ev.getSource();
Frame f = null;
Dialog d = null;
if (w instanceof Frame) {
f = (Frame) w;
} else if (w instanceof Dialog) {
d = (Dialog) w;
}
int cursor = getCursor(calculateCorner(w, ev.getX(), ev.getY()));
if ((cursor != 0)
&& (((f != null) && (f.isResizable() && ((f.getExtendedState() & Frame.MAXIMIZED_BOTH) == 0)))
|| ((d != null) && d.isResizable()))) {
w.setCursor(Cursor.getPredefinedCursor(cursor));
} else {
w.setCursor(lastCursor);
}
}
private void adjust(@NotNull final Rectangle bounds, final Dimension min, final int deltaX,
final int deltaY, final int deltaWidth, final int deltaHeight) {
bounds.x += deltaX;
bounds.y += deltaY;
bounds.width += deltaWidth;
bounds.height += deltaHeight;
if (min != null) {
if (bounds.width < min.width) {
int correction = min.width - bounds.width;
if (deltaX != 0) {
bounds.x -= correction;
}
bounds.width = min.width;
}
if (bounds.height < min.height) {
int correction = min.height - bounds.height;
if (deltaY != 0) {
bounds.y -= correction;
}
bounds.height = min.height;
}
}
}
public void mouseDragged(@NotNull final MouseEvent ev) {
Window w = (Window) ev.getSource();
Point pt = ev.getPoint();
if (isMovingWindow) {
Point windowPt;
try {
windowPt = (Point) AccessController.doPrivileged(getLocationAction);
windowPt.x = windowPt.x - dragOffsetX;
windowPt.y = windowPt.y - dragOffsetY;
setLocation(window, windowPt.x, windowPt.y);
} catch (PrivilegedActionException ignored) {
}
} else if (dragCursor != 0) {
Rectangle r = w.getBounds();
Rectangle startBounds = new Rectangle(r);
Dimension min = w.getMinimumSize();
switch (dragCursor) {
case Cursor.E_RESIZE_CURSOR:
adjust(r, min, 0, 0, pt.x
+ (dragWidth - dragOffsetX) - r.width, 0);
break;
case Cursor.S_RESIZE_CURSOR:
adjust(r, min, 0, 0, 0, pt.y
+ (dragHeight - dragOffsetY) - r.height);
break;
case Cursor.N_RESIZE_CURSOR:
adjust(r, min, 0, pt.y - dragOffsetY, 0,
-(pt.y - dragOffsetY));
break;
case Cursor.W_RESIZE_CURSOR:
adjust(r, min, pt.x - dragOffsetX, 0,
-(pt.x - dragOffsetX), 0);
break;
case Cursor.NE_RESIZE_CURSOR:
adjust(r, min, 0, pt.y - dragOffsetY, pt.x
+ (dragWidth - dragOffsetX) - r.width,
-(pt.y - dragOffsetY));
break;
case Cursor.SE_RESIZE_CURSOR:
adjust(r, min, 0, 0, pt.x
+ (dragWidth - dragOffsetX) - r.width,
pt.y + (dragHeight - dragOffsetY)
- r.height);
break;
case Cursor.NW_RESIZE_CURSOR:
adjust(r, min, pt.x - dragOffsetX, pt.y
- dragOffsetY, -(pt.x - dragOffsetX),
-(pt.y - dragOffsetY));
break;
case Cursor.SW_RESIZE_CURSOR:
adjust(r, min, pt.x - dragOffsetX, 0,
-(pt.x - dragOffsetX), pt.y
+ (dragHeight - dragOffsetY)
- r.height);
break;
default:
break;
}
if (!r.equals(startBounds)) {
w.setBounds(r);
if (Toolkit.getDefaultToolkit().isDynamicLayoutActive()) {
w.validate();
getRootPane().repaint();
}
}
}
}
private DarkRootPaneUI.CursorState cursorState =
DarkRootPaneUI.CursorState.NIL;
public void mouseEntered(@NotNull final MouseEvent ev) {
Window w = (Window) ev.getSource();
if (cursorState == DarkRootPaneUI.CursorState.EXITED
|| cursorState == DarkRootPaneUI.CursorState.NIL) {
lastCursor = w.getCursor();
}
cursorState = DarkRootPaneUI.CursorState.ENTERED;
mouseMoved(ev);
}
public void mouseExited(@NotNull final MouseEvent ev) {
Window w = (Window) ev.getSource();
w.setCursor(lastCursor);
cursorState = DarkRootPaneUI.CursorState.EXITED;
}
public void mouseClicked(@NotNull final MouseEvent ev) {
Window w = (Window) ev.getSource();
Frame f;
if (w instanceof Frame) {
f = (Frame) w;
} else {
return;
}
JComponent windowTitlePane = getTitlePane();
if (windowTitlePane == null) {
return;
}
Point convertedPoint = SwingUtilities.convertPoint(w, ev.getPoint(), windowTitlePane);
int state = f.getExtendedState();
if (windowTitlePane.contains(convertedPoint)) {
if (((ev.getClickCount() % 2) == 0)
&& ((ev.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
if (f.isResizable()) {
if ((state & Frame.MAXIMIZED_BOTH) != 0) {
setMaximized();
f.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
} else {
setMaximized();
f.setExtendedState(state | Frame.MAXIMIZED_BOTH);
}
}
}
}
}
private int calculateCorner(@NotNull final Window w, final int x, final int y) {
Insets insets = w.getInsets();
int xPosition = calculatePosition(x - insets.left, w.getWidth() - insets.left - insets.right);
int yPosition = calculatePosition(y - insets.top, w.getHeight() - insets.top - insets.bottom);
if ((xPosition == -1) || (yPosition == -1)) {
return -1;
}
return yPosition * 5 + xPosition;
}
@Contract(pure = true)
private int getCursor(final int corner) {
if (corner == -1) {
return 0;
}
return cursorMapping[corner];
}
@Contract(pure = true)
private int calculatePosition(final int spot, final int width) {
if (spot < BORDER_DRAG_THICKNESS) {
return 0;
}
if (spot < CORNER_DRAG_WIDTH) {
return 1;
}
if (spot >= (width - BORDER_DRAG_THICKNESS)) {
return 4;
}
if (spot >= (width - CORNER_DRAG_WIDTH)) {
return 3;
}
return 2;
@Override
public void hierarchyChanged(HierarchyEvent e) {
Component parent = rootPane.getParent();
if (parent == null) {
return;
}
}
private void setLocation(@NotNull final Window w, final int x, final int y) {
WinDef.LPARAM lParam = new WinDef.LPARAM();
lParam.setValue((y << 16) | x);
w.setLocation(x, y);
// WindowsFrameUtil.User32dll.INSTANCE.SendMessage(WindowsFrameUtil.getHWND(w), 3,
// new WinDef.WPARAM(0), lParam);
}
private class TitleMouseInputHandler extends MouseInputAdapter {
private Point dragOffset = new Point(0, 0);
@Override
public void mousePressed(final MouseEvent ev) {
JRootPane rootPane = getRootPane();
if (rootPane.getWindowDecorationStyle() == JRootPane.NONE) {
return;
}
Point dragWindowOffset = ev.getPoint();
Component source = (Component) ev.getSource();
Point convertedDragWindowOffset = SwingUtilities.convertPoint(
source, dragWindowOffset, getTitlePane());
dragWindowOffset = SwingUtilities.convertPoint(source, dragWindowOffset, window);
if (getTitlePane() != null && getTitlePane().contains(convertedDragWindowOffset)) {
if (window != null) {
window.toFront();
dragOffset = dragWindowOffset;
}
}
if (parent.getClass().getName().startsWith("org.jdesktop.jdic.tray")
|| (parent.getClass().getName().compareTo("javax.swing.Popup$HeavyWeightWindow") == 0)) {
SwingUtilities.invokeLater(() -> {
rootPane.removeHierarchyListener(hierarchyListener);
hierarchyListener = null;
});
}
@Override
public void mouseDragged(@NotNull final MouseEvent ev) {
Component source = (Component) ev.getSource();
Point eventLocationOnScreen = ev.getLocationOnScreen();
if (eventLocationOnScreen == null) {
eventLocationOnScreen = new Point(
ev.getX() + source.getLocationOnScreen().x, ev.getY() + source.getLocationOnScreen().y);
}
if (window instanceof Frame) {
Frame f = (Frame) window;
int frameState = f.getExtendedState();
if (((frameState & Frame.MAXIMIZED_BOTH) == 0)) {
setLocation(window, eventLocationOnScreen.x - dragOffset.x, eventLocationOnScreen.y - dragOffset.y);
}
} else {
setLocation(window, eventLocationOnScreen.x - dragOffset.x, eventLocationOnScreen.y - dragOffset.y);
}
Window currWindow;
if (parent instanceof Window) {
currWindow = (Window) parent;
} else {
currWindow = SwingUtilities.getWindowAncestor(parent);
}
@Override
public void mouseClicked(final MouseEvent e) {
Frame f;
if (window instanceof Frame) {
f = (Frame) window;
} else {
return;
}
Point convertedPoint = SwingUtilities.convertPoint(window, e.getPoint(), getTitlePane());
int state = f.getExtendedState();
if ((getTitlePane() != null) && getTitlePane().contains(convertedPoint)) {
if (((e.getClickCount() % 2) == 0) && ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0)) {
if (f.isResizable()) {
if ((state & Frame.MAXIMIZED_BOTH) != 0) {
setMaximized();
f.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
} else {
setMaximized();
f.setExtendedState(state | Frame.MAXIMIZED_BOTH);
}
}
}
if (currWindow != null) {
if (currWindow != window) {
uninstallClientDecorations(rootPane);
installClientDecorations(rootPane);
}
window = currWindow;
}
}
}

141
src/main/java/com/weis/darklaf/ui/rootpane/DarkTitlePane.java

@ -16,8 +16,7 @@ package com.weis.darklaf.ui.rootpane;
* limitations under the License.
*/
import com.sun.jna.Structure;
import com.weis.darklaf.platform.windows.WindowsFrameUtil;
import com.weis.darklaf.platform.windows.JNIDecorations;
import com.weis.darklaf.ui.button.DarkButtonUI;
import com.weis.darklaf.util.GraphicsUtil;
import org.jetbrains.annotations.Contract;
@ -107,6 +106,7 @@ public class DarkTitlePane extends JComponent {
private JLabel titleLabel;
private Window window;
private long windowHandle;
private final JRootPane rootPane;
private JMenuBar menuBar;
private int state;
@ -117,7 +117,7 @@ public class DarkTitlePane extends JComponent {
private Color activeForeground;
private Color border;
public DarkTitlePane(final JRootPane root, final DarkRootPaneUI ui) {
public DarkTitlePane(final JRootPane root) {
this.rootPane = root;
rootPane.addContainerListener(rootPaneContainerListener);
rootPane.getLayeredPane().addContainerListener(layeredPaneContainerListener);
@ -131,6 +131,8 @@ public class DarkTitlePane extends JComponent {
public void uninstall() {
uninstallListeners();
window = null;
JNIDecorations.uninstallDecorations(windowHandle);
windowHandle = 0;
rootPane.removeContainerListener(rootPaneContainerListener);
removeAll();
}
@ -178,34 +180,15 @@ public class DarkTitlePane extends JComponent {
window = SwingUtilities.getWindowAncestor(this);
if (window != null) {
if (window instanceof Dialog || window instanceof Frame) {
//rootPane.getWindowDecorationStyle() == JRootPane.FRAME
WindowsFrameUtil.enableTitleBar(window, false, true);
windowHandle = JNIDecorations.getHWND(window);
JNIDecorations.installDecorations(windowHandle);
JNIDecorations.setResizable(windowHandle, isResizable(rootPane));
var color = UIManager.getColor("RootPane.background");
JNIDecorations.setBackground(windowHandle, color.getRed(), color.getGreen(), color.getBlue());
}
// var hwnd = WindowsFrameUtil.getHWND(window);
// NativeLibrary dwm = NativeLibrary.getInstance("dwmapi");
// dwm.getFunction("DwmExtendFrameIntoClientArea").invoke(WinNT.HRESULT.class,
// new Object[]{hwnd, new MARGINS(0, 0, 0, 0)});
// WindowsFrameUtil.User32dll.INSTANCE.SetWindowPos(hwnd, new WinDef.HWND(new Pointer(0)), 0, 0, 0, 0,
// WinUser.SWP_NOZORDER |
// WinUser.SWP_NOOWNERZORDER |
// WinUser.SWP_NOMOVE |
// WinUser.SWP_NOSIZE |
// WinUser.SWP_FRAMECHANGED);
// WindowsFrameUtil.setWindowCallback(window, new WindowsFrameUtil.WindowProc() {
// @Override
// public WinDef.LRESULT callback(final WinDef.HWND hWnd, final int uMsg, final WinDef.WPARAM uParam,
// final WinDef.LPARAM lParam) throws LastErrorException {
// if (uMsg == 0x0083 && uParam.intValue() != 0) {
// return new WinDef.LRESULT(0);
// }
// return parent.callback(hWnd, uMsg, uParam, lParam);
// }
// });
if (window instanceof Frame) {
titleLabel.setText(((Frame) window).getTitle());
((Frame) window).setMaximizedBounds(getMaximizedBounds());
setState(((Frame) window).getExtendedState());
} else {
setState(0);
@ -219,41 +202,13 @@ public class DarkTitlePane extends JComponent {
}
}
public class MARGINS extends Structure implements Structure.ByReference {
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
public MARGINS(final int a, final int b, final int c, final int d) {
cxLeftWidth = a;
cxRightWidth = b;
cyTopHeight = c;
cyBottomHeight = d;
}
@Override
protected List<String> getFieldOrder() {
return List.of("cxLeftWidth", "cxRightWidth", "cyTopHeight", "cyBottomHeight");
}
}
@NotNull
private Rectangle getMaximizedBounds() {
Insets screenInsets = getToolkit().getScreenInsets(getGraphicsConfiguration());
Rectangle screenSize = getGraphicsConfiguration().getBounds();
return new Rectangle(screenInsets.left + screenSize.x,
screenInsets.top + screenSize.y,
screenSize.x + screenSize.width - screenInsets.right - screenInsets.left,
screenSize.y + screenSize.height - screenInsets.bottom - screenInsets.top);
protected boolean isResizable(@NotNull final JRootPane rootPane) {
return JRootPane.NONE != rootPane.getWindowDecorationStyle();
}
public void removeNotify() {
super.removeNotify();
uninstallListeners();
window = null;
}
private void installSubcomponents() {
@ -271,12 +226,12 @@ public class DarkTitlePane extends JComponent {
add(maximizeToggleButton);
add(closeButton);
} else if (decorationStyle == JRootPane.PLAIN_DIALOG ||
decorationStyle == JRootPane.INFORMATION_DIALOG ||
decorationStyle == JRootPane.ERROR_DIALOG ||
decorationStyle == JRootPane.COLOR_CHOOSER_DIALOG ||
decorationStyle == JRootPane.FILE_CHOOSER_DIALOG ||
decorationStyle == JRootPane.QUESTION_DIALOG ||
decorationStyle == JRootPane.WARNING_DIALOG) {
decorationStyle == JRootPane.INFORMATION_DIALOG ||
decorationStyle == JRootPane.ERROR_DIALOG ||
decorationStyle == JRootPane.COLOR_CHOOSER_DIALOG ||
decorationStyle == JRootPane.FILE_CHOOSER_DIALOG ||
decorationStyle == JRootPane.QUESTION_DIALOG ||
decorationStyle == JRootPane.WARNING_DIALOG) {
add(closeButton);
}
}
@ -316,10 +271,11 @@ public class DarkTitlePane extends JComponent {
windowIconButton = new JButton();
windowIconButton.setComponentPopupMenu(createMenu());
windowIconButton.putClientProperty("JButton.variant", "onlyLabel");
windowIconButton.addActionListener(
e -> windowIconButton.getComponentPopupMenu().show(windowIconButton,
windowIconButton.getWidth() / 2,
windowIconButton.getHeight() / 2));
windowIconButton.addActionListener(e -> windowIconButton
.getComponentPopupMenu()
.show(windowIconButton,
windowIconButton.getWidth() / 2,
windowIconButton.getHeight() / 2));
windowIconButton.setFocusable(false);
windowIconButton.setBorderPainted(false);
return windowIconButton;
@ -355,38 +311,21 @@ public class DarkTitlePane extends JComponent {
private void close() {
Window window = getWindow();
if (window != null) {
window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING));
}
}
private void minimize() {
Frame frame = getFrame();
if (frame != null) {
frame.setExtendedState(state | Frame.ICONIFIED);
}
JNIDecorations.minimize(windowHandle);
}
private void maximize() {
Frame frame = getFrame();
if (frame != null) {
frame.setExtendedState(state | Frame.MAXIMIZED_BOTH);
}
JNIDecorations.maximize(windowHandle);
}
private void restore() {
Frame frame = getFrame();
if (frame == null) {
return;
}
if ((state & Frame.ICONIFIED) != 0) {
frame.setExtendedState(state & ~Frame.ICONIFIED);
} else {
frame.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
}
JNIDecorations.restore(windowHandle);
}
private void createActions() {
@ -470,17 +409,13 @@ public class DarkTitlePane extends JComponent {
if (frame != null) {
JRootPane rootPane = getRootPane();
if (((state & Frame.MAXIMIZED_BOTH) != 0) &&
(rootPane.getBorder() == null ||
(rootPane.getBorder() instanceof UIResource)) &&
frame.isShowing()) {
if (((state & Frame.MAXIMIZED_BOTH) != 0)
&& (rootPane.getBorder() == null
|| (rootPane.getBorder() instanceof UIResource))
&& frame.isShowing()) {
rootPane.setBorder(null);
} else if ((state & Frame.MAXIMIZED_BOTH) == 0) {
// This is a croak, if state becomes bound, this can
// be nuked.
//Todo
// rootPaneUI.installBorder(rootPane);
}
if (frame.isResizable()) {
if ((state & Frame.MAXIMIZED_BOTH) != 0) {
updateToggleButton(restoreAction, restoreIcon);
@ -491,8 +426,7 @@ public class DarkTitlePane extends JComponent {
maximizeAction.setEnabled(true);
restoreAction.setEnabled(false);
}
if (maximizeToggleButton.getParent() == null ||
minimizeButton.getParent() == null) {
if (maximizeToggleButton.getParent() == null || minimizeButton.getParent() == null) {
add(maximizeToggleButton);
add(minimizeButton);
revalidate();
@ -653,36 +587,46 @@ public class DarkTitlePane extends JComponent {
int start = 0;
int y = 0;
int height = computeHeight();
int left = 0;
int right = 0;
if (leftToRight) {
if (windowIconButton != null) {
windowIconButton.setBounds(start + PAD, y, ICON_WIDTH, height);
start += ICON_WIDTH + PAD;
left = start;
}
if (menuBar != null) {
int menuWidth = getPreferredMenuSize().width;
Insets menuInsets = menuBar.getInsets();
menuBar.setBounds(start + PAD, y, menuWidth, height + menuInsets.bottom);
start += menuWidth + PAD;
left += menuWidth;
}
x = w;
if (closeButton != null) {
x -= BUTTON_WIDTH;
right += BUTTON_WIDTH;
closeButton.setBounds(x, y, BUTTON_WIDTH, height);
}
if (getWindowDecorationStyle() == JRootPane.FRAME) {
if (Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
if (minimizeButton != null && maximizeToggleButton.getParent() != null) {
x -= BUTTON_WIDTH;
right += BUTTON_WIDTH;
maximizeToggleButton.setBounds(x, y, BUTTON_WIDTH, height);
}
if (minimizeButton != null && minimizeButton.getParent() != null) {
x -= BUTTON_WIDTH;
right += BUTTON_WIDTH;
minimizeButton.setBounds(x, y, BUTTON_WIDTH, height);
}
}
}
titleLabel.setBounds(start + TITLE_PAD, 0, x - start - 2 * TITLE_PAD, height);
JNIDecorations.updateValues(windowHandle, (int) (left * GraphicsUtil.SCALE_X),
(int) (right * GraphicsUtil.SCALE_X),
(int) (height * GraphicsUtil.SCALE_Y));
} else {
//Todo.
}
@ -698,6 +642,7 @@ public class DarkTitlePane extends JComponent {
setState(frame.getExtendedState(), true);
}
if ("resizable".equals(name)) {
JNIDecorations.setResizable(windowHandle, Boolean.TRUE.equals(pce.getNewValue()));
getRootPane().repaint();
}
} else if ("title".equals(name)) {

213
src/main/java/com/weis/darklaf/ui/rootpane/SubstanceRootLayout.java

@ -0,0 +1,213 @@
package com.weis.darklaf.ui.rootpane;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
class SubstanceRootLayout implements LayoutManager2 {
public Dimension preferredLayoutSize(@NotNull final Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
int cpHeight = 0;
int mbWidth = 0;
int mbHeight = 0;
int tpWidth = 0;
int tpHeight = 0;
Insets i = parent.getInsets();
JRootPane root = (JRootPane) parent;
if (root.getContentPane() != null) {
cpd = root.getContentPane().getPreferredSize();
} else {
cpd = root.getSize();
}
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
if (root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getPreferredSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI()).getTitlePane();
if (titlePane != null) {
tpd = titlePane.getPreferredSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth)
+ i.left + i.right,
cpHeight + mbHeight + tpHeight + i.top + i.bottom);
}
public Dimension minimumLayoutSize(@NotNull final Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
int cpHeight = 0;
int mbWidth = 0;
int mbHeight = 0;
int tpWidth = 0;
int tpHeight = 0;
Insets i = parent.getInsets();
JRootPane root = (JRootPane) parent;
if (root.getContentPane() != null) {
cpd = root.getContentPane().getMinimumSize();
} else {
cpd = root.getSize();
}
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
if (root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getMinimumSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI())
.getTitlePane();
if (titlePane != null) {
tpd = titlePane.getMinimumSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
return new Dimension(Math.max(Math.max(cpWidth, mbWidth), tpWidth)
+ i.left + i.right, cpHeight + mbHeight + tpHeight + i.top
+ i.bottom);
}
public Dimension maximumLayoutSize(@NotNull final Container target) {
Dimension cpd, mbd, tpd;
int cpWidth = Integer.MAX_VALUE;
int cpHeight = Integer.MAX_VALUE;
int mbWidth = Integer.MAX_VALUE;
int mbHeight = Integer.MAX_VALUE;
int tpWidth = Integer.MAX_VALUE;
int tpHeight = Integer.MAX_VALUE;
Insets i = target.getInsets();
JRootPane root = (JRootPane) target;
if (root.getContentPane() != null) {
cpd = root.getContentPane().getMaximumSize();
if (cpd != null) {
cpWidth = cpd.width;
cpHeight = cpd.height;
}
}
if (root.getJMenuBar() != null) {
mbd = root.getJMenuBar().getMaximumSize();
if (mbd != null) {
mbWidth = mbd.width;
mbHeight = mbd.height;
}
}
if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI())
.getTitlePane();
if (titlePane != null) {
tpd = titlePane.getMaximumSize();
if (tpd != null) {
tpWidth = tpd.width;
tpHeight = tpd.height;
}
}
}
int maxHeight = Math.max(Math.max(cpHeight, mbHeight), tpHeight);
if (maxHeight != Integer.MAX_VALUE) {
maxHeight = cpHeight + mbHeight + tpHeight + i.top + i.bottom;
}
int maxWidth = Math.max(Math.max(cpWidth, mbWidth), tpWidth);
if (maxWidth != Integer.MAX_VALUE) {
maxWidth += i.left + i.right;
}
return new Dimension(maxWidth, maxHeight);
}
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;
if (root.getLayeredPane() != null) {
root.getLayeredPane().setBounds(i.left, i.top, w, h);
}
if (root.getGlassPane() != null) {
root.getGlassPane().setBounds(i.left, i.top, w, h);
}
if ((root.getWindowDecorationStyle() != JRootPane.NONE)
&& (root.getUI() instanceof DarkRootPaneUI)) {
JComponent titlePane = ((DarkRootPaneUI) root.getUI())
.getTitlePane();
if (titlePane != null) {
Dimension tpd = titlePane.getPreferredSize();
if (tpd != null) {
int tpHeight = tpd.height;
titlePane.setBounds(0, 0, w, tpHeight);
nextY += tpHeight;
}
}
}
if (root.getJMenuBar() != null) {
Dimension mbd = root.getJMenuBar().getPreferredSize();
root.getJMenuBar().setBounds(0, nextY, w, mbd.height);
nextY += mbd.height;
}
if (root.getContentPane() != null) {
root.getContentPane().setBounds(0, nextY, w, h < nextY ? 0 : h - nextY);
}
}
public void addLayoutComponent(final String name, final Component comp) {
}
public void removeLayoutComponent(final Component comp) {
}
public void addLayoutComponent(final Component comp, final Object constraints) {
}
public float getLayoutAlignmentX(final Container target) {
return 0.0f;
}
public float getLayoutAlignmentY(final Container target) {
return 0.0f;
}
public void invalidateLayout(final Container target) {
}
}

1
src/main/java/com/weis/darklaf/util/DarkUIUtil.java

@ -160,6 +160,7 @@ public final class DarkUIUtil {
return SwingUtilities.getWindowAncestor(owner) == w;
}
@SuppressWarnings("unchecked")
@Nullable
public static <T> T getParentOfType(final Class<? extends T> cls, final Component c) {
for (Component eachParent = c; eachParent != null; eachParent = eachParent.getParent()) {

2
src/main/resources/com/weis/darklaf/darcula.properties

@ -133,7 +133,7 @@ Panel.background = 3c3f41
#RootPane
RootPaneUI = com.weis.darklaf.ui.rootpane.DarkRootPaneUI
RootPane.background = 3c3f41
#OptionPane
OptionPane.messageForeground = bbbbbb

1
src/test/java/ColorChooserDemo.java

@ -10,6 +10,7 @@ public final class ColorChooserDemo {
LafManager.loadLaf(LafManager.Theme.Dark);
JColorChooser.showDialog(null, "Color Chooser without transparency",
Color.RED, true);
// JOptionPane.showMessageDialog(null, "This is a test!","THis is a test!", JOptionPane.ERROR_MESSAGE);
});
}
}

Loading…
Cancel
Save