Add dark mode support
This commit is contained in:
parent
e653e8fda1
commit
feed204b88
@ -36,6 +36,7 @@ public class Preferences {
|
||||
protected final BooleanProperty ignoreMouseTransparent = new SimpleBooleanProperty(false);
|
||||
protected final BooleanProperty enableEventLog = new SimpleBooleanProperty(false); // non-UI
|
||||
protected final IntegerProperty maxEventLogSize = new SimpleIntegerProperty(DEFAULT_EVENT_LOG_SIZE);
|
||||
protected final BooleanProperty darkMode = new SimpleBooleanProperty(false);
|
||||
|
||||
protected final HostServices hostServices;
|
||||
|
||||
@ -206,6 +207,21 @@ public class Preferences {
|
||||
maxEventLogSize.set(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates or deactivates dark mode for the dev tools UI.
|
||||
*/
|
||||
public BooleanProperty darkModeProperty() {
|
||||
return darkMode;
|
||||
}
|
||||
|
||||
public boolean getDarkMode() {
|
||||
return darkMode.get();
|
||||
}
|
||||
|
||||
public void setDarkMode(boolean darkMode) {
|
||||
this.darkMode.set(darkMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Preferences{" +
|
||||
@ -219,6 +235,7 @@ public class Preferences {
|
||||
", ignoreMouseTransparent=" + ignoreMouseTransparent +
|
||||
", enableEventLog=" + enableEventLog +
|
||||
", maxEventLogSize=" + maxEventLogSize +
|
||||
", darkMode=" + darkMode +
|
||||
", hostServices=" + hostServices +
|
||||
'}';
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import devtoolsfx.connector.ConnectorOptions;
|
||||
import devtoolsfx.connector.Env;
|
||||
import devtoolsfx.connector.HighlightOptions;
|
||||
import devtoolsfx.event.*;
|
||||
import devtoolsfx.gui.controls.Dialog;
|
||||
import devtoolsfx.gui.controls.TabLine;
|
||||
import devtoolsfx.gui.env.EnvironmentTab;
|
||||
import devtoolsfx.gui.eventlog.EventLogTab;
|
||||
@ -24,6 +25,7 @@ import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Window;
|
||||
import javafx.util.Duration;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
@ -42,6 +44,7 @@ public final class ToolPane extends BorderPane {
|
||||
|
||||
private static final Logger LOGGER = System.getLogger(ToolPane.class.getName());
|
||||
private static final PseudoClass ACTIVE = PseudoClass.getPseudoClass("active");
|
||||
private static final PseudoClass DARK_MODE = PseudoClass.getPseudoClass("dark");
|
||||
|
||||
// we can't use close() because we are not in FXThread
|
||||
private final Connector connector;
|
||||
@ -91,7 +94,8 @@ public final class ToolPane extends BorderPane {
|
||||
createLayout();
|
||||
initListeners();
|
||||
|
||||
getStylesheets().add(GUI.USER_AGENT_STYLESHEET);
|
||||
toggleDarkMode(preferences.getDarkMode());
|
||||
|
||||
tabLine.selectTab(InspectorTab.TAB_NAME);
|
||||
startListenToEvents(false);
|
||||
}
|
||||
@ -247,6 +251,11 @@ public final class ToolPane extends BorderPane {
|
||||
LOGGER.log(Level.WARNING, Formatters.exceptionToString(e));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserAgentStylesheet() {
|
||||
return GUI.USER_AGENT_STYLESHEET;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// UI construction //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -283,6 +292,8 @@ public final class ToolPane extends BorderPane {
|
||||
preferences.showBoundsInParentProperty().subscribe(refreshSelectionHandler);
|
||||
preferences.showBaselineProperty().subscribe(refreshSelectionHandler);
|
||||
|
||||
preferences.darkModeProperty().addListener((obs, old, val) -> toggleDarkMode(val));
|
||||
|
||||
tabLine.setOnTabSelect(tab -> {
|
||||
switch (tab) {
|
||||
case InspectorTab.TAB_NAME -> inspectorTab.toFront();
|
||||
@ -377,4 +388,11 @@ public final class ToolPane extends BorderPane {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleDarkMode(boolean darkMode) {
|
||||
pseudoClassStateChanged(DARK_MODE, darkMode);
|
||||
Window.getWindows().stream()
|
||||
.filter(w -> w instanceof Dialog<?>)
|
||||
.forEach(dialog -> ((Dialog<?>) dialog).toggleDarkMode(darkMode));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package devtoolsfx.gui.controls;
|
||||
|
||||
import devtoolsfx.gui.GUI;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Modality;
|
||||
@ -15,32 +16,42 @@ import java.util.Objects;
|
||||
@NullMarked
|
||||
public final class Dialog<P extends Parent> extends Stage {
|
||||
|
||||
private static final PseudoClass DARK_MODE = PseudoClass.getPseudoClass("dark");
|
||||
private static final double DEFAULT_WIDTH = 640;
|
||||
private static final double DEFAULT_HEIGHT = 480;
|
||||
|
||||
private final P root;
|
||||
|
||||
public Dialog(P root) {
|
||||
this(root, "", DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||
public Dialog(P root, boolean darkMode) {
|
||||
this(root, "", DEFAULT_WIDTH, DEFAULT_HEIGHT, darkMode);
|
||||
}
|
||||
|
||||
public Dialog(P root, String title, double width, double height) {
|
||||
public Dialog(P root,
|
||||
String title,
|
||||
double width,
|
||||
double height,
|
||||
boolean darkMode) {
|
||||
super();
|
||||
|
||||
this.root = Objects.requireNonNull(root, "parent node cannot be null");
|
||||
createLayout(root, title, width, height);
|
||||
createLayout(root, title, width, height, darkMode);
|
||||
}
|
||||
|
||||
public P getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
private void createLayout(P parent, String title, double width, double height) {
|
||||
public void toggleDarkMode(boolean darkMode) {
|
||||
getRoot().pseudoClassStateChanged(DARK_MODE, darkMode);
|
||||
}
|
||||
|
||||
private void createLayout(P parent, String title, double width, double height, boolean darkMode) {
|
||||
setTitle(title);
|
||||
initModality(Modality.NONE);
|
||||
|
||||
var scene = new Scene(parent);
|
||||
scene.getStylesheets().add(GUI.USER_AGENT_STYLESHEET);
|
||||
scene.setUserAgentStylesheet(GUI.USER_AGENT_STYLESHEET);
|
||||
toggleDarkMode(darkMode);
|
||||
|
||||
setWidth(width);
|
||||
setHeight(height);
|
||||
|
@ -52,11 +52,13 @@ public class EnvironmentTab extends VBox {
|
||||
);
|
||||
private @Nullable Dialog<TextView> textViewDialog = null;
|
||||
|
||||
private final ToolPane toolPane;
|
||||
private final Env env;
|
||||
|
||||
public EnvironmentTab(ToolPane toolPane) {
|
||||
super();
|
||||
|
||||
this.toolPane = toolPane;
|
||||
this.env = toolPane.getConnector().getEnv();
|
||||
|
||||
createLayout();
|
||||
@ -229,7 +231,13 @@ public class EnvironmentTab extends VBox {
|
||||
|
||||
private Dialog<TextView> getOrCreateTextViewDialog() {
|
||||
if (textViewDialog == null) {
|
||||
textViewDialog = new Dialog<>(new TextView(), "Details", 640, 480);
|
||||
textViewDialog = new Dialog<>(
|
||||
new TextView(),
|
||||
"Details",
|
||||
640,
|
||||
480,
|
||||
toolPane.getPreferences().getDarkMode()
|
||||
);
|
||||
}
|
||||
|
||||
return textViewDialog;
|
||||
|
@ -8,7 +8,6 @@ import devtoolsfx.gui.controls.TextView;
|
||||
import devtoolsfx.gui.util.Formatters;
|
||||
import devtoolsfx.gui.util.GUIHelpers;
|
||||
import javafx.css.PseudoClass;
|
||||
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
@ -231,7 +230,13 @@ public final class EventLogTab extends VBox {
|
||||
|
||||
private Dialog<TextView> getOrCreateTextViewDialog() {
|
||||
if (textViewDialog == null) {
|
||||
textViewDialog = new Dialog<>(new TextView(), "Log Entry", 640, 480);
|
||||
textViewDialog = new Dialog<>(
|
||||
new TextView(),
|
||||
"Log Entry",
|
||||
640,
|
||||
480,
|
||||
toolPane.getPreferences().getDarkMode()
|
||||
);
|
||||
}
|
||||
|
||||
return textViewDialog;
|
||||
|
@ -33,6 +33,7 @@ public class PreferencesTab extends VBox {
|
||||
private void createLayout() {
|
||||
getChildren().setAll(
|
||||
createSceneGraphGroup(),
|
||||
createAppearanceGroup(),
|
||||
createInspectionGroup(),
|
||||
createEventLogGroup()
|
||||
);
|
||||
@ -73,6 +74,17 @@ public class PreferencesTab extends VBox {
|
||||
return createPreferencesGroup("Scene Graph", content);
|
||||
}
|
||||
|
||||
private VBox createAppearanceGroup() {
|
||||
var darkModeToggle = new CheckBox("Dark Mode");
|
||||
darkModeToggle.selectedProperty().bindBidirectional(
|
||||
toolPane.getPreferences().darkModeProperty()
|
||||
);
|
||||
|
||||
var content = new FlowPane(darkModeToggle);
|
||||
|
||||
return createPreferencesGroup("Appearance", content);
|
||||
}
|
||||
|
||||
private VBox createInspectionGroup() {
|
||||
var layoutBoundsToggle = new CheckBox("Highlight layout bounds");
|
||||
layoutBoundsToggle.selectedProperty().bindBidirectional(
|
||||
|
@ -153,7 +153,13 @@ public final class StylesheetTab extends VBox {
|
||||
|
||||
private Dialog<TextView> getOrCreateTextViewDialog() {
|
||||
if (textViewDialog == null) {
|
||||
textViewDialog = new Dialog<>(new TextView(), "Source Code", 640, 480);
|
||||
textViewDialog = new Dialog<>(
|
||||
new TextView(),
|
||||
"Source Code",
|
||||
640,
|
||||
480,
|
||||
toolPane.getPreferences().getDarkMode()
|
||||
);
|
||||
}
|
||||
|
||||
return textViewDialog;
|
||||
|
@ -10,7 +10,7 @@
|
||||
-palette-color-accent: #0969da;
|
||||
-palette-color-danger: #cf222e;
|
||||
-palette-color-success: #1a7f37;
|
||||
-palette-color-warning: #fff8c5;
|
||||
-palette-text-highlight: #fff8c5;
|
||||
|
||||
/* base */
|
||||
-fx-background-color: -palette-color-bg;
|
||||
@ -38,7 +38,7 @@
|
||||
-color-input-border: -palette-color-border;
|
||||
-color-input-bg-focused: -palette-color-bg;
|
||||
-color-input-border-focused: -palette-color-accent;
|
||||
-color-input-bg-readonly: -palette-color-bg-hover;
|
||||
-color-input-bg-readonly: -palette-color-bg;
|
||||
-color-input-bg-highlight: -palette-color-neutral;
|
||||
-color-input-fg-highlight: -palette-color-fg;
|
||||
|
||||
@ -56,6 +56,20 @@
|
||||
-color-button-border-pressed: -palette-color-border;
|
||||
}
|
||||
|
||||
.root:dark {
|
||||
-palette-color-bg: #202124;
|
||||
-palette-color-bg-hover: #36373a;
|
||||
-palette-color-fg: #c9d1d9;
|
||||
-palette-color-fg-muted: #6e7681;
|
||||
-palette-color-border: #42474e;
|
||||
-palette-color-neutral: #3d3d3d;
|
||||
-palette-color-neutral-hover: #313131;
|
||||
-palette-color-accent: #58a6ff;
|
||||
-palette-color-danger: #f85149;
|
||||
-palette-color-success: #1a7f37;
|
||||
-palette-text-highlight: #bb800940;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* JavaFX Controls / Theme
|
||||
******************************************************************************/
|
||||
@ -608,7 +622,7 @@ Text {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
#scene-graph-tree .tree-cell:filtered .label {
|
||||
-fx-background-color: -palette-color-warning;
|
||||
-fx-background-color: -palette-text-highlight;
|
||||
}
|
||||
|
||||
#scene-graph-search-field {
|
||||
@ -816,6 +830,9 @@ Text {
|
||||
-fx-max-height: 0.9em;
|
||||
-fx-background-color: -palette-color-fg;
|
||||
}
|
||||
#stylesheet-tab .tree-table-row-cell > .tree-table-cell {
|
||||
-fx-alignment: BASELINE_LEFT;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Environment
|
||||
@ -912,11 +929,9 @@ Text {
|
||||
.text-view {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.text-view .text-area {
|
||||
-fx-font-family: monospaced;
|
||||
-fx-background-insets: 0;
|
||||
-fx-background-radius: 0;
|
||||
-fx-border: 0;
|
||||
}
|
||||
|
||||
.hyperlink:empty {
|
||||
|
Loading…
x
Reference in New Issue
Block a user