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 all 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 dark title bar on Windows. [#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.6.0")

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
}
57 changes: 57 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 @@ -13,9 +13,12 @@

import javafx.application.ColorScheme;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;
import javafx.stage.Window;

import org.jabref.gui.WorkspacePreferences;
import org.jabref.gui.icon.IconTheme;
Expand All @@ -26,6 +29,8 @@
import org.jabref.model.util.FileUpdateMonitor;

import com.google.common.annotations.VisibleForTesting;
import com.pixelduke.window.ThemeWindowManager;
import com.pixelduke.window.ThemeWindowManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -53,9 +58,11 @@ public class ThemeManager {
private final WorkspacePreferences workspacePreferences;
private final FileUpdateMonitor fileUpdateMonitor;
private final Consumer<Runnable> updateRunner;
private final ThemeWindowManager themeWindowManager;

private final StyleSheet baseStyleSheet;
private Theme theme;
private boolean isDarkMode;

private Scene mainWindowScene;
private final Set<WebEngine> webEngines = Collections.newSetFromMap(new WeakHashMap<>());
Expand All @@ -66,9 +73,13 @@ public ThemeManager(WorkspacePreferences workspacePreferences,
this.workspacePreferences = Objects.requireNonNull(workspacePreferences);
this.fileUpdateMonitor = Objects.requireNonNull(fileUpdateMonitor);
this.updateRunner = Objects.requireNonNull(updateRunner);
this.themeWindowManager = ThemeWindowManagerFactory.create();

this.baseStyleSheet = StyleSheet.create(Theme.BASE_CSS).get();
this.theme = workspacePreferences.getTheme();
this.isDarkMode = Theme.EMBEDDED_DARK_CSS.equals(this.theme.getName());

initializeWindowThemeUpdater(this.isDarkMode);

// 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)
Expand All @@ -83,6 +94,46 @@ public ThemeManager(WorkspacePreferences workspacePreferences,
updateThemeSettings();
}

private void initializeWindowThemeUpdater(boolean darkMode) {
this.isDarkMode = 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 -> stage.showingProperty()
.addListener(_ -> applyDarkModeToWindow(stage, isDarkMode)));
}
};

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

LOGGER.debug("Window theme monitoring initialized");
}

private void applyDarkModeToWindow(Stage stage, boolean darkMode) {
if (stage == null || !stage.isShowing()) {
return;
}

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

private void applyDarkModeToAllWindows(boolean darkMode) {
this.isDarkMode = darkMode;
Window.getWindows().stream()
.filter(Window::isShowing)
.filter(window -> window instanceof Stage)
.map(window -> (Stage) window)
.forEach(stage -> applyDarkModeToWindow(stage, darkMode));
}

private void updateThemeSettings() {
Theme newTheme = Objects.requireNonNull(workspacePreferences.getTheme());

Expand All @@ -103,6 +154,12 @@ private void updateThemeSettings() {
this.theme = newTheme;
LOGGER.info("Theme set to {} with base css {}", newTheme, baseStyleSheet);

boolean isDarkTheme = Theme.EMBEDDED_DARK_CSS.equals(newTheme.getName());
if (this.isDarkMode != isDarkTheme) {
this.isDarkMode = isDarkTheme;
applyDarkModeToAllWindows(isDarkTheme);
}

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

Expand Down
Loading