Как изменить цвет панели управления окном JavaFX

Возможно ли изменить цвет стандартной панели управления окном Windows на JavaFXЭтой самой панели

Буду благодарен за ответ


Ответы (1 шт):

Автор решения: Librian

Вы можете использовать WinAPI для изменения цвета заголовка окна.

В Windows 10 его можно сделать черного цвета через DwmSetWindowAttribute с помощью атрибута DWMWA_USE_IMMERSIVE_DARK_MODE(20)

https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmsetwindowattribute

Пример использования этой функции на java 21 (аргументы jvm для запуска --enable-preview --enable-native-access=ALL-UNNAMED) с javafx 23:

import com.sun.javafx.tk.TKStage;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.SymbolLookup;
import java.lang.invoke.MethodHandle;

import static java.lang.foreign.ValueLayout.*;

public class App extends Application {
    void run() {
        launch();
    }

    @Override
    public void start(Stage stage) {
        stage.setScene(new Scene(new Pane()));
        
        useImmersiveDarkMode(stage);
    }

    void useImmersiveDarkMode(Stage stage) {
        final int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;

        Arena arena = Arena.ofConfined();
        Linker linker = Linker.nativeLinker();
        MethodHandle dwmSetWindowAttribute = linker.downcallHandle(
                SymbolLookup
                        .libraryLookup("Dwmapi", arena)
                        .find("DwmSetWindowAttribute")
                        .orElseThrow(),
                FunctionDescriptor.of(JAVA_LONG, JAVA_LONG, JAVA_INT, ADDRESS, JAVA_INT));

        stage.setWidth(499);
        stage.setHeight(499);
        stage.show();

        try {
            // Получение дескриптора окна
            var getPeer = stage.getClass().getSuperclass().getDeclaredMethod("getPeer");
            getPeer.setAccessible(true);
            var hwnd = ((TKStage) getPeer.invoke(stage)).getRawHandle();

            dwmSetWindowAttribute.invoke(
                    hwnd,
                    DWMWA_USE_IMMERSIVE_DARK_MODE,
                    arena.allocate(JAVA_INT, 1),
                    4
            );
        } catch (Throwable _) {
        }

        arena.close();

        //обновления размеров окна чтобы изменения стали видимыми
        stage.setWidth(500);
        stage.setHeight(500);
    }
}

void main() {
    new App().run();
}

на более старых версиях foreign functions api может отличаться или можно вызвать DwmSetWindowAttribute через JNI:

native void useImmersiveDarkMode(long hwnd, boolean value);

#include <jni.h>
#include "Dwmapi.h"

JNIEXPORT void JNICALL Java_App_useImmersiveDarkMode(JNIEnv* env, jobject obj, jlong hwnd, jint value) {
    DwmSetWindowAttribute((HWND)hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(int));
}

Команда компиляции через clang: clang -shared -I"C:\Program Files\Java\jdk-21\include" -I"C:\Program Files\Java\jdk-21\include\win32" -lDwmapi -o lib.dll lib.c

import com.sun.javafx.tk.TKStage;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.io.File;

public class App extends Application {
    void run() {
        System.load(new File("./lib.dll").getAbsolutePath());
        launch();
    }

    native void useImmersiveDarkMode(long hwnd, boolean value);

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(new Pane()));

        stage.setWidth(499);
        stage.setHeight(499);
        stage.show();

        var getPeer = stage.getClass().getSuperclass().getDeclaredMethod("getPeer");
        getPeer.setAccessible(true);
        var hwnd = ((TKStage) getPeer.invoke(stage)).getRawHandle();

        useImmersiveDarkMode(hwnd, true);

        stage.setWidth(500);
        stage.setHeight(500);
    }
}

В Windows 11 добавили возможность установить любой цвет для фона и цвет шрифта заголовка через атрибуты

DWMWA_BORDER_COLOR(34)

DWMWA_CAPTION_COLOR(35)

DWMWA_TEXT_COLOR(36)

→ Ссылка