Skip to content

Dark titlebar #13386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We added functionality to focus running instance when trying to start a second instance. [#13129](https://github.com/JabRef/jabref/issues/13129)
- We added a new setting in the 'Entry Editor' preferences to hide the 'File Annotations' tab when no annotations are available. [#13143](https://github.com/JabRef/jabref/issues/13143)
- We added support for multi-file import across different formats. [#13269](https://github.com/JabRef/jabref/issues/13269)
- We added support for Windows dark title bar. [#11457](https://github.com/JabRef/jabref/issues/11457)

### Changed

Expand Down
2 changes: 2 additions & 0 deletions jabgui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ dependencies {
implementation("org.openjfx:javafx-swing")
implementation("org.openjfx:javafx-web")

implementation("com.pixelduke:fxthemes:1.5.1")

implementation("org.slf4j:slf4j-api")
implementation("org.tinylog:tinylog-api")
implementation("org.tinylog:slf4j-tinylog")
Expand Down
4 changes: 3 additions & 1 deletion jabgui/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,15 @@

// region: other libraries (alphabetically)
// requires cuid;
requires com.dlsc.pdfviewfx;
requires com.pixelduke.fxthemes;
// requires com.sun.jna;
// requires dd.plist;
requires io.github.adr;
// required by okhttp and some AI library
// requires kotlin.stdlib;
// requires mslinks;
requires org.antlr.antlr4.runtime;
requires org.libreoffice.uno;
requires com.dlsc.pdfviewfx;
// endregion
}
77 changes: 77 additions & 0 deletions jabgui/src/main/java/org/jabref/gui/desktop/os/ThemeListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.jabref.gui.desktop.os;

import javafx.collections.ListChangeListener;
import javafx.stage.Stage;
import javafx.stage.Window;

import org.jabref.gui.util.BindingsHelper;
import org.jabref.logic.os.OS;

import com.pixelduke.window.ThemeWindowManager;
import com.pixelduke.window.ThemeWindowManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Listener controlling platform-specific window title bar appearance using FXThemes.
*/
public class ThemeListener {
private static final Logger LOGGER = LoggerFactory.getLogger(ThemeListener.class);
private static final ThemeWindowManager THEME_WINDOW_MANAGER = ThemeWindowManagerFactory.create();
private static final boolean SUPPORTS_DARK_MODE = OS.WINDOWS || OS.OS_X;
private static boolean initialized = false;
private static boolean currentDarkMode = false;

public static void initialize(boolean darkMode) {
if (initialized || !SUPPORTS_DARK_MODE) {
return;
}

currentDarkMode = darkMode;

ListChangeListener<Window> windowsListener = change -> {
while (change.next()) {
if (!change.wasAdded()) {
continue;
}
change.getAddedSubList().stream()
.filter(Stage.class::isInstance)
.map(Stage.class::cast)
.forEach(stage -> {
BindingsHelper.subscribeFuture(stage.showingProperty(), showing -> {
if (showing) {
setDarkMode(stage, currentDarkMode);
}
});
if (stage.isShowing()) {
setDarkMode(stage, currentDarkMode);
}
});
}
};

Window.getWindows().addListener(windowsListener);
setDarkMode(darkMode);

initialized = true;
LOGGER.debug("ThemeListener initialized with window monitoring");
}

public static void setDarkMode(Stage stage, boolean darkMode) {
if (!SUPPORTS_DARK_MODE || stage == null || !stage.isShowing()) {
return;
}

THEME_WINDOW_MANAGER.setDarkModeForWindowFrame(stage, darkMode);
LOGGER.debug("Applied {} mode to window: {}", darkMode ? "dark" : "light", stage);
}

public static void setDarkMode(boolean darkMode) {
currentDarkMode = darkMode;
Window.getWindows().stream()
.filter(Window::isShowing)
.filter(window -> window instanceof Stage)
.map(window -> (Stage) window)
.forEach(stage -> setDarkMode(stage, darkMode));
}
}
5 changes: 5 additions & 0 deletions jabgui/src/main/java/org/jabref/gui/theme/ThemeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import javafx.scene.web.WebEngine;

import org.jabref.gui.WorkspacePreferences;
import org.jabref.gui.desktop.os.ThemeListener;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.UiTaskExecutor;
Expand Down Expand Up @@ -70,6 +71,8 @@ public ThemeManager(WorkspacePreferences workspacePreferences,
this.baseStyleSheet = StyleSheet.create(Theme.BASE_CSS).get();
this.theme = workspacePreferences.getTheme();

ThemeListener.initialize(this.theme.getName().equals(Theme.EMBEDDED_DARK_CSS));

// Watching base CSS only works in development and test scenarios, where the build system exposes the CSS as a
// file (e.g. for Gradle run task it will be in build/resources/main/org/jabref/gui/Base.css)
addStylesheetToWatchlist(this.baseStyleSheet, this::baseCssLiveUpdate);
Expand Down Expand Up @@ -103,6 +106,8 @@ private void updateThemeSettings() {
this.theme = newTheme;
LOGGER.info("Theme set to {} with base css {}", newTheme, baseStyleSheet);

ThemeListener.setDarkMode(newTheme.getName().equals(Theme.EMBEDDED_DARK_CSS));

this.theme.getAdditionalStylesheet().ifPresent(
styleSheet -> addStylesheetToWatchlist(styleSheet, this::additionalCssLiveUpdate));

Expand Down
Loading