Add theme quick switching menu
This commit is contained in:
parent
69ba0ed7af
commit
13563c6118
2
pom.xml
2
pom.xml
@ -68,7 +68,7 @@
|
|||||||
|
|
||||||
<lib.commons-lang.version>3.12.0</lib.commons-lang.version>
|
<lib.commons-lang.version>3.12.0</lib.commons-lang.version>
|
||||||
<lib.cssfx.version>11.5.1</lib.cssfx.version>
|
<lib.cssfx.version>11.5.1</lib.cssfx.version>
|
||||||
<lib.ikonli.version>12.2.0</lib.ikonli.version>
|
<lib.ikonli.version>12.3.1</lib.ikonli.version>
|
||||||
<lib.datafaker.version>1.3.0</lib.datafaker.version>
|
<lib.datafaker.version>1.3.0</lib.datafaker.version>
|
||||||
<lib.jetbrains-annotations.version>22.0.0</lib.jetbrains-annotations.version>
|
<lib.jetbrains-annotations.version>22.0.0</lib.jetbrains-annotations.version>
|
||||||
<test.assertj.version>3.21.0</test.assertj.version>
|
<test.assertj.version>3.21.0</test.assertj.version>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
package atlantafx.sampler.page;
|
package atlantafx.sampler.page;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Popover;
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.theme.ThemeManager;
|
import atlantafx.sampler.theme.ThemeManager;
|
||||||
@ -16,6 +17,7 @@ import net.datafaker.Faker;
|
|||||||
import org.kordamp.ikonli.Ikon;
|
import org.kordamp.ikonli.Ikon;
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2OutlinedMZ;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -25,7 +27,9 @@ import java.util.function.Supplier;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static atlantafx.base.controls.Popover.ArrowLocation.TOP_CENTER;
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
||||||
|
import static atlantafx.base.theme.Styles.FLAT;
|
||||||
import static atlantafx.sampler.util.Containers.setScrollConstraints;
|
import static atlantafx.sampler.util.Containers.setScrollConstraints;
|
||||||
|
|
||||||
public abstract class AbstractPage extends BorderPane implements Page {
|
public abstract class AbstractPage extends BorderPane implements Page {
|
||||||
@ -38,6 +42,8 @@ public abstract class AbstractPage extends BorderPane implements Page {
|
|||||||
private static final Ikon ICON_CODE = Feather.CODE;
|
private static final Ikon ICON_CODE = Feather.CODE;
|
||||||
private static final Ikon ICON_SAMPLE = Feather.LAYOUT;
|
private static final Ikon ICON_SAMPLE = Feather.LAYOUT;
|
||||||
|
|
||||||
|
protected Button quickConfigBtn;
|
||||||
|
protected Popover quickConfigPopover;
|
||||||
protected Button sourceCodeToggleBtn;
|
protected Button sourceCodeToggleBtn;
|
||||||
protected StackPane codeViewerWrapper;
|
protected StackPane codeViewerWrapper;
|
||||||
protected CodeViewer codeViewer;
|
protected CodeViewer codeViewer;
|
||||||
@ -63,16 +69,21 @@ public abstract class AbstractPage extends BorderPane implements Page {
|
|||||||
codeViewerWrapper.getStyleClass().add("wrapper");
|
codeViewerWrapper.getStyleClass().add("wrapper");
|
||||||
codeViewerWrapper.getChildren().setAll(codeViewer);
|
codeViewerWrapper.getChildren().setAll(codeViewer);
|
||||||
|
|
||||||
|
quickConfigBtn = new Button("", new FontIcon(Material2OutlinedMZ.STYLE));
|
||||||
|
quickConfigBtn.getStyleClass().addAll(BUTTON_ICON, FLAT);
|
||||||
|
quickConfigBtn.setTooltip(new Tooltip("Change theme"));
|
||||||
|
quickConfigBtn.setOnAction(e -> showThemeConfigPopover());
|
||||||
|
|
||||||
sourceCodeToggleBtn = new Button("", new FontIcon(ICON_CODE));
|
sourceCodeToggleBtn = new Button("", new FontIcon(ICON_CODE));
|
||||||
sourceCodeToggleBtn.getStyleClass().addAll(BUTTON_ICON);
|
sourceCodeToggleBtn.getStyleClass().addAll(BUTTON_ICON, FLAT);
|
||||||
sourceCodeToggleBtn.setTooltip(new Tooltip("Source Code"));
|
sourceCodeToggleBtn.setTooltip(new Tooltip("Source code"));
|
||||||
sourceCodeToggleBtn.setOnAction(e -> toggleSourceCode());
|
sourceCodeToggleBtn.setOnAction(e -> toggleSourceCode());
|
||||||
|
|
||||||
var header = new HBox();
|
var header = new HBox(30);
|
||||||
header.getStyleClass().add("header");
|
header.getStyleClass().add("header");
|
||||||
header.setMinHeight(HEADER_HEIGHT);
|
header.setMinHeight(HEADER_HEIGHT);
|
||||||
header.setAlignment(Pos.CENTER_LEFT);
|
header.setAlignment(Pos.CENTER_LEFT);
|
||||||
header.getChildren().setAll(titleLabel, new Spacer(), sourceCodeToggleBtn);
|
header.getChildren().setAll(titleLabel, new Spacer(), quickConfigBtn, sourceCodeToggleBtn);
|
||||||
|
|
||||||
// == user content ==
|
// == user content ==
|
||||||
|
|
||||||
@ -120,6 +131,20 @@ public abstract class AbstractPage extends BorderPane implements Page {
|
|||||||
// to the scene graph and here is the place do this.
|
// to the scene graph and here is the place do this.
|
||||||
protected void onRendered() { }
|
protected void onRendered() { }
|
||||||
|
|
||||||
|
private void showThemeConfigPopover() {
|
||||||
|
if (quickConfigPopover == null) {
|
||||||
|
var content = new QuickConfigMenu();
|
||||||
|
content.setExitHandler(() -> quickConfigPopover.hide());
|
||||||
|
|
||||||
|
quickConfigPopover = new Popover(content);
|
||||||
|
quickConfigPopover.setHeaderAlwaysVisible(false);
|
||||||
|
quickConfigPopover.setDetachable(false);
|
||||||
|
quickConfigPopover.setArrowLocation(TOP_CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
quickConfigPopover.show(quickConfigBtn);
|
||||||
|
}
|
||||||
|
|
||||||
protected void toggleSourceCode() {
|
protected void toggleSourceCode() {
|
||||||
var graphic = (FontIcon) sourceCodeToggleBtn.getGraphic();
|
var graphic = (FontIcon) sourceCodeToggleBtn.getGraphic();
|
||||||
|
|
||||||
|
@ -0,0 +1,172 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
package atlantafx.sampler.page;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Spacer;
|
||||||
|
import atlantafx.sampler.theme.ThemeManager;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
|
import javafx.geometry.HorizontalDirection;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Separator;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2AL;
|
||||||
|
import org.kordamp.ikonli.material2.Material2MZ;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static atlantafx.base.theme.Styles.*;
|
||||||
|
import static javafx.geometry.Pos.CENTER_LEFT;
|
||||||
|
import static org.kordamp.ikonli.material2.Material2AL.ARROW_BACK;
|
||||||
|
import static org.kordamp.ikonli.material2.Material2AL.ARROW_FORWARD;
|
||||||
|
|
||||||
|
public class QuickConfigMenu extends StackPane {
|
||||||
|
|
||||||
|
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
||||||
|
private static final String EXIT_ID = "Exit";
|
||||||
|
|
||||||
|
private MainMenu mainMenu;
|
||||||
|
private ThemeSelectionMenu themeSelectionMenu;
|
||||||
|
private Runnable exitHandler;
|
||||||
|
|
||||||
|
private final Consumer<String> navHandler = s -> {
|
||||||
|
Pane pane = null;
|
||||||
|
switch (s) {
|
||||||
|
case MainMenu.ID -> pane = getOrCreateMainMenu();
|
||||||
|
case ThemeSelectionMenu.ID -> {
|
||||||
|
ThemeSelectionMenu menu = getOrCreateThemeSelectionMenu();
|
||||||
|
menu.update();
|
||||||
|
pane = menu;
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
if (exitHandler != null) {
|
||||||
|
exitHandler.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getChildren().setAll(Objects.requireNonNull(pane));
|
||||||
|
};
|
||||||
|
|
||||||
|
public void setExitHandler(Runnable exitHandler) {
|
||||||
|
this.exitHandler = exitHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuickConfigMenu() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
getChildren().setAll(getOrCreateMainMenu());
|
||||||
|
setId("quick-config-menu");
|
||||||
|
}
|
||||||
|
|
||||||
|
private MainMenu getOrCreateMainMenu() {
|
||||||
|
if (mainMenu == null) { mainMenu = new MainMenu(navHandler); }
|
||||||
|
return mainMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ThemeSelectionMenu getOrCreateThemeSelectionMenu() {
|
||||||
|
if (themeSelectionMenu == null) { themeSelectionMenu = new ThemeSelectionMenu(navHandler); }
|
||||||
|
return themeSelectionMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pane menu(String title, HorizontalDirection direction) {
|
||||||
|
var label = new Label(title);
|
||||||
|
label.getStyleClass().add(TEXT_CAPTION);
|
||||||
|
|
||||||
|
var root = new HBox();
|
||||||
|
root.setAlignment(CENTER_LEFT);
|
||||||
|
root.getStyleClass().addAll("row", "action");
|
||||||
|
|
||||||
|
switch (direction) {
|
||||||
|
case LEFT -> root.getChildren().setAll(new FontIcon(ARROW_BACK), new Spacer(), label, new Spacer());
|
||||||
|
case RIGHT -> root.getChildren().setAll(label, new Spacer(), new FontIcon(ARROW_FORWARD));
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private static class MainMenu extends VBox {
|
||||||
|
|
||||||
|
private static final String ID = "MainMenu";
|
||||||
|
|
||||||
|
public MainMenu(Consumer<String> navHandler) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Objects.requireNonNull(navHandler);
|
||||||
|
|
||||||
|
var themeSelectionMenu = menu("Theme", HorizontalDirection.RIGHT);
|
||||||
|
themeSelectionMenu.setOnMouseClicked(e -> navHandler.accept(ThemeSelectionMenu.ID));
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
var zoomInBtn = new Button("", new FontIcon(Material2MZ.MINUS));
|
||||||
|
zoomInBtn.getStyleClass().addAll(BUTTON_CIRCLE, BUTTON_ICON, FLAT);
|
||||||
|
|
||||||
|
var zoomOutBtn = new Button("", new FontIcon(Material2MZ.PLUS));
|
||||||
|
zoomOutBtn.getStyleClass().addAll(BUTTON_CIRCLE, BUTTON_ICON, FLAT);
|
||||||
|
|
||||||
|
var zoomLabel = new Label("100%");
|
||||||
|
|
||||||
|
var zoomBox = new HBox(zoomInBtn, new Spacer(), zoomLabel, new Spacer(), zoomOutBtn);
|
||||||
|
zoomBox.setAlignment(CENTER_LEFT);
|
||||||
|
zoomBox.getStyleClass().addAll("row");
|
||||||
|
zoomBox.setDisable(true); // not yet implemented
|
||||||
|
|
||||||
|
// !
|
||||||
|
|
||||||
|
getChildren().setAll(themeSelectionMenu, new Separator(), zoomBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ThemeSelectionMenu extends VBox {
|
||||||
|
|
||||||
|
public static final String ID = "ThemeSelectionMenu";
|
||||||
|
|
||||||
|
private final List<Node> items = new ArrayList<>();
|
||||||
|
|
||||||
|
public ThemeSelectionMenu(Consumer<String> navHandler) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Objects.requireNonNull(navHandler);
|
||||||
|
var tm = ThemeManager.getInstance();
|
||||||
|
|
||||||
|
var mainMenu = menu("Theme", HorizontalDirection.LEFT);
|
||||||
|
mainMenu.setOnMouseClicked(e -> navHandler.accept(MainMenu.ID));
|
||||||
|
|
||||||
|
getChildren().setAll(mainMenu, new Separator());
|
||||||
|
|
||||||
|
tm.getAvailableThemes().forEach(theme -> {
|
||||||
|
var icon = new FontIcon(Material2AL.CHECK);
|
||||||
|
|
||||||
|
var item = new HBox(20, icon, new Label(theme.getName()));
|
||||||
|
item.getStyleClass().addAll("row", "action", "radio");
|
||||||
|
item.setUserData(theme.getName());
|
||||||
|
item.setOnMouseClicked(e -> {
|
||||||
|
tm.setTheme(theme);
|
||||||
|
tm.reloadCustomCSS();
|
||||||
|
navHandler.accept(MainMenu.ID);
|
||||||
|
navHandler.accept(QuickConfigMenu.EXIT_ID);
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add(item);
|
||||||
|
getChildren().add(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
items.forEach(item -> item.pseudoClassStateChanged(
|
||||||
|
SELECTED,
|
||||||
|
Objects.equals(item.getUserData(), ThemeManager.getInstance().getTheme().getName())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,10 @@ public class ThemePage extends AbstractPage {
|
|||||||
optionsGrid(),
|
optionsGrid(),
|
||||||
colorPalette
|
colorPalette
|
||||||
);
|
);
|
||||||
|
quickConfigBtn.setVisible(false);
|
||||||
|
quickConfigBtn.setManaged(false);
|
||||||
sourceCodeToggleBtn.setVisible(false);
|
sourceCodeToggleBtn.setVisible(false);
|
||||||
|
sourceCodeToggleBtn.setManaged(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GridPane optionsGrid() {
|
private GridPane optionsGrid() {
|
||||||
|
@ -306,6 +306,24 @@
|
|||||||
-fx-background-color: #388e3c;
|
-fx-background-color: #388e3c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#quick-config-menu {
|
||||||
|
-fx-min-width: 200px;
|
||||||
|
}
|
||||||
|
#quick-config-menu .row {
|
||||||
|
-fx-padding: 6px;
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
}
|
||||||
|
#quick-config-menu .action:hover {
|
||||||
|
/* FIXME: Not visible in some themes (subtle over subtle) */
|
||||||
|
-fx-background-color: -color-bg-subtle;
|
||||||
|
}
|
||||||
|
#quick-config-menu .radio > .ikonli-font-icon {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
#quick-config-menu .radio:selected > .ikonli-font-icon {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
.popover .date-picker-popup {
|
.popover .date-picker-popup {
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
-fx-background-insets: 0;
|
-fx-background-insets: 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user