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.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.jetbrains-annotations.version>22.0.0</lib.jetbrains-annotations.version>
|
||||
<test.assertj.version>3.21.0</test.assertj.version>
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
package atlantafx.sampler.page;
|
||||
|
||||
import atlantafx.base.controls.Popover;
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import atlantafx.sampler.theme.ThemeManager;
|
||||
@ -16,6 +17,7 @@ import net.datafaker.Faker;
|
||||
import org.kordamp.ikonli.Ikon;
|
||||
import org.kordamp.ikonli.feather.Feather;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
import org.kordamp.ikonli.material2.Material2OutlinedMZ;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@ -25,7 +27,9 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
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.FLAT;
|
||||
import static atlantafx.sampler.util.Containers.setScrollConstraints;
|
||||
|
||||
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_SAMPLE = Feather.LAYOUT;
|
||||
|
||||
protected Button quickConfigBtn;
|
||||
protected Popover quickConfigPopover;
|
||||
protected Button sourceCodeToggleBtn;
|
||||
protected StackPane codeViewerWrapper;
|
||||
protected CodeViewer codeViewer;
|
||||
@ -63,16 +69,21 @@ public abstract class AbstractPage extends BorderPane implements Page {
|
||||
codeViewerWrapper.getStyleClass().add("wrapper");
|
||||
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.getStyleClass().addAll(BUTTON_ICON);
|
||||
sourceCodeToggleBtn.setTooltip(new Tooltip("Source Code"));
|
||||
sourceCodeToggleBtn.getStyleClass().addAll(BUTTON_ICON, FLAT);
|
||||
sourceCodeToggleBtn.setTooltip(new Tooltip("Source code"));
|
||||
sourceCodeToggleBtn.setOnAction(e -> toggleSourceCode());
|
||||
|
||||
var header = new HBox();
|
||||
var header = new HBox(30);
|
||||
header.getStyleClass().add("header");
|
||||
header.setMinHeight(HEADER_HEIGHT);
|
||||
header.setAlignment(Pos.CENTER_LEFT);
|
||||
header.getChildren().setAll(titleLabel, new Spacer(), sourceCodeToggleBtn);
|
||||
header.getChildren().setAll(titleLabel, new Spacer(), quickConfigBtn, sourceCodeToggleBtn);
|
||||
|
||||
// == 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.
|
||||
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() {
|
||||
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(),
|
||||
colorPalette
|
||||
);
|
||||
quickConfigBtn.setVisible(false);
|
||||
quickConfigBtn.setManaged(false);
|
||||
sourceCodeToggleBtn.setVisible(false);
|
||||
sourceCodeToggleBtn.setManaged(false);
|
||||
}
|
||||
|
||||
private GridPane optionsGrid() {
|
||||
|
@ -306,6 +306,24 @@
|
||||
-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 {
|
||||
-fx-background-color: transparent;
|
||||
-fx-background-insets: 0;
|
||||
|
Loading…
Reference in New Issue
Block a user