Rewrite all Sampler pages to better UI design
This commit is contained in:
parent
7ebfa7ac0e
commit
35d1107b3d
@ -184,8 +184,9 @@ public class ModalPane extends Control {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether content should be treated as persistent or not.
|
* Specifies whether content should be treated as persistent or not.
|
||||||
* By default, modal pane exits when on ESC button or mouse click outside the contenbt are.
|
* By default, the modal pane exits when the ESC button is pressed or when
|
||||||
* This property prevents this behavior and plays bouncing animation instead.
|
* the mouse is clicked outside the content area. This property prevents
|
||||||
|
* this behavior and plays bouncing animation instead.
|
||||||
*/
|
*/
|
||||||
public BooleanProperty persistentProperty() {
|
public BooleanProperty persistentProperty() {
|
||||||
return persistent;
|
return persistent;
|
||||||
|
@ -141,4 +141,4 @@ public class PasswordTextFormatterTest {
|
|||||||
assertEquals("+".repeat(5), field.getText());
|
assertEquals("+".repeat(5), field.getText());
|
||||||
assertEquals("123++", fmt.getPassword());
|
assertEquals("123++", fmt.getPassword());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
pom.xml
5
pom.xml
@ -94,6 +94,11 @@
|
|||||||
<artifactId>javafx-controls</artifactId>
|
<artifactId>javafx-controls</artifactId>
|
||||||
<version>${openjfx.version}</version>
|
<version>${openjfx.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-fxml</artifactId>
|
||||||
|
<version>${openjfx.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-swing</artifactId>
|
<artifactId>javafx-swing</artifactId>
|
||||||
|
@ -43,6 +43,10 @@
|
|||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-controls</artifactId>
|
<artifactId>javafx-controls</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-fxml</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-swing</artifactId>
|
<artifactId>javafx-swing</artifactId>
|
||||||
@ -162,7 +166,7 @@
|
|||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>validate</id>
|
<id>validate</id>
|
||||||
<phase>validate</phase>
|
<phase>verify</phase>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>check</goal>
|
<goal>check</goal>
|
||||||
</goals>
|
</goals>
|
||||||
@ -243,7 +247,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<toolName>jlink</toolName>
|
<toolName>jlink</toolName>
|
||||||
<addModules>
|
<addModules>
|
||||||
java.base,java.logging,jdk.localedata,java.desktop,java.prefs,javafx.controls,javafx.swing,javafx.web
|
java.base,java.logging,jdk.localedata,java.desktop,java.prefs,javafx.controls,javafx.fxml,javafx.swing,javafx.web
|
||||||
</addModules>
|
</addModules>
|
||||||
<modulePath>${build.platformModulesDir}</modulePath>
|
<modulePath>${build.platformModulesDir}</modulePath>
|
||||||
<output>${build.package.runtimeImageDir}</output>
|
<output>${build.package.runtimeImageDir}</output>
|
||||||
|
@ -59,7 +59,7 @@ public class Launcher extends Application {
|
|||||||
var antialiasing = Platform.isSupported(ConditionalFeature.SCENE3D)
|
var antialiasing = Platform.isSupported(ConditionalFeature.SCENE3D)
|
||||||
? SceneAntialiasing.BALANCED
|
? SceneAntialiasing.BALANCED
|
||||||
: SceneAntialiasing.DISABLED;
|
: SceneAntialiasing.DISABLED;
|
||||||
var scene = new Scene(root, 1200, 768, false, antialiasing);
|
var scene = new Scene(root, ApplicationWindow.MIN_WIDTH + 80, 768, false, antialiasing);
|
||||||
scene.setOnKeyPressed(this::dispatchHotkeys);
|
scene.setOnKeyPressed(this::dispatchHotkeys);
|
||||||
|
|
||||||
var tm = ThemeManager.getInstance();
|
var tm = ThemeManager.getInstance();
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.fake;
|
|
||||||
|
|
||||||
import static atlantafx.sampler.util.Controls.menuItem;
|
|
||||||
import static javafx.scene.input.KeyCombination.CONTROL_DOWN;
|
|
||||||
import static javafx.scene.input.KeyCombination.SHIFT_DOWN;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.CaptionMenuItem;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.scene.control.CheckMenuItem;
|
|
||||||
import javafx.scene.control.Menu;
|
|
||||||
import javafx.scene.control.MenuBar;
|
|
||||||
import javafx.scene.control.MenuItem;
|
|
||||||
import javafx.scene.control.RadioMenuItem;
|
|
||||||
import javafx.scene.control.SeparatorMenuItem;
|
|
||||||
import javafx.scene.control.ToggleGroup;
|
|
||||||
import javafx.scene.input.KeyCode;
|
|
||||||
import javafx.scene.input.KeyCodeCombination;
|
|
||||||
import net.datafaker.Faker;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
|
||||||
|
|
||||||
public class SampleMenuBar extends MenuBar {
|
|
||||||
|
|
||||||
private static final EventHandler<ActionEvent> PRINT_SOURCE = System.out::println;
|
|
||||||
|
|
||||||
public SampleMenuBar(Faker faker) {
|
|
||||||
getMenus().addAll(
|
|
||||||
fileMenu(faker),
|
|
||||||
editMenu(),
|
|
||||||
viewMenu(),
|
|
||||||
toolsMenu(),
|
|
||||||
aboutMenu()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Menu fileMenu(Faker faker) {
|
|
||||||
var fileMenu = new Menu("_File");
|
|
||||||
fileMenu.setMnemonicParsing(true);
|
|
||||||
fileMenu.setOnAction(PRINT_SOURCE);
|
|
||||||
|
|
||||||
var newMenu = menuItem("_New", null, new KeyCodeCombination(KeyCode.N, CONTROL_DOWN));
|
|
||||||
newMenu.setMnemonicParsing(true);
|
|
||||||
newMenu.setOnAction(PRINT_SOURCE);
|
|
||||||
|
|
||||||
var openRecentMenu = new Menu("Open _Recent");
|
|
||||||
openRecentMenu.setMnemonicParsing(true);
|
|
||||||
openRecentMenu.setOnAction(PRINT_SOURCE);
|
|
||||||
openRecentMenu.getItems().addAll(
|
|
||||||
IntStream.range(0, 10).mapToObj(x -> new MenuItem(faker.file().fileName())).toList()
|
|
||||||
);
|
|
||||||
|
|
||||||
fileMenu.getItems().addAll(
|
|
||||||
newMenu,
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
menuItem("Open", Feather.FOLDER, new KeyCodeCombination(KeyCode.O, CONTROL_DOWN)),
|
|
||||||
openRecentMenu,
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
menuItem("Save", Feather.SAVE, new KeyCodeCombination(KeyCode.S, CONTROL_DOWN)),
|
|
||||||
new MenuItem("Save As"),
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
new MenuItem("Exit")
|
|
||||||
);
|
|
||||||
return fileMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Menu editMenu() {
|
|
||||||
var editMenu = new Menu("_Edit");
|
|
||||||
editMenu.setMnemonicParsing(true);
|
|
||||||
editMenu.setOnAction(PRINT_SOURCE);
|
|
||||||
|
|
||||||
editMenu.getItems().addAll(
|
|
||||||
menuItem("Undo", Feather.CORNER_DOWN_LEFT, new KeyCodeCombination(KeyCode.Z, CONTROL_DOWN)),
|
|
||||||
menuItem("Redo", Feather.CORNER_DOWN_RIGHT, new KeyCodeCombination(KeyCode.Y, CONTROL_DOWN)),
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
menuItem("Cut", Feather.SCISSORS, new KeyCodeCombination(KeyCode.X, CONTROL_DOWN)),
|
|
||||||
menuItem("Copy", Feather.COPY, new KeyCodeCombination(KeyCode.C, CONTROL_DOWN), true),
|
|
||||||
menuItem("Paste", Feather.CORNER_DOWN_LEFT, new KeyCodeCombination(KeyCode.V, CONTROL_DOWN))
|
|
||||||
);
|
|
||||||
return editMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Menu viewMenu() {
|
|
||||||
var viewMenu = new Menu("_View");
|
|
||||||
viewMenu.setMnemonicParsing(true);
|
|
||||||
viewMenu.setOnAction(PRINT_SOURCE);
|
|
||||||
|
|
||||||
var showToolbarItem = new CheckMenuItem("Show Toolbar", new FontIcon(Feather.TOOL));
|
|
||||||
showToolbarItem.setSelected(true);
|
|
||||||
showToolbarItem.setAccelerator(new KeyCodeCombination(KeyCode.T, CONTROL_DOWN));
|
|
||||||
|
|
||||||
var showGridItem = new CheckMenuItem("Show Grid", new FontIcon(Feather.GRID));
|
|
||||||
|
|
||||||
var captionItem = new CaptionMenuItem("Layout");
|
|
||||||
|
|
||||||
var viewToggleGroup = new ToggleGroup();
|
|
||||||
|
|
||||||
var toggleItem1 = new RadioMenuItem("Single", new FontIcon(Material2OutlinedAL.LOOKS_ONE));
|
|
||||||
toggleItem1.setSelected(true);
|
|
||||||
toggleItem1.setToggleGroup(viewToggleGroup);
|
|
||||||
|
|
||||||
var toggleItem2 = new RadioMenuItem("Two Columns", new FontIcon(Material2OutlinedAL.LOOKS_TWO));
|
|
||||||
toggleItem2.setToggleGroup(viewToggleGroup);
|
|
||||||
|
|
||||||
var toggleItem3 = new RadioMenuItem("Three Columns", new FontIcon(Material2OutlinedAL.LOOKS_3));
|
|
||||||
toggleItem3.setToggleGroup(viewToggleGroup);
|
|
||||||
|
|
||||||
viewMenu.getItems().addAll(
|
|
||||||
showToolbarItem,
|
|
||||||
showGridItem,
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
captionItem,
|
|
||||||
toggleItem1,
|
|
||||||
toggleItem2,
|
|
||||||
toggleItem3
|
|
||||||
);
|
|
||||||
return viewMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Menu toolsMenu() {
|
|
||||||
var toolsMenu = new Menu("_Tools");
|
|
||||||
toolsMenu.setMnemonicParsing(true);
|
|
||||||
toolsMenu.setOnAction(PRINT_SOURCE);
|
|
||||||
toolsMenu.setDisable(true);
|
|
||||||
return toolsMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Menu aboutMenu() {
|
|
||||||
var aboutMenu = new Menu("_About", new FontIcon(Feather.HELP_CIRCLE));
|
|
||||||
aboutMenu.setMnemonicParsing(true);
|
|
||||||
aboutMenu.setOnAction(PRINT_SOURCE);
|
|
||||||
|
|
||||||
var deeplyNestedMenu = new Menu("Very...", null,
|
|
||||||
new Menu("Very...", null,
|
|
||||||
new Menu("Deeply", null,
|
|
||||||
new Menu("Nested", null,
|
|
||||||
new MenuItem("Menu")
|
|
||||||
))));
|
|
||||||
// NOTE: this won't be displayed because right container is reserved for submenu indication
|
|
||||||
deeplyNestedMenu.setAccelerator(new KeyCodeCombination(
|
|
||||||
KeyCode.DIGIT1, SHIFT_DOWN, CONTROL_DOWN)
|
|
||||||
);
|
|
||||||
|
|
||||||
aboutMenu.getItems().addAll(
|
|
||||||
new MenuItem("Help"),
|
|
||||||
new MenuItem("About"),
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
deeplyNestedMenu
|
|
||||||
);
|
|
||||||
return aboutMenu;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,61 @@
|
|||||||
|
package atlantafx.sampler.fake.domain;
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
|
||||||
|
public final class Metric {
|
||||||
|
|
||||||
|
private final SimpleStringProperty queries = new SimpleStringProperty("");
|
||||||
|
private final SimpleStringProperty cacheHitRate = new SimpleStringProperty("");
|
||||||
|
private final SimpleStringProperty latency = new SimpleStringProperty("");
|
||||||
|
private final SimpleStringProperty requests = new SimpleStringProperty("");
|
||||||
|
|
||||||
|
public Metric() { }
|
||||||
|
|
||||||
|
public String getQueries() {
|
||||||
|
return queries.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleStringProperty queriesProperty() {
|
||||||
|
return queries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQueries(String queries) {
|
||||||
|
this.queries.set(queries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCacheHitRate() {
|
||||||
|
return cacheHitRate.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleStringProperty cacheHitRateProperty() {
|
||||||
|
return cacheHitRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCacheHitRate(String cacheHitRate) {
|
||||||
|
this.cacheHitRate.set(cacheHitRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLatency() {
|
||||||
|
return latency.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleStringProperty latencyProperty() {
|
||||||
|
return latency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLatency(String latency) {
|
||||||
|
this.latency.set(latency);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequests() {
|
||||||
|
return requests.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleStringProperty requestsProperty() {
|
||||||
|
return requests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequests(String requests) {
|
||||||
|
this.requests.set(requests);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,9 @@ import javafx.scene.layout.StackPane;
|
|||||||
|
|
||||||
public class ApplicationWindow extends AnchorPane {
|
public class ApplicationWindow extends AnchorPane {
|
||||||
|
|
||||||
|
public static final int MIN_WIDTH = 1200;
|
||||||
|
public static final int SIDEBAR_WIDTH = 250;
|
||||||
|
|
||||||
public ApplicationWindow() {
|
public ApplicationWindow() {
|
||||||
// this is the place to apply user custom CSS,
|
// this is the place to apply user custom CSS,
|
||||||
// one level below the ':root'
|
// one level below the ':root'
|
||||||
|
@ -6,7 +6,6 @@ import static atlantafx.base.theme.Styles.TEXT_SMALL;
|
|||||||
import static atlantafx.base.theme.Styles.TITLE_3;
|
import static atlantafx.base.theme.Styles.TITLE_3;
|
||||||
import static atlantafx.base.theme.Styles.TITLE_4;
|
import static atlantafx.base.theme.Styles.TITLE_4;
|
||||||
import static atlantafx.sampler.Launcher.IS_DEV_MODE;
|
import static atlantafx.sampler.Launcher.IS_DEV_MODE;
|
||||||
import static atlantafx.sampler.layout.MainLayer.SIDEBAR_WIDTH;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.sampler.Resources;
|
import atlantafx.sampler.Resources;
|
||||||
@ -80,9 +79,9 @@ class HeaderBar extends HBox {
|
|||||||
var logoBox = new HBox(10, logoImageBox, logoLabel, versionLabel);
|
var logoBox = new HBox(10, logoImageBox, logoLabel, versionLabel);
|
||||||
logoBox.getStyleClass().add("logo");
|
logoBox.getStyleClass().add("logo");
|
||||||
logoBox.setAlignment(Pos.CENTER_LEFT);
|
logoBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
logoBox.setMinWidth(SIDEBAR_WIDTH);
|
logoBox.setMinWidth(ApplicationWindow.SIDEBAR_WIDTH);
|
||||||
logoBox.setPrefWidth(SIDEBAR_WIDTH);
|
logoBox.setPrefWidth(ApplicationWindow.SIDEBAR_WIDTH);
|
||||||
logoBox.setMaxWidth(SIDEBAR_WIDTH);
|
logoBox.setMaxWidth(ApplicationWindow.SIDEBAR_WIDTH);
|
||||||
|
|
||||||
var titleLabel = new Label();
|
var titleLabel = new Label();
|
||||||
titleLabel.getStyleClass().addAll("page-title", TITLE_4);
|
titleLabel.getStyleClass().addAll("page-title", TITLE_4);
|
||||||
|
@ -26,7 +26,6 @@ import javafx.util.Duration;
|
|||||||
|
|
||||||
class MainLayer extends BorderPane {
|
class MainLayer extends BorderPane {
|
||||||
|
|
||||||
static final int SIDEBAR_WIDTH = 250;
|
|
||||||
static final int PAGE_TRANSITION_DURATION = 500; // ms
|
static final int PAGE_TRANSITION_DURATION = 500; // ms
|
||||||
|
|
||||||
private final MainModel model = new MainModel();
|
private final MainModel model = new MainModel();
|
||||||
@ -51,8 +50,8 @@ class MainLayer extends BorderPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private void createView() {
|
||||||
sidebar.setMinWidth(SIDEBAR_WIDTH);
|
sidebar.setMinWidth(ApplicationWindow.SIDEBAR_WIDTH);
|
||||||
sidebar.setMaxWidth(SIDEBAR_WIDTH);
|
sidebar.setMaxWidth(ApplicationWindow.SIDEBAR_WIDTH);
|
||||||
|
|
||||||
codeViewer = new CodeViewer();
|
codeViewer = new CodeViewer();
|
||||||
|
|
||||||
|
@ -7,27 +7,22 @@ import static atlantafx.sampler.layout.MainModel.SubLayer.SOURCE_CODE;
|
|||||||
|
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.Page;
|
||||||
import atlantafx.sampler.page.components.AccordionPage;
|
import atlantafx.sampler.page.components.AccordionPage;
|
||||||
import atlantafx.sampler.page.components.BBCodePage;
|
|
||||||
import atlantafx.sampler.page.components.BreadcrumbsPage;
|
|
||||||
import atlantafx.sampler.page.components.ButtonPage;
|
import atlantafx.sampler.page.components.ButtonPage;
|
||||||
import atlantafx.sampler.page.components.ChartPage;
|
import atlantafx.sampler.page.components.ChartPage;
|
||||||
import atlantafx.sampler.page.components.CheckBoxPage;
|
import atlantafx.sampler.page.components.CheckBoxPage;
|
||||||
|
import atlantafx.sampler.page.components.ChoiceBoxPage;
|
||||||
import atlantafx.sampler.page.components.ColorPickerPage;
|
import atlantafx.sampler.page.components.ColorPickerPage;
|
||||||
import atlantafx.sampler.page.components.ComboBoxPage;
|
import atlantafx.sampler.page.components.ComboBoxPage;
|
||||||
import atlantafx.sampler.page.components.CustomTextFieldPage;
|
import atlantafx.sampler.page.components.ContextMenuPage;
|
||||||
import atlantafx.sampler.page.components.DatePickerPage;
|
import atlantafx.sampler.page.components.DatePickerPage;
|
||||||
import atlantafx.sampler.page.components.DialogPage;
|
import atlantafx.sampler.page.components.DialogPage;
|
||||||
import atlantafx.sampler.page.components.HtmlEditorPage;
|
import atlantafx.sampler.page.components.HtmlEditorPage;
|
||||||
import atlantafx.sampler.page.components.InputGroupPage;
|
|
||||||
import atlantafx.sampler.page.components.LabelPage;
|
import atlantafx.sampler.page.components.LabelPage;
|
||||||
import atlantafx.sampler.page.components.ListPage;
|
import atlantafx.sampler.page.components.ListViewPage;
|
||||||
|
import atlantafx.sampler.page.components.MenuBarPage;
|
||||||
import atlantafx.sampler.page.components.MenuButtonPage;
|
import atlantafx.sampler.page.components.MenuButtonPage;
|
||||||
import atlantafx.sampler.page.components.MenuPage;
|
|
||||||
import atlantafx.sampler.page.components.ModalPanePage;
|
|
||||||
import atlantafx.sampler.page.components.OverviewPage;
|
|
||||||
import atlantafx.sampler.page.components.PaginationPage;
|
import atlantafx.sampler.page.components.PaginationPage;
|
||||||
import atlantafx.sampler.page.components.PopoverPage;
|
import atlantafx.sampler.page.components.ProgressIndicatorPage;
|
||||||
import atlantafx.sampler.page.components.ProgressPage;
|
|
||||||
import atlantafx.sampler.page.components.RadioButtonPage;
|
import atlantafx.sampler.page.components.RadioButtonPage;
|
||||||
import atlantafx.sampler.page.components.ScrollPanePage;
|
import atlantafx.sampler.page.components.ScrollPanePage;
|
||||||
import atlantafx.sampler.page.components.SeparatorPage;
|
import atlantafx.sampler.page.components.SeparatorPage;
|
||||||
@ -35,22 +30,30 @@ import atlantafx.sampler.page.components.SliderPage;
|
|||||||
import atlantafx.sampler.page.components.SpinnerPage;
|
import atlantafx.sampler.page.components.SpinnerPage;
|
||||||
import atlantafx.sampler.page.components.SplitPanePage;
|
import atlantafx.sampler.page.components.SplitPanePage;
|
||||||
import atlantafx.sampler.page.components.TabPanePage;
|
import atlantafx.sampler.page.components.TabPanePage;
|
||||||
import atlantafx.sampler.page.components.TablePage;
|
import atlantafx.sampler.page.components.TableViewPage;
|
||||||
import atlantafx.sampler.page.components.TextAreaPage;
|
import atlantafx.sampler.page.components.TextAreaPage;
|
||||||
import atlantafx.sampler.page.components.TextFieldPage;
|
import atlantafx.sampler.page.components.TextFieldPage;
|
||||||
import atlantafx.sampler.page.components.TitledPanePage;
|
import atlantafx.sampler.page.components.TitledPanePage;
|
||||||
import atlantafx.sampler.page.components.ToggleButtonPage;
|
import atlantafx.sampler.page.components.ToggleButtonPage;
|
||||||
import atlantafx.sampler.page.components.ToggleSwitchPage;
|
|
||||||
import atlantafx.sampler.page.components.ToolBarPage;
|
import atlantafx.sampler.page.components.ToolBarPage;
|
||||||
import atlantafx.sampler.page.components.TooltipPage;
|
import atlantafx.sampler.page.components.TooltipPage;
|
||||||
import atlantafx.sampler.page.components.TreePage;
|
import atlantafx.sampler.page.components.TreeTableViewPage;
|
||||||
import atlantafx.sampler.page.components.TreeTablePage;
|
import atlantafx.sampler.page.components.TreeViewPage;
|
||||||
|
import atlantafx.sampler.page.extras.BBCodePage;
|
||||||
|
import atlantafx.sampler.page.extras.BreadcrumbsPage;
|
||||||
|
import atlantafx.sampler.page.extras.CalendarPage;
|
||||||
|
import atlantafx.sampler.page.extras.CustomTextFieldPage;
|
||||||
|
import atlantafx.sampler.page.extras.DeckPanePage;
|
||||||
|
import atlantafx.sampler.page.extras.InputGroupPage;
|
||||||
|
import atlantafx.sampler.page.extras.ModalPanePage;
|
||||||
|
import atlantafx.sampler.page.extras.PopoverPage;
|
||||||
|
import atlantafx.sampler.page.extras.ToggleSwitchPage;
|
||||||
import atlantafx.sampler.page.general.IconsPage;
|
import atlantafx.sampler.page.general.IconsPage;
|
||||||
|
import atlantafx.sampler.page.general.OverviewPage;
|
||||||
import atlantafx.sampler.page.general.ThemePage;
|
import atlantafx.sampler.page.general.ThemePage;
|
||||||
import atlantafx.sampler.page.general.TypographyPage;
|
import atlantafx.sampler.page.general.TypographyPage;
|
||||||
import atlantafx.sampler.page.showcase.filemanager.FileManagerPage;
|
import atlantafx.sampler.page.showcase.filemanager.FileManagerPage;
|
||||||
import atlantafx.sampler.page.showcase.musicplayer.MusicPlayerPage;
|
import atlantafx.sampler.page.showcase.musicplayer.MusicPlayerPage;
|
||||||
import atlantafx.sampler.page.showcase.widget.WidgetCollectionPage;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -156,24 +159,26 @@ public class MainModel {
|
|||||||
NAV_TREE.get(ButtonPage.class),
|
NAV_TREE.get(ButtonPage.class),
|
||||||
NAV_TREE.get(ChartPage.class),
|
NAV_TREE.get(ChartPage.class),
|
||||||
NAV_TREE.get(CheckBoxPage.class),
|
NAV_TREE.get(CheckBoxPage.class),
|
||||||
|
NAV_TREE.get(ChoiceBoxPage.class),
|
||||||
NAV_TREE.get(ColorPickerPage.class),
|
NAV_TREE.get(ColorPickerPage.class),
|
||||||
NAV_TREE.get(ComboBoxPage.class),
|
NAV_TREE.get(ComboBoxPage.class),
|
||||||
|
NAV_TREE.get(ContextMenuPage.class),
|
||||||
NAV_TREE.get(DatePickerPage.class),
|
NAV_TREE.get(DatePickerPage.class),
|
||||||
NAV_TREE.get(DialogPage.class),
|
NAV_TREE.get(DialogPage.class),
|
||||||
NAV_TREE.get(HtmlEditorPage.class),
|
NAV_TREE.get(HtmlEditorPage.class),
|
||||||
NAV_TREE.get(LabelPage.class),
|
NAV_TREE.get(LabelPage.class),
|
||||||
NAV_TREE.get(ListPage.class),
|
NAV_TREE.get(ListViewPage.class),
|
||||||
NAV_TREE.get(MenuPage.class),
|
NAV_TREE.get(MenuBarPage.class),
|
||||||
NAV_TREE.get(MenuButtonPage.class),
|
NAV_TREE.get(MenuButtonPage.class),
|
||||||
NAV_TREE.get(PaginationPage.class),
|
NAV_TREE.get(PaginationPage.class),
|
||||||
NAV_TREE.get(ProgressPage.class),
|
NAV_TREE.get(ProgressIndicatorPage.class),
|
||||||
NAV_TREE.get(RadioButtonPage.class),
|
NAV_TREE.get(RadioButtonPage.class),
|
||||||
NAV_TREE.get(ScrollPanePage.class),
|
NAV_TREE.get(ScrollPanePage.class),
|
||||||
NAV_TREE.get(SeparatorPage.class),
|
NAV_TREE.get(SeparatorPage.class),
|
||||||
NAV_TREE.get(SliderPage.class),
|
NAV_TREE.get(SliderPage.class),
|
||||||
NAV_TREE.get(SpinnerPage.class),
|
NAV_TREE.get(SpinnerPage.class),
|
||||||
NAV_TREE.get(SplitPanePage.class),
|
NAV_TREE.get(SplitPanePage.class),
|
||||||
NAV_TREE.get(TablePage.class),
|
NAV_TREE.get(TableViewPage.class),
|
||||||
NAV_TREE.get(TabPanePage.class),
|
NAV_TREE.get(TabPanePage.class),
|
||||||
NAV_TREE.get(TextAreaPage.class),
|
NAV_TREE.get(TextAreaPage.class),
|
||||||
NAV_TREE.get(TextFieldPage.class),
|
NAV_TREE.get(TextFieldPage.class),
|
||||||
@ -181,16 +186,18 @@ public class MainModel {
|
|||||||
NAV_TREE.get(ToggleButtonPage.class),
|
NAV_TREE.get(ToggleButtonPage.class),
|
||||||
NAV_TREE.get(ToolBarPage.class),
|
NAV_TREE.get(ToolBarPage.class),
|
||||||
NAV_TREE.get(TooltipPage.class),
|
NAV_TREE.get(TooltipPage.class),
|
||||||
NAV_TREE.get(TreePage.class),
|
NAV_TREE.get(TreeTableViewPage.class),
|
||||||
NAV_TREE.get(TreeTablePage.class)
|
NAV_TREE.get(TreeViewPage.class)
|
||||||
);
|
);
|
||||||
|
|
||||||
var extras = NavTree.Item.group("Extras", new FontIcon(Material2OutlinedMZ.TOGGLE_ON));
|
var extras = NavTree.Item.group("Extras", new FontIcon(Material2OutlinedMZ.TOGGLE_ON));
|
||||||
extras.getChildren().setAll(
|
extras.getChildren().setAll(
|
||||||
NAV_TREE.get(InputGroupPage.class),
|
|
||||||
NAV_TREE.get(BBCodePage.class),
|
NAV_TREE.get(BBCodePage.class),
|
||||||
NAV_TREE.get(BreadcrumbsPage.class),
|
NAV_TREE.get(BreadcrumbsPage.class),
|
||||||
|
NAV_TREE.get(CalendarPage.class),
|
||||||
NAV_TREE.get(CustomTextFieldPage.class),
|
NAV_TREE.get(CustomTextFieldPage.class),
|
||||||
|
NAV_TREE.get(DeckPanePage.class),
|
||||||
|
NAV_TREE.get(InputGroupPage.class),
|
||||||
NAV_TREE.get(ModalPanePage.class),
|
NAV_TREE.get(ModalPanePage.class),
|
||||||
NAV_TREE.get(PopoverPage.class),
|
NAV_TREE.get(PopoverPage.class),
|
||||||
NAV_TREE.get(ToggleSwitchPage.class)
|
NAV_TREE.get(ToggleSwitchPage.class)
|
||||||
@ -199,8 +206,7 @@ public class MainModel {
|
|||||||
var showcases = NavTree.Item.group("Showcase", new FontIcon(Material2OutlinedMZ.VISIBILITY));
|
var showcases = NavTree.Item.group("Showcase", new FontIcon(Material2OutlinedMZ.VISIBILITY));
|
||||||
showcases.getChildren().setAll(
|
showcases.getChildren().setAll(
|
||||||
NAV_TREE.get(FileManagerPage.class),
|
NAV_TREE.get(FileManagerPage.class),
|
||||||
NAV_TREE.get(MusicPlayerPage.class),
|
NAV_TREE.get(MusicPlayerPage.class)
|
||||||
NAV_TREE.get(WidgetCollectionPage.class)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
var root = NavTree.Item.root();
|
var root = NavTree.Item.root();
|
||||||
@ -216,33 +222,39 @@ public class MainModel {
|
|||||||
public static Map<Class<? extends Page>, NavTree.Item> createNavItems() {
|
public static Map<Class<? extends Page>, NavTree.Item> createNavItems() {
|
||||||
var map = new HashMap<Class<? extends Page>, NavTree.Item>();
|
var map = new HashMap<Class<? extends Page>, NavTree.Item>();
|
||||||
|
|
||||||
|
// general
|
||||||
map.put(OverviewPage.class, NavTree.Item.page(OverviewPage.NAME, OverviewPage.class));
|
map.put(OverviewPage.class, NavTree.Item.page(OverviewPage.NAME, OverviewPage.class));
|
||||||
map.put(ThemePage.class, NavTree.Item.page(ThemePage.NAME, ThemePage.class));
|
map.put(ThemePage.class, NavTree.Item.page(ThemePage.NAME, ThemePage.class));
|
||||||
map.put(TypographyPage.class, NavTree.Item.page(TypographyPage.NAME, TypographyPage.class));
|
map.put(TypographyPage.class, NavTree.Item.page(TypographyPage.NAME, TypographyPage.class));
|
||||||
map.put(IconsPage.class, NavTree.Item.page(IconsPage.NAME, IconsPage.class));
|
map.put(IconsPage.class, NavTree.Item.page(IconsPage.NAME, IconsPage.class));
|
||||||
|
|
||||||
|
// components
|
||||||
map.put(InputGroupPage.class, NavTree.Item.page(InputGroupPage.NAME, InputGroupPage.class));
|
map.put(InputGroupPage.class, NavTree.Item.page(InputGroupPage.NAME, InputGroupPage.class));
|
||||||
map.put(AccordionPage.class, NavTree.Item.page(AccordionPage.NAME, AccordionPage.class));
|
map.put(AccordionPage.class, NavTree.Item.page(AccordionPage.NAME, AccordionPage.class));
|
||||||
map.put(BreadcrumbsPage.class, NavTree.Item.page(BreadcrumbsPage.NAME, BreadcrumbsPage.class));
|
map.put(BreadcrumbsPage.class, NavTree.Item.page(BreadcrumbsPage.NAME, BreadcrumbsPage.class));
|
||||||
map.put(ButtonPage.class, NavTree.Item.page(ButtonPage.NAME, ButtonPage.class));
|
map.put(ButtonPage.class, NavTree.Item.page(ButtonPage.NAME, ButtonPage.class));
|
||||||
map.put(BBCodePage.class, NavTree.Item.page(BBCodePage.NAME, BBCodePage.class));
|
map.put(BBCodePage.class, NavTree.Item.page(BBCodePage.NAME, BBCodePage.class));
|
||||||
|
map.put(CalendarPage.class, NavTree.Item.page(CalendarPage.NAME, CalendarPage.class));
|
||||||
map.put(ChartPage.class, NavTree.Item.page(ChartPage.NAME, ChartPage.class));
|
map.put(ChartPage.class, NavTree.Item.page(ChartPage.NAME, ChartPage.class));
|
||||||
|
map.put(ChoiceBoxPage.class, NavTree.Item.page(ChoiceBoxPage.NAME, ChoiceBoxPage.class));
|
||||||
map.put(CheckBoxPage.class, NavTree.Item.page(CheckBoxPage.NAME, CheckBoxPage.class));
|
map.put(CheckBoxPage.class, NavTree.Item.page(CheckBoxPage.NAME, CheckBoxPage.class));
|
||||||
map.put(ColorPickerPage.class, NavTree.Item.page(ColorPickerPage.NAME, ColorPickerPage.class));
|
map.put(ColorPickerPage.class, NavTree.Item.page(ColorPickerPage.NAME, ColorPickerPage.class));
|
||||||
map.put(
|
map.put(
|
||||||
ComboBoxPage.class,
|
ComboBoxPage.class,
|
||||||
NavTree.Item.page(ComboBoxPage.NAME, ComboBoxPage.class, "ChoiceBox")
|
NavTree.Item.page(ComboBoxPage.NAME, ComboBoxPage.class, "ChoiceBox")
|
||||||
);
|
);
|
||||||
|
map.put(ContextMenuPage.class, NavTree.Item.page(ContextMenuPage.NAME, ContextMenuPage.class));
|
||||||
map.put(
|
map.put(
|
||||||
CustomTextFieldPage.class,
|
CustomTextFieldPage.class,
|
||||||
NavTree.Item.page(CustomTextFieldPage.NAME, CustomTextFieldPage.class, "MaskTextField", "PasswordTextField")
|
NavTree.Item.page(CustomTextFieldPage.NAME, CustomTextFieldPage.class, "MaskTextField", "PasswordTextField")
|
||||||
);
|
);
|
||||||
map.put(DatePickerPage.class, NavTree.Item.page(DatePickerPage.NAME, DatePickerPage.class));
|
map.put(DatePickerPage.class, NavTree.Item.page(DatePickerPage.NAME, DatePickerPage.class));
|
||||||
|
map.put(DeckPanePage.class, NavTree.Item.page(DeckPanePage.NAME, DeckPanePage.class));
|
||||||
map.put(DialogPage.class, NavTree.Item.page(DialogPage.NAME, DialogPage.class));
|
map.put(DialogPage.class, NavTree.Item.page(DialogPage.NAME, DialogPage.class));
|
||||||
map.put(HtmlEditorPage.class, NavTree.Item.page(HtmlEditorPage.NAME, HtmlEditorPage.class));
|
map.put(HtmlEditorPage.class, NavTree.Item.page(HtmlEditorPage.NAME, HtmlEditorPage.class));
|
||||||
map.put(LabelPage.class, NavTree.Item.page(LabelPage.NAME, LabelPage.class));
|
map.put(LabelPage.class, NavTree.Item.page(LabelPage.NAME, LabelPage.class));
|
||||||
map.put(ListPage.class, NavTree.Item.page(ListPage.NAME, ListPage.class));
|
map.put(ListViewPage.class, NavTree.Item.page(ListViewPage.NAME, ListViewPage.class));
|
||||||
map.put(MenuPage.class, NavTree.Item.page(MenuPage.NAME, MenuPage.class));
|
map.put(MenuBarPage.class, NavTree.Item.page(MenuBarPage.NAME, MenuBarPage.class));
|
||||||
map.put(MenuButtonPage.class, NavTree.Item.page(
|
map.put(MenuButtonPage.class, NavTree.Item.page(
|
||||||
MenuButtonPage.NAME,
|
MenuButtonPage.NAME,
|
||||||
MenuButtonPage.class, "SplitMenuButton")
|
MenuButtonPage.class, "SplitMenuButton")
|
||||||
@ -250,14 +262,17 @@ public class MainModel {
|
|||||||
map.put(ModalPanePage.class, NavTree.Item.page(ModalPanePage.NAME, ModalPanePage.class));
|
map.put(ModalPanePage.class, NavTree.Item.page(ModalPanePage.NAME, ModalPanePage.class));
|
||||||
map.put(PaginationPage.class, NavTree.Item.page(PaginationPage.NAME, PaginationPage.class));
|
map.put(PaginationPage.class, NavTree.Item.page(PaginationPage.NAME, PaginationPage.class));
|
||||||
map.put(PopoverPage.class, NavTree.Item.page(PopoverPage.NAME, PopoverPage.class));
|
map.put(PopoverPage.class, NavTree.Item.page(PopoverPage.NAME, PopoverPage.class));
|
||||||
map.put(ProgressPage.class, NavTree.Item.page(ProgressPage.NAME, ProgressPage.class));
|
map.put(ProgressIndicatorPage.class, NavTree.Item.page(
|
||||||
|
ProgressIndicatorPage.NAME,
|
||||||
|
ProgressIndicatorPage.class, "ProgressBar")
|
||||||
|
);
|
||||||
map.put(RadioButtonPage.class, NavTree.Item.page(RadioButtonPage.NAME, RadioButtonPage.class));
|
map.put(RadioButtonPage.class, NavTree.Item.page(RadioButtonPage.NAME, RadioButtonPage.class));
|
||||||
map.put(ScrollPanePage.class, NavTree.Item.page(ScrollPanePage.NAME, ScrollPanePage.class));
|
map.put(ScrollPanePage.class, NavTree.Item.page(ScrollPanePage.NAME, ScrollPanePage.class));
|
||||||
map.put(SeparatorPage.class, NavTree.Item.page(SeparatorPage.NAME, SeparatorPage.class));
|
map.put(SeparatorPage.class, NavTree.Item.page(SeparatorPage.NAME, SeparatorPage.class));
|
||||||
map.put(SliderPage.class, NavTree.Item.page(SliderPage.NAME, SliderPage.class));
|
map.put(SliderPage.class, NavTree.Item.page(SliderPage.NAME, SliderPage.class));
|
||||||
map.put(SpinnerPage.class, NavTree.Item.page(SpinnerPage.NAME, SpinnerPage.class));
|
map.put(SpinnerPage.class, NavTree.Item.page(SpinnerPage.NAME, SpinnerPage.class));
|
||||||
map.put(SplitPanePage.class, NavTree.Item.page(SplitPanePage.NAME, SplitPanePage.class));
|
map.put(SplitPanePage.class, NavTree.Item.page(SplitPanePage.NAME, SplitPanePage.class));
|
||||||
map.put(TablePage.class, NavTree.Item.page(TablePage.NAME, TablePage.class));
|
map.put(TableViewPage.class, NavTree.Item.page(TableViewPage.NAME, TableViewPage.class));
|
||||||
map.put(TabPanePage.class, NavTree.Item.page(TabPanePage.NAME, TabPanePage.class));
|
map.put(TabPanePage.class, NavTree.Item.page(TabPanePage.NAME, TabPanePage.class));
|
||||||
map.put(TextAreaPage.class, NavTree.Item.page(TextAreaPage.NAME, TextAreaPage.class));
|
map.put(TextAreaPage.class, NavTree.Item.page(TextAreaPage.NAME, TextAreaPage.class));
|
||||||
map.put(TextFieldPage.class, NavTree.Item.page(
|
map.put(TextFieldPage.class, NavTree.Item.page(
|
||||||
@ -269,15 +284,12 @@ public class MainModel {
|
|||||||
map.put(ToggleSwitchPage.class, NavTree.Item.page(ToggleSwitchPage.NAME, ToggleSwitchPage.class));
|
map.put(ToggleSwitchPage.class, NavTree.Item.page(ToggleSwitchPage.NAME, ToggleSwitchPage.class));
|
||||||
map.put(ToolBarPage.class, NavTree.Item.page(ToolBarPage.NAME, ToolBarPage.class));
|
map.put(ToolBarPage.class, NavTree.Item.page(ToolBarPage.NAME, ToolBarPage.class));
|
||||||
map.put(TooltipPage.class, NavTree.Item.page(TooltipPage.NAME, TooltipPage.class));
|
map.put(TooltipPage.class, NavTree.Item.page(TooltipPage.NAME, TooltipPage.class));
|
||||||
map.put(TreePage.class, NavTree.Item.page(TreePage.NAME, TreePage.class));
|
map.put(TreeTableViewPage.class, NavTree.Item.page(TreeTableViewPage.NAME, TreeTableViewPage.class));
|
||||||
map.put(TreeTablePage.class, NavTree.Item.page(TreeTablePage.NAME, TreeTablePage.class));
|
map.put(TreeViewPage.class, NavTree.Item.page(TreeViewPage.NAME, TreeViewPage.class));
|
||||||
|
|
||||||
|
// showcases
|
||||||
map.put(FileManagerPage.class, NavTree.Item.page(FileManagerPage.NAME, FileManagerPage.class));
|
map.put(FileManagerPage.class, NavTree.Item.page(FileManagerPage.NAME, FileManagerPage.class));
|
||||||
map.put(MusicPlayerPage.class, NavTree.Item.page(MusicPlayerPage.NAME, MusicPlayerPage.class));
|
map.put(MusicPlayerPage.class, NavTree.Item.page(MusicPlayerPage.NAME, MusicPlayerPage.class));
|
||||||
map.put(WidgetCollectionPage.class, NavTree.Item.page(
|
|
||||||
WidgetCollectionPage.NAME,
|
|
||||||
WidgetCollectionPage.class, "Card", "Message", "Stepper", "Tag")
|
|
||||||
);
|
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ public class NavTree extends TreeView<Nav> {
|
|||||||
root.getChildren().setAll(titleLabel, new Spacer(), arrowIcon);
|
root.getChildren().setAll(titleLabel, new Spacer(), arrowIcon);
|
||||||
root.setCursor(Cursor.HAND);
|
root.setCursor(Cursor.HAND);
|
||||||
root.getStyleClass().add("container");
|
root.getStyleClass().add("container");
|
||||||
root.setMaxWidth(MainLayer.SIDEBAR_WIDTH - 10);
|
root.setMaxWidth(ApplicationWindow.SIDEBAR_WIDTH - 10);
|
||||||
|
|
||||||
root.setOnMouseClicked(e -> {
|
root.setOnMouseClicked(e -> {
|
||||||
if (!(getTreeItem() instanceof Item item)) {
|
if (!(getTreeItem() instanceof Item item)) {
|
||||||
|
@ -8,7 +8,6 @@ import atlantafx.base.theme.Styles;
|
|||||||
import atlantafx.base.theme.Tweaks;
|
import atlantafx.base.theme.Tweaks;
|
||||||
import atlantafx.sampler.page.OverlayDialog;
|
import atlantafx.sampler.page.OverlayDialog;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import javafx.event.EventType;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
@ -4,29 +4,22 @@ package atlantafx.sampler.page;
|
|||||||
|
|
||||||
import static atlantafx.sampler.util.Containers.setScrollConstraints;
|
import static atlantafx.sampler.util.Containers.setScrollConstraints;
|
||||||
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.AS_NEEDED;
|
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.AS_NEEDED;
|
||||||
|
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.NEVER;
|
||||||
|
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.layout.Overlay;
|
import atlantafx.sampler.layout.Overlay;
|
||||||
import java.util.Arrays;
|
import javafx.geometry.Pos;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import net.datafaker.Faker;
|
import javafx.scene.layout.VBox;
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
public abstract class AbstractPage extends BorderPane implements Page {
|
public abstract class AbstractPage extends StackPane implements Page {
|
||||||
|
|
||||||
protected static final Faker FAKER = new Faker();
|
protected final VBox userContent = new VBox();
|
||||||
protected static final Random RANDOM = new Random();
|
|
||||||
|
|
||||||
protected final StackPane userContent = new StackPane();
|
|
||||||
protected Overlay overlay;
|
protected Overlay overlay;
|
||||||
protected boolean isRendered = false;
|
protected boolean isRendered = false;
|
||||||
|
|
||||||
@ -40,11 +33,16 @@ public abstract class AbstractPage extends BorderPane implements Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void createPageLayout() {
|
protected void createPageLayout() {
|
||||||
var scrollPane = new ScrollPane(userContent);
|
var userContentArea = new StackPane(userContent);
|
||||||
setScrollConstraints(scrollPane, AS_NEEDED, true, AS_NEEDED, true);
|
userContentArea.setAlignment(Pos.TOP_CENTER);
|
||||||
scrollPane.setMaxHeight(10_000);
|
userContent.setMinWidth(Math.min(Page.MAX_WIDTH, 800));
|
||||||
|
userContent.setMaxWidth(Math.min(Page.MAX_WIDTH, 800));
|
||||||
|
|
||||||
setCenter(scrollPane);
|
var scrollPane = new ScrollPane(userContentArea);
|
||||||
|
setScrollConstraints(scrollPane, AS_NEEDED, true, NEVER, true);
|
||||||
|
scrollPane.setMaxHeight(20_000);
|
||||||
|
|
||||||
|
getChildren().setAll(scrollPane);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setUserContent(Node content) {
|
protected void setUserContent(Node content) {
|
||||||
@ -87,23 +85,20 @@ public abstract class AbstractPage extends BorderPane implements Page {
|
|||||||
this.overlay = lookupOverlay();
|
this.overlay = lookupOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void addNode(Node node) {
|
||||||
|
userContent.getChildren().add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addPlainText(String text) {
|
||||||
|
userContent.getChildren().add(new TextFlow(new Text(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addFormattedText(String text) {
|
||||||
|
userContent.getChildren().add(BBCodeParser.createFormattedText(text));
|
||||||
|
}
|
||||||
|
|
||||||
protected Overlay lookupOverlay() {
|
protected Overlay lookupOverlay() {
|
||||||
return getScene() != null && getScene().lookup("." + Overlay.STYLE_CLASS) instanceof Overlay ov ? ov : null;
|
return getScene() != null
|
||||||
}
|
&& getScene().lookup("." + Overlay.STYLE_CLASS) instanceof Overlay ov ? ov : null;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
protected HBox expandingHBox(Node... nodes) {
|
|
||||||
var box = new HBox(PAGE_HGAP, nodes);
|
|
||||||
Arrays.stream(nodes).forEach(n -> HBox.setHgrow(n, Priority.ALWAYS));
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <T> List<T> generate(Supplier<T> supplier, int count) {
|
|
||||||
return Stream.generate(supplier).limit(count).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Feather randomIcon() {
|
|
||||||
return Feather.values()[RANDOM.nextInt(Feather.values().length)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
package atlantafx.sampler.page;
|
||||||
|
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
|
// This code is adapted from RichTextFX JavaKeywordsDemo:
|
||||||
|
// https://github.com/FXMisc/RichTextFX/blob/master/richtextfx-demos
|
||||||
|
public class BBSyntaxHighlighter {
|
||||||
|
|
||||||
|
private static final String[] KEYWORDS = new String[] {
|
||||||
|
"abstract", "assert", "boolean", "break", "byte",
|
||||||
|
"case", "catch", "char", "class", "const",
|
||||||
|
"continue", "default", "do", "double", "else",
|
||||||
|
"enum", "extends", "final", "finally", "float",
|
||||||
|
"for", "if", "implements", "import",
|
||||||
|
"instanceof", "int", "interface", "long", "native",
|
||||||
|
"new", "package", "private", "protected", "public",
|
||||||
|
"return", "short", "static", "super",
|
||||||
|
"switch", "synchronized", "this", "throw", "throws",
|
||||||
|
"transient", "try", "void", "volatile", "while",
|
||||||
|
"var", "record", "with", "yield", "sealed", "non-sealed"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String KEYWORD_PATTERN = "\\b(" + String.join("|", KEYWORDS) + ")\\b";
|
||||||
|
private static final String PAREN_PATTERN = "\\(|\\)";
|
||||||
|
private static final String STRING_PATTERN = "\"([^\"\\\\]|\\\\.)*\"";
|
||||||
|
private static final String COMMENT_PATTERN = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/"
|
||||||
|
+ "|" + "/\\*[^\\v]*" + "|" + "^\\h*\\*([^\\v]*|/)";
|
||||||
|
|
||||||
|
private static final Pattern PATTERN = Pattern.compile(
|
||||||
|
"(?<KEYWORD>" + KEYWORD_PATTERN + ")"
|
||||||
|
+ "|(?<STRING>" + STRING_PATTERN + ")"
|
||||||
|
+ "|(?<PAREN>" + PAREN_PATTERN + ")"
|
||||||
|
+ "|(?<COMMENT>" + COMMENT_PATTERN + ")"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enclose keywords to BBCode tags.
|
||||||
|
public static String format(String text) {
|
||||||
|
Matcher m = PATTERN.matcher(text);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int lastKwEnd = 0;
|
||||||
|
|
||||||
|
while (m.find()) {
|
||||||
|
String styleClass =
|
||||||
|
m.group("KEYWORD") != null ? "keyword" :
|
||||||
|
m.group("PAREN") != null ? "paren" :
|
||||||
|
m.group("STRING") != null ? "string" :
|
||||||
|
m.group("COMMENT") != null ? "comment" :
|
||||||
|
null;
|
||||||
|
|
||||||
|
sb.append(text, lastKwEnd, m.start())
|
||||||
|
.append("[span='")
|
||||||
|
.append(Objects.requireNonNullElse(styleClass, "absent"))
|
||||||
|
.append("']")
|
||||||
|
.append(text, m.start(), m.end())
|
||||||
|
.append("[/span]");
|
||||||
|
lastKwEnd = m.end();
|
||||||
|
}
|
||||||
|
sb.append(text, lastKwEnd, text.length());
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextFlow highlight(String text) {
|
||||||
|
return BBCodeParser.createFormattedText(format(text));
|
||||||
|
}
|
||||||
|
}
|
117
sampler/src/main/java/atlantafx/sampler/page/ExampleBox.java
Normal file
117
sampler/src/main/java/atlantafx/sampler/page/ExampleBox.java
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Spacer;
|
||||||
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Tooltip;
|
||||||
|
import javafx.scene.input.Clipboard;
|
||||||
|
import javafx.scene.input.ClipboardContent;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
||||||
|
|
||||||
|
// This widget emulates TabPane behavior, because TabPane itself doesn't work as it should:
|
||||||
|
// https://bugs.openjdk.org/browse/JDK-8145490
|
||||||
|
public class ExampleBox extends VBox {
|
||||||
|
|
||||||
|
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
||||||
|
private final ObjectProperty<Label> selectedTab = new SimpleObjectProperty<>();
|
||||||
|
private final ToggleSwitch stateToggle;
|
||||||
|
|
||||||
|
public ExampleBox(Node preview, Snippet snippet) {
|
||||||
|
this(preview, snippet, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExampleBox(Node preview, Snippet snippet, @Nullable Node description) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
Objects.requireNonNull(preview, "preview");
|
||||||
|
Objects.requireNonNull(snippet, "snippet");
|
||||||
|
|
||||||
|
var previewTab = createTabLabel("Preview");
|
||||||
|
var codeTab = createTabLabel("Code");
|
||||||
|
|
||||||
|
var copyBtn = new Button("", new FontIcon(Material2OutlinedAL.CODE));
|
||||||
|
copyBtn.getStyleClass().addAll(Styles.BUTTON_CIRCLE, Styles.FLAT, Styles.SMALL);
|
||||||
|
copyBtn.setTooltip(new Tooltip("Copy source code"));
|
||||||
|
|
||||||
|
stateToggle = new ToggleSwitch();
|
||||||
|
HBox.setMargin(stateToggle, new Insets(0, 0, 0, 10));
|
||||||
|
|
||||||
|
var tabs = new HBox(
|
||||||
|
previewTab, codeTab,
|
||||||
|
new Spacer(), copyBtn, stateToggle
|
||||||
|
);
|
||||||
|
tabs.getStyleClass().add("tabs");
|
||||||
|
tabs.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var content = new VBox();
|
||||||
|
|
||||||
|
getStyleClass().add("example-box");
|
||||||
|
if (description != null) {
|
||||||
|
getChildren().add(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildren().addAll(tabs, content);
|
||||||
|
|
||||||
|
// == INIT ==
|
||||||
|
|
||||||
|
stateToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
|
if (selectedTab.get() == previewTab) {
|
||||||
|
content.getChildren().forEach(c -> c.setDisable(val));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
copyBtn.setOnAction(e -> {
|
||||||
|
var cc = new ClipboardContent();
|
||||||
|
cc.putString(snippet.getSourceCode());
|
||||||
|
Clipboard.getSystemClipboard().setContent(cc);
|
||||||
|
});
|
||||||
|
|
||||||
|
selectedTab.addListener((obs, old, val) -> {
|
||||||
|
if (val == codeTab) {
|
||||||
|
stateToggle.setDisable(true);
|
||||||
|
content.getChildren().setAll(snippet.render());
|
||||||
|
} else {
|
||||||
|
stateToggle.setDisable(false);
|
||||||
|
content.getChildren().setAll(preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old != null) {
|
||||||
|
old.pseudoClassStateChanged(SELECTED, false);
|
||||||
|
}
|
||||||
|
if (val != null) {
|
||||||
|
val.pseudoClassStateChanged(SELECTED, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
selectedTab.set(previewTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowDisable(boolean allow) {
|
||||||
|
stateToggle.setDisable(!allow);
|
||||||
|
stateToggle.setVisible(allow);
|
||||||
|
stateToggle.setManaged(allow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Label createTabLabel(String title) {
|
||||||
|
var label = new Label(title);
|
||||||
|
label.setOnMouseClicked(e -> selectedTab.set(label));
|
||||||
|
label.setPrefWidth(120);
|
||||||
|
label.setAlignment(Pos.CENTER);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
277
sampler/src/main/java/atlantafx/sampler/page/OutlinePage.java
Executable file
277
sampler/src/main/java/atlantafx/sampler/page/OutlinePage.java
Executable file
@ -0,0 +1,277 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page;
|
||||||
|
|
||||||
|
import static atlantafx.sampler.util.Containers.setScrollConstraints;
|
||||||
|
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.AS_NEEDED;
|
||||||
|
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.NEVER;
|
||||||
|
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.sampler.layout.Overlay;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.Separator;
|
||||||
|
import javafx.scene.input.MouseButton;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
public abstract class OutlinePage extends StackPane implements Page {
|
||||||
|
|
||||||
|
protected static final int OUTLINE_WIDTH = 200;
|
||||||
|
|
||||||
|
protected final ScrollPane scrollPane = new ScrollPane();
|
||||||
|
protected final VBox userContent = new VBox();
|
||||||
|
protected final Outline outline = new Outline(createOutlineHandler());
|
||||||
|
|
||||||
|
protected Overlay overlay;
|
||||||
|
protected boolean isRendered = false;
|
||||||
|
|
||||||
|
protected OutlinePage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
userContent.getStyleClass().add("user-content");
|
||||||
|
getStyleClass().add("outline-page");
|
||||||
|
|
||||||
|
createPageLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createPageLayout() {
|
||||||
|
var userContentArea = new StackPane(userContent);
|
||||||
|
StackPane.setMargin(userContent, new Insets(0, OUTLINE_WIDTH, 0, 0));
|
||||||
|
userContent.setMinWidth(Page.MAX_WIDTH - OUTLINE_WIDTH - 100);
|
||||||
|
userContent.setMaxWidth(Page.MAX_WIDTH - OUTLINE_WIDTH - 100);
|
||||||
|
|
||||||
|
scrollPane.setContent(userContentArea);
|
||||||
|
setScrollConstraints(scrollPane, AS_NEEDED, true, NEVER, true);
|
||||||
|
scrollPane.setMaxHeight(20_000);
|
||||||
|
|
||||||
|
// scroll spy
|
||||||
|
scrollPane.vvalueProperty().addListener((obs, old, val) ->
|
||||||
|
// we need a little gap between changing vValue and fetching header bounds
|
||||||
|
Platform.runLater(() -> outline.select(getFirstVisibleHeader()))
|
||||||
|
);
|
||||||
|
|
||||||
|
var pageBody = new StackPane();
|
||||||
|
pageBody.getChildren().setAll(scrollPane, outline);
|
||||||
|
pageBody.getStyleClass().add("body");
|
||||||
|
StackPane.setAlignment(outline, Pos.TOP_RIGHT);
|
||||||
|
StackPane.setMargin(outline, new Insets(50, 20, 0, 0));
|
||||||
|
|
||||||
|
setMinWidth(Page.MAX_WIDTH);
|
||||||
|
getChildren().setAll(pageBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Consumer<Heading> createOutlineHandler() {
|
||||||
|
return heading -> {
|
||||||
|
if (heading != Heading.TOP) {
|
||||||
|
Parent container = heading.anchor().getParent();
|
||||||
|
int indexInParent = container.getChildrenUnmodifiable().indexOf(heading.anchor());
|
||||||
|
|
||||||
|
Node target;
|
||||||
|
double targetY;
|
||||||
|
|
||||||
|
if (container.getChildrenUnmodifiable().size() > indexInParent + 1) {
|
||||||
|
// aims to the middle of the content node (the one that is next to header)
|
||||||
|
target = container.getChildrenUnmodifiable().get(indexInParent + 1);
|
||||||
|
var bounds = target.getBoundsInParent();
|
||||||
|
targetY = bounds.getMaxY() - (bounds.getHeight() / 2);
|
||||||
|
} else {
|
||||||
|
target = heading.anchor();
|
||||||
|
targetY = target.getBoundsInParent().getMaxY();
|
||||||
|
}
|
||||||
|
|
||||||
|
double height = scrollPane.getContent().getBoundsInLocal().getHeight();
|
||||||
|
scrollPane.setVvalue(targetY / height);
|
||||||
|
} else {
|
||||||
|
scrollPane.setVvalue(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String getFirstVisibleHeader() {
|
||||||
|
var scrollBounds = scrollPane.localToScene(scrollPane.getBoundsInParent());
|
||||||
|
Label lastHeading = null;
|
||||||
|
for (var node : userContent.getChildren()) {
|
||||||
|
if (!(node instanceof Label heading)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var headingBounds = heading.localToScene(heading.getBoundsInLocal());
|
||||||
|
if (outline.contains(heading.getText())) {
|
||||||
|
// viewport should fully contain heading bounds, not just a part of it
|
||||||
|
if (scrollBounds.contains(headingBounds)) {
|
||||||
|
return heading.getText();
|
||||||
|
} else {
|
||||||
|
lastHeading = heading;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastHeading != null ? lastHeading.getText() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addNode(Node node) {
|
||||||
|
userContent.getChildren().add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addPlainText(String text) {
|
||||||
|
userContent.getChildren().add(new TextFlow(new Text(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addFormattedText(String text) {
|
||||||
|
addFormattedText(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addFormattedText(String text, boolean handleUrl) {
|
||||||
|
userContent.getChildren().add(createFormattedText(text, handleUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addSection(String title, Node content) {
|
||||||
|
var titleIcon = new FontIcon(Feather.HASH);
|
||||||
|
titleIcon.getStyleClass().add("icon-subtle");
|
||||||
|
|
||||||
|
var titleLabel = new Label(title);
|
||||||
|
titleLabel.getStyleClass().add(Styles.TITLE_3);
|
||||||
|
titleLabel.setGraphic(titleIcon);
|
||||||
|
titleLabel.setGraphicTextGap(10);
|
||||||
|
titleLabel.setPadding(new Insets(20, 0, 0, 0));
|
||||||
|
|
||||||
|
userContent.getChildren().addAll(titleLabel, content);
|
||||||
|
outline.add(new Heading(title, titleLabel));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pane getView() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canDisplaySourceCode() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canChangeThemeSettings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void layoutChildren() {
|
||||||
|
super.layoutChildren();
|
||||||
|
if (isRendered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRendered = true;
|
||||||
|
onRendered();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some properties can only be obtained after node placed
|
||||||
|
// to the scene graph and here is the place do this.
|
||||||
|
protected void onRendered() {
|
||||||
|
this.overlay = lookupOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Overlay lookupOverlay() {
|
||||||
|
var scene = getScene();
|
||||||
|
return scene != null && scene.lookup("." + Overlay.STYLE_CLASS) instanceof Overlay o ? o : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public record Heading(String title, Node anchor) {
|
||||||
|
|
||||||
|
private static final Heading TOP = new Heading("Top", new Text());
|
||||||
|
|
||||||
|
public Heading {
|
||||||
|
Objects.requireNonNull(title, "title");
|
||||||
|
Objects.requireNonNull(anchor, "anchor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Outline extends VBox {
|
||||||
|
|
||||||
|
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
||||||
|
|
||||||
|
private final Set<String> toc = new LinkedHashSet<>();
|
||||||
|
private final Consumer<Heading> clickHandler;
|
||||||
|
private Label selected;
|
||||||
|
|
||||||
|
public Outline(Consumer<Heading> clickHandler) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.clickHandler = Objects.requireNonNull(clickHandler, "clickHandler");
|
||||||
|
|
||||||
|
// outline has two items by default at the bottom
|
||||||
|
getChildren().add(new Separator());
|
||||||
|
getChildren().add(createEntry(Heading.TOP));
|
||||||
|
setPickOnBounds(false); // do not consume scroll events on transparent pixels
|
||||||
|
|
||||||
|
getStyleClass().add("outline");
|
||||||
|
setMinWidth(OUTLINE_WIDTH);
|
||||||
|
setMaxWidth(OUTLINE_WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Heading heading) {
|
||||||
|
var label = createEntry(heading);
|
||||||
|
if (getChildren().size() == 2) { // top entry
|
||||||
|
label.pseudoClassStateChanged(SELECTED, true);
|
||||||
|
this.selected = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
Objects.requireNonNull(heading, "heading");
|
||||||
|
getChildren().add(getChildren().size() - 2, label);
|
||||||
|
toc.add(heading.title());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(String title) {
|
||||||
|
return title != null && toc.contains(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void select(String title) {
|
||||||
|
if (selected != null) {
|
||||||
|
selected.pseudoClassStateChanged(SELECTED, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var node : getChildren()) {
|
||||||
|
if (node instanceof Label label && Objects.equals(label.getText(), title)) {
|
||||||
|
label.pseudoClassStateChanged(SELECTED, true);
|
||||||
|
selected = label;
|
||||||
|
break; // handle multiple entries share the same title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Label createEntry(Heading heading) {
|
||||||
|
var label = new Label(heading.title());
|
||||||
|
label.setWrapText(true);
|
||||||
|
label.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
label.setOnMouseClicked(e -> {
|
||||||
|
if (e.getClickCount() == 1 && e.getButton() == MouseButton.PRIMARY) {
|
||||||
|
clickHandler.accept(heading);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,33 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page;
|
package atlantafx.sampler.page;
|
||||||
|
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.event.BrowseEvent;
|
||||||
|
import atlantafx.sampler.event.DefaultEventBus;
|
||||||
|
import atlantafx.sampler.layout.ApplicationWindow;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.Hyperlink;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import net.datafaker.Faker;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
|
||||||
public interface Page {
|
public interface Page {
|
||||||
|
|
||||||
|
int MAX_WIDTH = ApplicationWindow.MIN_WIDTH - ApplicationWindow.SIDEBAR_WIDTH;
|
||||||
|
int HGAP_20 = 20;
|
||||||
|
int HGAP_30 = 30;
|
||||||
|
int VGAP_10 = 10;
|
||||||
|
int VGAP_20 = 20;
|
||||||
|
|
||||||
|
Faker FAKER = new Faker();
|
||||||
|
Random RANDOM = new Random();
|
||||||
int PAGE_HGAP = 30;
|
int PAGE_HGAP = 30;
|
||||||
int PAGE_VGAP = 30;
|
int PAGE_VGAP = 30;
|
||||||
|
|
||||||
@ -18,4 +41,35 @@ public interface Page {
|
|||||||
boolean canChangeThemeSettings();
|
boolean canChangeThemeSettings();
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
default <T> List<T> generate(Supplier<T> supplier, int count) {
|
||||||
|
return Stream.generate(supplier).limit(count).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
default Feather randomIcon() {
|
||||||
|
return Feather.values()[RANDOM.nextInt(Feather.values().length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
default Node createFormattedText(String text, boolean handleUrl) {
|
||||||
|
var node = BBCodeParser.createFormattedText(text);
|
||||||
|
|
||||||
|
if (handleUrl) {
|
||||||
|
node.addEventFilter(ActionEvent.ACTION, e -> {
|
||||||
|
if (e.getTarget() instanceof Hyperlink link && link.getUserData() != null) {
|
||||||
|
DefaultEventBus.getInstance().publish(
|
||||||
|
new BrowseEvent(URI.create((String) link.getUserData()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
e.consume();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
default Label captionLabel(String text) {
|
||||||
|
var label = new Label(text);
|
||||||
|
label.setStyle("-fx-font-family:monospace");
|
||||||
|
return label;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Random;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.shape.Polygon;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import net.datafaker.Faker;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
|
|
||||||
public class SampleBlock extends VBox {
|
|
||||||
|
|
||||||
public static final int BLOCK_HGAP = 20;
|
|
||||||
public static final int BLOCK_VGAP = 10;
|
|
||||||
|
|
||||||
protected static final Faker FAKER = new Faker();
|
|
||||||
protected static final Random RANDOM = new Random();
|
|
||||||
|
|
||||||
protected final Label titleLabel;
|
|
||||||
protected final Node content; // can be either Pane or Control
|
|
||||||
protected TextFlow descriptionText;
|
|
||||||
|
|
||||||
public SampleBlock(String title, Node content) {
|
|
||||||
this(title, content, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SampleBlock(String title, Node content, String description) {
|
|
||||||
titleLabel = new Label(Objects.requireNonNull(title));
|
|
||||||
|
|
||||||
Polygon polygon = new Polygon();
|
|
||||||
polygon.getPoints().addAll(0.0, 0.0, 20.0, 10.0, 20.0, 0.0);
|
|
||||||
polygon.getStyleClass().add("polygon");
|
|
||||||
|
|
||||||
var titleBox = new VBox(titleLabel, polygon);
|
|
||||||
titleBox.getStyleClass().add("title");
|
|
||||||
VBox.setMargin(titleBox, new Insets(-5, 0, 0, -40));
|
|
||||||
|
|
||||||
this.content = Objects.requireNonNull(content);
|
|
||||||
content.getStyleClass().add("content");
|
|
||||||
|
|
||||||
getChildren().setAll(titleBox, content);
|
|
||||||
if (description != null && !description.isBlank()) {
|
|
||||||
descriptionText = new TextFlow(new Text(description));
|
|
||||||
getChildren().add(descriptionText);
|
|
||||||
}
|
|
||||||
|
|
||||||
getStyleClass().add("sample-block");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return titleLabel.getText();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String text) {
|
|
||||||
titleLabel.setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFillHeight(boolean fillHeight) {
|
|
||||||
if (fillHeight) {
|
|
||||||
VBox.setVgrow(content, Priority.ALWAYS);
|
|
||||||
} else {
|
|
||||||
VBox.setVgrow(content, Priority.NEVER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Feather randomIcon() {
|
|
||||||
return Feather.values()[RANDOM.nextInt(Feather.values().length)];
|
|
||||||
}
|
|
||||||
}
|
|
59
sampler/src/main/java/atlantafx/sampler/page/Snippet.java
Normal file
59
sampler/src/main/java/atlantafx/sampler/page/Snippet.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
|
public class Snippet {
|
||||||
|
|
||||||
|
private final Class<?> sourceClass;
|
||||||
|
private final int id;
|
||||||
|
private HBox container = null;
|
||||||
|
|
||||||
|
public Snippet(Class<?> sourceClass, int id) {
|
||||||
|
this.sourceClass = Objects.requireNonNull(sourceClass, "sourceClass");
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node render() {
|
||||||
|
var snippet = getSourceCode();
|
||||||
|
if (container == null && !snippet.isBlank()) {
|
||||||
|
var textFlow = new TextFlow(BBSyntaxHighlighter.highlight(snippet));
|
||||||
|
HBox.setHgrow(textFlow, Priority.ALWAYS);
|
||||||
|
container = new HBox(textFlow);
|
||||||
|
container.getStyleClass().add("snippet");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Objects.requireNonNullElse(container, new TextFlow(new Text("Code snippet not found.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceCode() {
|
||||||
|
var sourceFileName = sourceClass.getSimpleName() + ".java";
|
||||||
|
try (var stream = sourceClass.getResourceAsStream(sourceFileName)) {
|
||||||
|
Objects.requireNonNull(stream, "Missing source file '" + sourceFileName + "';");
|
||||||
|
|
||||||
|
var sourceCode = new String(stream.readAllBytes());
|
||||||
|
var startTag = "//snippet_" + id + ":start";
|
||||||
|
var endTag = "//snippet_" + id + ":end";
|
||||||
|
var start = sourceCode.indexOf(startTag);
|
||||||
|
var end = sourceCode.indexOf(endTag);
|
||||||
|
|
||||||
|
var snippet = "";
|
||||||
|
if (start >= 0 && end >= 0) {
|
||||||
|
snippet = sourceCode.substring(start + startTag.length(), end)
|
||||||
|
.stripIndent()
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return snippet;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.DENSE;
|
|
||||||
import static atlantafx.base.theme.Styles.toggleStyleClass;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.base.theme.Tweaks;
|
import atlantafx.base.theme.Tweaks;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.Resources;
|
import atlantafx.sampler.Resources;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Accordion;
|
import javafx.scene.control.Accordion;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
@ -23,11 +23,12 @@ import javafx.scene.control.TitledPane;
|
|||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.scene.text.TextFlow;
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
public class AccordionPage extends AbstractPage {
|
public class AccordionPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Accordion";
|
public static final String NAME = "Accordion";
|
||||||
|
|
||||||
@ -36,70 +37,130 @@ public class AccordionPage extends AbstractPage {
|
|||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BooleanProperty expandedProperty = new SimpleBooleanProperty(true);
|
|
||||||
private final BooleanProperty animatedProperty = new SimpleBooleanProperty(true);
|
|
||||||
|
|
||||||
private final Accordion accordion;
|
|
||||||
|
|
||||||
public AccordionPage() {
|
public AccordionPage() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
accordion = createPlayground();
|
addFormattedText("""
|
||||||
var sample = new SampleBlock(
|
A user interface component that allows you to display a list of expandable \
|
||||||
"Playground",
|
items and only one item can be open at a time. Each item in the [i]Accordion[/i] \
|
||||||
new VBox(SampleBlock.BLOCK_VGAP, createControls(), accordion)
|
is made up of two parts, the header, and the content. The header is typically \
|
||||||
|
a text or graphic and the content can be any valid JavaFX node. When a user \
|
||||||
|
clicks on a header of an item, it will expand or collapse its content."""
|
||||||
);
|
);
|
||||||
sample.setFillHeight(true);
|
addSection("Usage", usageExample());
|
||||||
setUserContent(sample);
|
addSection("Dense", denseExample());
|
||||||
|
addSection("Alternative Icon", altIconExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
}
|
}
|
||||||
|
|
||||||
private HBox createControls() {
|
private ExampleBox usageExample() {
|
||||||
var animatedToggle = new ToggleSwitch("Animated");
|
//snippet_1:start
|
||||||
animatedProperty.bind(animatedToggle.selectedProperty());
|
Supplier<Node> gen = () -> {
|
||||||
animatedToggle.setSelected(true);
|
var text = FAKER.lorem().paragraph();
|
||||||
|
var textFlow = new TextFlow(new Text(text));
|
||||||
|
textFlow.setMinHeight(100);
|
||||||
|
VBox.setVgrow(textFlow, Priority.ALWAYS);
|
||||||
|
return new VBox(textFlow);
|
||||||
|
};
|
||||||
|
|
||||||
var expandedToggle = new ToggleSwitch("Always expanded");
|
var tp1 = new TitledPane("Item 1", gen.get());
|
||||||
expandedProperty.bind(expandedToggle.selectedProperty());
|
var tp2 = new TitledPane("Item 2", gen.get());
|
||||||
expandedToggle.setSelected(true);
|
var tp3 = new TitledPane("Item 3", gen.get());
|
||||||
|
var accordion = new Accordion(tp1, tp2, tp3);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
var denseToggle = new ToggleSwitch("Dense");
|
var description = BBCodeParser.createFormattedText("""
|
||||||
denseToggle.selectedProperty().addListener(
|
An [i]Accordion[/i] consists of a group of [i]TitlePanes[/i], \
|
||||||
(obs, old, val) -> accordion.getPanes().forEach(p -> toggleStyleClass(p, DENSE))
|
each of which can have its content expanded or collapsed."""
|
||||||
);
|
);
|
||||||
|
|
||||||
var altIconToggle = new ToggleSwitch("Alt icon");
|
return new ExampleBox(accordion, new Snippet(getClass(), 1), description);
|
||||||
altIconToggle.selectedProperty().addListener(
|
|
||||||
(obs, old, val) -> accordion.getPanes().forEach(p -> toggleStyleClass(p, Tweaks.ALT_ICON))
|
|
||||||
);
|
|
||||||
|
|
||||||
var controls = new HBox(
|
|
||||||
BLOCK_HGAP,
|
|
||||||
animatedToggle,
|
|
||||||
expandedToggle,
|
|
||||||
denseToggle,
|
|
||||||
altIconToggle
|
|
||||||
);
|
|
||||||
controls.setAlignment(Pos.CENTER);
|
|
||||||
controls.setPadding(new Insets(0, 0, 0, 2));
|
|
||||||
|
|
||||||
return controls;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Accordion createPlayground() {
|
private ExampleBox denseExample() {
|
||||||
var textBlockContent = new Label(FAKER.chuckNorris().fact());
|
//snippet_2:start
|
||||||
var textBlock = new TitledPane("_Quote", textBlockContent);
|
Supplier<Node> gen = () -> {
|
||||||
|
var text = FAKER.lorem().paragraph();
|
||||||
|
var textFlow = new TextFlow(new Text(text));
|
||||||
|
textFlow.setMinHeight(100);
|
||||||
|
VBox.setVgrow(textFlow, Priority.ALWAYS);
|
||||||
|
return new VBox(textFlow);
|
||||||
|
};
|
||||||
|
|
||||||
|
var tp1 = new TitledPane("Item 1", gen.get());
|
||||||
|
tp1.getStyleClass().add(Styles.DENSE);
|
||||||
|
|
||||||
|
var tp2 = new TitledPane("Item 2", gen.get());
|
||||||
|
tp2.getStyleClass().add(Styles.DENSE);
|
||||||
|
|
||||||
|
var tp3 = new TitledPane("Item 3", gen.get());
|
||||||
|
tp3.getStyleClass().add(Styles.DENSE);
|
||||||
|
|
||||||
|
var accordion = new Accordion(tp1, tp2, tp3);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
If you need more compact view there's [code]Styles.DENSE[/code] for that."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(accordion, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox altIconExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
Supplier<Node> gen = () -> {
|
||||||
|
var text = FAKER.lorem().paragraph();
|
||||||
|
var textFlow = new TextFlow(new Text(text));
|
||||||
|
textFlow.setMinHeight(100);
|
||||||
|
VBox.setVgrow(textFlow, Priority.ALWAYS);
|
||||||
|
return new VBox(textFlow);
|
||||||
|
};
|
||||||
|
|
||||||
|
var tp1 = new TitledPane("Item 1", gen.get());
|
||||||
|
tp1.getStyleClass().add(Tweaks.ALT_ICON);
|
||||||
|
|
||||||
|
var tp2 = new TitledPane("Item 2", gen.get());
|
||||||
|
tp2.getStyleClass().add(Tweaks.ALT_ICON);
|
||||||
|
|
||||||
|
var tp3 = new TitledPane("Item 3", gen.get());
|
||||||
|
tp3.getStyleClass().add(Tweaks.ALT_ICON);
|
||||||
|
|
||||||
|
var accordion = new Accordion(tp1, tp2, tp3);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
There's also additional tweak [code]Tweaks.ALT_ICON[/code] to change header \
|
||||||
|
arrow icon to the classic style."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(accordion, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private VBox playground() {
|
||||||
|
final var expandedProperty = new SimpleBooleanProperty(true);
|
||||||
|
final var animatedProperty = new SimpleBooleanProperty(true);
|
||||||
|
|
||||||
|
// == ACCORDION ==
|
||||||
|
|
||||||
|
var labelBlock = new TitledPane("_Quote", new Label(FAKER.chuckNorris().fact()));
|
||||||
|
labelBlock.setMnemonicParsing(true);
|
||||||
|
labelBlock.animatedProperty().bind(animatedProperty);
|
||||||
|
|
||||||
|
var textFlow = new TextFlow(new Text(
|
||||||
|
String.join("\n\n", FAKER.lorem().paragraphs(10)))
|
||||||
|
);
|
||||||
|
textFlow.setPadding(new Insets(0, 10, 0, 0));
|
||||||
|
var textScroll = new ScrollPane(textFlow);
|
||||||
|
textScroll.setMinHeight(200);
|
||||||
|
textScroll.setFitToWidth(true);
|
||||||
|
var textBlock = new TitledPane("_Scrollable Text", textScroll);
|
||||||
textBlock.setMnemonicParsing(true);
|
textBlock.setMnemonicParsing(true);
|
||||||
textBlock.animatedProperty().bind(animatedProperty);
|
textBlock.animatedProperty().bind(animatedProperty);
|
||||||
|
|
||||||
var textFlow = new TextFlow(new Text(String.join("\n\n", FAKER.lorem().paragraphs(10))));
|
|
||||||
textFlow.setPadding(new Insets(0, 10, 0, 0));
|
|
||||||
var scrollTextBlockContent = new ScrollPane(textFlow);
|
|
||||||
scrollTextBlockContent.setMinHeight(200);
|
|
||||||
scrollTextBlockContent.setFitToWidth(true);
|
|
||||||
var scrollableTextBlock = new TitledPane("_Scrollable Text", scrollTextBlockContent);
|
|
||||||
scrollableTextBlock.setMnemonicParsing(true);
|
|
||||||
scrollableTextBlock.animatedProperty().bind(animatedProperty);
|
|
||||||
|
|
||||||
var disabledBlock = new TitledPane("Disabled Block", null);
|
var disabledBlock = new TitledPane("Disabled Block", null);
|
||||||
disabledBlock.setDisable(true);
|
disabledBlock.setDisable(true);
|
||||||
|
|
||||||
@ -110,14 +171,7 @@ public class AccordionPage extends AbstractPage {
|
|||||||
imageBlock.animatedProperty().bind(animatedProperty);
|
imageBlock.animatedProperty().bind(animatedProperty);
|
||||||
imageBlock.setMnemonicParsing(true);
|
imageBlock.setMnemonicParsing(true);
|
||||||
|
|
||||||
// ~
|
var accordion = new Accordion(labelBlock, textBlock, disabledBlock, imageBlock);
|
||||||
|
|
||||||
var accordion = new Accordion(
|
|
||||||
textBlock,
|
|
||||||
scrollableTextBlock,
|
|
||||||
disabledBlock,
|
|
||||||
imageBlock
|
|
||||||
);
|
|
||||||
|
|
||||||
// prevents accordion from being completely collapsed
|
// prevents accordion from being completely collapsed
|
||||||
accordion.expandedPaneProperty().addListener((obs, old, val) -> {
|
accordion.expandedPaneProperty().addListener((obs, old, val) -> {
|
||||||
@ -128,6 +182,35 @@ public class AccordionPage extends AbstractPage {
|
|||||||
});
|
});
|
||||||
accordion.setExpandedPane(accordion.getPanes().get(1));
|
accordion.setExpandedPane(accordion.getPanes().get(1));
|
||||||
|
|
||||||
return accordion;
|
// == TOGGLES ==
|
||||||
|
|
||||||
|
var animatedToggle = new ToggleSwitch("Animated");
|
||||||
|
animatedProperty.bind(animatedToggle.selectedProperty());
|
||||||
|
animatedToggle.setSelected(true);
|
||||||
|
|
||||||
|
var expandedToggle = new ToggleSwitch("Always expanded");
|
||||||
|
expandedProperty.bind(expandedToggle.selectedProperty());
|
||||||
|
expandedToggle.setSelected(true);
|
||||||
|
|
||||||
|
var denseToggle = new ToggleSwitch("Dense");
|
||||||
|
denseToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> accordion.getPanes().forEach(p -> Styles.toggleStyleClass(p, Styles.DENSE))
|
||||||
|
);
|
||||||
|
|
||||||
|
var altIconToggle = new ToggleSwitch("Alt icon");
|
||||||
|
altIconToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> accordion.getPanes().forEach(p -> Styles.toggleStyleClass(p, Tweaks.ALT_ICON))
|
||||||
|
);
|
||||||
|
|
||||||
|
var controls = new HBox(HGAP_20, animatedToggle, expandedToggle, denseToggle, altIconToggle);
|
||||||
|
controls.setAlignment(Pos.CENTER);
|
||||||
|
controls.setPadding(new Insets(0, 0, 0, 2));
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The playground demonstrates the most important [i]Accordion[/i] features \
|
||||||
|
and also serves as an object for monkey testing."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new VBox(VGAP_10, description, accordion, controls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.Breadcrumbs;
|
|
||||||
import atlantafx.base.controls.Breadcrumbs.BreadCrumbItem;
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.ButtonBase;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.util.Callback;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.material2.Material2AL;
|
|
||||||
|
|
||||||
public class BreadcrumbsPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "Breadcrumbs";
|
|
||||||
|
|
||||||
private static final int CRUMB_COUNT = 5;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BreadcrumbsPage() {
|
|
||||||
super();
|
|
||||||
setUserContent(new VBox(
|
|
||||||
Page.PAGE_VGAP,
|
|
||||||
basicSample(),
|
|
||||||
customCrumbSample(),
|
|
||||||
customDividerSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
|
||||||
return new SampleBlock("Basic", createBreadcrumbs(null, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock customCrumbSample() {
|
|
||||||
Callback<BreadCrumbItem<String>, ButtonBase> crumbFactory = crumb -> {
|
|
||||||
var btn = new Button(crumb.getValue(), new FontIcon(randomIcon()));
|
|
||||||
btn.getStyleClass().add(Styles.FLAT);
|
|
||||||
btn.setFocusTraversable(false);
|
|
||||||
return btn;
|
|
||||||
};
|
|
||||||
|
|
||||||
return new SampleBlock("Flat Button", createBreadcrumbs(crumbFactory, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock customDividerSample() {
|
|
||||||
Callback<BreadCrumbItem<String>, ? extends Node> dividerFactory = item -> {
|
|
||||||
if (item == null) {
|
|
||||||
return new Label("", new FontIcon(Material2AL.HOME));
|
|
||||||
}
|
|
||||||
return !item.isLast() ? new Label("", new FontIcon(Material2AL.CHEVRON_RIGHT)) : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return new SampleBlock("Custom Divider", createBreadcrumbs(null, dividerFactory));
|
|
||||||
}
|
|
||||||
|
|
||||||
private HBox createBreadcrumbs(Callback<BreadCrumbItem<String>, ButtonBase> crumbFactory,
|
|
||||||
Callback<BreadCrumbItem<String>, ? extends Node> dividerFactory) {
|
|
||||||
BreadCrumbItem<String> model = Breadcrumbs.buildTreeModel(
|
|
||||||
generate(() -> FAKER.science().element(), CRUMB_COUNT).toArray(String[]::new)
|
|
||||||
);
|
|
||||||
|
|
||||||
var nextBtn = new Button("Next");
|
|
||||||
nextBtn.getStyleClass().addAll(Styles.ACCENT);
|
|
||||||
|
|
||||||
var breadcrumbs = new Breadcrumbs<>(model);
|
|
||||||
breadcrumbs.setSelectedCrumb(getAncestor(model, CRUMB_COUNT / 2));
|
|
||||||
if (crumbFactory != null) {
|
|
||||||
breadcrumbs.setCrumbFactory(crumbFactory);
|
|
||||||
}
|
|
||||||
if (dividerFactory != null) {
|
|
||||||
breadcrumbs.setDividerFactory(dividerFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
nextBtn.setOnAction(e -> {
|
|
||||||
BreadCrumbItem<String> selected = breadcrumbs.getSelectedCrumb();
|
|
||||||
if (selected.getChildren().size() > 0) {
|
|
||||||
breadcrumbs.setSelectedCrumb((BreadCrumbItem<String>) selected.getChildren().get(0));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
breadcrumbs.selectedCrumbProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null) {
|
|
||||||
nextBtn.setDisable(val.getChildren().isEmpty());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var box = new HBox(40, nextBtn, breadcrumbs);
|
|
||||||
box.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> BreadCrumbItem<T> getAncestor(BreadCrumbItem<T> node, int height) {
|
|
||||||
var counter = height;
|
|
||||||
var current = node;
|
|
||||||
while (counter > 0 && current.getParent() != null) {
|
|
||||||
current = (BreadCrumbItem<T>) current.getParent();
|
|
||||||
counter--;
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,33 +2,23 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.ACCENT;
|
import atlantafx.base.theme.Styles;
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_CIRCLE;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_OUTLINED;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import static atlantafx.base.theme.Styles.DANGER;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import static atlantafx.base.theme.Styles.FLAT;
|
|
||||||
import static atlantafx.base.theme.Styles.LARGE;
|
|
||||||
import static atlantafx.base.theme.Styles.ROUNDED;
|
|
||||||
import static atlantafx.base.theme.Styles.SMALL;
|
|
||||||
import static atlantafx.base.theme.Styles.SUCCESS;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
import atlantafx.sampler.theme.CSSFragment;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
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.Material2AL;
|
import org.kordamp.ikonli.material2.Material2AL;
|
||||||
|
|
||||||
public class ButtonPage extends AbstractPage {
|
public class ButtonPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Button";
|
public static final String NAME = "Button";
|
||||||
|
|
||||||
@ -39,223 +29,382 @@ public class ButtonPage extends AbstractPage {
|
|||||||
|
|
||||||
public ButtonPage() {
|
public ButtonPage() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
|
addFormattedText("""
|
||||||
|
A simple button control. The button control can contain text and/or a graphic.
|
||||||
|
A button control has three different modes:
|
||||||
|
|
||||||
|
[ul]
|
||||||
|
[li]Normal: A normal push button.[/li]
|
||||||
|
[li]Default: A default button is the button that receives a keyboard [code]VK_ENTER[/code] press,
|
||||||
|
if no other node in the scene consumes it.[/li]
|
||||||
|
[li]Cancel: A cancel button is the button that receives a keyboard [code]VK_ESC[/code] press,
|
||||||
|
if no other node in the scene consumes it.[/li][/ul]"""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Colored", coloredButtonExample());
|
||||||
|
addSection("Icon Button", iconButtonExample());
|
||||||
|
addSection("Circular", circularButtonExample());
|
||||||
|
addSection("Outlined", outlinedButtonExample());
|
||||||
|
addSection("Rounded", roundedButtonExample());
|
||||||
|
addSection("Button Size", buttonSizeExample());
|
||||||
|
addSection("Custom Styles", customColorExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private ExampleBox usageExample() {
|
||||||
var grid = new GridPane();
|
//snippet_1:start
|
||||||
grid.setHgap(Page.PAGE_HGAP);
|
var normalBtn = new Button("_Normal");
|
||||||
grid.setVgap(Page.PAGE_VGAP);
|
normalBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
grid.add(basicSample(), 0, 0);
|
|
||||||
grid.add(iconButtonSample(), 1, 0);
|
|
||||||
|
|
||||||
grid.add(coloredSample(), 0, 1);
|
|
||||||
grid.add(circularSample(), 1, 1);
|
|
||||||
|
|
||||||
grid.add(outlinedSample(), 0, 2);
|
|
||||||
grid.add(sizeSample(), 1, 2);
|
|
||||||
|
|
||||||
grid.add(roundedSample(), 0, 3);
|
|
||||||
grid.add(customColorSample(), 1, 3);
|
|
||||||
|
|
||||||
grid.add(disabledSample(), 0, 4);
|
|
||||||
|
|
||||||
setUserContent(grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
|
||||||
var basicBtn = new Button("_Basic");
|
|
||||||
basicBtn.setMnemonicParsing(true);
|
|
||||||
|
|
||||||
var defaultBtn = new Button("_Default");
|
var defaultBtn = new Button("_Default");
|
||||||
defaultBtn.setDefaultButton(true);
|
defaultBtn.setDefaultButton(true);
|
||||||
defaultBtn.setMnemonicParsing(true);
|
defaultBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var flatBtn = new Button("_Flat");
|
var outlinedBtn = new Button("Out_lined");
|
||||||
flatBtn.getStyleClass().add(FLAT);
|
outlinedBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_OUTLINED, Styles.ACCENT
|
||||||
|
);
|
||||||
|
outlinedBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, basicBtn, defaultBtn, flatBtn);
|
var flatBtn = new Button("_Flat");
|
||||||
return new SampleBlock("Basic", content);
|
flatBtn.getStyleClass().add(Styles.FLAT);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, normalBtn, defaultBtn, outlinedBtn, flatBtn);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]Button[/i] comes with four CSS variants: normal (default), colored, \
|
||||||
|
outlined, and flat (or text). To change the appearance of the [i]Button[/i], \
|
||||||
|
you set the corresponding style classes that work as modifiers."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock coloredSample() {
|
private ExampleBox coloredButtonExample() {
|
||||||
|
//snippet_2:start
|
||||||
var accentBtn = new Button("_Accent");
|
var accentBtn = new Button("_Accent");
|
||||||
accentBtn.getStyleClass().add(ACCENT);
|
accentBtn.getStyleClass().add(Styles.ACCENT);
|
||||||
accentBtn.setMnemonicParsing(true);
|
accentBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var successBtn = new Button("_Success", new FontIcon(Feather.CHECK));
|
var successBtn = new Button(
|
||||||
successBtn.getStyleClass().add(SUCCESS);
|
"_Success", new FontIcon(Feather.CHECK)
|
||||||
|
);
|
||||||
|
successBtn.getStyleClass().add(Styles.SUCCESS);
|
||||||
successBtn.setMnemonicParsing(true);
|
successBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var dangerBtn = new Button("Da_nger", new FontIcon(Feather.TRASH));
|
var dangerBtn = new Button(
|
||||||
dangerBtn.getStyleClass().add(DANGER);
|
"Da_nger", new FontIcon(Feather.TRASH)
|
||||||
|
);
|
||||||
|
dangerBtn.getStyleClass().add(Styles.DANGER);
|
||||||
dangerBtn.setContentDisplay(ContentDisplay.RIGHT);
|
dangerBtn.setContentDisplay(ContentDisplay.RIGHT);
|
||||||
dangerBtn.setMnemonicParsing(true);
|
dangerBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, accentBtn, successBtn, dangerBtn);
|
// ~
|
||||||
return new SampleBlock("Colored", content);
|
var accentOutBtn = new Button("Accen_t");
|
||||||
|
accentOutBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_OUTLINED, Styles.ACCENT
|
||||||
|
);
|
||||||
|
accentOutBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
var successOutBtn = new Button(
|
||||||
|
"S_uccess", new FontIcon(Feather.CHECK)
|
||||||
|
);
|
||||||
|
successOutBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_OUTLINED, Styles.SUCCESS
|
||||||
|
);
|
||||||
|
successOutBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
var dangerOutBtn = new Button(
|
||||||
|
"Dan_ger", new FontIcon(Feather.TRASH)
|
||||||
|
);
|
||||||
|
dangerOutBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_OUTLINED, Styles.DANGER
|
||||||
|
);
|
||||||
|
dangerOutBtn.setContentDisplay(ContentDisplay.RIGHT);
|
||||||
|
dangerOutBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var accentFlatBtn = new Button("Accen_t");
|
||||||
|
accentFlatBtn.getStyleClass().addAll(
|
||||||
|
Styles.FLAT, Styles.ACCENT
|
||||||
|
);
|
||||||
|
accentFlatBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
var successFlatBtn = new Button(
|
||||||
|
"S_uccess", new FontIcon(Feather.CHECK)
|
||||||
|
);
|
||||||
|
successFlatBtn.getStyleClass().addAll(
|
||||||
|
Styles.FLAT, Styles.SUCCESS
|
||||||
|
);
|
||||||
|
successFlatBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
var dangerFlatBtn = new Button(
|
||||||
|
"Dan_ger", new FontIcon(Feather.TRASH)
|
||||||
|
);
|
||||||
|
dangerFlatBtn.getStyleClass().addAll(
|
||||||
|
Styles.FLAT, Styles.DANGER
|
||||||
|
);
|
||||||
|
dangerFlatBtn.setContentDisplay(ContentDisplay.RIGHT);
|
||||||
|
dangerFlatBtn.setMnemonicParsing(true);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new VBox(
|
||||||
|
VGAP_20,
|
||||||
|
new HBox(HGAP_20, accentBtn, successBtn, dangerBtn),
|
||||||
|
new HBox(HGAP_20, accentOutBtn, successOutBtn, dangerOutBtn),
|
||||||
|
new HBox(HGAP_20, accentFlatBtn, successFlatBtn, dangerFlatBtn)
|
||||||
|
);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can change the [i]Button[/i] color simply by using predefined style class modifiers."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock iconButtonSample() {
|
private ExampleBox iconButtonExample() {
|
||||||
var basicBtn = new Button("", new FontIcon(Feather.MORE_HORIZONTAL));
|
//snippet_3:start
|
||||||
basicBtn.getStyleClass().addAll(BUTTON_ICON);
|
var normalBtn = new Button("", new FontIcon(Feather.MORE_HORIZONTAL));
|
||||||
|
normalBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
|
|
||||||
var accentBtn = new Button("", new FontIcon(Feather.MENU));
|
var accentBtn = new Button("", new FontIcon(Feather.MENU));
|
||||||
accentBtn.getStyleClass().addAll(BUTTON_ICON, ACCENT);
|
accentBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.ACCENT
|
||||||
|
);
|
||||||
|
|
||||||
var successBtn = new Button("", new FontIcon(Feather.CHECK));
|
var successBtn = new Button("", new FontIcon(Feather.CHECK));
|
||||||
successBtn.getStyleClass().addAll(BUTTON_ICON, SUCCESS);
|
successBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.SUCCESS
|
||||||
|
);
|
||||||
|
|
||||||
var dangerBtn = new Button("", new FontIcon(Feather.TRASH));
|
var dangerBtn = new Button("", new FontIcon(Feather.TRASH));
|
||||||
dangerBtn.getStyleClass().addAll(BUTTON_ICON, BUTTON_OUTLINED, DANGER);
|
dangerBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.BUTTON_OUTLINED, Styles.DANGER
|
||||||
|
);
|
||||||
|
|
||||||
var flatAccentBtn = new Button("", new FontIcon(Feather.MIC));
|
var flatAccentBtn = new Button("", new FontIcon(Feather.MIC));
|
||||||
flatAccentBtn.getStyleClass().addAll(BUTTON_ICON, FLAT, ACCENT);
|
flatAccentBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.FLAT, Styles.ACCENT
|
||||||
|
);
|
||||||
|
|
||||||
var flatSuccessBtn = new Button("", new FontIcon(Feather.USER));
|
var flatSuccessBtn = new Button("", new FontIcon(Feather.USER));
|
||||||
flatSuccessBtn.getStyleClass().addAll(BUTTON_ICON, FLAT, SUCCESS);
|
flatSuccessBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.FLAT, Styles.SUCCESS
|
||||||
|
);
|
||||||
|
|
||||||
var flatDangerBtn = new Button("", new FontIcon(Feather.CROSSHAIR));
|
var flatDangerBtn = new Button("", new FontIcon(Feather.CROSSHAIR));
|
||||||
flatDangerBtn.getStyleClass().addAll(BUTTON_ICON, FLAT, DANGER);
|
flatDangerBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.FLAT, Styles.DANGER
|
||||||
|
);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP,
|
var box = new HBox(HGAP_20,
|
||||||
basicBtn, accentBtn, successBtn, dangerBtn,
|
normalBtn, accentBtn, successBtn, dangerBtn,
|
||||||
flatAccentBtn, flatSuccessBtn, flatDangerBtn
|
flatAccentBtn, flatSuccessBtn, flatDangerBtn
|
||||||
);
|
);
|
||||||
return new SampleBlock("Icon", content);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Icon buttons are present in two variants. The first one is just a \
|
||||||
|
normal [i]Button[/i] but with no text and the second one is a flat button - \
|
||||||
|
suitable for toolbars and similar controls."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock circularSample() {
|
private ExampleBox circularButtonExample() {
|
||||||
var basicBtn = new Button("", new FontIcon(Feather.MORE_HORIZONTAL));
|
//snippet_4:start
|
||||||
basicBtn.getStyleClass().addAll(BUTTON_CIRCLE);
|
var normalBtn = new Button("", new FontIcon(Feather.MORE_HORIZONTAL));
|
||||||
basicBtn.setShape(new Circle(50));
|
normalBtn.getStyleClass().addAll(Styles.BUTTON_CIRCLE);
|
||||||
|
normalBtn.setShape(new Circle(50));
|
||||||
|
|
||||||
var accentBtn = new Button("", new FontIcon(Feather.MENU));
|
var accentBtn = new Button("", new FontIcon(Feather.MENU));
|
||||||
accentBtn.getStyleClass().addAll(BUTTON_CIRCLE, ACCENT);
|
accentBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_CIRCLE, Styles.ACCENT
|
||||||
|
);
|
||||||
accentBtn.setShape(new Circle(50));
|
accentBtn.setShape(new Circle(50));
|
||||||
|
|
||||||
var successBtn = new Button("", new FontIcon(Feather.CHECK));
|
var successBtn = new Button("", new FontIcon(Feather.CHECK));
|
||||||
successBtn.getStyleClass().addAll(BUTTON_CIRCLE, SUCCESS);
|
successBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_CIRCLE, Styles.SUCCESS
|
||||||
|
);
|
||||||
successBtn.setShape(new Circle(50));
|
successBtn.setShape(new Circle(50));
|
||||||
|
|
||||||
var dangerBtn = new Button("", new FontIcon(Feather.TRASH));
|
var dangerBtn = new Button("", new FontIcon(Feather.TRASH));
|
||||||
dangerBtn.getStyleClass().addAll(BUTTON_CIRCLE, BUTTON_OUTLINED, DANGER);
|
dangerBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_CIRCLE, Styles.BUTTON_OUTLINED, Styles.DANGER
|
||||||
|
);
|
||||||
dangerBtn.setShape(new Circle(50));
|
dangerBtn.setShape(new Circle(50));
|
||||||
|
|
||||||
var flatAccentBtn = new Button("", new FontIcon(Feather.MIC));
|
var flatAccentBtn = new Button("", new FontIcon(Feather.MIC));
|
||||||
flatAccentBtn.getStyleClass().addAll(BUTTON_CIRCLE, FLAT, ACCENT);
|
flatAccentBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_CIRCLE, Styles.FLAT, Styles.ACCENT
|
||||||
|
);
|
||||||
flatAccentBtn.setShape(new Circle(50));
|
flatAccentBtn.setShape(new Circle(50));
|
||||||
|
|
||||||
var flatSuccessBtn = new Button("", new FontIcon(Feather.USER));
|
var flatSuccessBtn = new Button("", new FontIcon(Feather.USER));
|
||||||
flatSuccessBtn.getStyleClass().addAll(BUTTON_CIRCLE, FLAT, SUCCESS);
|
flatSuccessBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_CIRCLE, Styles.FLAT, Styles.SUCCESS
|
||||||
|
);
|
||||||
flatSuccessBtn.setShape(new Circle(50));
|
flatSuccessBtn.setShape(new Circle(50));
|
||||||
|
|
||||||
var flatDangerBtn = new Button("", new FontIcon(Feather.CROSSHAIR));
|
var flatDangerBtn = new Button("", new FontIcon(Feather.CROSSHAIR));
|
||||||
flatDangerBtn.getStyleClass().addAll(BUTTON_CIRCLE, FLAT, DANGER);
|
flatDangerBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_CIRCLE, Styles.FLAT, Styles.DANGER
|
||||||
|
);
|
||||||
flatDangerBtn.setShape(new Circle(50));
|
flatDangerBtn.setShape(new Circle(50));
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP,
|
var box = new HBox(HGAP_20,
|
||||||
basicBtn, accentBtn, successBtn, dangerBtn,
|
normalBtn, accentBtn, successBtn, dangerBtn,
|
||||||
flatAccentBtn, flatSuccessBtn, flatDangerBtn
|
flatAccentBtn, flatSuccessBtn, flatDangerBtn
|
||||||
);
|
);
|
||||||
return new SampleBlock("Circular", content);
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can also apply the [code]setShape()[/code] method to make the \
|
||||||
|
[i]Button[/i] look circular.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock outlinedSample() {
|
private ExampleBox outlinedButtonExample() {
|
||||||
var accentBtn = new Button("Accen_t");
|
//snippet_5:start
|
||||||
accentBtn.getStyleClass().addAll(BUTTON_OUTLINED, ACCENT);
|
var accentOutBtn = new Button("Accen_t");
|
||||||
accentBtn.setMnemonicParsing(true);
|
accentOutBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_OUTLINED, Styles.ACCENT
|
||||||
|
);
|
||||||
|
accentOutBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var successBtn = new Button("S_uccess", new FontIcon(Feather.CHECK));
|
var successOutBtn = new Button("S_uccess", new FontIcon(Feather.CHECK));
|
||||||
successBtn.getStyleClass().addAll(BUTTON_OUTLINED, SUCCESS);
|
successOutBtn.getStyleClass().addAll(
|
||||||
successBtn.setMnemonicParsing(true);
|
Styles.BUTTON_OUTLINED, Styles.SUCCESS
|
||||||
|
);
|
||||||
|
successOutBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var dangerBtn = new Button("Dan_ger", new FontIcon(Feather.TRASH));
|
var dangerOutBtn = new Button("Dan_ger", new FontIcon(Feather.TRASH));
|
||||||
dangerBtn.getStyleClass().addAll(BUTTON_OUTLINED, DANGER);
|
dangerOutBtn.getStyleClass().addAll(
|
||||||
dangerBtn.setContentDisplay(ContentDisplay.RIGHT);
|
Styles.BUTTON_OUTLINED, Styles.DANGER
|
||||||
dangerBtn.setMnemonicParsing(true);
|
);
|
||||||
|
dangerOutBtn.setContentDisplay(ContentDisplay.RIGHT);
|
||||||
|
dangerOutBtn.setMnemonicParsing(true);
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, accentBtn, successBtn, dangerBtn);
|
var box = new HBox(HGAP_20, accentOutBtn, successOutBtn, dangerOutBtn);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Outlined buttons are medium-emphasis buttons. They contain actions that are \
|
||||||
|
important but aren't the primary action in an app."""
|
||||||
|
);
|
||||||
|
|
||||||
return new SampleBlock("Outlined", content);
|
return new ExampleBox(box, new Snippet(getClass(), 5), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock roundedSample() {
|
private ExampleBox roundedButtonExample() {
|
||||||
var basicBtn = new Button("Basic");
|
//snippet_6:start
|
||||||
basicBtn.getStyleClass().addAll(SMALL, ROUNDED);
|
var normalBtn = new Button("Normal");
|
||||||
|
normalBtn.getStyleClass().addAll(
|
||||||
|
Styles.SMALL, Styles.ROUNDED
|
||||||
|
);
|
||||||
|
|
||||||
var accentBtn = new Button("Accent");
|
var accentBtn = new Button("Accent");
|
||||||
accentBtn.getStyleClass().addAll(ROUNDED, ACCENT);
|
accentBtn.getStyleClass().addAll(
|
||||||
|
Styles.ROUNDED, Styles.ACCENT
|
||||||
|
);
|
||||||
|
|
||||||
var successBtn = new Button("Success", new FontIcon(Feather.CHECK));
|
var successBtn = new Button("Success", new FontIcon(Feather.CHECK));
|
||||||
successBtn.getStyleClass().addAll(LARGE, ROUNDED, BUTTON_OUTLINED, SUCCESS);
|
successBtn.getStyleClass().addAll(
|
||||||
|
Styles.LARGE, Styles.ROUNDED, Styles.BUTTON_OUTLINED, Styles.SUCCESS
|
||||||
|
);
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, basicBtn, accentBtn, successBtn);
|
var box = new HBox(HGAP_20, normalBtn, accentBtn, successBtn);
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
return new SampleBlock("Rounded", content);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
[i]Button[/i] corners can be rounded with the [code]Styles.ROUNDED[/code] \
|
||||||
|
style class modifier."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 6), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock sizeSample() {
|
private ExampleBox buttonSizeExample() {
|
||||||
|
//snippet_7:start
|
||||||
var smallBtn = new Button("Small");
|
var smallBtn = new Button("Small");
|
||||||
smallBtn.getStyleClass().addAll(SMALL);
|
smallBtn.getStyleClass().addAll(Styles.SMALL);
|
||||||
|
|
||||||
var normalBtn = new Button("Normal");
|
var normalBtn = new Button("Normal");
|
||||||
|
|
||||||
var largeBtn = new Button("Large");
|
var largeBtn = new Button("Large");
|
||||||
largeBtn.getStyleClass().addAll(LARGE);
|
largeBtn.getStyleClass().addAll(Styles.LARGE);
|
||||||
|
//snippet_7:end
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, smallBtn, normalBtn, largeBtn);
|
var box = new HBox(HGAP_20, smallBtn, normalBtn, largeBtn);
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
return new SampleBlock("Size", content);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
For larger or smaller buttons, use the [code]Styles.SMALL[/code] or \
|
||||||
|
[code]Styles.LARGE[/code] style classes, respectively."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 7), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock customColorSample() {
|
private ExampleBox customColorExample() {
|
||||||
var btn = new Button("DO SOMETHING!");
|
var customStyle = """
|
||||||
btn.getStyleClass().addAll(SUCCESS, LARGE);
|
-color-button-bg: linear-gradient(
|
||||||
btn.setStyle("""
|
to bottom right, -color-success-emphasis, darkblue
|
||||||
-color-button-bg: linear-gradient(to bottom right, -color-success-emphasis, darkblue);
|
);
|
||||||
-color-button-bg-hover: -color-button-bg;
|
-color-button-bg-hover: -color-button-bg;
|
||||||
-color-button-bg-focused: -color-button-bg;
|
-color-button-bg-focused: -color-button-bg;
|
||||||
-color-button-bg-pressed: -color-button-bg;
|
-color-button-bg-pressed: -color-button-bg;""";
|
||||||
""");
|
|
||||||
|
|
||||||
var iconBtn = new Button("", new FontIcon(Material2AL.FAVORITE));
|
String dataClass = """
|
||||||
iconBtn.getStyleClass().addAll("favorite-button", BUTTON_CIRCLE, FLAT, DANGER);
|
|
||||||
new CSSFragment("""
|
|
||||||
.favorite-button.button >.ikonli-font-icon {
|
.favorite-button.button >.ikonli-font-icon {
|
||||||
-fx-fill: linear-gradient(to bottom right, pink, -color-danger-emphasis);
|
-fx-fill: linear-gradient(
|
||||||
-fx-icon-color: linear-gradient(to bottom right, pink, -color-danger-emphasis);
|
to bottom right, pink, -color-danger-emphasis
|
||||||
|
);
|
||||||
|
-fx-icon-color: linear-gradient(
|
||||||
|
to bottom right, pink, -color-danger-emphasis
|
||||||
|
);
|
||||||
-fx-font-size: 32px;
|
-fx-font-size: 32px;
|
||||||
-fx-icon-size: 32px;
|
-fx-icon-size: 32px;
|
||||||
}
|
}""";
|
||||||
""").addTo(iconBtn);
|
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, btn, iconBtn);
|
//snippet_8:start
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
var btn = new Button("DO SOMETHING!");
|
||||||
|
btn.getStyleClass().addAll(Styles.SUCCESS, Styles.LARGE);
|
||||||
|
// -color-button-bg: linear-gradient(
|
||||||
|
// to bottom right, -color-success-emphasis, darkblue
|
||||||
|
// );
|
||||||
|
// -color-button-bg-hover: -color-button-bg;
|
||||||
|
// -color-button-bg-focused: -color-button-bg;
|
||||||
|
// -color-button-bg-pressed: -color-button-bg;
|
||||||
|
btn.setStyle(customStyle);
|
||||||
|
|
||||||
return new SampleBlock("Custom Color", content);
|
var iconBtn = new Button("", new FontIcon(Material2AL.FAVORITE));
|
||||||
}
|
iconBtn.getStyleClass().addAll(
|
||||||
|
"favorite-button",
|
||||||
|
Styles.BUTTON_CIRCLE, Styles.FLAT, Styles.DANGER
|
||||||
|
);
|
||||||
|
// .favorite-button.button >.ikonli-font-icon {
|
||||||
|
// -fx-fill: linear-gradient(
|
||||||
|
// to bottom right, pink, -color-danger-emphasis
|
||||||
|
// );
|
||||||
|
// -fx-icon-color: linear-gradient(
|
||||||
|
// to bottom right, pink, -color-danger-emphasis
|
||||||
|
// );
|
||||||
|
// -fx-font-size: 32px;
|
||||||
|
// -fx-icon-size: 32px;
|
||||||
|
// }
|
||||||
|
new CSSFragment(dataClass).addTo(iconBtn);
|
||||||
|
//snippet_8:end
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
var box = new HBox(HGAP_20, btn, iconBtn);
|
||||||
var basicBtn = new Button("Basic");
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
basicBtn.setDisable(true);
|
|
||||||
|
|
||||||
var defaultBtn = new Button("Default");
|
var description = BBCodeParser.createFormattedText("""
|
||||||
defaultBtn.setDefaultButton(true);
|
In addition to using the predefined [i]Button[/i] colors, you can add custom ones \
|
||||||
defaultBtn.setDisable(true);
|
by manipulating the looked-up color variables."""
|
||||||
|
);
|
||||||
|
|
||||||
var flatBtn = new Button("Flat");
|
return new ExampleBox(box, new Snippet(getClass(), 8), description);
|
||||||
flatBtn.getStyleClass().addAll(FLAT);
|
|
||||||
flatBtn.setDisable(true);
|
|
||||||
|
|
||||||
var iconBtn = new Button("", new FontIcon(Feather.TAG));
|
|
||||||
iconBtn.getStyleClass().addAll(BUTTON_ICON);
|
|
||||||
iconBtn.setDisable(true);
|
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, basicBtn, defaultBtn, flatBtn, iconBtn);
|
|
||||||
return new SampleBlock("Disabled", content);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,25 +2,20 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import java.time.Month;
|
import java.time.Month;
|
||||||
import java.time.format.TextStyle;
|
import java.time.format.TextStyle;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.chart.AreaChart;
|
import javafx.scene.chart.AreaChart;
|
||||||
import javafx.scene.chart.BarChart;
|
import javafx.scene.chart.BarChart;
|
||||||
import javafx.scene.chart.BubbleChart;
|
import javafx.scene.chart.BubbleChart;
|
||||||
import javafx.scene.chart.CategoryAxis;
|
import javafx.scene.chart.CategoryAxis;
|
||||||
import javafx.scene.chart.Chart;
|
|
||||||
import javafx.scene.chart.LineChart;
|
import javafx.scene.chart.LineChart;
|
||||||
import javafx.scene.chart.NumberAxis;
|
import javafx.scene.chart.NumberAxis;
|
||||||
import javafx.scene.chart.PieChart;
|
import javafx.scene.chart.PieChart;
|
||||||
@ -28,14 +23,8 @@ import javafx.scene.chart.ScatterChart;
|
|||||||
import javafx.scene.chart.StackedAreaChart;
|
import javafx.scene.chart.StackedAreaChart;
|
||||||
import javafx.scene.chart.StackedBarChart;
|
import javafx.scene.chart.StackedBarChart;
|
||||||
import javafx.scene.chart.XYChart;
|
import javafx.scene.chart.XYChart;
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.util.StringConverter;
|
|
||||||
|
|
||||||
public class ChartPage extends AbstractPage {
|
public class ChartPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Chart";
|
public static final String NAME = "Chart";
|
||||||
|
|
||||||
@ -44,107 +33,27 @@ public class ChartPage extends AbstractPage {
|
|||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BorderPane chartWrapper = new BorderPane();
|
|
||||||
private final ComboBox<Example> exampleSelect = new ComboBox<>();
|
|
||||||
|
|
||||||
public ChartPage() {
|
public ChartPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new VBox(
|
|
||||||
new SampleBlock("Playground", createPlayground())
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createPlayground() {
|
addPlainText("""
|
||||||
exampleSelect.setMaxWidth(Double.MAX_VALUE);
|
JavaFX provides a set of chart components specifically designed \
|
||||||
exampleSelect.getItems().setAll(Example.values());
|
for data visualization. The charts include common types such as \
|
||||||
exampleSelect.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
Bar, Line, Area, Pie, Scatter, and Bubble charts."""
|
||||||
if (val == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chart newChart = createChart(val);
|
|
||||||
|
|
||||||
// copy existing properties to the new chart
|
|
||||||
findDisplayedChart().ifPresent(chart -> newChart.setDisable(chart.isDisable()));
|
|
||||||
|
|
||||||
chartWrapper.setCenter(newChart);
|
|
||||||
});
|
|
||||||
exampleSelect.setConverter(new StringConverter<>() {
|
|
||||||
@Override
|
|
||||||
public String toString(Example example) {
|
|
||||||
return example == null ? "" : example.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Example fromString(String s) {
|
|
||||||
return Example.find(s);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var disableToggle = new ToggleSwitch("Disable");
|
|
||||||
disableToggle.selectedProperty().addListener((obs, old, val) -> findDisplayedChart().ifPresent(ch -> {
|
|
||||||
if (val != null) {
|
|
||||||
ch.setDisable(val);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
var controls = new HBox(disableToggle);
|
|
||||||
controls.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
VBox playground = new VBox(SampleBlock.BLOCK_VGAP);
|
|
||||||
playground.getChildren().setAll(
|
|
||||||
new HBox(new Label("Select an example:"), new Spacer(), disableToggle),
|
|
||||||
exampleSelect,
|
|
||||||
chartWrapper
|
|
||||||
);
|
);
|
||||||
|
addSection("Area Chart", areaChart());
|
||||||
return playground;
|
addSection("Stacked Area Chart", stackedAreaChart());
|
||||||
}
|
addSection("Bar Chart", barChart());
|
||||||
|
addSection("Stacked Bar Chart", stackedBarChart());
|
||||||
@Override
|
addSection("Bubble Chart", bubbleChart());
|
||||||
protected void onRendered() {
|
addSection("Line Chart", lineChart());
|
||||||
super.onRendered();
|
addSection("Pie Chart", pieChart());
|
||||||
exampleSelect.getSelectionModel().selectFirst();
|
addSection("Scatter Chart", scatterChart());
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<Chart> findDisplayedChart() {
|
|
||||||
return chartWrapper.getChildren().size() > 0
|
|
||||||
? Optional.of((Chart) chartWrapper.getChildren().get(0))
|
|
||||||
: Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Chart createChart(Example example) {
|
|
||||||
switch (example) {
|
|
||||||
case AREA_CHART -> {
|
|
||||||
return areaChart(false);
|
|
||||||
}
|
|
||||||
case BAR_CHART -> {
|
|
||||||
return barChart(false);
|
|
||||||
}
|
|
||||||
case BUBBLE_CHART -> {
|
|
||||||
return bubbleChart();
|
|
||||||
}
|
|
||||||
case LINE_CHART -> {
|
|
||||||
return lineChart();
|
|
||||||
}
|
|
||||||
case PIE_CHART -> {
|
|
||||||
return pieChart();
|
|
||||||
}
|
|
||||||
case SCATTER_CHART -> {
|
|
||||||
return scatterChart();
|
|
||||||
}
|
|
||||||
case STACKED_AREA_CHART -> {
|
|
||||||
return areaChart(true);
|
|
||||||
}
|
|
||||||
case STACKED_BAR_CHART -> {
|
|
||||||
return barChart(true);
|
|
||||||
}
|
|
||||||
default -> throw new IllegalArgumentException("Unexpected enum value: " + example);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Chart areaChart(boolean stacked) {
|
private ExampleBox areaChart() {
|
||||||
|
//snippet_1:start
|
||||||
var x = new NumberAxis(1, 31, 1);
|
var x = new NumberAxis(1, 31, 1);
|
||||||
x.setLabel("Day");
|
x.setLabel("Day");
|
||||||
|
|
||||||
@ -163,15 +72,56 @@ public class ChartPage extends AbstractPage {
|
|||||||
new XYChart.Data<>(i, FAKER.random().nextInt(15, 30))
|
new XYChart.Data<>(i, FAKER.random().nextInt(15, 30))
|
||||||
));
|
));
|
||||||
|
|
||||||
var chart = stacked ? new StackedAreaChart<>(x, y) : new AreaChart<>(x, y);
|
var chart = new AreaChart<>(x, y);
|
||||||
chart.setTitle("Temperature Monitoring");
|
chart.setTitle("Temperature Monitoring");
|
||||||
|
chart.setMinHeight(300);
|
||||||
chart.getData().addAll(april, may);
|
chart.getData().addAll(april, may);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
return chart;
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Plots the area between the line that connects the data points and the axis. \
|
||||||
|
Good for comparing cumulated totals over time.""");
|
||||||
|
|
||||||
|
return new ExampleBox(chart, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Chart barChart(boolean stacked) {
|
private ExampleBox stackedAreaChart() {
|
||||||
|
//snippet_2:start
|
||||||
|
var x = new NumberAxis(1, 31, 1);
|
||||||
|
x.setLabel("Day");
|
||||||
|
|
||||||
|
var y = new NumberAxis();
|
||||||
|
y.setLabel("Temperature");
|
||||||
|
|
||||||
|
var april = new XYChart.Series<Number, Number>();
|
||||||
|
april.setName("April");
|
||||||
|
IntStream.range(1, 30).forEach(i -> april.getData().add(
|
||||||
|
new XYChart.Data<>(i, FAKER.random().nextInt(15, 30))
|
||||||
|
));
|
||||||
|
|
||||||
|
var may = new XYChart.Series<Number, Number>();
|
||||||
|
may.setName("May");
|
||||||
|
IntStream.range(1, 30).forEach(i -> may.getData().add(
|
||||||
|
new XYChart.Data<>(i, FAKER.random().nextInt(15, 30))
|
||||||
|
));
|
||||||
|
|
||||||
|
var chart = new StackedAreaChart<>(x, y);
|
||||||
|
chart.setTitle("Temperature Monitoring");
|
||||||
|
chart.setMinHeight(300);
|
||||||
|
chart.getData().addAll(april, may);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A variation of [i]AreaChart[/i] that displays trends of the contribution of each value."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(chart, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ExampleBox barChart() {
|
||||||
|
//snippet_3:start
|
||||||
final var rnd = FAKER.random();
|
final var rnd = FAKER.random();
|
||||||
final var countries = IntStream.range(0, 5).boxed()
|
final var countries = IntStream.range(0, 5).boxed()
|
||||||
.map(i -> FAKER.country().countryCode3().toUpperCase())
|
.map(i -> FAKER.country().countryCode3().toUpperCase())
|
||||||
@ -201,15 +151,69 @@ public class ChartPage extends AbstractPage {
|
|||||||
new XYChart.Data<>(countries.get(i), rnd.nextInt(10, 80))
|
new XYChart.Data<>(countries.get(i), rnd.nextInt(10, 80))
|
||||||
));
|
));
|
||||||
|
|
||||||
var chart = stacked ? new StackedBarChart<>(x, y) : new BarChart<>(x, y);
|
var chart = new BarChart<>(x, y);
|
||||||
chart.setTitle("Country Summary");
|
chart.setTitle("Country Summary");
|
||||||
|
chart.setMinHeight(300);
|
||||||
chart.getData().addAll(january, february, march);
|
chart.getData().addAll(january, february, march);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
return chart;
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Plots rectangular bars with heights indicating data values they represent, \
|
||||||
|
and corresponding to the categories they belongs to. Used for displaying \
|
||||||
|
discontinuous or discrete data."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(chart, new Snippet(getClass(), 3), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Chart bubbleChart() {
|
private ExampleBox stackedBarChart() {
|
||||||
|
//snippet_4:start
|
||||||
|
final var rnd = FAKER.random();
|
||||||
|
final var countries = IntStream.range(0, 5).boxed()
|
||||||
|
.map(i -> FAKER.country().countryCode3().toUpperCase())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var x = new CategoryAxis();
|
||||||
|
x.setLabel("Country");
|
||||||
|
|
||||||
|
var y = new NumberAxis(0, 80, 10);
|
||||||
|
y.setLabel("Value");
|
||||||
|
|
||||||
|
var january = new XYChart.Series<String, Number>();
|
||||||
|
january.setName("January");
|
||||||
|
IntStream.range(0, countries.size()).forEach(i -> january.getData().add(
|
||||||
|
new XYChart.Data<>(countries.get(i), rnd.nextInt(10, 80))
|
||||||
|
));
|
||||||
|
|
||||||
|
var february = new XYChart.Series<String, Number>();
|
||||||
|
february.setName("February");
|
||||||
|
IntStream.range(0, countries.size()).forEach(i -> february.getData().add(
|
||||||
|
new XYChart.Data<>(countries.get(i), rnd.nextInt(10, 80))
|
||||||
|
));
|
||||||
|
|
||||||
|
var march = new XYChart.Series<String, Number>();
|
||||||
|
march.setName("March");
|
||||||
|
IntStream.range(0, countries.size()).forEach(i -> march.getData().add(
|
||||||
|
new XYChart.Data<>(countries.get(i), rnd.nextInt(10, 80))
|
||||||
|
));
|
||||||
|
|
||||||
|
var chart = new StackedBarChart<>(x, y);
|
||||||
|
chart.setTitle("Country Summary");
|
||||||
|
chart.setMinHeight(300);
|
||||||
|
chart.getData().addAll(january, february, march);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A variation of [i]BarChart[/i] that plots bars indicating data values for a category."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(chart, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ExampleBox bubbleChart() {
|
||||||
|
//snippet_5:start
|
||||||
final var rnd = FAKER.random();
|
final var rnd = FAKER.random();
|
||||||
|
|
||||||
var x = new NumberAxis(1, 53, 4);
|
var x = new NumberAxis(1, 53, 4);
|
||||||
@ -221,24 +225,40 @@ public class ChartPage extends AbstractPage {
|
|||||||
var series1 = new XYChart.Series<Number, Number>();
|
var series1 = new XYChart.Series<Number, Number>();
|
||||||
series1.setName(FAKER.commerce().productName());
|
series1.setName(FAKER.commerce().productName());
|
||||||
IntStream.range(1, 10).forEach(i -> series1.getData().add(
|
IntStream.range(1, 10).forEach(i -> series1.getData().add(
|
||||||
new XYChart.Data<>(rnd.nextInt(1, 53), rnd.nextInt(10, 80), rnd.nextDouble(1, 10))
|
new XYChart.Data<>(
|
||||||
|
rnd.nextInt(1, 53),
|
||||||
|
rnd.nextInt(10, 80),
|
||||||
|
rnd.nextDouble(1, 10)
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
var series2 = new XYChart.Series<Number, Number>();
|
var series2 = new XYChart.Series<Number, Number>();
|
||||||
series2.setName(FAKER.commerce().productName());
|
series2.setName(FAKER.commerce().productName());
|
||||||
IntStream.range(1, 10).forEach(i -> series2.getData().add(
|
IntStream.range(1, 10).forEach(i -> series2.getData().add(
|
||||||
new XYChart.Data<>(rnd.nextInt(1, 53), rnd.nextInt(10, 80), rnd.nextDouble(1, 10))
|
new XYChart.Data<>(
|
||||||
|
rnd.nextInt(1, 53),
|
||||||
|
rnd.nextInt(10, 80),
|
||||||
|
rnd.nextDouble(1, 10)
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
var chart = new BubbleChart<>(x, y);
|
var chart = new BubbleChart<>(x, y);
|
||||||
chart.setTitle("Budget Monitoring");
|
chart.setTitle("Budget Monitoring");
|
||||||
|
chart.setMinHeight(300);
|
||||||
chart.getData().addAll(series1, series2);
|
chart.getData().addAll(series1, series2);
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
return chart;
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Plots bubbles for data points in a series. Each plotted entity depicts \
|
||||||
|
three parameters in a 2D chart and hence a unique chart type."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(chart, new Snippet(getClass(), 5), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Chart lineChart() {
|
private ExampleBox lineChart() {
|
||||||
|
//snippet_6:start
|
||||||
final var rnd = FAKER.random();
|
final var rnd = FAKER.random();
|
||||||
|
|
||||||
var x = new CategoryAxis();
|
var x = new CategoryAxis();
|
||||||
@ -250,23 +270,36 @@ public class ChartPage extends AbstractPage {
|
|||||||
var series1 = new XYChart.Series<String, Number>();
|
var series1 = new XYChart.Series<String, Number>();
|
||||||
series1.setName(FAKER.stock().nsdqSymbol());
|
series1.setName(FAKER.stock().nsdqSymbol());
|
||||||
IntStream.range(1, 12).forEach(i -> series1.getData().add(
|
IntStream.range(1, 12).forEach(i -> series1.getData().add(
|
||||||
new XYChart.Data<>(Month.of(i).getDisplayName(TextStyle.SHORT, Locale.getDefault()), rnd.nextInt(10, 80))
|
new XYChart.Data<>(
|
||||||
|
Month.of(i).getDisplayName(TextStyle.SHORT, Locale.getDefault()),
|
||||||
|
rnd.nextInt(10, 80)
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
var series2 = new XYChart.Series<String, Number>();
|
var series2 = new XYChart.Series<String, Number>();
|
||||||
series2.setName(FAKER.stock().nsdqSymbol());
|
series2.setName(FAKER.stock().nsdqSymbol());
|
||||||
IntStream.range(1, 12).forEach(i -> series2.getData().add(
|
IntStream.range(1, 12).forEach(i -> series2.getData().add(
|
||||||
new XYChart.Data<>(Month.of(i).getDisplayName(TextStyle.SHORT, Locale.getDefault()), rnd.nextInt(10, 80))
|
new XYChart.Data<>(
|
||||||
|
Month.of(i).getDisplayName(TextStyle.SHORT, Locale.getDefault()),
|
||||||
|
rnd.nextInt(10, 80)
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
var chart = new LineChart<>(x, y);
|
var chart = new LineChart<>(x, y);
|
||||||
chart.setTitle("Stock Monitoring");
|
chart.setTitle("Stock Monitoring");
|
||||||
|
chart.setMinHeight(300);
|
||||||
chart.getData().addAll(series1, series2);
|
chart.getData().addAll(series1, series2);
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
return chart;
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Plots line between the data points in a series. Used usually to view data trends over time."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(chart, new Snippet(getClass(), 6), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Chart pieChart() {
|
private ExampleBox pieChart() {
|
||||||
|
//snippet_7:start
|
||||||
final var rnd = FAKER.random();
|
final var rnd = FAKER.random();
|
||||||
|
|
||||||
ObservableList<PieChart.Data> data = FXCollections.observableArrayList(
|
ObservableList<PieChart.Data> data = FXCollections.observableArrayList(
|
||||||
@ -278,13 +311,21 @@ public class ChartPage extends AbstractPage {
|
|||||||
);
|
);
|
||||||
|
|
||||||
var chart = new PieChart(data);
|
var chart = new PieChart(data);
|
||||||
|
chart.setMinHeight(300);
|
||||||
chart.setTitle("Imported Fruits");
|
chart.setTitle("Imported Fruits");
|
||||||
|
//snippet_7:end
|
||||||
|
|
||||||
return chart;
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Plots circular chart divided into segments with each segment representing a value \
|
||||||
|
as a proportion of the total. It looks like a Pie and hence the name."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(chart, new Snippet(getClass(), 7), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Chart scatterChart() {
|
private ExampleBox scatterChart() {
|
||||||
|
//snippet_8:start
|
||||||
final var rnd = FAKER.random();
|
final var rnd = FAKER.random();
|
||||||
|
|
||||||
var x = new NumberAxis(0, 10, 1);
|
var x = new NumberAxis(0, 10, 1);
|
||||||
@ -296,47 +337,32 @@ public class ChartPage extends AbstractPage {
|
|||||||
var series1 = new XYChart.Series<Number, Number>();
|
var series1 = new XYChart.Series<Number, Number>();
|
||||||
series1.setName("Equities");
|
series1.setName("Equities");
|
||||||
IntStream.range(1, 10).forEach(i -> series1.getData().add(
|
IntStream.range(1, 10).forEach(i -> series1.getData().add(
|
||||||
new XYChart.Data<>(rnd.nextDouble(0, 10), rnd.nextDouble(-100, 500))
|
new XYChart.Data<>(
|
||||||
|
rnd.nextDouble(0, 10),
|
||||||
|
rnd.nextDouble(-100, 500)
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
var series2 = new XYChart.Series<Number, Number>();
|
var series2 = new XYChart.Series<Number, Number>();
|
||||||
series2.setName("Mutual funds");
|
series2.setName("Mutual funds");
|
||||||
IntStream.range(1, 10).forEach(i -> series2.getData().add(
|
IntStream.range(1, 10).forEach(i -> series2.getData().add(
|
||||||
new XYChart.Data<>(rnd.nextDouble(0, 10), rnd.nextDouble(-100, 500))
|
new XYChart.Data<>(
|
||||||
|
rnd.nextDouble(0, 10),
|
||||||
|
rnd.nextDouble(-100, 500)
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
var chart = new ScatterChart<>(x, y);
|
var chart = new ScatterChart<>(x, y);
|
||||||
chart.setTitle("Investment Overview");
|
chart.setTitle("Investment Overview");
|
||||||
|
chart.setMinHeight(300);
|
||||||
chart.getData().addAll(series1, series2);
|
chart.getData().addAll(series1, series2);
|
||||||
|
//snippet_8:end
|
||||||
|
|
||||||
return chart;
|
var description = BBCodeParser.createFormattedText("""
|
||||||
}
|
Plots symbols for the data points in a series. This type of chart is useful in viewing \
|
||||||
|
distribution of data and its correlation, if there is any clustering."""
|
||||||
|
);
|
||||||
|
|
||||||
private enum Example {
|
return new ExampleBox(chart, new Snippet(getClass(), 8), description);
|
||||||
AREA_CHART("Area Chart"),
|
|
||||||
BAR_CHART("Bar Chart"),
|
|
||||||
BUBBLE_CHART("Bubble Chart"),
|
|
||||||
LINE_CHART("Line Chart"),
|
|
||||||
PIE_CHART("Pie Chart"),
|
|
||||||
SCATTER_CHART("Scatter Chart"),
|
|
||||||
STACKED_AREA_CHART("Stacked Area Chart"),
|
|
||||||
STACKED_BAR_CHART("Stacked Bar Chart");
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
Example(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Example find(String name) {
|
|
||||||
return Arrays.stream(Example.values())
|
|
||||||
.filter(example -> Objects.equals(example.getName(), name))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
|
|
||||||
public class CheckBoxPage extends AbstractPage {
|
public class CheckBoxPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "CheckBox";
|
public static final String NAME = "CheckBox";
|
||||||
|
|
||||||
@ -23,46 +24,66 @@ public class CheckBoxPage extends AbstractPage {
|
|||||||
|
|
||||||
public CheckBoxPage() {
|
public CheckBoxPage() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
addFormattedText("""
|
||||||
setUserContent(new FlowPane(
|
A tri-state selection control is typically skinned as a box \
|
||||||
PAGE_HGAP, PAGE_VGAP,
|
with a checkmark or tick mark when checked."""
|
||||||
basicSample(),
|
|
||||||
indeterminateSample(),
|
|
||||||
disabledSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
|
||||||
basicCheck = new CheckBox("_Check Me");
|
|
||||||
basicCheck.setMnemonicParsing(true);
|
|
||||||
return new SampleBlock("Basic", basicCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock indeterminateSample() {
|
|
||||||
indeterminateCheck = new CheckBox("C_heck Me");
|
|
||||||
indeterminateCheck.setAllowIndeterminate(true);
|
|
||||||
indeterminateCheck.setIndeterminate(true);
|
|
||||||
indeterminateCheck.setMnemonicParsing(true);
|
|
||||||
return new SampleBlock("Indeterminate", indeterminateCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
|
||||||
var basicCheck = new CheckBox("Check Me");
|
|
||||||
basicCheck.setSelected(true);
|
|
||||||
basicCheck.setDisable(true);
|
|
||||||
|
|
||||||
var indeterminateCheck = new CheckBox("Check Me");
|
|
||||||
indeterminateCheck.setAllowIndeterminate(true);
|
|
||||||
indeterminateCheck.setIndeterminate(true);
|
|
||||||
indeterminateCheck.setDisable(true);
|
|
||||||
|
|
||||||
return new SampleBlock(
|
|
||||||
"Disabled",
|
|
||||||
new HBox(SampleBlock.BLOCK_HGAP, basicCheck, indeterminateCheck)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Indeterminate", indeterminateExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var cb1 = new CheckBox("_Unchecked");
|
||||||
|
cb1.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
var cb2 = new CheckBox("Checked");
|
||||||
|
cb2.setSelected(true);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(40, cb1, cb2);
|
||||||
|
basicCheck = cb1;
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A [i]CheckBox[/i] can be in one of three states:
|
||||||
|
|
||||||
|
[ul]
|
||||||
|
[li][b]checked[/b]: indeterminate == false, checked == true[/li]
|
||||||
|
[li][b]unchecked[/b]: indeterminate == false, checked == false[/li]
|
||||||
|
[li][b]undefined[/b]: indeterminate == true[/li][/ul]"""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox indeterminateExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var cb1 = new CheckBox("I_ndeterminate");
|
||||||
|
cb1.setAllowIndeterminate(true);
|
||||||
|
cb1.setIndeterminate(true);
|
||||||
|
cb1.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
var cb2 = new CheckBox("Indeterminate + Checked");
|
||||||
|
cb2.setAllowIndeterminate(true);
|
||||||
|
cb2.setIndeterminate(false);
|
||||||
|
cb2.setSelected(true);
|
||||||
|
|
||||||
|
var cb3 = new CheckBox("Indeterminate + Unchecked");
|
||||||
|
cb3.setAllowIndeterminate(true);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(40, cb1, cb2, cb3);
|
||||||
|
indeterminateCheck = cb1;
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [code]allowIndeterminate[/code] variable, if true, allows the user to \
|
||||||
|
cycle through the undefined state. A [i]CheckBox[/i] is undefined if \
|
||||||
|
indeterminate is true, regardless of the state of selected."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// visually compare normal and indeterminate checkboxes size
|
// visually compare normal and indeterminate checkboxes size
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
public class ChoiceBoxPage extends ComboBoxPage {
|
||||||
|
|
||||||
|
public static final String NAME = "ChoiceBox";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
}
|
@ -2,22 +2,16 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.geometry.HPos;
|
|
||||||
import javafx.scene.control.ChoiceBox;
|
|
||||||
import javafx.scene.control.ColorPicker;
|
import javafx.scene.control.ColorPicker;
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.ColumnConstraints;
|
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
public class ColorPickerPage extends AbstractPage {
|
public class ColorPickerPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "ColorPicker";
|
public static final String NAME = "ColorPicker";
|
||||||
|
|
||||||
@ -28,79 +22,68 @@ public class ColorPickerPage extends AbstractPage {
|
|||||||
|
|
||||||
public ColorPickerPage() {
|
public ColorPickerPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new VBox(
|
|
||||||
new SampleBlock("Playground", createPlayground())
|
addFormattedText("""
|
||||||
));
|
ColorPicker control allows the user to select a color from either \
|
||||||
|
a standard palette of colors with a simple one click selection or \
|
||||||
|
define their own custom color."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Style", styleExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private GridPane createPlayground() {
|
private ExampleBox usageExample() {
|
||||||
var colorPicker = new ColorPicker();
|
//snippet_1:start
|
||||||
colorPicker.setValue(Color.DEEPSKYBLUE);
|
var cp = new ColorPicker();
|
||||||
|
cp.setValue(Color.RED);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
var labelToggle = new ToggleSwitch();
|
var box = new HBox(cp);
|
||||||
labelToggle.setSelected(true);
|
box.setMinHeight(50);
|
||||||
labelToggle.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
colorPicker.setStyle("-fx-color-label-visible: false;");
|
|
||||||
if (val) {
|
|
||||||
colorPicker.setStyle("-fx-color-label-visible: true;");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var disableToggle = new ToggleSwitch();
|
var description = BBCodeParser.createFormattedText("""
|
||||||
colorPicker.disableProperty().bind(disableToggle.selectedProperty());
|
The [i]ColorPicker[/i] control provides a color palette with a predefined \
|
||||||
|
set of colors. If the user does not want to choose from the predefined set, \
|
||||||
var grid = new GridPane();
|
they can create a custom color by interacting with a custom color dialog. \
|
||||||
grid.setHgap(BLOCK_HGAP);
|
This dialog provides RGB, HSB and Web modes of interaction, to create new \
|
||||||
grid.setVgap(BLOCK_VGAP);
|
colors. It also lets the opacity of the color to be modified."""
|
||||||
grid.add(colorPicker, 0, 0, 1, GridPane.REMAINING);
|
|
||||||
grid.add(createLabel("Show label"), 1, 0);
|
|
||||||
grid.add(labelToggle, 2, 0);
|
|
||||||
grid.add(createLabel("Picker style"), 1, 1);
|
|
||||||
grid.add(createPickerStyleChoice(colorPicker), 2, 1);
|
|
||||||
grid.add(createLabel("Disable"), 1, 2);
|
|
||||||
grid.add(disableToggle, 2, 2);
|
|
||||||
|
|
||||||
grid.getColumnConstraints().setAll(
|
|
||||||
new ColumnConstraints(200),
|
|
||||||
new ColumnConstraints(),
|
|
||||||
new ColumnConstraints()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return grid;
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Label createLabel(String text) {
|
private ExampleBox styleExample() {
|
||||||
var label = new Label(text);
|
//snippet_2:start
|
||||||
GridPane.setHalignment(label, HPos.RIGHT);
|
var cp1 = new ColorPicker();
|
||||||
return label;
|
cp1.setValue(Color.RED);
|
||||||
}
|
cp1.getStyleClass().add(ColorPicker.STYLE_CLASS_BUTTON);
|
||||||
|
|
||||||
private ChoiceBox<String> createPickerStyleChoice(ColorPicker colorPicker) {
|
var cp2 = new ColorPicker();
|
||||||
var optDefault = "Default";
|
cp2.setValue(Color.GREEN);
|
||||||
var optButton = "Button";
|
cp2.getStyleClass().add(ColorPicker.STYLE_CLASS_SPLIT_BUTTON);
|
||||||
var optSplitButton = "Split Button";
|
|
||||||
|
|
||||||
var choice = new ChoiceBox<String>();
|
var cp3 = new ColorPicker();
|
||||||
choice.getItems().setAll(optDefault, optButton, optSplitButton);
|
cp3.setValue(Color.BLUE);
|
||||||
choice.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
cp3.setStyle("-fx-color-label-visible: false");
|
||||||
if (val == null) {
|
//snippet_2:end
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
colorPicker.getStyleClass().removeAll(
|
var grid = new GridPane();
|
||||||
ColorPicker.STYLE_CLASS_BUTTON,
|
grid.setHgap(30);
|
||||||
ColorPicker.STYLE_CLASS_SPLIT_BUTTON
|
grid.setVgap(10);
|
||||||
);
|
grid.addRow(0,
|
||||||
|
captionLabel("STYLE_CLASS_BUTTON"),
|
||||||
|
captionLabel("STYLE_CLASS_SPLIT_BUTTON"),
|
||||||
|
captionLabel("-fx-color-label-visible")
|
||||||
|
);
|
||||||
|
grid.addRow(1, cp1, cp2, cp3);
|
||||||
|
|
||||||
if (optButton.equals(val)) {
|
var description = BBCodeParser.createFormattedText("""
|
||||||
colorPicker.getStyleClass().add(ColorPicker.STYLE_CLASS_BUTTON);
|
The [i]ColorPicker[/i] control can be styled in two ways: a simple \
|
||||||
}
|
[i]Button[/i] mode or the default [i]MenuButton[/i] mode. While there \
|
||||||
if (optSplitButton.equals(val)) {
|
is also a [i]SplitMenuButton[/i] mode available, it is not supported by \
|
||||||
colorPicker.getStyleClass().add(ColorPicker.STYLE_CLASS_SPLIT_BUTTON);
|
AtlantaFX and looks the same as the default option."""
|
||||||
}
|
);
|
||||||
});
|
|
||||||
choice.getSelectionModel().select(optDefault);
|
|
||||||
|
|
||||||
return choice;
|
return new ExampleBox(grid, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,32 +2,25 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.STATE_DANGER;
|
import atlantafx.base.theme.Styles;
|
||||||
import static atlantafx.base.theme.Styles.STATE_SUCCESS;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
import static atlantafx.sampler.util.Containers.H_GROW_NEVER;
|
|
||||||
import static javafx.collections.FXCollections.observableArrayList;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Tweaks;
|
import atlantafx.base.theme.Tweaks;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import java.util.function.Consumer;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.geometry.HPos;
|
|
||||||
import javafx.scene.control.ChoiceBox;
|
import javafx.scene.control.ChoiceBox;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.HBox;
|
||||||
import org.kordamp.ikonli.Ikon;
|
import org.kordamp.ikonli.Ikon;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
public class ComboBoxPage extends AbstractPage {
|
public class ComboBoxPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "ComboBox";
|
public static final String NAME = "ComboBox";
|
||||||
private static final int PREF_WIDTH = 200;
|
private static final int PREF_WIDTH = 200;
|
||||||
@ -39,177 +32,258 @@ public class ComboBoxPage extends AbstractPage {
|
|||||||
|
|
||||||
public ComboBoxPage() {
|
public ComboBoxPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new VBox(
|
|
||||||
new SampleBlock("Examples", createPlayground())
|
addFormattedText("""
|
||||||
|
A user interface component which shows a list of items out of which \
|
||||||
|
user can select at most one item. JavaFX provides two pretty similar \
|
||||||
|
controls for that purpose, namely the [i]ComboBox[/i] and [i]ChoiceBox[/i]."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Editable", editableExample());
|
||||||
|
addSection("Placeholder", placeholderExample());
|
||||||
|
addSection("Custom Items", customItemsExample());
|
||||||
|
addSection("Color", colorExample());
|
||||||
|
addSection("Overflow", overflowExample());
|
||||||
|
addSection("Alternative Icon", altIconExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var cmb1 = new ComboBox<String>();
|
||||||
|
cmb1.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb1.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
));
|
));
|
||||||
}
|
cmb1.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var cmb2 = new ComboBox<String>();
|
||||||
|
cmb2.setPrefWidth(PREF_WIDTH);
|
||||||
|
|
||||||
|
var chb1 = new ChoiceBox<String>();
|
||||||
|
chb1.setPrefWidth(PREF_WIDTH);
|
||||||
|
chb1.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
|
));
|
||||||
|
chb1.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var chb2 = new ChoiceBox<String>();
|
||||||
|
chb2.setPrefWidth(PREF_WIDTH);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
private GridPane createPlayground() {
|
|
||||||
var grid = new GridPane();
|
var grid = new GridPane();
|
||||||
grid.setHgap(BLOCK_HGAP);
|
grid.setHgap(30);
|
||||||
grid.setVgap(BLOCK_VGAP);
|
grid.setVgap(10);
|
||||||
grid.getColumnConstraints().setAll(H_GROW_NEVER, H_GROW_NEVER, H_GROW_NEVER);
|
grid.addRow(0, captionLabel("ComboBox"), cmb1, cmb2);
|
||||||
|
grid.addRow(1, captionLabel("ChoiceBox"), chb1, chb2);
|
||||||
|
|
||||||
var comboLabel = new Label("C_omboBox");
|
var description = BBCodeParser.createFormattedText("""
|
||||||
comboLabel.setMnemonicParsing(true);
|
The [i]ComboBox[/i] is an implementation of the [i]ComboBoxBase[/i] abstract class, \
|
||||||
grid.add(comboLabel, 0, 0);
|
whereas the [i]CheckBox[/i] is more similar to the [i]MenuButton[/i]. Both controls \
|
||||||
|
provide a selection model to manage the selected state."""
|
||||||
|
);
|
||||||
|
|
||||||
var choiceLabel = new Label("C_hoiceBox");
|
return new ExampleBox(grid, new Snippet(ComboBoxPage.class, 1), description);
|
||||||
choiceLabel.setMnemonicParsing(true);
|
|
||||||
grid.add(choiceLabel, 2, 0);
|
|
||||||
|
|
||||||
// default
|
|
||||||
grid.add(createComboBox(), 0, 1);
|
|
||||||
grid.add(createLabel("empty"), 1, 1);
|
|
||||||
grid.add(createChoiceBox(), 2, 1);
|
|
||||||
|
|
||||||
// editable
|
|
||||||
grid.add(createComboBoxWith(c -> {
|
|
||||||
c.setItems(createItems(5));
|
|
||||||
c.setEditable(true);
|
|
||||||
}), 0, 2);
|
|
||||||
grid.add(createLabel("editable"), 1, 2);
|
|
||||||
|
|
||||||
// placeholder
|
|
||||||
grid.add(createComboBoxWith(c -> c.setPlaceholder(new Label("Loading..."))), 0, 3);
|
|
||||||
grid.add(createLabel("placeholder"), 1, 3);
|
|
||||||
|
|
||||||
// with icons
|
|
||||||
var badges = IntStream.range(0, 5).boxed()
|
|
||||||
.map(i -> new Badge(FAKER.hipster().word(), randomIcon()))
|
|
||||||
.collect(Collectors.toCollection(FXCollections::observableArrayList));
|
|
||||||
var badgeCombo = new ComboBox<>(badges);
|
|
||||||
badgeCombo.setPrefWidth(PREF_WIDTH);
|
|
||||||
badgeCombo.setButtonCell(new BadgeCell());
|
|
||||||
badgeCombo.setCellFactory(lv -> new BadgeCell());
|
|
||||||
badgeCombo.getSelectionModel().selectFirst();
|
|
||||||
grid.add(badgeCombo, 0, 4);
|
|
||||||
grid.add(createLabel("graphic"), 1, 4);
|
|
||||||
|
|
||||||
// success
|
|
||||||
grid.add(createComboBoxWith(c -> {
|
|
||||||
c.setItems(createItems(5));
|
|
||||||
c.pseudoClassStateChanged(STATE_SUCCESS, true);
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 0, 5);
|
|
||||||
grid.add(createLabel("success"), 1, 5);
|
|
||||||
grid.add(createChoiceBoxWith(c -> {
|
|
||||||
c.setItems(createItems(5));
|
|
||||||
c.pseudoClassStateChanged(STATE_SUCCESS, true);
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 2, 5);
|
|
||||||
|
|
||||||
// negative
|
|
||||||
grid.add(createComboBoxWith(c -> {
|
|
||||||
c.setItems(createItems(5));
|
|
||||||
c.pseudoClassStateChanged(STATE_DANGER, true);
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 0, 6);
|
|
||||||
grid.add(createLabel("success"), 1, 6);
|
|
||||||
grid.add(createChoiceBoxWith(c -> {
|
|
||||||
c.setItems(createItems(5));
|
|
||||||
c.pseudoClassStateChanged(STATE_DANGER, true);
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 2, 6);
|
|
||||||
|
|
||||||
// alt icon
|
|
||||||
grid.add(createComboBoxWith(c -> {
|
|
||||||
c.setItems(createItems(5));
|
|
||||||
c.getStyleClass().add(Tweaks.ALT_ICON);
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 0, 7);
|
|
||||||
grid.add(createLabel("alt icon"), 1, 7);
|
|
||||||
grid.add(createChoiceBoxWith(c -> {
|
|
||||||
c.setItems(createItems(5));
|
|
||||||
c.getStyleClass().add(Tweaks.ALT_ICON);
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 2, 7);
|
|
||||||
|
|
||||||
// disabled
|
|
||||||
grid.add(createComboBoxWith(c -> c.setDisable(true)), 0, 8);
|
|
||||||
grid.add(createLabel("disabled"), 1, 8);
|
|
||||||
grid.add(createChoiceBoxWith(c -> c.setDisable(true)), 2, 8);
|
|
||||||
|
|
||||||
// vertical overflow
|
|
||||||
grid.add(createComboBoxWith(c -> {
|
|
||||||
c.setItems(createItems(50));
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 0, 9);
|
|
||||||
grid.add(createLabel("large list"), 1, 9);
|
|
||||||
grid.add(createChoiceBoxWith(c -> {
|
|
||||||
c.setItems(createItems(50));
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 2, 9);
|
|
||||||
|
|
||||||
// horizontal overflow
|
|
||||||
grid.add(createComboBoxWith(c -> {
|
|
||||||
c.setItems(observableArrayList(generate(() -> FAKER.chuckNorris().fact(), 5)));
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 0, 10);
|
|
||||||
grid.add(createLabel("wide text"), 1, 10);
|
|
||||||
grid.add(createChoiceBoxWith(c -> {
|
|
||||||
c.setItems(observableArrayList(generate(() -> FAKER.chuckNorris().fact(), 5)));
|
|
||||||
c.getSelectionModel().selectFirst();
|
|
||||||
}), 2, 10);
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Label createLabel(String text) {
|
private ExampleBox editableExample() {
|
||||||
var label = new Label(text);
|
//snippet_2:start
|
||||||
GridPane.setHalignment(label, HPos.CENTER);
|
var cmb = new ComboBox<String>();
|
||||||
return label;
|
cmb.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
|
));
|
||||||
|
cmb.getSelectionModel().selectFirst();
|
||||||
|
cmb.setEditable(true);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(cmb);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]ComboBox[/i] provides a way for end-users to input values \
|
||||||
|
that are not available as options."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(ComboBoxPage.class, 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ComboBox<String> createComboBox() {
|
private ExampleBox placeholderExample() {
|
||||||
return createComboBoxWith(null);
|
//snippet_3:start
|
||||||
|
var cmb = new ComboBox<String>();
|
||||||
|
cmb.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb.setPlaceholder(new Label("Loading..."));
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(cmb);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Placeholder is a node that is shown to the user when the [i]ComboBox[/i] \
|
||||||
|
has no content to display."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(ComboBoxPage.class, 3), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ComboBox<String> createComboBoxWith(Consumer<ComboBox<String>> mutator) {
|
private ExampleBox customItemsExample() {
|
||||||
var c = new ComboBox<String>();
|
//snippet_4:start
|
||||||
c.setPrefWidth(PREF_WIDTH);
|
record Badge(String text, Ikon icon) {
|
||||||
if (mutator != null) {
|
|
||||||
mutator.accept(c);
|
|
||||||
}
|
}
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChoiceBox<String> createChoiceBox() {
|
class BadgeCell extends ListCell<Badge> {
|
||||||
return createChoiceBoxWith(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChoiceBox<String> createChoiceBoxWith(Consumer<ChoiceBox<String>> mutator) {
|
@Override
|
||||||
var c = new ChoiceBox<String>();
|
protected void updateItem(Badge badge, boolean isEmpty) {
|
||||||
c.setPrefWidth(PREF_WIDTH);
|
super.updateItem(badge, isEmpty);
|
||||||
if (mutator != null) {
|
|
||||||
mutator.accept(c);
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObservableList<String> createItems(int count) {
|
if (isEmpty) {
|
||||||
return observableArrayList(generate(() -> FAKER.hipster().word(), count));
|
setGraphic(null);
|
||||||
}
|
setText(null);
|
||||||
|
} else {
|
||||||
///////////////////////////////////////////////////////////////////////////
|
setGraphic(new FontIcon(badge.icon()));
|
||||||
|
setText(badge.text());
|
||||||
@SuppressWarnings("unused")
|
}
|
||||||
private record Badge(String text, Ikon icon) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BadgeCell extends ListCell<Badge> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateItem(Badge item, boolean isEmpty) {
|
|
||||||
super.updateItem(item, isEmpty);
|
|
||||||
|
|
||||||
if (isEmpty) {
|
|
||||||
setGraphic(null);
|
|
||||||
setText(null);
|
|
||||||
} else {
|
|
||||||
setGraphic(new FontIcon(item.icon()));
|
|
||||||
setText(item.text());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var items = IntStream.range(0, 5).boxed()
|
||||||
|
.map(i -> new Badge(FAKER.hipster().word(), randomIcon()))
|
||||||
|
.collect(Collectors.toCollection(
|
||||||
|
FXCollections::observableArrayList
|
||||||
|
));
|
||||||
|
|
||||||
|
var cmb = new ComboBox<Badge>(items);
|
||||||
|
cmb.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb.setButtonCell(new BadgeCell());
|
||||||
|
cmb.setCellFactory(c -> new BadgeCell());
|
||||||
|
cmb.getSelectionModel().selectFirst();
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var box = new HBox(cmb);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]ComboBox[/i] provides a custom cell factory that allows for \
|
||||||
|
complete customization of how the items are rendered."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(ComboBoxPage.class, 4), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox colorExample() {
|
||||||
|
//snippet_5:start
|
||||||
|
var cmb1 = new ComboBox<String>();
|
||||||
|
cmb1.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb1.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
|
));
|
||||||
|
cmb1.getSelectionModel().selectFirst();
|
||||||
|
cmb1.pseudoClassStateChanged(Styles.STATE_SUCCESS, true);
|
||||||
|
|
||||||
|
var cmb2 = new ComboBox<String>();
|
||||||
|
cmb2.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb2.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
|
));
|
||||||
|
cmb2.getSelectionModel().selectFirst();
|
||||||
|
cmb2.pseudoClassStateChanged(Styles.STATE_DANGER, true);
|
||||||
|
|
||||||
|
var chb1 = new ChoiceBox<String>();
|
||||||
|
chb1.setPrefWidth(PREF_WIDTH);
|
||||||
|
chb1.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
|
));
|
||||||
|
chb1.getSelectionModel().selectFirst();
|
||||||
|
chb1.pseudoClassStateChanged(Styles.STATE_SUCCESS, true);
|
||||||
|
|
||||||
|
var chb2 = new ChoiceBox<String>();
|
||||||
|
chb2.setPrefWidth(PREF_WIDTH);
|
||||||
|
chb2.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
|
));
|
||||||
|
chb2.getSelectionModel().selectFirst();
|
||||||
|
chb2.pseudoClassStateChanged(Styles.STATE_DANGER, true);
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
|
var grid = new GridPane();
|
||||||
|
grid.setHgap(30);
|
||||||
|
grid.setVgap(10);
|
||||||
|
grid.addRow(0, captionLabel("ComboBox"), cmb1, cmb2);
|
||||||
|
grid.addRow(1, captionLabel("ChoiceBox"), chb1, chb2);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can use [code]Styles.STATE_SUCCESS[/code] or [code]Styles.STATE_DANGER[/code] \
|
||||||
|
pseudo-classes to change the control color. This especially useful to indicate \
|
||||||
|
the validation result."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(grid, new Snippet(ComboBoxPage.class, 5), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox overflowExample() {
|
||||||
|
//snippet_7:start
|
||||||
|
var cmb1 = new ComboBox<String>();
|
||||||
|
cmb1.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb1.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 50)
|
||||||
|
));
|
||||||
|
cmb1.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var cmb2 = new ComboBox<String>();
|
||||||
|
cmb2.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb2.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.chuckNorris().fact(), 10)
|
||||||
|
));
|
||||||
|
cmb2.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var chb1 = new ChoiceBox<String>();
|
||||||
|
chb1.setPrefWidth(PREF_WIDTH);
|
||||||
|
chb1.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 50)
|
||||||
|
));
|
||||||
|
chb1.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var chb2 = new ChoiceBox<String>();
|
||||||
|
chb2.setPrefWidth(PREF_WIDTH);
|
||||||
|
chb2.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.chuckNorris().fact(), 10)
|
||||||
|
));
|
||||||
|
chb2.getSelectionModel().selectFirst();
|
||||||
|
//snippet_7:end
|
||||||
|
|
||||||
|
var grid = new GridPane();
|
||||||
|
grid.setHgap(30);
|
||||||
|
grid.setVgap(10);
|
||||||
|
grid.addRow(0, captionLabel("ComboBox"), cmb1, cmb2);
|
||||||
|
grid.addRow(1, captionLabel("ChoiceBox"), chb1, chb2);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This is just a simple example to test what happens when the item size \
|
||||||
|
exceeds the limits of the popup window."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(grid, new Snippet(ComboBoxPage.class, 7), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox altIconExample() {
|
||||||
|
//snippet_6:start
|
||||||
|
var cmb = new ComboBox<String>();
|
||||||
|
cmb.setPrefWidth(PREF_WIDTH);
|
||||||
|
cmb.getStyleClass().add(Tweaks.ALT_ICON);
|
||||||
|
cmb.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
|
));
|
||||||
|
cmb.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var chb = new ChoiceBox<String>();
|
||||||
|
chb.setPrefWidth(PREF_WIDTH);
|
||||||
|
chb.getStyleClass().add(Tweaks.ALT_ICON);
|
||||||
|
chb.setItems(FXCollections.observableArrayList(
|
||||||
|
generate(() -> FAKER.hipster().word(), 5)
|
||||||
|
));
|
||||||
|
chb.getSelectionModel().selectFirst();
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
|
var box = new HBox(30, cmb, chb);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
There's additional tweak [code]Tweaks.ALT_ICON[/code] to change the control \
|
||||||
|
dropdown icon."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(ComboBoxPage.class, 6), description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import static javafx.scene.input.KeyCombination.CONTROL_DOWN;
|
||||||
|
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.sampler.page.AbstractPage;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.ContextMenu;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.SeparatorMenuItem;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyCodeCombination;
|
||||||
|
import javafx.scene.input.KeyCombination;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.kordamp.ikonli.Ikon;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
public class ContextMenuPage extends AbstractPage {
|
||||||
|
|
||||||
|
public static final String NAME = "ContextMenu";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContextMenuPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
A popup control containing a list of menu items. It allows for any [i]MenuItem[/i], \
|
||||||
|
including its subclasses, to be inserted. A common use case for this class is \
|
||||||
|
creating and showing context menus to users."""
|
||||||
|
);
|
||||||
|
addNode(contextMenuExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Label contextMenuExample() {
|
||||||
|
var contextMenu = new ContextMenu();
|
||||||
|
|
||||||
|
var undoItem = createItem("_Undo", Feather.CORNER_DOWN_LEFT,
|
||||||
|
new KeyCodeCombination(KeyCode.Z, CONTROL_DOWN));
|
||||||
|
undoItem.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
var redoItem = createItem("_Redo", Feather.CORNER_DOWN_RIGHT,
|
||||||
|
new KeyCodeCombination(KeyCode.Y, CONTROL_DOWN));
|
||||||
|
redoItem.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
contextMenu.getItems().addAll(
|
||||||
|
undoItem,
|
||||||
|
redoItem,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
createItem("Cut", Feather.SCISSORS, new KeyCodeCombination(KeyCode.X, CONTROL_DOWN)),
|
||||||
|
createItem("Copy", Feather.COPY, new KeyCodeCombination(KeyCode.C, CONTROL_DOWN)),
|
||||||
|
createItem("Paste", null, new KeyCodeCombination(KeyCode.V, CONTROL_DOWN))
|
||||||
|
);
|
||||||
|
|
||||||
|
var clickArea = new Label("Right-Click Here");
|
||||||
|
clickArea.setAlignment(Pos.CENTER);
|
||||||
|
clickArea.setMinSize(MAX_WIDTH / 2d, 200);
|
||||||
|
clickArea.setMaxSize(MAX_WIDTH / 2d, 200);
|
||||||
|
clickArea.setContextMenu(contextMenu);
|
||||||
|
clickArea.getStyleClass().add(Styles.BORDERED);
|
||||||
|
|
||||||
|
return clickArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem createItem(@Nullable String text,
|
||||||
|
@Nullable Ikon graphic,
|
||||||
|
@Nullable KeyCombination accelerator) {
|
||||||
|
|
||||||
|
var item = new MenuItem(text);
|
||||||
|
|
||||||
|
if (graphic != null) {
|
||||||
|
item.setGraphic(new FontIcon(graphic));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accelerator != null) {
|
||||||
|
item.setAccelerator(accelerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
@ -1,151 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.STATE_DANGER;
|
|
||||||
import static atlantafx.base.theme.Styles.STATE_SUCCESS;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.CustomTextField;
|
|
||||||
import atlantafx.base.controls.MaskTextField;
|
|
||||||
import atlantafx.base.controls.PasswordTextField;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import java.time.LocalTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.format.DateTimeParseException;
|
|
||||||
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Cursor;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
|
||||||
import org.kordamp.ikonli.material2.Material2OutlinedMZ;
|
|
||||||
|
|
||||||
public class CustomTextFieldPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "CustomTextField";
|
|
||||||
private static final int PREF_WIDTH = 120;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomTextFieldPage() {
|
|
||||||
super();
|
|
||||||
setUserContent(new FlowPane(
|
|
||||||
PAGE_HGAP, PAGE_VGAP,
|
|
||||||
leftIconSample(),
|
|
||||||
rightIconSample(),
|
|
||||||
bothIconsSample(),
|
|
||||||
successSample(),
|
|
||||||
dangerSample(),
|
|
||||||
passwordSample(),
|
|
||||||
maskSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock leftIconSample() {
|
|
||||||
var tf = new CustomTextField();
|
|
||||||
tf.setPromptText("Prompt text");
|
|
||||||
tf.setRight(new FontIcon(Feather.X));
|
|
||||||
tf.setPrefWidth(PREF_WIDTH);
|
|
||||||
return new SampleBlock("Left", tf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock rightIconSample() {
|
|
||||||
var tf = new CustomTextField();
|
|
||||||
tf.setPromptText("Prompt text");
|
|
||||||
tf.setLeft(new FontIcon(Feather.MAP_PIN));
|
|
||||||
tf.setPrefWidth(PREF_WIDTH);
|
|
||||||
return new SampleBlock("Right", tf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock bothIconsSample() {
|
|
||||||
var tf = new CustomTextField("Text");
|
|
||||||
tf.setLeft(new FontIcon(Feather.MAP_PIN));
|
|
||||||
tf.setRight(new FontIcon(Feather.X));
|
|
||||||
tf.setPrefWidth(PREF_WIDTH);
|
|
||||||
return new SampleBlock("Both Sides", tf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock successSample() {
|
|
||||||
var tf = new CustomTextField("Text");
|
|
||||||
tf.pseudoClassStateChanged(STATE_SUCCESS, true);
|
|
||||||
tf.setRight(new FontIcon(Feather.X));
|
|
||||||
tf.setPrefWidth(PREF_WIDTH);
|
|
||||||
return new SampleBlock("Success", tf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock dangerSample() {
|
|
||||||
var tf = new CustomTextField();
|
|
||||||
tf.pseudoClassStateChanged(STATE_DANGER, true);
|
|
||||||
tf.setLeft(new FontIcon(Feather.MAP_PIN));
|
|
||||||
tf.setPrefWidth(PREF_WIDTH);
|
|
||||||
return new SampleBlock("Danger", tf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock passwordSample() {
|
|
||||||
var tf = new PasswordTextField("qwerty");
|
|
||||||
tf.setPrefWidth(PREF_WIDTH);
|
|
||||||
|
|
||||||
var icon = new FontIcon(Feather.EYE_OFF);
|
|
||||||
icon.setCursor(Cursor.HAND);
|
|
||||||
icon.setOnMouseClicked(e -> {
|
|
||||||
if (tf.revealPasswordProperty().get()) {
|
|
||||||
tf.revealPasswordProperty().set(false);
|
|
||||||
icon.setIconCode(Feather.EYE_OFF);
|
|
||||||
} else {
|
|
||||||
tf.revealPasswordProperty().set(true);
|
|
||||||
icon.setIconCode(Feather.EYE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
tf.setRight(icon);
|
|
||||||
|
|
||||||
return new SampleBlock("Password", tf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock maskSample() {
|
|
||||||
var phoneField = new MaskTextField("(999) 999 99 99");
|
|
||||||
phoneField.setPromptText("(999) 999 99 99");
|
|
||||||
phoneField.setLeft(new FontIcon(Material2OutlinedMZ.PHONE));
|
|
||||||
phoneField.setPrefWidth(180);
|
|
||||||
|
|
||||||
var cardField = new MaskTextField("9999-9999-9999-9999");
|
|
||||||
cardField.setLeft(new FontIcon(Material2OutlinedAL.CREDIT_CARD));
|
|
||||||
cardField.setPrefWidth(200);
|
|
||||||
|
|
||||||
var timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
|
|
||||||
var timeField = new MaskTextField("29:59");
|
|
||||||
timeField.setText(LocalTime.now(ZoneId.systemDefault()).format(timeFormatter));
|
|
||||||
timeField.setLeft(new FontIcon(Material2OutlinedMZ.TIMER));
|
|
||||||
timeField.setPrefWidth(120);
|
|
||||||
timeField.textProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null) {
|
|
||||||
try {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
LocalTime.parse(val, timeFormatter);
|
|
||||||
timeField.pseudoClassStateChanged(STATE_DANGER, false);
|
|
||||||
} catch (DateTimeParseException e) {
|
|
||||||
timeField.pseudoClassStateChanged(STATE_DANGER, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var content = new HBox(
|
|
||||||
BLOCK_HGAP,
|
|
||||||
new VBox(5, new Label("Phone Number"), phoneField),
|
|
||||||
new VBox(5, new Label("Bank Card"), cardField),
|
|
||||||
new VBox(5, new Label("Time"), timeField)
|
|
||||||
);
|
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
return new SampleBlock("Input Mask", content);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,334 +2,111 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import static atlantafx.base.theme.Styles.FLAT;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import static javafx.scene.layout.GridPane.REMAINING;
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
|
||||||
import atlantafx.base.controls.InlineDatePicker;
|
|
||||||
import atlantafx.base.controls.Spacer;
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalTime;
|
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.chrono.HijrahChronology;
|
import java.time.chrono.HijrahChronology;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Objects;
|
import java.time.format.DateTimeParseException;
|
||||||
import javafx.animation.Animation;
|
|
||||||
import javafx.animation.KeyFrame;
|
|
||||||
import javafx.animation.Timeline;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.DateCell;
|
|
||||||
import javafx.scene.control.DatePicker;
|
import javafx.scene.control.DatePicker;
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
import javafx.scene.layout.Region;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.util.Callback;
|
|
||||||
import javafx.util.Duration;
|
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.material2.Material2AL;
|
|
||||||
|
|
||||||
public class DatePickerPage extends AbstractPage {
|
public class DatePickerPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "DatePicker";
|
public static final String NAME = "DatePicker";
|
||||||
private static final LocalDate TODAY = LocalDate.now(ZoneId.systemDefault());
|
|
||||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_DATE;
|
|
||||||
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
|
||||||
private static final String DATE_FORMATTER_PROMPT = "yyyy-MM-dd";
|
|
||||||
private static final int INLINE_DATE_PICKER_COL = 0;
|
|
||||||
private static final int INLINE_DATE_PICKER_ROW = 4;
|
|
||||||
private static final CSSFragment NO_YEAR_MONTH_STYLE = new CSSFragment("""
|
|
||||||
.date-picker-popup >.month-year-pane {
|
|
||||||
visibility: hidden;
|
|
||||||
-fx-min-width: 0;
|
|
||||||
-fx-pref-width: 0;
|
|
||||||
-fx-max-width: 0;
|
|
||||||
-fx-min-height: 0;
|
|
||||||
-fx-pref-height: 0;
|
|
||||||
-fx-max-height: 0;
|
|
||||||
}
|
|
||||||
""");
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BooleanProperty weekNumProperty = new SimpleBooleanProperty();
|
|
||||||
private final BooleanProperty showClockProperty = new SimpleBooleanProperty();
|
|
||||||
private final BooleanProperty editableProperty = new SimpleBooleanProperty();
|
|
||||||
private final BooleanProperty offPastDatesProperty = new SimpleBooleanProperty();
|
|
||||||
private final BooleanProperty disableProperty = new SimpleBooleanProperty();
|
|
||||||
private final Clock clock = new Clock();
|
|
||||||
private DatePickerColorSelector colorSelector;
|
|
||||||
|
|
||||||
public DatePickerPage() {
|
public DatePickerPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new VBox(
|
|
||||||
new SampleBlock("Playground", createPlayground())
|
addFormattedText("""
|
||||||
));
|
A date picker control that allows the user to enter a date as text or to select \
|
||||||
|
a date from a calendar popup. The calendar is based on either the standard \
|
||||||
|
ISO-8601 chronology or any of the other chronology classes defined in the \
|
||||||
|
[code]java.time.chrono[/code] package."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Editable", editableExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private GridPane createPlayground() {
|
public ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var today = LocalDate.now(ZoneId.systemDefault());
|
||||||
|
|
||||||
|
var dp1 = new DatePicker(today);
|
||||||
|
dp1.setPrefWidth(200);
|
||||||
|
|
||||||
|
var dp2 = new DatePicker(today.plusDays(10));
|
||||||
|
dp2.setShowWeekNumbers(true);
|
||||||
|
dp2.setPrefWidth(200);
|
||||||
|
|
||||||
|
var dp3 = new DatePicker(today.plusMonths(1));
|
||||||
|
dp3.setChronology(HijrahChronology.INSTANCE);
|
||||||
|
dp3.setPrefWidth(200);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
var grid = new GridPane();
|
var grid = new GridPane();
|
||||||
grid.setHgap(40);
|
grid.setHgap(50);
|
||||||
grid.setVgap(BLOCK_VGAP);
|
grid.setVgap(10);
|
||||||
|
grid.addRow(0, captionLabel("Default"), dp1);
|
||||||
|
grid.addRow(1, captionLabel("Week Numbers"), dp2);
|
||||||
|
grid.addRow(2, captionLabel("Second Chronology"), dp3);
|
||||||
|
|
||||||
final var popupDatePicker = createPopupDatePicker();
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]DatePicker[/i] control consists of a combo box with a date \
|
||||||
|
field and a date chooser."""
|
||||||
|
);
|
||||||
|
|
||||||
colorSelector = new DatePickerColorSelector(grid);
|
return new ExampleBox(grid, new Snippet(getClass(), 1), description);
|
||||||
final var inlineDatePicker = createInlineDatePicker(null);
|
}
|
||||||
|
|
||||||
// == CONTROLS ==
|
public ExampleBox editableExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
final var today = LocalDate.now(ZoneId.systemDefault());
|
||||||
|
final var formatter = DateTimeFormatter.ISO_DATE;
|
||||||
|
|
||||||
var weekNumToggle = new ToggleSwitch("Week numbers");
|
var dp = new DatePicker(today);
|
||||||
weekNumProperty.bind(weekNumToggle.selectedProperty());
|
dp.setPromptText("yyyy-MM-dd");
|
||||||
weekNumToggle.setSelected(true);
|
dp.setEditable(true);
|
||||||
|
dp.setPrefWidth(200);
|
||||||
|
dp.setConverter(new StringConverter<>() {
|
||||||
|
@Override
|
||||||
|
public String toString(LocalDate localDate) {
|
||||||
|
if (localDate == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return formatter.format(localDate);
|
||||||
|
}
|
||||||
|
|
||||||
var showClockToggle = new ToggleSwitch("Show clock");
|
@Override
|
||||||
showClockProperty.bind(showClockToggle.selectedProperty());
|
public LocalDate fromString(String dateString) {
|
||||||
showClockToggle.setSelected(true);
|
if (dateString == null || dateString.trim().isEmpty()) {
|
||||||
|
return today;
|
||||||
var showYearMonthToggle = new ToggleSwitch("Show header");
|
}
|
||||||
showYearMonthToggle.setSelected(true);
|
try {
|
||||||
showYearMonthToggle.selectedProperty().addListener((obs, old, val) -> {
|
return LocalDate.parse(dateString, formatter);
|
||||||
if (!val) {
|
} catch (DateTimeParseException e) {
|
||||||
NO_YEAR_MONTH_STYLE.addTo(grid);
|
return today;
|
||||||
} else {
|
}
|
||||||
NO_YEAR_MONTH_STYLE.removeFrom(grid);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
var chronologyToggle = new ToggleSwitch("Second chronology");
|
var box = new HBox(dp);
|
||||||
chronologyToggle.selectedProperty().addListener(
|
var description = BBCodeParser.createFormattedText("""
|
||||||
(obs, old, val) -> popupDatePicker.setChronology(val ? HijrahChronology.INSTANCE : null)
|
The [code]editable[/code] property controls whether the [i]DatePicker[/i] \
|
||||||
|
allows users to manually input a date."""
|
||||||
);
|
);
|
||||||
|
|
||||||
var editableToggle = new ToggleSwitch("Editable");
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
editableProperty.bind(editableToggle.selectedProperty());
|
|
||||||
// clear selected value to demonstrate prompt text
|
|
||||||
editableProperty.addListener(
|
|
||||||
(obs, old, val) -> popupDatePicker.setValue(val ? null : TODAY)
|
|
||||||
);
|
|
||||||
|
|
||||||
var offPastDatesToggle = new ToggleSwitch("No past dates");
|
|
||||||
offPastDatesProperty.bind(offPastDatesToggle.selectedProperty());
|
|
||||||
offPastDatesProperty.addListener((obs, old, val) -> {
|
|
||||||
popupDatePicker.setDayCellFactory(val ? dp -> new FutureDateCell() : null);
|
|
||||||
popupDatePicker.setValue(TODAY);
|
|
||||||
|
|
||||||
// we have to create new date picker, because changing cell factory won't update existing cells
|
|
||||||
var datePicker = createInlineDatePicker(val ? dp -> new FutureDateCell() : null);
|
|
||||||
grid.getChildren().removeIf(n -> n instanceof InlineDatePicker);
|
|
||||||
grid.add(datePicker, INLINE_DATE_PICKER_COL, INLINE_DATE_PICKER_ROW);
|
|
||||||
});
|
|
||||||
|
|
||||||
var disablePickerToggle = new ToggleSwitch("Disable");
|
|
||||||
disableProperty.bind(disablePickerToggle.selectedProperty());
|
|
||||||
|
|
||||||
var controls = new VBox(
|
|
||||||
BLOCK_VGAP,
|
|
||||||
weekNumToggle,
|
|
||||||
showClockToggle,
|
|
||||||
showYearMonthToggle,
|
|
||||||
chronologyToggle,
|
|
||||||
editableToggle,
|
|
||||||
offPastDatesToggle,
|
|
||||||
disablePickerToggle
|
|
||||||
);
|
|
||||||
controls.setAlignment(Pos.CENTER_RIGHT);
|
|
||||||
|
|
||||||
// == GRID ==
|
|
||||||
|
|
||||||
var defaultLabel = new Label("Default");
|
|
||||||
defaultLabel.getStyleClass().add(Styles.TEXT_BOLD);
|
|
||||||
|
|
||||||
var inlineLabel = new Label("Inline");
|
|
||||||
inlineLabel.getStyleClass().add(Styles.TEXT_BOLD);
|
|
||||||
|
|
||||||
grid.add(defaultLabel, 0, 0);
|
|
||||||
grid.add(popupDatePicker, 0, 1);
|
|
||||||
grid.add(new Spacer(20), 0, 2);
|
|
||||||
grid.add(inlineLabel, 0, 3);
|
|
||||||
grid.add(inlineDatePicker, INLINE_DATE_PICKER_COL, INLINE_DATE_PICKER_ROW);
|
|
||||||
grid.add(controls, 1, 0, 1, REMAINING);
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DatePicker createPopupDatePicker() {
|
|
||||||
var datePicker = new DatePicker();
|
|
||||||
datePicker.setConverter(DATE_CONVERTER);
|
|
||||||
datePicker.setPromptText(DATE_FORMATTER_PROMPT);
|
|
||||||
datePicker.setMaxWidth(Double.MAX_VALUE);
|
|
||||||
datePicker.setValue(TODAY);
|
|
||||||
datePicker.showWeekNumbersProperty().bind(weekNumProperty);
|
|
||||||
datePicker.editableProperty().bind(editableProperty);
|
|
||||||
datePicker.disableProperty().bind(disableProperty);
|
|
||||||
return datePicker;
|
|
||||||
}
|
|
||||||
|
|
||||||
private InlineDatePicker createInlineDatePicker(Callback<InlineDatePicker, DateCell> dayCellFactory) {
|
|
||||||
var datePicker = new InlineDatePicker();
|
|
||||||
datePicker.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
|
|
||||||
datePicker.setDayCellFactory(dayCellFactory);
|
|
||||||
datePicker.setValue(TODAY);
|
|
||||||
datePicker.showWeekNumbersProperty().bind(weekNumProperty);
|
|
||||||
datePicker.disableProperty().bind(disableProperty);
|
|
||||||
|
|
||||||
datePicker.setTopNode(clock);
|
|
||||||
datePicker.setBottomNode(colorSelector);
|
|
||||||
|
|
||||||
datePicker.topNodeProperty().bind(Bindings.createObjectBinding(
|
|
||||||
() -> showClockProperty.get() ? clock : null, showClockProperty)
|
|
||||||
);
|
|
||||||
|
|
||||||
return datePicker;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private static final StringConverter<LocalDate> DATE_CONVERTER = new StringConverter<>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(LocalDate localDate) {
|
|
||||||
if (localDate == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return DATE_FORMATTER.format(localDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalDate fromString(String dateString) {
|
|
||||||
if (dateString == null || dateString.trim().isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return LocalDate.parse(dateString, DATE_FORMATTER);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static class FutureDateCell extends DateCell {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateItem(LocalDate date, boolean empty) {
|
|
||||||
super.updateItem(date, empty);
|
|
||||||
setDisable(empty || date.isBefore(TODAY));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Clock extends VBox {
|
|
||||||
|
|
||||||
public Clock() {
|
|
||||||
var clockLabel = new Label(
|
|
||||||
TIME_FORMATTER.format(LocalTime.now(ZoneId.systemDefault()))
|
|
||||||
);
|
|
||||||
clockLabel.getStyleClass().add(Styles.TITLE_2);
|
|
||||||
|
|
||||||
var dateLabel = new Label(
|
|
||||||
DateTimeFormatter.ofPattern("EEEE, LLLL dd, yyyy").format(LocalDate.now(ZoneId.systemDefault()))
|
|
||||||
);
|
|
||||||
|
|
||||||
setStyle("""
|
|
||||||
-fx-border-width: 0 0 0.5 0;
|
|
||||||
-fx-border-color: -color-border-default;"""
|
|
||||||
);
|
|
||||||
setSpacing(BLOCK_VGAP);
|
|
||||||
getChildren().setAll(clockLabel, dateLabel);
|
|
||||||
|
|
||||||
var t = new Timeline(new KeyFrame(
|
|
||||||
Duration.seconds(1),
|
|
||||||
e -> clockLabel.setText(
|
|
||||||
TIME_FORMATTER.format(LocalTime.now(ZoneId.systemDefault()))
|
|
||||||
)
|
|
||||||
));
|
|
||||||
t.setCycleCount(Animation.INDEFINITE);
|
|
||||||
t.playFromStart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This class shares stylesheet with the AccentColorSelector
|
|
||||||
private static class DatePickerColorSelector extends HBox {
|
|
||||||
|
|
||||||
private final Pane parent;
|
|
||||||
private final ObjectProperty<CSSFragment> style = new SimpleObjectProperty<>();
|
|
||||||
|
|
||||||
public DatePickerColorSelector(Pane parent) {
|
|
||||||
super();
|
|
||||||
this.parent = Objects.requireNonNull(parent);
|
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
|
||||||
var resetBtn = new Button("", new FontIcon(Material2AL.CLEAR));
|
|
||||||
resetBtn.getStyleClass().addAll(BUTTON_ICON, FLAT);
|
|
||||||
resetBtn.setOnAction(e -> style.set(null));
|
|
||||||
|
|
||||||
style.addListener((obs, old, val) -> {
|
|
||||||
if (old != null) {
|
|
||||||
old.removeFrom(parent);
|
|
||||||
}
|
|
||||||
if (val != null) {
|
|
||||||
val.addTo(parent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setAlignment(Pos.CENTER);
|
|
||||||
getChildren().setAll(
|
|
||||||
colorButton("-color-accent-emphasis", "-color-fg-emphasis"),
|
|
||||||
colorButton("-color-success-emphasis", "-color-fg-emphasis"),
|
|
||||||
colorButton("-color-danger-emphasis", "-color-fg-emphasis"),
|
|
||||||
resetBtn
|
|
||||||
);
|
|
||||||
getStyleClass().add("color-selector");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Button colorButton(String bgColorName, String fgColorName) {
|
|
||||||
var icon = new Region();
|
|
||||||
icon.getStyleClass().add("icon");
|
|
||||||
icon.setStyle("-color-primary:" + bgColorName + ";");
|
|
||||||
|
|
||||||
var btn = new Button("", icon);
|
|
||||||
btn.getStyleClass().addAll(BUTTON_ICON, FLAT, "color-button");
|
|
||||||
btn.setOnAction(e -> updateStyle(bgColorName, fgColorName));
|
|
||||||
|
|
||||||
return btn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateStyle(String bgColorName, String fgColorName) {
|
|
||||||
style.set(new CSSFragment(String.format("""
|
|
||||||
.date-picker-popup {
|
|
||||||
-color-date-border: %s;
|
|
||||||
-color-date-month-year-bg: %s;
|
|
||||||
-color-date-month-year-fg: %s;
|
|
||||||
}
|
|
||||||
.date-picker-popup >.top-node {
|
|
||||||
-fx-background-color: %s;
|
|
||||||
-color-fg-default: %s;
|
|
||||||
-color-border-default: %s;
|
|
||||||
-fx-border-color: %s;
|
|
||||||
}""",
|
|
||||||
bgColorName, bgColorName, fgColorName,
|
|
||||||
bgColorName, fgColorName, fgColorName, fgColorName
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,38 +2,30 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static javafx.scene.control.Alert.AlertType;
|
import static javafx.scene.control.Alert.AlertType;
|
||||||
import static javafx.scene.control.ButtonBar.ButtonData;
|
import static javafx.scene.control.ButtonBar.ButtonData;
|
||||||
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.geometry.Orientation;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.ChoiceDialog;
|
import javafx.scene.control.ChoiceDialog;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Separator;
|
|
||||||
import javafx.scene.control.TextArea;
|
import javafx.scene.control.TextArea;
|
||||||
import javafx.scene.control.TextInputDialog;
|
import javafx.scene.control.TextInputDialog;
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.stage.StageStyle;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
public class DialogPage extends AbstractPage {
|
public class DialogPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Dialog";
|
public static final String NAME = "Dialog";
|
||||||
|
|
||||||
@ -42,96 +34,69 @@ public class DialogPage extends AbstractPage {
|
|||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BooleanProperty showHeaderProperty = new SimpleBooleanProperty(true);
|
|
||||||
private final BooleanProperty minDecorationsProperty = new SimpleBooleanProperty(true);
|
|
||||||
|
|
||||||
public DialogPage() {
|
public DialogPage() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
addFormattedText("""
|
||||||
var showHeaderToggle = new ToggleSwitch("Show header");
|
Dialog is a user interface component that allows to create dialog windows \
|
||||||
showHeaderProperty.bind(showHeaderToggle.selectedProperty());
|
that can be used to prompt users for information or to display messages or warnings."""
|
||||||
showHeaderToggle.setSelected(true);
|
|
||||||
|
|
||||||
var minDecorationsToggle = new ToggleSwitch("Minimum decorations");
|
|
||||||
minDecorationsProperty.bind(minDecorationsToggle.selectedProperty());
|
|
||||||
minDecorationsToggle.setSelected(true);
|
|
||||||
|
|
||||||
var controls = new HBox(BLOCK_HGAP, showHeaderToggle, minDecorationsToggle);
|
|
||||||
controls.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
var samples = new FlowPane(
|
|
||||||
PAGE_HGAP, PAGE_VGAP,
|
|
||||||
infoDialogSample(),
|
|
||||||
warningDialogSample(),
|
|
||||||
errorDialogSample(),
|
|
||||||
exceptionDialogSample(),
|
|
||||||
confirmationDialogSample(),
|
|
||||||
textInputDialogSample(),
|
|
||||||
choiceDialogSample()
|
|
||||||
);
|
);
|
||||||
|
addSection("Notifications", notificationDialogExample());
|
||||||
setUserContent(new VBox(
|
addSection("Exception Dialog", exceptionDialogExample());
|
||||||
10,
|
addSection("Confirmation Dialog", confirmationDialogExample());
|
||||||
controls,
|
addSection("Text Input Dialog", textInputDialogExample());
|
||||||
new Separator(Orientation.HORIZONTAL),
|
addSection("Choice Dialog", choiceDialogExample());
|
||||||
samples
|
addSection("No Header", notificationNoHeaderDialogExample());
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock infoDialogSample() {
|
private ExampleBox notificationDialogExample() {
|
||||||
var button = new Button("Click", new FontIcon(Feather.INFO));
|
//snippet_1:start
|
||||||
button.setOnAction(e -> {
|
var infoBtn = new Button("Info", new FontIcon(Feather.INFO));
|
||||||
|
infoBtn.setOnAction(e -> {
|
||||||
var alert = new Alert(AlertType.INFORMATION);
|
var alert = new Alert(AlertType.INFORMATION);
|
||||||
alert.setTitle("Information Dialog");
|
alert.setTitle("Information Dialog");
|
||||||
alert.setHeaderText(randomHeader());
|
alert.setHeaderText(FAKER.chuckNorris().fact());
|
||||||
alert.setContentText(FAKER.lorem().paragraph(3));
|
alert.setContentText(FAKER.lorem().paragraph(3));
|
||||||
alert.initOwner(getScene().getWindow());
|
alert.initOwner(getScene().getWindow());
|
||||||
alert.initStyle(getModality());
|
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
});
|
});
|
||||||
|
|
||||||
return new SampleBlock("Information", button);
|
var warnBtn = new Button("Click", new FontIcon(Feather.ALERT_TRIANGLE));
|
||||||
}
|
warnBtn.setOnAction(e -> {
|
||||||
|
|
||||||
private SampleBlock warningDialogSample() {
|
|
||||||
var button = new Button("Click", new FontIcon(Feather.ALERT_TRIANGLE));
|
|
||||||
button.setOnAction(e -> {
|
|
||||||
var alert = new Alert(AlertType.WARNING);
|
var alert = new Alert(AlertType.WARNING);
|
||||||
alert.setTitle("Warning Dialog");
|
alert.setTitle("Warning Dialog");
|
||||||
alert.setHeaderText(randomHeader());
|
alert.setHeaderText(FAKER.chuckNorris().fact());
|
||||||
alert.setContentText(FAKER.lorem().paragraph(3));
|
alert.setContentText(FAKER.lorem().paragraph(3));
|
||||||
alert.initOwner(getScene().getWindow());
|
alert.initOwner(getScene().getWindow());
|
||||||
alert.initStyle(getModality());
|
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
});
|
});
|
||||||
|
|
||||||
return new SampleBlock("Warning", button);
|
var errorBtn = new Button("Click", new FontIcon(Feather.X_CIRCLE));
|
||||||
}
|
errorBtn.setOnAction(e -> {
|
||||||
|
|
||||||
private SampleBlock errorDialogSample() {
|
|
||||||
var button = new Button("Click", new FontIcon(Feather.X_CIRCLE));
|
|
||||||
button.setOnAction(e -> {
|
|
||||||
var alert = new Alert(AlertType.ERROR);
|
var alert = new Alert(AlertType.ERROR);
|
||||||
alert.setTitle("Error Dialog");
|
alert.setTitle("Error Dialog");
|
||||||
alert.setHeaderText(randomHeader());
|
alert.setHeaderText(FAKER.chuckNorris().fact());
|
||||||
alert.setContentText(FAKER.lorem().paragraph(3));
|
alert.setContentText(FAKER.lorem().paragraph(3));
|
||||||
alert.initOwner(getScene().getWindow());
|
alert.initOwner(getScene().getWindow());
|
||||||
alert.initStyle(getModality());
|
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
});
|
});
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
return new SampleBlock("Error", button);
|
var box = new HBox(30, infoBtn, warnBtn, errorBtn);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Pre-built dialog types for displaying information, warnings, and errors."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock exceptionDialogSample() {
|
private ExampleBox exceptionDialogExample() {
|
||||||
|
//snippet_2:start
|
||||||
var button = new Button("Click", new FontIcon(Feather.MEH));
|
var button = new Button("Click", new FontIcon(Feather.MEH));
|
||||||
button.setOnAction(e -> {
|
button.setOnAction(e -> {
|
||||||
var alert = new Alert(AlertType.ERROR);
|
var alert = new Alert(AlertType.ERROR);
|
||||||
alert.setTitle("Exception Dialog");
|
alert.setTitle("Exception Dialog");
|
||||||
alert.setHeaderText(randomHeader());
|
alert.setHeaderText(FAKER.chuckNorris().fact());
|
||||||
alert.setContentText(FAKER.lorem().paragraph(3));
|
alert.setContentText(FAKER.lorem().paragraph(3));
|
||||||
|
|
||||||
var exception = new RuntimeException(FAKER.chuckNorris().fact());
|
var exception = new RuntimeException(FAKER.chuckNorris().fact());
|
||||||
@ -155,75 +120,128 @@ public class DialogPage extends AbstractPage {
|
|||||||
|
|
||||||
alert.getDialogPane().setExpandableContent(content);
|
alert.getDialogPane().setExpandableContent(content);
|
||||||
alert.initOwner(getScene().getWindow());
|
alert.initOwner(getScene().getWindow());
|
||||||
alert.initStyle(getModality());
|
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
});
|
});
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
return new SampleBlock("Exception", button);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A custom dialog that is designed to display information about exceptions that \
|
||||||
|
are thrown in JavaFX applications."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(new HBox(button), new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock confirmationDialogSample() {
|
private ExampleBox confirmationDialogExample() {
|
||||||
var button = new Button("Click", new FontIcon(Feather.CHECK_SQUARE));
|
//snippet_3:start
|
||||||
|
var button = new Button(
|
||||||
|
"Click", new FontIcon(Feather.CHECK_SQUARE)
|
||||||
|
);
|
||||||
button.setOnAction(e -> {
|
button.setOnAction(e -> {
|
||||||
var alert = new Alert(AlertType.CONFIRMATION);
|
var alert = new Alert(AlertType.CONFIRMATION);
|
||||||
alert.setTitle("Confirmation Dialog");
|
alert.setTitle("Confirmation Dialog");
|
||||||
alert.setHeaderText(randomHeader());
|
alert.setHeaderText(FAKER.chuckNorris().fact());
|
||||||
alert.setContentText(FAKER.lorem().paragraph(3));
|
alert.setContentText(FAKER.lorem().paragraph(3));
|
||||||
|
|
||||||
ButtonType yesBtn = new ButtonType("Yes", ButtonData.YES);
|
ButtonType yesBtn = new ButtonType("Yes", ButtonData.YES);
|
||||||
ButtonType noBtn = new ButtonType("No", ButtonData.NO);
|
ButtonType noBtn = new ButtonType("No", ButtonData.NO);
|
||||||
ButtonType cancelBtn = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
|
ButtonType cancelBtn = new ButtonType(
|
||||||
|
"Cancel", ButtonData.CANCEL_CLOSE
|
||||||
|
);
|
||||||
|
|
||||||
alert.getButtonTypes().setAll(yesBtn, noBtn, cancelBtn);
|
alert.getButtonTypes().setAll(yesBtn, noBtn, cancelBtn);
|
||||||
|
|
||||||
alert.initOwner(getScene().getWindow());
|
alert.initOwner(getScene().getWindow());
|
||||||
alert.initStyle(getModality());
|
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
});
|
});
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
return new SampleBlock("Confirmation", button);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The confirmation alert type configures the [i]Alert[/i] dialog to appear in a way that \
|
||||||
|
suggests the content of the dialog is seeking confirmation from the user."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(new HBox(button), new Snippet(getClass(), 3), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock textInputDialogSample() {
|
private ExampleBox textInputDialogExample() {
|
||||||
|
//snippet_4:start
|
||||||
var button = new Button("Click", new FontIcon(Feather.EDIT_2));
|
var button = new Button("Click", new FontIcon(Feather.EDIT_2));
|
||||||
button.setOnAction(e -> {
|
button.setOnAction(e -> {
|
||||||
var dialog = new TextInputDialog();
|
var dialog = new TextInputDialog();
|
||||||
dialog.setTitle("Text Input Dialog");
|
dialog.setTitle("Text Input Dialog");
|
||||||
dialog.setHeaderText(randomHeader());
|
dialog.setHeaderText(FAKER.chuckNorris().fact());
|
||||||
dialog.setContentText("Enter your name:");
|
dialog.setContentText("Enter your name:");
|
||||||
dialog.initOwner(getScene().getWindow());
|
dialog.initOwner(getScene().getWindow());
|
||||||
dialog.initStyle(getModality());
|
|
||||||
dialog.showAndWait();
|
dialog.showAndWait();
|
||||||
});
|
});
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
return new SampleBlock("Text Input", button);
|
var description = BBCodeParser.createFormattedText(
|
||||||
|
"A dialog that shows a text input control to the user."
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(new HBox(button), new Snippet(getClass(), 4), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock choiceDialogSample() {
|
private ExampleBox choiceDialogExample() {
|
||||||
|
//snippet_5:start
|
||||||
var button = new Button("Click", new FontIcon(Feather.LIST));
|
var button = new Button("Click", new FontIcon(Feather.LIST));
|
||||||
button.setOnAction(e -> {
|
button.setOnAction(e -> {
|
||||||
var choices = new ArrayList<>();
|
var choices = List.of("A", "B", "C");
|
||||||
choices.add("A");
|
|
||||||
choices.add("B");
|
|
||||||
choices.add("C");
|
|
||||||
|
|
||||||
var dialog = new ChoiceDialog<>(choices.get(0), choices);
|
var dialog = new ChoiceDialog<>(choices.get(0), choices);
|
||||||
dialog.setTitle("Choice Dialog");
|
dialog.setTitle("Choice Dialog");
|
||||||
dialog.setHeaderText(randomHeader());
|
dialog.setHeaderText(FAKER.chuckNorris().fact());
|
||||||
dialog.setContentText("Choose your letter:");
|
dialog.setContentText("Choose your letter:");
|
||||||
dialog.initOwner(getScene().getWindow());
|
dialog.initOwner(getScene().getWindow());
|
||||||
dialog.initStyle(getModality());
|
|
||||||
dialog.showAndWait();
|
dialog.showAndWait();
|
||||||
});
|
});
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
return new SampleBlock("Choice", button);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A dialog that shows a list of choices to the user, from which they can pick one item at most."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(new HBox(button), new Snippet(getClass(), 5), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String randomHeader() {
|
private ExampleBox notificationNoHeaderDialogExample() {
|
||||||
return showHeaderProperty.get() ? FAKER.chuckNorris().fact() : null;
|
//snippet_6:start
|
||||||
}
|
var infoBtn = new Button("Info", new FontIcon(Feather.INFO));
|
||||||
|
infoBtn.setOnAction(e -> {
|
||||||
|
var alert = new Alert(AlertType.INFORMATION);
|
||||||
|
alert.setTitle("Information Dialog");
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText(FAKER.lorem().paragraph(3));
|
||||||
|
alert.initOwner(getScene().getWindow());
|
||||||
|
alert.showAndWait();
|
||||||
|
});
|
||||||
|
|
||||||
private StageStyle getModality() {
|
var warnBtn = new Button("Click", new FontIcon(Feather.ALERT_TRIANGLE));
|
||||||
return minDecorationsProperty.get() ? StageStyle.UTILITY : StageStyle.DECORATED;
|
warnBtn.setOnAction(e -> {
|
||||||
|
var alert = new Alert(AlertType.WARNING);
|
||||||
|
alert.setTitle("Warning Dialog");
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText(FAKER.lorem().paragraph(3));
|
||||||
|
alert.initOwner(getScene().getWindow());
|
||||||
|
alert.showAndWait();
|
||||||
|
});
|
||||||
|
|
||||||
|
var errorBtn = new Button("Click", new FontIcon(Feather.X_CIRCLE));
|
||||||
|
errorBtn.setOnAction(e -> {
|
||||||
|
var alert = new Alert(AlertType.ERROR);
|
||||||
|
alert.setTitle("Error Dialog");
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText(FAKER.lorem().paragraph(3));
|
||||||
|
alert.initOwner(getScene().getWindow());
|
||||||
|
alert.showAndWait();
|
||||||
|
});
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
|
var box = new HBox(30, infoBtn, warnBtn, errorBtn);
|
||||||
|
var description = BBCodeParser.createFormattedText(
|
||||||
|
"The header text can be hidden."
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 6), description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,18 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
import atlantafx.base.theme.Theme;
|
import atlantafx.base.theme.Theme;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.event.DefaultEventBus;
|
import atlantafx.sampler.event.DefaultEventBus;
|
||||||
import atlantafx.sampler.event.ThemeEvent;
|
import atlantafx.sampler.event.ThemeEvent;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.AbstractPage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.theme.HighlightJSTheme;
|
import atlantafx.sampler.theme.HighlightJSTheme;
|
||||||
import atlantafx.sampler.theme.ThemeManager;
|
import atlantafx.sampler.theme.ThemeManager;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import javafx.scene.web.HTMLEditor;
|
import javafx.scene.web.HTMLEditor;
|
||||||
|
|
||||||
public class HtmlEditorPage extends AbstractPage {
|
public class HtmlEditorPage extends AbstractPage {
|
||||||
@ -30,13 +27,16 @@ public class HtmlEditorPage extends AbstractPage {
|
|||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HTMLEditor editor = createEditor();
|
private HTMLEditor editor = createHTMLEditor();
|
||||||
|
|
||||||
public HtmlEditorPage() {
|
public HtmlEditorPage() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
setUserContent(new VBox(editorSample()));
|
addFormattedText("""
|
||||||
editor.requestFocus();
|
A control that allows for users to edit text, and apply styling to this text. \
|
||||||
|
The underlying data model is HTML, although this is not shown visually to the end-user."""
|
||||||
|
);
|
||||||
|
addNode(editorSample());
|
||||||
|
|
||||||
// update editor colors on app theme change
|
// update editor colors on app theme change
|
||||||
DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> {
|
DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> {
|
||||||
@ -45,42 +45,46 @@ public class HtmlEditorPage extends AbstractPage {
|
|||||||
editor.requestFocus();
|
editor.requestFocus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
editor.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock editorSample() {
|
private VBox editorSample() {
|
||||||
var description = new Text("""
|
var description = BBCodeParser.createFormattedText("""
|
||||||
HTMLEditor toolbar buttons use images from 'com/sun/javafx/scene/control/skin/modena'.
|
Since AtlantaFX themes are also distributed as CSS files, they can't contain any images. \
|
||||||
In opposite, since AtlantaFX themes are also distributed as single CSS files, it contains no images.
|
Unfortunately, reusing Modena resources in theme also isn't possible, because the they \
|
||||||
Unfortunately reusing Modena resources isn't possible, because the package isn't opened in OpenJFX
|
are located in [font=monospace]'com/sun/javafx/*'[/font] package, which isn't opened in \
|
||||||
'module-info'.
|
OpenJFX [font=monospace]'module-info'[/font]. But you can still copy Modena images and \
|
||||||
"""
|
overwrite [i]HMTLEditor[/i] CSS in your app."""
|
||||||
);
|
);
|
||||||
|
|
||||||
var fixToggle = new ToggleSwitch("Apply Fix");
|
var fixToggle = new ToggleSwitch("Apply Fix");
|
||||||
|
|
||||||
var content = new VBox(BLOCK_VGAP, editor, new TextFlow(description), fixToggle);
|
var content = new VBox(20, editor, description, fixToggle);
|
||||||
content.setAlignment(Pos.CENTER);
|
content.setAlignment(Pos.TOP_CENTER);
|
||||||
|
VBox.setVgrow(content, Priority.ALWAYS);
|
||||||
|
|
||||||
fixToggle.selectedProperty().addListener((obs, old, val) -> {
|
fixToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
// toolbar icons can't be changed back without creating new editor instance #javafx-bug
|
// toolbar icons can't be changed back without creating new editor instance #javafx-bug
|
||||||
try {
|
try {
|
||||||
editor = createEditor();
|
editor = createHTMLEditor();
|
||||||
editor.pseudoClassStateChanged(USE_LOCAL_URL, val);
|
editor.pseudoClassStateChanged(USE_LOCAL_URL, val);
|
||||||
content.getChildren().set(0, editor);
|
content.getChildren().set(0, editor);
|
||||||
editor.requestFocus();
|
editor.requestFocus();
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
// hush internal HTML editor errors, because everything
|
// hush internal HTML editor errors, because everything
|
||||||
// we do here is ugly hacks around legacy control anyway
|
// we do here is an ugly hack around legacy control anyway
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return new SampleBlock("Playground", content);
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HTMLEditor createEditor() {
|
private HTMLEditor createHTMLEditor() {
|
||||||
var editor = new HTMLEditor();
|
var editor = new HTMLEditor();
|
||||||
editor.setPrefHeight(400);
|
editor.setPrefHeight(400);
|
||||||
editor.setHtmlText(generateContent());
|
editor.setHtmlText(generateContent());
|
||||||
|
VBox.setVgrow(editor, Priority.ALWAYS);
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +92,10 @@ public class HtmlEditorPage extends AbstractPage {
|
|||||||
var tm = ThemeManager.getInstance();
|
var tm = ThemeManager.getInstance();
|
||||||
Theme samplerTheme = tm.getTheme();
|
Theme samplerTheme = tm.getTheme();
|
||||||
HighlightJSTheme hlTheme = tm.getMatchingSourceCodeHighlightTheme(samplerTheme);
|
HighlightJSTheme hlTheme = tm.getMatchingSourceCodeHighlightTheme(samplerTheme);
|
||||||
|
var text = String.join("<br/><br/>", generate(
|
||||||
|
() -> String.join(" ", FAKER.lorem().paragraphs(5)), 5)
|
||||||
|
);
|
||||||
|
|
||||||
return "<!DOCTYPE html>"
|
return "<!DOCTYPE html>"
|
||||||
+ "<html>"
|
+ "<html>"
|
||||||
+ "<body style=\""
|
+ "<body style=\""
|
||||||
@ -96,7 +104,7 @@ public class HtmlEditorPage extends AbstractPage {
|
|||||||
+ "font-family:" + tm.getFontFamily() + ";"
|
+ "font-family:" + tm.getFontFamily() + ";"
|
||||||
+ "font-size:" + tm.getFontSize() + "px;"
|
+ "font-size:" + tm.getFontSize() + "px;"
|
||||||
+ "\">"
|
+ "\">"
|
||||||
+ String.join("<br/><br/>", FAKER.lorem().paragraphs(10))
|
+ text
|
||||||
+ "</body>"
|
+ "</body>"
|
||||||
+ "</html>";
|
+ "</html>";
|
||||||
}
|
}
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.CheckBox;
|
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.MenuButton;
|
|
||||||
import javafx.scene.control.MenuItem;
|
|
||||||
import javafx.scene.control.TextField;
|
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
public class InputGroupPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "Input Group";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputGroupPage() {
|
|
||||||
super();
|
|
||||||
setUserContent(new VBox(
|
|
||||||
Page.PAGE_VGAP,
|
|
||||||
expandingHBox(httpMethodSample(), passwordSample()),
|
|
||||||
expandingHBox(networkSample(), dropdownSample()),
|
|
||||||
labelSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock httpMethodSample() {
|
|
||||||
var leftCombo = new ComboBox<>();
|
|
||||||
leftCombo.getItems().addAll("POST", "GET", "PUT", "PATCH", "DELETE");
|
|
||||||
leftCombo.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
leftCombo.getSelectionModel().selectFirst();
|
|
||||||
|
|
||||||
var rightText = new TextField("https://example.org");
|
|
||||||
rightText.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
|
|
||||||
var box = new HBox(leftCombo, rightText);
|
|
||||||
box.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
return new SampleBlock("ComboBox & TextField", box);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock passwordSample() {
|
|
||||||
var leftPassword = new TextField();
|
|
||||||
leftPassword.setText(FAKER.internet().password());
|
|
||||||
leftPassword.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
|
|
||||||
var rightBtn = new Button("", new FontIcon(Feather.REFRESH_CW));
|
|
||||||
rightBtn.getStyleClass().addAll(BUTTON_ICON);
|
|
||||||
rightBtn.setOnAction(e -> leftPassword.setText(FAKER.internet().password()));
|
|
||||||
rightBtn.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
|
|
||||||
var box = new HBox(leftPassword, rightBtn);
|
|
||||||
box.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
return new SampleBlock("Text Field & Button", box);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock networkSample() {
|
|
||||||
var leftText = new TextField("192.168.1.10");
|
|
||||||
leftText.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
leftText.setPrefWidth(140);
|
|
||||||
|
|
||||||
var centerText = new TextField("24");
|
|
||||||
centerText.getStyleClass().add(Styles.CENTER_PILL);
|
|
||||||
centerText.setPrefWidth(70);
|
|
||||||
|
|
||||||
var rightText = new TextField("192.168.1.1");
|
|
||||||
rightText.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
rightText.setPrefWidth(140);
|
|
||||||
|
|
||||||
var box = new HBox(leftText, centerText, rightText);
|
|
||||||
box.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
return new SampleBlock("Text Fields", box);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock dropdownSample() {
|
|
||||||
var rightText = new TextField(FAKER.harryPotter().spell());
|
|
||||||
rightText.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
|
|
||||||
var spellItem = new MenuItem("Spell");
|
|
||||||
spellItem.setOnAction(e -> rightText.setText(FAKER.harryPotter().spell()));
|
|
||||||
|
|
||||||
var characterItem = new MenuItem("Character");
|
|
||||||
characterItem.setOnAction(e -> rightText.setText(FAKER.harryPotter().character()));
|
|
||||||
|
|
||||||
var locationItem = new MenuItem("Location");
|
|
||||||
locationItem.setOnAction(e -> rightText.setText(FAKER.harryPotter().location()));
|
|
||||||
|
|
||||||
var leftMenu = new MenuButton("Generate");
|
|
||||||
leftMenu.getItems().addAll(spellItem, characterItem, locationItem);
|
|
||||||
leftMenu.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
|
|
||||||
var box = new HBox(leftMenu, rightText);
|
|
||||||
box.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
return new SampleBlock("MenuButton & TextField", box);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock labelSample() {
|
|
||||||
var leftLabel1 = new Label("", new CheckBox());
|
|
||||||
leftLabel1.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
|
|
||||||
var rightText1 = new TextField();
|
|
||||||
rightText1.setPromptText("Username");
|
|
||||||
rightText1.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
rightText1.setPrefWidth(100);
|
|
||||||
|
|
||||||
var sample1 = new HBox(leftLabel1, rightText1);
|
|
||||||
sample1.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
var leftText2 = new TextField("johndoe");
|
|
||||||
leftText2.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
leftText2.setPrefWidth(100);
|
|
||||||
|
|
||||||
var centerLabel2 = new Label("@");
|
|
||||||
centerLabel2.getStyleClass().add(Styles.CENTER_PILL);
|
|
||||||
|
|
||||||
var rightText2 = new TextField("gmail.com");
|
|
||||||
rightText2.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
rightText2.setPrefWidth(100);
|
|
||||||
|
|
||||||
var sample2 = new HBox(leftText2, centerLabel2, rightText2);
|
|
||||||
sample2.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
var leftText3 = new TextField("+123456");
|
|
||||||
leftText3.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
leftText3.setPrefWidth(100);
|
|
||||||
|
|
||||||
var rightLabel3 = new Label("", new FontIcon(Feather.DOLLAR_SIGN));
|
|
||||||
rightLabel3.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
|
|
||||||
var sample3 = new HBox(leftText3, rightLabel3);
|
|
||||||
sample3.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
var flowPane = new FlowPane(
|
|
||||||
BLOCK_HGAP, BLOCK_VGAP,
|
|
||||||
sample1,
|
|
||||||
sample2,
|
|
||||||
sample3
|
|
||||||
);
|
|
||||||
|
|
||||||
return new SampleBlock("Label & TextField", flowPane);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,12 +2,9 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.AbstractPage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.FlowPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
@ -25,17 +22,16 @@ public class LabelPage extends AbstractPage {
|
|||||||
|
|
||||||
public LabelPage() {
|
public LabelPage() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
|
addFormattedText("""
|
||||||
|
Label is a non-editable text control. A [i]Label[/i] is useful for displaying text that \
|
||||||
|
is required to fit within a specific space, and thus may need to use an ellipsis \
|
||||||
|
or truncation to size the string to fit."""
|
||||||
|
);
|
||||||
|
addNode(colorExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private VBox colorExample() {
|
||||||
setUserContent(new VBox(
|
|
||||||
PAGE_VGAP,
|
|
||||||
expandingHBox(colorSample())
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock colorSample() {
|
|
||||||
var defaultLabel = new Label("default", createFontIcon());
|
var defaultLabel = new Label("default", createFontIcon());
|
||||||
|
|
||||||
var accentLabel = new Label("accent", createFontIcon());
|
var accentLabel = new Label("accent", createFontIcon());
|
||||||
@ -56,17 +52,23 @@ public class LabelPage extends AbstractPage {
|
|||||||
var subtleLabel = new Label("subtle", createFontIcon());
|
var subtleLabel = new Label("subtle", createFontIcon());
|
||||||
subtleLabel.getStyleClass().add(Styles.TEXT_SUBTLE);
|
subtleLabel.getStyleClass().add(Styles.TEXT_SUBTLE);
|
||||||
|
|
||||||
var content = new VBox(
|
var description = BBCodeParser.createFormattedText("""
|
||||||
BLOCK_VGAP,
|
You can use pseudo-classes to set the [i]Label[/i] color. Note that icon \
|
||||||
new Label("You can also use pseudo-classes to set Label color."),
|
inherits label color by default."""
|
||||||
new Label("Note that icon inherits label color by default."),
|
);
|
||||||
new FlowPane(
|
|
||||||
BLOCK_HGAP, BLOCK_VGAP,
|
|
||||||
defaultLabel, accentLabel, successLabel, warningLabel, dangerLabel,
|
|
||||||
mutedLabel, subtleLabel
|
|
||||||
));
|
|
||||||
|
|
||||||
return new SampleBlock("Colors", content);
|
var labels = new FlowPane(
|
||||||
|
20, 20,
|
||||||
|
defaultLabel,
|
||||||
|
accentLabel,
|
||||||
|
successLabel,
|
||||||
|
warningLabel,
|
||||||
|
dangerLabel,
|
||||||
|
mutedLabel,
|
||||||
|
subtleLabel
|
||||||
|
);
|
||||||
|
|
||||||
|
return new VBox(20, description, labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FontIcon createFontIcon(String... stylesClass) {
|
private FontIcon createFontIcon(String... stylesClass) {
|
||||||
|
@ -1,337 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.ACCENT;
|
|
||||||
import static atlantafx.base.theme.Styles.BORDERED;
|
|
||||||
import static atlantafx.base.theme.Styles.DENSE;
|
|
||||||
import static atlantafx.base.theme.Styles.STRIPED;
|
|
||||||
import static atlantafx.base.theme.Styles.toggleStyleClass;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
|
||||||
import atlantafx.base.theme.Tweaks;
|
|
||||||
import atlantafx.sampler.fake.domain.Book;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.Hyperlink;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.ListCell;
|
|
||||||
import javafx.scene.control.ListView;
|
|
||||||
import javafx.scene.control.cell.CheckBoxListCell;
|
|
||||||
import javafx.scene.control.cell.ChoiceBoxListCell;
|
|
||||||
import javafx.scene.control.cell.ComboBoxListCell;
|
|
||||||
import javafx.scene.control.cell.TextFieldListCell;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.util.StringConverter;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
public class ListPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "ListView";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<Book> dataList = generate(() -> Book.random(FAKER), 50);
|
|
||||||
private final StringConverter<Book> bookStringConverter = new BookStringConverter(dataList);
|
|
||||||
private final BorderPane listWrapper = new BorderPane();
|
|
||||||
private final ComboBox<Example> exampleSelect = createExampleSelect();
|
|
||||||
|
|
||||||
public ListPage() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
var sample = new SampleBlock("Playground", createPlayground());
|
|
||||||
sample.setFillHeight(true);
|
|
||||||
setUserContent(sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createPlayground() {
|
|
||||||
var borderedToggle = new ToggleSwitch("Bordered");
|
|
||||||
borderedToggle.selectedProperty()
|
|
||||||
.addListener((obs, old, value) -> toggleListProperty(lv -> toggleStyleClass(lv, BORDERED)));
|
|
||||||
|
|
||||||
var denseToggle = new ToggleSwitch("Dense");
|
|
||||||
denseToggle.selectedProperty()
|
|
||||||
.addListener((obs, old, value) -> toggleListProperty(lv -> toggleStyleClass(lv, DENSE)));
|
|
||||||
|
|
||||||
var stripedToggle = new ToggleSwitch("Striped");
|
|
||||||
stripedToggle.selectedProperty()
|
|
||||||
.addListener((obs, old, value) -> toggleListProperty(lv -> toggleStyleClass(lv, STRIPED)));
|
|
||||||
|
|
||||||
var edge2edgeToggle = new ToggleSwitch("Edge to edge");
|
|
||||||
edge2edgeToggle.selectedProperty()
|
|
||||||
.addListener((obs, old, value) -> toggleListProperty(lv -> toggleStyleClass(lv, Tweaks.EDGE_TO_EDGE)));
|
|
||||||
|
|
||||||
var disableToggle = new ToggleSwitch("Disable");
|
|
||||||
disableToggle.selectedProperty().addListener((obs, old, val) -> findDisplayedList().ifPresent(lv -> {
|
|
||||||
if (val != null) {
|
|
||||||
lv.setDisable(val);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
var controls = new HBox(BLOCK_HGAP, borderedToggle, denseToggle, stripedToggle, edge2edgeToggle);
|
|
||||||
controls.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
VBox.setVgrow(listWrapper, Priority.ALWAYS);
|
|
||||||
|
|
||||||
var playground = new VBox(
|
|
||||||
BLOCK_VGAP,
|
|
||||||
new HBox(new Label("Select an example:"), new Spacer(), disableToggle),
|
|
||||||
exampleSelect,
|
|
||||||
listWrapper,
|
|
||||||
controls
|
|
||||||
);
|
|
||||||
playground.setMinHeight(100);
|
|
||||||
|
|
||||||
return playground;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ComboBox<Example> createExampleSelect() {
|
|
||||||
var select = new ComboBox<Example>();
|
|
||||||
select.setMaxWidth(Double.MAX_VALUE);
|
|
||||||
select.getItems().setAll(Example.values());
|
|
||||||
|
|
||||||
select.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView<?> newList = createList(val);
|
|
||||||
|
|
||||||
// copy existing style classes and properties to the new list
|
|
||||||
findDisplayedList().ifPresent(lv -> {
|
|
||||||
List<String> currentStyles = lv.getStyleClass();
|
|
||||||
currentStyles.remove("list-view");
|
|
||||||
newList.getStyleClass().addAll(currentStyles);
|
|
||||||
newList.setDisable(lv.isDisable());
|
|
||||||
});
|
|
||||||
|
|
||||||
listWrapper.setCenter(newList);
|
|
||||||
});
|
|
||||||
|
|
||||||
select.setConverter(new StringConverter<>() {
|
|
||||||
@Override
|
|
||||||
public String toString(Example example) {
|
|
||||||
return example == null ? "" : example.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Example fromString(String s) {
|
|
||||||
return Example.find(s);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return select;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onRendered() {
|
|
||||||
super.onRendered();
|
|
||||||
exampleSelect.getSelectionModel().selectFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<ListView<?>> findDisplayedList() {
|
|
||||||
return listWrapper.getChildren().size() > 0
|
|
||||||
? Optional.of((ListView<?>) listWrapper.getChildren().get(0))
|
|
||||||
: Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleListProperty(Consumer<ListView<?>> consumer) {
|
|
||||||
findDisplayedList().ifPresent(lv -> {
|
|
||||||
if (consumer != null) {
|
|
||||||
consumer.accept(lv);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListView<?> createList(Example example) {
|
|
||||||
switch (example) {
|
|
||||||
case TEXT -> {
|
|
||||||
return stringList();
|
|
||||||
}
|
|
||||||
case EDITABLE -> {
|
|
||||||
return editableList();
|
|
||||||
}
|
|
||||||
case CHECK_BOX -> {
|
|
||||||
return checkBoxList();
|
|
||||||
}
|
|
||||||
case CHOICE_BOX -> {
|
|
||||||
return choiceBoxList();
|
|
||||||
}
|
|
||||||
case COMBO_BOX -> {
|
|
||||||
return comboBoxList();
|
|
||||||
}
|
|
||||||
case NESTED_CONTROLS -> {
|
|
||||||
return nestedControlsList();
|
|
||||||
}
|
|
||||||
default -> throw new IllegalArgumentException("Unexpected enum value: " + example);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListView<String> stringList() {
|
|
||||||
var lv = new ListView<String>();
|
|
||||||
lv.getItems().setAll(dataList.stream().map(bookStringConverter::toString).collect(Collectors.toList()));
|
|
||||||
return lv;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListView<String> editableList() {
|
|
||||||
var lv = new ListView<String>();
|
|
||||||
lv.setEditable(true);
|
|
||||||
lv.setCellFactory(TextFieldListCell.forListView());
|
|
||||||
lv.getItems().setAll(
|
|
||||||
// small size to see the empty cells
|
|
||||||
dataList.stream().limit(5).map(bookStringConverter::toString).collect(Collectors.toList())
|
|
||||||
);
|
|
||||||
return lv;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListView<Book> checkBoxList() {
|
|
||||||
var lv = new ListView<Book>();
|
|
||||||
lv.setCellFactory(CheckBoxListCell.forListView(Book::stateProperty, bookStringConverter));
|
|
||||||
lv.getItems().setAll(dataList.stream().limit(10).collect(Collectors.toList()));
|
|
||||||
return lv;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListView<Book> choiceBoxList() {
|
|
||||||
var lv = new ListView<Book>();
|
|
||||||
lv.setEditable(true);
|
|
||||||
lv.setCellFactory(
|
|
||||||
ChoiceBoxListCell.forListView(bookStringConverter, dataList.subList(0, 10).toArray(Book[]::new)));
|
|
||||||
lv.getItems().setAll(dataList.stream().limit(10).collect(Collectors.toList()));
|
|
||||||
return lv;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListView<Book> comboBoxList() {
|
|
||||||
var lv = new ListView<Book>();
|
|
||||||
lv.setEditable(true);
|
|
||||||
lv.setCellFactory(
|
|
||||||
ComboBoxListCell.forListView(bookStringConverter, dataList.subList(0, 10).toArray(Book[]::new)));
|
|
||||||
lv.getItems().setAll(dataList.stream().limit(10).collect(Collectors.toList()));
|
|
||||||
return lv;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListView<Book> nestedControlsList() {
|
|
||||||
var lv = new ListView<Book>();
|
|
||||||
lv.setCellFactory(book -> new NestedControlsListCell());
|
|
||||||
lv.getItems().setAll(dataList.stream().limit(10).collect(Collectors.toList()));
|
|
||||||
return lv;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private enum Example {
|
|
||||||
TEXT("Text"),
|
|
||||||
EDITABLE("TextFieldListCell"),
|
|
||||||
CHECK_BOX("CheckBoxListCell"),
|
|
||||||
CHOICE_BOX("ChoiceBoxListCell"),
|
|
||||||
COMBO_BOX("ComboBoxListCell"),
|
|
||||||
NESTED_CONTROLS("Nested controls");
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
Example(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Example find(String name) {
|
|
||||||
return Arrays.stream(Example.values())
|
|
||||||
.filter(example -> Objects.equals(example.getName(), name))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BookStringConverter extends StringConverter<Book> {
|
|
||||||
|
|
||||||
private final List<Book> dataList;
|
|
||||||
|
|
||||||
public BookStringConverter(List<Book> dataList) {
|
|
||||||
this.dataList = dataList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(Book book) {
|
|
||||||
if (book == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return String.format("\"%s\" by %s", book.getTitle(), book.getAuthor());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Book fromString(String s) {
|
|
||||||
if (s == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sep = s.indexOf("\" by");
|
|
||||||
String title = s.substring(1, sep);
|
|
||||||
String author = s.substring(sep + "\" by".length());
|
|
||||||
|
|
||||||
return dataList.stream()
|
|
||||||
.filter(b -> Objects.equals(b.getTitle(), title) && Objects.equals(b.getAuthor(), author))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NestedControlsListCell extends ListCell<Book> {
|
|
||||||
|
|
||||||
private final HBox root;
|
|
||||||
private final Label titleLabel;
|
|
||||||
private final Hyperlink authorLink;
|
|
||||||
|
|
||||||
public NestedControlsListCell() {
|
|
||||||
titleLabel = new Label();
|
|
||||||
authorLink = new Hyperlink();
|
|
||||||
|
|
||||||
var purchaseBtn = new Button("Purchase");
|
|
||||||
purchaseBtn.getStyleClass().addAll(ACCENT);
|
|
||||||
purchaseBtn.setGraphic(new FontIcon(Feather.SHOPPING_CART));
|
|
||||||
|
|
||||||
root = new HBox(5,
|
|
||||||
titleLabel,
|
|
||||||
new Label(" by"),
|
|
||||||
authorLink,
|
|
||||||
new Spacer(),
|
|
||||||
purchaseBtn
|
|
||||||
);
|
|
||||||
root.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateItem(Book book, boolean empty) {
|
|
||||||
super.updateItem(book, empty);
|
|
||||||
|
|
||||||
if (empty) {
|
|
||||||
setGraphic(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
titleLabel.setText("\"" + book.getTitle() + "\"");
|
|
||||||
authorLink.setText(book.getAuthor());
|
|
||||||
setGraphic(root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,477 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Spacer;
|
||||||
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.theme.Tweaks;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.fake.domain.Book;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.Hyperlink;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ListCell;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.control.cell.CheckBoxListCell;
|
||||||
|
import javafx.scene.control.cell.ChoiceBoxListCell;
|
||||||
|
import javafx.scene.control.cell.ComboBoxListCell;
|
||||||
|
import javafx.scene.control.cell.TextFieldListCell;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
public class ListViewPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "ListView";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListViewPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
A [i]ListView[/i] displays a horizontal or vertical list of items from which the \
|
||||||
|
user may select, or with which the user may interact. A ListView is able to \
|
||||||
|
have its generic type set to represent the type of data in the backing model.""");
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Row Style", rowStyleExample());
|
||||||
|
addSection("Selection Color", selectionColorExample());
|
||||||
|
addSection("Edge-to-Edge", edge2EdgeExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var names = FXCollections.observableArrayList("Julia", "Ian", "Sue");
|
||||||
|
var lv = new ListView<>(names);
|
||||||
|
lv.setMinHeight(200);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new VBox(20, lv);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can create a list view by instantiating the \
|
||||||
|
[font=monospace]javafx.scene.control.ListView[/font] class."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox rowStyleExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var names = FXCollections.observableArrayList("Julia", "Ian", "Sue");
|
||||||
|
var lv = new ListView<>(names);
|
||||||
|
lv.setMinHeight(200);
|
||||||
|
|
||||||
|
var borderToggle = new ToggleSwitch("Bordered");
|
||||||
|
borderToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(lv, Styles.BORDERED)
|
||||||
|
);
|
||||||
|
|
||||||
|
var stripeToggle = new ToggleSwitch("Striped");
|
||||||
|
stripeToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(lv, Styles.STRIPED)
|
||||||
|
);
|
||||||
|
|
||||||
|
var denseToggle = new ToggleSwitch("Dense");
|
||||||
|
denseToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(lv, Styles.DENSE)
|
||||||
|
);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var togglesBox = new HBox(HGAP_20, borderToggle, stripeToggle, denseToggle);
|
||||||
|
togglesBox.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_10, lv, togglesBox);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]ListView[/i] rows can be styled simply by adding CSS classes:
|
||||||
|
|
||||||
|
[ul]
|
||||||
|
[li][code]Styles.BORDERED[/code] - adds borders between the rows.[/li]
|
||||||
|
[li][code]Styles.STRIPED[/code] - adds zebra-striping.[/li]
|
||||||
|
[li][code]Styles.DENSE[/code] - makes the [i]ListView[/i] more compact by cutting \
|
||||||
|
cell padding.[/li][/ul]"""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox selectionColorExample() {
|
||||||
|
var style = """
|
||||||
|
-color-cell-bg-selected: -color-accent-emphasis;
|
||||||
|
-color-cell-fg-selected: -color-fg-emphasis;
|
||||||
|
-color-cell-bg-selected-focused: -color-accent-emphasis;
|
||||||
|
-color-cell-fg-selected-focused: -color-fg-emphasis;""";
|
||||||
|
|
||||||
|
//snippet_3:start
|
||||||
|
var names = FXCollections.observableArrayList("Julia", "Ian", "Sue");
|
||||||
|
var lv = new ListView<>(names);
|
||||||
|
lv.setMinHeight(200);
|
||||||
|
// -color-cell-bg-selected: -color-accent-emphasis;
|
||||||
|
// -color-cell-fg-selected: -color-fg-emphasis;
|
||||||
|
// -color-cell-bg-selected-focused: -color-accent-emphasis;
|
||||||
|
// -color-cell-fg-selected-focused: -color-fg-emphasis;
|
||||||
|
lv.setStyle(style);
|
||||||
|
lv.getSelectionModel().selectFirst();
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(lv);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Cell selection color (and more) can be changed via looked-up color variables. \
|
||||||
|
You can find all supported color variables in the docs."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox edge2EdgeExample() {
|
||||||
|
//snippet_5:start
|
||||||
|
var names = FXCollections.observableArrayList("Julia", "Ian", "Sue");
|
||||||
|
var lv = new ListView<>(names);
|
||||||
|
lv.setMinHeight(150);
|
||||||
|
lv.getStyleClass().add(Tweaks.EDGE_TO_EDGE);
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
|
var box = new VBox(lv);
|
||||||
|
box.setStyle("""
|
||||||
|
-fx-border-color: -color-accent-emphasis;
|
||||||
|
-fx-border-width: 2px;"""
|
||||||
|
);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Use [code]Tweaks.EDGE_TO_EDGE[/code] style class to remove the [i]ListView[/i] \
|
||||||
|
outer borders. This is useful if you want to place the table into external \
|
||||||
|
container that already has its own borders."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 5), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private final List<Book> dataList = generate(() -> Book.random(FAKER), 50);
|
||||||
|
private final StringConverter<Book> bookStringConverter = new BookStringConverter(dataList);
|
||||||
|
private final StackPane listWrapper = new StackPane();
|
||||||
|
private final ComboBox<Example> exampleSelect = createExampleSelect();
|
||||||
|
|
||||||
|
private VBox playground() {
|
||||||
|
var borderToggle = new ToggleSwitch("Bordered");
|
||||||
|
borderToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, value) -> toggleListProperty(lv ->
|
||||||
|
Styles.toggleStyleClass(lv, Styles.BORDERED)
|
||||||
|
));
|
||||||
|
|
||||||
|
var denseToggle = new ToggleSwitch("Dense");
|
||||||
|
denseToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, value) -> toggleListProperty(lv ->
|
||||||
|
Styles.toggleStyleClass(lv, Styles.DENSE)
|
||||||
|
));
|
||||||
|
|
||||||
|
var stripeToggle = new ToggleSwitch("Striped");
|
||||||
|
stripeToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, value) -> toggleListProperty(lv ->
|
||||||
|
Styles.toggleStyleClass(lv, Styles.STRIPED)
|
||||||
|
));
|
||||||
|
|
||||||
|
var edge2edgeToggle = new ToggleSwitch("Edge to edge");
|
||||||
|
edge2edgeToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, value) -> toggleListProperty(lv ->
|
||||||
|
Styles.toggleStyleClass(lv, Tweaks.EDGE_TO_EDGE)
|
||||||
|
));
|
||||||
|
|
||||||
|
var controls = new HBox(
|
||||||
|
HGAP_20,
|
||||||
|
borderToggle, denseToggle, stripeToggle, edge2edgeToggle
|
||||||
|
);
|
||||||
|
controls.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
listWrapper.setMinHeight(400);
|
||||||
|
VBox.setVgrow(listWrapper, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The playground demonstrates the most important [i]ListView[/i] features \
|
||||||
|
and also serves as an object for monkey testing."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new VBox(
|
||||||
|
VGAP_10,
|
||||||
|
description,
|
||||||
|
new HBox(new Label("Select an example:")),
|
||||||
|
exampleSelect,
|
||||||
|
listWrapper,
|
||||||
|
controls
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComboBox<Example> createExampleSelect() {
|
||||||
|
var select = new ComboBox<Example>();
|
||||||
|
select.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
select.getItems().setAll(Example.values());
|
||||||
|
|
||||||
|
select.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView<?> newList = createList(val);
|
||||||
|
|
||||||
|
// copy existing style classes and properties to the new list
|
||||||
|
findDisplayedList().ifPresent(lv -> {
|
||||||
|
List<String> currentStyles = lv.getStyleClass();
|
||||||
|
currentStyles.remove("list-view");
|
||||||
|
newList.getStyleClass().addAll(currentStyles);
|
||||||
|
newList.setDisable(lv.isDisable());
|
||||||
|
});
|
||||||
|
|
||||||
|
listWrapper.getChildren().setAll(newList);
|
||||||
|
});
|
||||||
|
|
||||||
|
select.setConverter(new StringConverter<>() {
|
||||||
|
@Override
|
||||||
|
public String toString(Example example) {
|
||||||
|
return example == null ? "" : example.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Example fromString(String s) {
|
||||||
|
return Example.find(s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRendered() {
|
||||||
|
super.onRendered();
|
||||||
|
exampleSelect.getSelectionModel().selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<ListView<?>> findDisplayedList() {
|
||||||
|
return listWrapper.getChildren().size() > 0
|
||||||
|
? Optional.of((ListView<?>) listWrapper.getChildren().get(0))
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleListProperty(Consumer<ListView<?>> consumer) {
|
||||||
|
findDisplayedList().ifPresent(lv -> {
|
||||||
|
if (consumer != null) {
|
||||||
|
consumer.accept(lv);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListView<?> createList(Example example) {
|
||||||
|
switch (example) {
|
||||||
|
case TEXT -> {
|
||||||
|
return stringList();
|
||||||
|
}
|
||||||
|
case EDITABLE -> {
|
||||||
|
return editableList();
|
||||||
|
}
|
||||||
|
case CHECK_BOX -> {
|
||||||
|
return checkBoxList();
|
||||||
|
}
|
||||||
|
case CHOICE_BOX -> {
|
||||||
|
return choiceBoxList();
|
||||||
|
}
|
||||||
|
case COMBO_BOX -> {
|
||||||
|
return comboBoxList();
|
||||||
|
}
|
||||||
|
case NESTED_CONTROLS -> {
|
||||||
|
return nestedControlsList();
|
||||||
|
}
|
||||||
|
default -> throw new IllegalArgumentException("Unexpected enum value: " + example);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListView<String> stringList() {
|
||||||
|
var lv = new ListView<String>();
|
||||||
|
lv.getItems().setAll(dataList.stream()
|
||||||
|
.map(bookStringConverter::toString).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
return lv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListView<String> editableList() {
|
||||||
|
var lv = new ListView<String>();
|
||||||
|
lv.setEditable(true);
|
||||||
|
lv.setCellFactory(TextFieldListCell.forListView());
|
||||||
|
lv.getItems().setAll(
|
||||||
|
// small size to see the empty cells
|
||||||
|
dataList.stream().limit(5).map(
|
||||||
|
bookStringConverter::toString).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
return lv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListView<Book> checkBoxList() {
|
||||||
|
var lv = new ListView<Book>();
|
||||||
|
lv.setCellFactory(
|
||||||
|
CheckBoxListCell.forListView(Book::stateProperty, bookStringConverter)
|
||||||
|
);
|
||||||
|
lv.getItems().setAll(
|
||||||
|
dataList.stream().limit(10).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
return lv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListView<Book> choiceBoxList() {
|
||||||
|
var lv = new ListView<Book>();
|
||||||
|
lv.setEditable(true);
|
||||||
|
lv.setCellFactory(
|
||||||
|
ChoiceBoxListCell.forListView(
|
||||||
|
bookStringConverter,
|
||||||
|
dataList.subList(0, 10).toArray(Book[]::new))
|
||||||
|
);
|
||||||
|
lv.getItems().setAll(
|
||||||
|
dataList.stream().limit(10).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
return lv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListView<Book> comboBoxList() {
|
||||||
|
var lv = new ListView<Book>();
|
||||||
|
lv.setEditable(true);
|
||||||
|
lv.setCellFactory(
|
||||||
|
ComboBoxListCell.forListView(
|
||||||
|
bookStringConverter,
|
||||||
|
dataList.subList(0, 10).toArray(Book[]::new))
|
||||||
|
);
|
||||||
|
lv.getItems().setAll(
|
||||||
|
dataList.stream().limit(10).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
return lv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListView<Book> nestedControlsList() {
|
||||||
|
var lv = new ListView<Book>();
|
||||||
|
lv.setCellFactory(book -> new NestedControlsListCell());
|
||||||
|
lv.getItems().setAll(
|
||||||
|
dataList.stream().limit(10).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
return lv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Example {
|
||||||
|
TEXT("Text"),
|
||||||
|
EDITABLE("TextFieldListCell"),
|
||||||
|
CHECK_BOX("CheckBoxListCell"),
|
||||||
|
CHOICE_BOX("ChoiceBoxListCell"),
|
||||||
|
COMBO_BOX("ComboBoxListCell"),
|
||||||
|
NESTED_CONTROLS("Nested controls");
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
Example(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Example find(String name) {
|
||||||
|
return Arrays.stream(Example.values())
|
||||||
|
.filter(example -> Objects.equals(example.getName(), name))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BookStringConverter extends StringConverter<Book> {
|
||||||
|
|
||||||
|
private final List<Book> dataList;
|
||||||
|
|
||||||
|
public BookStringConverter(List<Book> dataList) {
|
||||||
|
this.dataList = dataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(Book book) {
|
||||||
|
if (book == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return String.format("\"%s\" by %s", book.getTitle(), book.getAuthor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Book fromString(String s) {
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sep = s.indexOf("\" by");
|
||||||
|
String title = s.substring(1, sep);
|
||||||
|
String author = s.substring(sep + "\" by".length());
|
||||||
|
|
||||||
|
return dataList.stream()
|
||||||
|
.filter(b -> Objects.equals(b.getTitle(), title)
|
||||||
|
&& Objects.equals(b.getAuthor(), author)
|
||||||
|
)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NestedControlsListCell extends ListCell<Book> {
|
||||||
|
|
||||||
|
private final HBox root;
|
||||||
|
private final Label titleLabel;
|
||||||
|
private final Hyperlink authorLink;
|
||||||
|
|
||||||
|
public NestedControlsListCell() {
|
||||||
|
titleLabel = new Label();
|
||||||
|
authorLink = new Hyperlink();
|
||||||
|
|
||||||
|
var purchaseBtn = new Button("Purchase");
|
||||||
|
purchaseBtn.getStyleClass().addAll(Styles.ACCENT);
|
||||||
|
purchaseBtn.setGraphic(new FontIcon(Feather.SHOPPING_CART));
|
||||||
|
|
||||||
|
root = new HBox(5,
|
||||||
|
titleLabel,
|
||||||
|
new Label(" by"),
|
||||||
|
authorLink,
|
||||||
|
new Spacer(),
|
||||||
|
purchaseBtn
|
||||||
|
);
|
||||||
|
root.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateItem(Book book, boolean empty) {
|
||||||
|
super.updateItem(book, empty);
|
||||||
|
|
||||||
|
if (empty) {
|
||||||
|
setGraphic(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.setText("\"" + book.getTitle() + "\"");
|
||||||
|
authorLink.setText(book.getAuthor());
|
||||||
|
setGraphic(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,236 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import static javafx.scene.input.KeyCombination.CONTROL_DOWN;
|
||||||
|
import static javafx.scene.input.KeyCombination.SHIFT_DOWN;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.CaptionMenuItem;
|
||||||
|
import atlantafx.sampler.page.AbstractPage;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.scene.control.CheckMenuItem;
|
||||||
|
import javafx.scene.control.Menu;
|
||||||
|
import javafx.scene.control.MenuBar;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.RadioMenuItem;
|
||||||
|
import javafx.scene.control.SeparatorMenuItem;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyCodeCombination;
|
||||||
|
import javafx.scene.input.KeyCombination;
|
||||||
|
import net.datafaker.Faker;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.kordamp.ikonli.Ikon;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
||||||
|
|
||||||
|
public class MenuBarPage extends AbstractPage {
|
||||||
|
|
||||||
|
public static final String NAME = "MenuBar";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MenuBarPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
A menu bar is a user interface component that typically appears at the top of \
|
||||||
|
an application window or screen, and provides a series of drop-down menus that \
|
||||||
|
allow users to access various features and functions of the application."""
|
||||||
|
);
|
||||||
|
addNode(menuBarSample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleMenuBar menuBarSample() {
|
||||||
|
return new ExampleMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public static class ExampleMenuBar extends MenuBar {
|
||||||
|
|
||||||
|
private static final Faker FAKER = new Faker();
|
||||||
|
private static final EventHandler<ActionEvent> SYSTEM_OUT = System.out::println;
|
||||||
|
|
||||||
|
public ExampleMenuBar() {
|
||||||
|
getMenus().addAll(
|
||||||
|
fileMenu(),
|
||||||
|
editMenu(),
|
||||||
|
viewMenu(),
|
||||||
|
toolsMenu(),
|
||||||
|
aboutMenu()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu fileMenu() {
|
||||||
|
var menu = new Menu("_File");
|
||||||
|
menu.setMnemonicParsing(true);
|
||||||
|
menu.setOnAction(SYSTEM_OUT);
|
||||||
|
|
||||||
|
var newMenu = createItem(
|
||||||
|
"_New", null, new KeyCodeCombination(KeyCode.N, CONTROL_DOWN)
|
||||||
|
);
|
||||||
|
newMenu.setMnemonicParsing(true);
|
||||||
|
newMenu.setOnAction(SYSTEM_OUT);
|
||||||
|
|
||||||
|
var openRecentMenu = new Menu("Open _Recent");
|
||||||
|
openRecentMenu.setMnemonicParsing(true);
|
||||||
|
openRecentMenu.setOnAction(SYSTEM_OUT);
|
||||||
|
openRecentMenu.getItems().addAll(
|
||||||
|
IntStream.range(0, 10)
|
||||||
|
.mapToObj(x -> new MenuItem(FAKER.file().fileName()))
|
||||||
|
.toList()
|
||||||
|
);
|
||||||
|
|
||||||
|
menu.getItems().addAll(
|
||||||
|
newMenu,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
createItem(
|
||||||
|
"Open", Feather.FOLDER, new KeyCodeCombination(KeyCode.O, CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
openRecentMenu,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
createItem(
|
||||||
|
"Save", Feather.SAVE, new KeyCodeCombination(KeyCode.S, CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
new MenuItem("Save As"),
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
new MenuItem("Exit")
|
||||||
|
);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu editMenu() {
|
||||||
|
var menu = new Menu("_Edit");
|
||||||
|
menu.setMnemonicParsing(true);
|
||||||
|
menu.setOnAction(SYSTEM_OUT);
|
||||||
|
menu.getItems().addAll(
|
||||||
|
createItem(
|
||||||
|
"Undo", Feather.CORNER_DOWN_LEFT, new KeyCodeCombination(KeyCode.Z, CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
createItem(
|
||||||
|
"Redo", Feather.CORNER_DOWN_RIGHT, new KeyCodeCombination(KeyCode.Y, CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
createItem(
|
||||||
|
"Cut", Feather.SCISSORS, new KeyCodeCombination(KeyCode.X, CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
createItem(
|
||||||
|
"Copy", Feather.COPY, new KeyCodeCombination(KeyCode.C, CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
createItem(
|
||||||
|
"Paste", Feather.CORNER_DOWN_LEFT, new KeyCodeCombination(KeyCode.V, CONTROL_DOWN)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu viewMenu() {
|
||||||
|
var menu = new Menu("_View");
|
||||||
|
menu.setMnemonicParsing(true);
|
||||||
|
menu.setOnAction(SYSTEM_OUT);
|
||||||
|
|
||||||
|
var showToolbarItem = new CheckMenuItem(
|
||||||
|
"Show Toolbar", new FontIcon(Feather.TOOL)
|
||||||
|
);
|
||||||
|
showToolbarItem.setSelected(true);
|
||||||
|
showToolbarItem.setAccelerator(
|
||||||
|
new KeyCodeCombination(KeyCode.T, CONTROL_DOWN)
|
||||||
|
);
|
||||||
|
|
||||||
|
var showGridItem = new CheckMenuItem(
|
||||||
|
"Show Grid", new FontIcon(Feather.GRID));
|
||||||
|
|
||||||
|
var captionItem = new CaptionMenuItem("Layout");
|
||||||
|
|
||||||
|
var viewToggleGroup = new ToggleGroup();
|
||||||
|
|
||||||
|
var toggleItem1 = new RadioMenuItem(
|
||||||
|
"Single", new FontIcon(Material2OutlinedAL.LOOKS_ONE)
|
||||||
|
);
|
||||||
|
toggleItem1.setSelected(true);
|
||||||
|
toggleItem1.setToggleGroup(viewToggleGroup);
|
||||||
|
|
||||||
|
var toggleItem2 = new RadioMenuItem(
|
||||||
|
"Two Columns", new FontIcon(Material2OutlinedAL.LOOKS_TWO)
|
||||||
|
);
|
||||||
|
toggleItem2.setToggleGroup(viewToggleGroup);
|
||||||
|
|
||||||
|
var toggleItem3 = new RadioMenuItem(
|
||||||
|
"Three Columns", new FontIcon(Material2OutlinedAL.LOOKS_3)
|
||||||
|
);
|
||||||
|
toggleItem3.setToggleGroup(viewToggleGroup);
|
||||||
|
|
||||||
|
menu.getItems().addAll(
|
||||||
|
showToolbarItem,
|
||||||
|
showGridItem,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
captionItem,
|
||||||
|
toggleItem1,
|
||||||
|
toggleItem2,
|
||||||
|
toggleItem3
|
||||||
|
);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu toolsMenu() {
|
||||||
|
var menu = new Menu("_Tools");
|
||||||
|
menu.setMnemonicParsing(true);
|
||||||
|
menu.setOnAction(SYSTEM_OUT);
|
||||||
|
menu.setDisable(true);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu aboutMenu() {
|
||||||
|
var menu = new Menu(
|
||||||
|
"_About", new FontIcon(Feather.HELP_CIRCLE)
|
||||||
|
);
|
||||||
|
menu.setMnemonicParsing(true);
|
||||||
|
menu.setOnAction(SYSTEM_OUT);
|
||||||
|
|
||||||
|
var deeplyNestedMenu = new Menu(
|
||||||
|
"Very...", null,
|
||||||
|
new Menu("Very...", null,
|
||||||
|
new Menu("Deeply", null,
|
||||||
|
new Menu("Nested", null,
|
||||||
|
new MenuItem("Menu")
|
||||||
|
))));
|
||||||
|
|
||||||
|
// NOTE: this won't be displayed because right container is reserved for submenu indication
|
||||||
|
deeplyNestedMenu.setAccelerator(
|
||||||
|
new KeyCodeCombination(KeyCode.DIGIT1, SHIFT_DOWN, CONTROL_DOWN)
|
||||||
|
);
|
||||||
|
|
||||||
|
menu.getItems().addAll(
|
||||||
|
new MenuItem("Help"),
|
||||||
|
new MenuItem("About"),
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
deeplyNestedMenu
|
||||||
|
);
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem createItem(@Nullable String text,
|
||||||
|
@Nullable Ikon graphic,
|
||||||
|
@Nullable KeyCombination accelerator) {
|
||||||
|
|
||||||
|
var item = new MenuItem(text);
|
||||||
|
|
||||||
|
if (graphic != null) {
|
||||||
|
item.setGraphic(new FontIcon(graphic));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accelerator != null) {
|
||||||
|
item.setAccelerator(accelerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,19 +2,12 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.ACCENT;
|
import atlantafx.base.theme.Styles;
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_OUTLINED;
|
|
||||||
import static atlantafx.base.theme.Styles.DANGER;
|
|
||||||
import static atlantafx.base.theme.Styles.FLAT;
|
|
||||||
import static atlantafx.base.theme.Styles.SUCCESS;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Tweaks;
|
import atlantafx.base.theme.Tweaks;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.control.MenuButton;
|
import javafx.scene.control.MenuButton;
|
||||||
@ -23,14 +16,12 @@ import javafx.scene.control.SplitMenuButton;
|
|||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.FlowPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
public class MenuButtonPage extends AbstractPage {
|
public class MenuButtonPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "MenuButton";
|
public static final String NAME = "MenuButton";
|
||||||
private static final int PREF_WIDTH = 150;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -39,91 +30,114 @@ public class MenuButtonPage extends AbstractPage {
|
|||||||
|
|
||||||
public MenuButtonPage() {
|
public MenuButtonPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new VBox(
|
|
||||||
Page.PAGE_VGAP,
|
|
||||||
expandingHBox(basicSample(), iconOnlySample()),
|
|
||||||
expandingHBox(coloredSample(), outlinedSample()),
|
|
||||||
expandingHBox(popupSideSample(), noArrowSample()),
|
|
||||||
disabledSample()
|
|
||||||
|
|
||||||
));
|
addFormattedText("""
|
||||||
|
[i]MenuButton[/i] is a button which, when clicked or pressed, will \
|
||||||
|
show a context (dropdown) menu.
|
||||||
|
|
||||||
|
[i]SplitMenuButton[/i] is a variation of menu button. It is broken into two \
|
||||||
|
pieces, the [i]action[/i] area and the [i]menu open[/i] area. If the user clicks \
|
||||||
|
in the action area, the [i]SplitMenuButton[/i] will act similarly to [i]Button[/i], \
|
||||||
|
while the menu open area of the control will show a context menu if clicked."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Colored", coloredExample());
|
||||||
|
addSection("Popup Side", popupSideExample());
|
||||||
|
addSection("Icon Button", iconButtonExample());
|
||||||
|
addSection("Outlined", outlinedExample());
|
||||||
|
addSection("No Arrow", noArrowExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
private ExampleBox usageExample() {
|
||||||
var basicMenuBtn = new MenuButton("_Menu Button");
|
//snippet_1:start
|
||||||
basicMenuBtn.getItems().setAll(createItems(5));
|
var normalMenuBtn = new MenuButton("_Menu Button");
|
||||||
basicMenuBtn.setMnemonicParsing(true);
|
normalMenuBtn.getItems().setAll(createItems(5));
|
||||||
|
normalMenuBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var basicSplitBtn = new SplitMenuButton(createItems(5));
|
var normalSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
basicSplitBtn.setText("_Split Menu Button");
|
normalSplitBtn.setText("_Split Menu Button");
|
||||||
basicSplitBtn.setMnemonicParsing(true);
|
normalSplitBtn.setMnemonicParsing(true);
|
||||||
|
|
||||||
var flatMenuBtn = new MenuButton("Flat");
|
var flatMenuBtn = new MenuButton("Flat");
|
||||||
flatMenuBtn.getItems().setAll(createItems(5));
|
flatMenuBtn.getItems().setAll(createItems(5));
|
||||||
flatMenuBtn.getStyleClass().add(FLAT);
|
flatMenuBtn.getStyleClass().add(Styles.FLAT);
|
||||||
|
|
||||||
var flatSplitBtn = new SplitMenuButton(createItems(5));
|
var flatSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
flatSplitBtn.setText("Flat");
|
flatSplitBtn.setText("Flat");
|
||||||
flatSplitBtn.getStyleClass().add(FLAT);
|
flatSplitBtn.getStyleClass().add(Styles.FLAT);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
var content = new FlowPane(BLOCK_HGAP, BLOCK_VGAP);
|
var box = new FlowPane(
|
||||||
content.getChildren().addAll(basicMenuBtn, basicSplitBtn, flatMenuBtn, flatSplitBtn);
|
HGAP_20, VGAP_10,
|
||||||
|
normalMenuBtn, normalSplitBtn, flatMenuBtn, flatSplitBtn
|
||||||
|
);
|
||||||
|
|
||||||
return new SampleBlock("Basic", content);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Just like the [i]Button[/i], [i]MenuButton[/i] comes with four \
|
||||||
|
CSS variants: normal (default), colored, outlined, and flat (or text). \
|
||||||
|
To change the appearance of the [i]Button[/i], you set the corresponding \
|
||||||
|
style classes that work as modifiers."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock coloredSample() {
|
private ExampleBox coloredExample() {
|
||||||
|
//snippet_2:start
|
||||||
var accentMenuBtn = new MenuButton("_Accent");
|
var accentMenuBtn = new MenuButton("_Accent");
|
||||||
accentMenuBtn.getItems().setAll(createItems(5));
|
accentMenuBtn.getItems().setAll(createItems(5));
|
||||||
accentMenuBtn.getStyleClass().add(ACCENT);
|
accentMenuBtn.getStyleClass().add(Styles.ACCENT);
|
||||||
accentMenuBtn.setMnemonicParsing(true);
|
accentMenuBtn.setMnemonicParsing(true);
|
||||||
accentMenuBtn.setPrefWidth(PREF_WIDTH);
|
accentMenuBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var accentSplitBtn = new SplitMenuButton(createItems(5));
|
var accentSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
accentSplitBtn.setText("Accent");
|
accentSplitBtn.setText("Accent");
|
||||||
accentSplitBtn.getStyleClass().add(ACCENT);
|
accentSplitBtn.getStyleClass().add(Styles.ACCENT);
|
||||||
accentSplitBtn.setPrefWidth(PREF_WIDTH);
|
accentSplitBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var successMenuBtn = new MenuButton("Success");
|
var successMenuBtn = new MenuButton("Success");
|
||||||
successMenuBtn.getItems().setAll(createItems(5));
|
successMenuBtn.getItems().setAll(createItems(5));
|
||||||
successMenuBtn.setGraphic(new FontIcon(Feather.CHECK));
|
successMenuBtn.setGraphic(new FontIcon(Feather.CHECK));
|
||||||
successMenuBtn.getStyleClass().add(SUCCESS);
|
successMenuBtn.getStyleClass().add(Styles.SUCCESS);
|
||||||
successMenuBtn.setPrefWidth(PREF_WIDTH);
|
successMenuBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var successSplitBtn = new SplitMenuButton(createItems(5));
|
var successSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
successSplitBtn.setGraphic(new FontIcon(Feather.CHECK));
|
successSplitBtn.setGraphic(new FontIcon(Feather.CHECK));
|
||||||
successSplitBtn.setText("_Success");
|
successSplitBtn.setText("_Success");
|
||||||
successSplitBtn.getStyleClass().add(SUCCESS);
|
successSplitBtn.getStyleClass().add(Styles.SUCCESS);
|
||||||
successSplitBtn.setMnemonicParsing(true);
|
successSplitBtn.setMnemonicParsing(true);
|
||||||
successSplitBtn.setPrefWidth(PREF_WIDTH);
|
successSplitBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var dangerMenuBtn = new MenuButton("Danger");
|
var dangerMenuBtn = new MenuButton("Danger");
|
||||||
dangerMenuBtn.setGraphic(new FontIcon(Feather.TRASH));
|
dangerMenuBtn.setGraphic(new FontIcon(Feather.TRASH));
|
||||||
dangerMenuBtn.getItems().setAll(createItems(5));
|
dangerMenuBtn.getItems().setAll(createItems(5));
|
||||||
dangerMenuBtn.getStyleClass().add(DANGER);
|
dangerMenuBtn.getStyleClass().add(Styles.DANGER);
|
||||||
dangerMenuBtn.setPrefWidth(PREF_WIDTH);
|
dangerMenuBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var dangerSplitBtn = new SplitMenuButton(createItems(5));
|
var dangerSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
dangerSplitBtn.setGraphic(new FontIcon(Feather.TRASH));
|
dangerSplitBtn.setGraphic(new FontIcon(Feather.TRASH));
|
||||||
dangerSplitBtn.setText("_Danger");
|
dangerSplitBtn.setText("_Danger");
|
||||||
dangerSplitBtn.getStyleClass().add(DANGER);
|
dangerSplitBtn.getStyleClass().add(Styles.DANGER);
|
||||||
dangerSplitBtn.setMnemonicParsing(true);
|
dangerSplitBtn.setMnemonicParsing(true);
|
||||||
dangerSplitBtn.setPrefWidth(PREF_WIDTH);
|
dangerSplitBtn.setPrefWidth(150);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
var content = new GridPane();
|
var grid = new GridPane();
|
||||||
content.setHgap(BLOCK_HGAP);
|
grid.setHgap(HGAP_20);
|
||||||
content.setVgap(BLOCK_VGAP);
|
grid.setVgap(VGAP_10);
|
||||||
content.add(accentMenuBtn, 0, 0);
|
grid.addColumn(0, accentMenuBtn, successMenuBtn, dangerMenuBtn);
|
||||||
content.add(accentSplitBtn, 1, 0);
|
grid.addColumn(1, accentSplitBtn, successSplitBtn, dangerSplitBtn);
|
||||||
content.add(successMenuBtn, 0, 1);
|
|
||||||
content.add(successSplitBtn, 1, 1);
|
|
||||||
content.add(dangerMenuBtn, 0, 2);
|
|
||||||
content.add(dangerSplitBtn, 1, 2);
|
|
||||||
|
|
||||||
return new SampleBlock("Colored", content);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can change the [i]MenuButton[/i] color simply by using predefined \
|
||||||
|
style class modifiers."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(grid, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock popupSideSample() {
|
private ExampleBox popupSideExample() {
|
||||||
|
//snippet_3:start
|
||||||
var topMenuBtn = new MenuButton("Top");
|
var topMenuBtn = new MenuButton("Top");
|
||||||
topMenuBtn.getItems().setAll(createItems(5));
|
topMenuBtn.getItems().setAll(createItems(5));
|
||||||
topMenuBtn.setPopupSide(Side.TOP);
|
topMenuBtn.setPopupSide(Side.TOP);
|
||||||
@ -131,145 +145,165 @@ public class MenuButtonPage extends AbstractPage {
|
|||||||
var rightMenuBtn = new MenuButton("Right");
|
var rightMenuBtn = new MenuButton("Right");
|
||||||
rightMenuBtn.getItems().setAll(createItems(5));
|
rightMenuBtn.getItems().setAll(createItems(5));
|
||||||
rightMenuBtn.setPopupSide(Side.RIGHT);
|
rightMenuBtn.setPopupSide(Side.RIGHT);
|
||||||
rightMenuBtn.getStyleClass().add(ACCENT);
|
rightMenuBtn.getStyleClass().add(Styles.ACCENT);
|
||||||
|
|
||||||
var bottomMenuBtn = new MenuButton("Bottom");
|
var bottomMenuBtn = new MenuButton("Bottom");
|
||||||
bottomMenuBtn.getItems().setAll(createItems(5));
|
bottomMenuBtn.getItems().setAll(createItems(5));
|
||||||
bottomMenuBtn.setPopupSide(Side.BOTTOM);
|
bottomMenuBtn.setPopupSide(Side.BOTTOM);
|
||||||
bottomMenuBtn.getStyleClass().add(SUCCESS);
|
bottomMenuBtn.getStyleClass().add(Styles.SUCCESS);
|
||||||
|
|
||||||
var leftMenuBtn = new MenuButton("Left");
|
var leftMenuBtn = new MenuButton("Left");
|
||||||
leftMenuBtn.getItems().setAll(createItems(5));
|
leftMenuBtn.getItems().setAll(createItems(5));
|
||||||
leftMenuBtn.setPopupSide(Side.LEFT);
|
leftMenuBtn.setPopupSide(Side.LEFT);
|
||||||
leftMenuBtn.getStyleClass().add(DANGER);
|
leftMenuBtn.getStyleClass().add(Styles.DANGER);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
var content = new FlowPane(BLOCK_HGAP, BLOCK_VGAP);
|
var box = new FlowPane(
|
||||||
content.getChildren().addAll(topMenuBtn, rightMenuBtn, bottomMenuBtn, leftMenuBtn);
|
HGAP_20, VGAP_10,
|
||||||
|
topMenuBtn, rightMenuBtn, bottomMenuBtn, leftMenuBtn
|
||||||
|
);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A [i]MenuButton[/i] can be set to show its menu on any side of the button. \
|
||||||
|
This is specified using the [code]popupSide[/code] property. By default the \
|
||||||
|
menu appears below the button. However, regardless of the popupSide specified, \
|
||||||
|
if there is not enough room, the [i]ContextMenu[/i] will be smartly repositioned, \
|
||||||
|
most probably to be on the opposite side of the [i]MenuButton[/i].""");
|
||||||
|
|
||||||
return new SampleBlock("Popup Side", content);
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock iconOnlySample() {
|
private ExampleBox iconButtonExample() {
|
||||||
var basicMenuBtn = new MenuButton();
|
//snippet_4:start
|
||||||
basicMenuBtn.setGraphic(new FontIcon(Feather.MORE_HORIZONTAL));
|
var normalMenuBtn = new MenuButton();
|
||||||
basicMenuBtn.getItems().setAll(createItems(5));
|
normalMenuBtn.setGraphic(new FontIcon(Feather.MORE_HORIZONTAL));
|
||||||
basicMenuBtn.getStyleClass().addAll(BUTTON_ICON);
|
normalMenuBtn.getItems().setAll(createItems(5));
|
||||||
|
normalMenuBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
|
|
||||||
var basicSplitBtn = new SplitMenuButton(createItems(5));
|
var normalSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
basicSplitBtn.setGraphic(new FontIcon(Feather.MORE_HORIZONTAL));
|
normalSplitBtn.setGraphic(new FontIcon(Feather.MORE_HORIZONTAL));
|
||||||
basicSplitBtn.getStyleClass().addAll(BUTTON_ICON);
|
normalSplitBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
|
|
||||||
var accentMenuBtn = new MenuButton();
|
var accentMenuBtn = new MenuButton();
|
||||||
accentMenuBtn.setGraphic(new FontIcon(Feather.MENU));
|
accentMenuBtn.setGraphic(new FontIcon(Feather.MENU));
|
||||||
accentMenuBtn.getItems().setAll(createItems(5));
|
accentMenuBtn.getItems().setAll(createItems(5));
|
||||||
accentMenuBtn.getStyleClass().addAll(BUTTON_ICON, ACCENT);
|
accentMenuBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.ACCENT
|
||||||
|
);
|
||||||
|
|
||||||
var accentSplitBtn = new SplitMenuButton(createItems(5));
|
var accentSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
accentSplitBtn.setGraphic(new FontIcon(Feather.MENU));
|
accentSplitBtn.setGraphic(new FontIcon(Feather.MENU));
|
||||||
accentSplitBtn.getStyleClass().addAll(BUTTON_ICON, ACCENT);
|
accentSplitBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.ACCENT
|
||||||
|
);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
var content = new FlowPane(BLOCK_HGAP, BLOCK_VGAP);
|
var box = new FlowPane(
|
||||||
content.getChildren().addAll(basicMenuBtn, basicSplitBtn, accentMenuBtn, accentSplitBtn);
|
HGAP_20, VGAP_10,
|
||||||
|
normalMenuBtn, normalSplitBtn, accentMenuBtn, accentSplitBtn
|
||||||
|
);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can hide the [i]MenuButton[/i] text by applying [code]Styles.BUTTON_ICON[/code] \
|
||||||
|
style class modifier."""
|
||||||
|
);
|
||||||
|
|
||||||
return new SampleBlock("Icons", content);
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock outlinedSample() {
|
private ExampleBox outlinedExample() {
|
||||||
|
//snippet_5:start
|
||||||
var accentMenuBtn = new MenuButton("Accen_t");
|
var accentMenuBtn = new MenuButton("Accen_t");
|
||||||
accentMenuBtn.getItems().setAll(createItems(5));
|
accentMenuBtn.getItems().setAll(createItems(5));
|
||||||
accentMenuBtn.getStyleClass().addAll(BUTTON_OUTLINED, ACCENT);
|
accentMenuBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_OUTLINED, Styles.ACCENT
|
||||||
|
);
|
||||||
accentMenuBtn.setMnemonicParsing(true);
|
accentMenuBtn.setMnemonicParsing(true);
|
||||||
accentMenuBtn.setPrefWidth(PREF_WIDTH);
|
accentMenuBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var accentSplitBtn = new SplitMenuButton(createItems(5));
|
var accentSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
accentSplitBtn.setText("Accent");
|
accentSplitBtn.setText("Accent");
|
||||||
accentSplitBtn.getStyleClass().addAll(BUTTON_OUTLINED, ACCENT);
|
accentSplitBtn.getStyleClass().addAll(
|
||||||
accentSplitBtn.setPrefWidth(PREF_WIDTH);
|
Styles.BUTTON_OUTLINED, Styles.ACCENT
|
||||||
|
);
|
||||||
|
accentSplitBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var successMenuBtn = new MenuButton("S_uccess");
|
var successMenuBtn = new MenuButton("S_uccess");
|
||||||
successMenuBtn.getItems().setAll(createItems(5));
|
successMenuBtn.getItems().setAll(createItems(5));
|
||||||
successMenuBtn.setGraphic(new FontIcon(Feather.CHECK));
|
successMenuBtn.setGraphic(new FontIcon(Feather.CHECK));
|
||||||
successMenuBtn.getStyleClass().addAll(BUTTON_OUTLINED, SUCCESS);
|
successMenuBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_OUTLINED, Styles.SUCCESS
|
||||||
|
);
|
||||||
successMenuBtn.setMnemonicParsing(true);
|
successMenuBtn.setMnemonicParsing(true);
|
||||||
successMenuBtn.setPrefWidth(PREF_WIDTH);
|
successMenuBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var successSplitBtn = new SplitMenuButton(createItems(5));
|
var successSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
successSplitBtn.setGraphic(new FontIcon(Feather.CHECK));
|
successSplitBtn.setGraphic(new FontIcon(Feather.CHECK));
|
||||||
successSplitBtn.setText("Success");
|
successSplitBtn.setText("Success");
|
||||||
successSplitBtn.getStyleClass().addAll(BUTTON_OUTLINED, SUCCESS);
|
successSplitBtn.getStyleClass().addAll(
|
||||||
successSplitBtn.setPrefWidth(PREF_WIDTH);
|
Styles.BUTTON_OUTLINED, Styles.SUCCESS
|
||||||
|
);
|
||||||
|
successSplitBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var dangerMenuBtn = new MenuButton("Danger");
|
var dangerMenuBtn = new MenuButton("Danger");
|
||||||
dangerMenuBtn.setGraphic(new FontIcon(Feather.TRASH));
|
dangerMenuBtn.setGraphic(new FontIcon(Feather.TRASH));
|
||||||
dangerMenuBtn.getItems().setAll(createItems(5));
|
dangerMenuBtn.getItems().setAll(createItems(5));
|
||||||
dangerMenuBtn.getStyleClass().addAll(BUTTON_OUTLINED, DANGER);
|
dangerMenuBtn.getStyleClass().addAll(
|
||||||
dangerMenuBtn.setPrefWidth(PREF_WIDTH);
|
Styles.BUTTON_OUTLINED, Styles.DANGER
|
||||||
|
);
|
||||||
|
dangerMenuBtn.setPrefWidth(150);
|
||||||
|
|
||||||
var dangerSplitBtn = new SplitMenuButton(createItems(5));
|
var dangerSplitBtn = new SplitMenuButton(createItems(5));
|
||||||
dangerSplitBtn.setGraphic(new FontIcon(Feather.TRASH));
|
dangerSplitBtn.setGraphic(new FontIcon(Feather.TRASH));
|
||||||
dangerSplitBtn.setText("Dan_ger");
|
dangerSplitBtn.setText("Dan_ger");
|
||||||
dangerSplitBtn.getStyleClass().addAll(BUTTON_OUTLINED, DANGER);
|
dangerSplitBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_OUTLINED, Styles.DANGER
|
||||||
|
);
|
||||||
dangerSplitBtn.setMnemonicParsing(true);
|
dangerSplitBtn.setMnemonicParsing(true);
|
||||||
dangerSplitBtn.setPrefWidth(PREF_WIDTH);
|
dangerSplitBtn.setPrefWidth(150);
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
var content = new GridPane();
|
var grid = new GridPane();
|
||||||
content.setHgap(BLOCK_HGAP);
|
grid.setHgap(HGAP_20);
|
||||||
content.setVgap(BLOCK_VGAP);
|
grid.setVgap(VGAP_10);
|
||||||
content.add(accentMenuBtn, 0, 0);
|
grid.addColumn(0, accentMenuBtn, successMenuBtn, dangerMenuBtn);
|
||||||
content.add(accentSplitBtn, 1, 0);
|
grid.addColumn(1, accentSplitBtn, successSplitBtn, dangerSplitBtn);
|
||||||
content.add(successMenuBtn, 0, 1);
|
|
||||||
content.add(successSplitBtn, 1, 1);
|
|
||||||
content.add(dangerMenuBtn, 0, 2);
|
|
||||||
content.add(dangerSplitBtn, 1, 2);
|
|
||||||
|
|
||||||
return new SampleBlock("Outlined", content);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Outlined buttons are medium-emphasis buttons. They contain actions that are \
|
||||||
|
important but aren't the primary action in an app."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(grid, new Snippet(getClass(), 5), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
private ExampleBox noArrowExample() {
|
||||||
var basicMenuBtn = new MenuButton("Menu Button");
|
//snippet_6:start
|
||||||
basicMenuBtn.getItems().setAll(createItems(5));
|
var normalMenuBtn = new MenuButton("_Menu Button");
|
||||||
basicMenuBtn.setDisable(true);
|
normalMenuBtn.getItems().setAll(createItems(5));
|
||||||
|
normalMenuBtn.getStyleClass().addAll(Tweaks.NO_ARROW);
|
||||||
var accentSplitBtn = new SplitMenuButton();
|
|
||||||
accentSplitBtn.setText("Accent");
|
|
||||||
accentSplitBtn.getItems().setAll(createItems(5));
|
|
||||||
accentSplitBtn.getStyleClass().addAll(ACCENT);
|
|
||||||
accentSplitBtn.setDisable(true);
|
|
||||||
|
|
||||||
var flatMenuBtn = new MenuButton("Flat");
|
var flatMenuBtn = new MenuButton("Flat");
|
||||||
flatMenuBtn.getItems().setAll(createItems(5));
|
flatMenuBtn.getItems().setAll(createItems(5));
|
||||||
flatMenuBtn.getStyleClass().addAll(FLAT);
|
flatMenuBtn.getStyleClass().addAll(
|
||||||
flatMenuBtn.setDisable(true);
|
Styles.FLAT, Tweaks.NO_ARROW
|
||||||
|
);
|
||||||
var iconMenuBtn = new MenuButton();
|
|
||||||
iconMenuBtn.getItems().setAll(createItems(5));
|
|
||||||
iconMenuBtn.getStyleClass().addAll(BUTTON_ICON);
|
|
||||||
iconMenuBtn.setDisable(true);
|
|
||||||
|
|
||||||
var sample = new HBox(BLOCK_HGAP);
|
|
||||||
sample.getChildren().addAll(basicMenuBtn, accentSplitBtn, flatMenuBtn, iconMenuBtn);
|
|
||||||
|
|
||||||
return new SampleBlock("Disabled", sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock noArrowSample() {
|
|
||||||
var basicMenuBtn = new MenuButton("_Menu Button");
|
|
||||||
basicMenuBtn.getItems().setAll(createItems(5));
|
|
||||||
basicMenuBtn.getStyleClass().addAll(Tweaks.NO_ARROW);
|
|
||||||
|
|
||||||
var flatMenuBtn = new MenuButton("Flat");
|
|
||||||
flatMenuBtn.getItems().setAll(createItems(5));
|
|
||||||
flatMenuBtn.getStyleClass().addAll(FLAT, Tweaks.NO_ARROW);
|
|
||||||
|
|
||||||
var iconMenuBtn = new MenuButton();
|
var iconMenuBtn = new MenuButton();
|
||||||
iconMenuBtn.setGraphic(new FontIcon(Feather.MORE_HORIZONTAL));
|
iconMenuBtn.setGraphic(new FontIcon(Feather.MORE_HORIZONTAL));
|
||||||
iconMenuBtn.getItems().setAll(createItems(5));
|
iconMenuBtn.getItems().setAll(createItems(5));
|
||||||
iconMenuBtn.getStyleClass().addAll(BUTTON_ICON, Tweaks.NO_ARROW);
|
iconMenuBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Tweaks.NO_ARROW
|
||||||
|
);
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP);
|
var box = new HBox(HGAP_20);
|
||||||
content.getChildren().addAll(basicMenuBtn, flatMenuBtn, iconMenuBtn);
|
box.getChildren().addAll(normalMenuBtn, flatMenuBtn, iconMenuBtn);
|
||||||
|
|
||||||
return new SampleBlock("No Arrow", content);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
To hide the [i]MenuButton[/i] arrow, use the [code]Tweak.NO_ARROW[/code] \
|
||||||
|
style class modifier."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 6), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
@SuppressWarnings("SameParameterValue")
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static javafx.scene.input.KeyCombination.CONTROL_DOWN;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.sampler.fake.SampleMenuBar;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.util.Controls;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.ContextMenu;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.SeparatorMenuItem;
|
|
||||||
import javafx.scene.input.KeyCode;
|
|
||||||
import javafx.scene.input.KeyCodeCombination;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
|
|
||||||
public class MenuPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "Menu";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuPage() {
|
|
||||||
super();
|
|
||||||
setUserContent(new VBox(Page.PAGE_VGAP,
|
|
||||||
menuBarSample(),
|
|
||||||
contextMenuExample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock menuBarSample() {
|
|
||||||
return new SampleBlock("Menu Bar", new SampleMenuBar(FAKER));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock contextMenuExample() {
|
|
||||||
var contextMenu = new ContextMenu();
|
|
||||||
|
|
||||||
var undoItem =
|
|
||||||
Controls.menuItem("_Undo", Feather.CORNER_DOWN_LEFT, new KeyCodeCombination(KeyCode.Z, CONTROL_DOWN));
|
|
||||||
undoItem.setMnemonicParsing(true);
|
|
||||||
|
|
||||||
var redoItem =
|
|
||||||
Controls.menuItem("_Redo", Feather.CORNER_DOWN_RIGHT, new KeyCodeCombination(KeyCode.Y, CONTROL_DOWN));
|
|
||||||
redoItem.setMnemonicParsing(true);
|
|
||||||
|
|
||||||
contextMenu.getItems().addAll(
|
|
||||||
undoItem,
|
|
||||||
redoItem,
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
Controls.menuItem("Cut", Feather.SCISSORS, new KeyCodeCombination(KeyCode.X, CONTROL_DOWN)),
|
|
||||||
Controls.menuItem("Copy", Feather.COPY, new KeyCodeCombination(KeyCode.C, CONTROL_DOWN)),
|
|
||||||
Controls.menuItem("Paste", null, new KeyCodeCombination(KeyCode.V, CONTROL_DOWN))
|
|
||||||
);
|
|
||||||
|
|
||||||
var content = new Label("Right-Click Here");
|
|
||||||
content.setAlignment(Pos.CENTER);
|
|
||||||
content.setMinSize(400, 80);
|
|
||||||
content.setMaxSize(400, 80);
|
|
||||||
content.setContextMenu(contextMenu);
|
|
||||||
content.getStyleClass().add(Styles.BORDERED);
|
|
||||||
|
|
||||||
return new SampleBlock("Context Menu", content);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,239 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.ModalPane;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.geometry.Side;
|
|
||||||
import javafx.geometry.VPos;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.ScrollPane;
|
|
||||||
import javafx.scene.layout.Background;
|
|
||||||
import javafx.scene.layout.BackgroundFill;
|
|
||||||
import javafx.scene.layout.CornerRadii;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.RowConstraints;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javafx.scene.shape.Rectangle;
|
|
||||||
|
|
||||||
public class ModalPanePage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "Modal Pane";
|
|
||||||
|
|
||||||
private final ModalPane modalPaneL1 = new ModalPane();
|
|
||||||
private final ModalPane modalPaneL2 = new ModalPane(-15);
|
|
||||||
private final ModalPane modalPaneL3 = new ModalPane(-20);
|
|
||||||
private VBox centerDialog;
|
|
||||||
private VBox topDialog;
|
|
||||||
private VBox rightDialog;
|
|
||||||
private VBox bottomDialog;
|
|
||||||
private VBox leftDialog;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModalPanePage() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
userContent.getChildren().setAll(
|
|
||||||
modalPaneL1,
|
|
||||||
modalPaneL2,
|
|
||||||
modalPaneL3,
|
|
||||||
new SampleBlock("Playground", createPlayground())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createPlayground() {
|
|
||||||
var controlPane = new GridPane();
|
|
||||||
controlPane.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
|
|
||||||
controlPane.setMaxSize(300, 300);
|
|
||||||
controlPane.setHgap(20);
|
|
||||||
controlPane.setVgap(20);
|
|
||||||
controlPane.getRowConstraints().addAll(
|
|
||||||
new RowConstraints(50, 50, 50, Priority.NEVER, VPos.CENTER, false),
|
|
||||||
new RowConstraints(50, 50, 50, Priority.NEVER, VPos.CENTER, false),
|
|
||||||
new RowConstraints(50, 50, 50, Priority.NEVER, VPos.CENTER, false)
|
|
||||||
);
|
|
||||||
|
|
||||||
var topBtn = new Button("Top");
|
|
||||||
topBtn.setOnAction(e -> {
|
|
||||||
modalPaneL1.setAlignment(Pos.TOP_CENTER);
|
|
||||||
modalPaneL1.usePredefinedTransitionFactories(Side.TOP);
|
|
||||||
modalPaneL1.show(getOrCreateTopDialog());
|
|
||||||
});
|
|
||||||
controlPane.add(topBtn, 1, 0);
|
|
||||||
|
|
||||||
var rightBtn = new Button("Right");
|
|
||||||
rightBtn.setOnAction(e -> {
|
|
||||||
modalPaneL1.setAlignment(Pos.TOP_RIGHT);
|
|
||||||
modalPaneL1.usePredefinedTransitionFactories(Side.RIGHT);
|
|
||||||
modalPaneL1.show(getOrCreateRightDialog());
|
|
||||||
});
|
|
||||||
controlPane.add(rightBtn, 2, 1);
|
|
||||||
|
|
||||||
var centerBtn = new Button("Center");
|
|
||||||
centerBtn.setOnAction(e -> {
|
|
||||||
modalPaneL1.setAlignment(Pos.CENTER);
|
|
||||||
modalPaneL1.usePredefinedTransitionFactories(null);
|
|
||||||
modalPaneL1.show(getOrCreateCenterDialog());
|
|
||||||
});
|
|
||||||
controlPane.add(centerBtn, 1, 1);
|
|
||||||
|
|
||||||
var bottomBtn = new Button("Bottom");
|
|
||||||
bottomBtn.setOnAction(e -> {
|
|
||||||
modalPaneL1.setAlignment(Pos.BOTTOM_CENTER);
|
|
||||||
modalPaneL1.usePredefinedTransitionFactories(Side.BOTTOM);
|
|
||||||
modalPaneL1.show(getOrCreateBottomDialog());
|
|
||||||
});
|
|
||||||
controlPane.add(bottomBtn, 1, 2);
|
|
||||||
|
|
||||||
var leftBtn = new Button("Left");
|
|
||||||
leftBtn.setOnAction(e -> {
|
|
||||||
modalPaneL1.setAlignment(Pos.TOP_LEFT);
|
|
||||||
modalPaneL1.usePredefinedTransitionFactories(Side.LEFT);
|
|
||||||
modalPaneL1.show(getOrCreateLeftDialog());
|
|
||||||
});
|
|
||||||
controlPane.add(leftBtn, 0, 1);
|
|
||||||
|
|
||||||
controlPane.getChildren().forEach(c -> ((Button) c).setPrefWidth(100));
|
|
||||||
|
|
||||||
var root = new VBox(controlPane);
|
|
||||||
root.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Pane getOrCreateCenterDialog() {
|
|
||||||
if (centerDialog != null) {
|
|
||||||
return centerDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
centerDialog = createGenericDialog(450, 450, e -> modalPaneL1.hide(true));
|
|
||||||
|
|
||||||
return centerDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node getOrCreateTopDialog() {
|
|
||||||
if (topDialog != null) {
|
|
||||||
return topDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
topDialog = createGenericDialog(-1, 150, e -> modalPaneL1.hide(true));
|
|
||||||
return topDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node getOrCreateRightDialog() {
|
|
||||||
if (rightDialog != null) {
|
|
||||||
return rightDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
rightDialog = createGenericDialog(250, -1, e -> modalPaneL1.hide(true));
|
|
||||||
return rightDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node getOrCreateBottomDialog() {
|
|
||||||
if (bottomDialog != null) {
|
|
||||||
return bottomDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
bottomDialog = createGenericDialog(-1, 150, e -> modalPaneL1.hide(true));
|
|
||||||
return bottomDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node getOrCreateLeftDialog() {
|
|
||||||
if (leftDialog != null) {
|
|
||||||
return leftDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
leftDialog = createGenericDialog(250, -1, e -> modalPaneL1.hide(true));
|
|
||||||
return leftDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createOverflowDialog() {
|
|
||||||
var dialog = createGenericDialog(400, 400, e1 -> modalPaneL1.hide(true));
|
|
||||||
|
|
||||||
var content = new VBox();
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
var r = new Rectangle(600, 100);
|
|
||||||
if (i % 2 == 0) {
|
|
||||||
r.setFill(Color.AZURE);
|
|
||||||
} else {
|
|
||||||
r.setFill(Color.TOMATO);
|
|
||||||
}
|
|
||||||
content.getChildren().add(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
var scrollPane = new ScrollPane();
|
|
||||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
|
|
||||||
scrollPane.setFitToHeight(true);
|
|
||||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
|
|
||||||
scrollPane.setFitToWidth(true);
|
|
||||||
scrollPane.setMaxHeight(20_000);
|
|
||||||
scrollPane.setContent(content);
|
|
||||||
|
|
||||||
dialog.getChildren().setAll(scrollPane);
|
|
||||||
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createFullScreenDialog() {
|
|
||||||
return createGenericDialog(-1, -1, e1 -> modalPaneL1.hide(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createLevel1Dialog() {
|
|
||||||
var dialog = createGenericDialog(600, 600, e1 -> modalPaneL1.hide(true));
|
|
||||||
|
|
||||||
var nextDialogBtn = new Button("Dialog 2");
|
|
||||||
nextDialogBtn.setOnAction(e -> {
|
|
||||||
modalPaneL2.setAlignment(Pos.CENTER);
|
|
||||||
modalPaneL2.usePredefinedTransitionFactories(null);
|
|
||||||
modalPaneL2.show(createLevel2Dialog());
|
|
||||||
});
|
|
||||||
dialog.getChildren().add(nextDialogBtn);
|
|
||||||
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createLevel2Dialog() {
|
|
||||||
var dialog = createGenericDialog(450, 450, e2 -> modalPaneL2.hide(true));
|
|
||||||
|
|
||||||
var nextDialogBtn = new Button("Dialog 3");
|
|
||||||
nextDialogBtn.setOnAction(e -> {
|
|
||||||
modalPaneL3.setAlignment(Pos.CENTER);
|
|
||||||
modalPaneL3.usePredefinedTransitionFactories(null);
|
|
||||||
modalPaneL3.show(createLevel3Dialog());
|
|
||||||
});
|
|
||||||
dialog.getChildren().add(nextDialogBtn);
|
|
||||||
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createLevel3Dialog() {
|
|
||||||
return createGenericDialog(300, 300, e2 -> modalPaneL3.hide(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createGenericDialog(double width, double height, EventHandler<ActionEvent> closeHandler) {
|
|
||||||
var dialog = new VBox(10);
|
|
||||||
dialog.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
|
|
||||||
dialog.setAlignment(Pos.CENTER);
|
|
||||||
dialog.setMinSize(width, height);
|
|
||||||
dialog.setMaxSize(width, height);
|
|
||||||
|
|
||||||
var closeBtn = new Button("Close");
|
|
||||||
closeBtn.setOnAction(closeHandler);
|
|
||||||
dialog.getChildren().add(closeBtn);
|
|
||||||
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,326 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.ACCENT;
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_CIRCLE;
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_OUTLINED;
|
|
||||||
import static atlantafx.base.theme.Styles.DANGER;
|
|
||||||
import static atlantafx.base.theme.Styles.FLAT;
|
|
||||||
import static atlantafx.base.theme.Styles.LEFT_PILL;
|
|
||||||
import static atlantafx.base.theme.Styles.RIGHT_PILL;
|
|
||||||
import static atlantafx.base.theme.Styles.SUCCESS;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.ProgressSliderSkin;
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
|
||||||
import atlantafx.base.theme.Tweaks;
|
|
||||||
import atlantafx.base.util.IntegerStringConverter;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.CheckBox;
|
|
||||||
import javafx.scene.control.ChoiceBox;
|
|
||||||
import javafx.scene.control.ColorPicker;
|
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.DatePicker;
|
|
||||||
import javafx.scene.control.MenuButton;
|
|
||||||
import javafx.scene.control.MenuItem;
|
|
||||||
import javafx.scene.control.PasswordField;
|
|
||||||
import javafx.scene.control.RadioButton;
|
|
||||||
import javafx.scene.control.Slider;
|
|
||||||
import javafx.scene.control.Spinner;
|
|
||||||
import javafx.scene.control.SplitMenuButton;
|
|
||||||
import javafx.scene.control.TextArea;
|
|
||||||
import javafx.scene.control.TextField;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.control.ToggleGroup;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javafx.scene.shape.Circle;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
public class OverviewPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "Overview";
|
|
||||||
|
|
||||||
private static final int BUTTON_WIDTH = 120;
|
|
||||||
private static final int COMBO_BOX_WIDTH = 150;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OverviewPage() {
|
|
||||||
super();
|
|
||||||
setUserContent(new VBox(
|
|
||||||
PAGE_VGAP,
|
|
||||||
buttonSample(),
|
|
||||||
expandingHBox(iconButtonSample(), dropdownMenuSample()),
|
|
||||||
expandingHBox(checkBoxSample(), radioButtonSample(), toggleSwitchSample()),
|
|
||||||
comboBoxSample(),
|
|
||||||
sliderSample(),
|
|
||||||
expandingHBox(textFieldSample(), spinnerSample()),
|
|
||||||
textAreaSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock buttonSample() {
|
|
||||||
var basicBtn = new Button("Basic");
|
|
||||||
basicBtn.setPrefWidth(BUTTON_WIDTH);
|
|
||||||
|
|
||||||
var defaultBtn = new Button("Default");
|
|
||||||
defaultBtn.setDefaultButton(true);
|
|
||||||
defaultBtn.setPrefWidth(BUTTON_WIDTH);
|
|
||||||
|
|
||||||
var successBtn = new Button("Success");
|
|
||||||
successBtn.getStyleClass().add(SUCCESS);
|
|
||||||
successBtn.setPrefWidth(BUTTON_WIDTH);
|
|
||||||
|
|
||||||
var dangerBtn = new Button("Danger");
|
|
||||||
dangerBtn.getStyleClass().add(DANGER);
|
|
||||||
dangerBtn.setPrefWidth(BUTTON_WIDTH);
|
|
||||||
|
|
||||||
var flatBtn = new Button("Flat");
|
|
||||||
flatBtn.getStyleClass().add(FLAT);
|
|
||||||
flatBtn.setPrefWidth(BUTTON_WIDTH);
|
|
||||||
|
|
||||||
var outlinedBtn = new Button("Outlined");
|
|
||||||
outlinedBtn.getStyleClass().addAll(BUTTON_OUTLINED, ACCENT);
|
|
||||||
outlinedBtn.setPrefWidth(BUTTON_WIDTH);
|
|
||||||
|
|
||||||
var twoButtonGroup = new ToggleGroup();
|
|
||||||
var leftPill = createToggleButton("Toggle 1", twoButtonGroup, true, LEFT_PILL);
|
|
||||||
leftPill.setPrefWidth(BUTTON_WIDTH + BLOCK_HGAP / 2.0);
|
|
||||||
var rightPill = createToggleButton("Toggle 2", twoButtonGroup, false, RIGHT_PILL);
|
|
||||||
rightPill.setPrefWidth(BUTTON_WIDTH + BLOCK_HGAP / 2.0);
|
|
||||||
var twoButtonBox = new HBox(leftPill, rightPill);
|
|
||||||
|
|
||||||
var grid = new GridPane();
|
|
||||||
grid.setVgap(BLOCK_VGAP);
|
|
||||||
grid.setHgap(BLOCK_HGAP);
|
|
||||||
grid.add(basicBtn, 0, 0);
|
|
||||||
grid.add(flatBtn, 1, 0);
|
|
||||||
grid.add(successBtn, 2, 0);
|
|
||||||
grid.add(dangerBtn, 3, 0);
|
|
||||||
|
|
||||||
grid.add(defaultBtn, 0, 1);
|
|
||||||
grid.add(outlinedBtn, 1, 1);
|
|
||||||
grid.add(twoButtonBox, 2, 1, 2, 1);
|
|
||||||
|
|
||||||
return new SampleBlock("Buttons", grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock iconButtonSample() {
|
|
||||||
var basicBtn = new Button("", new FontIcon(Feather.MORE_HORIZONTAL));
|
|
||||||
basicBtn.getStyleClass().addAll(BUTTON_ICON);
|
|
||||||
|
|
||||||
var successBtn = new Button("", new FontIcon(Feather.PLUS));
|
|
||||||
successBtn.getStyleClass().addAll(BUTTON_ICON, ACCENT);
|
|
||||||
|
|
||||||
var dangerBtn = new Button("", new FontIcon(Feather.TRASH));
|
|
||||||
dangerBtn.getStyleClass().addAll(BUTTON_ICON, DANGER);
|
|
||||||
|
|
||||||
var basicCircularBtn = new Button("", new FontIcon(Feather.MORE_VERTICAL));
|
|
||||||
basicCircularBtn.getStyleClass().addAll(BUTTON_CIRCLE);
|
|
||||||
basicCircularBtn.setShape(new Circle(50));
|
|
||||||
|
|
||||||
var flatBtn = new Button("", new FontIcon(Feather.MIC));
|
|
||||||
flatBtn.getStyleClass().addAll(BUTTON_CIRCLE, FLAT);
|
|
||||||
flatBtn.setShape(new Circle(50));
|
|
||||||
|
|
||||||
var grid = new GridPane();
|
|
||||||
grid.setVgap(BLOCK_VGAP);
|
|
||||||
grid.setHgap(BLOCK_HGAP);
|
|
||||||
grid.add(basicBtn, 0, 0);
|
|
||||||
grid.add(successBtn, 1, 0);
|
|
||||||
grid.add(dangerBtn, 2, 0);
|
|
||||||
grid.add(basicCircularBtn, 0, 1);
|
|
||||||
grid.add(flatBtn, 1, 1);
|
|
||||||
|
|
||||||
return new SampleBlock("Icon Buttons", grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock dropdownMenuSample() {
|
|
||||||
var basicMenuBtn = new MenuButton("Menu Button");
|
|
||||||
basicMenuBtn.getItems().setAll(createMenuItems());
|
|
||||||
basicMenuBtn.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
|
|
||||||
var basicIconBtn = new MenuButton();
|
|
||||||
basicIconBtn.setGraphic(new FontIcon(Feather.MORE_HORIZONTAL));
|
|
||||||
basicIconBtn.getItems().setAll(createMenuItems());
|
|
||||||
basicIconBtn.getStyleClass().addAll(BUTTON_ICON);
|
|
||||||
|
|
||||||
var accentIconBtn = new MenuButton();
|
|
||||||
accentIconBtn.setGraphic(new FontIcon(Feather.MENU));
|
|
||||||
accentIconBtn.getItems().setAll(createMenuItems());
|
|
||||||
accentIconBtn.getStyleClass().addAll(BUTTON_ICON, ACCENT);
|
|
||||||
|
|
||||||
var basicSplitBtn = new SplitMenuButton(createMenuItems());
|
|
||||||
basicSplitBtn.setText("Split Menu Button");
|
|
||||||
|
|
||||||
var outlinedSplitBtn = new SplitMenuButton(createMenuItems());
|
|
||||||
outlinedSplitBtn.setGraphic(new FontIcon(Feather.TRASH));
|
|
||||||
outlinedSplitBtn.setText("Danger");
|
|
||||||
outlinedSplitBtn.getStyleClass().addAll(BUTTON_OUTLINED, DANGER);
|
|
||||||
outlinedSplitBtn.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
|
|
||||||
var grid = new GridPane();
|
|
||||||
grid.setVgap(BLOCK_VGAP);
|
|
||||||
grid.setHgap(BLOCK_HGAP);
|
|
||||||
grid.add(basicMenuBtn, 0, 0);
|
|
||||||
grid.add(basicIconBtn, 1, 0);
|
|
||||||
grid.add(accentIconBtn, 2, 0);
|
|
||||||
grid.add(basicSplitBtn, 1, 1, 2, 1);
|
|
||||||
grid.add(outlinedSplitBtn, 0, 1);
|
|
||||||
|
|
||||||
return new SampleBlock("Dropdown Menus", grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock checkBoxSample() {
|
|
||||||
var opt1 = new CheckBox("Option 1");
|
|
||||||
|
|
||||||
var opt2 = new CheckBox("Option 2");
|
|
||||||
opt2.setSelected(true);
|
|
||||||
|
|
||||||
var opt3 = new CheckBox("Option 3");
|
|
||||||
opt3.setAllowIndeterminate(true);
|
|
||||||
opt3.setIndeterminate(true);
|
|
||||||
|
|
||||||
var container = new VBox(BLOCK_VGAP, opt1, opt2, opt3);
|
|
||||||
return new SampleBlock("Check Boxes", container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock radioButtonSample() {
|
|
||||||
var group = new ToggleGroup();
|
|
||||||
|
|
||||||
var opt1 = new RadioButton("Option 1");
|
|
||||||
opt1.setToggleGroup(group);
|
|
||||||
|
|
||||||
var opt2 = new RadioButton("Option 2");
|
|
||||||
opt2.setToggleGroup(group);
|
|
||||||
opt2.setSelected(true);
|
|
||||||
|
|
||||||
var opt3 = new RadioButton("Option 3");
|
|
||||||
opt3.setToggleGroup(group);
|
|
||||||
|
|
||||||
var container = new VBox(BLOCK_VGAP, opt1, opt2, opt3);
|
|
||||||
return new SampleBlock("Radio Buttons", container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock toggleSwitchSample() {
|
|
||||||
var switch1 = new ToggleSwitch();
|
|
||||||
|
|
||||||
var switch2 = new ToggleSwitch();
|
|
||||||
switch2.setSelected(true);
|
|
||||||
|
|
||||||
var container = new VBox(BLOCK_VGAP, switch1, switch2);
|
|
||||||
return new SampleBlock("Switches", container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock comboBoxSample() {
|
|
||||||
var comboBox = new ComboBox<String>();
|
|
||||||
comboBox.getItems().setAll("Option 1", "Option 2", "Option 3");
|
|
||||||
comboBox.getStyleClass().add(Tweaks.ALT_ICON);
|
|
||||||
comboBox.getSelectionModel().selectFirst();
|
|
||||||
comboBox.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
|
|
||||||
var choiceBox = new ChoiceBox<String>();
|
|
||||||
choiceBox.getItems().setAll("Option 1", "Option 2", "Option 3");
|
|
||||||
choiceBox.getSelectionModel().selectFirst();
|
|
||||||
choiceBox.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
|
|
||||||
var datePicker = new DatePicker();
|
|
||||||
datePicker.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
datePicker.setValue(LocalDate.now(ZoneId.systemDefault()));
|
|
||||||
|
|
||||||
var colorPicker = new ColorPicker();
|
|
||||||
colorPicker.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
colorPicker.setValue(Color.ORANGE);
|
|
||||||
|
|
||||||
var container = new HBox(BLOCK_HGAP, comboBox, choiceBox, datePicker, colorPicker);
|
|
||||||
return new SampleBlock("Combo Boxes", container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock sliderSample() {
|
|
||||||
var slider = new Slider(1, 5, 3);
|
|
||||||
slider.setPrefWidth(BUTTON_WIDTH * 2);
|
|
||||||
|
|
||||||
var tickSlider = new Slider(0, 5, 3);
|
|
||||||
tickSlider.setShowTickLabels(true);
|
|
||||||
tickSlider.setShowTickMarks(true);
|
|
||||||
tickSlider.setMajorTickUnit(1);
|
|
||||||
tickSlider.setBlockIncrement(1);
|
|
||||||
tickSlider.setMinorTickCount(5);
|
|
||||||
tickSlider.setSnapToTicks(true);
|
|
||||||
tickSlider.setPrefWidth(BUTTON_WIDTH * 2);
|
|
||||||
tickSlider.setSkin(new ProgressSliderSkin(tickSlider));
|
|
||||||
|
|
||||||
var container = new HBox(BLOCK_HGAP, slider, tickSlider);
|
|
||||||
return new SampleBlock("Sliders", container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock textFieldSample() {
|
|
||||||
var textField = new TextField("Text");
|
|
||||||
textField.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
|
|
||||||
var passwordField = new PasswordField();
|
|
||||||
passwordField.setText(FAKER.internet().password());
|
|
||||||
passwordField.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
|
|
||||||
var container = new HBox(BLOCK_HGAP, textField, passwordField);
|
|
||||||
return new SampleBlock("Text Fields", container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock spinnerSample() {
|
|
||||||
var spinner1 = new Spinner<Integer>(1, 10, 1);
|
|
||||||
IntegerStringConverter.createFor(spinner1);
|
|
||||||
spinner1.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
|
|
||||||
var spinner2 = new Spinner<Integer>(1, 10, 1);
|
|
||||||
IntegerStringConverter.createFor(spinner2);
|
|
||||||
spinner2.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
|
|
||||||
spinner2.setPrefWidth(COMBO_BOX_WIDTH);
|
|
||||||
|
|
||||||
var container = new HBox(BLOCK_HGAP, spinner1, spinner2);
|
|
||||||
return new SampleBlock("Spinners", container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock textAreaSample() {
|
|
||||||
var textArea = new TextArea(String.join("\n\n", FAKER.lorem().paragraphs(3)));
|
|
||||||
textArea.setWrapText(true);
|
|
||||||
textArea.setMaxWidth(Double.MAX_VALUE);
|
|
||||||
textArea.setMinHeight(100);
|
|
||||||
|
|
||||||
return new SampleBlock("Text Area", textArea);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ToggleButton createToggleButton(String text,
|
|
||||||
ToggleGroup group,
|
|
||||||
boolean selected,
|
|
||||||
String... styleClasses) {
|
|
||||||
var toggleButton = new ToggleButton(text);
|
|
||||||
if (group != null) {
|
|
||||||
toggleButton.setToggleGroup(group);
|
|
||||||
}
|
|
||||||
toggleButton.setSelected(selected);
|
|
||||||
toggleButton.getStyleClass().addAll(styleClasses);
|
|
||||||
|
|
||||||
return toggleButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MenuItem[] createMenuItems() {
|
|
||||||
return IntStream.range(0, 5)
|
|
||||||
.mapToObj(i -> new MenuItem(FAKER.babylon5().character()))
|
|
||||||
.toArray(MenuItem[]::new);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,13 +2,12 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Pagination;
|
import javafx.scene.control.Pagination;
|
||||||
@ -16,12 +15,12 @@ import javafx.scene.control.Separator;
|
|||||||
import javafx.scene.control.Spinner;
|
import javafx.scene.control.Spinner;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
public class PaginationPage extends AbstractPage {
|
public class PaginationPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Pagination";
|
public static final String NAME = "Pagination";
|
||||||
private static final int PREF_CONTROL_WIDTH = 120;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -30,43 +29,113 @@ public class PaginationPage extends AbstractPage {
|
|||||||
|
|
||||||
public PaginationPage() {
|
public PaginationPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new VBox(
|
|
||||||
new SampleBlock("Playground", createPlayground())
|
addFormattedText("""
|
||||||
));
|
A [i]Pagination[/i] control is used for navigation between pages of a single content, \
|
||||||
|
which has been divided into smaller parts."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Bullet Style", bulletStyleExample());
|
||||||
|
addSection("No Arrows", noArrowsExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
}
|
}
|
||||||
|
|
||||||
private VBox createPlayground() {
|
private ExampleBox usageExample() {
|
||||||
var pagination = new Pagination();
|
//snippet_1:start
|
||||||
pagination.setCurrentPageIndex(1);
|
var pg = new Pagination(25, 0);
|
||||||
pagination.setPageFactory(index -> {
|
pg.setMaxPageIndicatorCount(5);
|
||||||
|
pg.setPageFactory(index -> {
|
||||||
|
var label = new Label("Page #" + (index + 1));
|
||||||
|
label.setStyle("-fx-font-size: 2em;");
|
||||||
|
return new BorderPane(label);
|
||||||
|
});
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(pg);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A pagination control consists of the page content and the page navigation areas. \
|
||||||
|
The [i]Page[/i] content area renders and lays out the content according to the \
|
||||||
|
application logic. The [i]Page[/i] navigation area contains a prefabricated control \
|
||||||
|
to preview a particular part of the content."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox bulletStyleExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var pg = new Pagination(25, 0);
|
||||||
|
pg.setMaxPageIndicatorCount(5);
|
||||||
|
pg.getStyleClass().add(Pagination.STYLE_CLASS_BULLET);
|
||||||
|
pg.setPageFactory(index -> {
|
||||||
|
var label = new Label("Page #" + (index + 1));
|
||||||
|
label.setStyle("-fx-font-size: 2em;");
|
||||||
|
return new BorderPane(label);
|
||||||
|
});
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(pg);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The control can be customized to display bullet style indicators by setting \
|
||||||
|
the style class [code]STYLE_CLASS_BULLET[/code]."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox noArrowsExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var pg = new Pagination(25, 0);
|
||||||
|
pg.setMaxPageIndicatorCount(5);
|
||||||
|
pg.setStyle("-fx-arrows-visible:false;");
|
||||||
|
pg.setPageFactory(index -> {
|
||||||
|
var label = new Label("Page #" + (index + 1));
|
||||||
|
label.setStyle("-fx-font-size: 2em;");
|
||||||
|
return new BorderPane(label);
|
||||||
|
});
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(pg);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
[code]-fx-arrows-visible[/code] can be used to to toggle the visibility of the [i]Next[/i] \
|
||||||
|
and [i]Previous[/i] button arrows."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private VBox playground() {
|
||||||
|
var pg = new Pagination();
|
||||||
|
pg.setCurrentPageIndex(0);
|
||||||
|
pg.setPageFactory(index -> {
|
||||||
var label = new Label("Page #" + (index + 1));
|
var label = new Label("Page #" + (index + 1));
|
||||||
label.setStyle("-fx-font-size: 3em;");
|
label.setStyle("-fx-font-size: 3em;");
|
||||||
|
return new BorderPane(label);
|
||||||
var page = new BorderPane();
|
|
||||||
page.setCenter(label);
|
|
||||||
|
|
||||||
return page;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// == CONTROLS ==
|
// == CONTROLS ==
|
||||||
|
|
||||||
var pageCountSpinner = new Spinner<Integer>(0, 50, 25);
|
var pageCountSpinner = new Spinner<Integer>(0, 50, 25);
|
||||||
pageCountSpinner.setPrefWidth(PREF_CONTROL_WIDTH);
|
pageCountSpinner.setPrefWidth(120);
|
||||||
pagination.pageCountProperty().bind(pageCountSpinner.valueProperty());
|
pg.pageCountProperty().bind(pageCountSpinner.valueProperty());
|
||||||
|
|
||||||
var visibleCountSpinner = new Spinner<Integer>(3, 10, 5);
|
var visibleCountSpinner = new Spinner<Integer>(3, 10, 5);
|
||||||
visibleCountSpinner.setPrefWidth(PREF_CONTROL_WIDTH);
|
visibleCountSpinner.setPrefWidth(120);
|
||||||
pagination.maxPageIndicatorCountProperty().bind(visibleCountSpinner.valueProperty());
|
pg.maxPageIndicatorCountProperty().bind(visibleCountSpinner.valueProperty());
|
||||||
|
|
||||||
var bulletToggle = new ToggleSwitch();
|
var bulletToggle = new ToggleSwitch();
|
||||||
bulletToggle.selectedProperty().addListener(
|
bulletToggle.selectedProperty().addListener(
|
||||||
(obs, old, val) -> Styles.toggleStyleClass(pagination, Pagination.STYLE_CLASS_BULLET)
|
(obs, old, val) -> Styles.toggleStyleClass(pg, Pagination.STYLE_CLASS_BULLET)
|
||||||
);
|
);
|
||||||
|
|
||||||
var showArrowsToggle = new ToggleSwitch();
|
var showArrowsToggle = new ToggleSwitch();
|
||||||
showArrowsToggle.selectedProperty().addListener((obs, old, val) -> {
|
showArrowsToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
pagination.setStyle(String.format("-fx-arrows-visible: %s;", val));
|
pg.setStyle(String.format("-fx-arrows-visible: %s;", val));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showArrowsToggle.setSelected(true);
|
showArrowsToggle.setSelected(true);
|
||||||
@ -74,17 +143,17 @@ public class PaginationPage extends AbstractPage {
|
|||||||
var showPageInfoToggle = new ToggleSwitch();
|
var showPageInfoToggle = new ToggleSwitch();
|
||||||
showPageInfoToggle.selectedProperty().addListener((obs, old, val) -> {
|
showPageInfoToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
pagination.setStyle(String.format("-fx-page-information-visible: %s;", val));
|
pg.setStyle(String.format("-fx-page-information-visible: %s;", val));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showPageInfoToggle.setSelected(true);
|
showPageInfoToggle.setSelected(true);
|
||||||
|
|
||||||
var disableToggle = new ToggleSwitch();
|
var disableToggle = new ToggleSwitch();
|
||||||
pagination.disableProperty().bind(disableToggle.selectedProperty());
|
pg.disableProperty().bind(disableToggle.selectedProperty());
|
||||||
|
|
||||||
var controls = new GridPane();
|
var controls = new GridPane();
|
||||||
controls.setHgap(BLOCK_HGAP);
|
controls.setHgap(20);
|
||||||
controls.setVgap(BLOCK_VGAP);
|
controls.setVgap(10);
|
||||||
controls.setAlignment(Pos.CENTER);
|
controls.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
controls.add(new Label("Page count"), 0, 0);
|
controls.add(new Label("Page count"), 0, 0);
|
||||||
@ -107,8 +176,13 @@ public class PaginationPage extends AbstractPage {
|
|||||||
|
|
||||||
// ~
|
// ~
|
||||||
|
|
||||||
var playground = new VBox(BLOCK_VGAP, pagination, new Separator(), controls);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
playground.setMinHeight(100);
|
The playground demonstrates the most important [i]Pagination[/i] features \
|
||||||
|
and also serves as an object for monkey testing."""
|
||||||
|
);
|
||||||
|
|
||||||
|
var playground = new VBox(VGAP_10, description, pg, new Separator(), controls);
|
||||||
|
playground.setPrefHeight(200);
|
||||||
|
|
||||||
return playground;
|
return playground;
|
||||||
}
|
}
|
||||||
|
@ -1,168 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.InlineDatePicker;
|
|
||||||
import atlantafx.base.controls.Popover;
|
|
||||||
import atlantafx.base.controls.Popover.ArrowLocation;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.Hyperlink;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
public class PopoverPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "Popover";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PopoverPage() {
|
|
||||||
super();
|
|
||||||
setUserContent(new VBox(Page.PAGE_VGAP,
|
|
||||||
new HBox(PAGE_HGAP, textSample(), datePickerSample(), dialogSample()),
|
|
||||||
positionSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock textSample() {
|
|
||||||
var popover = new Popover(createTextFlow(30));
|
|
||||||
popover.setTitle("Lorem Ipsum");
|
|
||||||
popover.setHeaderAlwaysVisible(true);
|
|
||||||
popover.setDetachable(true);
|
|
||||||
|
|
||||||
var link = createHyperlink("Click me");
|
|
||||||
link.setOnAction(e -> popover.show(link));
|
|
||||||
|
|
||||||
return new SampleBlock("Text", link);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock datePickerSample() {
|
|
||||||
var datePicker = new InlineDatePicker();
|
|
||||||
datePicker.setValue(LocalDate.now(ZoneId.systemDefault()));
|
|
||||||
|
|
||||||
var popover = new Popover(datePicker);
|
|
||||||
popover.setHeaderAlwaysVisible(false);
|
|
||||||
popover.setDetachable(true);
|
|
||||||
|
|
||||||
var link = createHyperlink("Click me");
|
|
||||||
link.setOnAction(e -> popover.show(link));
|
|
||||||
new CSSFragment("""
|
|
||||||
.popover .date-picker-popup {
|
|
||||||
-color-date-border: transparent;
|
|
||||||
-color-date-bg: transparent;
|
|
||||||
-color-date-day-bg: transparent;
|
|
||||||
-color-date-month-year-bg: transparent;
|
|
||||||
-color-date-day-bg-hover: -color-bg-subtle;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
).addTo(link);
|
|
||||||
|
|
||||||
return new SampleBlock("Date Picker", link);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock dialogSample() {
|
|
||||||
var root = new VBox(BLOCK_VGAP);
|
|
||||||
|
|
||||||
var popover = new Popover(root);
|
|
||||||
popover.setHeaderAlwaysVisible(false);
|
|
||||||
popover.setDetachable(true);
|
|
||||||
|
|
||||||
var icon = new FontIcon(Feather.ALERT_TRIANGLE);
|
|
||||||
icon.setIconSize(32); // not always works
|
|
||||||
icon.setStyle(
|
|
||||||
"-fx-icon-size:32px;-fx-icon-color:-color-warning-fg;-fx-fill:-color-warning-fg;" + icon.getStyle());
|
|
||||||
|
|
||||||
var label = new Label(FAKER.chuckNorris().fact(), icon);
|
|
||||||
label.setStyle("-fx-graphic-text-gap:10;");
|
|
||||||
label.setWrapText(true);
|
|
||||||
label.setMaxWidth(300);
|
|
||||||
label.setMaxHeight(Double.MAX_VALUE);
|
|
||||||
root.getChildren().add(label);
|
|
||||||
|
|
||||||
var yesBtn = new Button("Yes");
|
|
||||||
yesBtn.setDefaultButton(true);
|
|
||||||
yesBtn.setOnAction(e -> popover.hide());
|
|
||||||
|
|
||||||
var noBtn = new Button("No");
|
|
||||||
noBtn.setOnAction(e -> popover.hide());
|
|
||||||
|
|
||||||
var box = new HBox(10, yesBtn, noBtn);
|
|
||||||
box.setPadding(new Insets(0, 0, 0, 42));
|
|
||||||
box.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
root.getChildren().add(box);
|
|
||||||
|
|
||||||
var link = createHyperlink("Click me");
|
|
||||||
link.setOnAction(e -> popover.show(link));
|
|
||||||
|
|
||||||
return new SampleBlock("Dialog", link);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock positionSample() {
|
|
||||||
var grid = new GridPane();
|
|
||||||
grid.setHgap(BLOCK_HGAP);
|
|
||||||
grid.setVgap(BLOCK_VGAP);
|
|
||||||
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.TOP_LEFT), 0, 0);
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.TOP_CENTER), 0, 1);
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.TOP_RIGHT), 0, 2);
|
|
||||||
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.RIGHT_TOP), 1, 0);
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.RIGHT_CENTER), 1, 1);
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.RIGHT_BOTTOM), 1, 2);
|
|
||||||
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.BOTTOM_LEFT), 2, 0);
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.BOTTOM_CENTER), 2, 1);
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.BOTTOM_RIGHT), 2, 2);
|
|
||||||
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.LEFT_TOP), 3, 0);
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.LEFT_CENTER), 3, 1);
|
|
||||||
grid.add(createArrowPositionBlock(ArrowLocation.LEFT_BOTTOM), 3, 2);
|
|
||||||
|
|
||||||
return new SampleBlock("Position", grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Hyperlink createHyperlink(String text) {
|
|
||||||
Hyperlink hyperlink = new Hyperlink(text);
|
|
||||||
hyperlink.setMinWidth(50);
|
|
||||||
hyperlink.setMinHeight(50);
|
|
||||||
hyperlink.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
return hyperlink;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TextFlow createTextFlow(int wordCount) {
|
|
||||||
var textFlow = new TextFlow(new Text(FAKER.lorem().sentence(wordCount)));
|
|
||||||
textFlow.setPrefWidth(300);
|
|
||||||
return textFlow;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Hyperlink createArrowPositionBlock(ArrowLocation arrowLocation) {
|
|
||||||
var popover = new Popover(createTextFlow(50));
|
|
||||||
popover.setHeaderAlwaysVisible(false);
|
|
||||||
popover.setArrowLocation(arrowLocation);
|
|
||||||
|
|
||||||
var link = createHyperlink(String.valueOf(arrowLocation));
|
|
||||||
link.setOnAction(e -> popover.show(link));
|
|
||||||
|
|
||||||
return link;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,372 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.RingProgressIndicator;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import atlantafx.sampler.theme.CSSFragment;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.geometry.HPos;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.geometry.VPos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ProgressBar;
|
||||||
|
import javafx.scene.control.ProgressIndicator;
|
||||||
|
import javafx.scene.control.ToggleButton;
|
||||||
|
import javafx.scene.layout.ColumnConstraints;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.RowConstraints;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
// #javafx-bug Indeterminate (animated) progress bar and also progress indicator
|
||||||
|
// are very resource expensive. It consumes a single CPU core and a lot of memory.
|
||||||
|
public class ProgressIndicatorPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "ProgressIndicator";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProgressIndicatorPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
JavaFX provides the two types of progress indicators: the [i]ProgressIndicator[/i] and \
|
||||||
|
the [i]ProgressBar[/i]. In addition to them AtlantaFX also provides the \
|
||||||
|
[i]RingProgressIndicator[/i]."""
|
||||||
|
);
|
||||||
|
addSection("Progress Bar", basicBarExample());
|
||||||
|
addSection("Progress Indicator", basicIndicatorExample());
|
||||||
|
addSection("Size", barSizeSample());
|
||||||
|
addSection("Ring Indicator", ringIndicatorExample());
|
||||||
|
addSection("Indeterminate", indeterminateExample());
|
||||||
|
addSection("Dynamic Color Change", colorChangeExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox basicBarExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var bar1 = new ProgressBar(0);
|
||||||
|
bar1.setPrefWidth(250);
|
||||||
|
|
||||||
|
var bar2 = new ProgressBar(0.5);
|
||||||
|
bar2.setPrefWidth(250);
|
||||||
|
|
||||||
|
var bar3 = new ProgressBar(1);
|
||||||
|
bar3.setPrefWidth(250);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_20, bar1, bar2, bar3);
|
||||||
|
box.setMinHeight(100);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
[i]ProgressBar[/i] is a specialization of the [i]ProgressIndicator[/i] \
|
||||||
|
which is represented as a horizontal bar."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox basicIndicatorExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var ind1 = new ProgressIndicator(0);
|
||||||
|
ind1.setMinSize(50, 50);
|
||||||
|
|
||||||
|
var ind2 = new ProgressIndicator(0.5);
|
||||||
|
ind2.setMinSize(50, 50);
|
||||||
|
|
||||||
|
var ind3 = new ProgressIndicator(1);
|
||||||
|
ind3.setMinSize(50, 50);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(VGAP_20, ind1, ind2, ind3);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
[i]ProgressIndicator[/i] is a circular control which is used for indicating progress, \
|
||||||
|
either infinite (aka indeterminate) or finite."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox barSizeSample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var smallBar = new ProgressBar(0.5);
|
||||||
|
smallBar.setPrefWidth(250);
|
||||||
|
smallBar.getStyleClass().add(Styles.SMALL);
|
||||||
|
|
||||||
|
var mediumBar = new ProgressBar(0.5);
|
||||||
|
mediumBar.setPrefWidth(250);
|
||||||
|
mediumBar.getStyleClass().add(Styles.MEDIUM);
|
||||||
|
|
||||||
|
var largeBar = new ProgressBar(0.5);
|
||||||
|
largeBar.setPrefWidth(250);
|
||||||
|
largeBar.getStyleClass().add(Styles.LARGE);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new VBox(
|
||||||
|
VGAP_20,
|
||||||
|
new HBox(HGAP_30, smallBar, new Text("small")),
|
||||||
|
new HBox(HGAP_30, mediumBar, new Text("medium")),
|
||||||
|
new HBox(HGAP_30, largeBar, new Text("large"))
|
||||||
|
);
|
||||||
|
box.setAlignment(Pos.TOP_LEFT);
|
||||||
|
box.getChildren().forEach(c -> ((HBox) c).setAlignment(Pos.CENTER_LEFT));
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
To change the height of the [i]ProgressBar[/i], you can apply the \
|
||||||
|
[code]Styles.SMALL[/code] or [code]Styles.LARGE[/code] style classes, respectively. \
|
||||||
|
Use the large variant if you want to display a text on top of the [i]ProgressBar[/i]."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox ringIndicatorExample() {
|
||||||
|
//snippet_4:start
|
||||||
|
var basicInd = new RingProgressIndicator(0, false);
|
||||||
|
|
||||||
|
var customTextInd = new RingProgressIndicator(0.5, false);
|
||||||
|
customTextInd.setMinSize(75, 75);
|
||||||
|
customTextInd.setStringConverter(new StringConverter<>() {
|
||||||
|
@Override
|
||||||
|
public String toString(Double progress) {
|
||||||
|
return (int) Math.ceil(progress * 100) + "°";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double fromString(String progress) {
|
||||||
|
return 0d;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var reverseInd = new RingProgressIndicator(0.25, true);
|
||||||
|
reverseInd.setMinSize(150, 150);
|
||||||
|
|
||||||
|
var reverseLabel = new Label("25%");
|
||||||
|
reverseLabel.getStyleClass().add(Styles.TITLE_4);
|
||||||
|
|
||||||
|
var reverseBtn = new Button("", new FontIcon(Feather.PLAY));
|
||||||
|
reverseBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_CIRCLE, Styles.FLAT
|
||||||
|
);
|
||||||
|
reverseBtn.disableProperty().bind(
|
||||||
|
reverseInd.progressProperty().greaterThan(0.25)
|
||||||
|
);
|
||||||
|
reverseBtn.setOnAction(evt1 -> {
|
||||||
|
var task = new Task<Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void call() throws Exception {
|
||||||
|
int steps = 100;
|
||||||
|
for (int i = 25; i <= steps; i++) {
|
||||||
|
Thread.sleep(100);
|
||||||
|
updateProgress(i, steps);
|
||||||
|
updateMessage(i + "%");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// reset properties, so we can start a new task
|
||||||
|
task.setOnSucceeded(evt2 -> {
|
||||||
|
reverseInd.progressProperty().unbind();
|
||||||
|
reverseLabel.textProperty().unbind();
|
||||||
|
reverseInd.setProgress(0.25);
|
||||||
|
reverseLabel.setText("25%");
|
||||||
|
});
|
||||||
|
|
||||||
|
reverseInd.progressProperty().bind(task.progressProperty());
|
||||||
|
reverseLabel.textProperty().bind(task.messageProperty());
|
||||||
|
|
||||||
|
new Thread(task).start();
|
||||||
|
});
|
||||||
|
|
||||||
|
var reverseGraphic = new VBox(10, reverseLabel, reverseBtn);
|
||||||
|
reverseGraphic.setAlignment(Pos.CENTER);
|
||||||
|
reverseInd.setGraphic(reverseGraphic);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, basicInd, customTextInd, reverseInd);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]RingProgressIndicator[/i] is a type of progress indicator \
|
||||||
|
that displays progress as a ring that gradually empties out as a task is completed."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox indeterminateExample() {
|
||||||
|
//snippet_5:start
|
||||||
|
var barToggle = new ToggleButton("Start");
|
||||||
|
barToggle.textProperty().bind(Bindings.createStringBinding(
|
||||||
|
() -> barToggle.isSelected() ? "Stop" : "Start",
|
||||||
|
barToggle.selectedProperty()
|
||||||
|
));
|
||||||
|
var bar = new ProgressBar(0);
|
||||||
|
bar.progressProperty().bind(Bindings.createDoubleBinding(
|
||||||
|
() -> barToggle.isSelected() ? -1d : 0d,
|
||||||
|
barToggle.selectedProperty()
|
||||||
|
));
|
||||||
|
|
||||||
|
var indicatorToggle = new ToggleButton("Start");
|
||||||
|
indicatorToggle.textProperty().bind(Bindings.createStringBinding(
|
||||||
|
() -> indicatorToggle.isSelected() ? "Stop" : "Start",
|
||||||
|
indicatorToggle.selectedProperty()
|
||||||
|
));
|
||||||
|
var indicator = new ProgressIndicator(0);
|
||||||
|
indicator.setMinSize(60, 60);
|
||||||
|
indicator.progressProperty().bind(Bindings.createDoubleBinding(
|
||||||
|
() -> indicatorToggle.isSelected() ? -1d : 0d,
|
||||||
|
indicatorToggle.selectedProperty()
|
||||||
|
));
|
||||||
|
|
||||||
|
var ringToggle = new ToggleButton("Start");
|
||||||
|
ringToggle.textProperty().bind(Bindings.createStringBinding(
|
||||||
|
() -> ringToggle.isSelected() ? "Stop" : "Start",
|
||||||
|
ringToggle.selectedProperty()
|
||||||
|
));
|
||||||
|
var ring = new RingProgressIndicator(0, false);
|
||||||
|
ring.setMinSize(75, 75);
|
||||||
|
ring.progressProperty().bind(Bindings.createDoubleBinding(
|
||||||
|
() -> ringToggle.isSelected() ? -1d : 0d,
|
||||||
|
ringToggle.selectedProperty()
|
||||||
|
));
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
|
var grid = new GridPane();
|
||||||
|
grid.setHgap(60);
|
||||||
|
grid.setVgap(10);
|
||||||
|
grid.getColumnConstraints().setAll(
|
||||||
|
new ColumnConstraints(-1, -1, -1, Priority.NEVER, HPos.CENTER, true),
|
||||||
|
new ColumnConstraints(-1, -1, -1, Priority.NEVER, HPos.CENTER, true),
|
||||||
|
new ColumnConstraints(-1, -1, -1, Priority.NEVER, HPos.CENTER, true)
|
||||||
|
);
|
||||||
|
grid.getRowConstraints().setAll(
|
||||||
|
new RowConstraints(-1, -1, -1, Priority.ALWAYS, VPos.CENTER, true),
|
||||||
|
new RowConstraints(-1, -1, -1, Priority.NEVER, VPos.CENTER, true)
|
||||||
|
);
|
||||||
|
grid.addColumn(0, bar, barToggle);
|
||||||
|
grid.addColumn(1, indicator, indicatorToggle);
|
||||||
|
grid.addColumn(2, ring, ringToggle);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Animated JavaFX progress indicators aren't cheap. They can consume quite a lot of CPU time."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(grid, new Snippet(getClass(), 5), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox colorChangeExample() {
|
||||||
|
var dataClass = """
|
||||||
|
.example:success .progress-bar {
|
||||||
|
-color-progress-bar-fill: -color-success-emphasis;
|
||||||
|
}
|
||||||
|
.example:danger .progress-bar {
|
||||||
|
-color-progress-bar-fill: -color-danger-emphasis;
|
||||||
|
}
|
||||||
|
.example:success .label,
|
||||||
|
.example:danger .label {
|
||||||
|
-fx-text-fill: -color-fg-emphasis;
|
||||||
|
}""";
|
||||||
|
|
||||||
|
//snippet_6:start
|
||||||
|
var bar = new ProgressBar(0);
|
||||||
|
bar.getStyleClass().add(Styles.LARGE);
|
||||||
|
bar.setPrefWidth(300);
|
||||||
|
bar.setMaxWidth(300);
|
||||||
|
|
||||||
|
var barText = new Label();
|
||||||
|
|
||||||
|
var barStack = new StackPane(bar, barText);
|
||||||
|
barStack.getStyleClass().add("example");
|
||||||
|
barStack.setPrefWidth(300);
|
||||||
|
barStack.setMaxWidth(300);
|
||||||
|
|
||||||
|
var runBtn = new Button("Start");
|
||||||
|
runBtn.disableProperty().bind(
|
||||||
|
bar.progressProperty().greaterThan(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
var content = new VBox(VGAP_10, barStack, runBtn);
|
||||||
|
content.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
content.setPrefHeight(200);
|
||||||
|
// .example:success .progress-bar {
|
||||||
|
// -color-progress-bar-fill: -color-success-emphasis;
|
||||||
|
// }
|
||||||
|
// .example:danger .progress-bar {
|
||||||
|
// -color-progress-bar-fill: -color-danger-emphasis;
|
||||||
|
// }
|
||||||
|
// .example:success .label,
|
||||||
|
// .example:danger .label {
|
||||||
|
// -fx-text-fill: -color-fg-emphasis;
|
||||||
|
// }
|
||||||
|
new CSSFragment(dataClass).addTo(content);
|
||||||
|
|
||||||
|
bar.progressProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val.floatValue() > 0.80) {
|
||||||
|
barStack.pseudoClassStateChanged(Styles.STATE_DANGER, true);
|
||||||
|
} else if (val.floatValue() > 0.47) {
|
||||||
|
barStack.pseudoClassStateChanged(Styles.STATE_SUCCESS, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
runBtn.setOnAction(evt1 -> {
|
||||||
|
var task = new Task<Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void call() throws Exception {
|
||||||
|
int steps = 1_000;
|
||||||
|
for (int i = 0; i < steps; i++) {
|
||||||
|
Thread.sleep(10);
|
||||||
|
updateProgress(i, steps);
|
||||||
|
updateMessage(String.valueOf(i));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// reset properties, so we can start a new task
|
||||||
|
task.setOnSucceeded(evt2 -> {
|
||||||
|
bar.progressProperty().unbind();
|
||||||
|
barText.textProperty().unbind();
|
||||||
|
|
||||||
|
bar.setProgress(0);
|
||||||
|
barText.setText(null);
|
||||||
|
|
||||||
|
barStack.pseudoClassStateChanged(Styles.STATE_SUCCESS, false);
|
||||||
|
barStack.pseudoClassStateChanged(Styles.STATE_DANGER, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
bar.progressProperty().bind(task.progressProperty());
|
||||||
|
barText.textProperty().bind(task.messageProperty());
|
||||||
|
|
||||||
|
new Thread(task).start();
|
||||||
|
});
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This example demonstrates how looked-up color variables can be used to \
|
||||||
|
change the progress bar color while the task is in progress."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(content, new Snippet(getClass(), 6), description);
|
||||||
|
}
|
||||||
|
}
|
@ -1,336 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_CIRCLE;
|
|
||||||
import static atlantafx.base.theme.Styles.FLAT;
|
|
||||||
import static atlantafx.base.theme.Styles.LARGE;
|
|
||||||
import static atlantafx.base.theme.Styles.MEDIUM;
|
|
||||||
import static atlantafx.base.theme.Styles.SMALL;
|
|
||||||
import static atlantafx.base.theme.Styles.TITLE_4;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.RingProgressIndicator;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.concurrent.Task;
|
|
||||||
import javafx.css.PseudoClass;
|
|
||||||
import javafx.geometry.HPos;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.geometry.VPos;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.ProgressBar;
|
|
||||||
import javafx.scene.control.ProgressIndicator;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.layout.ColumnConstraints;
|
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.RowConstraints;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.util.StringConverter;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
// #javafx-bug Indeterminate (animated) progress bar and also progress indicator
|
|
||||||
// are very resource expensive. It consumes a single CPU core and a lot of memory.
|
|
||||||
public class ProgressPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "Progress";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProgressPage() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
var grid = new GridPane();
|
|
||||||
grid.setHgap(Page.PAGE_HGAP);
|
|
||||||
grid.setVgap(Page.PAGE_VGAP);
|
|
||||||
|
|
||||||
grid.add(basicBarSample(), 0, 0);
|
|
||||||
grid.add(basicIndicatorSample(), 1, 0);
|
|
||||||
|
|
||||||
grid.add(ringIndicatorSample(), 0, 1);
|
|
||||||
grid.add(barSizeSample(), 1, 1);
|
|
||||||
|
|
||||||
grid.add(indeterminateSample(), 0, 2);
|
|
||||||
grid.add(colorChangeSample(), 1, 2);
|
|
||||||
|
|
||||||
setUserContent(grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock basicBarSample() {
|
|
||||||
var flowPane = new FlowPane(
|
|
||||||
BLOCK_HGAP, BLOCK_VGAP,
|
|
||||||
createBar(0, false),
|
|
||||||
createBar(0.5, false),
|
|
||||||
createBar(1, false),
|
|
||||||
createBar(0.5, true)
|
|
||||||
);
|
|
||||||
flowPane.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
return new SampleBlock("Progress Bar", flowPane);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock basicIndicatorSample() {
|
|
||||||
var flowPane = new FlowPane(
|
|
||||||
BLOCK_HGAP, BLOCK_VGAP,
|
|
||||||
createIndicator(0, false),
|
|
||||||
createIndicator(0.5, false),
|
|
||||||
createIndicator(1, false),
|
|
||||||
createIndicator(0.5, true)
|
|
||||||
);
|
|
||||||
flowPane.setAlignment(Pos.TOP_LEFT);
|
|
||||||
|
|
||||||
return new SampleBlock("Progress Indicator", flowPane);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock barSizeSample() {
|
|
||||||
var container = new VBox(
|
|
||||||
BLOCK_VGAP,
|
|
||||||
new HBox(20, createBar(0.5, false, SMALL), new Text("small")),
|
|
||||||
new HBox(20, createBar(0.5, false, MEDIUM), new Text("medium")),
|
|
||||||
new HBox(20, createBar(0.5, false, LARGE), new Text("large"))
|
|
||||||
);
|
|
||||||
container.setAlignment(Pos.TOP_LEFT);
|
|
||||||
container.getChildren().forEach(c -> ((HBox) c).setAlignment(Pos.CENTER_LEFT));
|
|
||||||
|
|
||||||
return new SampleBlock("Size", container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock ringIndicatorSample() {
|
|
||||||
var basicIndicator = new RingProgressIndicator(0, false);
|
|
||||||
|
|
||||||
var customTextIndicator = new RingProgressIndicator(0.5, false);
|
|
||||||
customTextIndicator.setMinSize(75, 75);
|
|
||||||
customTextIndicator.setStringConverter(new StringConverter<>() {
|
|
||||||
@Override
|
|
||||||
public String toString(Double progress) {
|
|
||||||
return (int) Math.ceil(progress * 100) + "°";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Double fromString(String progress) {
|
|
||||||
return 0d;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var reverseIndicator = new RingProgressIndicator(0.25, true);
|
|
||||||
reverseIndicator.setMinSize(150, 150);
|
|
||||||
|
|
||||||
var reverseIndicatorLabel = new Label("25%");
|
|
||||||
reverseIndicatorLabel.getStyleClass().add(TITLE_4);
|
|
||||||
|
|
||||||
var reversePlayButton = new Button("", new FontIcon(Feather.PLAY));
|
|
||||||
reversePlayButton.getStyleClass().addAll(BUTTON_CIRCLE, FLAT);
|
|
||||||
reversePlayButton.disableProperty().bind(reverseIndicator.progressProperty().greaterThan(0.25));
|
|
||||||
reversePlayButton.setOnAction(e1 -> {
|
|
||||||
var task = new Task<Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void call() throws Exception {
|
|
||||||
int steps = 100;
|
|
||||||
|
|
||||||
for (int i = 25; i <= steps; i++) {
|
|
||||||
Thread.sleep(100);
|
|
||||||
updateProgress(i, steps);
|
|
||||||
updateMessage(i + "%");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// reset properties, so we can start a new task
|
|
||||||
task.setOnSucceeded(e2 -> {
|
|
||||||
reverseIndicator.progressProperty().unbind();
|
|
||||||
reverseIndicatorLabel.textProperty().unbind();
|
|
||||||
|
|
||||||
reverseIndicator.setProgress(0.25);
|
|
||||||
reverseIndicatorLabel.setText("25%");
|
|
||||||
});
|
|
||||||
|
|
||||||
reverseIndicator.progressProperty().bind(task.progressProperty());
|
|
||||||
reverseIndicatorLabel.textProperty().bind(task.messageProperty());
|
|
||||||
|
|
||||||
new Thread(task).start();
|
|
||||||
});
|
|
||||||
|
|
||||||
var reverseBox = new VBox(10, reverseIndicatorLabel, reversePlayButton);
|
|
||||||
reverseBox.setAlignment(Pos.CENTER);
|
|
||||||
reverseIndicator.setGraphic(reverseBox);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
var box = new HBox(BLOCK_HGAP, basicIndicator, customTextIndicator, reverseIndicator);
|
|
||||||
box.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
return new SampleBlock("Ring Indicator", box);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock indeterminateSample() {
|
|
||||||
var grid = new GridPane();
|
|
||||||
grid.setHgap(40);
|
|
||||||
grid.setVgap(BLOCK_VGAP);
|
|
||||||
grid.getColumnConstraints().setAll(
|
|
||||||
new ColumnConstraints(-1, -1, -1, Priority.NEVER, HPos.CENTER, true),
|
|
||||||
new ColumnConstraints(-1, -1, -1, Priority.NEVER, HPos.CENTER, true),
|
|
||||||
new ColumnConstraints(-1, -1, -1, Priority.NEVER, HPos.CENTER, true)
|
|
||||||
);
|
|
||||||
grid.getRowConstraints().setAll(
|
|
||||||
new RowConstraints(-1, -1, -1, Priority.ALWAYS, VPos.CENTER, true),
|
|
||||||
new RowConstraints(-1, -1, -1, Priority.NEVER, VPos.CENTER, true)
|
|
||||||
);
|
|
||||||
|
|
||||||
var barToggle = new ToggleButton("Start");
|
|
||||||
barToggle.textProperty().bind(Bindings.createStringBinding(
|
|
||||||
() -> barToggle.isSelected() ? "Stop" : "Start", barToggle.selectedProperty())
|
|
||||||
);
|
|
||||||
var bar = createBar(0, false);
|
|
||||||
bar.progressProperty().bind(Bindings.createDoubleBinding(
|
|
||||||
() -> barToggle.isSelected() ? -1d : 0d, barToggle.selectedProperty())
|
|
||||||
);
|
|
||||||
grid.add(bar, 0, 0);
|
|
||||||
grid.add(barToggle, 0, 1);
|
|
||||||
|
|
||||||
var indicatorToggle = new ToggleButton("Start");
|
|
||||||
indicatorToggle.textProperty().bind(Bindings.createStringBinding(
|
|
||||||
() -> indicatorToggle.isSelected() ? "Stop" : "Start", indicatorToggle.selectedProperty())
|
|
||||||
);
|
|
||||||
var indicator = createIndicator(0, false);
|
|
||||||
indicator.setPrefSize(75, 75);
|
|
||||||
indicator.progressProperty().bind(Bindings.createDoubleBinding(
|
|
||||||
() -> indicatorToggle.isSelected() ? -1d : 0d, indicatorToggle.selectedProperty())
|
|
||||||
);
|
|
||||||
grid.add(indicator, 1, 0);
|
|
||||||
grid.add(indicatorToggle, 1, 1);
|
|
||||||
|
|
||||||
var ringToggle = new ToggleButton("Start");
|
|
||||||
ringToggle.textProperty().bind(Bindings.createStringBinding(
|
|
||||||
() -> ringToggle.isSelected() ? "Stop" : "Start", ringToggle.selectedProperty())
|
|
||||||
);
|
|
||||||
var ring = new RingProgressIndicator(0, false);
|
|
||||||
ring.setMinSize(75, 75);
|
|
||||||
ring.progressProperty().bind(Bindings.createDoubleBinding(
|
|
||||||
() -> ringToggle.isSelected() ? -1d : 0d, ringToggle.selectedProperty())
|
|
||||||
);
|
|
||||||
grid.add(ring, 2, 0);
|
|
||||||
grid.add(ringToggle, 2, 1);
|
|
||||||
|
|
||||||
return new SampleBlock("Indeterminate", grid,
|
|
||||||
"Animated JavaFX progress indicators aren't cheap. They can consume quite a lot of CPU time."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock colorChangeSample() {
|
|
||||||
final var stateSuccess = PseudoClass.getPseudoClass("state-success");
|
|
||||||
final var stateDanger = PseudoClass.getPseudoClass("state-danger");
|
|
||||||
final var width = 300;
|
|
||||||
|
|
||||||
var bar = new ProgressBar(0);
|
|
||||||
bar.getStyleClass().add(LARGE);
|
|
||||||
bar.setPrefWidth(width);
|
|
||||||
bar.setMaxWidth(width);
|
|
||||||
|
|
||||||
var barText = new Label();
|
|
||||||
|
|
||||||
var barStack = new StackPane(bar, barText);
|
|
||||||
barStack.getStyleClass().add("example");
|
|
||||||
barStack.setPrefWidth(width);
|
|
||||||
barStack.setMaxWidth(width);
|
|
||||||
|
|
||||||
var runBtn = new Button("Start");
|
|
||||||
runBtn.disableProperty().bind(bar.progressProperty().greaterThan(0));
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
var content = new VBox(BLOCK_VGAP);
|
|
||||||
content.getChildren().setAll(barStack, runBtn);
|
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
content.setPrefHeight(200);
|
|
||||||
|
|
||||||
bar.progressProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.floatValue() > 0.80) {
|
|
||||||
barStack.pseudoClassStateChanged(stateDanger, true);
|
|
||||||
} else if (val.floatValue() > 0.47) {
|
|
||||||
barStack.pseudoClassStateChanged(stateSuccess, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
new CSSFragment("""
|
|
||||||
.example:state-success .progress-bar {
|
|
||||||
-color-progress-bar-fill: -color-success-emphasis;
|
|
||||||
}
|
|
||||||
.example:state-danger .progress-bar {
|
|
||||||
-color-progress-bar-fill: -color-danger-emphasis;
|
|
||||||
}
|
|
||||||
.example:state-success .label,
|
|
||||||
.example:state-danger .label {
|
|
||||||
-fx-text-fill: -color-fg-emphasis;
|
|
||||||
}
|
|
||||||
""").addTo(content);
|
|
||||||
|
|
||||||
runBtn.setOnAction(e1 -> {
|
|
||||||
var task = new Task<Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void call() throws Exception {
|
|
||||||
int steps = 1_000;
|
|
||||||
|
|
||||||
for (int i = 0; i < steps; i++) {
|
|
||||||
Thread.sleep(10);
|
|
||||||
updateProgress(i, steps);
|
|
||||||
updateMessage(String.valueOf(i));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// reset properties, so we can start a new task
|
|
||||||
task.setOnSucceeded(e2 -> {
|
|
||||||
bar.progressProperty().unbind();
|
|
||||||
barText.textProperty().unbind();
|
|
||||||
|
|
||||||
bar.setProgress(0);
|
|
||||||
barText.setText(null);
|
|
||||||
|
|
||||||
barStack.pseudoClassStateChanged(stateSuccess, false);
|
|
||||||
barStack.pseudoClassStateChanged(stateDanger, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
bar.progressProperty().bind(task.progressProperty());
|
|
||||||
barText.textProperty().bind(task.messageProperty());
|
|
||||||
|
|
||||||
new Thread(task).start();
|
|
||||||
});
|
|
||||||
|
|
||||||
return new SampleBlock("Dynamic Color Change", content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProgressBar createBar(double progress, boolean disabled, String... styleClasses) {
|
|
||||||
var bar = new ProgressBar(progress);
|
|
||||||
bar.getStyleClass().addAll(styleClasses);
|
|
||||||
bar.setDisable(disabled);
|
|
||||||
return bar;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProgressIndicator createIndicator(double progress, boolean disabled) {
|
|
||||||
var indicator = new ProgressIndicator(progress);
|
|
||||||
indicator.setMinSize(50, 50);
|
|
||||||
indicator.setMaxSize(50, 50);
|
|
||||||
indicator.setDisable(disabled);
|
|
||||||
return indicator;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,17 +2,15 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.scene.control.RadioButton;
|
import javafx.scene.control.RadioButton;
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
public class RadioButtonPage extends AbstractPage {
|
public class RadioButtonPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "RadioButton";
|
public static final String NAME = "RadioButton";
|
||||||
|
|
||||||
@ -23,30 +21,30 @@ public class RadioButtonPage extends AbstractPage {
|
|||||||
|
|
||||||
public RadioButtonPage() {
|
public RadioButtonPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new FlowPane(
|
|
||||||
Page.PAGE_HGAP, Page.PAGE_VGAP,
|
addFormattedText("""
|
||||||
basicSample(),
|
[i]RadioButton[/i]'s create a series of items where only one item can be selected.""");
|
||||||
groupSample(),
|
addSection("Usage", usageExample());
|
||||||
disabledSample()
|
addSection("Toggle Group", toggleGroupExample());
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
var radio1 = new RadioButton("_Check Me");
|
var radio1 = new RadioButton("_Check Me");
|
||||||
radio1.setMnemonicParsing(true);
|
radio1.setMnemonicParsing(true);
|
||||||
|
|
||||||
var radio2 = new RadioButton("Check Me");
|
var radio2 = new RadioButton("Check Me");
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
return new SampleBlock("Basic", new VBox(BLOCK_VGAP, radio1, radio2));
|
var box = new VBox(VGAP_10, radio1, radio2);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A radio button control can be either selected or deselected.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
private ExampleBox toggleGroupExample() {
|
||||||
var radio = new RadioButton("Check Me");
|
//snippet_2:start
|
||||||
radio.setDisable(true);
|
|
||||||
return new SampleBlock("Disabled", radio);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock groupSample() {
|
|
||||||
var group = new ToggleGroup();
|
var group = new ToggleGroup();
|
||||||
|
|
||||||
var musicRadio = new RadioButton("Music");
|
var musicRadio = new RadioButton("Music");
|
||||||
@ -58,7 +56,14 @@ public class RadioButtonPage extends AbstractPage {
|
|||||||
|
|
||||||
var videosRadio = new RadioButton("Videos");
|
var videosRadio = new RadioButton("Videos");
|
||||||
videosRadio.setToggleGroup(group);
|
videosRadio.setToggleGroup(group);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
return new SampleBlock("Toggle Group", new VBox(5, musicRadio, imagesRadio, videosRadio));
|
var box = new VBox(VGAP_10, musicRadio, imagesRadio, videosRadio);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Typically radio buttons are combined into a group where only one button \
|
||||||
|
at a time can be selected. This behavior distinguishes them from toggle buttons, \
|
||||||
|
because all toggle buttons in a group can be in a deselected state.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.AbstractPage;
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.FlowPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
@ -15,7 +13,6 @@ import javafx.scene.layout.VBox;
|
|||||||
public class ScrollPanePage extends AbstractPage {
|
public class ScrollPanePage extends AbstractPage {
|
||||||
|
|
||||||
public static final String NAME = "ScrollPane";
|
public static final String NAME = "ScrollPane";
|
||||||
private static final int SPACING = 1;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -24,63 +21,69 @@ public class ScrollPanePage extends AbstractPage {
|
|||||||
|
|
||||||
public ScrollPanePage() {
|
public ScrollPanePage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new FlowPane(
|
|
||||||
Page.PAGE_VGAP, Page.PAGE_HGAP,
|
addFormattedText("""
|
||||||
horizontalScrollSample(),
|
A control that provides a scrolled, clipped viewport of its contents. \
|
||||||
verticalScrollSample(),
|
It allows the user to scroll the content around either directly (panning) \
|
||||||
gridScrollSample(),
|
or by using scroll bars.
|
||||||
disabledSample()
|
|
||||||
|
The [i]ScrollPane[/i] allows specification of the scroll bar policy, which \
|
||||||
|
determines when scroll bars are displayed: always, never, or only when they \
|
||||||
|
are needed. The scroll bar policy can be specified independently for the \
|
||||||
|
horizontal and vertical scroll bars."""
|
||||||
|
);
|
||||||
|
addNode(new FlowPane(
|
||||||
|
40, 40,
|
||||||
|
horizontalScrollExample(),
|
||||||
|
verticalScrollExample(),
|
||||||
|
gridScrollExample()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock horizontalScrollSample() {
|
private ScrollPane horizontalScrollExample() {
|
||||||
var scrollPane = new ScrollPane();
|
var scrollPane = new ScrollPane();
|
||||||
scrollPane.setMaxHeight(100);
|
scrollPane.setMaxHeight(100);
|
||||||
scrollPane.setMaxWidth(300);
|
scrollPane.setMaxWidth(300);
|
||||||
scrollPane.setContent(new HBox(SPACING,
|
scrollPane.setContent(new HBox(1,
|
||||||
createRegion(200, 100, "-color-success-emphasis"),
|
createRegion(200, 100, "-color-success-emphasis"),
|
||||||
createRegion(200, 100, "-color-danger-emphasis")
|
createRegion(200, 100, "-color-danger-emphasis")
|
||||||
));
|
));
|
||||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||||
|
|
||||||
return new SampleBlock("Horizontal Scrolling", scrollPane);
|
return scrollPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock verticalScrollSample() {
|
private ScrollPane verticalScrollExample() {
|
||||||
var scrollPane = new ScrollPane();
|
var scrollPane = new ScrollPane();
|
||||||
scrollPane.setMaxHeight(100);
|
scrollPane.setMaxHeight(100);
|
||||||
scrollPane.setMaxWidth(300);
|
scrollPane.setMaxWidth(300);
|
||||||
scrollPane.setContent(new VBox(SPACING,
|
scrollPane.setContent(new VBox(1,
|
||||||
createRegion(300, 75, "-color-success-emphasis"),
|
createRegion(300, 75, "-color-success-emphasis"),
|
||||||
createRegion(300, 75, "-color-danger-emphasis")
|
createRegion(300, 75, "-color-danger-emphasis")
|
||||||
));
|
));
|
||||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||||
|
|
||||||
return new SampleBlock("Vertical Scrolling", scrollPane);
|
return scrollPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock gridScrollSample() {
|
private ScrollPane gridScrollExample() {
|
||||||
var grid = new GridPane();
|
var grid = new GridPane();
|
||||||
grid.add(createRegion(200, 75, "-color-success-emphasis"), 0, 0);
|
grid.setHgap(1);
|
||||||
grid.add(createRegion(200, 75, "-color-danger-emphasis"), 1, 0);
|
grid.setVgap(1);
|
||||||
grid.add(createRegion(200, 75, "-color-danger-emphasis"), 0, 1);
|
grid.addColumn(0,
|
||||||
grid.add(createRegion(200, 75, "-color-success-emphasis"), 1, 1);
|
createRegion(200, 75, "-color-success-emphasis"),
|
||||||
grid.setHgap(SPACING);
|
createRegion(200, 75, "-color-danger-emphasis")
|
||||||
grid.setVgap(SPACING);
|
);
|
||||||
|
grid.addColumn(1,
|
||||||
|
createRegion(200, 75, "-color-danger-emphasis"),
|
||||||
|
createRegion(200, 75, "-color-success-emphasis")
|
||||||
|
);
|
||||||
|
|
||||||
var gridScroll = new ScrollPane();
|
var gridScroll = new ScrollPane(grid);
|
||||||
gridScroll.setMaxHeight(100);
|
gridScroll.setMaxHeight(100);
|
||||||
gridScroll.setMaxWidth(300);
|
gridScroll.setMaxWidth(300);
|
||||||
gridScroll.setContent(grid);
|
|
||||||
|
|
||||||
return new SampleBlock("Scrolling", gridScroll);
|
return gridScroll;
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
|
||||||
var block = gridScrollSample();
|
|
||||||
block.setTitle("Disabled");
|
|
||||||
block.getContent().setDisable(true);
|
|
||||||
return block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Region createRegion(int width, int height, String bg) {
|
private Region createRegion(int width, int height, String bg) {
|
||||||
|
@ -2,28 +2,22 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static javafx.geometry.Orientation.HORIZONTAL;
|
|
||||||
import static javafx.geometry.Orientation.VERTICAL;
|
|
||||||
import static javafx.geometry.Pos.CENTER;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Orientation;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Separator;
|
import javafx.scene.control.Separator;
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
public final class SeparatorPage extends AbstractPage {
|
public final class SeparatorPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Separator";
|
public static final String NAME = "Separator";
|
||||||
private static final int SPACING = 50;
|
|
||||||
private static final int PANE_SIZE = 100;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -32,79 +26,85 @@ public final class SeparatorPage extends AbstractPage {
|
|||||||
|
|
||||||
public SeparatorPage() {
|
public SeparatorPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new FlowPane(
|
|
||||||
Page.PAGE_HGAP, Page.PAGE_VGAP,
|
addFormattedText("""
|
||||||
orientationSample(),
|
A horizontal or vertical separator line. A horizontal separator occupies \
|
||||||
sizeSample()
|
the full horizontal space allocated to it (less padding), and a vertical \
|
||||||
));
|
separator occupies the full vertical space allocated to it (less padding)."""
|
||||||
|
);
|
||||||
|
addSection("Orientation", orientationExample());
|
||||||
|
addSection("Size", sizeExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock orientationSample() {
|
private ExampleBox orientationExample() {
|
||||||
var hBox = new HBox(
|
//snippet_1:start
|
||||||
createPane("Left", VERTICAL),
|
var hbox = new HBox(
|
||||||
new Separator(VERTICAL),
|
createPane("Left", Orientation.VERTICAL),
|
||||||
createPane("Right", VERTICAL)
|
new Separator(Orientation.VERTICAL),
|
||||||
|
createPane("Right", Orientation.VERTICAL)
|
||||||
);
|
);
|
||||||
hBox.setAlignment(CENTER);
|
|
||||||
|
|
||||||
var vBox = new VBox(
|
var vbox = new VBox(
|
||||||
createPane("Top", HORIZONTAL),
|
createPane("Top", Orientation.HORIZONTAL),
|
||||||
new Separator(HORIZONTAL),
|
new Separator(Orientation.HORIZONTAL),
|
||||||
createPane("Bottom", HORIZONTAL)
|
createPane("Bottom", Orientation.HORIZONTAL)
|
||||||
);
|
);
|
||||||
vBox.setAlignment(CENTER);
|
//snippet_1:end
|
||||||
|
|
||||||
return new SampleBlock("Orientation", new HBox(SPACING, hBox, vBox));
|
var box = new HBox(50, hbox, vbox);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock sizeSample() {
|
private ExampleBox sizeExample() {
|
||||||
var smallSep = new Separator(VERTICAL);
|
//snippet_2:start
|
||||||
|
var smallSep = new Separator(Orientation.VERTICAL);
|
||||||
smallSep.getStyleClass().add(Styles.SMALL);
|
smallSep.getStyleClass().add(Styles.SMALL);
|
||||||
var smallBox = new HBox(
|
var smallBox = new HBox(
|
||||||
createPane("Left", VERTICAL),
|
createPane("Left", Orientation.VERTICAL),
|
||||||
smallSep,
|
smallSep,
|
||||||
createPane("Right", VERTICAL)
|
createPane("Right", Orientation.VERTICAL)
|
||||||
);
|
);
|
||||||
smallBox.setAlignment(CENTER);
|
|
||||||
|
|
||||||
var mediumSep = new Separator(VERTICAL);
|
var mediumSep = new Separator(Orientation.VERTICAL);
|
||||||
mediumSep.getStyleClass().add(Styles.MEDIUM);
|
mediumSep.getStyleClass().add(Styles.MEDIUM);
|
||||||
var mediumBox = new HBox(
|
var mediumBox = new HBox(
|
||||||
createPane("Left", VERTICAL),
|
createPane("Left", Orientation.VERTICAL),
|
||||||
mediumSep,
|
mediumSep,
|
||||||
createPane("Right", VERTICAL)
|
createPane("Right", Orientation.VERTICAL)
|
||||||
);
|
);
|
||||||
mediumBox.setAlignment(CENTER);
|
|
||||||
|
|
||||||
var largeSep = new Separator(VERTICAL);
|
var largeSep = new Separator(Orientation.VERTICAL);
|
||||||
largeSep.getStyleClass().add(Styles.LARGE);
|
largeSep.getStyleClass().add(Styles.LARGE);
|
||||||
var largeBox = new HBox(
|
var largeBox = new HBox(
|
||||||
createPane("Left", VERTICAL),
|
createPane("Left", Orientation.VERTICAL),
|
||||||
largeSep,
|
largeSep,
|
||||||
createPane("Right", VERTICAL)
|
createPane("Right", Orientation.VERTICAL)
|
||||||
);
|
);
|
||||||
largeBox.setAlignment(CENTER);
|
//snippet_2:end
|
||||||
|
|
||||||
return new SampleBlock("Size", new HBox(SPACING, smallBox, mediumBox, largeBox));
|
var grid = new GridPane();
|
||||||
|
grid.setHgap(50);
|
||||||
|
grid.setVgap(50);
|
||||||
|
grid.addRow(0, smallBox, mediumBox);
|
||||||
|
grid.addRow(1, largeBox);
|
||||||
|
|
||||||
|
return new ExampleBox(grid, new Snippet(getClass(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pane createPane(String text, Orientation orientation) {
|
private Pane createPane(String text, Orientation orientation) {
|
||||||
var pane = new StackPane();
|
var pane = new StackPane(new Label(text));
|
||||||
pane.getChildren().setAll(new Label(text));
|
|
||||||
pane.getStyleClass().add("bordered");
|
pane.getStyleClass().add("bordered");
|
||||||
|
pane.setMinSize(100, 100);
|
||||||
|
|
||||||
if (orientation == HORIZONTAL) {
|
if (orientation == Orientation.HORIZONTAL) {
|
||||||
pane.setMinHeight(PANE_SIZE);
|
pane.setPrefHeight(100);
|
||||||
pane.setPrefHeight(PANE_SIZE);
|
pane.setMaxHeight(100);
|
||||||
pane.setMaxHeight(PANE_SIZE);
|
|
||||||
pane.setMinWidth(PANE_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orientation == VERTICAL) {
|
if (orientation == Orientation.VERTICAL) {
|
||||||
pane.setMinWidth(PANE_SIZE);
|
pane.setPrefWidth(100);
|
||||||
pane.setPrefWidth(PANE_SIZE);
|
pane.setMaxWidth(100);
|
||||||
pane.setMaxWidth(PANE_SIZE);
|
|
||||||
pane.setMinHeight(PANE_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pane;
|
return pane;
|
||||||
|
@ -2,23 +2,18 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static javafx.geometry.Orientation.VERTICAL;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.ProgressSliderSkin;
|
import atlantafx.base.controls.ProgressSliderSkin;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import javafx.geometry.Orientation;
|
||||||
import javafx.scene.control.Slider;
|
import javafx.scene.control.Slider;
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
|
|
||||||
public class SliderPage extends AbstractPage {
|
public class SliderPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Slider";
|
public static final String NAME = "Slider";
|
||||||
private static final int SLIDER_SIZE = 180;
|
|
||||||
private static final int SPACING = 20;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -27,32 +22,44 @@ public class SliderPage extends AbstractPage {
|
|||||||
|
|
||||||
public SliderPage() {
|
public SliderPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new FlowPane(
|
|
||||||
Page.PAGE_HGAP, Page.PAGE_VGAP,
|
addFormattedText("""
|
||||||
basicSample(),
|
The [i]Slider[/i] control is used to display a continuous or discrete range of valid numeric \
|
||||||
smallSample(),
|
choices. It is typically represented visually as having a [i]track[/i] and a [i]knob[/i] \
|
||||||
largeSample(),
|
or [i]thumb[/i] which is dragged within the track. The [i]Slider[/i] can optionally show tick \
|
||||||
disabledSample()
|
marks and labels indicating the different slider position values.
|
||||||
));
|
|
||||||
|
AtlantaFX also provides the [b]ProgressSliderSkin[/b], which implements color support for \
|
||||||
|
[i]Slider[/i] progress indication. Additionally, it adds the [code]Styles.SMALL[/code] and \
|
||||||
|
[code]Styles.LARGE[/code] style class modifiers to change the [i]Slider[/i] size."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Small", smallExample());
|
||||||
|
addSection("Large", largeExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
var hSlider = new Slider(1, 5, 3);
|
var hSlider = new Slider(1, 5, 3);
|
||||||
|
|
||||||
var hTickSlider = createTickSlider();
|
var hTickSlider = createTickSlider();
|
||||||
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
||||||
|
|
||||||
var vSlider = new Slider(1, 5, 3);
|
var vSlider = new Slider(1, 5, 3);
|
||||||
vSlider.setOrientation(VERTICAL);
|
vSlider.setOrientation(Orientation.VERTICAL);
|
||||||
|
|
||||||
var vTickSlider = createTickSlider();
|
var vTickSlider = createTickSlider();
|
||||||
vTickSlider.setOrientation(VERTICAL);
|
vTickSlider.setOrientation(Orientation.VERTICAL);
|
||||||
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
return new SampleBlock("Basic", createContent(hSlider, hTickSlider, vSlider, vTickSlider));
|
var box = createGrid(hSlider, hTickSlider, vSlider, vTickSlider);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pane smallSample() {
|
private ExampleBox smallExample() {
|
||||||
|
//snippet_2:start
|
||||||
var hSlider = new Slider(1, 5, 3);
|
var hSlider = new Slider(1, 5, 3);
|
||||||
hSlider.getStyleClass().add(Styles.SMALL);
|
hSlider.getStyleClass().add(Styles.SMALL);
|
||||||
|
|
||||||
@ -61,18 +68,22 @@ public class SliderPage extends AbstractPage {
|
|||||||
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
||||||
|
|
||||||
var vSlider = new Slider(1, 5, 3);
|
var vSlider = new Slider(1, 5, 3);
|
||||||
vSlider.setOrientation(VERTICAL);
|
vSlider.setOrientation(Orientation.VERTICAL);
|
||||||
vSlider.getStyleClass().add(Styles.SMALL);
|
vSlider.getStyleClass().add(Styles.SMALL);
|
||||||
|
|
||||||
var vTickSlider = createTickSlider();
|
var vTickSlider = createTickSlider();
|
||||||
vTickSlider.setOrientation(VERTICAL);
|
vTickSlider.setOrientation(Orientation.VERTICAL);
|
||||||
vTickSlider.getStyleClass().add(Styles.SMALL);
|
vTickSlider.getStyleClass().add(Styles.SMALL);
|
||||||
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
return new SampleBlock("Small", createContent(hSlider, hTickSlider, vSlider, vTickSlider));
|
var box = createGrid(hSlider, hTickSlider, vSlider, vTickSlider);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pane largeSample() {
|
private ExampleBox largeExample() {
|
||||||
|
//snippet_3:start
|
||||||
var hSlider = new Slider(1, 5, 3);
|
var hSlider = new Slider(1, 5, 3);
|
||||||
hSlider.getStyleClass().add(Styles.LARGE);
|
hSlider.getStyleClass().add(Styles.LARGE);
|
||||||
|
|
||||||
@ -81,35 +92,18 @@ public class SliderPage extends AbstractPage {
|
|||||||
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
||||||
|
|
||||||
var vSlider = new Slider(1, 5, 3);
|
var vSlider = new Slider(1, 5, 3);
|
||||||
vSlider.setOrientation(VERTICAL);
|
vSlider.setOrientation(Orientation.VERTICAL);
|
||||||
vSlider.getStyleClass().add(Styles.LARGE);
|
vSlider.getStyleClass().add(Styles.LARGE);
|
||||||
|
|
||||||
var vTickSlider = createTickSlider();
|
var vTickSlider = createTickSlider();
|
||||||
vTickSlider.setOrientation(VERTICAL);
|
vTickSlider.setOrientation(Orientation.VERTICAL);
|
||||||
vTickSlider.getStyleClass().add(Styles.LARGE);
|
vTickSlider.getStyleClass().add(Styles.LARGE);
|
||||||
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
return new SampleBlock("Large", createContent(hSlider, hTickSlider, vSlider, vTickSlider));
|
var box = createGrid(hSlider, hTickSlider, vSlider, vTickSlider);
|
||||||
}
|
|
||||||
|
|
||||||
private Pane disabledSample() {
|
return new ExampleBox(box, new Snippet(getClass(), 3));
|
||||||
var hSlider = new Slider(1, 5, 3);
|
|
||||||
hSlider.setDisable(true);
|
|
||||||
|
|
||||||
var hTickSlider = createTickSlider();
|
|
||||||
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
|
||||||
hTickSlider.setDisable(true);
|
|
||||||
|
|
||||||
var vSlider = new Slider(1, 5, 3);
|
|
||||||
vSlider.setOrientation(VERTICAL);
|
|
||||||
vSlider.setDisable(true);
|
|
||||||
|
|
||||||
var vTickSlider = createTickSlider();
|
|
||||||
vTickSlider.setOrientation(VERTICAL);
|
|
||||||
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
|
||||||
vTickSlider.setDisable(true);
|
|
||||||
|
|
||||||
return new SampleBlock("Disabled", createContent(hSlider, hTickSlider, vSlider, vTickSlider));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Slider createTickSlider() {
|
private Slider createTickSlider() {
|
||||||
@ -123,22 +117,20 @@ public class SliderPage extends AbstractPage {
|
|||||||
return slider;
|
return slider;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GridPane createContent(Slider h1, Slider h2, Slider v1, Slider v2) {
|
private GridPane createGrid(Slider hs1, Slider hs2, Slider vs1, Slider vs2) {
|
||||||
var grid = new GridPane();
|
var grid = new GridPane();
|
||||||
grid.setVgap(SPACING);
|
grid.setVgap(20);
|
||||||
grid.setHgap(SPACING);
|
grid.setHgap(20);
|
||||||
|
|
||||||
h1.setPrefWidth(SLIDER_SIZE);
|
hs1.setMinWidth(200);
|
||||||
h2.setPrefWidth(SLIDER_SIZE);
|
hs2.setMinWidth(200);
|
||||||
|
vs1.setMinHeight(200);
|
||||||
|
vs2.setMinHeight(200);
|
||||||
|
|
||||||
v1.setPrefHeight(SLIDER_SIZE);
|
grid.add(hs1, 0, 0);
|
||||||
v2.setPrefHeight(SLIDER_SIZE);
|
grid.add(hs2, 0, 1);
|
||||||
|
grid.add(vs1, 1, 0, 1, GridPane.REMAINING);
|
||||||
grid.add(h1, 0, 0);
|
grid.add(vs2, 2, 0, 1, GridPane.REMAINING);
|
||||||
grid.add(h2, 0, 1);
|
|
||||||
|
|
||||||
grid.add(v1, 1, 0, 1, GridPane.REMAINING);
|
|
||||||
grid.add(v2, 2, 0, 1, GridPane.REMAINING);
|
|
||||||
|
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,19 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.base.util.IntegerStringConverter;
|
import atlantafx.base.util.IntegerStringConverter;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Spinner;
|
import javafx.scene.control.Spinner;
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
public final class SpinnerPage extends AbstractPage {
|
public final class SpinnerPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Spinner";
|
public static final String NAME = "Spinner";
|
||||||
private static final int PREF_WIDTH = 120;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -21,75 +23,97 @@ public final class SpinnerPage extends AbstractPage {
|
|||||||
|
|
||||||
public SpinnerPage() {
|
public SpinnerPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new FlowPane(
|
|
||||||
Page.PAGE_HGAP, Page.PAGE_VGAP,
|
addPlainText("""
|
||||||
basicSample(),
|
A single line text field that lets the user select a number or an object \
|
||||||
arrowsLeftVerticalSample(),
|
value from an ordered sequence. The user may also be allowed to type a (legal) \
|
||||||
arrowsLeftHorizontalSample(),
|
value directly into the spinner."""
|
||||||
arrowsRightHorizontalSample(),
|
);
|
||||||
arrowsSplitHorizontalSample(),
|
addSection("Usage", usageExample());
|
||||||
arrowsSplitVerticalSample(),
|
addSection("Horizontal", horizontalExample());
|
||||||
disabledSample()
|
addSection("Vertical", verticalExample());
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
var spinner = new Spinner<Integer>(1, 10, 1);
|
var spinner = new Spinner<Integer>(1, 10, 1);
|
||||||
IntegerStringConverter.createFor(spinner);
|
IntegerStringConverter.createFor(spinner);
|
||||||
spinner.setPrefWidth(PREF_WIDTH);
|
|
||||||
spinner.setEditable(true);
|
spinner.setEditable(true);
|
||||||
return new SampleBlock("Basic", spinner);
|
spinner.setPrefWidth(120);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(spinner);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A [i]Spinner[/i] has a [i]TextField[/i] child component that is responsible \
|
||||||
|
for displaying and potentially changing the current value of the [i]Spinner[/i], \
|
||||||
|
which is called the editor. By default the Spinner is non-editable, but input \
|
||||||
|
can be accepted if the editable property is set to true. The [i]Spinner[/i] editor stays \
|
||||||
|
in sync with the value factory by listening for changes to the value property \
|
||||||
|
of the value factory."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
private ExampleBox horizontalExample() {
|
||||||
var spinner = new Spinner<Integer>(1, 10, 1);
|
//snippet_2:start
|
||||||
spinner.setPrefWidth(PREF_WIDTH);
|
var sp1 = new Spinner<Integer>(1, 10, 1);
|
||||||
spinner.setDisable(true);
|
IntegerStringConverter.createFor(sp1);
|
||||||
return new SampleBlock("Disabled", spinner);
|
sp1.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_VERTICAL);
|
||||||
|
sp1.setPrefWidth(120);
|
||||||
|
sp1.setEditable(true);
|
||||||
|
|
||||||
|
var sp2 = new Spinner<Integer>(1, 10, 1);
|
||||||
|
IntegerStringConverter.createFor(sp2);
|
||||||
|
sp2.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_HORIZONTAL);
|
||||||
|
sp2.setPrefWidth(120);
|
||||||
|
sp2.setEditable(true);
|
||||||
|
|
||||||
|
var sp3 = new Spinner<Integer>(1, 10, 1);
|
||||||
|
IntegerStringConverter.createFor(sp3);
|
||||||
|
sp3.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_RIGHT_HORIZONTAL);
|
||||||
|
sp3.setPrefWidth(120);
|
||||||
|
sp3.setEditable(true);
|
||||||
|
|
||||||
|
var sp4 = new Spinner<Integer>(1, 10, 1);
|
||||||
|
IntegerStringConverter.createFor(sp4);
|
||||||
|
sp4.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
|
||||||
|
sp4.setPrefWidth(120);
|
||||||
|
sp4.setEditable(true);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var grid = new GridPane();
|
||||||
|
grid.setVgap(VGAP_20);
|
||||||
|
grid.setHgap(HGAP_30);
|
||||||
|
grid.addRow(0, captionLabel("STYLE_CLASS_ARROWS_ON_LEFT_VERTICAL"), sp1);
|
||||||
|
grid.addRow(1, captionLabel("STYLE_CLASS_ARROWS_ON_LEFT_HORIZONTAL"), sp2);
|
||||||
|
grid.addRow(2, captionLabel("STYLE_CLASS_ARROWS_ON_RIGHT_HORIZONTAL"), sp3);
|
||||||
|
grid.addRow(3, captionLabel("STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL"), sp4);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]Spinner[/i] also supports several style class modifiers that determine \
|
||||||
|
the arrow’s position."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(grid, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock arrowsLeftVerticalSample() {
|
private ExampleBox verticalExample() {
|
||||||
var spinner = new Spinner<Integer>(1, 10, 1);
|
//snippet_3:start
|
||||||
IntegerStringConverter.createFor(spinner);
|
var sp = new Spinner<Integer>(1, 10, 1);
|
||||||
spinner.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_VERTICAL);
|
IntegerStringConverter.createFor(sp);
|
||||||
spinner.setPrefWidth(PREF_WIDTH);
|
sp.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_VERTICAL);
|
||||||
spinner.setEditable(true);
|
sp.setEditable(true);
|
||||||
return new SampleBlock("Left & Vertical", spinner);
|
sp.setPrefWidth(40);
|
||||||
}
|
//snippet_3:end
|
||||||
|
|
||||||
private SampleBlock arrowsLeftHorizontalSample() {
|
var box = new HBox(HGAP_30, captionLabel("STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL"), sp);
|
||||||
var spinner = new Spinner<Integer>(1, 10, 1);
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
IntegerStringConverter.createFor(spinner);
|
|
||||||
spinner.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_HORIZONTAL);
|
|
||||||
spinner.setPrefWidth(PREF_WIDTH);
|
|
||||||
spinner.setEditable(true);
|
|
||||||
return new SampleBlock("Left & Horizontal", spinner);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock arrowsRightHorizontalSample() {
|
var description = BBCodeParser.createFormattedText("""
|
||||||
var spinner = new Spinner<Integer>(1, 10, 1);
|
Similar to the previous example, but for the vertical direction."""
|
||||||
IntegerStringConverter.createFor(spinner);
|
);
|
||||||
spinner.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_RIGHT_HORIZONTAL);
|
|
||||||
spinner.setPrefWidth(PREF_WIDTH);
|
|
||||||
spinner.setEditable(true);
|
|
||||||
return new SampleBlock("Right & Horizontal", spinner);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock arrowsSplitHorizontalSample() {
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
var spinner = new Spinner<Integer>(1, 10, 1);
|
|
||||||
IntegerStringConverter.createFor(spinner);
|
|
||||||
spinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
|
|
||||||
spinner.setPrefWidth(PREF_WIDTH);
|
|
||||||
spinner.setEditable(true);
|
|
||||||
return new SampleBlock("Split & Horizontal", spinner);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock arrowsSplitVerticalSample() {
|
|
||||||
var spinner = new Spinner<Integer>(1, 10, 1);
|
|
||||||
IntegerStringConverter.createFor(spinner);
|
|
||||||
spinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_VERTICAL);
|
|
||||||
spinner.setEditable(true);
|
|
||||||
spinner.setPrefWidth(40);
|
|
||||||
return new SampleBlock("Split & Vertical", spinner);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,18 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Orientation;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.SplitPane;
|
import javafx.scene.control.SplitPane;
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
|
|
||||||
public class SplitPanePage extends AbstractPage {
|
public class SplitPanePage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "SplitPane";
|
public static final String NAME = "SplitPane";
|
||||||
|
|
||||||
@ -25,75 +24,116 @@ public class SplitPanePage extends AbstractPage {
|
|||||||
|
|
||||||
public SplitPanePage() {
|
public SplitPanePage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new FlowPane(
|
|
||||||
Page.PAGE_VGAP, Page.PAGE_HGAP,
|
addFormattedText("""
|
||||||
hSplitSample(),
|
A control that has two or more sides, each separated by a divider, which can be \
|
||||||
vSplitSample(),
|
dragged by the user to give more space to one of the sides, resulting in the other \
|
||||||
disabledSample(),
|
side shrinking by an equal amount."""
|
||||||
gridSample()
|
);
|
||||||
));
|
addSection("Horizontal", hSplitExample());
|
||||||
|
addSection("Vertical", vSplitExample());
|
||||||
|
addSection("Nested", nestedSplitExample());
|
||||||
|
addSection("Multiple Dividers", multiDividersSplitExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock hSplitSample() {
|
private ExampleBox hSplitExample() {
|
||||||
var splitPane = new SplitPane();
|
//snippet_1:start
|
||||||
splitPane.setOrientation(Orientation.HORIZONTAL);
|
var sp = new SplitPane(
|
||||||
splitPane.setDividerPositions(0.5);
|
createBox("Left"),
|
||||||
splitPane.getItems().setAll(createBox("Left"), createBox("Right"));
|
createBox("Right")
|
||||||
splitPane.setMinSize(200, 100);
|
);
|
||||||
splitPane.setMaxSize(200, 100);
|
sp.setOrientation(Orientation.HORIZONTAL);
|
||||||
return new SampleBlock("Horizontal", splitPane);
|
sp.setDividerPositions(0.5);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
sp.setMinSize(400, 100);
|
||||||
|
sp.setMaxSize(400, 100);
|
||||||
|
var box = new HBox(sp);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock vSplitSample() {
|
private ExampleBox vSplitExample() {
|
||||||
var splitPane = new SplitPane();
|
//snippet_2:start
|
||||||
splitPane.setOrientation(Orientation.VERTICAL);
|
var sp = new SplitPane(
|
||||||
splitPane.setDividerPositions(0.5);
|
createBox("Top"),
|
||||||
splitPane.getItems().setAll(createBox("Top"), createBox("Bottom"));
|
createBox("Bottom")
|
||||||
splitPane.setMinSize(100, 200);
|
);
|
||||||
splitPane.setMaxSize(100, 200);
|
sp.setOrientation(Orientation.VERTICAL);
|
||||||
return new SampleBlock("Vertical", splitPane);
|
sp.setDividerPositions(0.5);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
sp.setMinSize(400, 200);
|
||||||
|
sp.setMaxSize(400, 200);
|
||||||
|
var box = new HBox(sp);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock gridSample() {
|
private ExampleBox nestedSplitExample() {
|
||||||
var topSplitPane = new SplitPane();
|
//snippet_3:start
|
||||||
|
var topSplitPane = new SplitPane(
|
||||||
|
createBox("Quarter 4"),
|
||||||
|
createBox("Quarter 1")
|
||||||
|
);
|
||||||
topSplitPane.setOrientation(Orientation.HORIZONTAL);
|
topSplitPane.setOrientation(Orientation.HORIZONTAL);
|
||||||
topSplitPane.setDividerPositions(0.5);
|
topSplitPane.setDividerPositions(0.5);
|
||||||
topSplitPane.getItems().setAll(createBox("Quarter 4"), createBox("Quarter 1"));
|
|
||||||
VBox.setVgrow(topSplitPane, Priority.ALWAYS);
|
VBox.setVgrow(topSplitPane, Priority.ALWAYS);
|
||||||
|
|
||||||
var topBox = new VBox(topSplitPane);
|
var topBox = new VBox(topSplitPane);
|
||||||
topBox.setAlignment(Pos.CENTER);
|
topBox.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
var bottomSplitPane = new SplitPane();
|
var bottomSplitPane = new SplitPane(
|
||||||
|
createBox("Quarter 3"),
|
||||||
|
createBox("Quarter 2")
|
||||||
|
);
|
||||||
bottomSplitPane.setOrientation(Orientation.HORIZONTAL);
|
bottomSplitPane.setOrientation(Orientation.HORIZONTAL);
|
||||||
bottomSplitPane.setDividerPositions(0.5);
|
bottomSplitPane.setDividerPositions(0.5);
|
||||||
bottomSplitPane.getItems().setAll(createBox("Quarter 3"), createBox("Quarter 2"));
|
|
||||||
VBox.setVgrow(bottomSplitPane, Priority.ALWAYS);
|
VBox.setVgrow(bottomSplitPane, Priority.ALWAYS);
|
||||||
|
|
||||||
var bottomBox = new VBox(bottomSplitPane);
|
var bottomBox = new VBox(bottomSplitPane);
|
||||||
bottomBox.setAlignment(Pos.CENTER);
|
bottomBox.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
var doubleSplitPane = new SplitPane();
|
var doubleSplitPane = new SplitPane(topBox, bottomBox);
|
||||||
doubleSplitPane.setOrientation(Orientation.VERTICAL);
|
doubleSplitPane.setOrientation(Orientation.VERTICAL);
|
||||||
doubleSplitPane.setDividerPositions(0.5);
|
doubleSplitPane.setDividerPositions(0.5);
|
||||||
doubleSplitPane.getItems().setAll(topBox, bottomBox);
|
//snippet_3:end
|
||||||
|
|
||||||
doubleSplitPane.setMinSize(400, 200);
|
doubleSplitPane.setMinSize(400, 200);
|
||||||
doubleSplitPane.setMaxSize(400, 200);
|
doubleSplitPane.setMaxSize(400, 200);
|
||||||
|
var box = new HBox(doubleSplitPane);
|
||||||
|
|
||||||
return new SampleBlock("Nested", doubleSplitPane);
|
return new ExampleBox(box, new Snippet(getClass(), 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
private ExampleBox multiDividersSplitExample() {
|
||||||
var block = hSplitSample();
|
//snippet_4:start
|
||||||
block.setTitle("Disabled");
|
var sp = new SplitPane(
|
||||||
block.getContent().setDisable(true);
|
createBox("First"),
|
||||||
|
createBox("Second"),
|
||||||
|
createBox("Third"),
|
||||||
|
createBox("Fourth")
|
||||||
|
);
|
||||||
|
sp.setOrientation(Orientation.HORIZONTAL);
|
||||||
|
sp.setDividerPositions(0.25, 0.5, 0.75);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
return block;
|
sp.setMinSize(600, 200);
|
||||||
|
sp.setMaxSize(600, 200);
|
||||||
|
var box = new HBox(sp);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
private HBox createBox(String text) {
|
private HBox createBox(String s) {
|
||||||
var brick = new HBox(new Text(text));
|
var label = new Label(s);
|
||||||
brick.setAlignment(Pos.CENTER);
|
label.setMinSize(120, 80);
|
||||||
return brick;
|
label.setAlignment(Pos.CENTER);
|
||||||
|
label.setStyle("-fx-background-color:-color-accent-subtle;");
|
||||||
|
|
||||||
|
var box = new HBox(label);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
return box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,15 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.ACCENT;
|
import static javafx.scene.control.TabPane.TabClosingPolicy;
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
|
||||||
import static atlantafx.base.theme.Styles.DENSE;
|
|
||||||
import static atlantafx.base.theme.Styles.toggleStyleClass;
|
|
||||||
import static javafx.scene.control.TabPane.TabClosingPolicy.ALL_TABS;
|
|
||||||
import static javafx.scene.control.TabPane.TabClosingPolicy.UNAVAILABLE;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
@ -29,49 +26,219 @@ import javafx.scene.control.ToggleGroup;
|
|||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
public class TabPanePage extends AbstractPage {
|
public class TabPanePage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "TabPane";
|
public static final String NAME = "TabPane";
|
||||||
private static final double TAB_MIN_HEIGHT = 60;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TabPanePage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
[i]TabPane[/i] is a control that provides a container for a group of tabs. By clicking \
|
||||||
|
on a tab, the content of that tab becomes visible, while the content of the previously \
|
||||||
|
selected tab gets hidden."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Tab Style", tabStyleExample());
|
||||||
|
addSection("Vertical Tabs", verticalTabsExample());
|
||||||
|
addSection("Dense", denseExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var tab1 = new Tab("One");
|
||||||
|
tab1.setGraphic(new FontIcon(randomIcon()));
|
||||||
|
|
||||||
|
var tab2 = new Tab("Two");
|
||||||
|
tab2.setGraphic(new FontIcon(randomIcon()));
|
||||||
|
|
||||||
|
var tab3 = new Tab("Three");
|
||||||
|
tab3.setGraphic(new FontIcon(randomIcon()));
|
||||||
|
|
||||||
|
var tabs = new TabPane(tab1, tab2, tab3);
|
||||||
|
tabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
tabs.setMinWidth(450);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(tabs);
|
||||||
|
box.setMinHeight(50);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Tabs are placed within a [font=monospace]TabPane[/font], where each tab represents a single \
|
||||||
|
"page". Any node such as controls or groups of nodes added to a tab layout container. When \
|
||||||
|
the user clicks on a tab in the tab content becomes visible."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox tabStyleExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var defaultTabs = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two"), new Tab("Three")
|
||||||
|
);
|
||||||
|
defaultTabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
defaultTabs.setMinWidth(450);
|
||||||
|
|
||||||
|
var floatingTabs = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two"), new Tab("Three")
|
||||||
|
);
|
||||||
|
floatingTabs.getStyleClass().add(Styles.TABS_FLOATING);
|
||||||
|
floatingTabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
floatingTabs.setMinWidth(450);
|
||||||
|
|
||||||
|
var classicTabs = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two"), new Tab("Three")
|
||||||
|
);
|
||||||
|
classicTabs.getStyleClass().add(Styles.TABS_CLASSIC);
|
||||||
|
classicTabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
classicTabs.setMinWidth(450);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_20, defaultTabs, floatingTabs, classicTabs);
|
||||||
|
box.setMinHeight(200);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can use two additional styles classes to modify tab pane style:
|
||||||
|
|
||||||
|
[ul]
|
||||||
|
[li][code]Styles.TABS_FLOATING[/code][/li]
|
||||||
|
[li][code]Styles.TABS_CLASSIC[/code][/li][/ul]"""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox verticalTabsExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var defaultTabsLeft = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two")
|
||||||
|
);
|
||||||
|
defaultTabsLeft.setSide(Side.LEFT);
|
||||||
|
defaultTabsLeft.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
|
||||||
|
var floatingTabsLeft = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two")
|
||||||
|
);
|
||||||
|
floatingTabsLeft.setSide(Side.LEFT);
|
||||||
|
floatingTabsLeft.getStyleClass().add(Styles.TABS_FLOATING);
|
||||||
|
floatingTabsLeft.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
|
||||||
|
var classicTabsLeft = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two")
|
||||||
|
);
|
||||||
|
classicTabsLeft.setSide(Side.LEFT);
|
||||||
|
classicTabsLeft.getStyleClass().add(Styles.TABS_CLASSIC);
|
||||||
|
classicTabsLeft.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
|
||||||
|
var defaultTabsRight = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two")
|
||||||
|
);
|
||||||
|
defaultTabsRight.setSide(Side.RIGHT);
|
||||||
|
defaultTabsRight.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
|
||||||
|
var floatingTabsRight = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two")
|
||||||
|
);
|
||||||
|
floatingTabsRight.setSide(Side.RIGHT);
|
||||||
|
floatingTabsRight.getStyleClass().add(Styles.TABS_FLOATING);
|
||||||
|
floatingTabsRight.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
|
||||||
|
var classicTabsRight = new TabPane(
|
||||||
|
new Tab("One"), new Tab("Two")
|
||||||
|
);
|
||||||
|
classicTabsRight.setSide(Side.RIGHT);
|
||||||
|
classicTabsRight.getStyleClass().add(Styles.TABS_CLASSIC);
|
||||||
|
classicTabsRight.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(
|
||||||
|
HGAP_20,
|
||||||
|
defaultTabsLeft, floatingTabsLeft, classicTabsLeft,
|
||||||
|
new Spacer(),
|
||||||
|
classicTabsRight, floatingTabsRight, defaultTabsRight
|
||||||
|
);
|
||||||
|
box.setMinHeight(450);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Tabs in a tab pane can be positioned at any of the four sides by specifying the side."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox denseExample() {
|
||||||
|
//snippet_4:start
|
||||||
|
var tab1 = new Tab("One");
|
||||||
|
tab1.setGraphic(new FontIcon(randomIcon()));
|
||||||
|
|
||||||
|
var tab2 = new Tab("Two");
|
||||||
|
tab2.setGraphic(new FontIcon(randomIcon()));
|
||||||
|
|
||||||
|
var tab3 = new Tab("Three");
|
||||||
|
tab3.setGraphic(new FontIcon(randomIcon()));
|
||||||
|
|
||||||
|
var tabs = new TabPane(tab1, tab2, tab3);
|
||||||
|
tabs.getStyleClass().add(Styles.DENSE);
|
||||||
|
tabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
|
tabs.setMinWidth(450);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var box = new HBox(tabs);
|
||||||
|
box.setMinHeight(50);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
There's also [code]Styles.DENSE[/code] to make tabs look more compact by cutting label padding."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private Side tabSide = Side.TOP;
|
private Side tabSide = Side.TOP;
|
||||||
private boolean fullWidth = false;
|
private boolean fullWidth = false;
|
||||||
|
|
||||||
public TabPanePage() {
|
private Pane playground() {
|
||||||
super();
|
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
|
||||||
var tabs = createTabPane();
|
var tabs = createTabPane();
|
||||||
|
|
||||||
var tabsLayer = new BorderPane();
|
var tabsLayer = new BorderPane();
|
||||||
tabsLayer.setTop(tabs);
|
tabsLayer.setTop(tabs);
|
||||||
tabs.getTabs().addListener((ListChangeListener<Tab>) c -> updateTabsWidth(tabsLayer, tabs, fullWidth));
|
tabs.getTabs().addListener((ListChangeListener<Tab>) c ->
|
||||||
|
updateTabsWidth(tabsLayer, tabs, fullWidth)
|
||||||
|
);
|
||||||
|
|
||||||
var controller = createController(tabsLayer, tabs);
|
var controller = createController(tabsLayer, tabs);
|
||||||
controller.setPrefSize(500, 300);
|
|
||||||
var controllerLayer = new BorderPane();
|
var controllerLayer = new BorderPane(controller);
|
||||||
controllerLayer.setCenter(controller);
|
controllerLayer.setMinSize(500, 300);
|
||||||
controllerLayer.setMaxSize(500, 300);
|
controllerLayer.setMaxSize(500, 300);
|
||||||
|
|
||||||
var root = new StackPane();
|
var description = BBCodeParser.createFormattedText("""
|
||||||
root.getStyleClass().add(Styles.BORDERED);
|
The playground demonstrates the most important [i]TabPane[/i] features \
|
||||||
root.getChildren().addAll(tabsLayer, controllerLayer);
|
and also serves as an object for monkey testing."""
|
||||||
VBox.setVgrow(root, Priority.ALWAYS);
|
);
|
||||||
|
|
||||||
setUserContent(new SampleBlock("Playground", root));
|
var stack = new StackPane(tabsLayer, controllerLayer);
|
||||||
|
stack.getStyleClass().add(Styles.BORDERED);
|
||||||
|
stack.setMinSize(600, 500);
|
||||||
|
|
||||||
|
return new VBox(VGAP_10, description, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -79,29 +246,31 @@ public class TabPanePage extends AbstractPage {
|
|||||||
// == BUTTONS ==
|
// == BUTTONS ==
|
||||||
|
|
||||||
var toTopBtn = new Button("", new FontIcon(Feather.ARROW_UP));
|
var toTopBtn = new Button("", new FontIcon(Feather.ARROW_UP));
|
||||||
toTopBtn.getStyleClass().addAll(BUTTON_ICON);
|
toTopBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toTopBtn.setOnAction(e -> rotateTabs(borderPane, tabs, Side.TOP));
|
toTopBtn.setOnAction(e -> rotateTabs(borderPane, tabs, Side.TOP));
|
||||||
|
|
||||||
var toRightBtn = new Button("", new FontIcon(Feather.ARROW_RIGHT));
|
var toRightBtn = new Button("", new FontIcon(Feather.ARROW_RIGHT));
|
||||||
toRightBtn.getStyleClass().addAll(BUTTON_ICON);
|
toRightBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toRightBtn.setOnAction(e -> rotateTabs(borderPane, tabs, Side.RIGHT));
|
toRightBtn.setOnAction(e -> rotateTabs(borderPane, tabs, Side.RIGHT));
|
||||||
|
|
||||||
var toBottomBtn = new Button("", new FontIcon(Feather.ARROW_DOWN));
|
var toBottomBtn = new Button("", new FontIcon(Feather.ARROW_DOWN));
|
||||||
toBottomBtn.getStyleClass().addAll(BUTTON_ICON);
|
toBottomBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toBottomBtn.setOnAction(e -> rotateTabs(borderPane, tabs, Side.BOTTOM));
|
toBottomBtn.setOnAction(e -> rotateTabs(borderPane, tabs, Side.BOTTOM));
|
||||||
|
|
||||||
var toLeftBtn = new Button("", new FontIcon(Feather.ARROW_LEFT));
|
var toLeftBtn = new Button("", new FontIcon(Feather.ARROW_LEFT));
|
||||||
toLeftBtn.getStyleClass().addAll(BUTTON_ICON);
|
toLeftBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toLeftBtn.setOnAction(e -> rotateTabs(borderPane, tabs, Side.LEFT));
|
toLeftBtn.setOnAction(e -> rotateTabs(borderPane, tabs, Side.LEFT));
|
||||||
|
|
||||||
var appendBtn = new Button("", new FontIcon(Feather.PLUS));
|
var appendBtn = new Button("", new FontIcon(Feather.PLUS));
|
||||||
appendBtn.getStyleClass().addAll(BUTTON_ICON, ACCENT);
|
appendBtn.getStyleClass().addAll(Styles.BUTTON_ICON, Styles.ACCENT);
|
||||||
appendBtn.setOnAction(e -> tabs.getTabs().add(createRandomTab()));
|
appendBtn.setOnAction(e -> tabs.getTabs().add(createRandomTab()));
|
||||||
|
|
||||||
var buttonsPane = new BorderPane();
|
var buttonsPane = new BorderPane();
|
||||||
buttonsPane.setMinSize(120, 120);
|
buttonsPane.setMinSize(120, 120);
|
||||||
buttonsPane.setMaxSize(120, 120);
|
buttonsPane.setMaxSize(120, 120);
|
||||||
|
|
||||||
|
buttonsPane.setCenter(appendBtn);
|
||||||
|
|
||||||
buttonsPane.setTop(toTopBtn);
|
buttonsPane.setTop(toTopBtn);
|
||||||
BorderPane.setAlignment(toTopBtn, Pos.CENTER);
|
BorderPane.setAlignment(toTopBtn, Pos.CENTER);
|
||||||
|
|
||||||
@ -114,16 +283,14 @@ public class TabPanePage extends AbstractPage {
|
|||||||
buttonsPane.setLeft(toLeftBtn);
|
buttonsPane.setLeft(toLeftBtn);
|
||||||
BorderPane.setAlignment(toLeftBtn, Pos.CENTER);
|
BorderPane.setAlignment(toLeftBtn, Pos.CENTER);
|
||||||
|
|
||||||
buttonsPane.setCenter(appendBtn);
|
|
||||||
|
|
||||||
// == TOGGLES ==
|
// == TOGGLES ==
|
||||||
|
|
||||||
var closeableToggle = new ToggleSwitch();
|
var closeableToggle = new ToggleSwitch();
|
||||||
closeableToggle.selectedProperty().addListener((obs, old, val) -> {
|
closeableToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
if (val) {
|
if (val) {
|
||||||
tabs.setTabClosingPolicy(ALL_TABS);
|
tabs.setTabClosingPolicy(TabClosingPolicy.ALL_TABS);
|
||||||
} else {
|
} else {
|
||||||
tabs.setTabClosingPolicy(UNAVAILABLE);
|
tabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -133,7 +300,10 @@ public class TabPanePage extends AbstractPage {
|
|||||||
if (val != null && val) {
|
if (val != null && val) {
|
||||||
tabs.setStyle("");
|
tabs.setStyle("");
|
||||||
} else {
|
} else {
|
||||||
tabs.setStyle("-fx-open-tab-animation:none;-fx-close-tab-animation:none;");
|
tabs.setStyle("""
|
||||||
|
-fx-open-tab-animation:none;\
|
||||||
|
-fx-close-tab-animation:none;"""
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -146,7 +316,9 @@ public class TabPanePage extends AbstractPage {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var denseToggle = new ToggleSwitch();
|
var denseToggle = new ToggleSwitch();
|
||||||
denseToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(tabs, DENSE));
|
denseToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(tabs, Styles.DENSE)
|
||||||
|
);
|
||||||
|
|
||||||
var disableToggle = new ToggleSwitch();
|
var disableToggle = new ToggleSwitch();
|
||||||
disableToggle.selectedProperty().addListener((obs, old, val) -> {
|
disableToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
@ -158,21 +330,11 @@ public class TabPanePage extends AbstractPage {
|
|||||||
var togglesGrid = new GridPane();
|
var togglesGrid = new GridPane();
|
||||||
togglesGrid.setHgap(10);
|
togglesGrid.setHgap(10);
|
||||||
togglesGrid.setVgap(10);
|
togglesGrid.setVgap(10);
|
||||||
|
togglesGrid.addRow(0, createGridLabel("Closeable"), closeableToggle);
|
||||||
togglesGrid.add(createGridLabel("Closeable"), 0, 0);
|
togglesGrid.addRow(1, createGridLabel("Animated"), animatedToggle);
|
||||||
togglesGrid.add(closeableToggle, 1, 0);
|
togglesGrid.addRow(2, createGridLabel("Full width"), fullWidthToggle);
|
||||||
|
togglesGrid.addRow(3, createGridLabel("Dense"), denseToggle);
|
||||||
togglesGrid.add(createGridLabel("Animated"), 0, 1);
|
togglesGrid.addRow(4, createGridLabel("Disable"), disableToggle);
|
||||||
togglesGrid.add(animatedToggle, 1, 1);
|
|
||||||
|
|
||||||
togglesGrid.add(createGridLabel("Full width"), 0, 2);
|
|
||||||
togglesGrid.add(fullWidthToggle, 1, 2);
|
|
||||||
|
|
||||||
togglesGrid.add(createGridLabel("Dense"), 0, 3);
|
|
||||||
togglesGrid.add(denseToggle, 1, 3);
|
|
||||||
|
|
||||||
togglesGrid.add(createGridLabel("Disable"), 0, 4);
|
|
||||||
togglesGrid.add(disableToggle, 1, 4);
|
|
||||||
|
|
||||||
// == TAB STYLE ==
|
// == TAB STYLE ==
|
||||||
|
|
||||||
@ -180,18 +342,24 @@ public class TabPanePage extends AbstractPage {
|
|||||||
|
|
||||||
var defaultStyleToggle = new ToggleButton("Default");
|
var defaultStyleToggle = new ToggleButton("Default");
|
||||||
defaultStyleToggle.setToggleGroup(styleToggleGroup);
|
defaultStyleToggle.setToggleGroup(styleToggleGroup);
|
||||||
defaultStyleToggle.setUserData(List.of("whatever", Styles.TABS_FLOATING, Styles.TABS_CLASSIC));
|
defaultStyleToggle.setUserData(
|
||||||
|
List.of("whatever", Styles.TABS_FLOATING, Styles.TABS_CLASSIC)
|
||||||
|
);
|
||||||
defaultStyleToggle.getStyleClass().add(Styles.LEFT_PILL);
|
defaultStyleToggle.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
defaultStyleToggle.setSelected(true);
|
defaultStyleToggle.setSelected(true);
|
||||||
|
|
||||||
var floatingStyleToggle = new ToggleButton("Floating");
|
var floatingStyleToggle = new ToggleButton("Floating");
|
||||||
floatingStyleToggle.setToggleGroup(styleToggleGroup);
|
floatingStyleToggle.setToggleGroup(styleToggleGroup);
|
||||||
floatingStyleToggle.setUserData(List.of(Styles.TABS_FLOATING, "whatever", Styles.TABS_CLASSIC));
|
floatingStyleToggle.setUserData(
|
||||||
|
List.of(Styles.TABS_FLOATING, "whatever", Styles.TABS_CLASSIC)
|
||||||
|
);
|
||||||
floatingStyleToggle.getStyleClass().add(Styles.CENTER_PILL);
|
floatingStyleToggle.getStyleClass().add(Styles.CENTER_PILL);
|
||||||
|
|
||||||
var classicStyleToggle = new ToggleButton("Classic");
|
var classicStyleToggle = new ToggleButton("Classic");
|
||||||
classicStyleToggle.setToggleGroup(styleToggleGroup);
|
classicStyleToggle.setToggleGroup(styleToggleGroup);
|
||||||
classicStyleToggle.setUserData(List.of(Styles.TABS_CLASSIC, "whatever", Styles.TABS_FLOATING));
|
classicStyleToggle.setUserData(
|
||||||
|
List.of(Styles.TABS_CLASSIC, "whatever", Styles.TABS_FLOATING)
|
||||||
|
);
|
||||||
classicStyleToggle.getStyleClass().add(Styles.RIGHT_PILL);
|
classicStyleToggle.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
|
||||||
styleToggleGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
styleToggleGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
||||||
@ -201,17 +369,13 @@ public class TabPanePage extends AbstractPage {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
var styleBox = new HBox(defaultStyleToggle, floatingStyleToggle, classicStyleToggle);
|
var styleBox = new HBox(defaultStyleToggle, floatingStyleToggle, classicStyleToggle);
|
||||||
styleBox.setAlignment(Pos.CENTER);
|
styleBox.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
// == LAYOUT ==
|
// == LAYOUT ==
|
||||||
|
|
||||||
var controls = new HBox(40,
|
var controls = new HBox(
|
||||||
new Spacer(),
|
40, new Spacer(), buttonsPane, togglesGrid, new Spacer()
|
||||||
buttonsPane,
|
|
||||||
togglesGrid,
|
|
||||||
new Spacer()
|
|
||||||
);
|
);
|
||||||
controls.setAlignment(Pos.CENTER);
|
controls.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
@ -256,8 +420,8 @@ public class TabPanePage extends AbstractPage {
|
|||||||
|
|
||||||
private TabPane createTabPane() {
|
private TabPane createTabPane() {
|
||||||
var tabs = new TabPane();
|
var tabs = new TabPane();
|
||||||
tabs.setTabClosingPolicy(UNAVAILABLE);
|
tabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
|
||||||
tabs.setMinHeight(TAB_MIN_HEIGHT);
|
tabs.setMinHeight(60);
|
||||||
|
|
||||||
// NOTE: Individually disabled tab is still closeable even while it looks
|
// NOTE: Individually disabled tab is still closeable even while it looks
|
||||||
// like disabled. To prevent it from closing one can use "black hole"
|
// like disabled. To prevent it from closing one can use "black hole"
|
||||||
|
@ -1,331 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.BORDERED;
|
|
||||||
import static atlantafx.base.theme.Styles.DENSE;
|
|
||||||
import static atlantafx.base.theme.Styles.STRIPED;
|
|
||||||
import static atlantafx.base.theme.Styles.toggleStyleClass;
|
|
||||||
import static atlantafx.base.theme.Tweaks.ALIGN_CENTER;
|
|
||||||
import static atlantafx.base.theme.Tweaks.ALIGN_LEFT;
|
|
||||||
import static atlantafx.base.theme.Tweaks.ALIGN_RIGHT;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
import static javafx.collections.FXCollections.observableArrayList;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.CaptionMenuItem;
|
|
||||||
import atlantafx.base.controls.Spacer;
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.base.theme.Tweaks;
|
|
||||||
import atlantafx.sampler.fake.domain.Product;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.StringBinding;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.collections.transformation.FilteredList;
|
|
||||||
import javafx.collections.transformation.SortedList;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.CheckMenuItem;
|
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.MenuButton;
|
|
||||||
import javafx.scene.control.RadioMenuItem;
|
|
||||||
import javafx.scene.control.SelectionMode;
|
|
||||||
import javafx.scene.control.SeparatorMenuItem;
|
|
||||||
import javafx.scene.control.TableCell;
|
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import javafx.scene.control.TableView;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.control.ToggleGroup;
|
|
||||||
import javafx.scene.control.cell.CheckBoxTableCell;
|
|
||||||
import javafx.scene.control.cell.ChoiceBoxTableCell;
|
|
||||||
import javafx.scene.control.cell.ComboBoxTableCell;
|
|
||||||
import javafx.scene.control.cell.ProgressBarTableCell;
|
|
||||||
import javafx.scene.control.cell.PropertyValueFactory;
|
|
||||||
import javafx.scene.control.cell.TextFieldTableCell;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.util.Callback;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIconTableCell;
|
|
||||||
|
|
||||||
public class TablePage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "TableView";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TableView<Product> table;
|
|
||||||
private final List<Product> dataList = IntStream.range(1, 51).boxed()
|
|
||||||
.map(i -> Product.random(i, FAKER))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
public TablePage() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
var sample = new SampleBlock("Playground", createPlayground());
|
|
||||||
sample.setFillHeight(true);
|
|
||||||
setUserContent(sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createPlayground() {
|
|
||||||
// == FOOTER ==
|
|
||||||
|
|
||||||
var bordersToggle = new ToggleSwitch("Bordered");
|
|
||||||
bordersToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(table, BORDERED));
|
|
||||||
|
|
||||||
var denseToggle = new ToggleSwitch("Dense");
|
|
||||||
denseToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(table, DENSE));
|
|
||||||
|
|
||||||
var stripesToggle = new ToggleSwitch("Striped");
|
|
||||||
stripesToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(table, STRIPED));
|
|
||||||
|
|
||||||
var edge2edgeToggle = new ToggleSwitch("Edge to edge");
|
|
||||||
edge2edgeToggle.selectedProperty().addListener(
|
|
||||||
(obs, old, value) -> toggleStyleClass(table, Tweaks.EDGE_TO_EDGE)
|
|
||||||
);
|
|
||||||
|
|
||||||
var maxRowCount = 100;
|
|
||||||
var rowCountChoice = new ComboBox<>(observableArrayList(0, 5, 10, 25, maxRowCount));
|
|
||||||
rowCountChoice.setValue(maxRowCount);
|
|
||||||
|
|
||||||
var rowCountBox = new HBox(BLOCK_HGAP, new Label("rows"), rowCountChoice);
|
|
||||||
rowCountBox.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
var footer = new HBox(
|
|
||||||
BLOCK_HGAP,
|
|
||||||
new Spacer(),
|
|
||||||
bordersToggle,
|
|
||||||
denseToggle,
|
|
||||||
stripesToggle,
|
|
||||||
edge2edgeToggle,
|
|
||||||
new Spacer(),
|
|
||||||
rowCountBox
|
|
||||||
);
|
|
||||||
footer.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
// == TABLE ==
|
|
||||||
|
|
||||||
var filteredData = new FilteredList<>(observableArrayList(dataList));
|
|
||||||
filteredData.predicateProperty().bind(Bindings.createObjectBinding(
|
|
||||||
() -> product -> product.getId() <= rowCountChoice.getValue(),
|
|
||||||
rowCountChoice.valueProperty()
|
|
||||||
));
|
|
||||||
|
|
||||||
var sortedData = new SortedList<>(filteredData);
|
|
||||||
|
|
||||||
table = createTable();
|
|
||||||
table.setItems(sortedData);
|
|
||||||
sortedData.comparatorProperty().bind(table.comparatorProperty());
|
|
||||||
VBox.setVgrow(table, Priority.ALWAYS);
|
|
||||||
|
|
||||||
// == HEADER ==
|
|
||||||
|
|
||||||
var alignGroup = new ToggleGroup();
|
|
||||||
|
|
||||||
var alignLeftBtn = new ToggleButton("", new FontIcon(Feather.ALIGN_LEFT));
|
|
||||||
alignLeftBtn.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
alignLeftBtn.setToggleGroup(alignGroup);
|
|
||||||
alignLeftBtn.setSelected(true);
|
|
||||||
alignLeftBtn.setOnAction(e -> {
|
|
||||||
for (TableColumn<?, ?> c : table.getColumns()) {
|
|
||||||
c.getStyleClass().removeAll(ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var alignCenterBtn = new ToggleButton("", new FontIcon(Feather.ALIGN_CENTER));
|
|
||||||
alignCenterBtn.getStyleClass().add(Styles.CENTER_PILL);
|
|
||||||
alignCenterBtn.setToggleGroup(alignGroup);
|
|
||||||
alignCenterBtn.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
for (TableColumn<?, ?> c : table.getColumns()) {
|
|
||||||
addStyleClass(c, ALIGN_CENTER, ALIGN_LEFT, ALIGN_RIGHT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var alignRightBtn = new ToggleButton("", new FontIcon(Feather.ALIGN_RIGHT));
|
|
||||||
alignRightBtn.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
alignRightBtn.setToggleGroup(alignGroup);
|
|
||||||
alignRightBtn.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
for (TableColumn<?, ?> c : table.getColumns()) {
|
|
||||||
addStyleClass(c, ALIGN_RIGHT, ALIGN_LEFT, ALIGN_CENTER);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var alignBox = new HBox(alignLeftBtn, alignCenterBtn, alignRightBtn);
|
|
||||||
|
|
||||||
var disableToggle = new ToggleSwitch("Disable");
|
|
||||||
disableToggle.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null) {
|
|
||||||
table.setDisable(val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var header = new HBox(
|
|
||||||
createTablePropertiesMenu(table),
|
|
||||||
new Spacer(),
|
|
||||||
alignBox,
|
|
||||||
new Spacer(),
|
|
||||||
disableToggle
|
|
||||||
);
|
|
||||||
header.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
var playground = new VBox(BLOCK_VGAP, header, table, footer);
|
|
||||||
playground.setMinHeight(100);
|
|
||||||
|
|
||||||
return playground;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private TableView<Product> createTable() {
|
|
||||||
var stateCol = new TableColumn<Product, Boolean>("Selected");
|
|
||||||
stateCol.setCellValueFactory(new PropertyValueFactory<>("state"));
|
|
||||||
stateCol.setCellFactory(CheckBoxTableCell.forTableColumn(stateCol));
|
|
||||||
stateCol.setEditable(true);
|
|
||||||
|
|
||||||
// an example of creating index column if data object
|
|
||||||
// doesn't provide index property
|
|
||||||
var indexCol = new TableColumn<Product, String>("Index");
|
|
||||||
indexCol.setCellFactory(col -> {
|
|
||||||
TableCell<Product, String> cell = new TableCell<>();
|
|
||||||
StringBinding value = Bindings.when(cell.emptyProperty())
|
|
||||||
.then("")
|
|
||||||
.otherwise(cell.indexProperty().add(1).asString());
|
|
||||||
cell.textProperty().bind(value);
|
|
||||||
return cell;
|
|
||||||
});
|
|
||||||
|
|
||||||
var iconCol = new TableColumn<Product, Feather>("Logo");
|
|
||||||
iconCol.setCellValueFactory(c -> new SimpleObjectProperty<>(randomIcon()));
|
|
||||||
iconCol.setCellFactory(FontIconTableCell.forTableColumn());
|
|
||||||
iconCol.setEditable(false);
|
|
||||||
|
|
||||||
var brandCol = new TableColumn<Product, String>("Brand 🖉");
|
|
||||||
brandCol.setCellValueFactory(new PropertyValueFactory<>("brand"));
|
|
||||||
brandCol.setCellFactory(ChoiceBoxTableCell.forTableColumn(
|
|
||||||
generate(() -> FAKER.commerce().brand(), 10).toArray(String[]::new)
|
|
||||||
));
|
|
||||||
brandCol.setEditable(true);
|
|
||||||
|
|
||||||
var nameCol = new TableColumn<Product, String>("Name 🖉");
|
|
||||||
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
|
|
||||||
nameCol.setCellFactory(ComboBoxTableCell.forTableColumn(
|
|
||||||
generate(() -> FAKER.commerce().productName(), 10).toArray(String[]::new)
|
|
||||||
));
|
|
||||||
nameCol.setEditable(true);
|
|
||||||
|
|
||||||
var priceCol = new TableColumn<Product, String>("Price 🖉");
|
|
||||||
priceCol.setCellValueFactory(new PropertyValueFactory<>("price"));
|
|
||||||
priceCol.setCellFactory(TextFieldTableCell.forTableColumn());
|
|
||||||
priceCol.setEditable(true);
|
|
||||||
|
|
||||||
var stockCountCol = new TableColumn<Product, Integer>("Count");
|
|
||||||
stockCountCol.setCellValueFactory(new PropertyValueFactory<>("count"));
|
|
||||||
stockCountCol.setEditable(false);
|
|
||||||
|
|
||||||
var stockAvailCol = new TableColumn<Product, Double>("Available");
|
|
||||||
stockAvailCol.setCellValueFactory(new PropertyValueFactory<>("availability"));
|
|
||||||
stockAvailCol.setCellFactory(ProgressBarTableCell.forTableColumn());
|
|
||||||
stockAvailCol.setEditable(false);
|
|
||||||
|
|
||||||
var stockCol = new TableColumn<Product, Double>("Stock");
|
|
||||||
stockCol.getColumns().setAll(stockCountCol, stockAvailCol);
|
|
||||||
|
|
||||||
var tableView = new TableView<Product>();
|
|
||||||
tableView.getColumns().setAll(stateCol, indexCol, iconCol, brandCol, nameCol, priceCol, stockCol);
|
|
||||||
|
|
||||||
return tableView;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MenuButton createTablePropertiesMenu(TableView<Product> table) {
|
|
||||||
final var resizePolicyCaption = new CaptionMenuItem("Resize Policy");
|
|
||||||
final var resizePolicyGroup = new ToggleGroup();
|
|
||||||
resizePolicyGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null && val.getUserData() instanceof Callback<?, ?> policy) {
|
|
||||||
//noinspection rawtypes,unchecked
|
|
||||||
table.setColumnResizePolicy((Callback<TableView.ResizeFeatures, Boolean>) policy);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final var unconstrainedResizeItem = new RadioMenuItem("Unconstrained");
|
|
||||||
unconstrainedResizeItem.setToggleGroup(resizePolicyGroup);
|
|
||||||
unconstrainedResizeItem.setUserData(TableView.UNCONSTRAINED_RESIZE_POLICY);
|
|
||||||
unconstrainedResizeItem.setSelected(true);
|
|
||||||
|
|
||||||
final var constrainedResizeItem = new RadioMenuItem("Constrained");
|
|
||||||
constrainedResizeItem.setToggleGroup(resizePolicyGroup);
|
|
||||||
constrainedResizeItem.setUserData(TableView.CONSTRAINED_RESIZE_POLICY);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
final var selectionModeCaption = new CaptionMenuItem("Selection Mode");
|
|
||||||
final var selectionModeGroup = new ToggleGroup();
|
|
||||||
selectionModeGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null && val.getUserData() instanceof SelectionMode mode) {
|
|
||||||
table.getSelectionModel().setSelectionMode(mode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final var singleSelectionItem = new RadioMenuItem("Single");
|
|
||||||
singleSelectionItem.setToggleGroup(selectionModeGroup);
|
|
||||||
singleSelectionItem.setUserData(SelectionMode.SINGLE);
|
|
||||||
|
|
||||||
final var multiSelectionItem = new RadioMenuItem("Multiple");
|
|
||||||
multiSelectionItem.setToggleGroup(selectionModeGroup);
|
|
||||||
multiSelectionItem.setUserData(SelectionMode.MULTIPLE);
|
|
||||||
multiSelectionItem.setSelected(true);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
final var editCellsItem = new CheckMenuItem("Editable");
|
|
||||||
table.editableProperty().bind(editCellsItem.selectedProperty());
|
|
||||||
editCellsItem.setSelected(true);
|
|
||||||
|
|
||||||
final var cellSelectionItem = new CheckMenuItem("Enable cell selection");
|
|
||||||
table.getSelectionModel().cellSelectionEnabledProperty().bind(cellSelectionItem.selectedProperty());
|
|
||||||
cellSelectionItem.setSelected(false);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
final var menuButtonItem = new CheckMenuItem("Show menu button");
|
|
||||||
table.tableMenuButtonVisibleProperty().bind(menuButtonItem.selectedProperty());
|
|
||||||
menuButtonItem.setSelected(true);
|
|
||||||
|
|
||||||
final var propertiesMenu = new MenuButton("Properties");
|
|
||||||
propertiesMenu.getItems().setAll(
|
|
||||||
resizePolicyCaption,
|
|
||||||
unconstrainedResizeItem,
|
|
||||||
constrainedResizeItem,
|
|
||||||
selectionModeCaption,
|
|
||||||
singleSelectionItem,
|
|
||||||
multiSelectionItem,
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
editCellsItem,
|
|
||||||
cellSelectionItem,
|
|
||||||
menuButtonItem
|
|
||||||
);
|
|
||||||
|
|
||||||
return propertiesMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addStyleClass(TableColumn<?, ?> c, String styleClass, String... excludes) {
|
|
||||||
Objects.requireNonNull(c);
|
|
||||||
Objects.requireNonNull(styleClass);
|
|
||||||
|
|
||||||
if (excludes != null && excludes.length > 0) {
|
|
||||||
c.getStyleClass().removeAll(excludes);
|
|
||||||
}
|
|
||||||
c.getStyleClass().add(styleClass);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,735 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import static javafx.collections.FXCollections.observableArrayList;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.CaptionMenuItem;
|
||||||
|
import atlantafx.base.controls.Spacer;
|
||||||
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.theme.Tweaks;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.fake.domain.Product;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.StringBinding;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.collections.transformation.FilteredList;
|
||||||
|
import javafx.collections.transformation.SortedList;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.CheckBox;
|
||||||
|
import javafx.scene.control.CheckMenuItem;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.MenuButton;
|
||||||
|
import javafx.scene.control.Pagination;
|
||||||
|
import javafx.scene.control.RadioMenuItem;
|
||||||
|
import javafx.scene.control.SelectionMode;
|
||||||
|
import javafx.scene.control.SeparatorMenuItem;
|
||||||
|
import javafx.scene.control.TableCell;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.control.cell.CheckBoxTableCell;
|
||||||
|
import javafx.scene.control.cell.ChoiceBoxTableCell;
|
||||||
|
import javafx.scene.control.cell.ComboBoxTableCell;
|
||||||
|
import javafx.scene.control.cell.ProgressBarTableCell;
|
||||||
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import javafx.scene.control.cell.TextFieldTableCell;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIconTableCell;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public class TableViewPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "TableView";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Book(String author,
|
||||||
|
String title,
|
||||||
|
String genre,
|
||||||
|
String publisher,
|
||||||
|
SimpleBooleanProperty status) {
|
||||||
|
|
||||||
|
public static Book random() {
|
||||||
|
return new Book(
|
||||||
|
FAKER.book().author(),
|
||||||
|
FAKER.book().title(),
|
||||||
|
FAKER.book().genre(),
|
||||||
|
FAKER.book().publisher(),
|
||||||
|
new SimpleBooleanProperty()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObservableList<Book> random(int count) {
|
||||||
|
return IntStream.range(0, count).mapToObj(i -> Book.random())
|
||||||
|
.collect(Collectors.toCollection(FXCollections::observableArrayList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Flight(String arrival,
|
||||||
|
String city,
|
||||||
|
String aircraft,
|
||||||
|
String airline,
|
||||||
|
String flight) {
|
||||||
|
|
||||||
|
public static Flight random() {
|
||||||
|
return new Flight(
|
||||||
|
FAKER.date().past(12, TimeUnit.HOURS, "HH:mm"),
|
||||||
|
FAKER.country().capital(),
|
||||||
|
FAKER.aviation().aircraft().toUpperCase(Locale.ROOT),
|
||||||
|
FAKER.aviation().airline(),
|
||||||
|
FAKER.aviation().flight()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObservableList<Flight> random(int count) {
|
||||||
|
return IntStream.range(0, count).mapToObj(i -> Flight.random())
|
||||||
|
.sorted(Comparator.comparing(Flight::arrival))
|
||||||
|
.collect(Collectors.toCollection(FXCollections::observableArrayList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public TableViewPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
The [i]TableView[/i] control is designed to visualize an unlimited number of rows of data, \
|
||||||
|
broken out into columns."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Row Style", rowStyleExample());
|
||||||
|
addSection("Selection Color", selectionColorExample());
|
||||||
|
addSection("Edge-to-Edge", edge2EdgeExample());
|
||||||
|
addSection("Alignment", alignmentExample());
|
||||||
|
addSection("Editable", editableExample());
|
||||||
|
addSection("Column Grouping", columnGroupingExample());
|
||||||
|
addSection("Pagination", paginationExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var col1 = new TableColumn<Flight, String>("Arrival");
|
||||||
|
col1.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().arrival())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col2 = new TableColumn<Flight, String>("City");
|
||||||
|
col2.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().city())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col3 = new TableColumn<Flight, String>("Airline");
|
||||||
|
col3.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().airline())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col4 = new TableColumn<Flight, String>("Flight");
|
||||||
|
col4.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().flight())
|
||||||
|
);
|
||||||
|
|
||||||
|
var table = new TableView<>(Flight.random(5));
|
||||||
|
table.getColumns().setAll(col1, col2, col3, col4);
|
||||||
|
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
table.getSelectionModel().selectFirst();
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
table.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
table.setMinHeight(300);
|
||||||
|
HBox.setHgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(table);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can create a table view by instantiating the \
|
||||||
|
[font=monospace]javafx.scene.control.TableView[/font] class."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox rowStyleExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var col1 = new TableColumn<Flight, String>("Arrival");
|
||||||
|
col1.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().arrival())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col2 = new TableColumn<Flight, String>("City");
|
||||||
|
col2.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().city())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col3 = new TableColumn<Flight, String>("Airline");
|
||||||
|
col3.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().airline())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col4 = new TableColumn<Flight, String>("Flight");
|
||||||
|
col4.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().flight())
|
||||||
|
);
|
||||||
|
|
||||||
|
var table = new TableView<>(Flight.random(5));
|
||||||
|
table.getColumns().setAll(col1, col2, col3, col4);
|
||||||
|
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
table.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var borderToggle = new ToggleSwitch("Bordered");
|
||||||
|
borderToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(table, Styles.BORDERED)
|
||||||
|
);
|
||||||
|
|
||||||
|
var stripeToggle = new ToggleSwitch("Striped");
|
||||||
|
stripeToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(table, Styles.STRIPED)
|
||||||
|
);
|
||||||
|
|
||||||
|
var denseToggle = new ToggleSwitch("Dense");
|
||||||
|
denseToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(table, Styles.DENSE)
|
||||||
|
);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
table.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
table.setMinHeight(300);
|
||||||
|
HBox.setHgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var togglesBox = new HBox(HGAP_20, borderToggle, stripeToggle, denseToggle);
|
||||||
|
togglesBox.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_10, table, togglesBox);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]TableView[/i] rows can be styled simply by adding CSS classes:
|
||||||
|
|
||||||
|
[ul]
|
||||||
|
[li][code]Styles.BORDERED[/code] - adds borders between the columns.[/li]
|
||||||
|
[li][code]Styles.STRIPED[/code] - adds zebra-striping.[/li]
|
||||||
|
[li][code]Styles.DENSE[/code] - makes the table more compact by cutting cell padding.[/li][/ul]"""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox selectionColorExample() {
|
||||||
|
var style = """
|
||||||
|
-color-cell-bg-selected: -color-accent-emphasis;
|
||||||
|
-color-cell-fg-selected: -color-fg-emphasis;
|
||||||
|
-color-cell-bg-selected-focused: -color-accent-emphasis;
|
||||||
|
-color-cell-fg-selected-focused: -color-fg-emphasis;""";
|
||||||
|
|
||||||
|
//snippet_3:start
|
||||||
|
var col1 = new TableColumn<Flight, String>("Arrival");
|
||||||
|
col1.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().arrival())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col2 = new TableColumn<Flight, String>("City");
|
||||||
|
col2.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().city())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col3 = new TableColumn<Flight, String>("Airline");
|
||||||
|
col3.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().airline())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col4 = new TableColumn<Flight, String>("Flight");
|
||||||
|
col4.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().flight())
|
||||||
|
);
|
||||||
|
|
||||||
|
var table = new TableView<>(Flight.random(5));
|
||||||
|
table.getColumns().setAll(col1, col2, col3, col4);
|
||||||
|
// -color-cell-bg-selected: -color-accent-emphasis;
|
||||||
|
// -color-cell-fg-selected: -color-fg-emphasis;
|
||||||
|
// -color-cell-bg-selected-focused: -color-accent-emphasis;
|
||||||
|
// -color-cell-fg-selected-focused: -color-fg-emphasis;
|
||||||
|
table.setStyle(style);
|
||||||
|
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
table.getSelectionModel().selectFirst();
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
table.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
table.setMinHeight(250);
|
||||||
|
HBox.setHgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(table);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Cell selection color (and more) can be changed via looked-up color variables. \
|
||||||
|
You can find all supported color variables in the docs."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox edge2EdgeExample() {
|
||||||
|
//snippet_5:start
|
||||||
|
var col1 = new TableColumn<Flight, String>("Arrival");
|
||||||
|
col1.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().arrival())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col2 = new TableColumn<Flight, String>("City");
|
||||||
|
col2.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().city())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col3 = new TableColumn<Flight, String>("Airline");
|
||||||
|
col3.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().airline())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col4 = new TableColumn<Flight, String>("Flight");
|
||||||
|
col4.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().flight())
|
||||||
|
);
|
||||||
|
|
||||||
|
var table = new TableView<>(Flight.random(5));
|
||||||
|
table.getColumns().setAll(col1, col2, col3, col4);
|
||||||
|
table.getStyleClass().add(Tweaks.EDGE_TO_EDGE);
|
||||||
|
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
table.getSelectionModel().selectFirst();
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
|
table.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
table.setMinHeight(250);
|
||||||
|
HBox.setHgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(table);
|
||||||
|
box.setStyle("""
|
||||||
|
-fx-border-color: -color-accent-emphasis;
|
||||||
|
-fx-border-width: 2px;"""
|
||||||
|
);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Use [code]Tweaks.EDGE_TO_EDGE[/code] style class to remove the [i]TableView[/i] outer borders. \
|
||||||
|
This is useful if you want to place the table into external container that already has its \
|
||||||
|
own borders."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 5), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox alignmentExample() {
|
||||||
|
//snippet_6:start
|
||||||
|
var col1 = new TableColumn<Flight, String>("Arrival");
|
||||||
|
col1.getStyleClass().add(Tweaks.ALIGN_CENTER);
|
||||||
|
col1.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().arrival())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col2 = new TableColumn<Flight, String>("City");
|
||||||
|
col2.getStyleClass().add(Tweaks.ALIGN_RIGHT);
|
||||||
|
col2.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().city())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col3 = new TableColumn<Flight, String>("Airline");
|
||||||
|
col3.getStyleClass().add(Tweaks.ALIGN_RIGHT);
|
||||||
|
col3.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().airline())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col4 = new TableColumn<Flight, String>("Flight");
|
||||||
|
col4.getStyleClass().add(Tweaks.ALIGN_CENTER);
|
||||||
|
col4.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().flight())
|
||||||
|
);
|
||||||
|
|
||||||
|
var table = new TableView<>(Flight.random(5));
|
||||||
|
table.getColumns().setAll(col1, col2, col3, col4);
|
||||||
|
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
table.getSelectionModel().selectFirst();
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
|
table.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
table.setMinHeight(300);
|
||||||
|
HBox.setHgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(table);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Column content can be aligned by adding one of the following style class modifiers:
|
||||||
|
|
||||||
|
[ul]
|
||||||
|
[li][code]Tweaks.ALIGN_LEFT[/code] (default)[/li]
|
||||||
|
[li][code]Tweaks.ALIGN_CENTER[/code][/li]
|
||||||
|
[li][code]Tweaks.ALIGN_RIGHT[/code][/li][/ul]"""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 6), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox editableExample() {
|
||||||
|
//snippet_7:start
|
||||||
|
var selectAll = new CheckBox();
|
||||||
|
|
||||||
|
var col0 = new TableColumn<Book, Boolean>();
|
||||||
|
col0.setGraphic(selectAll);
|
||||||
|
col0.setSortable(false);
|
||||||
|
col0.setCellValueFactory(c -> c.getValue().status());
|
||||||
|
col0.setCellFactory(CheckBoxTableCell.forTableColumn(col0));
|
||||||
|
col0.setEditable(true);
|
||||||
|
|
||||||
|
var col1 = new TableColumn<Book, String>("Author");
|
||||||
|
col1.setCellFactory(TextFieldTableCell.forTableColumn());
|
||||||
|
col1.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().author())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col2 = new TableColumn<Book, String>("Title");
|
||||||
|
col2.setCellFactory(TextFieldTableCell.forTableColumn());
|
||||||
|
col2.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().title())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col3 = new TableColumn<Book, String>("Genre");
|
||||||
|
col3.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().genre())
|
||||||
|
);
|
||||||
|
col3.setCellFactory(ChoiceBoxTableCell.forTableColumn(
|
||||||
|
generate(() -> FAKER.book().genre(), 10).toArray(String[]::new)
|
||||||
|
));
|
||||||
|
|
||||||
|
var col4 = new TableColumn<Book, String>("Publisher");
|
||||||
|
col4.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().publisher())
|
||||||
|
);
|
||||||
|
col4.setCellFactory(ComboBoxTableCell.forTableColumn(
|
||||||
|
generate(() -> FAKER.book().publisher(), 10).toArray(String[]::new)
|
||||||
|
));
|
||||||
|
|
||||||
|
var table = new TableView<>(Book.random(5));
|
||||||
|
table.getColumns().setAll(col0, col1, col2, col3, col4);
|
||||||
|
table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
|
||||||
|
table.getSelectionModel().selectFirst();
|
||||||
|
selectAll.setOnAction(evt -> {
|
||||||
|
table.getItems().forEach(
|
||||||
|
item -> item.status().set(selectAll.isSelected())
|
||||||
|
);
|
||||||
|
evt.consume();
|
||||||
|
});
|
||||||
|
//snippet_7:end
|
||||||
|
|
||||||
|
table.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
table.setMinHeight(300);
|
||||||
|
table.setEditable(true);
|
||||||
|
HBox.setHgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(table);
|
||||||
|
var description = BBCodeParser.createFormattedText(
|
||||||
|
"the [i]TableView[/i] cells can be made editable by setting an appropriate cell factory."
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 7), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox columnGroupingExample() {
|
||||||
|
//snippet_8:start
|
||||||
|
var col11 = new TableColumn<Flight, String>("Time");
|
||||||
|
col11.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().arrival())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col12 = new TableColumn<Flight, String>("City");
|
||||||
|
col12.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().city())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col1 = new TableColumn<Flight, String>("Arrival");
|
||||||
|
col1.getColumns().setAll(col11, col12);
|
||||||
|
|
||||||
|
var col21 = new TableColumn<Flight, String>("Airline");
|
||||||
|
col21.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().airline())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col22 = new TableColumn<Flight, String>("Aircraft");
|
||||||
|
col22.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().aircraft())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col2 = new TableColumn<Flight, String>("Jet");
|
||||||
|
col2.getColumns().setAll(col21, col22);
|
||||||
|
|
||||||
|
var col3 = new TableColumn<Flight, String>("Flight");
|
||||||
|
col3.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().flight())
|
||||||
|
);
|
||||||
|
|
||||||
|
var table = new TableView<>(Flight.random(5));
|
||||||
|
table.getColumns().setAll(col1, col2, col3);
|
||||||
|
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
table.getSelectionModel().selectFirst();
|
||||||
|
//snippet_8:end
|
||||||
|
|
||||||
|
table.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
table.setMinHeight(300);
|
||||||
|
HBox.setHgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(table);
|
||||||
|
var description = BBCodeParser.createFormattedText(
|
||||||
|
"Any table column can contain nested columns which allows to group common data properties."
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 8), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox paginationExample() {
|
||||||
|
//snippet_9:start
|
||||||
|
var col1 = new TableColumn<Flight, String>("Arrival");
|
||||||
|
col1.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().arrival())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col2 = new TableColumn<Flight, String>("City");
|
||||||
|
col2.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().city())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col3 = new TableColumn<Flight, String>("Airline");
|
||||||
|
col3.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().airline())
|
||||||
|
);
|
||||||
|
|
||||||
|
var col4 = new TableColumn<Flight, String>("Flight");
|
||||||
|
col4.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().flight())
|
||||||
|
);
|
||||||
|
|
||||||
|
var table = new TableView<>(Flight.random(50));
|
||||||
|
table.getColumns().setAll(col1, col2, col3, col4);
|
||||||
|
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
table.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var pg = new Pagination(25, 0);
|
||||||
|
pg.setMaxPageIndicatorCount(5);
|
||||||
|
pg.setPageFactory(pageNum -> {
|
||||||
|
table.getItems().setAll(Flight.random(50));
|
||||||
|
return new StackPane(); // null isn't allowed
|
||||||
|
});
|
||||||
|
//snippet_9:end
|
||||||
|
|
||||||
|
table.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
table.setMinHeight(300);
|
||||||
|
HBox.setHgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new VBox(table, pg);
|
||||||
|
var description = BBCodeParser.createFormattedText(
|
||||||
|
"Pagination can be used to split up large amounts of data into smaller chunks."
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 9), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private TableView<Product> table;
|
||||||
|
private final List<Product> dataList = IntStream.range(1, 51).boxed()
|
||||||
|
.map(i -> atlantafx.sampler.fake.domain.Product.random(i, FAKER))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
private VBox playground() {
|
||||||
|
var maxRowCount = 200;
|
||||||
|
var rowCountChoice = new ComboBox<>(
|
||||||
|
observableArrayList(0, 1, 5, 10, 25, maxRowCount)
|
||||||
|
);
|
||||||
|
rowCountChoice.setValue(5);
|
||||||
|
|
||||||
|
var rowCountBox = new HBox(HGAP_20, new Label("rows"), rowCountChoice);
|
||||||
|
rowCountBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
// == FOOTER ==
|
||||||
|
|
||||||
|
var bordersToggle = new ToggleSwitch("Bordered");
|
||||||
|
bordersToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(table, Styles.BORDERED)
|
||||||
|
);
|
||||||
|
|
||||||
|
var denseToggle = new ToggleSwitch("Dense");
|
||||||
|
denseToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(table, Styles.DENSE)
|
||||||
|
);
|
||||||
|
|
||||||
|
var stripesToggle = new ToggleSwitch("Striped");
|
||||||
|
stripesToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> Styles.toggleStyleClass(table, Styles.STRIPED)
|
||||||
|
);
|
||||||
|
|
||||||
|
var edge2edgeToggle = new ToggleSwitch("Edge to edge");
|
||||||
|
edge2edgeToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, value) -> Styles.toggleStyleClass(table, Tweaks.EDGE_TO_EDGE)
|
||||||
|
);
|
||||||
|
|
||||||
|
var footer = new HBox(HGAP_20, bordersToggle, denseToggle, stripesToggle, edge2edgeToggle);
|
||||||
|
footer.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
// == TABLE ==
|
||||||
|
|
||||||
|
var filteredData = new FilteredList<>(observableArrayList(dataList));
|
||||||
|
filteredData.predicateProperty().bind(Bindings.createObjectBinding(
|
||||||
|
() -> product -> product.getId() <= rowCountChoice.getValue(),
|
||||||
|
rowCountChoice.valueProperty()
|
||||||
|
));
|
||||||
|
|
||||||
|
var sortedData = new SortedList<>(filteredData);
|
||||||
|
|
||||||
|
table = createTable();
|
||||||
|
table.setItems(sortedData);
|
||||||
|
table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
|
||||||
|
sortedData.comparatorProperty().bind(table.comparatorProperty());
|
||||||
|
VBox.setVgrow(table, Priority.ALWAYS);
|
||||||
|
|
||||||
|
// == HEADER ==
|
||||||
|
|
||||||
|
var header = new HBox(createTablePropertiesMenu(table), new Spacer(), rowCountBox);
|
||||||
|
header.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The playground demonstrates the most important [i]TableView[/i] features \
|
||||||
|
and also serves as an object for monkey testing."""
|
||||||
|
);
|
||||||
|
|
||||||
|
var playground = new VBox(VGAP_10, description, header, table, footer);
|
||||||
|
playground.setMinHeight(500);
|
||||||
|
|
||||||
|
return playground;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private TableView<Product> createTable() {
|
||||||
|
// an example of creating index column if data object
|
||||||
|
// doesn't provide index property
|
||||||
|
var indexCol = new TableColumn<Product, String>("Index");
|
||||||
|
indexCol.setCellFactory(col -> {
|
||||||
|
var cell = new TableCell<Product, String>();
|
||||||
|
StringBinding value = Bindings.when(cell.emptyProperty())
|
||||||
|
.then("")
|
||||||
|
.otherwise(cell.indexProperty().add(1).asString());
|
||||||
|
cell.textProperty().bind(value);
|
||||||
|
return cell;
|
||||||
|
});
|
||||||
|
|
||||||
|
var iconCol = new TableColumn<Product, Feather>("Logo");
|
||||||
|
iconCol.setCellValueFactory(c -> new SimpleObjectProperty<>(randomIcon()));
|
||||||
|
iconCol.setCellFactory(FontIconTableCell.forTableColumn());
|
||||||
|
|
||||||
|
var brandCol = new TableColumn<Product, String>("Brand");
|
||||||
|
brandCol.setCellValueFactory(new PropertyValueFactory<>("brand"));
|
||||||
|
|
||||||
|
var nameCol = new TableColumn<Product, String>("Name");
|
||||||
|
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
|
||||||
|
|
||||||
|
var priceCol = new TableColumn<Product, String>("Price");
|
||||||
|
priceCol.setCellValueFactory(new PropertyValueFactory<>("price"));
|
||||||
|
|
||||||
|
var stockCountCol = new TableColumn<Product, Integer>("Count");
|
||||||
|
stockCountCol.setCellValueFactory(new PropertyValueFactory<>("count"));
|
||||||
|
|
||||||
|
var stockAvailCol = new TableColumn<Product, Double>("Available");
|
||||||
|
stockAvailCol.setCellValueFactory(new PropertyValueFactory<>("availability"));
|
||||||
|
stockAvailCol.setCellFactory(ProgressBarTableCell.forTableColumn());
|
||||||
|
|
||||||
|
var stockCol = new TableColumn<Product, Double>("Stock");
|
||||||
|
stockCol.getColumns().setAll(stockCountCol, stockAvailCol);
|
||||||
|
|
||||||
|
var tableView = new TableView<Product>();
|
||||||
|
tableView.getColumns().setAll(indexCol, iconCol, brandCol, nameCol, priceCol, stockCol);
|
||||||
|
|
||||||
|
return tableView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuButton createTablePropertiesMenu(TableView<Product> table) {
|
||||||
|
final var resizePolCaption = new CaptionMenuItem("Resize Policy");
|
||||||
|
final var resizePolGroup = new ToggleGroup();
|
||||||
|
resizePolGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val != null && val.getUserData() instanceof Callback<?, ?> policy) {
|
||||||
|
//noinspection rawtypes,unchecked
|
||||||
|
table.setColumnResizePolicy((Callback<TableView.ResizeFeatures, Boolean>) policy);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final var unconResizeItem = new RadioMenuItem("Unconstrained");
|
||||||
|
unconResizeItem.setToggleGroup(resizePolGroup);
|
||||||
|
unconResizeItem.setUserData(TableView.UNCONSTRAINED_RESIZE_POLICY);
|
||||||
|
unconResizeItem.setSelected(true);
|
||||||
|
|
||||||
|
final var conResizeItem = new RadioMenuItem("Constrained");
|
||||||
|
conResizeItem.setToggleGroup(resizePolGroup);
|
||||||
|
conResizeItem.setUserData(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
final var selModeCaption = new CaptionMenuItem("Selection Mode");
|
||||||
|
final var selModeGroup = new ToggleGroup();
|
||||||
|
selModeGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val != null && val.getUserData() instanceof SelectionMode mode) {
|
||||||
|
table.getSelectionModel().setSelectionMode(mode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final var singleSelItem = new RadioMenuItem("Single");
|
||||||
|
singleSelItem.setToggleGroup(selModeGroup);
|
||||||
|
singleSelItem.setUserData(SelectionMode.SINGLE);
|
||||||
|
|
||||||
|
final var multiSelItem = new RadioMenuItem("Multiple");
|
||||||
|
multiSelItem.setToggleGroup(selModeGroup);
|
||||||
|
multiSelItem.setUserData(SelectionMode.MULTIPLE);
|
||||||
|
multiSelItem.setSelected(true);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
final var cellSelItem = new CheckMenuItem("Enable cell selection");
|
||||||
|
table.getSelectionModel().cellSelectionEnabledProperty().bind(cellSelItem.selectedProperty());
|
||||||
|
cellSelItem.setSelected(false);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
final var menuBtnItem = new CheckMenuItem("Show menu button");
|
||||||
|
table.tableMenuButtonVisibleProperty().bind(menuBtnItem.selectedProperty());
|
||||||
|
menuBtnItem.setSelected(true);
|
||||||
|
|
||||||
|
final var propsMenu = new MenuButton("Properties");
|
||||||
|
propsMenu.getItems().setAll(
|
||||||
|
resizePolCaption,
|
||||||
|
unconResizeItem,
|
||||||
|
conResizeItem,
|
||||||
|
selModeCaption,
|
||||||
|
singleSelItem,
|
||||||
|
multiSelItem,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
cellSelItem,
|
||||||
|
menuBtnItem
|
||||||
|
);
|
||||||
|
|
||||||
|
return propsMenu;
|
||||||
|
}
|
||||||
|
}
|
@ -2,21 +2,19 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.STATE_DANGER;
|
import atlantafx.base.theme.Styles;
|
||||||
import static atlantafx.base.theme.Styles.STATE_SUCCESS;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javafx.scene.control.TextArea;
|
import javafx.scene.control.TextArea;
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
public class TextAreaPage extends AbstractPage {
|
public class TextAreaPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "TextArea";
|
public static final String NAME = "TextArea";
|
||||||
private static final double CONTROL_WIDTH = 200;
|
|
||||||
private static final double CONTROL_HEIGHT = 120;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -25,68 +23,109 @@ public class TextAreaPage extends AbstractPage {
|
|||||||
|
|
||||||
public TextAreaPage() {
|
public TextAreaPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new FlowPane(
|
|
||||||
PAGE_HGAP, PAGE_VGAP,
|
|
||||||
basicSample(),
|
|
||||||
promptSample(),
|
|
||||||
scrollSample(),
|
|
||||||
readonlySample(),
|
|
||||||
successSample(),
|
|
||||||
dangerSample(),
|
|
||||||
disabledSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
addFormattedText(
|
||||||
var textArea = createTextArea("Text");
|
"Text input component that allows a user to enter multiple lines of plain text."
|
||||||
textArea.setWrapText(true);
|
|
||||||
return new SampleBlock("Basic", textArea);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock promptSample() {
|
|
||||||
var textArea = createTextArea(null);
|
|
||||||
textArea.setPromptText("Prompt text");
|
|
||||||
return new SampleBlock("Prompt", textArea);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock scrollSample() {
|
|
||||||
var textArea = createTextArea(
|
|
||||||
Stream.generate(() -> FAKER.lorem().paragraph()).limit(10).collect(Collectors.joining("\n"))
|
|
||||||
);
|
);
|
||||||
textArea.setWrapText(false);
|
addSection("Usage", usageExample());
|
||||||
return new SampleBlock("Scrolling", textArea);
|
addSection("Prompt Text", promptTextExample());
|
||||||
|
addSection("Readonly", readonlyExample());
|
||||||
|
addSection("Color", colorExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock readonlySample() {
|
private ExampleBox usageExample() {
|
||||||
var textArea = createTextArea("Text");
|
//snippet_1:start
|
||||||
textArea.setEditable(false);
|
var ta1 = new TextArea("Text");
|
||||||
return new SampleBlock("Readonly", textArea);
|
|
||||||
|
var ta2 = new TextArea(
|
||||||
|
Stream.generate(() -> FAKER.lorem().paragraph())
|
||||||
|
.limit(10)
|
||||||
|
.collect(Collectors.joining("\n"))
|
||||||
|
);
|
||||||
|
ta2.setWrapText(true);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
ta1.setMinSize(300, 120);
|
||||||
|
ta1.setMaxSize(300, 120);
|
||||||
|
|
||||||
|
ta2.setMinSize(300, 120);
|
||||||
|
ta2.setMaxSize(300, 120);
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, ta1, ta2);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You create a text area control by creating an instance of the \
|
||||||
|
[font=monospace]javafx.scene.control.TextArea[/font] class. \
|
||||||
|
By default long text won't be wrapped. You should set [code]setWrapText(true)[/code] \
|
||||||
|
to use this feature."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock successSample() {
|
private ExampleBox promptTextExample() {
|
||||||
var textArea = createTextArea("Text");
|
//snippet_2:start
|
||||||
textArea.pseudoClassStateChanged(STATE_SUCCESS, true);
|
var ta = new TextArea();
|
||||||
return new SampleBlock("Success", textArea);
|
ta.setPromptText("Prompt text");
|
||||||
|
ta.setWrapText(true);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
ta.setMaxSize(300, 120);
|
||||||
|
ta.setMinSize(300, 120);
|
||||||
|
|
||||||
|
var box = new HBox(ta);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]TextArea[/i] supports the notion of showing prompt text to the user when there \
|
||||||
|
is no text already in the text area (either via the user, or set programmatically). \
|
||||||
|
This is a useful way of informing the user as to what is expected in the [i]TextArea[/i], \
|
||||||
|
without having to resort to tooltips or on-screen labels."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock dangerSample() {
|
private ExampleBox readonlyExample() {
|
||||||
var textArea = createTextArea("Text");
|
//snippet_3:start
|
||||||
textArea.pseudoClassStateChanged(STATE_DANGER, true);
|
var ta = new TextArea("This text can't be modified");
|
||||||
return new SampleBlock("Danger", textArea);
|
ta.setWrapText(true);
|
||||||
|
ta.setEditable(false);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
ta.setMaxSize(300, 120);
|
||||||
|
ta.setMinSize(300, 120);
|
||||||
|
|
||||||
|
var box = new HBox(ta);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]TextArea[/i]'s [code]editable[/code] property indicates whether it \
|
||||||
|
can be edited by the user."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
private ExampleBox colorExample() {
|
||||||
var textArea = createTextArea("Text");
|
//snippet_4:start
|
||||||
textArea.setDisable(true);
|
var ta1 = new TextArea("Text");
|
||||||
return new SampleBlock("Disabled", textArea);
|
ta1.pseudoClassStateChanged(Styles.STATE_SUCCESS, true);
|
||||||
}
|
ta1.setWrapText(true);
|
||||||
|
|
||||||
private TextArea createTextArea(String text) {
|
var ta2 = new TextArea("Text");
|
||||||
var textArea = new TextArea(text);
|
ta2.pseudoClassStateChanged(Styles.STATE_DANGER, true);
|
||||||
textArea.setMinWidth(CONTROL_WIDTH);
|
ta2.setWrapText(true);
|
||||||
textArea.setMinHeight(CONTROL_HEIGHT);
|
//snippet_4:end
|
||||||
textArea.setMaxWidth(CONTROL_WIDTH);
|
|
||||||
textArea.setMaxHeight(CONTROL_HEIGHT);
|
ta1.setMaxSize(300, 120);
|
||||||
return textArea;
|
ta1.setMinSize(300, 120);
|
||||||
|
|
||||||
|
ta2.setMaxSize(300, 120);
|
||||||
|
ta2.setMinSize(300, 120);
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, ta1, ta2);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can use [code]Styles.STATE_SUCCESS[/code] or [code]Styles.STATE_DANGER[/code] \
|
||||||
|
pseudo-classes to change the [i]TextArea[/i] color. This especially useful to indicate \
|
||||||
|
the validation result."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,22 +2,17 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.LARGE;
|
import atlantafx.base.theme.Styles;
|
||||||
import static atlantafx.base.theme.Styles.ROUNDED;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import static atlantafx.base.theme.Styles.SMALL;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import static atlantafx.base.theme.Styles.STATE_DANGER;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import static atlantafx.base.theme.Styles.STATE_SUCCESS;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.PasswordField;
|
import javafx.scene.control.PasswordField;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
|
|
||||||
public class TextFieldPage extends AbstractPage {
|
public class TextFieldPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "TextField";
|
public static final String NAME = "TextField";
|
||||||
|
|
||||||
@ -28,81 +23,165 @@ public class TextFieldPage extends AbstractPage {
|
|||||||
|
|
||||||
public TextFieldPage() {
|
public TextFieldPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new VBox(
|
|
||||||
PAGE_VGAP,
|
addFormattedText("""
|
||||||
expandingHBox(basicSample(), promptSample(), passwordSample()),
|
Text input component that allows a user to enter a single line of unformatted text."""
|
||||||
expandingHBox(readonlySample(), successSample(), dangerSample()),
|
);
|
||||||
expandingHBox(sizeSample(), roundedSample()),
|
addSection("Usage", usageExample());
|
||||||
disabledSample()
|
addSection("Prompt Text", promptTextExample());
|
||||||
));
|
addSection("Readonly", readonlyExample());
|
||||||
|
addSection("Color", colorExample());
|
||||||
|
addSection("Password Field", passwordFieldExample());
|
||||||
|
addSection("Size", sizeExample());
|
||||||
|
addSection("Rounded", roundedExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
private ExampleBox usageExample() {
|
||||||
var field = new TextField("Text");
|
//snippet_1:start
|
||||||
return new SampleBlock("Basic", field);
|
var tf1 = new TextField("Text");
|
||||||
|
var tf2 = new TextField(FAKER.lorem().sentence(20));
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
tf1.setPrefWidth(200);
|
||||||
|
tf2.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, tf1, tf2);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You create a text field control by creating an instance of the \
|
||||||
|
[font=monospace]javafx.scene.control.TextField[/font] class."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock passwordSample() {
|
private ExampleBox promptTextExample() {
|
||||||
var field = new PasswordField();
|
//snippet_2:start
|
||||||
field.setText("qwerty");
|
var tf = new TextField();
|
||||||
return new SampleBlock("Password", field);
|
tf.setPromptText("Prompt text");
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
tf.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(tf);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]TextField[/i] supports the notion of showing prompt text to the user when there \
|
||||||
|
is no text already in the text field (either via the user, or set programmatically). \
|
||||||
|
This is a useful way of informing the user as to what is expected in the text field, \
|
||||||
|
without having to resort to tooltips or on-screen labels."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock promptSample() {
|
private ExampleBox readonlyExample() {
|
||||||
var field = new TextField();
|
//snippet_3:start
|
||||||
field.setPromptText("Prompt text");
|
var tf = new TextField("This text can't be modified");
|
||||||
return new SampleBlock("Prompt", field);
|
tf.setEditable(false);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
tf.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(tf);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
[i]TextField[/i]'s [code]editable[/code] property indicates whether it can be edited by the user."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock readonlySample() {
|
private ExampleBox colorExample() {
|
||||||
var field = new TextField("Text");
|
//snippet_4:start
|
||||||
field.setEditable(false);
|
var tf1 = new TextField("Text");
|
||||||
return new SampleBlock("Readonly", field);
|
tf1.pseudoClassStateChanged(Styles.STATE_SUCCESS, true);
|
||||||
|
|
||||||
|
var tf2 = new TextField("Text");
|
||||||
|
tf2.pseudoClassStateChanged(Styles.STATE_DANGER, true);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
tf1.setPrefWidth(200);
|
||||||
|
tf2.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, tf1, tf2);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can use [code]Styles.STATE_SUCCESS[/code] or [code]Styles.STATE_DANGER[/code] \
|
||||||
|
pseudo-classes to change the [i]TextField[/i] color. This especially useful to indicate \
|
||||||
|
the validation result."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock successSample() {
|
private ExampleBox passwordFieldExample() {
|
||||||
var field = new TextField("Text");
|
//snippet_5:start
|
||||||
field.pseudoClassStateChanged(STATE_SUCCESS, true);
|
var tf = new PasswordField();
|
||||||
return new SampleBlock("Success", field);
|
tf.setText("qwerty");
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
|
tf.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(tf);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]TextField[/i] flavor that masks entered characters. Note that password can \
|
||||||
|
not be revealed. If you need this particular feature try [code]PasswordTextField[/code]. \
|
||||||
|
It's the [code]CustomTextField[/code] flavor that does the same thing."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 5), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock dangerSample() {
|
private ExampleBox sizeExample() {
|
||||||
var field = new TextField("Text");
|
//snippet_6:start
|
||||||
field.pseudoClassStateChanged(STATE_DANGER, true);
|
|
||||||
return new SampleBlock("Danger", field);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock sizeSample() {
|
|
||||||
var smallField = new TextField("Small");
|
var smallField = new TextField("Small");
|
||||||
smallField.getStyleClass().add(SMALL);
|
smallField.getStyleClass().add(Styles.SMALL);
|
||||||
smallField.setPrefWidth(70);
|
|
||||||
|
|
||||||
var normalField = new TextField("Normal");
|
var normalField = new TextField("Normal");
|
||||||
normalField.setPrefWidth(120);
|
|
||||||
|
|
||||||
var largeField = new TextField("Large");
|
var largeField = new TextField("Large");
|
||||||
largeField.getStyleClass().add(LARGE);
|
largeField.getStyleClass().add(Styles.LARGE);
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
|
smallField.setPrefWidth(70);
|
||||||
|
normalField.setPrefWidth(120);
|
||||||
largeField.setPrefWidth(200);
|
largeField.setPrefWidth(200);
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, smallField, normalField, largeField);
|
var box = new HBox(HGAP_20, smallField, normalField, largeField);
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
return new SampleBlock("Size", content);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Use [code]Styles.SMALL[/code] and [code]Styles.LARGE[/code] style classes \
|
||||||
|
to change the [i]TextField[/i] size."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 6), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock roundedSample() {
|
private ExampleBox roundedExample() {
|
||||||
var field = new TextField("Text");
|
//snippet_7:start
|
||||||
field.getStyleClass().add(ROUNDED);
|
var smallField = new TextField("Small");
|
||||||
return new SampleBlock("Rounded", field);
|
smallField.getStyleClass().addAll(
|
||||||
}
|
Styles.SMALL, Styles.ROUNDED
|
||||||
|
);
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
var normalField = new TextField("Normal");
|
||||||
var field = new TextField("Text");
|
normalField.getStyleClass().addAll(Styles.ROUNDED);
|
||||||
field.setDisable(true);
|
|
||||||
|
|
||||||
var block = new SampleBlock("Disabled", field);
|
var largeField = new TextField("Large");
|
||||||
block.setMaxWidth(250);
|
largeField.getStyleClass().addAll(
|
||||||
|
Styles.LARGE, Styles.ROUNDED
|
||||||
|
);
|
||||||
|
//snippet_7:end
|
||||||
|
|
||||||
return block;
|
smallField.setPrefWidth(70);
|
||||||
|
normalField.setPrefWidth(120);
|
||||||
|
largeField.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, smallField, normalField, largeField);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Use [code]Styles.ROUNDED[/code] style class to round the [i]TextField[/i] corners."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 7), description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,26 +2,23 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.DENSE;
|
|
||||||
import static atlantafx.base.theme.Styles.ELEVATED_2;
|
|
||||||
import static atlantafx.base.theme.Styles.INTERACTIVE;
|
|
||||||
import static atlantafx.base.theme.Styles.toggleStyleClass;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
import static javafx.geometry.HPos.RIGHT;
|
import static javafx.geometry.HPos.RIGHT;
|
||||||
import static javafx.scene.layout.Priority.NEVER;
|
import static javafx.scene.layout.Priority.NEVER;
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.base.theme.Tweaks;
|
import atlantafx.base.theme.Tweaks;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.CheckBox;
|
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Slider;
|
import javafx.scene.control.Slider;
|
||||||
import javafx.scene.control.TitledPane;
|
import javafx.scene.control.TitledPane;
|
||||||
import javafx.scene.layout.ColumnConstraints;
|
import javafx.scene.layout.ColumnConstraints;
|
||||||
|
import javafx.scene.layout.FlowPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
@ -30,10 +27,9 @@ import javafx.scene.layout.VBox;
|
|||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.scene.text.TextFlow;
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
public class TitledPanePage extends AbstractPage {
|
public class TitledPanePage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "TitledPane";
|
public static final String NAME = "TitledPane";
|
||||||
private static final String ELEVATED_PREFIX = "elevated-";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -42,36 +38,160 @@ public class TitledPanePage extends AbstractPage {
|
|||||||
|
|
||||||
public TitledPanePage() {
|
public TitledPanePage() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
addFormattedText("""
|
||||||
var samples = new HBox(
|
[i]TitledPane[/i] is a panel with a title that can be opened and closed. \
|
||||||
PAGE_HGAP,
|
It holds one or more user interface elements and you can expand and collapse it. \
|
||||||
interactivePane(),
|
Some of the [i]TitledPane[/i] can be applied to the [i]Accordion[/i] as well."""
|
||||||
disabledPane(),
|
|
||||||
untitledPane()
|
|
||||||
);
|
);
|
||||||
samples.getChildren().forEach(c -> ((TitledPane) c).setPrefSize(500, 120));
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Elevation", elevationExample());
|
||||||
|
addSection("Dense", denseExample());
|
||||||
|
addSection("Alternative Icon", altIconExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
|
|
||||||
setUserContent(new VBox(
|
var dummyBox = new HBox();
|
||||||
Page.PAGE_VGAP,
|
dummyBox.setMinHeight(10);
|
||||||
createPlayground(),
|
addNode(dummyBox);
|
||||||
samples
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TitledPane createPlayground() {
|
private ExampleBox usageExample() {
|
||||||
var playground = new TitledPane();
|
//snippet_1:start
|
||||||
playground.setText("_Playground");
|
var tp1 = new TitledPane(
|
||||||
playground.setMnemonicParsing(true);
|
"Header",
|
||||||
playground.getStyleClass().add(ELEVATED_2);
|
new Label("Content")
|
||||||
|
);
|
||||||
|
|
||||||
var textFlow = new TextFlow(new Text(FAKER.lorem().paragraph(10)));
|
var tp2 = new TitledPane("Header", new Label("Content"));
|
||||||
|
tp2.setCollapsible(false);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
tp1.setPrefWidth(200);
|
||||||
|
tp2.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, tp1, tp2);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The panel in a [i]TitledPane[/i] can be any node such as controls or groups of nodes \
|
||||||
|
added to a layout container. Note that whilst [i]TitledPane[/i] extends from [i]Labeled[/i], \
|
||||||
|
the inherited properties are used to manipulate the [i]TitledPane[/i] header, not the \
|
||||||
|
content area itself."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox elevationExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var tp0 = new TitledPane(
|
||||||
|
"Default",
|
||||||
|
new Label("Content")
|
||||||
|
);
|
||||||
|
|
||||||
|
var tp1 = new TitledPane(
|
||||||
|
"Styles.ELEVATED_1",
|
||||||
|
new Label("Content")
|
||||||
|
);
|
||||||
|
tp1.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
|
||||||
|
var tp2 = new TitledPane(
|
||||||
|
"Styles.ELEVATED_2",
|
||||||
|
new Label("Content")
|
||||||
|
);
|
||||||
|
tp2.getStyleClass().add(Styles.ELEVATED_2);
|
||||||
|
|
||||||
|
var tp3 = new TitledPane(
|
||||||
|
"Styles.ELEVATED_3",
|
||||||
|
new Label("Content")
|
||||||
|
);
|
||||||
|
tp3.getStyleClass().add(Styles.ELEVATED_3);
|
||||||
|
|
||||||
|
var tp4 = new TitledPane(
|
||||||
|
"Styles.ELEVATED_4",
|
||||||
|
new Label("Content")
|
||||||
|
);
|
||||||
|
tp4.getStyleClass().add(Styles.ELEVATED_4);
|
||||||
|
|
||||||
|
var tp5 = new TitledPane(
|
||||||
|
"Styles.INTERACTIVE",
|
||||||
|
new Label("Hover here")
|
||||||
|
);
|
||||||
|
tp5.getStyleClass().add(Styles.INTERACTIVE);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
tp0.setPrefWidth(300);
|
||||||
|
tp1.setPrefWidth(300);
|
||||||
|
tp2.setPrefWidth(300);
|
||||||
|
tp3.setPrefWidth(300);
|
||||||
|
tp4.setPrefWidth(300);
|
||||||
|
tp5.setPrefWidth(300);
|
||||||
|
|
||||||
|
var box = new FlowPane(HGAP_30, VGAP_20, tp0, tp1, tp2, tp3, tp4, tp5);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
With [code]Styles.ELEVATED_N[/code] or [code]Styles.INTERACTIVE[/code] styles classes \
|
||||||
|
you can add raised shadow effect to the [i]TitledPane[/i]."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox denseExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var tp = new TitledPane(
|
||||||
|
"Header",
|
||||||
|
new Label("Content")
|
||||||
|
);
|
||||||
|
tp.getStyleClass().add(Styles.DENSE);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
tp.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(tp);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
If you need more compact view there's [code]Styles.DENSE[/code] for that."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox altIconExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var tp = new TitledPane(
|
||||||
|
"Header",
|
||||||
|
new Label("Content")
|
||||||
|
);
|
||||||
|
tp.getStyleClass().add(Tweaks.ALT_ICON);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
tp.setPrefWidth(200);
|
||||||
|
|
||||||
|
var box = new HBox(tp);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
There's additional tweak [code]Tweaks.ALT_ICON[/code] to change header \
|
||||||
|
arrow icon to the classic style."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private VBox playground() {
|
||||||
|
var titledPane = new TitledPane();
|
||||||
|
titledPane.setText("_Header");
|
||||||
|
titledPane.setMnemonicParsing(true);
|
||||||
|
titledPane.getStyleClass().add(Styles.ELEVATED_2);
|
||||||
|
|
||||||
|
var textFlow = new TextFlow(new Text(
|
||||||
|
FAKER.lorem().paragraph(10))
|
||||||
|
);
|
||||||
textFlow.setMinHeight(Region.USE_PREF_SIZE);
|
textFlow.setMinHeight(Region.USE_PREF_SIZE);
|
||||||
textFlow.setMaxHeight(Region.USE_PREF_SIZE);
|
textFlow.setMaxHeight(Region.USE_PREF_SIZE);
|
||||||
|
VBox.setVgrow(textFlow, Priority.ALWAYS);
|
||||||
textFlow.setLineSpacing(5);
|
textFlow.setLineSpacing(5);
|
||||||
|
|
||||||
|
String elevationPrefix = "elevated-";
|
||||||
var elevationSlider = new Slider(0, 4, 2);
|
var elevationSlider = new Slider(0, 4, 2);
|
||||||
elevationSlider.setShowTickLabels(true);
|
elevationSlider.setShowTickLabels(true);
|
||||||
elevationSlider.setShowTickMarks(true);
|
elevationSlider.setShowTickMarks(true);
|
||||||
@ -82,15 +202,17 @@ public class TitledPanePage extends AbstractPage {
|
|||||||
elevationSlider.setMinWidth(150);
|
elevationSlider.setMinWidth(150);
|
||||||
elevationSlider.setMaxWidth(150);
|
elevationSlider.setMaxWidth(150);
|
||||||
elevationSlider.valueProperty().addListener((obs, old, val) -> {
|
elevationSlider.valueProperty().addListener((obs, old, val) -> {
|
||||||
playground.getStyleClass().removeAll(
|
titledPane.getStyleClass().removeAll(
|
||||||
playground.getStyleClass().stream().filter(c -> c.startsWith(ELEVATED_PREFIX)).toList()
|
titledPane.getStyleClass().stream()
|
||||||
|
.filter(c -> c.startsWith(elevationPrefix))
|
||||||
|
.toList()
|
||||||
);
|
);
|
||||||
if (val == null) {
|
if (val == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int level = val.intValue();
|
int level = val.intValue();
|
||||||
if (level > 0) {
|
if (level > 0) {
|
||||||
playground.getStyleClass().add(ELEVATED_PREFIX + level);
|
titledPane.getStyleClass().add(elevationPrefix + level);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -99,65 +221,46 @@ public class TitledPanePage extends AbstractPage {
|
|||||||
// for some reason it still preserves arrow button gap. #javafx-bug
|
// for some reason it still preserves arrow button gap. #javafx-bug
|
||||||
var collapseToggle = new ToggleSwitch("Collapsible");
|
var collapseToggle = new ToggleSwitch("Collapsible");
|
||||||
collapseToggle.setSelected(true);
|
collapseToggle.setSelected(true);
|
||||||
playground.collapsibleProperty().bind(collapseToggle.selectedProperty());
|
titledPane.collapsibleProperty().bind(collapseToggle.selectedProperty());
|
||||||
|
|
||||||
var animateToggle = new ToggleSwitch("Animated");
|
var animateToggle = new ToggleSwitch("Animated");
|
||||||
animateToggle.setSelected(true);
|
animateToggle.setSelected(true);
|
||||||
playground.animatedProperty().bind(animateToggle.selectedProperty());
|
titledPane.animatedProperty().bind(animateToggle.selectedProperty());
|
||||||
|
|
||||||
var denseToggle = new ToggleSwitch("Dense");
|
var denseToggle = new ToggleSwitch("Dense");
|
||||||
denseToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(playground, DENSE));
|
denseToggle.selectedProperty()
|
||||||
|
.addListener((obs, old, val) -> Styles.toggleStyleClass(titledPane, Styles.DENSE));
|
||||||
|
|
||||||
var altIconToggle = new ToggleSwitch("Alt icon");
|
var altIconToggle = new ToggleSwitch("Alt icon");
|
||||||
altIconToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(playground, Tweaks.ALT_ICON));
|
altIconToggle.selectedProperty()
|
||||||
|
.addListener((obs, old, val) -> Styles.toggleStyleClass(titledPane, Tweaks.ALT_ICON));
|
||||||
|
|
||||||
var toggles = new GridPane();
|
var toggles = new GridPane();
|
||||||
toggles.setVgap(10);
|
toggles.setHgap(HGAP_20);
|
||||||
toggles.setHgap(BLOCK_HGAP);
|
toggles.setVgap(VGAP_10);
|
||||||
toggles.getColumnConstraints().setAll(
|
toggles.getColumnConstraints().setAll(
|
||||||
new ColumnConstraints(-1, -1, -1, NEVER, RIGHT, false),
|
new ColumnConstraints(-1, -1, -1, NEVER, RIGHT, false),
|
||||||
new ColumnConstraints(-1, -1, -1, NEVER, RIGHT, false)
|
new ColumnConstraints(-1, -1, -1, NEVER, RIGHT, false)
|
||||||
);
|
);
|
||||||
toggles.add(collapseToggle, 0, 0);
|
toggles.addRow(0, collapseToggle, animateToggle);
|
||||||
toggles.add(animateToggle, 1, 0);
|
toggles.addRow(1, denseToggle, altIconToggle);
|
||||||
toggles.add(denseToggle, 0, 1);
|
|
||||||
toggles.add(altIconToggle, 1, 1);
|
|
||||||
|
|
||||||
var controls = new HBox(BLOCK_HGAP);
|
var elevationBox = new HBox(10, new Label("Elevation"), elevationSlider);
|
||||||
controls.setMinHeight(80);
|
|
||||||
|
var controls = new HBox(VGAP_10);
|
||||||
|
controls.setMinHeight(100);
|
||||||
controls.setFillHeight(false);
|
controls.setFillHeight(false);
|
||||||
controls.setAlignment(Pos.CENTER_LEFT);
|
controls.setAlignment(Pos.CENTER_LEFT);
|
||||||
controls.getChildren().setAll(
|
controls.getChildren().setAll(elevationBox, new Spacer(), toggles);
|
||||||
new Label("Elevation"),
|
|
||||||
elevationSlider,
|
var content = new VBox(VGAP_10, textFlow, controls);
|
||||||
new Spacer(),
|
titledPane.setContent(content);
|
||||||
toggles
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The playground demonstrates the most important [i]TitledPane[/i] features \
|
||||||
|
and also serves as an object for monkey testing."""
|
||||||
);
|
);
|
||||||
|
|
||||||
var content = new VBox(BLOCK_VGAP, textFlow, controls);
|
return new VBox(VGAP_10, description, titledPane);
|
||||||
VBox.setVgrow(textFlow, Priority.ALWAYS);
|
|
||||||
playground.setContent(content);
|
|
||||||
|
|
||||||
return playground;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TitledPane interactivePane() {
|
|
||||||
var titledPane = new TitledPane("Interactive", new Text("Hover here."));
|
|
||||||
titledPane.setCollapsible(false);
|
|
||||||
titledPane.getStyleClass().add(INTERACTIVE);
|
|
||||||
return titledPane;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TitledPane disabledPane() {
|
|
||||||
var titledPane = new TitledPane("Disabled", new CheckBox("This checkbox is disabled."));
|
|
||||||
titledPane.setCollapsible(false);
|
|
||||||
titledPane.setDisable(true);
|
|
||||||
return titledPane;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TitledPane untitledPane() {
|
|
||||||
var titledPane = new TitledPane("This pane has no title.", new Text());
|
|
||||||
titledPane.setCollapsible(false);
|
|
||||||
return titledPane;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,26 +2,21 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
import atlantafx.base.theme.Styles;
|
||||||
import static atlantafx.base.theme.Styles.CENTER_PILL;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import static atlantafx.base.theme.Styles.LEFT_PILL;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import static atlantafx.base.theme.Styles.RIGHT_PILL;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import static atlantafx.sampler.util.Controls.toggleButton;
|
import javafx.geometry.Pos;
|
||||||
import static javafx.scene.layout.GridPane.REMAINING;
|
|
||||||
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
|
import javafx.scene.control.ToggleButton;
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
public class ToggleButtonPage extends AbstractPage {
|
public class ToggleButtonPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "ToggleButton";
|
public static final String NAME = "ToggleButton";
|
||||||
|
|
||||||
@ -32,103 +27,158 @@ public class ToggleButtonPage extends AbstractPage {
|
|||||||
|
|
||||||
public ToggleButtonPage() {
|
public ToggleButtonPage() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
|
addFormattedText("""
|
||||||
|
A [i]ToggleButton[/i] is a special control having the ability to be selected."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Icon Only", iconOnlyExample());
|
||||||
|
addSection("Segmented Group", segmentedGroupExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private ExampleBox usageExample() {
|
||||||
var grid = new GridPane();
|
//snippet_1:start
|
||||||
grid.setHgap(Page.PAGE_HGAP);
|
var singleBtn = new ToggleButton("Toggle");
|
||||||
grid.setVgap(Page.PAGE_VGAP);
|
singleBtn.setSelected(true);
|
||||||
|
|
||||||
grid.add(basicSample(), 0, 0, REMAINING, 1);
|
// ~
|
||||||
grid.add(wizardSample(), 0, 1);
|
|
||||||
grid.add(iconOnlySample(), 1, 1);
|
|
||||||
grid.add(disabledSample(), 0, 2);
|
|
||||||
|
|
||||||
setUserContent(grid);
|
var leftBtn1 = new ToggleButton("._left-pill");
|
||||||
}
|
leftBtn1.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
leftBtn1.setSelected(true);
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
var rightBtn1 = new ToggleButton("._right-pill");
|
||||||
var threeButtonGroup = new ToggleGroup();
|
rightBtn1.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
|
||||||
var leftPill = toggleButton("._left-pill", null, threeButtonGroup, true, LEFT_PILL);
|
var twoBtnGroup = new ToggleGroup();
|
||||||
leftPill.setMnemonicParsing(true);
|
twoBtnGroup.getToggles().addAll(leftBtn1, rightBtn1);
|
||||||
|
|
||||||
var centerPill = toggleButton("._center-pill", null, threeButtonGroup, false, CENTER_PILL);
|
// ~
|
||||||
centerPill.setMnemonicParsing(true);
|
|
||||||
|
|
||||||
var rightPill = toggleButton("._right-pill", null, threeButtonGroup, false, RIGHT_PILL);
|
var leftBtn2 = new ToggleButton("._left-pill");
|
||||||
rightPill.setMnemonicParsing(true);
|
leftBtn2.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
leftBtn2.setMnemonicParsing(true);
|
||||||
|
leftBtn2.setSelected(true);
|
||||||
|
|
||||||
var threeButtonBox = new HBox(leftPill, centerPill, rightPill);
|
var centerBtn2 = new ToggleButton("._center-pill");
|
||||||
|
centerBtn2.getStyleClass().add(Styles.CENTER_PILL);
|
||||||
|
centerBtn2.setMnemonicParsing(true);
|
||||||
|
|
||||||
var twoButtonGroup = new ToggleGroup();
|
var rightBtn2 = new ToggleButton("._right-pill");
|
||||||
var twoButtonBox = new HBox(
|
rightBtn2.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
toggleButton(".left-pill", null, twoButtonGroup, true, LEFT_PILL),
|
rightBtn2.setMnemonicParsing(true);
|
||||||
toggleButton(".right-pill", null, twoButtonGroup, false, RIGHT_PILL)
|
|
||||||
|
var threeBtnGroup = new ToggleGroup();
|
||||||
|
threeBtnGroup.getToggles().addAll(leftBtn2, centerBtn2, rightBtn2);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var twoBtnBox = new HBox(leftBtn1, rightBtn1);
|
||||||
|
twoBtnBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var threeBtnBox = new HBox(leftBtn2, centerBtn2, rightBtn2);
|
||||||
|
threeBtnBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_30, singleBtn, twoBtnBox, threeBtnBox);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]ToggleButtons[/i] can also be placed in groups. When in groups, only one button \
|
||||||
|
at a time within that group can be selected. Use [code]Styles.*_PILL[/code] style \
|
||||||
|
classes if you want to toggle group to look like a single "segmented" button."""
|
||||||
);
|
);
|
||||||
|
|
||||||
return new SampleBlock("Basic", new HBox(BLOCK_HGAP, threeButtonBox, twoButtonBox));
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock wizardSample() {
|
private ExampleBox iconOnlyExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var leftBtn = new ToggleButton("", new FontIcon(Feather.BOLD));
|
||||||
|
leftBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.LEFT_PILL
|
||||||
|
);
|
||||||
|
leftBtn.setSelected(true);
|
||||||
|
|
||||||
|
var centerBtn = new ToggleButton("", new FontIcon(Feather.ITALIC));
|
||||||
|
centerBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.CENTER_PILL
|
||||||
|
);
|
||||||
|
|
||||||
|
var rightBtn = new ToggleButton("", new FontIcon(Feather.UNDERLINE));
|
||||||
|
rightBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.RIGHT_PILL
|
||||||
|
);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(leftBtn, centerBtn, rightBtn);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Sometimes it's desirable to create a button group that consists from icon \
|
||||||
|
buttons only. This is also supported."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox segmentedGroupExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var musicBtn = new ToggleButton(
|
||||||
|
"Music", new FontIcon(Feather.MUSIC)
|
||||||
|
);
|
||||||
|
musicBtn.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
musicBtn.setSelected(true);
|
||||||
|
|
||||||
|
var imagesBtn = new ToggleButton(
|
||||||
|
"Images", new FontIcon(Feather.IMAGE)
|
||||||
|
);
|
||||||
|
imagesBtn.getStyleClass().add(Styles.CENTER_PILL);
|
||||||
|
|
||||||
|
var videosBtn = new ToggleButton(
|
||||||
|
"Videos", new FontIcon(Feather.VIDEO)
|
||||||
|
);
|
||||||
|
videosBtn.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
|
||||||
var group = new ToggleGroup();
|
var group = new ToggleGroup();
|
||||||
|
group.getToggles().addAll(musicBtn, imagesBtn, videosBtn);
|
||||||
var prevBtn = new Button("\f", new FontIcon(Feather.CHEVRON_LEFT));
|
|
||||||
prevBtn.getStyleClass().addAll(BUTTON_ICON, LEFT_PILL, "toggle-button");
|
|
||||||
prevBtn.setOnAction(e -> {
|
|
||||||
int selected = group.getToggles().indexOf(group.getSelectedToggle());
|
|
||||||
if (selected > 0) {
|
|
||||||
group.selectToggle(group.getToggles().get(selected - 1));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var nextBtn = new Button("\f", new FontIcon(Feather.CHEVRON_RIGHT));
|
|
||||||
nextBtn.getStyleClass().addAll(BUTTON_ICON, RIGHT_PILL, "toggle-button");
|
|
||||||
nextBtn.setContentDisplay(ContentDisplay.RIGHT);
|
|
||||||
nextBtn.setOnAction(e -> {
|
|
||||||
int selected = group.getToggles().indexOf(group.getSelectedToggle());
|
|
||||||
if (selected < group.getToggles().size() - 1) {
|
|
||||||
group.selectToggle(group.getToggles().get(selected + 1));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var wizard = new HBox(
|
|
||||||
prevBtn,
|
|
||||||
toggleButton("Music", Feather.MUSIC, group, true, CENTER_PILL),
|
|
||||||
toggleButton("Images", Feather.IMAGE, group, false, CENTER_PILL),
|
|
||||||
toggleButton("Videos", Feather.VIDEO, group, false, CENTER_PILL),
|
|
||||||
nextBtn
|
|
||||||
);
|
|
||||||
group.selectedToggleProperty().addListener((obs, old, val) -> {
|
group.selectedToggleProperty().addListener((obs, old, val) -> {
|
||||||
if (val == null) {
|
if (val == null) {
|
||||||
old.setSelected(true);
|
old.setSelected(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return new SampleBlock("Wizard", wizard);
|
var prevBtn = new Button("\f", new FontIcon(Feather.CHEVRON_LEFT));
|
||||||
}
|
prevBtn.getStyleClass().addAll(
|
||||||
|
Styles.BUTTON_ICON, Styles.LEFT_PILL, "toggle-button"
|
||||||
|
);
|
||||||
|
prevBtn.setOnAction(evt -> {
|
||||||
|
int sel = group.getToggles().indexOf(group.getSelectedToggle());
|
||||||
|
if (sel > 0) {
|
||||||
|
group.selectToggle(group.getToggles().get(sel - 1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private SampleBlock iconOnlySample() {
|
var nextBtn = new Button("\f", new FontIcon(Feather.CHEVRON_RIGHT));
|
||||||
var icons = new HBox(
|
nextBtn.getStyleClass().addAll(
|
||||||
toggleButton("", Feather.BOLD, null, true, BUTTON_ICON, LEFT_PILL),
|
Styles.BUTTON_ICON, Styles.RIGHT_PILL, "toggle-button"
|
||||||
toggleButton("", Feather.ITALIC, null, false, BUTTON_ICON, CENTER_PILL),
|
);
|
||||||
toggleButton("", Feather.UNDERLINE, null, false, BUTTON_ICON, RIGHT_PILL)
|
nextBtn.setContentDisplay(ContentDisplay.RIGHT);
|
||||||
|
nextBtn.setOnAction(evt -> {
|
||||||
|
int sel = group.getToggles().indexOf(group.getSelectedToggle());
|
||||||
|
if (sel < group.getToggles().size() - 1) {
|
||||||
|
group.selectToggle(group.getToggles().get(sel + 1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var groupBox = new HBox(
|
||||||
|
prevBtn, musicBtn, imagesBtn, videosBtn, nextBtn
|
||||||
|
);
|
||||||
|
groupBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This example demonstrates how to create a complex segmented button group."""
|
||||||
);
|
);
|
||||||
|
|
||||||
return new SampleBlock("Icon only", icons);
|
return new ExampleBox(groupBox, new Snippet(getClass(), 3), description);
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock disabledSample() {
|
|
||||||
var group = new ToggleGroup();
|
|
||||||
var content = new HBox(
|
|
||||||
toggleButton(".left-pill", null, group, false, LEFT_PILL),
|
|
||||||
toggleButton(".center-pill", null, group, false, CENTER_PILL),
|
|
||||||
toggleButton(".right-pill", null, group, true, RIGHT_PILL)
|
|
||||||
);
|
|
||||||
content.getChildren().get(0).setDisable(true);
|
|
||||||
content.getChildren().get(1).setDisable(true);
|
|
||||||
|
|
||||||
return new SampleBlock("Disabled", content);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.geometry.HorizontalDirection;
|
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
|
|
||||||
public class ToggleSwitchPage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "ToggleSwitch";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ToggleSwitchPage() {
|
|
||||||
super();
|
|
||||||
setUserContent(new FlowPane(
|
|
||||||
Page.PAGE_HGAP, Page.PAGE_VGAP,
|
|
||||||
basicSample(),
|
|
||||||
stateSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
|
||||||
var leftToggle = new ToggleSwitch("Enabled");
|
|
||||||
leftToggle.selectedProperty().addListener(
|
|
||||||
(obs, old, val) -> leftToggle.setText(val ? "Enabled" : "Disabled")
|
|
||||||
);
|
|
||||||
leftToggle.setSelected(true);
|
|
||||||
|
|
||||||
var rightToggle = new ToggleSwitch("Disabled");
|
|
||||||
rightToggle.selectedProperty().addListener(
|
|
||||||
(obs, old, val) -> rightToggle.setText(val ? "Enabled" : "Disabled")
|
|
||||||
);
|
|
||||||
rightToggle.setLabelPosition(HorizontalDirection.RIGHT);
|
|
||||||
rightToggle.setSelected(false);
|
|
||||||
|
|
||||||
return new SampleBlock("Basic", new VBox(SampleBlock.BLOCK_VGAP, leftToggle, rightToggle));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock stateSample() {
|
|
||||||
var successToggle = new ToggleSwitch("Enabled");
|
|
||||||
successToggle.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
successToggle.setText(val ? "Enabled" : "Disabled");
|
|
||||||
successToggle.pseudoClassStateChanged(Styles.STATE_SUCCESS, val);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
successToggle.setSelected(true);
|
|
||||||
successToggle.pseudoClassStateChanged(Styles.STATE_SUCCESS, true);
|
|
||||||
|
|
||||||
var dangerToggle = new ToggleSwitch("Disabled");
|
|
||||||
dangerToggle.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
dangerToggle.setText(val ? "Enabled" : "Disabled");
|
|
||||||
dangerToggle.pseudoClassStateChanged(Styles.STATE_DANGER, val);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
dangerToggle.setLabelPosition(HorizontalDirection.RIGHT);
|
|
||||||
dangerToggle.setSelected(false);
|
|
||||||
|
|
||||||
return new SampleBlock("State", new VBox(SampleBlock.BLOCK_VGAP, successToggle, dangerToggle));
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,46 +2,44 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.ACCENT;
|
import atlantafx.base.controls.CaptionMenuItem;
|
||||||
import static atlantafx.base.theme.Styles.BOTTOM;
|
import atlantafx.base.controls.CustomTextField;
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
|
||||||
import static atlantafx.base.theme.Styles.CENTER_PILL;
|
|
||||||
import static atlantafx.base.theme.Styles.FLAT;
|
|
||||||
import static atlantafx.base.theme.Styles.LEFT;
|
|
||||||
import static atlantafx.base.theme.Styles.LEFT_PILL;
|
|
||||||
import static atlantafx.base.theme.Styles.RIGHT;
|
|
||||||
import static atlantafx.base.theme.Styles.RIGHT_PILL;
|
|
||||||
import static atlantafx.base.theme.Styles.TOP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
import static atlantafx.sampler.util.Controls.button;
|
|
||||||
import static atlantafx.sampler.util.Controls.iconButton;
|
|
||||||
import static atlantafx.sampler.util.Controls.toggleButton;
|
|
||||||
import static javafx.geometry.Orientation.HORIZONTAL;
|
|
||||||
import static javafx.geometry.Orientation.VERTICAL;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.fake.SampleMenuBar;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Orientation;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.CheckMenuItem;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Menu;
|
||||||
import javafx.scene.control.MenuBar;
|
import javafx.scene.control.MenuBar;
|
||||||
import javafx.scene.control.MenuButton;
|
import javafx.scene.control.MenuButton;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.RadioMenuItem;
|
||||||
import javafx.scene.control.Separator;
|
import javafx.scene.control.Separator;
|
||||||
|
import javafx.scene.control.SeparatorMenuItem;
|
||||||
import javafx.scene.control.TitledPane;
|
import javafx.scene.control.TitledPane;
|
||||||
|
import javafx.scene.control.ToggleButton;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.control.ToolBar;
|
import javafx.scene.control.ToolBar;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyCodeCombination;
|
||||||
|
import javafx.scene.input.KeyCombination;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
@ -50,10 +48,14 @@ import javafx.scene.layout.Region;
|
|||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
|
import net.datafaker.Faker;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
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.Material2OutlinedAL;
|
||||||
|
|
||||||
public class ToolBarPage extends AbstractPage {
|
public class ToolBarPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "ToolBar";
|
public static final String NAME = "ToolBar";
|
||||||
|
|
||||||
@ -62,60 +64,152 @@ public class ToolBarPage extends AbstractPage {
|
|||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Side toolbarPos = Side.TOP;
|
|
||||||
|
|
||||||
public ToolBarPage() {
|
public ToolBarPage() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
|
addFormattedText("""
|
||||||
|
A [i]ToolBar[/i] is a control which displays items horizontally or vertically."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private ExampleBox usageExample() {
|
||||||
var toolbar = new ToolBar(createButtons(HORIZONTAL));
|
//snippet_1:start
|
||||||
|
var toolbar1 = new ToolBar(
|
||||||
|
new Button("New", new FontIcon(Feather.PLUS)),
|
||||||
|
new Button("Open", new FontIcon(Feather.FILE)),
|
||||||
|
new Button("Save", new FontIcon(Feather.SAVE)),
|
||||||
|
new Separator(Orientation.VERTICAL),
|
||||||
|
new Button("Clean", new FontIcon(Feather.ROTATE_CCW)),
|
||||||
|
new Button("Compile", new FontIcon(Feather.LAYERS)),
|
||||||
|
new Button("Run", new FontIcon(Feather.PLAY))
|
||||||
|
);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var fontFamilyCmb = new ComboBox<>(
|
||||||
|
FXCollections.observableArrayList(Font.getFamilies())
|
||||||
|
);
|
||||||
|
fontFamilyCmb.setPrefWidth(150);
|
||||||
|
fontFamilyCmb.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var fontSizeCmb = new ComboBox<>(
|
||||||
|
IntStream.range(6, 32).mapToObj(String::valueOf).collect(
|
||||||
|
Collectors.toCollection(FXCollections::observableArrayList)
|
||||||
|
));
|
||||||
|
fontSizeCmb.getSelectionModel().select(6);
|
||||||
|
|
||||||
|
var toolbar2 = new ToolBar(
|
||||||
|
fontFamilyCmb,
|
||||||
|
fontSizeCmb,
|
||||||
|
new Separator(Orientation.VERTICAL),
|
||||||
|
toggleIconButton(Feather.BOLD, true),
|
||||||
|
toggleIconButton(Feather.ITALIC),
|
||||||
|
toggleIconButton(Feather.UNDERLINE),
|
||||||
|
new Separator(Orientation.VERTICAL),
|
||||||
|
toggleIconButton(Feather.ALIGN_LEFT),
|
||||||
|
toggleIconButton(Feather.ALIGN_CENTER),
|
||||||
|
toggleIconButton(Feather.ALIGN_RIGHT),
|
||||||
|
new Separator(Orientation.VERTICAL),
|
||||||
|
iconButton(Feather.IMAGE)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var textField = new CustomTextField("https://example.com");
|
||||||
|
textField.setPromptText("Search Doodle of type an URL");
|
||||||
|
textField.setLeft(new FontIcon(Feather.LOCK));
|
||||||
|
textField.setRight(new FontIcon(Feather.STAR));
|
||||||
|
HBox.setHgrow(textField, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var dropdown = new MenuButton("", new FontIcon(Feather.MENU));
|
||||||
|
dropdown.getItems().setAll(
|
||||||
|
new MenuItem("Action 1"),
|
||||||
|
new MenuItem("Action 2"),
|
||||||
|
new MenuItem("Action 3")
|
||||||
|
);
|
||||||
|
|
||||||
|
var toolbar3 = new ToolBar(
|
||||||
|
iconButton(Feather.CHEVRON_LEFT),
|
||||||
|
iconButton(Feather.CHEVRON_RIGHT),
|
||||||
|
new Separator(Orientation.VERTICAL),
|
||||||
|
iconButton(Feather.REFRESH_CW),
|
||||||
|
new Spacer(10),
|
||||||
|
textField,
|
||||||
|
new Spacer(10),
|
||||||
|
iconButton(Feather.BOOKMARK),
|
||||||
|
iconButton(Feather.USER),
|
||||||
|
dropdown
|
||||||
|
);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_20, toolbar1, toolbar2, toolbar3);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The most common items to place within a toolbar are [i]Button[/i], [i]ToggleButtons[/i] \
|
||||||
|
and [i]Separator[/i], but you are not restricted to just these, and can insert any node \
|
||||||
|
into it."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private Side toolbarPos = Side.TOP;
|
||||||
|
|
||||||
|
private VBox playground() {
|
||||||
|
var toolbar = new ToolBar(createButtons(Orientation.HORIZONTAL));
|
||||||
var toolbarLayer = new BorderPane();
|
var toolbarLayer = new BorderPane();
|
||||||
toolbarLayer.setTop(new TopBar(toolbar));
|
toolbarLayer.setTop(new TopBar(toolbar));
|
||||||
|
|
||||||
var controller = createController(toolbarLayer, toolbar);
|
var controller = createController(toolbarLayer, toolbar);
|
||||||
controller.setPrefSize(500, 300);
|
controller.setPrefSize(500, 300);
|
||||||
var controllerLayer = new BorderPane();
|
|
||||||
controllerLayer.setCenter(controller);
|
var controllerLayer = new BorderPane(controller);
|
||||||
controllerLayer.setMaxSize(500, 300);
|
controllerLayer.setMaxSize(500, 300);
|
||||||
|
|
||||||
var root = new StackPane();
|
var description = BBCodeParser.createFormattedText("""
|
||||||
root.getStyleClass().add(Styles.BORDERED);
|
The playground demonstrates the most important [i]Toolbar[/i] features \
|
||||||
root.getChildren().addAll(toolbarLayer, controllerLayer);
|
and also serves as an object for monkey testing."""
|
||||||
VBox.setVgrow(root, Priority.ALWAYS);
|
);
|
||||||
|
|
||||||
setUserContent(new SampleBlock("Playground", root));
|
var stack = new StackPane(toolbarLayer, controllerLayer);
|
||||||
|
stack.getStyleClass().add(Styles.BORDERED);
|
||||||
|
stack.setMinSize(600, 500);
|
||||||
|
|
||||||
|
return new VBox(VGAP_10, description, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TitledPane createController(BorderPane borderPane, ToolBar toolbar) {
|
private TitledPane createController(BorderPane borderPane, ToolBar toolbar) {
|
||||||
// == BUTTONS ==
|
// == BUTTONS ==
|
||||||
|
|
||||||
var toTopBtn = new Button("", new FontIcon(Feather.ARROW_UP));
|
var toTopBtn = new Button("", new FontIcon(Feather.ARROW_UP));
|
||||||
toTopBtn.getStyleClass().addAll(BUTTON_ICON);
|
toTopBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toTopBtn.setOnAction(e -> rotateToolbar(borderPane, toolbar, Side.TOP));
|
toTopBtn.setOnAction(e -> rotateToolbar(borderPane, toolbar, Side.TOP));
|
||||||
|
|
||||||
var toRightBtn = new Button("", new FontIcon(Feather.ARROW_RIGHT));
|
var toRightBtn = new Button("", new FontIcon(Feather.ARROW_RIGHT));
|
||||||
toRightBtn.getStyleClass().addAll(BUTTON_ICON);
|
toRightBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toRightBtn.setOnAction(e -> rotateToolbar(borderPane, toolbar, Side.RIGHT));
|
toRightBtn.setOnAction(e -> rotateToolbar(borderPane, toolbar, Side.RIGHT));
|
||||||
|
|
||||||
var toBottomBtn = new Button("", new FontIcon(Feather.ARROW_DOWN));
|
var toBottomBtn = new Button("", new FontIcon(Feather.ARROW_DOWN));
|
||||||
toBottomBtn.getStyleClass().addAll(BUTTON_ICON);
|
toBottomBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toBottomBtn.setOnAction(e -> rotateToolbar(borderPane, toolbar, Side.BOTTOM));
|
toBottomBtn.setOnAction(e -> rotateToolbar(borderPane, toolbar, Side.BOTTOM));
|
||||||
|
|
||||||
var toLeftBtn = new Button("", new FontIcon(Feather.ARROW_LEFT));
|
var toLeftBtn = new Button("", new FontIcon(Feather.ARROW_LEFT));
|
||||||
toLeftBtn.getStyleClass().addAll(BUTTON_ICON);
|
toLeftBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toLeftBtn.setOnAction(e -> rotateToolbar(borderPane, toolbar, Side.LEFT));
|
toLeftBtn.setOnAction(e -> rotateToolbar(borderPane, toolbar, Side.LEFT));
|
||||||
|
|
||||||
var appendBtn = new Button("", new FontIcon(Feather.PLUS));
|
var appendBtn = new Button("", new FontIcon(Feather.PLUS));
|
||||||
appendBtn.getStyleClass().addAll(BUTTON_ICON, ACCENT);
|
appendBtn.getStyleClass().addAll(Styles.BUTTON_ICON, Styles.ACCENT);
|
||||||
appendBtn.setOnAction(e -> {
|
appendBtn.setOnAction(e -> {
|
||||||
if (toolbar.getOrientation() == HORIZONTAL) {
|
if (toolbar.getOrientation() == Orientation.HORIZONTAL) {
|
||||||
var textBtn = new Button(FAKER.animal().name(), new FontIcon(randomIcon()));
|
var textBtn = new Button(FAKER.animal().name(), new FontIcon(randomIcon()));
|
||||||
toolbar.getItems().add(textBtn);
|
toolbar.getItems().add(textBtn);
|
||||||
} else {
|
} else {
|
||||||
var iconBtn = new Button("", new FontIcon(randomIcon()));
|
var iconBtn = new Button("", new FontIcon(randomIcon()));
|
||||||
iconBtn.getStyleClass().addAll(BUTTON_ICON);
|
iconBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
toolbar.getItems().add(iconBtn);
|
toolbar.getItems().add(iconBtn);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -124,6 +218,8 @@ public class ToolBarPage extends AbstractPage {
|
|||||||
buttonsPane.setMinSize(120, 120);
|
buttonsPane.setMinSize(120, 120);
|
||||||
buttonsPane.setMaxSize(120, 120);
|
buttonsPane.setMaxSize(120, 120);
|
||||||
|
|
||||||
|
buttonsPane.setCenter(appendBtn);
|
||||||
|
|
||||||
buttonsPane.setTop(toTopBtn);
|
buttonsPane.setTop(toTopBtn);
|
||||||
BorderPane.setAlignment(toTopBtn, Pos.CENTER);
|
BorderPane.setAlignment(toTopBtn, Pos.CENTER);
|
||||||
|
|
||||||
@ -136,14 +232,12 @@ public class ToolBarPage extends AbstractPage {
|
|||||||
buttonsPane.setLeft(toLeftBtn);
|
buttonsPane.setLeft(toLeftBtn);
|
||||||
BorderPane.setAlignment(toLeftBtn, Pos.CENTER);
|
BorderPane.setAlignment(toLeftBtn, Pos.CENTER);
|
||||||
|
|
||||||
buttonsPane.setCenter(appendBtn);
|
|
||||||
|
|
||||||
// == TOGGLES ==
|
// == TOGGLES ==
|
||||||
|
|
||||||
var menuBarToggle = new ToggleSwitch();
|
var menuBarToggle = new ToggleSwitch();
|
||||||
menuBarToggle.selectedProperty().addListener((obs, old, value) -> {
|
menuBarToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
TopBar topBar = (TopBar) borderPane.getTop();
|
TopBar topBar = (TopBar) borderPane.getTop();
|
||||||
if (value) {
|
if (val) {
|
||||||
topBar.showOrCreateMenuBar();
|
topBar.showOrCreateMenuBar();
|
||||||
} else {
|
} else {
|
||||||
topBar.hideMenuBar();
|
topBar.hideMenuBar();
|
||||||
@ -160,26 +254,22 @@ public class ToolBarPage extends AbstractPage {
|
|||||||
var togglesGrid = new GridPane();
|
var togglesGrid = new GridPane();
|
||||||
togglesGrid.setHgap(10);
|
togglesGrid.setHgap(10);
|
||||||
togglesGrid.setVgap(10);
|
togglesGrid.setVgap(10);
|
||||||
|
togglesGrid.addRow(0, createLabel("Show menu bar"), menuBarToggle);
|
||||||
togglesGrid.add(createLabel("Show menu bar"), 0, 0);
|
togglesGrid.addRow(1, createLabel("Disable"), disableToggle);
|
||||||
togglesGrid.add(menuBarToggle, 1, 0);
|
|
||||||
|
|
||||||
togglesGrid.add(createLabel("Disable"), 0, 1);
|
|
||||||
togglesGrid.add(disableToggle, 1, 1);
|
|
||||||
|
|
||||||
// == LAYOUT ==
|
// == LAYOUT ==
|
||||||
|
|
||||||
var controls = new HBox(40, new Spacer(), buttonsPane, togglesGrid, new Spacer());
|
var controls = new HBox(40, buttonsPane, togglesGrid);
|
||||||
controls.setAlignment(Pos.CENTER);
|
controls.setAlignment(Pos.CENTER);
|
||||||
|
controls.setFillHeight(true);
|
||||||
|
|
||||||
var content = new VBox(BLOCK_VGAP);
|
var wrapper = new VBox(VGAP_20, controls);
|
||||||
content.getChildren().setAll(controls);
|
wrapper.setAlignment(Pos.CENTER);
|
||||||
content.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
var root = new TitledPane("Controller", content);
|
var controller = new TitledPane("Controller", wrapper);
|
||||||
root.setCollapsible(false);
|
controller.setCollapsible(false);
|
||||||
|
|
||||||
return root;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rotateToolbar(BorderPane borderPane, ToolBar toolbar, Side pos) {
|
private void rotateToolbar(BorderPane borderPane, ToolBar toolbar, Side pos) {
|
||||||
@ -204,27 +294,35 @@ public class ToolBarPage extends AbstractPage {
|
|||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
switch (pos) {
|
switch (pos) {
|
||||||
case TOP -> {
|
case TOP -> {
|
||||||
toolbar.setOrientation(HORIZONTAL);
|
toolbar.setOrientation(Orientation.HORIZONTAL);
|
||||||
Styles.addStyleClass(toolbar, TOP, RIGHT, BOTTOM, LEFT);
|
Styles.addStyleClass(
|
||||||
toolbar.getItems().setAll(createButtons(HORIZONTAL));
|
toolbar, Styles.TOP, Styles.RIGHT, Styles.BOTTOM, Styles.LEFT
|
||||||
|
);
|
||||||
|
toolbar.getItems().setAll(createButtons(Orientation.HORIZONTAL));
|
||||||
topBar.setToolBar(toolbar);
|
topBar.setToolBar(toolbar);
|
||||||
}
|
}
|
||||||
case RIGHT -> {
|
case RIGHT -> {
|
||||||
toolbar.setOrientation(VERTICAL);
|
toolbar.setOrientation(Orientation.VERTICAL);
|
||||||
Styles.addStyleClass(toolbar, RIGHT, TOP, BOTTOM, LEFT);
|
Styles.addStyleClass(
|
||||||
toolbar.getItems().setAll(createButtons(VERTICAL));
|
toolbar, Styles.RIGHT, Styles.TOP, Styles.BOTTOM, Styles.LEFT
|
||||||
|
);
|
||||||
|
toolbar.getItems().setAll(createButtons(Orientation.VERTICAL));
|
||||||
borderPane.setRight(toolbar);
|
borderPane.setRight(toolbar);
|
||||||
}
|
}
|
||||||
case BOTTOM -> {
|
case BOTTOM -> {
|
||||||
toolbar.setOrientation(HORIZONTAL);
|
toolbar.setOrientation(Orientation.HORIZONTAL);
|
||||||
Styles.addStyleClass(toolbar, BOTTOM, TOP, RIGHT, LEFT);
|
Styles.addStyleClass(
|
||||||
toolbar.getItems().setAll(createButtons(HORIZONTAL));
|
toolbar, Styles.BOTTOM, Styles.TOP, Styles.RIGHT, Styles.LEFT
|
||||||
|
);
|
||||||
|
toolbar.getItems().setAll(createButtons(Orientation.HORIZONTAL));
|
||||||
borderPane.setBottom(toolbar);
|
borderPane.setBottom(toolbar);
|
||||||
}
|
}
|
||||||
case LEFT -> {
|
case LEFT -> {
|
||||||
toolbar.setOrientation(VERTICAL);
|
toolbar.setOrientation(Orientation.VERTICAL);
|
||||||
Styles.addStyleClass(toolbar, LEFT, RIGHT, TOP, BOTTOM);
|
Styles.addStyleClass(
|
||||||
toolbar.getItems().setAll(createButtons(VERTICAL));
|
toolbar, Styles.LEFT, Styles.RIGHT, Styles.TOP, Styles.BOTTOM
|
||||||
|
);
|
||||||
|
toolbar.getItems().setAll(createButtons(Orientation.VERTICAL));
|
||||||
borderPane.setLeft(toolbar);
|
borderPane.setLeft(toolbar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,39 +331,25 @@ public class ToolBarPage extends AbstractPage {
|
|||||||
|
|
||||||
public Node[] createButtons(Orientation orientation) {
|
public Node[] createButtons(Orientation orientation) {
|
||||||
var result = new ArrayList<Node>();
|
var result = new ArrayList<Node>();
|
||||||
result.add(iconButton(Feather.FILE, false));
|
result.add(iconButton(Feather.FILE));
|
||||||
result.add(iconButton(Feather.FOLDER, false));
|
result.add(iconButton(Feather.FOLDER));
|
||||||
result.add(iconButton(Feather.SAVE, false));
|
result.add(iconButton(Feather.SAVE));
|
||||||
result.add(new Separator());
|
result.add(new Separator());
|
||||||
|
|
||||||
if (orientation == HORIZONTAL) {
|
if (orientation == Orientation.HORIZONTAL) {
|
||||||
result.add(button("Undo", null, false));
|
result.add(new Button("Undo"));
|
||||||
result.add(button("Redo", null, true));
|
result.add(new Button("Redo"));
|
||||||
|
|
||||||
result.add(new Separator());
|
result.add(new Separator());
|
||||||
|
result.add(toggleIconButton(Feather.BOLD, true, Styles.LEFT_PILL));
|
||||||
result.add(toggleButton("", Feather.BOLD, null, true, BUTTON_ICON, LEFT_PILL));
|
result.add(toggleIconButton(Feather.ITALIC, Styles.CENTER_PILL));
|
||||||
result.add(toggleButton("", Feather.ITALIC, null, false, BUTTON_ICON, CENTER_PILL));
|
result.add(toggleIconButton(Feather.UNDERLINE, Styles.RIGHT_PILL));
|
||||||
result.add(toggleButton("", Feather.UNDERLINE, null, false, BUTTON_ICON, RIGHT_PILL));
|
|
||||||
|
|
||||||
result.add(new Spacer(5));
|
|
||||||
|
|
||||||
var fontCombo = new ComboBox<>(FXCollections.observableArrayList(Font.getFamilies()));
|
|
||||||
fontCombo.setPrefWidth(150);
|
|
||||||
fontCombo.getSelectionModel().selectFirst();
|
|
||||||
result.add(fontCombo);
|
|
||||||
|
|
||||||
var settingsMenu = new MenuButton("Settings", new FontIcon(Feather.SETTINGS), createItems(5));
|
|
||||||
settingsMenu.getStyleClass().add(FLAT);
|
|
||||||
result.add(new Spacer());
|
|
||||||
result.add(settingsMenu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orientation == VERTICAL) {
|
if (orientation == Orientation.VERTICAL) {
|
||||||
result.add(iconButton(Feather.CORNER_DOWN_LEFT, false));
|
result.add(iconButton(Feather.CORNER_DOWN_LEFT));
|
||||||
result.add(iconButton(Feather.CORNER_DOWN_RIGHT, true));
|
result.add(iconButton(Feather.CORNER_DOWN_RIGHT));
|
||||||
result.add(new Spacer(orientation));
|
result.add(new Spacer(orientation));
|
||||||
result.add(iconButton(Feather.SETTINGS, false));
|
result.add(iconButton(Feather.SETTINGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toArray(Node[]::new);
|
return result.toArray(Node[]::new);
|
||||||
@ -278,9 +362,42 @@ public class ToolBarPage extends AbstractPage {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MenuItem[] createItems(int count) {
|
public static Button iconButton(Ikon icon) {
|
||||||
return IntStream.range(0, count).mapToObj(i -> new MenuItem(FAKER.babylon5().character()))
|
var btn = new Button("");
|
||||||
.toArray(MenuItem[]::new);
|
if (icon != null) {
|
||||||
|
btn.setGraphic(new FontIcon(icon));
|
||||||
|
}
|
||||||
|
btn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
|
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToggleButton toggleIconButton(@Nullable Ikon icon,
|
||||||
|
String... styleClasses) {
|
||||||
|
return toggleIconButton(icon, null, false, styleClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToggleButton toggleIconButton(@Nullable Ikon icon,
|
||||||
|
boolean selected,
|
||||||
|
String... styleClasses) {
|
||||||
|
return toggleIconButton(icon, null, selected, styleClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToggleButton toggleIconButton(@Nullable Ikon icon,
|
||||||
|
@Nullable ToggleGroup group,
|
||||||
|
boolean selected,
|
||||||
|
String... styleClasses) {
|
||||||
|
var btn = new ToggleButton("");
|
||||||
|
if (icon != null) {
|
||||||
|
btn.setGraphic(new FontIcon(icon));
|
||||||
|
}
|
||||||
|
if (group != null) {
|
||||||
|
btn.setToggleGroup(group);
|
||||||
|
}
|
||||||
|
btn.getStyleClass().addAll(styleClasses);
|
||||||
|
btn.setSelected(selected);
|
||||||
|
|
||||||
|
return btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@ -318,4 +435,188 @@ public class ToolBarPage extends AbstractPage {
|
|||||||
getChildren().set(1, DUMMY_TOOLBAR);
|
getChildren().set(1, DUMMY_TOOLBAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SampleMenuBar extends MenuBar {
|
||||||
|
|
||||||
|
private static final EventHandler<ActionEvent> PRINT_SOURCE = System.out::println;
|
||||||
|
|
||||||
|
public SampleMenuBar(Faker faker) {
|
||||||
|
getMenus().addAll(
|
||||||
|
fileMenu(faker),
|
||||||
|
editMenu(),
|
||||||
|
viewMenu(),
|
||||||
|
toolsMenu(),
|
||||||
|
aboutMenu()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu fileMenu(Faker faker) {
|
||||||
|
var fileMenu = new Menu("_File");
|
||||||
|
fileMenu.setMnemonicParsing(true);
|
||||||
|
fileMenu.setOnAction(PRINT_SOURCE);
|
||||||
|
|
||||||
|
var newMenu = menuItem(
|
||||||
|
"_New", null,
|
||||||
|
new KeyCodeCombination(KeyCode.N, KeyCombination.CONTROL_DOWN)
|
||||||
|
);
|
||||||
|
newMenu.setMnemonicParsing(true);
|
||||||
|
newMenu.setOnAction(PRINT_SOURCE);
|
||||||
|
|
||||||
|
var openRecentMenu = new Menu("Open _Recent");
|
||||||
|
openRecentMenu.setMnemonicParsing(true);
|
||||||
|
openRecentMenu.setOnAction(PRINT_SOURCE);
|
||||||
|
openRecentMenu.getItems().addAll(
|
||||||
|
IntStream.range(0, 10)
|
||||||
|
.mapToObj(x -> new MenuItem(faker.file().fileName()))
|
||||||
|
.toList()
|
||||||
|
);
|
||||||
|
|
||||||
|
fileMenu.getItems().addAll(
|
||||||
|
newMenu,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
menuItem(
|
||||||
|
"Open", Feather.FOLDER,
|
||||||
|
new KeyCodeCombination(KeyCode.O, KeyCombination.CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
openRecentMenu,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
menuItem(
|
||||||
|
"Save", Feather.SAVE,
|
||||||
|
new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
new MenuItem("Save As"),
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
new MenuItem("Exit")
|
||||||
|
);
|
||||||
|
return fileMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu editMenu() {
|
||||||
|
var editMenu = new Menu("_Edit");
|
||||||
|
editMenu.setMnemonicParsing(true);
|
||||||
|
editMenu.setOnAction(PRINT_SOURCE);
|
||||||
|
|
||||||
|
var copyItem = menuItem(
|
||||||
|
"Copy", Feather.COPY,
|
||||||
|
new KeyCodeCombination(KeyCode.C, KeyCombination.CONTROL_DOWN)
|
||||||
|
);
|
||||||
|
copyItem.setDisable(true);
|
||||||
|
|
||||||
|
editMenu.getItems().addAll(
|
||||||
|
menuItem(
|
||||||
|
"Undo", Feather.CORNER_DOWN_LEFT,
|
||||||
|
new KeyCodeCombination(KeyCode.Z, KeyCombination.CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
menuItem("Redo", Feather.CORNER_DOWN_RIGHT,
|
||||||
|
new KeyCodeCombination(KeyCode.Y, KeyCombination.CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
menuItem(
|
||||||
|
"Cut", Feather.SCISSORS,
|
||||||
|
new KeyCodeCombination(KeyCode.X, KeyCombination.CONTROL_DOWN)
|
||||||
|
),
|
||||||
|
copyItem,
|
||||||
|
menuItem(
|
||||||
|
"Paste", Feather.CORNER_DOWN_LEFT,
|
||||||
|
new KeyCodeCombination(KeyCode.V, KeyCombination.CONTROL_DOWN)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return editMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu viewMenu() {
|
||||||
|
var viewMenu = new Menu("_View");
|
||||||
|
viewMenu.setMnemonicParsing(true);
|
||||||
|
viewMenu.setOnAction(PRINT_SOURCE);
|
||||||
|
|
||||||
|
var showToolbarItem = new CheckMenuItem(
|
||||||
|
"Show Toolbar", new FontIcon(Feather.TOOL)
|
||||||
|
);
|
||||||
|
showToolbarItem.setSelected(true);
|
||||||
|
showToolbarItem.setAccelerator(
|
||||||
|
new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN)
|
||||||
|
);
|
||||||
|
|
||||||
|
var showGridItem = new CheckMenuItem(
|
||||||
|
"Show Grid", new FontIcon(Feather.GRID)
|
||||||
|
);
|
||||||
|
|
||||||
|
var captionItem = new CaptionMenuItem("Layout");
|
||||||
|
|
||||||
|
var viewToggleGroup = new ToggleGroup();
|
||||||
|
|
||||||
|
var toggleItem1 = new RadioMenuItem(
|
||||||
|
"Single", new FontIcon(Material2OutlinedAL.LOOKS_ONE)
|
||||||
|
);
|
||||||
|
toggleItem1.setSelected(true);
|
||||||
|
toggleItem1.setToggleGroup(viewToggleGroup);
|
||||||
|
|
||||||
|
var toggleItem2 = new RadioMenuItem(
|
||||||
|
"Two Columns", new FontIcon(Material2OutlinedAL.LOOKS_TWO)
|
||||||
|
);
|
||||||
|
toggleItem2.setToggleGroup(viewToggleGroup);
|
||||||
|
|
||||||
|
var toggleItem3 = new RadioMenuItem(
|
||||||
|
"Three Columns", new FontIcon(Material2OutlinedAL.LOOKS_3)
|
||||||
|
);
|
||||||
|
toggleItem3.setToggleGroup(viewToggleGroup);
|
||||||
|
|
||||||
|
viewMenu.getItems().addAll(
|
||||||
|
showToolbarItem,
|
||||||
|
showGridItem,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
captionItem,
|
||||||
|
toggleItem1,
|
||||||
|
toggleItem2,
|
||||||
|
toggleItem3
|
||||||
|
);
|
||||||
|
return viewMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu toolsMenu() {
|
||||||
|
var toolsMenu = new Menu("_Tools");
|
||||||
|
toolsMenu.setMnemonicParsing(true);
|
||||||
|
toolsMenu.setOnAction(PRINT_SOURCE);
|
||||||
|
toolsMenu.setDisable(true);
|
||||||
|
return toolsMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Menu aboutMenu() {
|
||||||
|
var aboutMenu = new Menu("_About", new FontIcon(Feather.HELP_CIRCLE));
|
||||||
|
aboutMenu.setMnemonicParsing(true);
|
||||||
|
aboutMenu.setOnAction(PRINT_SOURCE);
|
||||||
|
|
||||||
|
var deeplyNestedMenu = new Menu("Very...", null,
|
||||||
|
new Menu("Very...", null,
|
||||||
|
new Menu("Deeply", null,
|
||||||
|
new Menu("Nested", null,
|
||||||
|
new MenuItem("Menu")
|
||||||
|
))));
|
||||||
|
// NOTE: this won't be displayed because right container is reserved for submenu indication
|
||||||
|
deeplyNestedMenu.setAccelerator(new KeyCodeCombination(
|
||||||
|
KeyCode.DIGIT1, KeyCombination.SHIFT_DOWN, KeyCombination.CONTROL_DOWN)
|
||||||
|
);
|
||||||
|
|
||||||
|
aboutMenu.getItems().addAll(
|
||||||
|
new MenuItem("Help"),
|
||||||
|
new MenuItem("About"),
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
deeplyNestedMenu
|
||||||
|
);
|
||||||
|
return aboutMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MenuItem menuItem(String text, Ikon graphic, KeyCombination accelerator) {
|
||||||
|
var item = new MenuItem(text);
|
||||||
|
|
||||||
|
if (graphic != null) {
|
||||||
|
item.setGraphic(new FontIcon(graphic));
|
||||||
|
}
|
||||||
|
if (accelerator != null) {
|
||||||
|
item.setAccelerator(accelerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,19 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import static javafx.geometry.Orientation.VERTICAL;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Separator;
|
|
||||||
import javafx.scene.control.Tooltip;
|
import javafx.scene.control.Tooltip;
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.stage.PopupWindow.AnchorLocation;
|
import javafx.stage.PopupWindow.AnchorLocation;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
public class TooltipPage extends AbstractPage {
|
public class TooltipPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Tooltip";
|
public static final String NAME = "Tooltip";
|
||||||
|
|
||||||
@ -30,88 +25,87 @@ public class TooltipPage extends AbstractPage {
|
|||||||
|
|
||||||
public TooltipPage() {
|
public TooltipPage() {
|
||||||
super();
|
super();
|
||||||
setUserContent(new VBox(Page.PAGE_VGAP,
|
|
||||||
expandingHBox(
|
addFormattedText("""
|
||||||
basicSample(),
|
Tooltips are used for showing additional information when the node is hovered over by the mouse.
|
||||||
textWrapSample(),
|
|
||||||
indefiniteSample()
|
[ul]
|
||||||
),
|
[li]Any node can show a tooltip.[/li]
|
||||||
positionSample()
|
[li]A Tooltip is able to show within it an arbitrary scenegraph of nodes.[/li]
|
||||||
));
|
[li]A single tooltip can be installed on multiple target nodes or multiple controls.[/li][/ul]""");
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Position", positionExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock basicSample() {
|
private ExampleBox usageExample() {
|
||||||
var tooltip = new Tooltip(FAKER.harryPotter().spell());
|
//snippet_1:start
|
||||||
tooltip.setHideDelay(Duration.seconds(3));
|
var basicTtp = new Tooltip(FAKER.harryPotter().spell());
|
||||||
|
basicTtp.setHideDelay(Duration.seconds(3));
|
||||||
|
|
||||||
var label = createLabel("Hover me");
|
var basicLbl = createLabel("Basic");
|
||||||
label.setTooltip(tooltip);
|
basicLbl.setTooltip(basicTtp);
|
||||||
|
|
||||||
return new SampleBlock("Basic", label);
|
var longTtp = new Tooltip(FAKER.lorem().paragraph(5));
|
||||||
}
|
longTtp.setHideDelay(Duration.seconds(3));
|
||||||
|
longTtp.setPrefWidth(200);
|
||||||
|
longTtp.setWrapText(true);
|
||||||
|
|
||||||
private SampleBlock textWrapSample() {
|
var longLbl = createLabel("Long Text");
|
||||||
var tooltip = new Tooltip(FAKER.lorem().paragraph(5));
|
longLbl.setTooltip(longTtp);
|
||||||
tooltip.setHideDelay(Duration.seconds(3));
|
//snippet_1:end
|
||||||
tooltip.setPrefWidth(200);
|
|
||||||
tooltip.setWrapText(true);
|
|
||||||
|
|
||||||
var label = createLabel("Hover me");
|
var box = new HBox(HGAP_20, basicLbl, longLbl);
|
||||||
label.setTooltip(tooltip);
|
|
||||||
|
|
||||||
return new SampleBlock("Text Wrap", label);
|
var description = BBCodeParser.createFormattedText("""
|
||||||
}
|
In most cases a [i]Tooltip[/i] is created and its text property is \
|
||||||
|
modified to show plain text to the user."""
|
||||||
private SampleBlock indefiniteSample() {
|
|
||||||
var tooltip = new Tooltip(FAKER.harryPotter().spell());
|
|
||||||
tooltip.setHideDelay(Duration.INDEFINITE);
|
|
||||||
|
|
||||||
var label = createLabel("Hover me");
|
|
||||||
label.setTooltip(tooltip);
|
|
||||||
|
|
||||||
return new SampleBlock("Indefinite", label);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock positionSample() {
|
|
||||||
var topLeftLabel = createLabel("Top Left");
|
|
||||||
topLeftLabel.setTooltip(createTooltip("Top Left", AnchorLocation.WINDOW_BOTTOM_RIGHT));
|
|
||||||
|
|
||||||
var topRightLabel = createLabel("Top Right");
|
|
||||||
topRightLabel.setTooltip(createTooltip("Top Right", AnchorLocation.WINDOW_BOTTOM_LEFT));
|
|
||||||
|
|
||||||
var bottomLeftLabel = createLabel("Bottom Left");
|
|
||||||
bottomLeftLabel.setTooltip(createTooltip("Bottom Left", AnchorLocation.WINDOW_TOP_RIGHT));
|
|
||||||
|
|
||||||
var bottomRightLabel = createLabel("Bottom Right");
|
|
||||||
bottomRightLabel.setTooltip(createTooltip("Bottom Right", AnchorLocation.WINDOW_TOP_LEFT));
|
|
||||||
|
|
||||||
var flowPane = new FlowPane(
|
|
||||||
BLOCK_HGAP, BLOCK_VGAP,
|
|
||||||
topLeftLabel,
|
|
||||||
new Separator(VERTICAL),
|
|
||||||
topRightLabel,
|
|
||||||
new Separator(VERTICAL),
|
|
||||||
bottomLeftLabel,
|
|
||||||
new Separator(VERTICAL),
|
|
||||||
bottomRightLabel
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return new SampleBlock("Position", flowPane);
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox positionExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
// if the tooltip disappears almost immediately,
|
||||||
|
// it's a #javafx-bug (you'll never walk alone)
|
||||||
|
var topLeftTtp = new Tooltip(FAKER.chuckNorris().fact());
|
||||||
|
topLeftTtp.setAnchorLocation(AnchorLocation.WINDOW_BOTTOM_RIGHT);
|
||||||
|
var topLeftLbl = createLabel("Top Left");
|
||||||
|
topLeftLbl.setTooltip(topLeftTtp);
|
||||||
|
|
||||||
|
var topRightTtp = new Tooltip(FAKER.chuckNorris().fact());
|
||||||
|
topRightTtp.setAnchorLocation(AnchorLocation.WINDOW_BOTTOM_LEFT);
|
||||||
|
var topRightLbl = createLabel("Top Right");
|
||||||
|
topRightLbl.setTooltip(topRightTtp);
|
||||||
|
|
||||||
|
var bottomLeftTtp = new Tooltip(FAKER.chuckNorris().fact());
|
||||||
|
bottomLeftTtp.setAnchorLocation(AnchorLocation.WINDOW_TOP_RIGHT);
|
||||||
|
var bottomLeftLbl = createLabel("Bottom Left");
|
||||||
|
bottomLeftLbl.setTooltip(bottomLeftTtp);
|
||||||
|
|
||||||
|
var bottomRightTtp = new Tooltip(FAKER.chuckNorris().fact());
|
||||||
|
bottomRightTtp.setAnchorLocation(AnchorLocation.WINDOW_TOP_LEFT);
|
||||||
|
var bottomRightLbl = createLabel("Bottom Right");
|
||||||
|
bottomRightLbl.setTooltip(bottomRightTtp);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, topLeftLbl, topRightLbl, bottomLeftLbl, bottomRightLbl);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can specify the popup anchor point which is used in [i]Tooltip[/i] positioning. \
|
||||||
|
The point can be set to a corner of the popup window or a corner of its content."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Label createLabel(String text) {
|
private Label createLabel(String text) {
|
||||||
Label label = new Label(text);
|
Label label = new Label(text);
|
||||||
label.setMinWidth(50);
|
label.setMinWidth(100);
|
||||||
label.setMinHeight(50);
|
label.setMinHeight(50);
|
||||||
label.setPadding(new Insets(10));
|
label.setPadding(new Insets(10));
|
||||||
label.setStyle("-fx-background-color:-color-accent-subtle;");
|
label.setStyle("-fx-background-color:-color-accent-subtle;");
|
||||||
label.setAlignment(Pos.CENTER_LEFT);
|
label.setAlignment(Pos.CENTER);
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tooltip createTooltip(String text, AnchorLocation arrowPos) {
|
|
||||||
var tooltip = new Tooltip(text);
|
|
||||||
tooltip.setAnchorLocation(arrowPos);
|
|
||||||
return tooltip;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,318 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.DENSE;
|
|
||||||
import static atlantafx.base.theme.Styles.toggleStyleClass;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
|
||||||
import atlantafx.base.theme.Tweaks;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.CheckBoxTreeItem;
|
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.TreeItem;
|
|
||||||
import javafx.scene.control.TreeView;
|
|
||||||
import javafx.scene.control.cell.CheckBoxTreeCell;
|
|
||||||
import javafx.scene.control.cell.ChoiceBoxTreeCell;
|
|
||||||
import javafx.scene.control.cell.ComboBoxTreeCell;
|
|
||||||
import javafx.scene.control.cell.TextFieldTreeCell;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.util.StringConverter;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
public class TreePage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "TreeView";
|
|
||||||
private static final int MAX_TREE_DEPTH = 3;
|
|
||||||
private static final int[] TREE_DICE = {-1, 0, 1};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final BorderPane treeWrapper = new BorderPane();
|
|
||||||
private final ComboBox<Example> exampleSelect = createExampleSelect();
|
|
||||||
|
|
||||||
public TreePage() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
var sample = new SampleBlock("Playground", createPlayground());
|
|
||||||
sample.setFillHeight(true);
|
|
||||||
setUserContent(sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createPlayground() {
|
|
||||||
var denseToggle = new ToggleSwitch("Dense");
|
|
||||||
denseToggle.selectedProperty().addListener(
|
|
||||||
(obs, old, value) -> findDisplayedTree().ifPresent(tv -> toggleStyleClass(tv, DENSE))
|
|
||||||
);
|
|
||||||
|
|
||||||
var showRootToggle = new ToggleSwitch("Show root");
|
|
||||||
showRootToggle.selectedProperty().addListener((obs, old, val) -> findDisplayedTree().ifPresent(tv -> {
|
|
||||||
if (val != null) {
|
|
||||||
tv.setShowRoot(val);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
showRootToggle.setSelected(true);
|
|
||||||
|
|
||||||
var altIconToggle = new ToggleSwitch("Alt icon");
|
|
||||||
altIconToggle.selectedProperty().addListener((obs, old, val) ->
|
|
||||||
findDisplayedTree().ifPresent(tv -> toggleStyleClass(tv, Tweaks.ALT_ICON))
|
|
||||||
);
|
|
||||||
|
|
||||||
var edge2edgeToggle = new ToggleSwitch("Edge to edge");
|
|
||||||
edge2edgeToggle.selectedProperty().addListener(
|
|
||||||
(obs, old, val) -> findDisplayedTree().ifPresent(tv -> toggleStyleClass(tv, Tweaks.EDGE_TO_EDGE))
|
|
||||||
);
|
|
||||||
|
|
||||||
var disableToggle = new ToggleSwitch("Disable");
|
|
||||||
disableToggle.selectedProperty().addListener((obs, old, val) -> findDisplayedTree().ifPresent(tv -> {
|
|
||||||
if (val != null) {
|
|
||||||
tv.setDisable(val);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
var controls = new HBox(BLOCK_HGAP, denseToggle, showRootToggle, altIconToggle, edge2edgeToggle);
|
|
||||||
controls.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
VBox.setVgrow(treeWrapper, Priority.ALWAYS);
|
|
||||||
|
|
||||||
var playground = new VBox(
|
|
||||||
BLOCK_VGAP,
|
|
||||||
new HBox(new Label("Select an example:"), new Spacer(), disableToggle),
|
|
||||||
exampleSelect,
|
|
||||||
treeWrapper,
|
|
||||||
controls
|
|
||||||
);
|
|
||||||
playground.setMinHeight(100);
|
|
||||||
|
|
||||||
return playground;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ComboBox<Example> createExampleSelect() {
|
|
||||||
var select = new ComboBox<Example>();
|
|
||||||
select.setMaxWidth(Double.MAX_VALUE);
|
|
||||||
select.getItems().setAll(Example.values());
|
|
||||||
select.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeView<String> newTree = createTree(val);
|
|
||||||
|
|
||||||
// copy existing style classes and properties to the new tree
|
|
||||||
findDisplayedTree().ifPresent(tv -> {
|
|
||||||
List<String> currentStyles = tv.getStyleClass();
|
|
||||||
currentStyles.remove("tree-view");
|
|
||||||
newTree.getStyleClass().addAll(currentStyles);
|
|
||||||
|
|
||||||
newTree.setShowRoot(tv.isShowRoot());
|
|
||||||
newTree.setDisable(tv.isDisable());
|
|
||||||
});
|
|
||||||
|
|
||||||
treeWrapper.setCenter(newTree);
|
|
||||||
});
|
|
||||||
select.setConverter(new StringConverter<>() {
|
|
||||||
@Override
|
|
||||||
public String toString(Example example) {
|
|
||||||
return example == null ? "" : example.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Example fromString(String s) {
|
|
||||||
return Example.find(s);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return select;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onRendered() {
|
|
||||||
super.onRendered();
|
|
||||||
exampleSelect.getSelectionModel().selectFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<TreeView<?>> findDisplayedTree() {
|
|
||||||
return treeWrapper.getChildren().size() > 0
|
|
||||||
? Optional.of((TreeView<?>) treeWrapper.getChildren().get(0))
|
|
||||||
: Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeView<String> createTree(Example example) {
|
|
||||||
switch (example) {
|
|
||||||
case TEXT -> {
|
|
||||||
return stringTree();
|
|
||||||
}
|
|
||||||
case GRAPHIC -> {
|
|
||||||
return graphicTree();
|
|
||||||
}
|
|
||||||
case EDITABLE -> {
|
|
||||||
return editableTree();
|
|
||||||
}
|
|
||||||
case CHECK_BOX -> {
|
|
||||||
return checkBoxTree();
|
|
||||||
}
|
|
||||||
case CHOICE_BOX -> {
|
|
||||||
return choiceBoxTree();
|
|
||||||
}
|
|
||||||
case COMBO_BOX -> {
|
|
||||||
return comboBoxTree();
|
|
||||||
}
|
|
||||||
default -> throw new IllegalArgumentException("Unexpected enum value: " + example);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> void generateTree(TreeItem<T> parent, Supplier<TreeItem<T>> supplier, int limit, int depth) {
|
|
||||||
if (limit == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = supplier.get();
|
|
||||||
parent.getChildren().add(item);
|
|
||||||
|
|
||||||
TreeItem<T> nextParent = parent; // sibling
|
|
||||||
int nextDepth = depth;
|
|
||||||
int rand = TREE_DICE[RANDOM.nextInt(TREE_DICE.length)];
|
|
||||||
|
|
||||||
if (rand < 0 && parent.getParent() != null) { // go up
|
|
||||||
nextParent = parent.getParent();
|
|
||||||
nextDepth = --depth;
|
|
||||||
}
|
|
||||||
if (rand > 0 && depth < MAX_TREE_DEPTH) { // go down
|
|
||||||
nextParent = item;
|
|
||||||
nextDepth = ++depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
generateTree(nextParent, supplier, --limit, nextDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeView<String> stringTree() {
|
|
||||||
var root = new TreeItem<>("root");
|
|
||||||
root.setExpanded(true);
|
|
||||||
|
|
||||||
var tree = new TreeView<String>();
|
|
||||||
|
|
||||||
generateTree(root, () -> new TreeItem<>(FAKER.internet().domainWord()), 30, 1);
|
|
||||||
tree.setRoot(root);
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeView<String> graphicTree() {
|
|
||||||
var root = new TreeItem<>("root", new FontIcon(Feather.FOLDER));
|
|
||||||
root.setExpanded(true);
|
|
||||||
|
|
||||||
var tree = new TreeView<String>();
|
|
||||||
|
|
||||||
generateTree(root, () -> new TreeItem<>(FAKER.internet().domainWord(), new FontIcon(Feather.FILE)), 30, 1);
|
|
||||||
tree.setRoot(root);
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeView<String> editableTree() {
|
|
||||||
var root = new TreeItem<>("root", new FontIcon(Feather.FOLDER));
|
|
||||||
root.setExpanded(true);
|
|
||||||
|
|
||||||
var tree = new TreeView<String>();
|
|
||||||
tree.setCellFactory(TextFieldTreeCell.forTreeView());
|
|
||||||
tree.setEditable(true);
|
|
||||||
|
|
||||||
generateTree(root, () -> new TreeItem<>(FAKER.internet().domainWord(), new FontIcon(Feather.FILE)), 30, 1);
|
|
||||||
tree.setRoot(root);
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that CheckBoxTreeCell is incompatible with user graphic,
|
|
||||||
// because it adds graphic inside .checkbox container. #javafx-bug
|
|
||||||
private TreeView<String> checkBoxTree() {
|
|
||||||
var root = new CheckBoxTreeItem<>("root");
|
|
||||||
root.setExpanded(true);
|
|
||||||
|
|
||||||
var tree = new TreeView<String>();
|
|
||||||
tree.setCellFactory(CheckBoxTreeCell.forTreeView());
|
|
||||||
|
|
||||||
generateTree(root, () -> new CheckBoxTreeItem<>(FAKER.internet().domainWord()), 30, 1);
|
|
||||||
tree.setRoot(root);
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeView<String> choiceBoxTree() {
|
|
||||||
var root = new TreeItem<>("root");
|
|
||||||
root.setExpanded(true);
|
|
||||||
|
|
||||||
var tree = new TreeView<String>();
|
|
||||||
tree.setCellFactory(ChoiceBoxTreeCell.forTreeView(
|
|
||||||
generate(() -> FAKER.internet().domainWord(), 10).toArray(String[]::new)
|
|
||||||
));
|
|
||||||
tree.setEditable(true);
|
|
||||||
|
|
||||||
generateTree(root, () -> new TreeItem<>(FAKER.internet().domainWord()), 30, 1);
|
|
||||||
tree.setRoot(root);
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeView<String> comboBoxTree() {
|
|
||||||
var root = new TreeItem<>("root", new FontIcon(Feather.FOLDER));
|
|
||||||
root.setExpanded(true);
|
|
||||||
|
|
||||||
var tree = new TreeView<String>();
|
|
||||||
tree.setCellFactory(ComboBoxTreeCell.forTreeView(
|
|
||||||
generate(() -> FAKER.internet().domainWord(), 10).toArray(String[]::new)
|
|
||||||
));
|
|
||||||
tree.setEditable(true);
|
|
||||||
|
|
||||||
generateTree(root, () -> new TreeItem<>(FAKER.internet().domainWord(), new FontIcon(Feather.FILE)), 30, 1);
|
|
||||||
tree.setRoot(root);
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private enum Example {
|
|
||||||
TEXT("Text"),
|
|
||||||
GRAPHIC("Text with icons"),
|
|
||||||
EDITABLE("TextFieldTreeCell"),
|
|
||||||
CHECK_BOX("CheckBoxTreeCell"),
|
|
||||||
CHOICE_BOX("ChoiceBoxTreeCell"),
|
|
||||||
COMBO_BOX("ComboBoxTreeCell");
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
Example(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Example find(String name) {
|
|
||||||
return Arrays.stream(Example.values())
|
|
||||||
.filter(example -> Objects.equals(example.getName(), name))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,315 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.BORDERED;
|
|
||||||
import static atlantafx.base.theme.Styles.DENSE;
|
|
||||||
import static atlantafx.base.theme.Styles.STRIPED;
|
|
||||||
import static atlantafx.base.theme.Styles.toggleStyleClass;
|
|
||||||
import static atlantafx.base.theme.Tweaks.ALIGN_CENTER;
|
|
||||||
import static atlantafx.base.theme.Tweaks.ALIGN_LEFT;
|
|
||||||
import static atlantafx.base.theme.Tweaks.ALIGN_RIGHT;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.CaptionMenuItem;
|
|
||||||
import atlantafx.base.controls.Spacer;
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.base.theme.Tweaks;
|
|
||||||
import atlantafx.sampler.fake.domain.Product;
|
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.CheckMenuItem;
|
|
||||||
import javafx.scene.control.MenuButton;
|
|
||||||
import javafx.scene.control.RadioMenuItem;
|
|
||||||
import javafx.scene.control.SelectionMode;
|
|
||||||
import javafx.scene.control.SeparatorMenuItem;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.control.ToggleGroup;
|
|
||||||
import javafx.scene.control.TreeItem;
|
|
||||||
import javafx.scene.control.TreeTableColumn;
|
|
||||||
import javafx.scene.control.TreeTableView;
|
|
||||||
import javafx.scene.control.cell.CheckBoxTreeTableCell;
|
|
||||||
import javafx.scene.control.cell.ChoiceBoxTreeTableCell;
|
|
||||||
import javafx.scene.control.cell.ComboBoxTreeTableCell;
|
|
||||||
import javafx.scene.control.cell.TextFieldTreeTableCell;
|
|
||||||
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.util.Callback;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
public class TreeTablePage extends AbstractPage {
|
|
||||||
|
|
||||||
public static final String NAME = "TreeTableView";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeTableView<Product> treeTable;
|
|
||||||
|
|
||||||
public TreeTablePage() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
var sample = new SampleBlock("Playground", createPlayground());
|
|
||||||
sample.setFillHeight(true);
|
|
||||||
setUserContent(sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
private VBox createPlayground() {
|
|
||||||
// == FOOTER ==
|
|
||||||
|
|
||||||
var bordersToggle = new ToggleSwitch("Bordered");
|
|
||||||
bordersToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(treeTable, BORDERED));
|
|
||||||
|
|
||||||
var denseToggle = new ToggleSwitch("Dense");
|
|
||||||
denseToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(treeTable, DENSE));
|
|
||||||
|
|
||||||
var stripesToggle = new ToggleSwitch("Striped");
|
|
||||||
stripesToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(treeTable, STRIPED));
|
|
||||||
|
|
||||||
var altIconToggle = new ToggleSwitch("Alt icon");
|
|
||||||
altIconToggle.selectedProperty().addListener((obs, old, val) -> toggleStyleClass(treeTable, Tweaks.ALT_ICON));
|
|
||||||
|
|
||||||
var edge2edgeToggle = new ToggleSwitch("Edge to edge");
|
|
||||||
edge2edgeToggle.selectedProperty().addListener(
|
|
||||||
(obs, old, value) -> toggleStyleClass(treeTable, Tweaks.EDGE_TO_EDGE)
|
|
||||||
);
|
|
||||||
|
|
||||||
var footer = new HBox(BLOCK_HGAP, bordersToggle, denseToggle, stripesToggle, altIconToggle, edge2edgeToggle);
|
|
||||||
footer.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
// == TREE TABLE ==
|
|
||||||
|
|
||||||
var rootVal = Product.empty(0);
|
|
||||||
rootVal.setBrand("Root");
|
|
||||||
var root = new TreeItem<>(rootVal);
|
|
||||||
|
|
||||||
for (int idx = 1; idx <= FAKER.random().nextInt(5, 10); idx++) {
|
|
||||||
String brand = FAKER.commerce().brand();
|
|
||||||
var groupVal = Product.empty(0);
|
|
||||||
groupVal.setBrand(brand);
|
|
||||||
|
|
||||||
var group = new TreeItem<>(groupVal);
|
|
||||||
group.getChildren().setAll(
|
|
||||||
createTreeItems(idx * 100, FAKER.random().nextInt(5, 10), brand)
|
|
||||||
);
|
|
||||||
root.getChildren().add(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
treeTable = createTreeTable();
|
|
||||||
treeTable.setRoot(root);
|
|
||||||
VBox.setVgrow(treeTable, Priority.ALWAYS);
|
|
||||||
|
|
||||||
// == HEADER ==
|
|
||||||
|
|
||||||
var alignGroup = new ToggleGroup();
|
|
||||||
|
|
||||||
var alignLeftBtn = new ToggleButton("", new FontIcon(Feather.ALIGN_LEFT));
|
|
||||||
alignLeftBtn.getStyleClass().add(Styles.LEFT_PILL);
|
|
||||||
alignLeftBtn.setToggleGroup(alignGroup);
|
|
||||||
alignLeftBtn.setSelected(true);
|
|
||||||
alignLeftBtn.setOnAction(e -> {
|
|
||||||
for (TreeTableColumn<?, ?> c : treeTable.getColumns()) {
|
|
||||||
c.getStyleClass().removeAll(ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var alignCenterBtn = new ToggleButton("", new FontIcon(Feather.ALIGN_CENTER));
|
|
||||||
alignCenterBtn.getStyleClass().add(Styles.CENTER_PILL);
|
|
||||||
alignCenterBtn.setToggleGroup(alignGroup);
|
|
||||||
alignCenterBtn.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
for (TreeTableColumn<?, ?> c : treeTable.getColumns()) {
|
|
||||||
addStyleClass(c, ALIGN_CENTER, ALIGN_LEFT, ALIGN_RIGHT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var alignRightBtn = new ToggleButton("", new FontIcon(Feather.ALIGN_RIGHT));
|
|
||||||
alignRightBtn.getStyleClass().add(Styles.RIGHT_PILL);
|
|
||||||
alignRightBtn.setToggleGroup(alignGroup);
|
|
||||||
alignRightBtn.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
for (TreeTableColumn<?, ?> c : treeTable.getColumns()) {
|
|
||||||
addStyleClass(c, ALIGN_RIGHT, ALIGN_LEFT, ALIGN_CENTER);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var alignBox = new HBox(alignLeftBtn, alignCenterBtn, alignRightBtn);
|
|
||||||
|
|
||||||
var disableToggle = new ToggleSwitch("Disable");
|
|
||||||
disableToggle.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null) {
|
|
||||||
treeTable.setDisable(val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var header = new HBox(
|
|
||||||
createPropertiesMenu(treeTable),
|
|
||||||
new Spacer(),
|
|
||||||
alignBox,
|
|
||||||
new Spacer(),
|
|
||||||
disableToggle
|
|
||||||
);
|
|
||||||
header.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
var playground = new VBox(BLOCK_VGAP, header, treeTable, footer);
|
|
||||||
playground.setMinHeight(100);
|
|
||||||
|
|
||||||
return playground;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TreeItem<Product>> createTreeItems(int startId, int count, String brand) {
|
|
||||||
return IntStream.range(startId, startId + count + 1).boxed()
|
|
||||||
.map(id -> Product.random(id, brand, FAKER))
|
|
||||||
.map(TreeItem::new)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private TreeTableView<Product> createTreeTable() {
|
|
||||||
var arrowCol = new TreeTableColumn<Product, String>("#");
|
|
||||||
// This is placeholder column for disclosure nodes. We need to fill it
|
|
||||||
// with empty strings or all .tree-table-cell will be marked as :empty,
|
|
||||||
// which in turn leads to absent borders.
|
|
||||||
arrowCol.setCellValueFactory(cell -> new SimpleStringProperty(""));
|
|
||||||
arrowCol.setMinWidth(50);
|
|
||||||
arrowCol.setMaxWidth(50);
|
|
||||||
|
|
||||||
var stateCol = new TreeTableColumn<Product, Boolean>("Selected");
|
|
||||||
stateCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("state"));
|
|
||||||
stateCol.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(stateCol));
|
|
||||||
stateCol.setEditable(true);
|
|
||||||
|
|
||||||
var idCol = new TreeTableColumn<Product, String>("ID");
|
|
||||||
idCol.setCellValueFactory(cell -> {
|
|
||||||
Product product = cell.getValue().getValue();
|
|
||||||
return new SimpleStringProperty(
|
|
||||||
product != null && product.getId() != 0 ? String.valueOf(product.getId()) : ""
|
|
||||||
);
|
|
||||||
});
|
|
||||||
idCol.setEditable(false);
|
|
||||||
idCol.setMinWidth(80);
|
|
||||||
|
|
||||||
var brandCol = new TreeTableColumn<Product, String>("Brand 🖉");
|
|
||||||
brandCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("brand"));
|
|
||||||
brandCol.setCellFactory(ChoiceBoxTreeTableCell.forTreeTableColumn(
|
|
||||||
generate(() -> FAKER.commerce().brand(), 10).toArray(String[]::new)
|
|
||||||
));
|
|
||||||
brandCol.setEditable(true);
|
|
||||||
|
|
||||||
var nameCol = new TreeTableColumn<Product, String>("Name 🖉");
|
|
||||||
nameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
|
|
||||||
nameCol.setCellFactory(ComboBoxTreeTableCell.forTreeTableColumn(
|
|
||||||
generate(() -> FAKER.commerce().productName(), 10).toArray(String[]::new)
|
|
||||||
));
|
|
||||||
nameCol.setEditable(true);
|
|
||||||
nameCol.setMinWidth(200);
|
|
||||||
|
|
||||||
var priceCol = new TreeTableColumn<Product, String>("Price 🖉");
|
|
||||||
priceCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("price"));
|
|
||||||
priceCol.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
|
|
||||||
priceCol.setEditable(true);
|
|
||||||
|
|
||||||
var table = new TreeTableView<Product>();
|
|
||||||
table.getColumns().setAll(arrowCol, stateCol, brandCol, idCol, nameCol, priceCol);
|
|
||||||
|
|
||||||
return table;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MenuButton createPropertiesMenu(TreeTableView<Product> treeTable) {
|
|
||||||
final var resizePolicyCaption = new CaptionMenuItem("Resize Policy");
|
|
||||||
final var resizePolicyGroup = new ToggleGroup();
|
|
||||||
resizePolicyGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null && val.getUserData() instanceof Callback<?, ?> policy) {
|
|
||||||
//noinspection rawtypes,unchecked
|
|
||||||
treeTable.setColumnResizePolicy((Callback<TreeTableView.ResizeFeatures, Boolean>) policy);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final var unconstrainedResizeItem = new RadioMenuItem("Unconstrained");
|
|
||||||
unconstrainedResizeItem.setToggleGroup(resizePolicyGroup);
|
|
||||||
unconstrainedResizeItem.setUserData(TreeTableView.UNCONSTRAINED_RESIZE_POLICY);
|
|
||||||
unconstrainedResizeItem.setSelected(true);
|
|
||||||
|
|
||||||
final var constrainedResizeItem = new RadioMenuItem("Constrained");
|
|
||||||
constrainedResizeItem.setToggleGroup(resizePolicyGroup);
|
|
||||||
constrainedResizeItem.setUserData(TreeTableView.CONSTRAINED_RESIZE_POLICY);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
final var selectionModeCaption = new CaptionMenuItem("Selection Mode");
|
|
||||||
final var selectionModeGroup = new ToggleGroup();
|
|
||||||
selectionModeGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null && val.getUserData() instanceof SelectionMode mode) {
|
|
||||||
treeTable.getSelectionModel().setSelectionMode(mode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final var singleSelectionItem = new RadioMenuItem("Single");
|
|
||||||
singleSelectionItem.setToggleGroup(selectionModeGroup);
|
|
||||||
singleSelectionItem.setUserData(SelectionMode.SINGLE);
|
|
||||||
|
|
||||||
final var multiSelectionItem = new RadioMenuItem("Multiple");
|
|
||||||
multiSelectionItem.setToggleGroup(selectionModeGroup);
|
|
||||||
multiSelectionItem.setUserData(SelectionMode.MULTIPLE);
|
|
||||||
multiSelectionItem.setSelected(true);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
final var showRootItem = new CheckMenuItem("Show root");
|
|
||||||
treeTable.showRootProperty().bind(showRootItem.selectedProperty());
|
|
||||||
showRootItem.setSelected(true);
|
|
||||||
|
|
||||||
final var editCellsItem = new CheckMenuItem("Editable");
|
|
||||||
treeTable.editableProperty().bind(editCellsItem.selectedProperty());
|
|
||||||
editCellsItem.setSelected(true);
|
|
||||||
|
|
||||||
final var cellSelectionItem = new CheckMenuItem("Enable cell selection");
|
|
||||||
treeTable.getSelectionModel().cellSelectionEnabledProperty().bind(cellSelectionItem.selectedProperty());
|
|
||||||
cellSelectionItem.setSelected(false);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
final var menuButtonItem = new CheckMenuItem("Show menu button");
|
|
||||||
treeTable.tableMenuButtonVisibleProperty().bind(menuButtonItem.selectedProperty());
|
|
||||||
menuButtonItem.setSelected(true);
|
|
||||||
|
|
||||||
final var propertiesMenu = new MenuButton("Properties");
|
|
||||||
propertiesMenu.getItems().setAll(
|
|
||||||
resizePolicyCaption,
|
|
||||||
unconstrainedResizeItem,
|
|
||||||
constrainedResizeItem,
|
|
||||||
selectionModeCaption,
|
|
||||||
singleSelectionItem,
|
|
||||||
multiSelectionItem,
|
|
||||||
new SeparatorMenuItem(),
|
|
||||||
showRootItem,
|
|
||||||
editCellsItem,
|
|
||||||
cellSelectionItem,
|
|
||||||
menuButtonItem
|
|
||||||
);
|
|
||||||
|
|
||||||
return propertiesMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addStyleClass(TreeTableColumn<?, ?> c, String styleClass, String... excludes) {
|
|
||||||
Objects.requireNonNull(c);
|
|
||||||
Objects.requireNonNull(styleClass);
|
|
||||||
|
|
||||||
if (excludes != null && excludes.length > 0) {
|
|
||||||
c.getStyleClass().removeAll(excludes);
|
|
||||||
}
|
|
||||||
c.getStyleClass().add(styleClass);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,384 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import static atlantafx.base.theme.Styles.BORDERED;
|
||||||
|
import static atlantafx.base.theme.Styles.DENSE;
|
||||||
|
import static atlantafx.base.theme.Styles.STRIPED;
|
||||||
|
import static atlantafx.base.theme.Styles.toggleStyleClass;
|
||||||
|
import static atlantafx.base.theme.Tweaks.ALIGN_CENTER;
|
||||||
|
import static atlantafx.base.theme.Tweaks.ALIGN_LEFT;
|
||||||
|
import static atlantafx.base.theme.Tweaks.ALIGN_RIGHT;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.CaptionMenuItem;
|
||||||
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
|
import atlantafx.base.theme.Tweaks;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.fake.domain.Product;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.CheckMenuItem;
|
||||||
|
import javafx.scene.control.Menu;
|
||||||
|
import javafx.scene.control.MenuButton;
|
||||||
|
import javafx.scene.control.RadioMenuItem;
|
||||||
|
import javafx.scene.control.SelectionMode;
|
||||||
|
import javafx.scene.control.SeparatorMenuItem;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.control.TreeItem;
|
||||||
|
import javafx.scene.control.TreeTableColumn;
|
||||||
|
import javafx.scene.control.TreeTableView;
|
||||||
|
import javafx.scene.control.cell.CheckBoxTreeTableCell;
|
||||||
|
import javafx.scene.control.cell.ChoiceBoxTreeTableCell;
|
||||||
|
import javafx.scene.control.cell.ComboBoxTreeTableCell;
|
||||||
|
import javafx.scene.control.cell.TextFieldTreeTableCell;
|
||||||
|
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
|
||||||
|
public class TreeTableViewPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "TreeTableView";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeTableView<Product> treeTable;
|
||||||
|
|
||||||
|
public TreeTableViewPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
The [i]TreeTableView[/i] control is conceptually very similar to the \
|
||||||
|
[i]TreeView[/i] and [i]TableView[/i] controls and basically supports the \
|
||||||
|
same features. Please, see the corresponding pages for more examples.""");
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
record Vehicle(String brand, String model) {
|
||||||
|
|
||||||
|
public static Vehicle random(String make) {
|
||||||
|
return new Vehicle(make, FAKER.vehicle().model());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var col1 = new TreeTableColumn<Vehicle, String>("Brand");
|
||||||
|
var col2 = new TreeTableColumn<Vehicle, String>("Model");
|
||||||
|
|
||||||
|
col1.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().getValue().brand())
|
||||||
|
);
|
||||||
|
col2.setCellValueFactory(
|
||||||
|
c -> new SimpleStringProperty(c.getValue().getValue().model())
|
||||||
|
);
|
||||||
|
|
||||||
|
var treeTable = new TreeTableView<Vehicle>();
|
||||||
|
treeTable.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
treeTable.getColumns().setAll(col1, col2);
|
||||||
|
|
||||||
|
var make1 = FAKER.vehicle().make();
|
||||||
|
var treeItem1 = new TreeItem<Vehicle>(new Vehicle(make1, "..."));
|
||||||
|
treeItem1.getChildren().setAll(
|
||||||
|
new TreeItem<>(Vehicle.random(make1)),
|
||||||
|
new TreeItem<>(Vehicle.random(make1)),
|
||||||
|
new TreeItem<>(Vehicle.random(make1))
|
||||||
|
);
|
||||||
|
|
||||||
|
var make2 = FAKER.vehicle().make();
|
||||||
|
var treeItem2 = new TreeItem<Vehicle>(new Vehicle(make2, "..."));
|
||||||
|
treeItem2.getChildren().setAll(
|
||||||
|
new TreeItem<>(Vehicle.random(make1)),
|
||||||
|
new TreeItem<>(Vehicle.random(make1)),
|
||||||
|
new TreeItem<>(Vehicle.random(make1))
|
||||||
|
);
|
||||||
|
|
||||||
|
var rootItem = new TreeItem<Vehicle>(new Vehicle("Vehicles", "..."));
|
||||||
|
rootItem.getChildren().setAll(treeItem1, treeItem2);
|
||||||
|
rootItem.setExpanded(true);
|
||||||
|
|
||||||
|
treeTable.setRoot(rootItem);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
treeTable.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
treeTable.setMinHeight(300);
|
||||||
|
HBox.setHgrow(treeTable, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(treeTable);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can create a table view by instantiating the \
|
||||||
|
[font=monospace]javafx.scene.control.TreeTableView[/font] class."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private VBox playground() {
|
||||||
|
// == FOOTER ==
|
||||||
|
|
||||||
|
var borderToggle = new ToggleSwitch("Bordered");
|
||||||
|
borderToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> toggleStyleClass(treeTable, BORDERED)
|
||||||
|
);
|
||||||
|
|
||||||
|
var denseToggle = new ToggleSwitch("Dense");
|
||||||
|
denseToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> toggleStyleClass(treeTable, DENSE)
|
||||||
|
);
|
||||||
|
|
||||||
|
var stripeToggle = new ToggleSwitch("Striped");
|
||||||
|
stripeToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> toggleStyleClass(treeTable, STRIPED)
|
||||||
|
);
|
||||||
|
|
||||||
|
var altIconToggle = new ToggleSwitch("Alt icon");
|
||||||
|
altIconToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> toggleStyleClass(treeTable, Tweaks.ALT_ICON)
|
||||||
|
);
|
||||||
|
|
||||||
|
var edge2edgeToggle = new ToggleSwitch("Edge to edge");
|
||||||
|
edge2edgeToggle.selectedProperty().addListener(
|
||||||
|
(obs, old, value) -> toggleStyleClass(treeTable, Tweaks.EDGE_TO_EDGE)
|
||||||
|
);
|
||||||
|
|
||||||
|
var footer = new HBox(
|
||||||
|
HGAP_20, borderToggle, denseToggle, stripeToggle,
|
||||||
|
altIconToggle, edge2edgeToggle
|
||||||
|
);
|
||||||
|
footer.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
// == TREE TABLE ==
|
||||||
|
|
||||||
|
var rootVal = Product.empty(0);
|
||||||
|
rootVal.setBrand("Root");
|
||||||
|
var root = new TreeItem<>(rootVal);
|
||||||
|
|
||||||
|
for (int idx = 1; idx <= FAKER.random().nextInt(5, 10); idx++) {
|
||||||
|
String brand = FAKER.commerce().brand();
|
||||||
|
var groupVal = Product.empty(0);
|
||||||
|
groupVal.setBrand(brand);
|
||||||
|
|
||||||
|
var group = new TreeItem<>(groupVal);
|
||||||
|
group.getChildren().setAll(
|
||||||
|
createTreeItems(idx * 100, FAKER.random().nextInt(5, 10), brand)
|
||||||
|
);
|
||||||
|
root.getChildren().add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
treeTable = createTreeTable();
|
||||||
|
treeTable.setColumnResizePolicy(TreeTableView.UNCONSTRAINED_RESIZE_POLICY);
|
||||||
|
treeTable.setRoot(root);
|
||||||
|
VBox.setVgrow(treeTable, Priority.ALWAYS);
|
||||||
|
|
||||||
|
// == HEADER ==
|
||||||
|
|
||||||
|
var header = new HBox(createPropertiesMenu(treeTable));
|
||||||
|
header.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The playground demonstrates the most important [i]TreeTableView[/i] features \
|
||||||
|
and also serves as an object for monkey testing."""
|
||||||
|
);
|
||||||
|
|
||||||
|
var playground = new VBox(VGAP_10, description, header, treeTable, footer);
|
||||||
|
playground.setMinHeight(500);
|
||||||
|
|
||||||
|
return playground;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TreeItem<Product>> createTreeItems(int startId, int count, String brand) {
|
||||||
|
return IntStream.range(startId, startId + count + 1).boxed()
|
||||||
|
.map(id -> Product.random(id, brand, FAKER))
|
||||||
|
.map(TreeItem::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private TreeTableView<Product> createTreeTable() {
|
||||||
|
var arrowCol = new TreeTableColumn<Product, String>("#");
|
||||||
|
// This is placeholder column for disclosure nodes. We need to fill it
|
||||||
|
// with empty strings or each .tree-table-cell will be marked as :empty,
|
||||||
|
// which in turn leads to absent borders.
|
||||||
|
arrowCol.setCellValueFactory(cell -> new SimpleStringProperty(""));
|
||||||
|
arrowCol.setMinWidth(50);
|
||||||
|
arrowCol.setMaxWidth(50);
|
||||||
|
|
||||||
|
var stateCol = new TreeTableColumn<Product, Boolean>("State");
|
||||||
|
stateCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("state"));
|
||||||
|
stateCol.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(stateCol));
|
||||||
|
stateCol.setEditable(true);
|
||||||
|
|
||||||
|
var idCol = new TreeTableColumn<Product, String>("ID");
|
||||||
|
idCol.setCellValueFactory(cell -> {
|
||||||
|
Product product = cell.getValue().getValue();
|
||||||
|
return new SimpleStringProperty(
|
||||||
|
product != null && product.getId() != 0 ? String.valueOf(product.getId()) : ""
|
||||||
|
);
|
||||||
|
});
|
||||||
|
idCol.setEditable(false);
|
||||||
|
idCol.setMinWidth(80);
|
||||||
|
|
||||||
|
var brandCol = new TreeTableColumn<Product, String>("Brand");
|
||||||
|
brandCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("brand"));
|
||||||
|
brandCol.setCellFactory(ChoiceBoxTreeTableCell.forTreeTableColumn(
|
||||||
|
generate(() -> FAKER.commerce().brand(), 10).toArray(String[]::new)
|
||||||
|
));
|
||||||
|
brandCol.setEditable(true);
|
||||||
|
|
||||||
|
var nameCol = new TreeTableColumn<Product, String>("Name");
|
||||||
|
nameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
|
||||||
|
nameCol.setCellFactory(ComboBoxTreeTableCell.forTreeTableColumn(
|
||||||
|
generate(() -> FAKER.commerce().productName(), 10).toArray(String[]::new)
|
||||||
|
));
|
||||||
|
nameCol.setEditable(true);
|
||||||
|
nameCol.setMinWidth(200);
|
||||||
|
|
||||||
|
var priceCol = new TreeTableColumn<Product, String>("Price");
|
||||||
|
priceCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("price"));
|
||||||
|
priceCol.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
|
||||||
|
priceCol.setEditable(true);
|
||||||
|
|
||||||
|
var table = new TreeTableView<Product>();
|
||||||
|
table.getColumns().setAll(
|
||||||
|
arrowCol, stateCol, brandCol, idCol, nameCol, priceCol
|
||||||
|
);
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuButton createPropertiesMenu(TreeTableView<Product> treeTable) {
|
||||||
|
final var resizePolCaption = new CaptionMenuItem("Resize Policy");
|
||||||
|
final var resizePolicyGroup = new ToggleGroup();
|
||||||
|
resizePolicyGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val != null && val.getUserData() instanceof Callback<?, ?> policy) {
|
||||||
|
//noinspection rawtypes,unchecked
|
||||||
|
treeTable.setColumnResizePolicy((Callback<TreeTableView.ResizeFeatures, Boolean>) policy);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final var unconResizeItem = new RadioMenuItem("Unconstrained");
|
||||||
|
unconResizeItem.setToggleGroup(resizePolicyGroup);
|
||||||
|
unconResizeItem.setUserData(TreeTableView.UNCONSTRAINED_RESIZE_POLICY);
|
||||||
|
unconResizeItem.setSelected(true);
|
||||||
|
|
||||||
|
final var conResizeItem = new RadioMenuItem("Constrained");
|
||||||
|
conResizeItem.setToggleGroup(resizePolicyGroup);
|
||||||
|
conResizeItem.setUserData(TreeTableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
final var selModeCaption = new CaptionMenuItem("Selection Mode");
|
||||||
|
final var selModeGroup = new ToggleGroup();
|
||||||
|
selModeGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val != null && val.getUserData() instanceof SelectionMode mode) {
|
||||||
|
treeTable.getSelectionModel().setSelectionMode(mode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final var singleSelItem = new RadioMenuItem("Single");
|
||||||
|
singleSelItem.setToggleGroup(selModeGroup);
|
||||||
|
singleSelItem.setUserData(SelectionMode.SINGLE);
|
||||||
|
|
||||||
|
final var multiSelItem = new RadioMenuItem("Multiple");
|
||||||
|
multiSelItem.setToggleGroup(selModeGroup);
|
||||||
|
multiSelItem.setUserData(SelectionMode.MULTIPLE);
|
||||||
|
multiSelItem.setSelected(true);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
final var alignLeftItem = new RadioMenuItem("Left");
|
||||||
|
alignLeftItem.setSelected(true);
|
||||||
|
alignLeftItem.setOnAction(e -> {
|
||||||
|
for (TreeTableColumn<?, ?> c : treeTable.getColumns()) {
|
||||||
|
c.getStyleClass().removeAll(ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final var alignCenterItem = new RadioMenuItem("Center");
|
||||||
|
alignCenterItem.setOnAction(e -> {
|
||||||
|
for (TreeTableColumn<?, ?> c : treeTable.getColumns()) {
|
||||||
|
addStyleClass(c, ALIGN_CENTER, ALIGN_LEFT, ALIGN_RIGHT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final var alignRightItem = new RadioMenuItem("Right");
|
||||||
|
alignRightItem.setOnAction(e -> {
|
||||||
|
for (TreeTableColumn<?, ?> c : treeTable.getColumns()) {
|
||||||
|
addStyleClass(c, ALIGN_RIGHT, ALIGN_LEFT, ALIGN_CENTER);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final var alignItem = new Menu("Alignment");
|
||||||
|
alignItem.getItems().setAll(alignLeftItem, alignCenterItem, alignRightItem);
|
||||||
|
|
||||||
|
final var alignGroup = new ToggleGroup();
|
||||||
|
alignGroup.getToggles().setAll(alignLeftItem, alignCenterItem, alignRightItem);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
final var showRootItem = new CheckMenuItem("Show root");
|
||||||
|
treeTable.showRootProperty().bind(showRootItem.selectedProperty());
|
||||||
|
|
||||||
|
final var editItem = new CheckMenuItem("Editable");
|
||||||
|
treeTable.editableProperty().bind(editItem.selectedProperty());
|
||||||
|
editItem.setSelected(true);
|
||||||
|
|
||||||
|
final var cellSelItem = new CheckMenuItem("Enable cell selection");
|
||||||
|
treeTable.getSelectionModel().cellSelectionEnabledProperty().bind(cellSelItem.selectedProperty());
|
||||||
|
cellSelItem.setSelected(false);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
final var menuBtnItem = new CheckMenuItem("Show menu button");
|
||||||
|
treeTable.tableMenuButtonVisibleProperty().bind(menuBtnItem.selectedProperty());
|
||||||
|
menuBtnItem.setSelected(true);
|
||||||
|
|
||||||
|
final var propsMenu = new MenuButton("Properties");
|
||||||
|
propsMenu.getItems().setAll(
|
||||||
|
resizePolCaption,
|
||||||
|
unconResizeItem,
|
||||||
|
conResizeItem,
|
||||||
|
selModeCaption,
|
||||||
|
singleSelItem,
|
||||||
|
multiSelItem,
|
||||||
|
new SeparatorMenuItem(),
|
||||||
|
alignItem,
|
||||||
|
showRootItem,
|
||||||
|
editItem,
|
||||||
|
cellSelItem,
|
||||||
|
menuBtnItem
|
||||||
|
);
|
||||||
|
|
||||||
|
return propsMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStyleClass(TreeTableColumn<?, ?> c,
|
||||||
|
String styleClass,
|
||||||
|
String... excludes) {
|
||||||
|
Objects.requireNonNull(c);
|
||||||
|
Objects.requireNonNull(styleClass);
|
||||||
|
|
||||||
|
if (excludes != null && excludes.length > 0) {
|
||||||
|
c.getStyleClass().removeAll(excludes);
|
||||||
|
}
|
||||||
|
c.getStyleClass().add(styleClass);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,542 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.theme.Tweaks;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.Resources;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.CheckBoxTreeItem;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TreeItem;
|
||||||
|
import javafx.scene.control.TreeView;
|
||||||
|
import javafx.scene.control.cell.CheckBoxTreeCell;
|
||||||
|
import javafx.scene.control.cell.ChoiceBoxTreeCell;
|
||||||
|
import javafx.scene.control.cell.ComboBoxTreeCell;
|
||||||
|
import javafx.scene.control.cell.TextFieldTreeCell;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
public class TreeViewPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "TreeView";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeViewPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
The [i]TreeView[/i] provides a way to present tree structures. A tree has \
|
||||||
|
a root node which contains all the hierarchical values."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Dense", denseExample());
|
||||||
|
addSection("Alt Icon", altIconExample());
|
||||||
|
addSection("Edge-to-Edge", edge2EdgeExample());
|
||||||
|
addSection("Playground", playground());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var item1 = new TreeItem<>("Programming", icon("code"));
|
||||||
|
item1.getChildren().setAll(
|
||||||
|
new TreeItem<>("Kotlin", icon("kotlin")),
|
||||||
|
new TreeItem<>("Python", icon("python")),
|
||||||
|
new TreeItem<>("C++", icon("cplusplus"))
|
||||||
|
);
|
||||||
|
|
||||||
|
var item2 = new TreeItem<>("Databases", icon("database"));
|
||||||
|
item2.getChildren().setAll(
|
||||||
|
new TreeItem<>("PostgreSQL", icon("postgresql")),
|
||||||
|
new TreeItem<>("Redis", icon("redis")),
|
||||||
|
new TreeItem<>("MongoDB", icon("mongodb"))
|
||||||
|
);
|
||||||
|
|
||||||
|
var item3 = new TreeItem<>("Cloud", icon("cloud"));
|
||||||
|
item3.getChildren().setAll(
|
||||||
|
new TreeItem<>("Terraform", icon("terraform")),
|
||||||
|
new TreeItem<>("Azure", icon("azure")),
|
||||||
|
new TreeItem<>("Kubernetes", icon("kubernetes"))
|
||||||
|
);
|
||||||
|
|
||||||
|
var root = new TreeItem<>("Technologies");
|
||||||
|
root.setExpanded(true);
|
||||||
|
root.getChildren().addAll(item1, item2, item3);
|
||||||
|
|
||||||
|
var tree = new TreeView<>(root);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
tree.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
tree.setMinHeight(250);
|
||||||
|
HBox.setHgrow(tree, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(tree);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
In each tree the highest object in the hierarchy is called the "root". \
|
||||||
|
The root contains several child items, which can have children as well. \
|
||||||
|
An item without children is called "leaf".""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox denseExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
class Helper {
|
||||||
|
public static TreeItem<String> scan(File dir, int depth) {
|
||||||
|
var parent = new TreeItem<>(
|
||||||
|
dir.getName(),
|
||||||
|
new FontIcon(Feather.FOLDER)
|
||||||
|
);
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
depth--;
|
||||||
|
|
||||||
|
if (files != null) {
|
||||||
|
for (File f : files) {
|
||||||
|
if (f.isDirectory() && depth > 0) {
|
||||||
|
parent.getChildren().add(scan(f, depth));
|
||||||
|
} else {
|
||||||
|
var leaf = new TreeItem<>(
|
||||||
|
f.getName(),
|
||||||
|
new FontIcon(Feather.FILE)
|
||||||
|
);
|
||||||
|
parent.getChildren().add(leaf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent.getChildren().sort(
|
||||||
|
Comparator.comparing(TreeItem::getValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = Helper.scan(new File(System.getProperty("user.home")), 3);
|
||||||
|
|
||||||
|
var tree = new TreeView<>(root);
|
||||||
|
tree.getStyleClass().add(Styles.DENSE);
|
||||||
|
tree.setShowRoot(false);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
tree.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
tree.setMinHeight(350);
|
||||||
|
HBox.setHgrow(tree, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(tree);
|
||||||
|
var description = BBCodeParser.createFormattedText(
|
||||||
|
"The [i]TreeView[/i] rows can be made more compact by cutting tree cell padding."
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ExampleBox altIconExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var item1 = new TreeItem<>("Algorithms");
|
||||||
|
item1.getChildren().setAll(
|
||||||
|
new TreeItem<>("Bubble Sort"),
|
||||||
|
new TreeItem<>("Selection Sort"),
|
||||||
|
new TreeItem<>("Merge Sort")
|
||||||
|
);
|
||||||
|
|
||||||
|
var item2 = new TreeItem<>("Data Structures");
|
||||||
|
item2.getChildren().setAll(
|
||||||
|
new TreeItem<>("Stack"),
|
||||||
|
new TreeItem<>("Queue"),
|
||||||
|
new TreeItem<>("Linked List")
|
||||||
|
);
|
||||||
|
|
||||||
|
var item3 = new TreeItem<>("Artificial Intelligence");
|
||||||
|
item3.getChildren().setAll(
|
||||||
|
new TreeItem<>("Machine Learning"),
|
||||||
|
new TreeItem<>("Natural Language Processing"),
|
||||||
|
new TreeItem<>("Robotics")
|
||||||
|
);
|
||||||
|
|
||||||
|
var root = new TreeItem<>("Computer Science");
|
||||||
|
root.setExpanded(true);
|
||||||
|
root.getChildren().addAll(item1, item2, item3);
|
||||||
|
|
||||||
|
var tree = new TreeView<>(root);
|
||||||
|
tree.getStyleClass().add(Tweaks.ALT_ICON);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
tree.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
tree.setMinHeight(250);
|
||||||
|
HBox.setHgrow(tree, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(tree);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
There's additional tweak [code]Tweaks.ALT_ICON[/code] to change the [i]TreeView[/i] \
|
||||||
|
arrow icon."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private ExampleBox edge2EdgeExample() {
|
||||||
|
//snippet_4:start
|
||||||
|
var item1 = new TreeItem<>("Action");
|
||||||
|
item1.getChildren().setAll(
|
||||||
|
new TreeItem<>("Grand Theft Auto V"),
|
||||||
|
new TreeItem<>("Call of Duty: Modern Warfare"),
|
||||||
|
new TreeItem<>("Uncharted 4: A Thief's End")
|
||||||
|
);
|
||||||
|
|
||||||
|
var item2 = new TreeItem<>("Adventure");
|
||||||
|
item2.getChildren().setAll(
|
||||||
|
new TreeItem<>("The Legend of Zelda: Breath of the Wild"),
|
||||||
|
new TreeItem<>("Tomb Raider"),
|
||||||
|
new TreeItem<>("Minecraft")
|
||||||
|
);
|
||||||
|
|
||||||
|
var item3 = new TreeItem<>("Role-playing");
|
||||||
|
item3.getChildren().setAll(
|
||||||
|
new TreeItem<>("The Elder Scrolls V: Skyrim"),
|
||||||
|
new TreeItem<>("Fallout 4"),
|
||||||
|
new TreeItem<>("Final Fantasy XV")
|
||||||
|
);
|
||||||
|
|
||||||
|
var root = new TreeItem<>("Games");
|
||||||
|
root.setExpanded(true);
|
||||||
|
root.getChildren().addAll(item1, item2, item3);
|
||||||
|
|
||||||
|
var tree = new TreeView<>(root);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
tree.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
tree.setMinHeight(250);
|
||||||
|
HBox.setHgrow(tree, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var box = new HBox(tree);
|
||||||
|
box.setStyle("""
|
||||||
|
-fx-border-color: -color-accent-emphasis;
|
||||||
|
-fx-border-width: 2px;"""
|
||||||
|
);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Use [code]Tweaks.EDGE_TO_EDGE[/code] style class to remove the [i]TreeView[/i] outer borders. \
|
||||||
|
This is useful if you want to place the table into external container that already has its \
|
||||||
|
own borders."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageView icon(String name) {
|
||||||
|
var img = new Image(Resources.getResourceAsStream("images/devicons/" + name + ".png"));
|
||||||
|
return new ImageView(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Playground //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private static final int MAX_TREE_DEPTH = 3;
|
||||||
|
private static final int[] TREE_DICE = {-1, 0, 1};
|
||||||
|
|
||||||
|
private final BorderPane treeWrapper = new BorderPane();
|
||||||
|
private final ComboBox<Example> exampleSelect = createExampleSelect();
|
||||||
|
|
||||||
|
private VBox playground() {
|
||||||
|
var denseToggle = new ToggleSwitch("Dense");
|
||||||
|
denseToggle.selectedProperty().addListener((obs, old, value) ->
|
||||||
|
findDisplayedTree().ifPresent(tv -> Styles.toggleStyleClass(tv, Styles.DENSE))
|
||||||
|
);
|
||||||
|
|
||||||
|
var showRootToggle = new ToggleSwitch("Show root");
|
||||||
|
showRootToggle.selectedProperty().addListener((obs, old, val) ->
|
||||||
|
findDisplayedTree().ifPresent(tv -> {
|
||||||
|
if (val != null) {
|
||||||
|
tv.setShowRoot(val);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
showRootToggle.setSelected(true);
|
||||||
|
|
||||||
|
var altIconToggle = new ToggleSwitch("Alt icon");
|
||||||
|
altIconToggle.selectedProperty().addListener((obs, old, val) ->
|
||||||
|
findDisplayedTree().ifPresent(tv -> Styles.toggleStyleClass(tv, Tweaks.ALT_ICON))
|
||||||
|
);
|
||||||
|
|
||||||
|
var edge2edgeToggle = new ToggleSwitch("Edge to edge");
|
||||||
|
edge2edgeToggle.selectedProperty().addListener((obs, old, val) ->
|
||||||
|
findDisplayedTree().ifPresent(tv -> Styles.toggleStyleClass(tv, Tweaks.EDGE_TO_EDGE))
|
||||||
|
);
|
||||||
|
|
||||||
|
var toggles = new HBox(VGAP_20, denseToggle, showRootToggle, altIconToggle, edge2edgeToggle);
|
||||||
|
toggles.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The playground demonstrates the most important [i]TreeView[/i] features \
|
||||||
|
and also serves as an object for monkey testing."""
|
||||||
|
);
|
||||||
|
|
||||||
|
VBox.setVgrow(treeWrapper, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var playground = new VBox(
|
||||||
|
VGAP_10,
|
||||||
|
description,
|
||||||
|
new Label("Select an example:"),
|
||||||
|
exampleSelect,
|
||||||
|
treeWrapper,
|
||||||
|
toggles
|
||||||
|
);
|
||||||
|
playground.setMinHeight(500);
|
||||||
|
|
||||||
|
return playground;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComboBox<Example> createExampleSelect() {
|
||||||
|
var select = new ComboBox<Example>();
|
||||||
|
select.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
select.getItems().setAll(Example.values());
|
||||||
|
|
||||||
|
select.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeView<String> newTree = createTree(val);
|
||||||
|
|
||||||
|
// copy existing style classes and properties to the new tree
|
||||||
|
findDisplayedTree().ifPresent(tv -> {
|
||||||
|
List<String> currentStyles = tv.getStyleClass();
|
||||||
|
currentStyles.remove("tree-view");
|
||||||
|
newTree.getStyleClass().addAll(currentStyles);
|
||||||
|
newTree.setShowRoot(tv.isShowRoot());
|
||||||
|
newTree.setDisable(tv.isDisable());
|
||||||
|
});
|
||||||
|
|
||||||
|
treeWrapper.setCenter(newTree);
|
||||||
|
});
|
||||||
|
|
||||||
|
select.setConverter(new StringConverter<>() {
|
||||||
|
@Override
|
||||||
|
public String toString(Example example) {
|
||||||
|
return example == null ? "" : example.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Example fromString(String s) {
|
||||||
|
return Example.find(s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRendered() {
|
||||||
|
super.onRendered();
|
||||||
|
exampleSelect.getSelectionModel().selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<TreeView<?>> findDisplayedTree() {
|
||||||
|
return treeWrapper.getChildren().size() > 0
|
||||||
|
? Optional.of((TreeView<?>) treeWrapper.getChildren().get(0))
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeView<String> createTree(Example example) {
|
||||||
|
switch (example) {
|
||||||
|
case TEXT -> {
|
||||||
|
return stringTree();
|
||||||
|
}
|
||||||
|
case GRAPHIC -> {
|
||||||
|
return graphicTree();
|
||||||
|
}
|
||||||
|
case EDITABLE -> {
|
||||||
|
return editableTree();
|
||||||
|
}
|
||||||
|
case CHECK_BOX -> {
|
||||||
|
return checkBoxTree();
|
||||||
|
}
|
||||||
|
case CHOICE_BOX -> {
|
||||||
|
return choiceBoxTree();
|
||||||
|
}
|
||||||
|
case COMBO_BOX -> {
|
||||||
|
return comboBoxTree();
|
||||||
|
}
|
||||||
|
default -> throw new IllegalArgumentException("Unexpected enum value: " + example);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void generateTree(TreeItem<T> parent,
|
||||||
|
Supplier<TreeItem<T>> itemFactory,
|
||||||
|
int childLimit,
|
||||||
|
int depth) {
|
||||||
|
if (childLimit == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = itemFactory.get();
|
||||||
|
parent.getChildren().add(item);
|
||||||
|
|
||||||
|
TreeItem<T> nextParent = parent; // sibling
|
||||||
|
int nextDepth = depth;
|
||||||
|
int rand = TREE_DICE[RANDOM.nextInt(TREE_DICE.length)];
|
||||||
|
|
||||||
|
if (rand < 0 && parent.getParent() != null) { // go up
|
||||||
|
nextParent = parent.getParent();
|
||||||
|
nextDepth = --depth;
|
||||||
|
}
|
||||||
|
if (rand > 0 && depth < MAX_TREE_DEPTH) { // go down
|
||||||
|
nextParent = item;
|
||||||
|
nextDepth = ++depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateTree(nextParent, itemFactory, --childLimit, nextDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeView<String> stringTree() {
|
||||||
|
var root = new TreeItem<>("root");
|
||||||
|
root.setExpanded(true);
|
||||||
|
|
||||||
|
var tree = new TreeView<String>();
|
||||||
|
|
||||||
|
generateTree(root, () -> new TreeItem<>(
|
||||||
|
FAKER.internet().domainWord()), 30, 1
|
||||||
|
);
|
||||||
|
tree.setRoot(root);
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeView<String> graphicTree() {
|
||||||
|
var root = new TreeItem<>("root", new FontIcon(Feather.FOLDER));
|
||||||
|
root.setExpanded(true);
|
||||||
|
|
||||||
|
var tree = new TreeView<String>();
|
||||||
|
|
||||||
|
generateTree(root, () -> new TreeItem<>(
|
||||||
|
FAKER.internet().domainWord(), new FontIcon(Feather.FILE)), 30, 1
|
||||||
|
);
|
||||||
|
tree.setRoot(root);
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeView<String> editableTree() {
|
||||||
|
var root = new TreeItem<>("root", new FontIcon(Feather.FOLDER));
|
||||||
|
root.setExpanded(true);
|
||||||
|
|
||||||
|
var tree = new TreeView<String>();
|
||||||
|
tree.setCellFactory(TextFieldTreeCell.forTreeView());
|
||||||
|
tree.setEditable(true);
|
||||||
|
|
||||||
|
generateTree(root, () -> new TreeItem<>(
|
||||||
|
FAKER.internet().domainWord(), new FontIcon(Feather.FILE)), 30, 1
|
||||||
|
);
|
||||||
|
tree.setRoot(root);
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that CheckBoxTreeCell is incompatible with user graphic,
|
||||||
|
// because it adds graphic inside .checkbox container. #javafx-bug
|
||||||
|
private TreeView<String> checkBoxTree() {
|
||||||
|
var root = new CheckBoxTreeItem<>("root");
|
||||||
|
root.setExpanded(true);
|
||||||
|
|
||||||
|
var tree = new TreeView<String>();
|
||||||
|
tree.setCellFactory(CheckBoxTreeCell.forTreeView());
|
||||||
|
|
||||||
|
generateTree(root, () -> new CheckBoxTreeItem<>(
|
||||||
|
FAKER.internet().domainWord()), 30, 1
|
||||||
|
);
|
||||||
|
tree.setRoot(root);
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeView<String> choiceBoxTree() {
|
||||||
|
var root = new TreeItem<>("root");
|
||||||
|
root.setExpanded(true);
|
||||||
|
|
||||||
|
var tree = new TreeView<String>();
|
||||||
|
tree.setCellFactory(ChoiceBoxTreeCell.forTreeView(
|
||||||
|
generate(() -> FAKER.internet().domainWord(), 10).toArray(String[]::new)
|
||||||
|
));
|
||||||
|
tree.setEditable(true);
|
||||||
|
|
||||||
|
generateTree(root, () -> new TreeItem<>(
|
||||||
|
FAKER.internet().domainWord()), 30, 1
|
||||||
|
);
|
||||||
|
tree.setRoot(root);
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeView<String> comboBoxTree() {
|
||||||
|
var root = new TreeItem<>("root", new FontIcon(Feather.FOLDER));
|
||||||
|
root.setExpanded(true);
|
||||||
|
|
||||||
|
var tree = new TreeView<String>();
|
||||||
|
tree.setCellFactory(ComboBoxTreeCell.forTreeView(
|
||||||
|
generate(() -> FAKER.internet().domainWord(), 10).toArray(String[]::new)
|
||||||
|
));
|
||||||
|
tree.setEditable(true);
|
||||||
|
|
||||||
|
generateTree(root, () -> new TreeItem<>(
|
||||||
|
FAKER.internet().domainWord(), new FontIcon(Feather.FILE)), 30, 1
|
||||||
|
);
|
||||||
|
tree.setRoot(root);
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Example {
|
||||||
|
|
||||||
|
TEXT("Text"),
|
||||||
|
GRAPHIC("Text with icons"),
|
||||||
|
EDITABLE("TextFieldTreeCell"),
|
||||||
|
CHECK_BOX("CheckBoxTreeCell"),
|
||||||
|
CHOICE_BOX("ChoiceBoxTreeCell"),
|
||||||
|
COMBO_BOX("ComboBoxTreeCell");
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
Example(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Example find(String name) {
|
||||||
|
return Arrays.stream(Example.values())
|
||||||
|
.filter(example -> Objects.equals(example.getName(), name))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +1,18 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.base.util.BBCodeParser;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.scene.text.TextFlow;
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
public class BBCodePage extends AbstractPage {
|
public class BBCodePage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "BBCode Markup";
|
public static final String NAME = "BBCode Markup";
|
||||||
|
|
||||||
@ -25,59 +24,59 @@ public class BBCodePage extends AbstractPage {
|
|||||||
public BBCodePage() {
|
public BBCodePage() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
setUserContent(new VBox(20,
|
addFormattedText("""
|
||||||
reference(),
|
BBCode (Bulletin Board Code) is a lightweight markup language used to \
|
||||||
article()
|
format messages in many Internet forum software. The available tags of \
|
||||||
));
|
BBCode are indicated by square brackets surrounding a keyword, and are \
|
||||||
|
parsed before being translated into [s]HTML[/s] JavaFX layout :)""");
|
||||||
|
|
||||||
|
addSection("Text Type", textTypeReference());
|
||||||
|
addSection("Text Style", textStyleReference());
|
||||||
|
addSection("Subscript and Superscript", subscriptReference());
|
||||||
|
addSection("Headings", headingReference());
|
||||||
|
addSection("Links", linksReference());
|
||||||
|
addSection("Lists", listsReference());
|
||||||
|
addSection("Alignment", alignmentReference());
|
||||||
|
addSection("Indentation", indentReference());
|
||||||
|
addSection("Abbreviation", abbreviationReference());
|
||||||
|
addSection("Separator", hrReference());
|
||||||
|
addSection("Test Article", article());
|
||||||
}
|
}
|
||||||
|
|
||||||
private VBox reference() {
|
private ReferenceBlock textTypeReference() {
|
||||||
var root = new VBox(20);
|
|
||||||
root.setAlignment(Pos.TOP_LEFT);
|
|
||||||
|
|
||||||
var header = BBCodeParser.createFormattedText("""
|
|
||||||
[left][heading=1]Reference[/heading][/left]\
|
|
||||||
|
|
||||||
BBCode ("Bulletin Board Code") is a lightweight markup language used to format messages \
|
|
||||||
in many Internet forum software. It was first introduced in 1998. The available tags of BBCode \
|
|
||||||
are usually indicated by square brackets surrounding a keyword, and are parsed before \
|
|
||||||
being translated into [s]HTML[/s] JavaFX layout.""");
|
|
||||||
|
|
||||||
root.getChildren().add(header);
|
|
||||||
|
|
||||||
ReferenceBlock block = new ReferenceBlock(
|
ReferenceBlock block = new ReferenceBlock(
|
||||||
"Bold, italics, underline and strikethrough",
|
|
||||||
"Makes the wrapped text bold, italic, underlined, or strikethrough."
|
"Makes the wrapped text bold, italic, underlined, or strikethrough."
|
||||||
);
|
);
|
||||||
block.addFormattedText("This is [b]bold[/b] text.");
|
block.addFormattedText("This is [b]bold[/b] text.");
|
||||||
block.addFormattedText("This is [i]italic[/i] text.");
|
block.addFormattedText("This is [i]italic[/i] text.");
|
||||||
block.addFormattedText("This is [u]underlined[/u] text.");
|
block.addFormattedText("This is [u]underlined[/u] text.");
|
||||||
block.addFormattedText("This is [s]strikethrough[/s] text.");
|
block.addFormattedText("This is [s]strikethrough[/s] text.");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock textStyleReference() {
|
||||||
"Text color, font and size",
|
ReferenceBlock block = new ReferenceBlock("Changes the color, font, or size of the wrapped text.");
|
||||||
"Changes the color, font, or size of the wrapped text."
|
|
||||||
);
|
|
||||||
block.addFormattedText("This is [color=red]red[/color] text.");
|
block.addFormattedText("This is [color=red]red[/color] text.");
|
||||||
block.addFormattedText("This is [color=-color-accent-emphasis]accent[/color] text.");
|
block.addFormattedText("This is [color=-color-accent-emphasis]accent[/color] text.");
|
||||||
block.addFormattedText("This is [label style='-fx-background-color: yellow']background[/label] color.");
|
block.addFormattedText(
|
||||||
|
"This is [label style='-fx-background-color: -color-warning-muted']background[/label] color.");
|
||||||
block.addFormattedText("This is [font=monospace]monospaced[/font] font.");
|
block.addFormattedText("This is [font=monospace]monospaced[/font] font.");
|
||||||
|
block.addFormattedText("This is a [code]public[/code] Java keyword.");
|
||||||
block.addFormattedText("This is [small]small[/small] and [size=1.5em]big[/size] text.");
|
block.addFormattedText("This is [small]small[/small] and [size=1.5em]big[/size] text.");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock subscriptReference() {
|
||||||
"Subscript and superscript",
|
ReferenceBlock block = new ReferenceBlock(
|
||||||
"A text that is set slightly below or above the normal line of type, respectively."
|
"A text that is set slightly below or above the normal line of type, respectively."
|
||||||
);
|
);
|
||||||
block.addLayout("log[sub][small]2[/small][/sub](256) = 8");
|
block.addLayout("log[sub][small]2[/small][/sub](256) = 8");
|
||||||
block.addLayout("10[sup][small]2[/small][/sup] = 100");
|
block.addLayout("10[sup][small]2[/small][/sup] = 100");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock headingReference() {
|
||||||
"Headings levels 1 to 5",
|
ReferenceBlock block = new ReferenceBlock("Marks text as a structured heading.");
|
||||||
"Marks text as a structured heading."
|
|
||||||
);
|
|
||||||
block.addFormattedText("""
|
block.addFormattedText("""
|
||||||
[heading=1]H1 headline[/heading]
|
[heading=1]H1 headline[/heading]
|
||||||
|
|
||||||
@ -98,12 +97,11 @@ public class BBCodePage extends AbstractPage {
|
|||||||
[caption]Caption[/caption]
|
[caption]Caption[/caption]
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non purus a nisi ornare facilisis.""");
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non purus a nisi ornare facilisis.""");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock listsReference() {
|
||||||
"Lists",
|
ReferenceBlock block = new ReferenceBlock("Displays a bulleted or numbered list.");
|
||||||
"Displays a bulleted or numbered list."
|
|
||||||
);
|
|
||||||
block.addLayout("""
|
block.addLayout("""
|
||||||
[ul]
|
[ul]
|
||||||
[li]Entry 1[/li]
|
[li]Entry 1[/li]
|
||||||
@ -129,56 +127,51 @@ public class BBCodePage extends AbstractPage {
|
|||||||
[li]Entry 1[/li]
|
[li]Entry 1[/li]
|
||||||
[li]Entry 2[/li]
|
[li]Entry 2[/li]
|
||||||
[/ol]""");
|
[/ol]""");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock linksReference() {
|
||||||
"Linking",
|
ReferenceBlock block = new ReferenceBlock(
|
||||||
"Links the wrapped text to the specified web page or email address."
|
"Links the wrapped text to the specified web page or email address."
|
||||||
);
|
);
|
||||||
block.addFormattedText("[url=https://www.example.com]Go to example.com[/url]");
|
block.addFormattedText("[url=https://www.example.com]Go to example.com[/url]");
|
||||||
block.addFormattedText("[email=johndoe@example.com]Email me[/email]");
|
block.addFormattedText("[email=johndoe@example.com]Email me[/email]");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock alignmentReference() {
|
||||||
"Alignment",
|
ReferenceBlock block = new ReferenceBlock("Changes the alignment of the wrapped text.");
|
||||||
"Changes the alignment of the wrapped text."
|
|
||||||
);
|
|
||||||
block.addLayout("[left]Left-aligned[/left]");
|
block.addLayout("[left]Left-aligned[/left]");
|
||||||
block.addLayout("[center]Center-aligned[/center]");
|
block.addLayout("[center]Center-aligned[/center]");
|
||||||
block.addLayout("[right]Right-aligned[/right]");
|
block.addLayout("[right]Right-aligned[/right]");
|
||||||
block.addLayout("[align=center]Center-aligned[/align]");
|
block.addLayout("[align=center]Center-aligned[/align]");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock indentReference() {
|
||||||
"Text indent",
|
ReferenceBlock block = new ReferenceBlock("Indents the wrapped text.");
|
||||||
"Indents the wrapped text."
|
|
||||||
);
|
|
||||||
block.addLayout("[indent]Indented text[/indent]");
|
block.addLayout("[indent]Indented text[/indent]");
|
||||||
block.addLayout("[indent=3]More indented[/indent]");
|
block.addLayout("[indent=3]More indented[/indent]");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock hrReference() {
|
||||||
"Horizontal line",
|
ReferenceBlock block = new ReferenceBlock("A horizontal separator line.");
|
||||||
"A horizontal separator line."
|
|
||||||
);
|
|
||||||
block.addLayout("Default line: [hr/]");
|
block.addLayout("Default line: [hr/]");
|
||||||
block.addLayout("Thick line: [hr=5/]");
|
block.addLayout("Thick line: [hr=5/]");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
block = new ReferenceBlock(
|
private ReferenceBlock abbreviationReference() {
|
||||||
"Abbreviation",
|
ReferenceBlock block = new ReferenceBlock("An abbreviation, with mouse-over expansion.");
|
||||||
"An abbreviation, with mouse-over expansion."
|
|
||||||
);
|
|
||||||
block.addLayout("[abbr='on hover text']text[/abbr]");
|
block.addLayout("[abbr='on hover text']text[/abbr]");
|
||||||
block.addLayout("[abbr]text[/abbr]");
|
block.addLayout("[abbr]text[/abbr]");
|
||||||
root.getChildren().add(block);
|
return block;
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private VBox article() {
|
private VBox article() {
|
||||||
var article = """
|
var article = """
|
||||||
[left][heading=1]Example[/heading][/left]\
|
[left][heading=1]JavaFX - Overview[/heading][/left]\
|
||||||
|
|
||||||
[b]JavaFX[/b] is a Java library used to build Rich Internet Applications. \
|
[b]JavaFX[/b] is a Java library used to build Rich Internet Applications. \
|
||||||
The applications written using this library can run consistently across multiple \
|
The applications written using this library can run consistently across multiple \
|
||||||
@ -208,7 +201,8 @@ public class BBCodePage extends AbstractPage {
|
|||||||
full-featured application.[/li]\
|
full-featured application.[/li]\
|
||||||
[li][b]CSS like Styling[/b] − JavaFX provides a CSS like styling. By using this, you can improve \
|
[li][b]CSS like Styling[/b] − JavaFX provides a CSS like styling. By using this, you can improve \
|
||||||
the design of your application with a simple knowledge of CSS.[/li]\
|
the design of your application with a simple knowledge of CSS.[/li]\
|
||||||
[li][b]Canvas and Printing[/b] − JavaFX provides [label style=-fx-background-color:yellow]Canvas[/label], \
|
[li][b]Canvas and Printing[/b] − JavaFX provides \
|
||||||
|
[label style=-fx-background-color:-color-warning-muted]Canvas[/label], \
|
||||||
an immediate mode style of rendering API. Within the package [font=monospace]javafx.scene.canvas[/font] \
|
an immediate mode style of rendering API. Within the package [font=monospace]javafx.scene.canvas[/font] \
|
||||||
it holds a set of classes for canvas, using which we can draw directly within an area of the \
|
it holds a set of classes for canvas, using which we can draw directly within an area of the \
|
||||||
JavaFX scene. JavaFX also provides classes for Printing purposes in the package javafx.print.[/li]\
|
JavaFX scene. JavaFX also provides classes for Printing purposes in the package javafx.print.[/li]\
|
||||||
@ -231,10 +225,10 @@ public class BBCodePage extends AbstractPage {
|
|||||||
private final VBox leftBox;
|
private final VBox leftBox;
|
||||||
private final VBox rightBox;
|
private final VBox rightBox;
|
||||||
|
|
||||||
public ReferenceBlock(String title, String description) {
|
public ReferenceBlock(String description) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
var titleLabel = new Label(title);
|
var titleLabel = new Label(description);
|
||||||
titleLabel.getStyleClass().add(Styles.TITLE_4);
|
titleLabel.getStyleClass().add(Styles.TITLE_4);
|
||||||
|
|
||||||
leftBox = new VBox(15);
|
leftBox = new VBox(15);
|
||||||
@ -260,7 +254,7 @@ public class BBCodePage extends AbstractPage {
|
|||||||
splitBox.setSpacing(20);
|
splitBox.setSpacing(20);
|
||||||
|
|
||||||
setSpacing(10);
|
setSpacing(10);
|
||||||
getChildren().addAll(titleLabel, new Label(description), splitBox);
|
getChildren().addAll(new Label(description), splitBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addFormattedText(String markup) {
|
public void addFormattedText(String markup) {
|
@ -0,0 +1,182 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Breadcrumbs;
|
||||||
|
import atlantafx.base.controls.Breadcrumbs.BreadCrumbItem;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2AL;
|
||||||
|
|
||||||
|
public class BreadcrumbsPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "Breadcrumbs";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreadcrumbsPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
Represents a bread crumb bar. This control is useful to visualize and navigate \
|
||||||
|
a hierarchical path structure, such as file systems."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Custom Item", customItemExample());
|
||||||
|
addSection("Custom Divider", customDividerExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var items = generate(() -> FAKER.science().element(), 4);
|
||||||
|
BreadCrumbItem<String> root = Breadcrumbs.buildTreeModel(
|
||||||
|
items.toArray(String[]::new)
|
||||||
|
);
|
||||||
|
|
||||||
|
var crumbs = new Breadcrumbs<>(root);
|
||||||
|
crumbs.setSelectedCrumb(getTreeItemByIndex(root, 2));
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var nextBtn = new Button("Next");
|
||||||
|
nextBtn.getStyleClass().addAll(Styles.ACCENT);
|
||||||
|
nextBtn.setOnAction(e -> {
|
||||||
|
var selected = crumbs.getSelectedCrumb();
|
||||||
|
if (selected.getChildren().size() > 0) {
|
||||||
|
var next = selected.getChildren().get(0);
|
||||||
|
crumbs.setSelectedCrumb((BreadCrumbItem<String>) next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
crumbs.selectedCrumbProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nextBtn.setDisable(val.getChildren().isEmpty());
|
||||||
|
});
|
||||||
|
|
||||||
|
var box = new HBox(40, nextBtn, crumbs);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]BreadCrumbs[/i] uses the [i]TreeItem[/i] API to maintain its model. \
|
||||||
|
You can create a tree model from a flat list by using the static \
|
||||||
|
[code]buildTreeModel()[/code] method."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox customItemExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var items = generate(() -> FAKER.science().element(), 4);
|
||||||
|
BreadCrumbItem<String> root = Breadcrumbs.buildTreeModel(
|
||||||
|
items.toArray(String[]::new)
|
||||||
|
);
|
||||||
|
|
||||||
|
var crumbs = new Breadcrumbs<>(root);
|
||||||
|
crumbs.setCrumbFactory(crumb -> {
|
||||||
|
var btn = new Button(crumb.getValue(), new FontIcon(randomIcon()));
|
||||||
|
btn.getStyleClass().add(Styles.FLAT);
|
||||||
|
btn.setFocusTraversable(false);
|
||||||
|
return btn;
|
||||||
|
});
|
||||||
|
crumbs.setSelectedCrumb(getTreeItemByIndex(root, 2));
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var nextBtn = new Button("Next");
|
||||||
|
nextBtn.getStyleClass().addAll(Styles.ACCENT);
|
||||||
|
nextBtn.setOnAction(e -> {
|
||||||
|
var selected = crumbs.getSelectedCrumb();
|
||||||
|
if (selected.getChildren().size() > 0) {
|
||||||
|
var next = selected.getChildren().get(0);
|
||||||
|
crumbs.setSelectedCrumb((BreadCrumbItem<String>) next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
crumbs.selectedCrumbProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nextBtn.setDisable(val.getChildren().isEmpty());
|
||||||
|
});
|
||||||
|
|
||||||
|
var box = new HBox(40, nextBtn, crumbs);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
By default, the [i]Breadcrumbs[/i] uses [i]Hyperlink[/i] to represent \
|
||||||
|
its items. If you want to use a different kind of item, you can provide \
|
||||||
|
a [code]crumbFactory[/code] to insert an arbitrary [i]Node[/i]."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox customDividerExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var items = generate(() -> FAKER.science().element(), 4);
|
||||||
|
BreadCrumbItem<String> root = Breadcrumbs.buildTreeModel(
|
||||||
|
items.toArray(String[]::new)
|
||||||
|
);
|
||||||
|
|
||||||
|
var crumbs = new Breadcrumbs<>(root);
|
||||||
|
crumbs.setDividerFactory(item -> {
|
||||||
|
if (item == null) {
|
||||||
|
return new Label("", new FontIcon(Material2AL.HOME));
|
||||||
|
}
|
||||||
|
return !item.isLast()
|
||||||
|
? new Label("", new FontIcon(Material2AL.CHEVRON_RIGHT))
|
||||||
|
: null;
|
||||||
|
});
|
||||||
|
crumbs.setSelectedCrumb(getTreeItemByIndex(root, 2));
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var nextBtn = new Button("Next");
|
||||||
|
nextBtn.getStyleClass().addAll(Styles.ACCENT);
|
||||||
|
nextBtn.setOnAction(e -> {
|
||||||
|
var selected = crumbs.getSelectedCrumb();
|
||||||
|
if (selected.getChildren().size() > 0) {
|
||||||
|
var next = selected.getChildren().get(0);
|
||||||
|
crumbs.setSelectedCrumb((BreadCrumbItem<String>) next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
crumbs.selectedCrumbProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nextBtn.setDisable(val.getChildren().isEmpty());
|
||||||
|
});
|
||||||
|
|
||||||
|
var box = new HBox(40, nextBtn, crumbs);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Similarly, you can customize the [i]Breadcrumbs[/i] divider by \
|
||||||
|
providing a [code]dividerFactory[/code]."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> BreadCrumbItem<T> getTreeItemByIndex(BreadCrumbItem<T> node, int index) {
|
||||||
|
var counter = index;
|
||||||
|
var current = node;
|
||||||
|
while (counter > 0 && current.getParent() != null) {
|
||||||
|
current = (BreadCrumbItem<T>) current.getParent();
|
||||||
|
counter--;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,167 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.InlineDatePicker;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import atlantafx.sampler.theme.CSSFragment;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import javafx.animation.Animation;
|
||||||
|
import javafx.animation.KeyFrame;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.scene.control.DateCell;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
public class CalendarPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "Calendar";
|
||||||
|
private static final LocalDate TODAY = LocalDate.now(ZoneId.systemDefault());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalendarPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
The date picker control that allows the user to select a date. Unlike standard JavaFX \
|
||||||
|
[font=monospace]javafx.scene.control.DatePicker[/font] the [i]Calendar[/i] is not concealed \
|
||||||
|
within a popup window."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("No past dates", noPastDatesExample());
|
||||||
|
addSection("User slots", clockExample());
|
||||||
|
addSection("Style", styleExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var dp = new InlineDatePicker(TODAY);
|
||||||
|
dp.setShowWeekNumbers(true);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(dp);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
In the default state, no date is selected. You can modify this behavior \
|
||||||
|
either by using the constructor or by utilizing the [font=monospace]setValue()[/font] \
|
||||||
|
method.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox noPastDatesExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
class FutureDateCell extends DateCell {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateItem(LocalDate date, boolean empty) {
|
||||||
|
super.updateItem(date, empty);
|
||||||
|
setDisable(empty || date.isBefore(TODAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dp = new InlineDatePicker(TODAY);
|
||||||
|
dp.setDayCellFactory(c -> new FutureDateCell());
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(dp);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This example demonstrates how you can disable past dates in the [i]Calendar[/i]."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox clockExample() {
|
||||||
|
final var style = """
|
||||||
|
-fx-border-width: 0 0 0.5 0;
|
||||||
|
-fx-border-color: -color-border-default;""";
|
||||||
|
|
||||||
|
//snippet_3:start
|
||||||
|
class Clock extends VBox {
|
||||||
|
|
||||||
|
static final DateTimeFormatter DATE_FORMATTER =
|
||||||
|
DateTimeFormatter.ofPattern("EEEE, LLLL dd, yyyy");
|
||||||
|
static final DateTimeFormatter TIME_FORMATTER =
|
||||||
|
DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||||
|
|
||||||
|
public Clock() {
|
||||||
|
var clockLbl = new Label(TIME_FORMATTER.format(
|
||||||
|
LocalTime.now(ZoneId.systemDefault()))
|
||||||
|
);
|
||||||
|
clockLbl.getStyleClass().add(Styles.TITLE_2);
|
||||||
|
|
||||||
|
var dateLbl = new Label(DATE_FORMATTER.format(
|
||||||
|
LocalDate.now(ZoneId.systemDefault()))
|
||||||
|
);
|
||||||
|
|
||||||
|
// -fx-border-width: 0 0 0.5 0;
|
||||||
|
// -fx-border-color: -color-border-default;
|
||||||
|
setStyle(style);
|
||||||
|
setSpacing(10);
|
||||||
|
getChildren().setAll(clockLbl, dateLbl);
|
||||||
|
|
||||||
|
var t = new Timeline(new KeyFrame(
|
||||||
|
Duration.seconds(1),
|
||||||
|
e -> {
|
||||||
|
var time = LocalTime.now(ZoneId.systemDefault());
|
||||||
|
clockLbl.setText(TIME_FORMATTER.format(time));
|
||||||
|
}
|
||||||
|
));
|
||||||
|
t.setCycleCount(Animation.INDEFINITE);
|
||||||
|
t.playFromStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dp = new InlineDatePicker(TODAY);
|
||||||
|
dp.setTopNode(new Clock());
|
||||||
|
dp.setShowWeekNumbers(true);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(dp);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]Calendar[/i] comes equipped with two slots (top and bottom) where \
|
||||||
|
users can place their own content. For example, you can use these slots to \
|
||||||
|
display a clock widget on top of the [i]Calendar[/i]."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox styleExample() {
|
||||||
|
var style = """
|
||||||
|
.date-picker-popup {
|
||||||
|
-color-date-border: -color-accent-emphasis;
|
||||||
|
-color-date-month-year-bg: -color-accent-emphasis;
|
||||||
|
-color-date-month-year-fg: -color-fg-emphasis;
|
||||||
|
}""";
|
||||||
|
//snippet_4:start
|
||||||
|
var dp = new InlineDatePicker(TODAY);
|
||||||
|
dp.setShowWeekNumbers(true);
|
||||||
|
|
||||||
|
// -color-date-border: -color-accent-emphasis;
|
||||||
|
// -color-date-month-year-bg: -color-accent-emphasis;
|
||||||
|
// -color-date-month-year-fg: -color-fg-emphasis;
|
||||||
|
new CSSFragment(style).addTo(dp);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var box = new HBox(dp);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can alter the style of the [i]Calendar[/i] by using looked-up color variables."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.CustomTextField;
|
||||||
|
import atlantafx.base.controls.MaskTextField;
|
||||||
|
import atlantafx.base.controls.PasswordTextField;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Cursor;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
||||||
|
import org.kordamp.ikonli.material2.Material2OutlinedMZ;
|
||||||
|
|
||||||
|
public class CustomTextFieldPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "CustomTextField";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomTextFieldPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
A base class for placing nodes inside the text field itself, without being \
|
||||||
|
on top of the users typed-in text."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Color", colorExample());
|
||||||
|
addSection("Password", passwordSample());
|
||||||
|
addSection("Input Mask", inputMaskExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var tf1 = new CustomTextField();
|
||||||
|
tf1.setPromptText("Prompt text");
|
||||||
|
tf1.setRight(new FontIcon(Feather.X));
|
||||||
|
tf1.setPrefWidth(150);
|
||||||
|
|
||||||
|
var tf2 = new CustomTextField();
|
||||||
|
tf2.setPromptText("Prompt text");
|
||||||
|
tf2.setLeft(new FontIcon(Feather.MAP_PIN));
|
||||||
|
tf2.setPrefWidth(150);
|
||||||
|
|
||||||
|
var tf3 = new CustomTextField("Text");
|
||||||
|
tf3.setLeft(new FontIcon(Feather.MAP_PIN));
|
||||||
|
tf3.setRight(new FontIcon(Feather.X));
|
||||||
|
tf3.setPrefWidth(150);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(30, tf1, tf2, tf3);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can add arbitrary nodes to the [i]CustomTextField[/i] by setting \
|
||||||
|
the [code]left[/code] and [code]right[/code] properties, respectively."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox colorExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var tf1 = new CustomTextField("Text");
|
||||||
|
tf1.pseudoClassStateChanged(Styles.STATE_SUCCESS, true);
|
||||||
|
tf1.setRight(new FontIcon(Feather.X));
|
||||||
|
tf1.setPrefWidth(150);
|
||||||
|
|
||||||
|
var tf2 = new CustomTextField();
|
||||||
|
tf2.pseudoClassStateChanged(Styles.STATE_DANGER, true);
|
||||||
|
tf2.setLeft(new FontIcon(Feather.MAP_PIN));
|
||||||
|
tf2.setPrefWidth(150);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(30, tf1, tf2);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Use [code]Styles.STATE_SUCCESS[/code] or [code]Styles.STATE_DANGER[/code] \
|
||||||
|
pseudo-classes to change the CustomTextField/i] color. This especially useful to indicate \
|
||||||
|
the validation result."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox passwordSample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var tf = new PasswordTextField("qwerty");
|
||||||
|
tf.setPrefWidth(250);
|
||||||
|
|
||||||
|
var icon = new FontIcon(Feather.EYE_OFF);
|
||||||
|
icon.setCursor(Cursor.HAND);
|
||||||
|
icon.setOnMouseClicked(e -> {
|
||||||
|
icon.setIconCode(tf.isRevealPassword()
|
||||||
|
? Feather.EYE_OFF : Feather.EYE
|
||||||
|
);
|
||||||
|
tf.setRevealPassword(!tf.isRevealPassword());
|
||||||
|
});
|
||||||
|
tf.setRight(icon);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(30, tf);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]PasswordTextField[/i] is a variant of [i]CustomTextField[/i] that allows users \
|
||||||
|
to input passwords. Unlike the standard JavaFX [i]PasswordField[/i], the content of \
|
||||||
|
the [i]PasswordTextField[/i] can be revealed."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox inputMaskExample() {
|
||||||
|
//snippet_4:start
|
||||||
|
var phoneField = new MaskTextField("(999) 999 99 99");
|
||||||
|
phoneField.setPromptText("(999) 999 99 99");
|
||||||
|
phoneField.setLeft(new FontIcon(Material2OutlinedMZ.PHONE));
|
||||||
|
phoneField.setPrefWidth(180);
|
||||||
|
|
||||||
|
var cardField = new MaskTextField("9999-9999-9999-9999");
|
||||||
|
cardField.setLeft(new FontIcon(Material2OutlinedAL.CREDIT_CARD));
|
||||||
|
cardField.setPrefWidth(200);
|
||||||
|
|
||||||
|
var timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
|
||||||
|
var timeField = new MaskTextField("29:59");
|
||||||
|
timeField.setText(
|
||||||
|
LocalTime.now(ZoneId.systemDefault()).format(timeFormatter)
|
||||||
|
);
|
||||||
|
timeField.setLeft(new FontIcon(Material2OutlinedMZ.TIMER));
|
||||||
|
timeField.setPrefWidth(120);
|
||||||
|
timeField.textProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val != null) {
|
||||||
|
try {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
LocalTime.parse(val, timeFormatter);
|
||||||
|
timeField.pseudoClassStateChanged(Styles.STATE_DANGER, false);
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
|
timeField.pseudoClassStateChanged(Styles.STATE_DANGER, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var box = new HBox(
|
||||||
|
HGAP_20,
|
||||||
|
new VBox(5, new Label("Phone Number"), phoneField),
|
||||||
|
new VBox(5, new Label("Bank Card"), cardField),
|
||||||
|
new VBox(5, new Label("Time"), timeField)
|
||||||
|
);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]MaskTextField[/i] allows to restrict user input by applying a \
|
||||||
|
position-based mask. This is useful for editing cases where the input \
|
||||||
|
string has a fixed length and each character can be restricted based on its position."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
|
import atlantafx.base.layout.DeckPane;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.sampler.Resources;
|
||||||
|
import atlantafx.sampler.page.AbstractPage;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.RadioButton;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2AL;
|
||||||
|
import org.kordamp.ikonli.material2.Material2MZ;
|
||||||
|
|
||||||
|
public class DeckPanePage extends AbstractPage {
|
||||||
|
|
||||||
|
public static final String NAME = "DeckPane";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeckPanePage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
[i]DeckPane[/i] represents a pane that displays all of its child nodes in a deck, \
|
||||||
|
where only one node can be visible at a time. It does not maintain any sequence \
|
||||||
|
(model), but only cares about the top node, which can be changed by various \
|
||||||
|
transition effects.
|
||||||
|
|
||||||
|
Using the control is as simple as calling the [code]swipeX(Node)[/code] and \
|
||||||
|
[code]slideX(Node)[/code] methods, where [code]X[/code] represents the direction \
|
||||||
|
of the transition that you want to achieve.""");
|
||||||
|
addNode(createGallery());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node createGallery() {
|
||||||
|
var galleryPane = new BorderPane();
|
||||||
|
galleryPane.setMinSize(570, 400);
|
||||||
|
galleryPane.setMaxSize(570, 400);
|
||||||
|
|
||||||
|
var image1 = new ImageView(new Image(
|
||||||
|
Resources.getResourceAsStream("images/gallery/kush-dwivedi-unsplash.jpg"))
|
||||||
|
);
|
||||||
|
image1.setFitWidth(450);
|
||||||
|
image1.setFitHeight(300);
|
||||||
|
|
||||||
|
var image2 = new ImageView(new Image(
|
||||||
|
Resources.getResourceAsStream("images/gallery/markus-spiske-unsplash.jpg"))
|
||||||
|
);
|
||||||
|
image2.setFitWidth(450);
|
||||||
|
image2.setFitHeight(300);
|
||||||
|
|
||||||
|
var image3 = new ImageView(new Image(
|
||||||
|
Resources.getResourceAsStream("images/gallery/r0m0_4-unsplash.jpg"))
|
||||||
|
);
|
||||||
|
image3.setFitWidth(450);
|
||||||
|
image3.setFitHeight(300);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
var transitionTypeBox = new VBox(VGAP_10);
|
||||||
|
transitionTypeBox.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var label = new Label("Transition Type");
|
||||||
|
label.getStyleClass().addAll(Styles.TEXT_CAPTION, Styles.TEXT_MUTED);
|
||||||
|
|
||||||
|
var swipeRadio = new RadioButton("Swipe");
|
||||||
|
swipeRadio.setSelected(true);
|
||||||
|
swipeRadio.setUserData(-1);
|
||||||
|
|
||||||
|
var slideRadio = new RadioButton("Slide");
|
||||||
|
slideRadio.setUserData(1);
|
||||||
|
|
||||||
|
var transitionType = new ToggleGroup();
|
||||||
|
transitionType.getToggles().setAll(swipeRadio, slideRadio);
|
||||||
|
|
||||||
|
var radioBox = new HBox(HGAP_20, swipeRadio, slideRadio);
|
||||||
|
radioBox.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
transitionTypeBox.getChildren().setAll(label, radioBox);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
var deck = new DeckPane(image1, image2, image3);
|
||||||
|
deck.setMinSize(450, 300);
|
||||||
|
deck.setMaxSize(450, 300);
|
||||||
|
deck.setAnimationDuration(Duration.millis(350));
|
||||||
|
galleryPane.setCenter(deck);
|
||||||
|
|
||||||
|
// circularly returns the next item from the deck
|
||||||
|
Supplier<Node> nextItem = () -> {
|
||||||
|
var next = (deck.getChildren().indexOf(deck.getTopNode()) + 1)
|
||||||
|
% deck.getChildren().size();
|
||||||
|
return deck.getChildren().get(next);
|
||||||
|
};
|
||||||
|
|
||||||
|
var topBtn = new Button("", new FontIcon(Material2MZ.NORTH));
|
||||||
|
topBtn.setOnAction(e -> {
|
||||||
|
if ((int) transitionType.getSelectedToggle().getUserData() < 0) {
|
||||||
|
deck.swipeUp(nextItem.get());
|
||||||
|
} else {
|
||||||
|
deck.slideUp(nextItem.get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
galleryPane.setTop(topBtn);
|
||||||
|
BorderPane.setAlignment(topBtn, Pos.CENTER);
|
||||||
|
|
||||||
|
var rightBtn = new Button("", new FontIcon(Material2AL.EAST));
|
||||||
|
rightBtn.setOnAction(e -> {
|
||||||
|
if ((int) transitionType.getSelectedToggle().getUserData() < 0) {
|
||||||
|
deck.swipeRight(nextItem.get());
|
||||||
|
} else {
|
||||||
|
deck.slideRight(nextItem.get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
galleryPane.setRight(rightBtn);
|
||||||
|
BorderPane.setAlignment(rightBtn, Pos.CENTER);
|
||||||
|
|
||||||
|
var bottomBtn = new Button("", new FontIcon(Material2MZ.SOUTH));
|
||||||
|
bottomBtn.setOnAction(e -> {
|
||||||
|
if ((int) transitionType.getSelectedToggle().getUserData() < 0) {
|
||||||
|
deck.swipeDown(nextItem.get());
|
||||||
|
} else {
|
||||||
|
deck.slideDown(nextItem.get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
galleryPane.setBottom(bottomBtn);
|
||||||
|
BorderPane.setAlignment(bottomBtn, Pos.CENTER);
|
||||||
|
|
||||||
|
var leftBtn = new Button("", new FontIcon(Material2MZ.WEST));
|
||||||
|
leftBtn.setOnAction(e -> {
|
||||||
|
if ((int) transitionType.getSelectedToggle().getUserData() < 0) {
|
||||||
|
deck.swipeLeft(nextItem.get());
|
||||||
|
} else {
|
||||||
|
deck.slideLeft(nextItem.get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
galleryPane.setLeft(leftBtn);
|
||||||
|
BorderPane.setAlignment(leftBtn, Pos.CENTER);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var root = new VBox(VGAP_20, galleryPane, transitionTypeBox);
|
||||||
|
root.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
224
sampler/src/main/java/atlantafx/sampler/page/extras/InputGroupPage.java
Executable file
224
sampler/src/main/java/atlantafx/sampler/page/extras/InputGroupPage.java
Executable file
@ -0,0 +1,224 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.CheckBox;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.MenuButton;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
public class InputGroupPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "Input Group";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputGroupPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
You can use the following utility classes: [code]Styles.LEFT_PILL[/code], \
|
||||||
|
[code]Styles.CENTER_PILL[/code], and [code]Styles.RIGHT_PILL[/code] to combine \
|
||||||
|
various input controls into input groups that allow them to appear as a single \
|
||||||
|
control. This is entirely a CSS feature and does not require any additional wrappers.""");
|
||||||
|
addSection("ComboBox", comboBoxExample());
|
||||||
|
addSection("Button", buttonExample());
|
||||||
|
addSection("Text Field", textFieldExample());
|
||||||
|
addSection("MenuButton", menuButtonExample());
|
||||||
|
addSection("Label", labelExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox comboBoxExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var leftCmb = new ComboBox<>();
|
||||||
|
leftCmb.getItems().addAll("POST", "GET", "PUT", "PATCH", "DELETE");
|
||||||
|
leftCmb.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
leftCmb.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
var rightTfd = new TextField("https://example.org");
|
||||||
|
rightTfd.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
HBox.setHgrow(rightTfd, Priority.ALWAYS);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(leftCmb, rightTfd);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
box.setMinWidth(400);
|
||||||
|
box.setMaxWidth(400);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This example demonstrates how a [i]ComboBox[/i] can be combined \
|
||||||
|
with a [i]TextField[/i].""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox buttonExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var leftTfd = new TextField();
|
||||||
|
leftTfd.setText(FAKER.internet().password());
|
||||||
|
leftTfd.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
HBox.setHgrow(leftTfd, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var rightBtn = new Button(
|
||||||
|
"", new FontIcon(Feather.REFRESH_CW)
|
||||||
|
);
|
||||||
|
rightBtn.getStyleClass().addAll(Styles.BUTTON_ICON);
|
||||||
|
rightBtn.setOnAction(
|
||||||
|
e -> leftTfd.setText(FAKER.internet().password())
|
||||||
|
);
|
||||||
|
rightBtn.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(leftTfd, rightBtn);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
box.setMinWidth(400);
|
||||||
|
box.setMaxWidth(400);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This example demonstrates how a [i]Button[/i] can be combined \
|
||||||
|
with a [i]TextField[/i].""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox textFieldExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var leftTfd = new TextField("192.168.1.10");
|
||||||
|
leftTfd.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
|
||||||
|
var centerTfd = new TextField("24");
|
||||||
|
centerTfd.getStyleClass().add(Styles.CENTER_PILL);
|
||||||
|
centerTfd.setPrefWidth(70);
|
||||||
|
|
||||||
|
var rightTfd = new TextField("192.168.1.1");
|
||||||
|
rightTfd.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(leftTfd, centerTfd, rightTfd);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
box.setMinWidth(400);
|
||||||
|
box.setMaxWidth(400);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This example demonstrates how a multiple [i]TextField[/i]'s can be \
|
||||||
|
combined into a input group.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox menuButtonExample() {
|
||||||
|
//snippet_4:start
|
||||||
|
var rightTfd = new TextField(FAKER.harryPotter().spell());
|
||||||
|
rightTfd.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
HBox.setHgrow(rightTfd, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var spellItem = new MenuItem("Spell");
|
||||||
|
spellItem.setOnAction(
|
||||||
|
e -> rightTfd.setText(FAKER.harryPotter().spell())
|
||||||
|
);
|
||||||
|
|
||||||
|
var characterItem = new MenuItem("Character");
|
||||||
|
characterItem.setOnAction(
|
||||||
|
e -> rightTfd.setText(FAKER.harryPotter().character())
|
||||||
|
);
|
||||||
|
|
||||||
|
var locationItem = new MenuItem("Location");
|
||||||
|
locationItem.setOnAction(
|
||||||
|
e -> rightTfd.setText(FAKER.harryPotter().location())
|
||||||
|
);
|
||||||
|
|
||||||
|
var leftMenu = new MenuButton("Dropdown");
|
||||||
|
leftMenu.getItems().addAll(spellItem, characterItem, locationItem);
|
||||||
|
leftMenu.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var box = new HBox(leftMenu, rightTfd);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
box.setMinWidth(400);
|
||||||
|
box.setMaxWidth(400);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This example demonstrates how a [i]MenuButton[/i] can be combined \
|
||||||
|
with a [i]TextField[/i].""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox labelExample() {
|
||||||
|
//snippet_5:start
|
||||||
|
var leftLbl1 = new Label("", new CheckBox());
|
||||||
|
leftLbl1.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
|
||||||
|
var rightTfd1 = new TextField();
|
||||||
|
rightTfd1.setPromptText("Username");
|
||||||
|
rightTfd1.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
HBox.setHgrow(rightTfd1, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var sample1 = new HBox(leftLbl1, rightTfd1);
|
||||||
|
sample1.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var leftTfd2 = new TextField("johndoe");
|
||||||
|
leftTfd2.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
HBox.setHgrow(leftTfd2, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var centerLbl2 = new Label("@");
|
||||||
|
centerLbl2.setMinWidth(50);
|
||||||
|
centerLbl2.setAlignment(Pos.CENTER);
|
||||||
|
centerLbl2.getStyleClass().add(Styles.CENTER_PILL);
|
||||||
|
|
||||||
|
var rightTfd2 = new TextField("gmail.com");
|
||||||
|
rightTfd2.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
HBox.setHgrow(rightTfd2, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var sample2 = new HBox(leftTfd2, centerLbl2, rightTfd2);
|
||||||
|
sample2.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var leftTfd3 = new TextField("+123456");
|
||||||
|
leftTfd3.getStyleClass().add(Styles.LEFT_PILL);
|
||||||
|
HBox.setHgrow(leftTfd3, Priority.ALWAYS);
|
||||||
|
|
||||||
|
var rightLbl3 = new Label("", new FontIcon(Feather.DOLLAR_SIGN));
|
||||||
|
rightLbl3.getStyleClass().add(Styles.RIGHT_PILL);
|
||||||
|
|
||||||
|
var sample3 = new HBox(leftTfd3, rightLbl3);
|
||||||
|
sample3.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
|
sample1.setMinWidth(400);
|
||||||
|
sample1.setMaxWidth(400);
|
||||||
|
|
||||||
|
sample2.setMinWidth(400);
|
||||||
|
sample2.setMaxWidth(400);
|
||||||
|
|
||||||
|
sample3.setMinWidth(400);
|
||||||
|
sample3.setMaxWidth(400);
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_20, sample1, sample2, sample3);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This example demonstrates how a [i]Label[/i] can be used \
|
||||||
|
in combination with various controls.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 5), description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,366 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.ModalPane;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.Resources;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.geometry.Side;
|
||||||
|
import javafx.scene.Cursor;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
|
public class ModalPanePage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "Modal Pane";
|
||||||
|
|
||||||
|
private final ModalPane modalPane = new ModalPane();
|
||||||
|
private final ModalPane modalPaneTop = new ModalPane(-15);
|
||||||
|
private final ModalPane modalPaneTopmost = new ModalPane(-20);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModalPanePage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// add modal pane to the root container, which is StackPane
|
||||||
|
getChildren().addAll(modalPane, modalPaneTop, modalPaneTopmost);
|
||||||
|
|
||||||
|
// reset side and transition to reuse a single modal pane between different examples
|
||||||
|
modalPane.displayProperty().addListener((obs, old, val) -> {
|
||||||
|
if (!val) {
|
||||||
|
modalPane.setAlignment(Pos.CENTER);
|
||||||
|
modalPane.usePredefinedTransitionFactories(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
A container for displaying application dialogs ot top of the current scene \
|
||||||
|
without opening a modal {@link javafx.stage.Stage}. It's a translucent (glass) pane \
|
||||||
|
that can hold arbitrary content as well as animate its appearance.""");
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Content Position", contentPositionExample());
|
||||||
|
addSection("Persistent", persistentExample());
|
||||||
|
addSection("Nesting", nestingExample());
|
||||||
|
addSection("Maximized", maximizedExample());
|
||||||
|
addSection("Overflowed", overflowedExample());
|
||||||
|
addSection("Lightbox", lightboxExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var dialog = new Dialog(450, 450);
|
||||||
|
|
||||||
|
var openBtn = new Button("Open Dialog");
|
||||||
|
openBtn.setOnAction(evt -> modalPane.show(dialog));
|
||||||
|
|
||||||
|
var closeBtn = new Button("Close");
|
||||||
|
closeBtn.setOnAction(evt -> modalPane.hide(true));
|
||||||
|
dialog.getChildren().setAll(closeBtn);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(openBtn);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
A [i]ModalPane[/i] can hold any content. By default, you just need to call the \
|
||||||
|
[code]show()[/code] method, which is a convenience method for setting the content \
|
||||||
|
of the modal pane and triggering its display state at the same time.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox contentPositionExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var topDialog = new Dialog(-1, 150);
|
||||||
|
topDialog.getChildren().setAll(new Label(
|
||||||
|
FAKER.country().name()
|
||||||
|
));
|
||||||
|
|
||||||
|
var openTopBtn = new Button("Top");
|
||||||
|
openTopBtn.setOnAction(evt -> {
|
||||||
|
modalPane.setAlignment(Pos.TOP_CENTER);
|
||||||
|
modalPane.usePredefinedTransitionFactories(Side.TOP);
|
||||||
|
modalPane.show(topDialog);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var rightDialog = new Dialog(250, -1);
|
||||||
|
rightDialog.getChildren().setAll(new Label(
|
||||||
|
FAKER.country().name()
|
||||||
|
));
|
||||||
|
|
||||||
|
var openRightBtn = new Button("Right");
|
||||||
|
openRightBtn.setOnAction(evt -> {
|
||||||
|
modalPane.setAlignment(Pos.TOP_RIGHT);
|
||||||
|
modalPane.usePredefinedTransitionFactories(Side.RIGHT);
|
||||||
|
modalPane.show(rightDialog);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var bottomDialog = new Dialog(-1, 150);
|
||||||
|
bottomDialog.getChildren().setAll(new Label(
|
||||||
|
FAKER.country().name()
|
||||||
|
));
|
||||||
|
|
||||||
|
var openBottomBtn = new Button("Bottom");
|
||||||
|
openBottomBtn.setOnAction(evt -> {
|
||||||
|
modalPane.setAlignment(Pos.BOTTOM_CENTER);
|
||||||
|
modalPane.usePredefinedTransitionFactories(Side.BOTTOM);
|
||||||
|
modalPane.show(bottomDialog);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var leftDialog = new Dialog(250, -1);
|
||||||
|
leftDialog.getChildren().setAll(new Label(
|
||||||
|
FAKER.country().name()
|
||||||
|
));
|
||||||
|
|
||||||
|
var openLeftBtn = new Button("Left");
|
||||||
|
openLeftBtn.setOnAction(evt -> {
|
||||||
|
modalPane.setAlignment(Pos.TOP_LEFT);
|
||||||
|
modalPane.usePredefinedTransitionFactories(Side.LEFT);
|
||||||
|
modalPane.show(leftDialog);
|
||||||
|
});
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
openTopBtn.setPrefWidth(100);
|
||||||
|
openRightBtn.setPrefWidth(100);
|
||||||
|
openBottomBtn.setPrefWidth(100);
|
||||||
|
openLeftBtn.setPrefWidth(100);
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, openTopBtn, openRightBtn, openBottomBtn, openLeftBtn);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The alignment and animated appearance of modal content can be changed \
|
||||||
|
via corresponding properties.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox persistentExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var dialog = new Dialog(450, 450);
|
||||||
|
|
||||||
|
var openBtn = new Button("Open Dialog");
|
||||||
|
openBtn.setOnAction(evt -> {
|
||||||
|
modalPane.setPersistent(true);
|
||||||
|
modalPane.show(dialog);
|
||||||
|
});
|
||||||
|
|
||||||
|
var closeBtn = new Button("Close");
|
||||||
|
closeBtn.setOnAction(evt -> {
|
||||||
|
modalPane.hide(true);
|
||||||
|
modalPane.setPersistent(false);
|
||||||
|
});
|
||||||
|
dialog.getChildren().setAll(closeBtn);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(openBtn);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
By default, the [i]ModalPane[/i] exits when the ESC button is pressed \
|
||||||
|
or when the mouse is clicked outside the content area. [code]setPersistent()[/code] \
|
||||||
|
property prevents this behavior and instead plays a bouncing animation.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox nestingExample() {
|
||||||
|
//snippet_4:start
|
||||||
|
var dialog = new Dialog(600, 600);
|
||||||
|
var topDialog = new Dialog(450, 450);
|
||||||
|
var topmostDialog = new Dialog(300, 300);
|
||||||
|
|
||||||
|
var openBtn = new Button("Open Dialog 1");
|
||||||
|
// topViewOrder = -10 (default)
|
||||||
|
openBtn.setOnAction(evt -> modalPane.show(dialog));
|
||||||
|
|
||||||
|
var topDialogBtn = new Button("Open Dialog 2");
|
||||||
|
topDialogBtn.setOnAction(// topViewOrder = -15
|
||||||
|
evt -> modalPaneTop.show(topDialog)
|
||||||
|
);
|
||||||
|
dialog.getChildren().add(topDialogBtn);
|
||||||
|
|
||||||
|
var topmostDialogBtn = new Button("Open Dialog 3");
|
||||||
|
topmostDialogBtn.setOnAction(// topViewOrder = -20
|
||||||
|
evt -> modalPaneTopmost.show(topmostDialog)
|
||||||
|
);
|
||||||
|
topDialog.getChildren().add(topmostDialogBtn);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var box = new HBox(openBtn);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
There is no a specific support for nested dialogs. But, you can achieve \
|
||||||
|
the same behavior by stacking multiple modal panes and using the corresponding \
|
||||||
|
[code]topViewOrder[/code] property value.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox maximizedExample() {
|
||||||
|
//snippet_5:start
|
||||||
|
var dialog = new Dialog(-1, -1);
|
||||||
|
|
||||||
|
var openBtn = new Button("Open Dialog");
|
||||||
|
openBtn.setOnAction(evt -> modalPane.show(dialog));
|
||||||
|
|
||||||
|
var closeBtn = new Button("Close");
|
||||||
|
closeBtn.setOnAction(evt -> modalPane.hide(true));
|
||||||
|
dialog.getChildren().setAll(closeBtn);
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
|
var box = new HBox(openBtn);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
To create a maximized dialog, simply use a content node such as [i]VBox[/i] \
|
||||||
|
that expands itself in both the horizontal and vertical directions.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 5), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox overflowedExample() {
|
||||||
|
//snippet_6:start
|
||||||
|
var dialog1 = new Dialog(450, -1);
|
||||||
|
dialog1.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
var openBtn1 = new Button("Open Dialog 1");
|
||||||
|
openBtn1.setOnAction(evt -> {
|
||||||
|
StackPane.setMargin(dialog1, new Insets(20));
|
||||||
|
modalPane.show(dialog1);
|
||||||
|
});
|
||||||
|
|
||||||
|
var textFlow1 = new TextFlow();
|
||||||
|
dialog1.getChildren().setAll(textFlow1);
|
||||||
|
for (int i = 0; i < 30; i++) {
|
||||||
|
textFlow1.getChildren().add(
|
||||||
|
new Text(FAKER.lorem().paragraph() + "\n\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var dialog2 = new Dialog(450, -1);
|
||||||
|
dialog2.setPadding(new Insets(10, 0, 10, 0));
|
||||||
|
|
||||||
|
var openBtn2 = new Button("Open Dialog 2");
|
||||||
|
openBtn2.setOnAction(evt -> {
|
||||||
|
StackPane.setMargin(dialog2, new Insets(20));
|
||||||
|
modalPane.show(dialog2);
|
||||||
|
});
|
||||||
|
|
||||||
|
var textFlow2 = new TextFlow();
|
||||||
|
textFlow2.setMaxWidth(430);
|
||||||
|
textFlow2.setPadding(new Insets(10, 20, 10, 20));
|
||||||
|
|
||||||
|
var scrollPane2 = new ScrollPane(textFlow2);
|
||||||
|
scrollPane2.setMaxHeight(10_000);
|
||||||
|
dialog2.getChildren().setAll(scrollPane2);
|
||||||
|
|
||||||
|
for (int i = 0; i < 30; i++) {
|
||||||
|
textFlow2.getChildren().add(
|
||||||
|
new Text(FAKER.lorem().paragraph() + "\n\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//snippet_6:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, openBtn1, openBtn2);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]ModalPane[/i] is already scrollable by default, but you can also use a \
|
||||||
|
[i]ScrollPane[/i] for the content node if needed.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 6), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox lightboxExample() {
|
||||||
|
//snippet_7:start
|
||||||
|
var modalImage = new ImageView();
|
||||||
|
|
||||||
|
var thumbnail1 = new ImageView(new Image(
|
||||||
|
Resources.getResourceAsStream(
|
||||||
|
"images/gallery/kush-dwivedi-unsplash.jpg"
|
||||||
|
)));
|
||||||
|
thumbnail1.setFitWidth(180);
|
||||||
|
thumbnail1.setFitHeight(120);
|
||||||
|
thumbnail1.setCursor(Cursor.HAND);
|
||||||
|
thumbnail1.setOnMouseClicked(evt -> {
|
||||||
|
modalImage.setImage(thumbnail1.getImage());
|
||||||
|
modalPane.show(modalImage);
|
||||||
|
modalImage.requestFocus();
|
||||||
|
});
|
||||||
|
|
||||||
|
var thumbnail2 = new ImageView(new Image(
|
||||||
|
Resources.getResourceAsStream(
|
||||||
|
"images/gallery/markus-spiske-unsplash.jpg"
|
||||||
|
)));
|
||||||
|
thumbnail2.setFitWidth(180);
|
||||||
|
thumbnail2.setFitHeight(120);
|
||||||
|
thumbnail2.setCursor(Cursor.HAND);
|
||||||
|
thumbnail2.setOnMouseClicked(evt -> {
|
||||||
|
modalImage.setImage(thumbnail2.getImage());
|
||||||
|
modalPane.show(modalImage);
|
||||||
|
modalImage.requestFocus();
|
||||||
|
});
|
||||||
|
|
||||||
|
var thumbnail3 = new ImageView(new Image(
|
||||||
|
Resources.getResourceAsStream(
|
||||||
|
"images/gallery/r0m0_4-unsplash.jpg"
|
||||||
|
)));
|
||||||
|
thumbnail3.setFitWidth(180);
|
||||||
|
thumbnail3.setFitHeight(120);
|
||||||
|
thumbnail3.setCursor(Cursor.HAND);
|
||||||
|
thumbnail3.setOnMouseClicked(evt -> {
|
||||||
|
modalImage.setImage(thumbnail3.getImage());
|
||||||
|
modalPane.show(modalImage);
|
||||||
|
modalImage.requestFocus();
|
||||||
|
});
|
||||||
|
//snippet_7:end
|
||||||
|
|
||||||
|
var box = new HBox(5, thumbnail1, thumbnail2, thumbnail3);
|
||||||
|
box.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
This simple example demonstrates how [i]ModalPane[/i] can be used to \
|
||||||
|
implement the famous JS lightbox effect."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 7), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private static class Dialog extends VBox {
|
||||||
|
|
||||||
|
public Dialog(int width, int height) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
setSpacing(10);
|
||||||
|
setAlignment(Pos.CENTER);
|
||||||
|
setMinSize(width, height);
|
||||||
|
setMaxSize(width, height);
|
||||||
|
setStyle("-fx-background-color: -color-bg-default;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.InlineDatePicker;
|
||||||
|
import atlantafx.base.controls.Popover;
|
||||||
|
import atlantafx.base.controls.Popover.ArrowLocation;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import atlantafx.sampler.theme.CSSFragment;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Hyperlink;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
|
public class PopoverPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "Popover";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PopoverPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
The [i]Popover[/i] is a control used to display additional information \
|
||||||
|
or perform actions. It appears as a small popup window that overlays the \
|
||||||
|
main interface, triggered by a user action such as a mouseover or tap. \
|
||||||
|
It provides contextual information or options related to a specific object \
|
||||||
|
or feature on the interface.""");
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Position", positionExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
var datePickerStyle = """
|
||||||
|
.popover .date-picker-popup {
|
||||||
|
-color-date-border: transparent;
|
||||||
|
-color-date-bg: transparent;
|
||||||
|
-color-date-day-bg: transparent;
|
||||||
|
-color-date-month-year-bg: transparent;
|
||||||
|
-color-date-day-bg-hover: -color-bg-subtle;
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
//snippet_1:start
|
||||||
|
var textFlow = new TextFlow(new Text(
|
||||||
|
FAKER.lorem().sentence(30)
|
||||||
|
));
|
||||||
|
textFlow.setPrefWidth(300);
|
||||||
|
textFlow.setPadding(new Insets(10, 0, 10, 0));
|
||||||
|
|
||||||
|
var pop1 = new Popover(textFlow);
|
||||||
|
pop1.setTitle("Lorem Ipsum");
|
||||||
|
pop1.setHeaderAlwaysVisible(true);
|
||||||
|
pop1.setDetachable(true);
|
||||||
|
|
||||||
|
var link1 = new Hyperlink("Text");
|
||||||
|
link1.setOnAction(e -> pop1.show(link1));
|
||||||
|
|
||||||
|
// ~
|
||||||
|
var datePicker = new InlineDatePicker();
|
||||||
|
datePicker.setValue(LocalDate.now(ZoneId.systemDefault()));
|
||||||
|
// -color-date-border: transparent;
|
||||||
|
// -color-date-bg: transparent;
|
||||||
|
// -color-date-day-bg: transparent;
|
||||||
|
// -color-date-month-year-bg: transparent;
|
||||||
|
// -color-date-day-bg-hover: -color-bg-subtle;
|
||||||
|
new CSSFragment(datePickerStyle).addTo(datePicker);
|
||||||
|
|
||||||
|
var pop2 = new Popover(datePicker);
|
||||||
|
pop2.setHeaderAlwaysVisible(false);
|
||||||
|
pop2.setDetachable(true);
|
||||||
|
|
||||||
|
var link2 = new Hyperlink("DatePicker");
|
||||||
|
link2.setOnAction(e -> pop2.show(link2));
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_30, link1, link2);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The popup window has a very lightweight appearance (no default window decorations) \
|
||||||
|
and an arrow pointing at the owner. Due to the nature of popup windows the \
|
||||||
|
[i]Popover[/i] will move around with the parent window when the user drags it."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox positionExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
class PopoverLink extends Hyperlink {
|
||||||
|
|
||||||
|
public PopoverLink(ArrowLocation arrowLocation) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
var textFlow = new TextFlow(new Text(
|
||||||
|
FAKER.lorem().sentence(30)
|
||||||
|
));
|
||||||
|
textFlow.setPrefWidth(300);
|
||||||
|
textFlow.setPadding(new Insets(10, 0, 10, 0));
|
||||||
|
|
||||||
|
var pop = new Popover(textFlow);
|
||||||
|
pop.setHeaderAlwaysVisible(false);
|
||||||
|
pop.setArrowLocation(arrowLocation);
|
||||||
|
|
||||||
|
setText(String.valueOf(arrowLocation));
|
||||||
|
setOnAction(e -> pop.show(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var grid = new GridPane();
|
||||||
|
grid.setHgap(20);
|
||||||
|
grid.setVgap(20);
|
||||||
|
grid.addColumn(0,
|
||||||
|
new PopoverLink(ArrowLocation.TOP_LEFT),
|
||||||
|
new PopoverLink(ArrowLocation.TOP_CENTER),
|
||||||
|
new PopoverLink(ArrowLocation.TOP_RIGHT)
|
||||||
|
);
|
||||||
|
grid.addColumn(1,
|
||||||
|
new PopoverLink(ArrowLocation.RIGHT_TOP),
|
||||||
|
new PopoverLink(ArrowLocation.RIGHT_CENTER),
|
||||||
|
new PopoverLink(ArrowLocation.RIGHT_BOTTOM)
|
||||||
|
);
|
||||||
|
grid.addColumn(2,
|
||||||
|
new PopoverLink(ArrowLocation.BOTTOM_LEFT),
|
||||||
|
new PopoverLink(ArrowLocation.BOTTOM_CENTER),
|
||||||
|
new PopoverLink(ArrowLocation.BOTTOM_RIGHT)
|
||||||
|
);
|
||||||
|
grid.addColumn(3,
|
||||||
|
new PopoverLink(ArrowLocation.LEFT_TOP),
|
||||||
|
new PopoverLink(ArrowLocation.LEFT_CENTER),
|
||||||
|
new PopoverLink(ArrowLocation.LEFT_BOTTOM)
|
||||||
|
);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]Popover[/i] popup window can be positioned by setting an \
|
||||||
|
appropriate ArrowLocation value.."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(grid, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.extras;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.ToggleSwitch;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import javafx.geometry.HorizontalDirection;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
|
public class ToggleSwitchPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "ToggleSwitch";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToggleSwitchPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addFormattedText("""
|
||||||
|
The [i]ToggleSwitch[/i] is a control used to activate or deactivate a feature. \
|
||||||
|
It consists of a horizontal bar with a small knob that can be moved to turn on \
|
||||||
|
or off a function."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Color", colorExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var toggle1 = new ToggleSwitch("Enabled");
|
||||||
|
toggle1.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> toggle1.setText(val ? "Enabled" : "Disabled")
|
||||||
|
);
|
||||||
|
toggle1.setSelected(true);
|
||||||
|
|
||||||
|
var toggle2 = new ToggleSwitch("Disabled");
|
||||||
|
toggle2.selectedProperty().addListener(
|
||||||
|
(obs, old, val) -> toggle2.setText(val ? "Enabled" : "Disabled")
|
||||||
|
);
|
||||||
|
toggle2.setLabelPosition(HorizontalDirection.RIGHT);
|
||||||
|
toggle2.setSelected(false);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_30, toggle1, toggle2);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The text position can be changed by setting the [code]labelPosition[/code] \
|
||||||
|
property value."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox colorExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var successToggle = new ToggleSwitch("Enabled");
|
||||||
|
successToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
|
successToggle.setText(val ? "Enabled" : "Disabled");
|
||||||
|
successToggle.pseudoClassStateChanged(Styles.STATE_SUCCESS, val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
successToggle.setSelected(true);
|
||||||
|
successToggle.pseudoClassStateChanged(Styles.STATE_SUCCESS, true);
|
||||||
|
|
||||||
|
var dangerToggle = new ToggleSwitch("Disabled");
|
||||||
|
dangerToggle.selectedProperty().addListener((obs, old, val) -> {
|
||||||
|
dangerToggle.setText(val ? "Enabled" : "Disabled");
|
||||||
|
dangerToggle.pseudoClassStateChanged(Styles.STATE_DANGER, val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
dangerToggle.setLabelPosition(HorizontalDirection.RIGHT);
|
||||||
|
dangerToggle.setSelected(false);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_30, successToggle, dangerToggle);
|
||||||
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Use [code]Styles.STATE_SUCCESS[/code] or [code]Styles.STATE_DANGER[/code] \
|
||||||
|
pseudo-classes to change the [i]ToggleSwitch[/i] color.""");
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,6 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import static atlantafx.sampler.util.Controls.hyperlink;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -14,105 +10,54 @@ import javafx.animation.KeyFrame;
|
|||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
class ColorPalette extends VBox {
|
class ColorPalette extends GridPane {
|
||||||
|
|
||||||
private final List<ColorPaletteBlock> blocks = new ArrayList<>();
|
private final List<ColorPaletteBlock> blocks = new ArrayList<>();
|
||||||
private final ReadOnlyObjectWrapper<Color> bgBaseColor = new ReadOnlyObjectWrapper<>(Color.WHITE);
|
private final ReadOnlyObjectWrapper<Color> bgBaseColor = new ReadOnlyObjectWrapper<>(Color.WHITE);
|
||||||
private final Consumer<ColorPaletteBlock> colorBlockActionHandler;
|
private final Consumer<ColorPaletteBlock> colorBlockActionHandler;
|
||||||
|
|
||||||
public ColorPalette(Consumer<ColorPaletteBlock> blockClickedHandler) {
|
public ColorPalette(Consumer<ColorPaletteBlock> actionHandler) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.colorBlockActionHandler = Objects.requireNonNull(blockClickedHandler);
|
this.colorBlockActionHandler = Objects.requireNonNull(actionHandler, "actionHandler");
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
add(colorBlock("-color-fg-default", "-color-bg-default", "-color-border-default"), 0, 0);
|
||||||
var headerLabel = new Label("Color Palette");
|
add(colorBlock("-color-fg-default", "-color-bg-overlay", "-color-border-default"), 1, 0);
|
||||||
headerLabel.getStyleClass().add(Styles.TITLE_4);
|
add(colorBlock("-color-fg-muted", "-color-bg-default", "-color-border-muted"), 2, 0);
|
||||||
|
add(colorBlock("-color-fg-subtle", "-color-bg-default", "-color-border-subtle"), 3, 0);
|
||||||
|
|
||||||
var headerBox = new HBox();
|
add(colorBlock("-color-fg-emphasis", "-color-accent-emphasis", "-color-accent-emphasis"), 0, 1);
|
||||||
headerBox.getChildren().setAll(headerLabel);
|
add(colorBlock("-color-accent-fg", "-color-bg-default", "-color-accent-emphasis"), 1, 1);
|
||||||
headerBox.setAlignment(Pos.CENTER_LEFT);
|
add(colorBlock("-color-fg-default", "-color-accent-muted", "-color-accent-emphasis"), 2, 1);
|
||||||
headerBox.getStyleClass().add("header");
|
add(colorBlock("-color-accent-fg", "-color-accent-subtle", "-color-accent-emphasis"), 3, 1);
|
||||||
|
|
||||||
var noteText = new VBox(6);
|
add(colorBlock("-color-fg-emphasis", "-color-neutral-emphasis-plus", "-color-neutral-emphasis-plus"), 0, 2);
|
||||||
noteText.getChildren().setAll(
|
add(colorBlock("-color-fg-emphasis", "-color-neutral-emphasis", "-color-neutral-emphasis"), 1, 2);
|
||||||
new TextFlow(
|
add(colorBlock("-color-fg-default", "-color-neutral-muted", "-color-neutral-emphasis"), 2, 2);
|
||||||
new Text("Color contrast between text and its background must meet "),
|
add(colorBlock("-color-fg-default", "-color-neutral-subtle", "-color-neutral-emphasis"), 3, 2);
|
||||||
hyperlink(
|
|
||||||
"required WCAG standards",
|
|
||||||
URI.create("https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html")
|
|
||||||
),
|
|
||||||
new Text(":")
|
|
||||||
),
|
|
||||||
new Text(" • 4.5:1 for normal text"),
|
|
||||||
new Text(" • 3:1 for large text (>24px)"),
|
|
||||||
new Text(" • 3:1 for UI elements and graphics"),
|
|
||||||
new Text(" • no contrast requirement for decorative and disabled elements"),
|
|
||||||
new Text(),
|
|
||||||
new Text("Click on any color block to observe and modify color combination via built-in contrast checker.")
|
|
||||||
);
|
|
||||||
|
|
||||||
var colorGrid = colorGrid();
|
add(colorBlock("-color-fg-emphasis", "-color-success-emphasis", "-color-success-emphasis"), 0, 3);
|
||||||
|
add(colorBlock("-color-success-fg", "-color-bg-default", "-color-success-emphasis"), 1, 3);
|
||||||
|
add(colorBlock("-color-fg-default", "-color-success-muted", "-color-success-emphasis"), 2, 3);
|
||||||
|
add(colorBlock("-color-success-fg", "-color-success-subtle", "-color-success-emphasis"), 3, 3);
|
||||||
|
|
||||||
backgroundProperty().addListener((obs, old, val) -> bgBaseColor.set(
|
add(colorBlock("-color-fg-emphasis", "-color-warning-emphasis", "-color-warning-emphasis"), 0, 4);
|
||||||
val != null && !val.getFills().isEmpty() ? (Color) val.getFills().get(0).getFill() : Color.WHITE
|
add(colorBlock("-color-warning-fg", "-color-bg-default", "-color-warning-emphasis"), 1, 4);
|
||||||
));
|
add(colorBlock("-color-fg-default", "-color-warning-muted", "-color-warning-emphasis"), 2, 4);
|
||||||
|
add(colorBlock("-color-warning-fg", "-color-warning-subtle", "-color-warning-emphasis"), 3, 4);
|
||||||
|
|
||||||
|
add(colorBlock("-color-fg-emphasis", "-color-danger-emphasis", "-color-danger-emphasis"), 0, 5);
|
||||||
|
add(colorBlock("-color-danger-fg", "-color-bg-default", "-color-danger-emphasis"), 1, 5);
|
||||||
|
add(colorBlock("-color-fg-default", "-color-danger-muted", "-color-danger-emphasis"), 2, 5);
|
||||||
|
add(colorBlock("-color-danger-fg", "-color-danger-subtle", "-color-danger-emphasis"), 3, 5);
|
||||||
|
|
||||||
getChildren().setAll(headerBox, noteText, colorGrid);
|
|
||||||
setId("color-palette");
|
setId("color-palette");
|
||||||
}
|
}
|
||||||
|
|
||||||
private GridPane colorGrid() {
|
|
||||||
var grid = new GridPane();
|
|
||||||
grid.getStyleClass().add("grid");
|
|
||||||
|
|
||||||
grid.add(colorBlock("-color-fg-default", "-color-bg-default", "-color-border-default"), 0, 0);
|
|
||||||
grid.add(colorBlock("-color-fg-default", "-color-bg-overlay", "-color-border-default"), 1, 0);
|
|
||||||
grid.add(colorBlock("-color-fg-muted", "-color-bg-default", "-color-border-muted"), 2, 0);
|
|
||||||
grid.add(colorBlock("-color-fg-subtle", "-color-bg-default", "-color-border-subtle"), 3, 0);
|
|
||||||
|
|
||||||
grid.add(colorBlock("-color-fg-emphasis", "-color-accent-emphasis", "-color-accent-emphasis"), 0, 1);
|
|
||||||
grid.add(colorBlock("-color-accent-fg", "-color-bg-default", "-color-accent-emphasis"), 1, 1);
|
|
||||||
grid.add(colorBlock("-color-fg-default", "-color-accent-muted", "-color-accent-emphasis"), 2, 1);
|
|
||||||
grid.add(colorBlock("-color-accent-fg", "-color-accent-subtle", "-color-accent-emphasis"), 3, 1);
|
|
||||||
|
|
||||||
grid.add(
|
|
||||||
colorBlock("-color-fg-emphasis", "-color-neutral-emphasis-plus", "-color-neutral-emphasis-plus"), 0, 2
|
|
||||||
);
|
|
||||||
grid.add(colorBlock("-color-fg-emphasis", "-color-neutral-emphasis", "-color-neutral-emphasis"), 1, 2);
|
|
||||||
grid.add(colorBlock("-color-fg-default", "-color-neutral-muted", "-color-neutral-emphasis"), 2, 2);
|
|
||||||
grid.add(colorBlock("-color-fg-default", "-color-neutral-subtle", "-color-neutral-emphasis"), 3, 2);
|
|
||||||
|
|
||||||
grid.add(colorBlock("-color-fg-emphasis", "-color-success-emphasis", "-color-success-emphasis"), 0, 3);
|
|
||||||
grid.add(colorBlock("-color-success-fg", "-color-bg-default", "-color-success-emphasis"), 1, 3);
|
|
||||||
grid.add(colorBlock("-color-fg-default", "-color-success-muted", "-color-success-emphasis"), 2, 3);
|
|
||||||
grid.add(colorBlock("-color-success-fg", "-color-success-subtle", "-color-success-emphasis"), 3, 3);
|
|
||||||
|
|
||||||
grid.add(colorBlock("-color-fg-emphasis", "-color-warning-emphasis", "-color-warning-emphasis"), 0, 4);
|
|
||||||
grid.add(colorBlock("-color-warning-fg", "-color-bg-default", "-color-warning-emphasis"), 1, 4);
|
|
||||||
grid.add(colorBlock("-color-fg-default", "-color-warning-muted", "-color-warning-emphasis"), 2, 4);
|
|
||||||
grid.add(colorBlock("-color-warning-fg", "-color-warning-subtle", "-color-warning-emphasis"), 3, 4);
|
|
||||||
|
|
||||||
grid.add(colorBlock("-color-fg-emphasis", "-color-danger-emphasis", "-color-danger-emphasis"), 0, 5);
|
|
||||||
grid.add(colorBlock("-color-danger-fg", "-color-bg-default", "-color-danger-emphasis"), 1, 5);
|
|
||||||
grid.add(colorBlock("-color-fg-default", "-color-danger-muted", "-color-danger-emphasis"), 2, 5);
|
|
||||||
grid.add(colorBlock("-color-danger-fg", "-color-danger-subtle", "-color-danger-emphasis"), 3, 5);
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ColorPaletteBlock colorBlock(String fgColor, String bgColor, String borderColor) {
|
private ColorPaletteBlock colorBlock(String fgColor, String bgColor, String borderColor) {
|
||||||
var block = new ColorPaletteBlock(fgColor, bgColor, borderColor, bgBaseColor.getReadOnlyProperty());
|
var block = new ColorPaletteBlock(fgColor, bgColor, borderColor, bgBaseColor.getReadOnlyProperty());
|
||||||
block.setOnAction(colorBlockActionHandler);
|
block.setOnAction(colorBlockActionHandler);
|
||||||
|
@ -2,23 +2,16 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.animation.KeyFrame;
|
import javafx.animation.KeyFrame;
|
||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.FlowPane;
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
class ColorScale extends VBox {
|
class ColorScale extends FlowPane {
|
||||||
|
|
||||||
private final ReadOnlyObjectWrapper<Color> bgBaseColor = new ReadOnlyObjectWrapper<>(Color.WHITE);
|
private final ReadOnlyObjectWrapper<Color> bgBaseColor = new ReadOnlyObjectWrapper<>(Color.WHITE);
|
||||||
private final List<ColorScaleBlock> blocks = Arrays.asList(
|
private final List<ColorScaleBlock> blocks = Arrays.asList(
|
||||||
@ -32,35 +25,15 @@ class ColorScale extends VBox {
|
|||||||
|
|
||||||
public ColorScale() {
|
public ColorScale() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
|
||||||
var headerLabel = new Label("Color Scale");
|
|
||||||
headerLabel.getStyleClass().add(Styles.TITLE_4);
|
|
||||||
|
|
||||||
var headerBox = new HBox();
|
|
||||||
headerBox.getChildren().setAll(headerLabel);
|
|
||||||
headerBox.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
headerBox.getStyleClass().add("header");
|
|
||||||
|
|
||||||
var noteText = new TextFlow(
|
|
||||||
new Text(
|
|
||||||
"Avoid referencing scale variables directly when building UI that needs "
|
|
||||||
+ "to adapt to different color themes. Instead, use the functional variables listed above."
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
backgroundProperty().addListener((obs, old, val) -> bgBaseColor.set(
|
backgroundProperty().addListener((obs, old, val) -> bgBaseColor.set(
|
||||||
val != null && !val.getFills().isEmpty() ? (Color) val.getFills().get(0).getFill() : Color.WHITE
|
val != null && !val.getFills().isEmpty()
|
||||||
|
? (Color) val.getFills().get(0).getFill()
|
||||||
|
: Color.WHITE
|
||||||
));
|
));
|
||||||
|
|
||||||
setId("color-scale");
|
setId("color-scale");
|
||||||
getChildren().setAll(
|
getChildren().setAll(blocks);
|
||||||
headerBox,
|
|
||||||
noteText,
|
|
||||||
colorTable()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateColorInfo(Duration delay) {
|
public void updateColorInfo(Duration delay) {
|
||||||
@ -68,11 +41,4 @@ class ColorScale extends VBox {
|
|||||||
t.setOnFinished(e -> blocks.forEach(ColorScaleBlock::update));
|
t.setOnFinished(e -> blocks.forEach(ColorScaleBlock::update));
|
||||||
t.play();
|
t.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
private FlowPane colorTable() {
|
|
||||||
var root = new FlowPane(20, 20);
|
|
||||||
root.getStyleClass().add("table");
|
|
||||||
root.getChildren().setAll(blocks);
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import javafx.scene.paint.Color;
|
|||||||
|
|
||||||
class ColorScaleBlock extends VBox {
|
class ColorScaleBlock extends VBox {
|
||||||
|
|
||||||
private static final double BLOCK_WIDTH = 250;
|
private static final double BLOCK_WIDTH = 200;
|
||||||
private static final double BLOCK_HEIGHT = 40;
|
private static final double BLOCK_HEIGHT = 40;
|
||||||
|
|
||||||
private final ReadOnlyObjectProperty<Color> bgBaseColor;
|
private final ReadOnlyObjectProperty<Color> bgBaseColor;
|
||||||
|
@ -2,25 +2,20 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
import static atlantafx.sampler.util.Controls.hyperlink;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.CustomTextField;
|
import atlantafx.base.controls.CustomTextField;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
import atlantafx.sampler.theme.CSSFragment;
|
||||||
import java.net.URI;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import org.kordamp.ikonli.Ikon;
|
import org.kordamp.ikonli.Ikon;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
import org.kordamp.ikonli.javafx.StackedFontIcon;
|
import org.kordamp.ikonli.javafx.StackedFontIcon;
|
||||||
@ -28,7 +23,7 @@ import org.kordamp.ikonli.material2.Material2AL;
|
|||||||
import org.kordamp.ikonli.material2.Material2MZ;
|
import org.kordamp.ikonli.material2.Material2MZ;
|
||||||
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
||||||
|
|
||||||
public class IconsPage extends AbstractPage {
|
public class IconsPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Icons";
|
public static final String NAME = "Icons";
|
||||||
|
|
||||||
@ -39,21 +34,116 @@ public class IconsPage extends AbstractPage {
|
|||||||
|
|
||||||
public IconsPage() {
|
public IconsPage() {
|
||||||
super();
|
super();
|
||||||
createView();
|
|
||||||
|
addFormattedText("""
|
||||||
|
AtlantaFX supports [url=https://kordamp.org/ikonli]Ikonli[/url] iconic fonts out \
|
||||||
|
of the box, which can be used in conjunction with certain JavaFX components.""", true);
|
||||||
|
addSection("Color", colorExample());
|
||||||
|
addSection("Stacking", stackingExample());
|
||||||
|
addSection("Icon Pack", iconBrowser());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private ExampleBox colorExample() {
|
||||||
var headerText = new TextFlow(
|
//snippet_1:start
|
||||||
new Text("AtlantaFX supports "),
|
var accentIcon = new FontIcon(Material2MZ.THUMB_UP);
|
||||||
hyperlink("Ikonli", URI.create("https://kordamp.org/ikonli")),
|
accentIcon.getStyleClass().add(Styles.ACCENT);
|
||||||
new Text(" iconic fonts that can be used together with some JavaFX components.")
|
|
||||||
);
|
|
||||||
|
|
||||||
var browserText = new TextFlow(
|
var successIcon = new FontIcon(Material2MZ.THUMB_UP);
|
||||||
new Text("There's a variety of icon packs. Sampler app uses "),
|
successIcon.getStyleClass().add(Styles.SUCCESS);
|
||||||
hyperlink("Material Icons", URI.create("https://kordamp.org/ikonli/cheat-sheet-material2.html")),
|
|
||||||
new Text(" you can preview below.")
|
var warningIcon = new FontIcon(Material2MZ.THUMB_UP);
|
||||||
|
warningIcon.getStyleClass().add(Styles.WARNING);
|
||||||
|
|
||||||
|
var dangerIcon = new FontIcon(Material2MZ.THUMB_UP);
|
||||||
|
dangerIcon.getStyleClass().add(Styles.DANGER);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, accentIcon, successIcon, warningIcon, dangerIcon);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
You can use pseudo-classes to set the icon color.""");
|
||||||
|
|
||||||
|
var example = new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
example.setAllowDisable(false);
|
||||||
|
|
||||||
|
return example;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox stackingExample() {
|
||||||
|
var style1 = """
|
||||||
|
.stacked-ikonli-font-icon > .outer-icon {
|
||||||
|
-fx-icon-size: 48px;
|
||||||
|
-fx-icon-color: -color-danger-emphasis;
|
||||||
|
}
|
||||||
|
.stacked-ikonli-font-icon > .inner-icon {
|
||||||
|
-fx-icon-size: 24px;
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var style2 = """
|
||||||
|
.stacked-ikonli-font-icon > .outer-icon {
|
||||||
|
-fx-icon-size: 48px;
|
||||||
|
}
|
||||||
|
.stacked-ikonli-font-icon > .inner-icon {
|
||||||
|
-fx-icon-size: 24px;
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
//snippet_2:start
|
||||||
|
var outerIcon1 = new FontIcon(Material2OutlinedAL.BLOCK);
|
||||||
|
outerIcon1.getStyleClass().add("outer-icon");
|
||||||
|
|
||||||
|
var innerIcon1 = new FontIcon(Material2MZ.PHOTO_CAMERA);
|
||||||
|
innerIcon1.getStyleClass().add("inner-icon");
|
||||||
|
|
||||||
|
var stackIcon1 = new StackedFontIcon();
|
||||||
|
stackIcon1.getChildren().addAll(innerIcon1, outerIcon1);
|
||||||
|
// .stacked-ikonli-font-icon > .outer-icon {
|
||||||
|
// -fx-icon-size: 48px;
|
||||||
|
// -fx-icon-color: -color-danger-emphasis;
|
||||||
|
// }
|
||||||
|
// .stacked-ikonli-font-icon > .inner-icon {
|
||||||
|
// -fx-icon-size: 24px;
|
||||||
|
// }
|
||||||
|
new CSSFragment(style1).addTo(stackIcon1);
|
||||||
|
|
||||||
|
var outerIcon2 = new FontIcon(
|
||||||
|
Material2OutlinedAL.CHECK_BOX_OUTLINE_BLANK
|
||||||
);
|
);
|
||||||
|
outerIcon2.getStyleClass().add("outer-icon");
|
||||||
|
|
||||||
|
var innerIcon2 = new FontIcon(Material2AL.LOCK);
|
||||||
|
innerIcon2.getStyleClass().add("inner-icon");
|
||||||
|
|
||||||
|
var stackIcon2 = new StackedFontIcon();
|
||||||
|
stackIcon2.getChildren().addAll(outerIcon2, innerIcon2);
|
||||||
|
// .stacked-ikonli-font-icon > .outer-icon {
|
||||||
|
// -fx-icon-size: 48px;
|
||||||
|
// }
|
||||||
|
// .stacked-ikonli-font-icon > .inner-icon {
|
||||||
|
// -fx-icon-size: 24px;
|
||||||
|
// }
|
||||||
|
new CSSFragment(style2).addTo(stackIcon2);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, stackIcon1, stackIcon2);
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
Ikonli also supports icon stacking, although it is currently in an \
|
||||||
|
initial state and requires some manual styling. However, it can be \
|
||||||
|
useful in certain cases.""");
|
||||||
|
|
||||||
|
var example = new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
example.setAllowDisable(false);
|
||||||
|
|
||||||
|
return example;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node iconBrowser() {
|
||||||
|
var description = createFormattedText("""
|
||||||
|
There are various icon packs available. The Sampler app uses \
|
||||||
|
[url=https://kordamp.org/ikonli/cheat-sheet-material2.html]Material Icons[/url] \
|
||||||
|
which you can preview below.""", true);
|
||||||
|
|
||||||
var filterText = new CustomTextField();
|
var filterText = new CustomTextField();
|
||||||
filterText.setLeft(new FontIcon(Material2MZ.SEARCH));
|
filterText.setLeft(new FontIcon(Material2MZ.SEARCH));
|
||||||
@ -69,77 +159,8 @@ public class IconsPage extends AbstractPage {
|
|||||||
var iconBrowser = new IconBrowser(5, icons);
|
var iconBrowser = new IconBrowser(5, icons);
|
||||||
VBox.setVgrow(iconBrowser, Priority.ALWAYS);
|
VBox.setVgrow(iconBrowser, Priority.ALWAYS);
|
||||||
iconBrowser.filterProperty().bind(filterText.textProperty());
|
iconBrowser.filterProperty().bind(filterText.textProperty());
|
||||||
|
iconBrowser.setMinHeight(500);
|
||||||
|
|
||||||
setUserContent(new VBox(
|
return new VBox(VGAP_10, description, filterBox, iconBrowser);
|
||||||
PAGE_VGAP,
|
|
||||||
headerText,
|
|
||||||
expandingHBox(colorSample(), stackingSample()),
|
|
||||||
browserText,
|
|
||||||
filterBox,
|
|
||||||
iconBrowser
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock colorSample() {
|
|
||||||
var accentIcon = new FontIcon(Material2MZ.THUMB_UP);
|
|
||||||
accentIcon.getStyleClass().add(Styles.ACCENT);
|
|
||||||
|
|
||||||
var successIcon = new FontIcon(Material2MZ.THUMB_UP);
|
|
||||||
successIcon.getStyleClass().add(Styles.SUCCESS);
|
|
||||||
|
|
||||||
var warningIcon = new FontIcon(Material2MZ.THUMB_UP);
|
|
||||||
warningIcon.getStyleClass().add(Styles.WARNING);
|
|
||||||
|
|
||||||
var dangerIcon = new FontIcon(Material2MZ.THUMB_UP);
|
|
||||||
dangerIcon.getStyleClass().add(Styles.DANGER);
|
|
||||||
|
|
||||||
var content = new VBox(
|
|
||||||
BLOCK_VGAP,
|
|
||||||
new Label("You can also use pseudo-classes to set icon color."),
|
|
||||||
new HBox(BLOCK_HGAP, accentIcon, successIcon, warningIcon, dangerIcon)
|
|
||||||
);
|
|
||||||
|
|
||||||
return new SampleBlock("Colors", content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock stackingSample() {
|
|
||||||
var outerIcon1 = new FontIcon(Material2OutlinedAL.BLOCK);
|
|
||||||
outerIcon1.getStyleClass().add("outer-icon");
|
|
||||||
|
|
||||||
var innerIcon1 = new FontIcon(Material2MZ.PHOTO_CAMERA);
|
|
||||||
innerIcon1.getStyleClass().add("inner-icon");
|
|
||||||
|
|
||||||
var stackIcon1 = new StackedFontIcon();
|
|
||||||
stackIcon1.getChildren().addAll(innerIcon1, outerIcon1);
|
|
||||||
new CSSFragment("""
|
|
||||||
.stacked-ikonli-font-icon > .outer-icon {
|
|
||||||
-fx-icon-size: 48px;
|
|
||||||
-fx-icon-color: -color-danger-emphasis;
|
|
||||||
}
|
|
||||||
.stacked-ikonli-font-icon > .inner-icon {
|
|
||||||
-fx-icon-size: 24px;
|
|
||||||
}
|
|
||||||
""").addTo(stackIcon1);
|
|
||||||
|
|
||||||
var outerIcon2 = new FontIcon(Material2OutlinedAL.CHECK_BOX_OUTLINE_BLANK);
|
|
||||||
outerIcon2.getStyleClass().add("outer-icon");
|
|
||||||
|
|
||||||
var innerIcon2 = new FontIcon(Material2AL.LOCK);
|
|
||||||
innerIcon2.getStyleClass().add("inner-icon");
|
|
||||||
|
|
||||||
var stackIcon2 = new StackedFontIcon();
|
|
||||||
stackIcon2.getChildren().addAll(outerIcon2, innerIcon2);
|
|
||||||
new CSSFragment("""
|
|
||||||
.stacked-ikonli-font-icon > .outer-icon {
|
|
||||||
-fx-icon-size: 48px;
|
|
||||||
}
|
|
||||||
.stacked-ikonli-font-icon > .inner-icon {
|
|
||||||
-fx-icon-size: 24px;
|
|
||||||
}
|
|
||||||
""").addTo(stackIcon2);
|
|
||||||
|
|
||||||
var content = new HBox(BLOCK_HGAP, stackIcon1, stackIcon2);
|
|
||||||
|
|
||||||
return new SampleBlock("Stacking Icons", content);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
71
sampler/src/main/java/atlantafx/sampler/page/general/OverviewPage.java
Executable file
71
sampler/src/main/java/atlantafx/sampler/page/general/OverviewPage.java
Executable file
@ -0,0 +1,71 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
|
import static atlantafx.sampler.util.Containers.setScrollConstraints;
|
||||||
|
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.AS_NEEDED;
|
||||||
|
|
||||||
|
import atlantafx.sampler.Resources;
|
||||||
|
import atlantafx.sampler.page.Page;
|
||||||
|
import java.io.IOException;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
public class OverviewPage extends ScrollPane implements Page {
|
||||||
|
|
||||||
|
public static final String NAME = "Overview";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverviewPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var wrapper = new VBox();
|
||||||
|
wrapper.setAlignment(Pos.TOP_CENTER);
|
||||||
|
|
||||||
|
var loader = new FXMLLoader(
|
||||||
|
Resources.getResource("assets/fxml/overview/index.fxml").toURL()
|
||||||
|
);
|
||||||
|
Parent fxmlContent = loader.load();
|
||||||
|
((Pane) fxmlContent).setMaxWidth(Page.MAX_WIDTH);
|
||||||
|
VBox.setVgrow(fxmlContent, Priority.ALWAYS);
|
||||||
|
wrapper.getChildren().setAll(fxmlContent);
|
||||||
|
|
||||||
|
setScrollConstraints(this, AS_NEEDED, true, AS_NEEDED, true);
|
||||||
|
setMaxHeight(20_000);
|
||||||
|
setContent(wrapper);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to load FXML file", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
setId("overview");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parent getView() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canDisplaySourceCode() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canChangeThemeSettings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
}
|
||||||
|
}
|
@ -2,71 +2,51 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import static atlantafx.sampler.event.ThemeEvent.EventType.COLOR_CHANGE;
|
import static atlantafx.sampler.event.ThemeEvent.EventType;
|
||||||
import static atlantafx.sampler.event.ThemeEvent.EventType.THEME_ADD;
|
|
||||||
import static atlantafx.sampler.event.ThemeEvent.EventType.THEME_CHANGE;
|
|
||||||
import static atlantafx.sampler.event.ThemeEvent.EventType.THEME_REMOVE;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
import static atlantafx.sampler.util.Controls.hyperlink;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.Resources;
|
import atlantafx.sampler.Resources;
|
||||||
import atlantafx.sampler.event.DefaultEventBus;
|
import atlantafx.sampler.event.DefaultEventBus;
|
||||||
import atlantafx.sampler.event.ThemeEvent;
|
import atlantafx.sampler.event.ThemeEvent;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import atlantafx.sampler.theme.SamplerTheme;
|
import atlantafx.sampler.theme.SamplerTheme;
|
||||||
import atlantafx.sampler.theme.ThemeManager;
|
import atlantafx.sampler.theme.ThemeManager;
|
||||||
import java.net.URI;
|
import atlantafx.sampler.util.Lazy;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
import javafx.geometry.HPos;
|
import javafx.geometry.HPos;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ChoiceBox;
|
import javafx.scene.control.ChoiceBox;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Spinner;
|
||||||
import javafx.scene.control.Tooltip;
|
import javafx.scene.control.Tooltip;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.text.TextFlow;
|
import javafx.scene.text.Font;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
import org.kordamp.ikonli.material2.Material2OutlinedMZ;
|
import org.kordamp.ikonli.material2.Material2OutlinedMZ;
|
||||||
|
|
||||||
@SuppressWarnings("UnnecessaryLambda")
|
@SuppressWarnings("UnnecessaryLambda")
|
||||||
public class ThemePage extends AbstractPage {
|
public class ThemePage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Theme";
|
public static final String NAME = "Theme";
|
||||||
|
|
||||||
private static final ThemeManager TM = ThemeManager.getInstance();
|
private static final ThemeManager TM = ThemeManager.getInstance();
|
||||||
|
private static final String DEFAULT_FONT_ID = "Default";
|
||||||
private static final Image SCENE_BUILDER_ICON = new Image(
|
private static final Image SCENE_BUILDER_ICON = new Image(
|
||||||
Resources.getResourceAsStream("images/scene-builder_32.png")
|
Resources.getResourceAsStream("images/scene-builder_32.png")
|
||||||
);
|
);
|
||||||
|
|
||||||
private final Consumer<ColorPaletteBlock> colorBlockActionHandler = colorBlock -> {
|
|
||||||
ContrastCheckerDialog dialog = getOrCreateContrastCheckerDialog();
|
|
||||||
dialog.getContent().setValues(
|
|
||||||
colorBlock.getFgColorName(),
|
|
||||||
colorBlock.getFgColor(),
|
|
||||||
colorBlock.getBgColorName(),
|
|
||||||
colorBlock.getBgColor()
|
|
||||||
);
|
|
||||||
overlay.setContent(dialog, HPos.CENTER);
|
|
||||||
overlay.toFront();
|
|
||||||
};
|
|
||||||
|
|
||||||
private final ColorPalette colorPalette = new ColorPalette(colorBlockActionHandler);
|
|
||||||
private final ColorScale colorScale = new ColorScale();
|
|
||||||
private final ChoiceBox<SamplerTheme> themeSelector = createThemeSelector();
|
|
||||||
|
|
||||||
private ThemeRepoManagerDialog themeRepoManagerDialog;
|
|
||||||
private ContrastCheckerDialog contrastCheckerDialog;
|
|
||||||
private SceneBuilderDialog sceneBuilderDialog;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return NAME;
|
return NAME;
|
||||||
@ -82,21 +62,81 @@ public class ThemePage extends AbstractPage {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private final Lazy<ThemeRepoManagerDialog> themeRepoManagerDialog;
|
||||||
|
private final Lazy<ContrastCheckerDialog> contrastCheckerDialog;
|
||||||
|
private final Lazy<SceneBuilderDialog> sceneBuilderDialog;
|
||||||
|
private final ColorPalette colorPalette;
|
||||||
|
private final ColorScale colorScale = new ColorScale();
|
||||||
|
private final ChoiceBox<SamplerTheme> themeSelector = createThemeSelector();
|
||||||
|
private final ComboBox<String> fontFamilyChooser = createFontFamilyChooser();
|
||||||
|
private final Spinner<Integer> fontSizeSpinner = createFontSizeSpinner();
|
||||||
|
|
||||||
public ThemePage() {
|
public ThemePage() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
createView();
|
colorPalette = new ColorPalette(colorBlock -> {
|
||||||
|
ContrastCheckerDialog dialog = getContrastCheckerDialog();
|
||||||
|
dialog.getContent().setValues(
|
||||||
|
colorBlock.getFgColorName(),
|
||||||
|
colorBlock.getFgColor(),
|
||||||
|
colorBlock.getBgColorName(),
|
||||||
|
colorBlock.getBgColor()
|
||||||
|
);
|
||||||
|
overlay.setContent(dialog, HPos.CENTER);
|
||||||
|
overlay.toFront();
|
||||||
|
});
|
||||||
|
|
||||||
|
themeRepoManagerDialog = new Lazy<>(() -> {
|
||||||
|
var dialog = new ThemeRepoManagerDialog();
|
||||||
|
dialog.setOnCloseRequest(() -> {
|
||||||
|
overlay.removeContent();
|
||||||
|
overlay.toBack();
|
||||||
|
});
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
});
|
||||||
|
|
||||||
|
contrastCheckerDialog = new Lazy<>(() -> {
|
||||||
|
var dialog = new ContrastCheckerDialog(getBgBaseColorProperty());
|
||||||
|
dialog.setOnCloseRequest(() -> {
|
||||||
|
overlay.removeContent();
|
||||||
|
overlay.toBack();
|
||||||
|
});
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
});
|
||||||
|
|
||||||
|
sceneBuilderDialog = new Lazy<>(() -> {
|
||||||
|
var dialog = new SceneBuilderDialog();
|
||||||
|
dialog.setOnCloseRequest(() -> {
|
||||||
|
overlay.removeContent();
|
||||||
|
overlay.toBack();
|
||||||
|
dialog.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
});
|
||||||
|
|
||||||
DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> {
|
DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> {
|
||||||
if (e.getEventType() == THEME_ADD || e.getEventType() == THEME_REMOVE) {
|
var eventType = e.getEventType();
|
||||||
|
if (eventType == EventType.THEME_ADD || eventType == EventType.THEME_REMOVE) {
|
||||||
themeSelector.getItems().setAll(TM.getRepository().getAll());
|
themeSelector.getItems().setAll(TM.getRepository().getAll());
|
||||||
selectCurrentTheme();
|
selectCurrentTheme();
|
||||||
}
|
}
|
||||||
if (e.getEventType() == THEME_CHANGE || e.getEventType() == COLOR_CHANGE) {
|
if (eventType == EventType.THEME_CHANGE || eventType == EventType.COLOR_CHANGE) {
|
||||||
colorPalette.updateColorInfo(Duration.seconds(1));
|
colorPalette.updateColorInfo(Duration.seconds(1));
|
||||||
colorScale.updateColorInfo(Duration.seconds(1));
|
colorScale.updateColorInfo(Duration.seconds(1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addSection("Theme", createThemeManagementSection());
|
||||||
|
addSection("Scene Builder", createSceneBuilderSection());
|
||||||
|
addSection("Color Palette", createColorPaletteSection());
|
||||||
|
addSection("Color Scale", createColorScaleSection());
|
||||||
|
|
||||||
|
selectCurrentTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -106,31 +146,12 @@ public class ThemePage extends AbstractPage {
|
|||||||
colorScale.updateColorInfo(Duration.ZERO);
|
colorScale.updateColorInfo(Duration.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private Node createThemeManagementSection() {
|
||||||
var noteText = new TextFlow(
|
|
||||||
new Text("AtlantaFX follows "),
|
|
||||||
hyperlink("Github Primer interface guidelines",
|
|
||||||
URI.create("https://primer.style/design/foundations/color")),
|
|
||||||
new Text(" and color system.")
|
|
||||||
);
|
|
||||||
|
|
||||||
setUserContent(new VBox(
|
|
||||||
Page.PAGE_VGAP,
|
|
||||||
createOptionsGrid(),
|
|
||||||
noteText,
|
|
||||||
colorPalette,
|
|
||||||
colorScale
|
|
||||||
));
|
|
||||||
|
|
||||||
selectCurrentTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
private GridPane createOptionsGrid() {
|
|
||||||
var themeRepoBtn = new Button("", new FontIcon(Material2OutlinedMZ.SETTINGS));
|
var themeRepoBtn = new Button("", new FontIcon(Material2OutlinedMZ.SETTINGS));
|
||||||
themeRepoBtn.getStyleClass().addAll(Styles.BUTTON_ICON, Styles.FLAT);
|
themeRepoBtn.getStyleClass().addAll(Styles.BUTTON_ICON, Styles.FLAT);
|
||||||
themeRepoBtn.setTooltip(new Tooltip("Settings"));
|
themeRepoBtn.setTooltip(new Tooltip("Settings"));
|
||||||
themeRepoBtn.setOnAction(e -> {
|
themeRepoBtn.setOnAction(e -> {
|
||||||
ThemeRepoManagerDialog dialog = getOrCreateThemeRepoManagerDialog();
|
ThemeRepoManagerDialog dialog = themeRepoManagerDialog.get();
|
||||||
overlay.setContent(dialog, HPos.CENTER);
|
overlay.setContent(dialog, HPos.CENTER);
|
||||||
dialog.getContent().update();
|
dialog.getContent().update();
|
||||||
overlay.toFront();
|
overlay.toFront();
|
||||||
@ -138,41 +159,78 @@ public class ThemePage extends AbstractPage {
|
|||||||
|
|
||||||
var accentSelector = new AccentColorSelector();
|
var accentSelector = new AccentColorSelector();
|
||||||
|
|
||||||
var sceneBuilderBtn = new Button("SceneBuilder Integration");
|
|
||||||
sceneBuilderBtn.setGraphic(new ImageView(SCENE_BUILDER_ICON));
|
|
||||||
sceneBuilderBtn.setOnAction(e -> {
|
|
||||||
SceneBuilderDialog dialog = getOrCreateScneBuilderDialog();
|
|
||||||
overlay.setContent(dialog, HPos.CENTER);
|
|
||||||
overlay.toFront();
|
|
||||||
});
|
|
||||||
|
|
||||||
// ~
|
// ~
|
||||||
|
|
||||||
var grid = new GridPane();
|
var grid = new GridPane();
|
||||||
grid.setHgap(BLOCK_HGAP);
|
grid.setHgap(HGAP_20);
|
||||||
grid.setVgap(BLOCK_VGAP);
|
grid.setVgap(VGAP_10);
|
||||||
|
grid.addRow(0, new Label("Color theme"), themeSelector, themeRepoBtn);
|
||||||
grid.add(new Label("Color theme"), 0, 0);
|
grid.addRow(1, new Label("Accent color"), accentSelector);
|
||||||
grid.add(themeSelector, 1, 0);
|
grid.addRow(2, new Label("Font"), new HBox(10, fontFamilyChooser, fontSizeSpinner));
|
||||||
grid.add(themeRepoBtn, 2, 0);
|
|
||||||
grid.add(new Label("Accent color"), 0, 1);
|
|
||||||
grid.add(accentSelector, 1, 1);
|
|
||||||
grid.add(sceneBuilderBtn, 0, 2, GridPane.REMAINING, 1);
|
|
||||||
|
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Node createSceneBuilderSection() {
|
||||||
|
var sceneBuilderBtn = new Button("SceneBuilder Integration");
|
||||||
|
sceneBuilderBtn.setGraphic(new ImageView(SCENE_BUILDER_ICON));
|
||||||
|
sceneBuilderBtn.setOnAction(e -> {
|
||||||
|
SceneBuilderDialog dialog = sceneBuilderDialog.get();
|
||||||
|
overlay.setContent(dialog, HPos.CENTER);
|
||||||
|
overlay.toFront();
|
||||||
|
});
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
While SceneBuilder does not support adding custom themes, it is \
|
||||||
|
possible to overwrite looked-up CSS paths to make the existing \
|
||||||
|
SceneBuilder menu options load custom CSS files."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new VBox(VGAP_20, description, sceneBuilderBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node createColorPaletteSection() {
|
||||||
|
var description = createFormattedText("""
|
||||||
|
AtlantaFX follows [url=https://primer.style/design/foundations/color]GitHub \
|
||||||
|
Primer interface guidelines[/url] and color system.
|
||||||
|
|
||||||
|
Color contrast between text and its background must meet \
|
||||||
|
[url=https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html]required WCAG standards[/url]:
|
||||||
|
|
||||||
|
[ul]
|
||||||
|
[li]4.5:1 for normal text[/li]
|
||||||
|
[li]3:1 for large text (>24px)[/li]
|
||||||
|
[li]3:1 for UI elements and graphics[/li]
|
||||||
|
[li]no contrast requirement for decorative and disabled elements[/li][/ul]
|
||||||
|
|
||||||
|
Click on any color block to observe and modify color combination via built-in contrast checker.
|
||||||
|
""", true
|
||||||
|
);
|
||||||
|
|
||||||
|
return new VBox(VGAP_10, description, colorPalette);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node createColorScaleSection() {
|
||||||
|
var description = createFormattedText("""
|
||||||
|
Avoid referencing scale variables directly when building UI that needs \
|
||||||
|
to adapt to different color themes. Instead, use the functional variables \
|
||||||
|
listed above.""", false
|
||||||
|
);
|
||||||
|
|
||||||
|
return new VBox(VGAP_10, description, colorScale);
|
||||||
|
}
|
||||||
|
|
||||||
private ChoiceBox<SamplerTheme> createThemeSelector() {
|
private ChoiceBox<SamplerTheme> createThemeSelector() {
|
||||||
var selector = new ChoiceBox<SamplerTheme>();
|
var choiceBox = new ChoiceBox<SamplerTheme>();
|
||||||
selector.getItems().setAll(TM.getRepository().getAll());
|
choiceBox.getItems().setAll(TM.getRepository().getAll());
|
||||||
selector.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
choiceBox.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
||||||
if (val != null && getScene() != null) {
|
if (val != null && getScene() != null) {
|
||||||
TM.setTheme(val);
|
TM.setTheme(val);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
selector.setPrefWidth(250);
|
choiceBox.setPrefWidth(310);
|
||||||
|
|
||||||
selector.setConverter(new StringConverter<>() {
|
choiceBox.setConverter(new StringConverter<>() {
|
||||||
@Override
|
@Override
|
||||||
public String toString(SamplerTheme theme) {
|
public String toString(SamplerTheme theme) {
|
||||||
return theme != null ? theme.getName() : "";
|
return theme != null ? theme.getName() : "";
|
||||||
@ -187,56 +245,66 @@ public class ThemePage extends AbstractPage {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return selector;
|
return choiceBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComboBox<String> createFontFamilyChooser() {
|
||||||
|
var comboBox = new ComboBox<String>();
|
||||||
|
comboBox.setPrefWidth(200);
|
||||||
|
|
||||||
|
// keyword to reset font family to its default value
|
||||||
|
comboBox.getItems().add(DEFAULT_FONT_ID);
|
||||||
|
comboBox.getItems().addAll(FXCollections.observableArrayList(Font.getFamilies()));
|
||||||
|
|
||||||
|
// select active font family value on page load
|
||||||
|
comboBox.getSelectionModel().select(TM.getFontFamily());
|
||||||
|
comboBox.valueProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val != null) {
|
||||||
|
TM.setFontFamily(DEFAULT_FONT_ID.equals(val) ? ThemeManager.DEFAULT_FONT_FAMILY_NAME : val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return comboBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Spinner<Integer> createFontSizeSpinner() {
|
||||||
|
var spinner = new Spinner<Integer>(
|
||||||
|
ThemeManager.SUPPORTED_FONT_SIZE.get(0),
|
||||||
|
ThemeManager.SUPPORTED_FONT_SIZE.get(ThemeManager.SUPPORTED_FONT_SIZE.size() - 1),
|
||||||
|
TM.getFontSize()
|
||||||
|
);
|
||||||
|
spinner.setPrefWidth(100);
|
||||||
|
|
||||||
|
// Instead of this we should obtain font size from a rendered node.
|
||||||
|
// But since it's not trivial (thanks to JavaFX doesn't expose relevant API)
|
||||||
|
// we just keep current font size inside ThemeManager singleton.
|
||||||
|
// It works fine if ThemeManager default font size value matches
|
||||||
|
// default theme font size value.
|
||||||
|
spinner.getValueFactory().setValue(TM.getFontSize());
|
||||||
|
|
||||||
|
spinner.valueProperty().addListener((obs, old, val) -> {
|
||||||
|
if (val != null) {
|
||||||
|
TM.setFontSize(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return spinner;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectCurrentTheme() {
|
private void selectCurrentTheme() {
|
||||||
if (TM.getTheme() == null) {
|
if (TM.getTheme() != null) {
|
||||||
return;
|
themeSelector.getItems().stream()
|
||||||
|
.filter(t -> Objects.equals(TM.getTheme().getName(), t.getName()))
|
||||||
|
.findFirst()
|
||||||
|
.ifPresent(t -> themeSelector.getSelectionModel().select(t));
|
||||||
}
|
}
|
||||||
themeSelector.getItems().stream()
|
|
||||||
.filter(t -> Objects.equals(TM.getTheme().getName(), t.getName()))
|
|
||||||
.findFirst()
|
|
||||||
.ifPresent(t -> themeSelector.getSelectionModel().select(t));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ThemeRepoManagerDialog getOrCreateThemeRepoManagerDialog() {
|
private ContrastCheckerDialog getContrastCheckerDialog() {
|
||||||
if (themeRepoManagerDialog == null) {
|
return contrastCheckerDialog.get();
|
||||||
themeRepoManagerDialog = new ThemeRepoManagerDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
themeRepoManagerDialog.setOnCloseRequest(() -> {
|
|
||||||
overlay.removeContent();
|
|
||||||
overlay.toBack();
|
|
||||||
});
|
|
||||||
|
|
||||||
return themeRepoManagerDialog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContrastCheckerDialog getOrCreateContrastCheckerDialog() {
|
private ReadOnlyObjectProperty<Color> getBgBaseColorProperty() {
|
||||||
if (contrastCheckerDialog == null) {
|
return colorPalette.bgBaseColorProperty();
|
||||||
contrastCheckerDialog = new ContrastCheckerDialog(colorPalette.bgBaseColorProperty());
|
|
||||||
}
|
|
||||||
|
|
||||||
contrastCheckerDialog.setOnCloseRequest(() -> {
|
|
||||||
overlay.removeContent();
|
|
||||||
overlay.toBack();
|
|
||||||
});
|
|
||||||
|
|
||||||
return contrastCheckerDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SceneBuilderDialog getOrCreateScneBuilderDialog() {
|
|
||||||
if (sceneBuilderDialog == null) {
|
|
||||||
sceneBuilderDialog = new SceneBuilderDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
sceneBuilderDialog.setOnCloseRequest(() -> {
|
|
||||||
overlay.removeContent();
|
|
||||||
overlay.toBack();
|
|
||||||
sceneBuilderDialog.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
return sceneBuilderDialog;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,360 +2,323 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.general;
|
package atlantafx.sampler.page.general;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.ACCENT;
|
import static atlantafx.sampler.event.ThemeEvent.EventType;
|
||||||
import static atlantafx.base.theme.Styles.DANGER;
|
|
||||||
import static atlantafx.base.theme.Styles.SUCCESS;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_BOLD;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_BOLDER;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_CAPTION;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_ITALIC;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_LIGHTER;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_MUTED;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_NORMAL;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_OBLIQUE;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_SMALL;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_STRIKETHROUGH;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_SUBTLE;
|
|
||||||
import static atlantafx.base.theme.Styles.TEXT_UNDERLINED;
|
|
||||||
import static atlantafx.base.theme.Styles.TITLE_1;
|
|
||||||
import static atlantafx.base.theme.Styles.TITLE_2;
|
|
||||||
import static atlantafx.base.theme.Styles.TITLE_3;
|
|
||||||
import static atlantafx.base.theme.Styles.TITLE_4;
|
|
||||||
import static atlantafx.base.theme.Styles.WARNING;
|
|
||||||
import static atlantafx.sampler.event.ThemeEvent.EventType.FONT_CHANGE;
|
|
||||||
import static atlantafx.sampler.event.ThemeEvent.EventType.THEME_CHANGE;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
import static atlantafx.sampler.theme.ThemeManager.DEFAULT_FONT_FAMILY_NAME;
|
|
||||||
import static atlantafx.sampler.theme.ThemeManager.SUPPORTED_FONT_SIZE;
|
|
||||||
|
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
import atlantafx.sampler.event.DefaultEventBus;
|
import atlantafx.sampler.event.DefaultEventBus;
|
||||||
import atlantafx.sampler.event.ThemeEvent;
|
import atlantafx.sampler.event.ThemeEvent;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.theme.ThemeManager;
|
import atlantafx.sampler.page.Snippet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.animation.KeyFrame;
|
|
||||||
import javafx.animation.Timeline;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.Hyperlink;
|
import javafx.scene.control.Hyperlink;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Spinner;
|
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.FlowPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Font;
|
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
public class TypographyPage extends AbstractPage {
|
public class TypographyPage extends OutlinePage {
|
||||||
|
|
||||||
public static final String NAME = "Typography";
|
public static final String NAME = "Typography";
|
||||||
private static final int CONTROL_WIDTH = 200;
|
|
||||||
private static final String DEFAULT_FONT_ID = "Default";
|
|
||||||
private static final ThemeManager TM = ThemeManager.getInstance();
|
|
||||||
|
|
||||||
private Pane fontSizeSampleContent;
|
private GridPane fontSizeGridPane;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canDisplaySourceCode() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canChangeThemeSettings() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypographyPage() {
|
public TypographyPage() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
createView();
|
addFormattedText("""
|
||||||
|
Because AtlantaFX is also distributed as a single CSS file, it does not come \
|
||||||
|
with any fonts. However, it does support several utility classes demonstrated \
|
||||||
|
below that can be used to manipulate font properties. If you need a formatted \
|
||||||
|
text support have a look at [i]BBCodeParser[/i].""");
|
||||||
|
addSection("Font Size", fontSizeExample());
|
||||||
|
addSection("Font Weight", fontWeightExample());
|
||||||
|
addSection("Font Style", fontStyleExample());
|
||||||
|
addSection("Text Color", textColorExample());
|
||||||
|
addSection("Hyperlink", hyperlinkExample());
|
||||||
|
|
||||||
DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> {
|
DefaultEventBus.getInstance().subscribe(ThemeEvent.class, e -> {
|
||||||
if (e.getEventType() == THEME_CHANGE || e.getEventType() == FONT_CHANGE) {
|
var eventType = e.getEventType();
|
||||||
|
if (eventType == EventType.THEME_CHANGE || eventType == EventType.FONT_CHANGE) {
|
||||||
updateFontInfo(Duration.seconds(1));
|
updateFontInfo(Duration.seconds(1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
|
||||||
var controlsGrid = new GridPane();
|
|
||||||
controlsGrid.setHgap(BLOCK_HGAP);
|
|
||||||
controlsGrid.setVgap(BLOCK_VGAP);
|
|
||||||
controlsGrid.add(new Label("Font family"), 0, 0);
|
|
||||||
controlsGrid.add(createFontFamilyChooser(), 1, 0);
|
|
||||||
controlsGrid.add(new Label("Font size"), 0, 1);
|
|
||||||
controlsGrid.add(crateFontSizeSpinner(), 1, 1);
|
|
||||||
|
|
||||||
var fontSizeSample = fontSizeSample();
|
|
||||||
fontSizeSampleContent = (Pane) fontSizeSample.getContent();
|
|
||||||
|
|
||||||
setUserContent(new VBox(
|
|
||||||
PAGE_VGAP,
|
|
||||||
controlsGrid,
|
|
||||||
fontSizeSample,
|
|
||||||
fontWeightSample(),
|
|
||||||
expandingHBox(fontStyleSample(), textColorSample(), hyperlinkSample()),
|
|
||||||
textFlowSample()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onRendered() {
|
protected void onRendered() {
|
||||||
super.onRendered();
|
super.onRendered();
|
||||||
// font metrics can only be obtained by requesting from a rendered node
|
// font metrics can only be obtained by requesting from a rendered node
|
||||||
updateFontInfo(Duration.ZERO);
|
updateFontInfo(Duration.seconds(1));
|
||||||
}
|
|
||||||
|
|
||||||
private ComboBox<String> createFontFamilyChooser() {
|
|
||||||
ComboBox<String> comboBox = new ComboBox<>();
|
|
||||||
comboBox.getItems().add(DEFAULT_FONT_ID); // keyword to reset font family to its default value
|
|
||||||
comboBox.getItems().addAll(FXCollections.observableArrayList(Font.getFamilies()));
|
|
||||||
comboBox.setPrefWidth(CONTROL_WIDTH);
|
|
||||||
comboBox.getSelectionModel().select(TM.getFontFamily()); // select active font family value on page load
|
|
||||||
comboBox.valueProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null) {
|
|
||||||
TM.setFontFamily(DEFAULT_FONT_ID.equals(val) ? DEFAULT_FONT_FAMILY_NAME : val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return comboBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Spinner<Integer> crateFontSizeSpinner() {
|
|
||||||
var spinner = new Spinner<Integer>(
|
|
||||||
SUPPORTED_FONT_SIZE.get(0),
|
|
||||||
SUPPORTED_FONT_SIZE.get(SUPPORTED_FONT_SIZE.size() - 1),
|
|
||||||
TM.getFontSize()
|
|
||||||
);
|
|
||||||
spinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
|
|
||||||
spinner.setPrefWidth(CONTROL_WIDTH);
|
|
||||||
|
|
||||||
// Instead of this we should obtain font size from a rendered node.
|
|
||||||
// But since it's not trivial (thanks to JavaFX doesn't expose relevant API)
|
|
||||||
// we just keep current font size inside ThemeManager singleton.
|
|
||||||
// It works fine if ThemeManager default font size value matches
|
|
||||||
// default theme font size value.
|
|
||||||
spinner.getValueFactory().setValue(TM.getFontSize());
|
|
||||||
|
|
||||||
spinner.valueProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null) {
|
|
||||||
TM.setFontSize(val);
|
|
||||||
updateFontInfo(Duration.seconds(1));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return spinner;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFontInfo(Duration delay) {
|
private void updateFontInfo(Duration delay) {
|
||||||
var t = new Timeline(new KeyFrame(delay));
|
if (fontSizeGridPane == null) {
|
||||||
t.setOnFinished(e -> {
|
return;
|
||||||
Map<String, Node> map = fontSizeSampleContent.getChildren().stream()
|
}
|
||||||
.collect(Collectors.toMap(
|
|
||||||
n -> GridPane.getColumnIndex(n).toString() + GridPane.getRowIndex(n).toString(),
|
Map<String, Node> map = fontSizeGridPane.getChildren().stream()
|
||||||
n -> n
|
.collect(Collectors.toMap(
|
||||||
));
|
n -> GridPane.getColumnIndex(n).toString() + GridPane.getRowIndex(n).toString(),
|
||||||
((Label) map.get("10")).setText(String.format("%.0fpx", getFontSize(map.get("00"))));
|
n -> n
|
||||||
((Label) map.get("11")).setText(String.format("%.0fpx", getFontSize(map.get("01"))));
|
));
|
||||||
((Label) map.get("12")).setText(String.format("%.0fpx", getFontSize(map.get("02"))));
|
((Label) map.get("10")).setText(String.format("=%.0fpx", getFontSize(map.get("00"))));
|
||||||
((Label) map.get("13")).setText(String.format("%.0fpx", getFontSize(map.get("03"))));
|
((Label) map.get("11")).setText(String.format("=%.0fpx", getFontSize(map.get("01"))));
|
||||||
((Label) map.get("30")).setText(String.format("%.0fpx", getFontSize(map.get("20"))));
|
((Label) map.get("12")).setText(String.format("=%.0fpx", getFontSize(map.get("02"))));
|
||||||
((Label) map.get("31")).setText(String.format("%.0fpx", getFontSize(map.get("21"))));
|
((Label) map.get("13")).setText(String.format("=%.0fpx", getFontSize(map.get("03"))));
|
||||||
((Label) map.get("32")).setText(String.format("%.0fpx", getFontSize(map.get("22"))));
|
((Label) map.get("30")).setText(String.format("=%.0fpx", getFontSize(map.get("20"))));
|
||||||
});
|
((Label) map.get("31")).setText(String.format("=%.0fpx", getFontSize(map.get("21"))));
|
||||||
t.play();
|
((Label) map.get("32")).setText(String.format("=%.0fpx", getFontSize(map.get("22"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getFontSize(Node node) {
|
private double getFontSize(Node node) {
|
||||||
return (node instanceof Text text) ? Math.ceil(text.getFont().getSize()) : 0;
|
return (node instanceof Text text) ? Math.ceil(text.getFont().getSize()) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock fontSizeSample() {
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private ExampleBox fontSizeExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var title1Text = new Text("Title 1");
|
||||||
|
title1Text.getStyleClass().addAll(Styles.TITLE_1);
|
||||||
|
|
||||||
|
var title2Text = new Text("Title 2");
|
||||||
|
title2Text.getStyleClass().addAll(Styles.TITLE_2);
|
||||||
|
|
||||||
|
var title3Text = new Text("Title 3");
|
||||||
|
title3Text.getStyleClass().addAll(Styles.TITLE_3);
|
||||||
|
|
||||||
|
var title4Text = new Text("Title 4");
|
||||||
|
title4Text.getStyleClass().addAll(Styles.TITLE_4);
|
||||||
|
|
||||||
|
var captionText = new Text("Caption");
|
||||||
|
captionText.getStyleClass().addAll(Styles.TEXT_CAPTION);
|
||||||
|
|
||||||
|
var defaultText = new Text("Default");
|
||||||
|
|
||||||
|
var smallText = new Text("Small");
|
||||||
|
smallText.getStyleClass().addAll(Styles.TEXT_SMALL);
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
var grid = new GridPane();
|
var grid = new GridPane();
|
||||||
grid.setHgap(BLOCK_HGAP);
|
grid.setHgap(HGAP_20);
|
||||||
grid.setVgap(BLOCK_VGAP);
|
grid.setVgap(VGAP_20);
|
||||||
|
grid.addRow(0,
|
||||||
grid.add(createText("Title 1", TITLE_1), 0, 0);
|
title1Text, createFontSizeLabel(),
|
||||||
grid.add(createFontSizeLabel(), 1, 0);
|
captionText, createFontSizeLabel()
|
||||||
grid.add(createText("Title 2", TITLE_2), 0, 1);
|
);
|
||||||
grid.add(createFontSizeLabel(), 1, 1);
|
grid.addRow(1,
|
||||||
grid.add(createText("Title 3", TITLE_3), 0, 2);
|
title2Text, createFontSizeLabel(),
|
||||||
grid.add(createFontSizeLabel(), 1, 2);
|
defaultText, createFontSizeLabel()
|
||||||
grid.add(createText("Title 4", TITLE_4), 0, 3);
|
);
|
||||||
grid.add(createFontSizeLabel(), 1, 3);
|
grid.addRow(2,
|
||||||
|
title3Text, createFontSizeLabel(),
|
||||||
grid.add(createText("Caption", TEXT_CAPTION), 2, 0);
|
smallText, createFontSizeLabel()
|
||||||
grid.add(createFontSizeLabel(), 3, 0);
|
);
|
||||||
grid.add(createText("Default"), 2, 1);
|
grid.addRow(3, title4Text, createFontSizeLabel());
|
||||||
grid.add(createFontSizeLabel(), 3, 1);
|
|
||||||
grid.add(createText("Small", TEXT_SMALL), 2, 2);
|
|
||||||
grid.add(createFontSizeLabel(), 3, 2);
|
|
||||||
|
|
||||||
grid.setAlignment(Pos.BASELINE_LEFT);
|
grid.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
return new SampleBlock("Font Size", grid);
|
fontSizeGridPane = grid;
|
||||||
|
|
||||||
|
var example = new ExampleBox(grid, new Snippet(getClass(), 1));
|
||||||
|
example.setAllowDisable(false);
|
||||||
|
|
||||||
|
return example;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock fontWeightSample() {
|
private ExampleBox fontWeightExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
class StyledText extends Text {
|
||||||
|
|
||||||
|
public StyledText(String text, String style) {
|
||||||
|
super();
|
||||||
|
setText(text);
|
||||||
|
setStyle(style + "-fx-fill:-color-fg-default;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var boldText = new Text("Bold");
|
||||||
|
boldText.getStyleClass().addAll(Styles.TEXT_BOLD);
|
||||||
|
|
||||||
|
var bolderText = new Text("Bolder");
|
||||||
|
bolderText.getStyleClass().addAll(Styles.TEXT_BOLDER);
|
||||||
|
|
||||||
|
var normalText = new Text("Normal");
|
||||||
|
normalText.getStyleClass().addAll(Styles.TEXT_NORMAL);
|
||||||
|
|
||||||
|
var lighterText = new Text("Lighter");
|
||||||
|
lighterText.getStyleClass().addAll(Styles.TEXT_LIGHTER);
|
||||||
|
|
||||||
var sample1 = new HBox(
|
var sample1 = new HBox(
|
||||||
BLOCK_HGAP,
|
HGAP_20, boldText, bolderText, normalText, lighterText
|
||||||
createText("Bold", TEXT_BOLD),
|
|
||||||
createText("Bolder", TEXT_BOLDER),
|
|
||||||
createText("Normal", TEXT_NORMAL),
|
|
||||||
createText("Lighter", TEXT_LIGHTER)
|
|
||||||
);
|
);
|
||||||
sample1.setAlignment(Pos.BASELINE_LEFT);
|
sample1.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
|
// ~
|
||||||
var sample2 = new HBox(
|
var sample2 = new HBox(
|
||||||
BLOCK_HGAP,
|
HGAP_20,
|
||||||
createStyledText("900", "-fx-font-weight:900;"),
|
new StyledText("900", "-fx-font-weight:900;"),
|
||||||
createStyledText("800", "-fx-font-weight:800;"),
|
new StyledText("800", "-fx-font-weight:800;"),
|
||||||
createStyledText("700", "-fx-font-weight:700;"),
|
new StyledText("700", "-fx-font-weight:700;"),
|
||||||
createStyledText("600", "-fx-font-weight:600;"),
|
new StyledText("600", "-fx-font-weight:600;"),
|
||||||
createStyledText("500", "-fx-font-weight:500;"),
|
new StyledText("500", "-fx-font-weight:500;"),
|
||||||
createStyledText("400", "-fx-font-weight:400;"),
|
new StyledText("400", "-fx-font-weight:400;"),
|
||||||
createStyledText("300", "-fx-font-weight:300;"),
|
new StyledText("300", "-fx-font-weight:300;"),
|
||||||
createStyledText("200", "-fx-font-weight:200;"),
|
new StyledText("200", "-fx-font-weight:200;"),
|
||||||
createStyledText("100", "-fx-font-weight:100;")
|
new StyledText("100", "-fx-font-weight:100;"),
|
||||||
|
new Text("\uD83E\uDC60 no difference")
|
||||||
);
|
);
|
||||||
sample2.setAlignment(Pos.BASELINE_LEFT);
|
sample2.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
|
// JDK-8090423:
|
||||||
|
// https://bugs.openjdk.org/browse/JDK-8090423
|
||||||
|
// Workaround:
|
||||||
|
// https://edencoding.com/resources/css_properties/fx-font-weight/
|
||||||
var sample3 = new HBox(
|
var sample3 = new HBox(
|
||||||
BLOCK_HGAP,
|
HGAP_20,
|
||||||
createStyledText("900", "-fx-font-family:'Inter Black';"),
|
new StyledText("900", "-fx-font-family:'Inter Black';"),
|
||||||
createStyledText("800", "-fx-font-family:'Inter Extra Bold';"),
|
new StyledText("800", "-fx-font-family:'Inter Extra Bold';"),
|
||||||
createStyledText("700", "-fx-font-family:'Inter Bold';"),
|
new StyledText("700", "-fx-font-family:'Inter Bold';"),
|
||||||
createStyledText("600", "-fx-font-family:'Inter Semi Bold';"),
|
new StyledText("600", "-fx-font-family:'Inter Semi Bold';"),
|
||||||
createStyledText("500", "-fx-font-family:'Inter Medium';"),
|
new StyledText("500", "-fx-font-family:'Inter Medium';"),
|
||||||
createStyledText("400", "-fx-font-family:'Inter Regular';"),
|
new StyledText("400", "-fx-font-family:'Inter Regular';"),
|
||||||
createStyledText("300", "-fx-font-family:'Inter Light';"),
|
new StyledText("300", "-fx-font-family:'Inter Light';"),
|
||||||
createStyledText("200", "-fx-font-family:'Inter Extra Light';"),
|
new StyledText("200", "-fx-font-family:'Inter Extra Light';"),
|
||||||
createStyledText("100", "-fx-font-family:'Inter Thin';")
|
new StyledText("100", "-fx-font-family:'Inter Thin';"),
|
||||||
|
new Text("\uD83E\uDC60 workaround")
|
||||||
);
|
);
|
||||||
sample3.setAlignment(Pos.BASELINE_LEFT);
|
sample3.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
// JDK-8090423: https://bugs.openjdk.org/browse/JDK-8090423
|
var box = new VBox(VGAP_20, sample1, sample2, sample3);
|
||||||
// Workaround: https://edencoding.com/resources/css_properties/fx-font-weight/
|
box.setAlignment(Pos.CENTER_LEFT);
|
||||||
return new SampleBlock("Font Weight", new VBox(
|
|
||||||
BLOCK_VGAP,
|
var description = BBCodeParser.createFormattedText("""
|
||||||
sample1,
|
JavaFX [color="-color-danger-fg"]only supports Bold or Regular[/color] font weight. \
|
||||||
sample2,
|
See the source code for workaround."""
|
||||||
sample3,
|
);
|
||||||
createText("JavaFX only supports Bold or Regular font weight. See the source code for workaround.", TEXT,
|
|
||||||
WARNING)
|
var example = new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
));
|
example.setAllowDisable(false);
|
||||||
|
|
||||||
|
return example;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock fontStyleSample() {
|
private ExampleBox fontStyleExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var italicText = new Text("Italic");
|
||||||
|
italicText.getStyleClass().addAll(
|
||||||
|
Styles.TEXT, Styles.TEXT_ITALIC
|
||||||
|
);
|
||||||
|
|
||||||
|
var obliqueText = new Text("Oblique");
|
||||||
|
obliqueText.getStyleClass().addAll(
|
||||||
|
Styles.TEXT, Styles.TEXT_OBLIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
var underlinedText = new Text("Underlined");
|
||||||
|
underlinedText.getStyleClass().addAll(
|
||||||
|
Styles.TEXT, Styles.TEXT_UNDERLINED
|
||||||
|
);
|
||||||
|
|
||||||
|
var strikethroughText = new Text("Strikethrough");
|
||||||
|
strikethroughText.getStyleClass().addAll(
|
||||||
|
Styles.TEXT, Styles.TEXT_STRIKETHROUGH
|
||||||
|
);
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
var box = new FlowPane(
|
var box = new FlowPane(
|
||||||
BLOCK_HGAP, BLOCK_VGAP,
|
HGAP_20, VGAP_20,
|
||||||
createText("Italic", TEXT_ITALIC),
|
italicText, obliqueText, underlinedText, strikethroughText
|
||||||
createText("Oblique", TEXT_OBLIQUE),
|
|
||||||
createText("Underlined", TEXT_UNDERLINED),
|
|
||||||
createText("Strikethrough", TEXT_STRIKETHROUGH)
|
|
||||||
);
|
);
|
||||||
box.setAlignment(Pos.BASELINE_LEFT);
|
box.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
return new SampleBlock("Font Style", box);
|
var example = new ExampleBox(box, new Snippet(getClass(), 3));
|
||||||
|
example.setAllowDisable(false);
|
||||||
|
|
||||||
|
return example;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock textColorSample() {
|
private ExampleBox textColorExample() {
|
||||||
|
//snippet_4:start
|
||||||
|
var accentText = new Text("Accent");
|
||||||
|
accentText.getStyleClass().addAll(Styles.TEXT, Styles.ACCENT);
|
||||||
|
|
||||||
|
var successText = new Text("Success");
|
||||||
|
successText.getStyleClass().addAll(Styles.TEXT, Styles.SUCCESS);
|
||||||
|
|
||||||
|
var warningText = new Text("Warning");
|
||||||
|
warningText.getStyleClass().addAll(Styles.TEXT, Styles.WARNING);
|
||||||
|
|
||||||
|
var dangerText = new Text("Danger");
|
||||||
|
dangerText.getStyleClass().addAll(Styles.TEXT, Styles.DANGER);
|
||||||
|
|
||||||
|
var mutedText = new Text("Muted");
|
||||||
|
mutedText.getStyleClass().addAll(Styles.TEXT, Styles.TEXT_MUTED);
|
||||||
|
|
||||||
|
var subtleText = new Text("Subtle");
|
||||||
|
subtleText.getStyleClass().addAll(Styles.TEXT, Styles.TEXT_SUBTLE);
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
var box = new FlowPane(
|
var box = new FlowPane(
|
||||||
BLOCK_HGAP, BLOCK_VGAP,
|
HGAP_20, VGAP_20,
|
||||||
createText("Accent", TEXT, ACCENT),
|
accentText, successText, warningText, dangerText, mutedText, subtleText
|
||||||
createText("Success", TEXT, SUCCESS),
|
|
||||||
createText("Warning", TEXT, WARNING),
|
|
||||||
createText("Danger", TEXT, DANGER),
|
|
||||||
createText("Muted", TEXT, TEXT_MUTED),
|
|
||||||
createText("Subtle", TEXT, TEXT_SUBTLE)
|
|
||||||
);
|
);
|
||||||
box.setAlignment(Pos.BASELINE_LEFT);
|
box.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
return new SampleBlock("Text Color", box);
|
var example = new ExampleBox(box, new Snippet(getClass(), 4));
|
||||||
|
example.setAllowDisable(false);
|
||||||
|
|
||||||
|
return example;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleBlock hyperlinkSample() {
|
private ExampleBox hyperlinkExample() {
|
||||||
var linkNormal = createHyperlink("_Normal", false, false);
|
//snippet_5:start
|
||||||
|
var linkNormal = new Hyperlink("_Normal");
|
||||||
linkNormal.setMnemonicParsing(true);
|
linkNormal.setMnemonicParsing(true);
|
||||||
|
|
||||||
var linkVisited = createHyperlink("_Visited", true, false);
|
var linkVisited = new Hyperlink("_Visited");
|
||||||
|
linkVisited.setVisited(true);
|
||||||
linkVisited.setMnemonicParsing(true);
|
linkVisited.setMnemonicParsing(true);
|
||||||
|
|
||||||
var linkBroken = createHyperlink("_Broken", true, false);
|
var linkBroken = new Hyperlink("_Broken");
|
||||||
linkBroken.setStyle("-color-link-fg-visited:-color-danger-fg;");
|
linkBroken.setStyle("-color-link-fg-visited:-color-danger-fg;");
|
||||||
|
linkBroken.setVisited(true);
|
||||||
linkBroken.setMnemonicParsing(true);
|
linkBroken.setMnemonicParsing(true);
|
||||||
|
|
||||||
|
var linkDisabled = new Hyperlink("Disabled");
|
||||||
|
linkDisabled.setDisable(true);
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
var box = new FlowPane(
|
var box = new FlowPane(
|
||||||
BLOCK_HGAP, BLOCK_VGAP,
|
HGAP_20, VGAP_20,
|
||||||
linkNormal,
|
linkNormal, linkVisited, linkBroken, linkDisabled
|
||||||
linkVisited,
|
|
||||||
linkBroken,
|
|
||||||
createHyperlink("Disabled", false, true)
|
|
||||||
);
|
);
|
||||||
box.setAlignment(Pos.BASELINE_LEFT);
|
box.setAlignment(Pos.BASELINE_LEFT);
|
||||||
|
|
||||||
return new SampleBlock("Hyperlink", box);
|
var example = new ExampleBox(box, new Snippet(getClass(), 5));
|
||||||
}
|
example.setAllowDisable(false);
|
||||||
|
|
||||||
private SampleBlock textFlowSample() {
|
return example;
|
||||||
var textFlow = new TextFlow(
|
|
||||||
new Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. "),
|
|
||||||
new Hyperlink("Vivamus at lorem"),
|
|
||||||
new Text(" in urna facilisis aliquam. Morbi ut "),
|
|
||||||
new Hyperlink("velit"),
|
|
||||||
new Text(" iaculis erat cursus molestie eget laoreet quam. "),
|
|
||||||
new Text(" Vivamus eu nulla sapien. Sed et malesuada augue. Nullam nec "),
|
|
||||||
new Hyperlink("consectetur"),
|
|
||||||
new Text(" "),
|
|
||||||
new Hyperlink("ipsum"),
|
|
||||||
new Text(", eget facilisis enim. Suspendisse potenti. Nulla euismod, nisl sed dapibus pretium,"
|
|
||||||
+ " augue ligula finibus arcu, in iaculis nulla neque a est. Sed in rutrum diam."
|
|
||||||
+ " Donec quis arcu molestie, facilisis ex fringilla, "),
|
|
||||||
new Hyperlink("volutpat velit"),
|
|
||||||
new Text(".")
|
|
||||||
);
|
|
||||||
|
|
||||||
return new SampleBlock("Text Flow", textFlow);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Text createText(String text, String... styleClasses) {
|
|
||||||
var t = new Text(text);
|
|
||||||
t.getStyleClass().addAll(styleClasses);
|
|
||||||
t.setUserData(text);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Text createStyledText(String text, String style) {
|
|
||||||
var t = new Text(text);
|
|
||||||
t.setStyle(style);
|
|
||||||
return t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Label createFontSizeLabel() {
|
private Label createFontSizeLabel() {
|
||||||
var label = new Label();
|
var label = new Label();
|
||||||
label.setPadding(new Insets(5, 40, 5, 10));
|
label.setPadding(new Insets(5, 40, 5, 10));
|
||||||
|
label.setStyle("-fx-font-family:monospace;");
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Hyperlink createHyperlink(String text, boolean visited, boolean disabled) {
|
|
||||||
var h = new Hyperlink(text);
|
|
||||||
h.setVisited(visited);
|
|
||||||
h.setDisable(disabled);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,68 +1,114 @@
|
|||||||
package atlantafx.sampler.page.showcase;
|
package atlantafx.sampler.page.showcase;
|
||||||
|
|
||||||
import static atlantafx.base.theme.Styles.ACCENT;
|
import atlantafx.base.controls.Spacer;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.sampler.page.AbstractPage;
|
import atlantafx.sampler.page.Page;
|
||||||
import atlantafx.sampler.util.NodeUtils;
|
import java.util.Objects;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
import javafx.geometry.Insets;
|
import javafx.scene.Node;
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Hyperlink;
|
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.StageStyle;
|
import javafx.stage.StageStyle;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
public abstract class ShowcasePage extends AbstractPage {
|
public abstract class ShowcasePage extends StackPane implements Page {
|
||||||
|
|
||||||
protected static final PseudoClass SHOWCASE_MODE = PseudoClass.getPseudoClass("showcase-mode");
|
protected static final PseudoClass SHOWCASE_MODE = PseudoClass.getPseudoClass("showcase-mode");
|
||||||
protected final StackPane showcase = new StackPane();
|
protected static final int DEFAULT_WIDTH = 800;
|
||||||
protected final HBox expandBox = new HBox(10);
|
protected static final int DEFAULT_HEIGHT = 600;
|
||||||
protected final HBox collapseBox = new HBox(10);
|
|
||||||
|
protected VBox showcaseWindow = new VBox();
|
||||||
|
protected Label windowTitle = new Label();
|
||||||
|
protected VBox showCaseContent = new VBox();
|
||||||
|
protected int windowWidth = DEFAULT_WIDTH;
|
||||||
|
protected int windowHeight = DEFAULT_HEIGHT;
|
||||||
|
protected BooleanProperty maximized = new SimpleBooleanProperty();
|
||||||
|
|
||||||
public ShowcasePage() {
|
public ShowcasePage() {
|
||||||
|
super();
|
||||||
|
|
||||||
createShowcaseLayout();
|
createShowcaseLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void createShowcaseLayout() {
|
||||||
|
windowTitle.getStyleClass().addAll("title");
|
||||||
|
|
||||||
|
var maximizeBtn = new FontIcon(Feather.MAXIMIZE_2);
|
||||||
|
maximizeBtn.getStyleClass().addAll(Styles.SMALL, Styles.FLAT);
|
||||||
|
maximizeBtn.setOnMouseClicked(e -> maximized.set(!maximized.get()));
|
||||||
|
|
||||||
|
var windowHeader = new HBox(windowTitle, new Spacer(), maximizeBtn);
|
||||||
|
windowHeader.getStyleClass().add("header");
|
||||||
|
|
||||||
|
showcaseWindow.getStyleClass().add("window");
|
||||||
|
showcaseWindow.getChildren().setAll(windowHeader, showCaseContent);
|
||||||
|
VBox.setVgrow(showCaseContent, Priority.ALWAYS);
|
||||||
|
|
||||||
|
getStyleClass().add("showcase-page");
|
||||||
|
getChildren().setAll(showcaseWindow);
|
||||||
|
|
||||||
|
maximized.addListener((obs, old, val) -> {
|
||||||
|
getScene().getRoot().pseudoClassStateChanged(SHOWCASE_MODE, val);
|
||||||
|
maximizeBtn.setIconCode(val ? Feather.MINIMIZE_2 : Feather.MAXIMIZE_2);
|
||||||
|
|
||||||
|
var width = val ? -1 : windowWidth;
|
||||||
|
var height = val ? -1 : windowHeight;
|
||||||
|
showcaseWindow.setMinSize(width, height);
|
||||||
|
showcaseWindow.setMaxSize(width, height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setWindowTitle(String text, @Nullable Node graphic) {
|
||||||
|
windowTitle.setText(Objects.requireNonNull(text, "text"));
|
||||||
|
if (graphic != null) {
|
||||||
|
windowTitle.setGraphic(graphic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setShowCaseContent(Node node) {
|
||||||
|
setShowCaseContent(node, DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setShowCaseContent(Node node, int width, int height) {
|
||||||
|
Objects.requireNonNull(node, "node");
|
||||||
|
|
||||||
|
this.windowWidth = width > 0 ? width : DEFAULT_WIDTH;
|
||||||
|
this.windowHeight = height > 0 ? height : DEFAULT_HEIGHT;
|
||||||
|
showcaseWindow.setMinSize(width, height);
|
||||||
|
showcaseWindow.setMaxSize(width, height);
|
||||||
|
|
||||||
|
showCaseContent.getChildren().setAll(node);
|
||||||
|
VBox.setVgrow(node, Priority.ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pane getView() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canDisplaySourceCode() {
|
public boolean canDisplaySourceCode() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createShowcaseLayout() {
|
@Override
|
||||||
var expandBtn = new Button("Expand");
|
public boolean canChangeThemeSettings() {
|
||||||
expandBtn.setGraphic(new FontIcon(Feather.MAXIMIZE_2));
|
return true;
|
||||||
expandBtn.getStyleClass().add(ACCENT);
|
}
|
||||||
expandBtn.setOnAction(e -> {
|
|
||||||
expandBtn.getScene().getRoot().pseudoClassStateChanged(SHOWCASE_MODE, true);
|
@Override
|
||||||
VBox.setVgrow(showcase, Priority.ALWAYS);
|
public void reset() {
|
||||||
NodeUtils.toggleVisibility(expandBox, false);
|
|
||||||
NodeUtils.toggleVisibility(collapseBox, true);
|
|
||||||
});
|
|
||||||
expandBox.getChildren().setAll(expandBtn);
|
|
||||||
expandBox.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
var collapseBtn = new Hyperlink("Exit showcase mode");
|
|
||||||
collapseBtn.setOnAction(e -> {
|
|
||||||
expandBtn.getScene().getRoot().pseudoClassStateChanged(SHOWCASE_MODE, false);
|
|
||||||
VBox.setVgrow(showcase, Priority.NEVER);
|
|
||||||
NodeUtils.toggleVisibility(expandBox, true);
|
|
||||||
NodeUtils.toggleVisibility(collapseBox, false);
|
|
||||||
});
|
|
||||||
collapseBox.getChildren().setAll(new FontIcon(Feather.MINIMIZE_2), collapseBtn);
|
|
||||||
collapseBox.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
collapseBox.setPadding(new Insets(5));
|
|
||||||
NodeUtils.toggleVisibility(collapseBox, false);
|
|
||||||
|
|
||||||
setUserContent(new VBox(showcase, expandBox, collapseBox));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("SameParameterValue")
|
|
||||||
protected void showWarning(String header, String description) {
|
protected void showWarning(String header, String description) {
|
||||||
var alert = new Alert(Alert.AlertType.WARNING);
|
var alert = new Alert(Alert.AlertType.WARNING);
|
||||||
alert.setTitle("Error Dialog");
|
alert.setTitle("Error Dialog");
|
||||||
|
@ -2,6 +2,7 @@ package atlantafx.sampler.page.showcase.filemanager;
|
|||||||
|
|
||||||
import static atlantafx.sampler.page.showcase.filemanager.Model.USER_HOME;
|
import static atlantafx.sampler.page.showcase.filemanager.Model.USER_HOME;
|
||||||
|
|
||||||
|
import atlantafx.base.theme.Tweaks;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
@ -14,7 +15,7 @@ import org.kordamp.ikonli.javafx.FontIcon;
|
|||||||
final class BookmarkList extends ListView<Bookmark> {
|
final class BookmarkList extends ListView<Bookmark> {
|
||||||
|
|
||||||
public BookmarkList(Model model) {
|
public BookmarkList(Model model) {
|
||||||
getStyleClass().add("bookmark-list");
|
getStyleClass().addAll("bookmark-list", Tweaks.EDGE_TO_EDGE);
|
||||||
|
|
||||||
// this is Linux specific and only for EN locale
|
// this is Linux specific and only for EN locale
|
||||||
getItems().setAll(
|
getItems().setAll(
|
||||||
|
@ -7,7 +7,6 @@ import static atlantafx.base.theme.Styles.FLAT;
|
|||||||
import static atlantafx.sampler.page.showcase.filemanager.FileList.PREDICATE_ANY;
|
import static atlantafx.sampler.page.showcase.filemanager.FileList.PREDICATE_ANY;
|
||||||
import static atlantafx.sampler.page.showcase.filemanager.FileList.PREDICATE_NOT_HIDDEN;
|
import static atlantafx.sampler.page.showcase.filemanager.FileList.PREDICATE_NOT_HIDDEN;
|
||||||
import static atlantafx.sampler.page.showcase.filemanager.Utils.openFile;
|
import static atlantafx.sampler.page.showcase.filemanager.Utils.openFile;
|
||||||
import static atlantafx.sampler.util.Controls.hyperlink;
|
|
||||||
import static atlantafx.sampler.util.Controls.iconButton;
|
import static atlantafx.sampler.util.Controls.iconButton;
|
||||||
|
|
||||||
import atlantafx.base.controls.Breadcrumbs;
|
import atlantafx.base.controls.Breadcrumbs;
|
||||||
@ -16,7 +15,6 @@ import atlantafx.base.controls.Spacer;
|
|||||||
import atlantafx.base.theme.Tweaks;
|
import atlantafx.base.theme.Tweaks;
|
||||||
import atlantafx.sampler.page.showcase.ShowcasePage;
|
import atlantafx.sampler.page.showcase.ShowcasePage;
|
||||||
import atlantafx.sampler.util.Containers;
|
import atlantafx.sampler.util.Containers;
|
||||||
import java.net.URI;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -24,7 +22,6 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ButtonBase;
|
import javafx.scene.control.ButtonBase;
|
||||||
@ -35,9 +32,7 @@ import javafx.scene.control.MenuItem;
|
|||||||
import javafx.scene.control.SplitPane;
|
import javafx.scene.control.SplitPane;
|
||||||
import javafx.scene.control.ToolBar;
|
import javafx.scene.control.ToolBar;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
@ -123,27 +118,12 @@ public class FileManagerPage extends ShowcasePage {
|
|||||||
(obs, old, val) -> splitPane.setDividerPosition(0, 200 / splitPane.getWidth())
|
(obs, old, val) -> splitPane.setDividerPosition(0, 200 / splitPane.getWidth())
|
||||||
);
|
);
|
||||||
|
|
||||||
var aboutBox = new HBox(new Text(
|
|
||||||
"Simple file manager. You can traverse through the file system and open files"
|
|
||||||
+ " via default system application."
|
|
||||||
));
|
|
||||||
aboutBox.setPadding(new Insets(5, 0, 5, 0));
|
|
||||||
aboutBox.getStyleClass().add("about");
|
|
||||||
|
|
||||||
var creditsBox = new HBox(5,
|
|
||||||
new Text("Inspired by ©"),
|
|
||||||
hyperlink("Gnome Files", URI.create("https://gitlab.gnome.org/GNOME/nautilus"))
|
|
||||||
);
|
|
||||||
creditsBox.getStyleClass().add("credits");
|
|
||||||
creditsBox.setAlignment(Pos.CENTER_RIGHT);
|
|
||||||
creditsBox.setPadding(new Insets(5, 0, 5, 0));
|
|
||||||
|
|
||||||
var root = new BorderPane();
|
var root = new BorderPane();
|
||||||
root.getStyleClass().add("file-manager-showcase");
|
root.getStyleClass().add("file-manager-showcase");
|
||||||
root.getStylesheets().add(STYLESHEET_URL);
|
root.getStylesheets().add(STYLESHEET_URL);
|
||||||
root.setTop(new VBox(aboutBox, topBar));
|
root.setTop(new VBox(topBar));
|
||||||
root.setCenter(splitPane);
|
root.setCenter(splitPane);
|
||||||
root.setBottom(creditsBox);
|
//root.setBottom(creditsBox);
|
||||||
|
|
||||||
toggleHiddenCheck.selectedProperty().addListener((obs, old, val) -> directoryView.getFileList()
|
toggleHiddenCheck.selectedProperty().addListener((obs, old, val) -> directoryView.getFileList()
|
||||||
.predicateProperty()
|
.predicateProperty()
|
||||||
@ -164,7 +144,9 @@ public class FileManagerPage extends ShowcasePage {
|
|||||||
directoryView.setDirectory(val);
|
directoryView.setDirectory(val);
|
||||||
});
|
});
|
||||||
|
|
||||||
showcase.getChildren().setAll(root);
|
setWindowTitle("File Manager", new FontIcon(Material2AL.FOLDER));
|
||||||
|
setShowCaseContent(root);
|
||||||
|
|
||||||
Containers.setAnchors(root, Insets.EMPTY);
|
Containers.setAnchors(root, Insets.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ final class TableDirectoryView extends AnchorPane implements DirectoryView {
|
|||||||
// ~
|
// ~
|
||||||
|
|
||||||
var table = new TableView<Path>();
|
var table = new TableView<Path>();
|
||||||
table.getStyleClass().add(Styles.STRIPED);
|
table.getStyleClass().addAll(Styles.STRIPED, Tweaks.EDGE_TO_EDGE);
|
||||||
table.getColumns().setAll(filenameCol, sizeCol, mtimeCol);
|
table.getColumns().setAll(filenameCol, sizeCol, mtimeCol);
|
||||||
table.getSortOrder().add(filenameCol);
|
table.getSortOrder().add(filenameCol);
|
||||||
table.setSortPolicy(param -> true);
|
table.setSortPolicy(param -> true);
|
||||||
|
@ -1,16 +1,9 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
.file-manager-showcase .bookmark-list {
|
|
||||||
-fx-border-width: 0 0 1 1;
|
|
||||||
}
|
|
||||||
.file-manager-showcase .bookmark-list .ikonli-font-icon {
|
.file-manager-showcase .bookmark-list .ikonli-font-icon {
|
||||||
-fx-icon-size: 18px;
|
-fx-icon-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-manager-showcase .tool-bar {
|
|
||||||
-fx-background-insets: 0, 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-manager-showcase .breadcrumbs >.divider {
|
.file-manager-showcase .breadcrumbs >.divider {
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
}
|
}
|
||||||
@ -19,7 +12,6 @@
|
|||||||
-color-header-bg: -color-bg-default;
|
-color-header-bg: -color-bg-default;
|
||||||
-color-cell-bg-selected: -color-accent-emphasis;
|
-color-cell-bg-selected: -color-accent-emphasis;
|
||||||
-color-cell-fg-selected: -color-fg-emphasis;
|
-color-cell-fg-selected: -color-fg-emphasis;
|
||||||
-fx-border-width: 0 1 1 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* setting opacity directly to the .table-cell or .table-row-cell
|
/* setting opacity directly to the .table-cell or .table-row-cell
|
||||||
|
@ -2,23 +2,17 @@
|
|||||||
|
|
||||||
package atlantafx.sampler.page.showcase.musicplayer;
|
package atlantafx.sampler.page.showcase.musicplayer;
|
||||||
|
|
||||||
import static atlantafx.sampler.util.Controls.hyperlink;
|
|
||||||
|
|
||||||
import atlantafx.sampler.page.showcase.ShowcasePage;
|
import atlantafx.sampler.page.showcase.ShowcasePage;
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import org.kordamp.ikonli.feather.Feather;
|
||||||
import javafx.scene.text.Text;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
public class MusicPlayerPage extends ShowcasePage {
|
public class MusicPlayerPage extends ShowcasePage {
|
||||||
|
|
||||||
public static final String NAME = "Music Player";
|
public static final String NAME = "Music Player";
|
||||||
public static final double BACKGROUND_OPACITY = 0.2;
|
|
||||||
public static final Set<String> SUPPORTED_MEDIA_TYPES = Set.of("mp3");
|
public static final Set<String> SUPPORTED_MEDIA_TYPES = Set.of("mp3");
|
||||||
private static final String STYLESHEET_URL = Objects.requireNonNull(
|
private static final String STYLESHEET_URL = Objects.requireNonNull(
|
||||||
MusicPlayerPage.class.getResource("music-player.css")).toExternalForm();
|
MusicPlayerPage.class.getResource("music-player.css")).toExternalForm();
|
||||||
@ -36,26 +30,11 @@ public class MusicPlayerPage extends ShowcasePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createView() {
|
private void createView() {
|
||||||
var aboutBox = new HBox(new Text("Simple music player that able to play MP3 files."));
|
|
||||||
aboutBox.setPadding(new Insets(5, 0, 5, 0));
|
|
||||||
aboutBox.getStyleClass().add("about");
|
|
||||||
|
|
||||||
var creditsBox = new HBox(5,
|
|
||||||
new Text("Inspired by ©"),
|
|
||||||
hyperlink("Amberol", URI.create("https://gitlab.gnome.org/World/amberol"))
|
|
||||||
);
|
|
||||||
creditsBox.getStyleClass().add("credits");
|
|
||||||
creditsBox.setAlignment(Pos.CENTER_RIGHT);
|
|
||||||
creditsBox.setPadding(new Insets(5, 0, 5, 0));
|
|
||||||
|
|
||||||
// ~
|
|
||||||
var startScreen = new StartScreen(model);
|
var startScreen = new StartScreen(model);
|
||||||
var playerScreen = new PlayerScreen(model);
|
var playerScreen = new PlayerScreen(model);
|
||||||
|
|
||||||
var root = new BorderPane();
|
var root = new BorderPane();
|
||||||
root.setCenter(startScreen);
|
root.setCenter(startScreen);
|
||||||
root.setTop(aboutBox);
|
|
||||||
root.setBottom(creditsBox);
|
|
||||||
|
|
||||||
root.getStylesheets().add(STYLESHEET_URL);
|
root.getStylesheets().add(STYLESHEET_URL);
|
||||||
root.getStyleClass().add("music-player-showcase");
|
root.getStyleClass().add("music-player-showcase");
|
||||||
@ -68,7 +47,8 @@ public class MusicPlayerPage extends ShowcasePage {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
showcase.getChildren().setAll(root);
|
setWindowTitle("Music Player", new FontIcon(Feather.MUSIC));
|
||||||
|
setShowCaseContent(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,160 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import javafx.scene.Group;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.Parent;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.Separator;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
|
|
||||||
public class Card extends VBox {
|
|
||||||
|
|
||||||
public static final String CSS = """
|
|
||||||
.card {
|
|
||||||
-fx-background-color: -color-bg-default;
|
|
||||||
}
|
|
||||||
.card > .subtitle {
|
|
||||||
-fx-text-fill: -color-fg-muted;
|
|
||||||
-fx-padding: 0px 15px 10px 15px;
|
|
||||||
}
|
|
||||||
.card > .title,
|
|
||||||
.card > .body,
|
|
||||||
.card > .footer {
|
|
||||||
-fx-padding: 10px 15px 10px 15px;
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
private final StringProperty title = new SimpleStringProperty();
|
|
||||||
private final StringProperty subtitle = new SimpleStringProperty();
|
|
||||||
private final ObjectProperty<ImageView> image = new SimpleObjectProperty<>();
|
|
||||||
private final ObjectProperty<Parent> body = new SimpleObjectProperty<>();
|
|
||||||
private final ObjectProperty<Parent> footer = new SimpleObjectProperty<>();
|
|
||||||
|
|
||||||
public Card() {
|
|
||||||
super();
|
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
|
||||||
var footerSep = new Separator();
|
|
||||||
footerSep.getStyleClass().add(Styles.SMALL);
|
|
||||||
footerSep.managedProperty().bind(Bindings.createObjectBinding(
|
|
||||||
() -> footer.get() != null && footer.get().isManaged(), footer
|
|
||||||
));
|
|
||||||
|
|
||||||
getChildren().setAll(
|
|
||||||
createPlaceholder(), // title
|
|
||||||
createPlaceholder(), // subtitle
|
|
||||||
createPlaceholder(), // image
|
|
||||||
createPlaceholder(), // body
|
|
||||||
footerSep,
|
|
||||||
createPlaceholder() // footer
|
|
||||||
);
|
|
||||||
|
|
||||||
image.addListener(
|
|
||||||
(obs, old, val) -> setChild(0, val, "image")
|
|
||||||
);
|
|
||||||
title.addListener(
|
|
||||||
(obs, old, val) -> setChild(1, val != null ? new Label(val) : null, "title", Styles.TITLE_4)
|
|
||||||
);
|
|
||||||
subtitle.addListener(
|
|
||||||
(obs, old, val) -> setChild(2, val != null ? new Label(val) : null, "subtitle")
|
|
||||||
);
|
|
||||||
body.addListener(
|
|
||||||
(obs, old, val) -> setChild(3, val, "body")
|
|
||||||
);
|
|
||||||
footer.addListener(
|
|
||||||
(obs, old, val) -> setChild(5, val, "footer")
|
|
||||||
);
|
|
||||||
|
|
||||||
getStyleClass().addAll("card", Styles.BORDERED);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setChild(int index, Node node, String... styleClass) {
|
|
||||||
if (node != null) {
|
|
||||||
for (var s : styleClass) {
|
|
||||||
if (!node.getStyleClass().contains(s)) {
|
|
||||||
node.getStyleClass().add(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getChildren().set(index, node);
|
|
||||||
} else {
|
|
||||||
getChildren().set(index, createPlaceholder());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return title.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty titleProperty() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.title.set(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSubtitle() {
|
|
||||||
return subtitle.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringProperty subtitleProperty() {
|
|
||||||
return subtitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSubtitle(String subtitle) {
|
|
||||||
this.subtitle.set(subtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageView getImage() {
|
|
||||||
return image.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<ImageView> imageProperty() {
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImage(ImageView image) {
|
|
||||||
this.image.set(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Parent getBody() {
|
|
||||||
return body.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Parent> bodyProperty() {
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBody(Parent body) {
|
|
||||||
this.body.set(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Parent getFooter() {
|
|
||||||
return footer.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Parent> footerProperty() {
|
|
||||||
return footer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFooter(Parent footer) {
|
|
||||||
this.footer.set(footer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Parent createPlaceholder() {
|
|
||||||
var g = new Group();
|
|
||||||
g.setManaged(false);
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,180 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import static atlantafx.sampler.page.Page.PAGE_HGAP;
|
|
||||||
import static atlantafx.sampler.page.Page.PAGE_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.sampler.Resources;
|
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.Hyperlink;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.image.PixelReader;
|
|
||||||
import javafx.scene.image.WritableImage;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import net.datafaker.Faker;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.material2.Material2AL;
|
|
||||||
|
|
||||||
public class CardSample extends HBox {
|
|
||||||
|
|
||||||
private static final Faker FAKER = new Faker();
|
|
||||||
private static final int CARD_WIDTH = 300;
|
|
||||||
|
|
||||||
public CardSample() {
|
|
||||||
new CSSFragment(Card.CSS).addTo(this);
|
|
||||||
|
|
||||||
setSpacing(PAGE_HGAP);
|
|
||||||
setAlignment(Pos.TOP_CENTER);
|
|
||||||
setMinWidth(CARD_WIDTH * 2 + PAGE_HGAP);
|
|
||||||
getChildren().setAll(
|
|
||||||
// column 0
|
|
||||||
new VBox(
|
|
||||||
PAGE_VGAP,
|
|
||||||
textFooterCard(),
|
|
||||||
titleTextCard(),
|
|
||||||
quoteCard()
|
|
||||||
),
|
|
||||||
// column 1
|
|
||||||
new VBox(
|
|
||||||
PAGE_VGAP,
|
|
||||||
imageTextCard(),
|
|
||||||
titleImageCard(),
|
|
||||||
statisticCard()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Card textFooterCard() {
|
|
||||||
var card = new Card();
|
|
||||||
card.getStyleClass().add(Styles.ELEVATED_1);
|
|
||||||
card.setMinWidth(CARD_WIDTH);
|
|
||||||
card.setMaxWidth(CARD_WIDTH);
|
|
||||||
|
|
||||||
var text = new Text(FAKER.chuckNorris().fact());
|
|
||||||
card.setBody(new TextFlow(text));
|
|
||||||
|
|
||||||
var btn = new Button("More!");
|
|
||||||
btn.getStyleClass().addAll(Styles.ACCENT, Styles.BUTTON_OUTLINED);
|
|
||||||
btn.setOnAction(e -> text.setText(FAKER.chuckNorris().fact()));
|
|
||||||
|
|
||||||
card.setFooter(new HBox(btn));
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Card imageTextCard() {
|
|
||||||
var card = new Card();
|
|
||||||
card.getStyleClass().add(Styles.ELEVATED_4);
|
|
||||||
card.setMinWidth(CARD_WIDTH);
|
|
||||||
card.setMaxWidth(CARD_WIDTH);
|
|
||||||
|
|
||||||
var image = new ImageView(new Image(Resources.getResourceAsStream("images/20_min_adventure.jpg")));
|
|
||||||
image.setFitWidth(300);
|
|
||||||
image.setPreserveRatio(true);
|
|
||||||
card.setImage(image);
|
|
||||||
|
|
||||||
var text = new Text(FAKER.rickAndMorty().quote());
|
|
||||||
card.setBody(new TextFlow(text));
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Card titleTextCard() {
|
|
||||||
var card = new Card();
|
|
||||||
card.getStyleClass().add(Styles.INTERACTIVE);
|
|
||||||
card.setMinWidth(CARD_WIDTH);
|
|
||||||
card.setMaxWidth(CARD_WIDTH);
|
|
||||||
|
|
||||||
card.setTitle("Interactive");
|
|
||||||
card.setSubtitle("Subtitle");
|
|
||||||
|
|
||||||
var text = new Text(FAKER.lorem().paragraph());
|
|
||||||
card.setBody(new TextFlow(text));
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Card titleImageCard() {
|
|
||||||
var card = new Card();
|
|
||||||
card.getStyleClass().add(Styles.ELEVATED_2);
|
|
||||||
card.setMinWidth(CARD_WIDTH);
|
|
||||||
card.setMaxWidth(CARD_WIDTH);
|
|
||||||
|
|
||||||
var image = new Image(Resources.getResourceAsStream("images/pattern.jpg"));
|
|
||||||
PixelReader pixelReader = image.getPixelReader();
|
|
||||||
var cropImage = new WritableImage(pixelReader, 0, 0, 300, 100);
|
|
||||||
|
|
||||||
card.setImage(new ImageView(cropImage));
|
|
||||||
card.setTitle("Title");
|
|
||||||
|
|
||||||
var text = new Text(FAKER.lorem().sentence());
|
|
||||||
card.setBody(new TextFlow(text));
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Card quoteCard() {
|
|
||||||
var card = new Card();
|
|
||||||
card.getStyleClass().add(Styles.ELEVATED_3);
|
|
||||||
card.setMinWidth(CARD_WIDTH);
|
|
||||||
card.setMaxWidth(CARD_WIDTH);
|
|
||||||
|
|
||||||
var quoteText = new Text(FAKER.bojackHorseman().quotes());
|
|
||||||
quoteText.getStyleClass().add(Styles.TITLE_3);
|
|
||||||
|
|
||||||
var authorText = new Text("Bojack Horseman");
|
|
||||||
|
|
||||||
card.setBody(new VBox(
|
|
||||||
10,
|
|
||||||
new TextFlow(quoteText),
|
|
||||||
authorText
|
|
||||||
));
|
|
||||||
|
|
||||||
card.setFooter(new TextFlow(
|
|
||||||
new Text("Share on "),
|
|
||||||
new Hyperlink("Twitter")
|
|
||||||
));
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Card statisticCard() {
|
|
||||||
var card = new Card();
|
|
||||||
card.setMinWidth(CARD_WIDTH);
|
|
||||||
card.setMaxWidth(CARD_WIDTH);
|
|
||||||
card.setTitle("Statistic");
|
|
||||||
|
|
||||||
var grid = new GridPane();
|
|
||||||
grid.setHgap(40);
|
|
||||||
grid.setVgap(10);
|
|
||||||
card.setBody(grid);
|
|
||||||
|
|
||||||
var leftHead = new Text("Active");
|
|
||||||
leftHead.getStyleClass().add(Styles.TEXT_MUTED);
|
|
||||||
grid.add(leftHead, 0, 0);
|
|
||||||
|
|
||||||
var leftData = new Label("12.87", new FontIcon(Material2AL.ARROW_UPWARD));
|
|
||||||
leftData.getStyleClass().addAll(Styles.SUCCESS, Styles.TITLE_2);
|
|
||||||
grid.add(leftData, 0, 1);
|
|
||||||
|
|
||||||
var rightHead = new Text("Idle");
|
|
||||||
rightHead.getStyleClass().add(Styles.TEXT_MUTED);
|
|
||||||
grid.add(rightHead, 1, 0);
|
|
||||||
|
|
||||||
var rightData = new Label("3.74", new FontIcon(Material2AL.ARROW_DOWNWARD));
|
|
||||||
rightData.getStyleClass().addAll(Styles.DANGER, Styles.TITLE_2);
|
|
||||||
grid.add(rightData, 1, 1);
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import javafx.animation.FadeTransition;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.text.TextFlow;
|
|
||||||
import javafx.util.Duration;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.material2.Material2AL;
|
|
||||||
|
|
||||||
public class Message extends StackPane {
|
|
||||||
|
|
||||||
private static final int ANIMATION_DURATION = 500;
|
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
INFO, SUCCESS, WARNING, DANGER
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String CSS = """
|
|
||||||
.message {
|
|
||||||
-color-message-bg: -color-bg-default;
|
|
||||||
-color-message-fg: -color-fg-default;
|
|
||||||
-fx-background-color: -color-message-bg;
|
|
||||||
-fx-border-color: -color-message-fg;
|
|
||||||
-fx-border-width: 0 0 0 5px;
|
|
||||||
-fx-pref-width: 600px;
|
|
||||||
-fx-alignment: TOP_LEFT;
|
|
||||||
}
|
|
||||||
.message > .header {
|
|
||||||
-fx-font-weight: bold;
|
|
||||||
}
|
|
||||||
.message Text {
|
|
||||||
-fx-fill: -color-message-fg;
|
|
||||||
}
|
|
||||||
.message > .button {
|
|
||||||
-color-button-fg: -color-message-fg;
|
|
||||||
}
|
|
||||||
.message.info {
|
|
||||||
-color-message-bg: -color-accent-subtle;
|
|
||||||
-color-message-fg: -color-accent-fg;
|
|
||||||
}
|
|
||||||
.message.success {
|
|
||||||
-color-message-bg: -color-success-subtle;
|
|
||||||
-color-message-fg: -color-success-fg;
|
|
||||||
}
|
|
||||||
.message.warning {
|
|
||||||
-color-message-bg: -color-warning-subtle;
|
|
||||||
-color-message-fg: -color-warning-fg;
|
|
||||||
}
|
|
||||||
.message.danger {
|
|
||||||
-color-message-bg: -color-danger-subtle;
|
|
||||||
-color-message-fg: -color-danger-fg;
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
private final Type type;
|
|
||||||
private final String header;
|
|
||||||
private final String text;
|
|
||||||
private Consumer<Message> closeHandler;
|
|
||||||
|
|
||||||
public Message(Type type, String header, String text) {
|
|
||||||
this.type = Objects.requireNonNull(type);
|
|
||||||
this.header = header;
|
|
||||||
this.text = Objects.requireNonNull(text);
|
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
|
||||||
if (header != null) {
|
|
||||||
var headerText = new Text(header);
|
|
||||||
headerText.getStyleClass().addAll("header");
|
|
||||||
StackPane.setMargin(headerText, new Insets(10, 10, 0, 15));
|
|
||||||
getChildren().add(headerText);
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageText = new TextFlow(new Text(text));
|
|
||||||
if (header != null) {
|
|
||||||
StackPane.setMargin(messageText, new Insets(40, 10, 10, 15));
|
|
||||||
} else {
|
|
||||||
StackPane.setMargin(messageText, new Insets(10, 10, 10, 15));
|
|
||||||
}
|
|
||||||
messageText.maxWidthProperty().bind(widthProperty().subtract(50));
|
|
||||||
getChildren().add(messageText);
|
|
||||||
|
|
||||||
var closeBtn = new Button("", new FontIcon(Material2AL.CLOSE));
|
|
||||||
closeBtn.getStyleClass().addAll(Styles.BUTTON_CIRCLE, Styles.FLAT);
|
|
||||||
closeBtn.setOnAction(e -> handleClose());
|
|
||||||
StackPane.setMargin(closeBtn, new Insets(2));
|
|
||||||
StackPane.setAlignment(closeBtn, Pos.TOP_RIGHT);
|
|
||||||
getChildren().add(closeBtn);
|
|
||||||
|
|
||||||
parentProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val != null) {
|
|
||||||
handleOpen();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getStyleClass().setAll("message", type.name().toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHeader() {
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getText() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCloseHandler(Consumer<Message> closeHandler) {
|
|
||||||
this.closeHandler = closeHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleOpen() {
|
|
||||||
var transition = new FadeTransition(new Duration(500), this);
|
|
||||||
transition.setFromValue(0);
|
|
||||||
transition.setToValue(1);
|
|
||||||
transition.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleClose() {
|
|
||||||
var transition = new FadeTransition(new Duration(ANIMATION_DURATION), this);
|
|
||||||
transition.setFromValue(1);
|
|
||||||
transition.setToValue(0);
|
|
||||||
transition.setOnFinished(e -> {
|
|
||||||
if (getParent() != null && getParent() instanceof Pane pane) {
|
|
||||||
pane.getChildren().remove(this);
|
|
||||||
}
|
|
||||||
if (closeHandler != null) {
|
|
||||||
closeHandler.accept(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
transition.play();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
|
|
||||||
public class MessageSample extends SampleBlock {
|
|
||||||
|
|
||||||
public MessageSample() {
|
|
||||||
super("Message", createContent());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Node createContent() {
|
|
||||||
var content = new VBox(BLOCK_VGAP);
|
|
||||||
content.setAlignment(Pos.TOP_CENTER);
|
|
||||||
VBox.setVgrow(content, Priority.ALWAYS);
|
|
||||||
new CSSFragment(Message.CSS).addTo(content);
|
|
||||||
|
|
||||||
var closeHandler = new Consumer<Message>() {
|
|
||||||
@Override
|
|
||||||
public void accept(Message msg) {
|
|
||||||
var newMsg = new Message(msg.getType(), msg.getHeader(), FAKER.chuckNorris().fact());
|
|
||||||
newMsg.setCloseHandler(this);
|
|
||||||
content.getChildren().add(newMsg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (Message.Type type : Message.Type.values()) {
|
|
||||||
var msg = new Message(type, type.name(), FAKER.chuckNorris().fact());
|
|
||||||
msg.setCloseHandler(closeHandler);
|
|
||||||
content.getChildren().add(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,259 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.css.PseudoClass;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.geometry.Side;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.ContentDisplay;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.Separator;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import org.kordamp.ikonli.Ikon;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
|
|
||||||
public class Stepper extends HBox {
|
|
||||||
|
|
||||||
public static final String CSS = """
|
|
||||||
.stepper {
|
|
||||||
-color-stepper-bg: -color-bg-subtle;
|
|
||||||
-color-stepper-fg: -color-fg-default;
|
|
||||||
-color-stepper-border: -color-border-default;
|
|
||||||
-fx-pref-width: 600px;
|
|
||||||
-fx-spacing: 10px;
|
|
||||||
}
|
|
||||||
.stepper > .item {
|
|
||||||
-fx-graphic-text-gap: 10px;
|
|
||||||
}
|
|
||||||
.stepper > .item > .graphic {
|
|
||||||
-fx-font-size: 1.1em;
|
|
||||||
-fx-min-width: 2.2em;
|
|
||||||
-fx-max-width: 2.2em;
|
|
||||||
-fx-min-height: 2.2em;
|
|
||||||
-fx-max-height: 2.2em;
|
|
||||||
-fx-text-fill: -color-stepper-fg;
|
|
||||||
-fx-background-color: -color-stepper-bg;
|
|
||||||
-fx-background-radius: 10em;
|
|
||||||
-fx-border-color: -color-stepper-border;
|
|
||||||
-fx-border-radius: 10em;
|
|
||||||
-fx-border-width: 1;
|
|
||||||
-fx-alignment: CENTER;
|
|
||||||
}
|
|
||||||
.stepper > .item .ikonli-font-icon {
|
|
||||||
-fx-fill: -color-stepper-fg;
|
|
||||||
-fx-icon-color: -color-stepper-fg;
|
|
||||||
}
|
|
||||||
.stepper > .item:selected {
|
|
||||||
-color-stepper-bg: -color-bg-default;
|
|
||||||
-color-stepper-fg: -color-accent-fg;
|
|
||||||
-color-stepper-border: -color-accent-emphasis;
|
|
||||||
}
|
|
||||||
.stepper > .item:selected > .graphic {
|
|
||||||
-color-stepper-bg: -color-bg-default;
|
|
||||||
-color-stepper-fg: -color-accent-fg;
|
|
||||||
-color-stepper-border: -color-accent-emphasis;
|
|
||||||
-fx-border-width: 2;
|
|
||||||
}
|
|
||||||
.stepper > .item:completed {
|
|
||||||
-color-stepper-bg: -color-accent-emphasis;
|
|
||||||
-color-stepper-fg: -color-fg-emphasis;
|
|
||||||
-color-stepper-border: -color-accent-emphasis;
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
|
|
||||||
private static final PseudoClass COMPLETED = PseudoClass.getPseudoClass("completed");
|
|
||||||
|
|
||||||
private final List<Item> items;
|
|
||||||
private final ObjectProperty<Side> textPosition = new SimpleObjectProperty<>(Side.LEFT);
|
|
||||||
private final ObjectProperty<Item> selectedItem = new SimpleObjectProperty<>();
|
|
||||||
private final BooleanBinding canGoBack;
|
|
||||||
private final BooleanBinding canGoForward;
|
|
||||||
|
|
||||||
public Stepper(Item... items) {
|
|
||||||
this(Arrays.asList(items));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stepper(List<Item> items) {
|
|
||||||
if (items == null || items.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Item list can't be null or empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.items = Collections.unmodifiableList(items);
|
|
||||||
|
|
||||||
canGoBack = Bindings.createBooleanBinding(() -> {
|
|
||||||
if (selectedItem.get() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var idx = items.indexOf(selectedItem.get());
|
|
||||||
return idx > 0 && idx <= items.size() - 1;
|
|
||||||
}, selectedItem);
|
|
||||||
|
|
||||||
canGoForward = Bindings.createBooleanBinding(() -> {
|
|
||||||
if (selectedItem.get() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var idx = items.indexOf(selectedItem.get());
|
|
||||||
return idx >= 0 && idx < items.size() - 1;
|
|
||||||
}, selectedItem);
|
|
||||||
|
|
||||||
selectedItem.addListener((obs, old, val) -> {
|
|
||||||
if (old != null) {
|
|
||||||
old.pseudoClassStateChanged(SELECTED, false);
|
|
||||||
}
|
|
||||||
if (val != null) {
|
|
||||||
val.pseudoClassStateChanged(SELECTED, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
|
||||||
alignmentProperty().bind(Bindings.createObjectBinding(
|
|
||||||
() -> switch (textPositionProperty().get()) {
|
|
||||||
case TOP -> Pos.TOP_LEFT;
|
|
||||||
case BOTTOM -> Pos.BOTTOM_LEFT;
|
|
||||||
default -> Pos.CENTER_LEFT;
|
|
||||||
}, textPositionProperty())
|
|
||||||
);
|
|
||||||
|
|
||||||
updateItems();
|
|
||||||
getStyleClass().add("stepper");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateItems() {
|
|
||||||
var children = new ArrayList<Node>();
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
|
||||||
var item = items.get(i);
|
|
||||||
item.contentDisplayProperty().bind(Bindings.createObjectBinding(
|
|
||||||
() -> switch (textPositionProperty().get()) {
|
|
||||||
case TOP -> ContentDisplay.TOP;
|
|
||||||
case BOTTOM -> ContentDisplay.BOTTOM;
|
|
||||||
case LEFT -> ContentDisplay.LEFT;
|
|
||||||
case RIGHT -> ContentDisplay.RIGHT;
|
|
||||||
}, textPositionProperty())
|
|
||||||
);
|
|
||||||
|
|
||||||
children.add(item);
|
|
||||||
|
|
||||||
if (i < items.size() - 1) {
|
|
||||||
var sep = new Separator();
|
|
||||||
HBox.setHgrow(sep, Priority.ALWAYS);
|
|
||||||
children.add(sep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getChildren().setAll(children);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Item> getItems() {
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Side getTextPosition() {
|
|
||||||
return textPosition.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTextPosition(Side textPosition) {
|
|
||||||
this.textPosition.set(textPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Side> textPositionProperty() {
|
|
||||||
return textPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Item getSelectedItem() {
|
|
||||||
return selectedItem.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Item> selectedItemProperty() {
|
|
||||||
return selectedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelectedItem(Item selectedItem) {
|
|
||||||
this.selectedItem.set(selectedItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding canGoBackProperty() {
|
|
||||||
return canGoBack;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void backward() {
|
|
||||||
if (!canGoBack.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var idx = items.indexOf(selectedItem.get());
|
|
||||||
selectedItem.set(items.get(idx - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding canGoForwardProperty() {
|
|
||||||
return canGoForward;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void forward() {
|
|
||||||
if (!canGoForward.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var idx = items.indexOf(selectedItem.get());
|
|
||||||
selectedItem.set(items.get(idx + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public static class Item extends Label {
|
|
||||||
|
|
||||||
private final BooleanProperty completed = new SimpleBooleanProperty();
|
|
||||||
|
|
||||||
public Item(String text) {
|
|
||||||
super(text);
|
|
||||||
|
|
||||||
var graphicLabel = new Label();
|
|
||||||
graphicLabel.getStyleClass().add("graphic");
|
|
||||||
setGraphic(graphicLabel);
|
|
||||||
|
|
||||||
completed.addListener((obs, old, val) -> pseudoClassStateChanged(COMPLETED, val));
|
|
||||||
getStyleClass().add("item");
|
|
||||||
setContentDisplay(ContentDisplay.LEFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGraphic(Ikon icon) {
|
|
||||||
var graphicLabel = ((Label) getGraphic());
|
|
||||||
if (icon != null) {
|
|
||||||
graphicLabel.setText(null);
|
|
||||||
graphicLabel.setGraphic(new FontIcon(icon));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGraphic(String text) {
|
|
||||||
var graphicLabel = ((Label) getGraphic());
|
|
||||||
if (text != null) {
|
|
||||||
graphicLabel.setText(text);
|
|
||||||
graphicLabel.setGraphic(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCompleted() {
|
|
||||||
return completed.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCompleted(boolean state) {
|
|
||||||
completed.set(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty completedProperty() {
|
|
||||||
return completed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import atlantafx.base.controls.Spacer;
|
|
||||||
import atlantafx.base.controls.ToggleSwitch;
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.page.showcase.widget.Stepper.Item;
|
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.geometry.Side;
|
|
||||||
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.StackPane;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.material2.Material2MZ;
|
|
||||||
|
|
||||||
public class StepperSample extends SampleBlock {
|
|
||||||
|
|
||||||
public StepperSample() {
|
|
||||||
super("Stepper", createContent());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Node createContent() {
|
|
||||||
var content = new VBox(BLOCK_VGAP);
|
|
||||||
new CSSFragment(Stepper.CSS).addTo(content);
|
|
||||||
|
|
||||||
// == STEPPER CONTENT ==
|
|
||||||
|
|
||||||
var stackContent = new Label();
|
|
||||||
stackContent.getStyleClass().add(Styles.TITLE_1);
|
|
||||||
stackContent.setStyle("-fx-background-color:-color-bg-default;");
|
|
||||||
stackContent.setWrapText(true);
|
|
||||||
stackContent.setMinHeight(200);
|
|
||||||
stackContent.setMaxWidth(400);
|
|
||||||
stackContent.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
var stack = new StackPane(stackContent);
|
|
||||||
stack.setPrefHeight(200);
|
|
||||||
|
|
||||||
// == STEPPER ==
|
|
||||||
|
|
||||||
var firstItem = new Item("First");
|
|
||||||
firstItem.setGraphic("A");
|
|
||||||
|
|
||||||
var secondItem = new Item("Second");
|
|
||||||
secondItem.setGraphic("B");
|
|
||||||
|
|
||||||
var thirdItem = new Item("Third");
|
|
||||||
thirdItem.setGraphic("C");
|
|
||||||
|
|
||||||
var stepper = new Stepper(firstItem, secondItem, thirdItem);
|
|
||||||
stepper.selectedItemProperty().addListener(
|
|
||||||
(obs, old, val) -> stackContent.setText(val != null ? val.getText() : null)
|
|
||||||
);
|
|
||||||
stepper.setSelectedItem(stepper.getItems().get(0));
|
|
||||||
|
|
||||||
// == CONTROLS ==
|
|
||||||
|
|
||||||
var nextBtn = new Button("Next");
|
|
||||||
nextBtn.setDefaultButton(true);
|
|
||||||
nextBtn.setOnAction(e -> {
|
|
||||||
// you can validate user input before moving forward here
|
|
||||||
stepper.getSelectedItem().setCompleted(true);
|
|
||||||
stepper.forward();
|
|
||||||
});
|
|
||||||
nextBtn.textProperty().bind(Bindings.createStringBinding(
|
|
||||||
() -> stepper.canGoForwardProperty().get() ? "Next" : "Done", stepper.canGoForwardProperty())
|
|
||||||
);
|
|
||||||
|
|
||||||
var prevBtn = new Button("Previous");
|
|
||||||
prevBtn.getStyleClass().addAll(Styles.FLAT);
|
|
||||||
prevBtn.setOnAction(e -> {
|
|
||||||
stepper.getSelectedItem().setCompleted(false);
|
|
||||||
stepper.backward();
|
|
||||||
});
|
|
||||||
prevBtn.disableProperty().bind(stepper.canGoBackProperty().not());
|
|
||||||
|
|
||||||
var iconToggle = new ToggleSwitch("Icons");
|
|
||||||
iconToggle.selectedProperty().addListener((obs, old, val) -> {
|
|
||||||
for (int i = 0; i < stepper.getItems().size(); i++) {
|
|
||||||
var item = stepper.getItems().get(i);
|
|
||||||
if (val) {
|
|
||||||
item.setGraphic(randomIcon());
|
|
||||||
} else {
|
|
||||||
item.setGraphic(String.valueOf(i + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var rotateBtn = new Button("Rotate", new FontIcon(Material2MZ.ROTATE_RIGHT));
|
|
||||||
rotateBtn.setOnAction(e -> {
|
|
||||||
Side nextSide = switch (stepper.getTextPosition()) {
|
|
||||||
case LEFT -> Side.TOP;
|
|
||||||
case TOP -> Side.RIGHT;
|
|
||||||
case RIGHT -> Side.BOTTOM;
|
|
||||||
case BOTTOM -> Side.LEFT;
|
|
||||||
};
|
|
||||||
stepper.setTextPosition(nextSide);
|
|
||||||
});
|
|
||||||
|
|
||||||
var controls = new HBox(
|
|
||||||
BLOCK_HGAP,
|
|
||||||
nextBtn,
|
|
||||||
prevBtn,
|
|
||||||
new Spacer(),
|
|
||||||
iconToggle,
|
|
||||||
rotateBtn
|
|
||||||
);
|
|
||||||
controls.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
content.getChildren().setAll(stepper, stack, new Separator(), controls);
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import static javafx.scene.control.ContentDisplay.RIGHT;
|
|
||||||
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
|
|
||||||
public class Tag extends Button {
|
|
||||||
|
|
||||||
public static final String CSS = """
|
|
||||||
.tag {
|
|
||||||
-fx-padding: 4px 6px 4px 6px;
|
|
||||||
-fx-cursor: hand;
|
|
||||||
-color-button-border-hover: -color-button-border;
|
|
||||||
-color-button-border-focused: -color-button-border;
|
|
||||||
-color-button-border-pressed: -color-button-border;
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
public Tag(String text) {
|
|
||||||
this(text, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Tag(String text, Node graphic) {
|
|
||||||
super(text, graphic);
|
|
||||||
|
|
||||||
if (graphic != null) {
|
|
||||||
graphic.setOnMouseEntered(e -> {
|
|
||||||
if (getContentDisplay() == RIGHT) {
|
|
||||||
graphic.setScaleX(1.2);
|
|
||||||
graphic.setScaleY(1.2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
graphic.setOnMouseExited(e -> {
|
|
||||||
if (getContentDisplay() == RIGHT) {
|
|
||||||
graphic.setScaleX(1);
|
|
||||||
graphic.setScaleY(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
graphic.setOnMouseClicked(e -> {
|
|
||||||
if (getContentDisplay() == RIGHT && getParent() != null && getParent() instanceof Pane pane) {
|
|
||||||
pane.getChildren().remove(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getStyleClass().add("tag");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,157 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import static atlantafx.sampler.page.Page.PAGE_HGAP;
|
|
||||||
import static atlantafx.sampler.page.Page.PAGE_VGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_HGAP;
|
|
||||||
import static atlantafx.sampler.page.SampleBlock.BLOCK_VGAP;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.sampler.page.SampleBlock;
|
|
||||||
import atlantafx.sampler.theme.CSSFragment;
|
|
||||||
import javafx.scene.control.ContentDisplay;
|
|
||||||
import javafx.scene.layout.FlowPane;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import org.kordamp.ikonli.feather.Feather;
|
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
|
||||||
import org.kordamp.ikonli.material2.Material2AL;
|
|
||||||
|
|
||||||
public class TagSample extends GridPane {
|
|
||||||
|
|
||||||
private static final int PREF_WIDTH = 300;
|
|
||||||
|
|
||||||
public TagSample() {
|
|
||||||
new CSSFragment(Tag.CSS).addTo(this);
|
|
||||||
|
|
||||||
setHgap(PAGE_HGAP);
|
|
||||||
setVgap(PAGE_VGAP);
|
|
||||||
|
|
||||||
add(filledTagSample(), 0, 0);
|
|
||||||
add(iconTagSample(), 1, 0);
|
|
||||||
add(outlinedTagSample(), 0, 1);
|
|
||||||
add(closeableTagSample(), 1, 1);
|
|
||||||
add(customColorTagSample(), 0, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock filledTagSample() {
|
|
||||||
var content = new FlowPane(BLOCK_HGAP, BLOCK_VGAP);
|
|
||||||
content.setPrefWidth(PREF_WIDTH);
|
|
||||||
|
|
||||||
var basicTag = new Tag("basic");
|
|
||||||
content.getChildren().add(basicTag);
|
|
||||||
|
|
||||||
var accentTag = new Tag("accent");
|
|
||||||
accentTag.getStyleClass().add(Styles.ACCENT);
|
|
||||||
content.getChildren().add(accentTag);
|
|
||||||
|
|
||||||
var successTag = new Tag("success");
|
|
||||||
successTag.getStyleClass().add(Styles.SUCCESS);
|
|
||||||
content.getChildren().add(successTag);
|
|
||||||
|
|
||||||
var dangerTag = new Tag("danger");
|
|
||||||
dangerTag.getStyleClass().add(Styles.DANGER);
|
|
||||||
content.getChildren().add(dangerTag);
|
|
||||||
|
|
||||||
return new SampleBlock("Filled", content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock iconTagSample() {
|
|
||||||
var content = new FlowPane(BLOCK_HGAP, BLOCK_VGAP);
|
|
||||||
content.setPrefWidth(PREF_WIDTH);
|
|
||||||
|
|
||||||
var basicTag = new Tag("image", new FontIcon(Feather.IMAGE));
|
|
||||||
content.getChildren().add(basicTag);
|
|
||||||
|
|
||||||
var accentTag = new Tag("music", new FontIcon(Feather.MUSIC));
|
|
||||||
content.getChildren().add(accentTag);
|
|
||||||
|
|
||||||
var successTag = new Tag("video", new FontIcon(Feather.VIDEO));
|
|
||||||
content.getChildren().add(successTag);
|
|
||||||
|
|
||||||
return new SampleBlock("Icon", content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock outlinedTagSample() {
|
|
||||||
var content = new FlowPane(BLOCK_HGAP, BLOCK_VGAP);
|
|
||||||
content.setPrefWidth(PREF_WIDTH);
|
|
||||||
|
|
||||||
var accentTag = new Tag("accent");
|
|
||||||
accentTag.getStyleClass().addAll(Styles.ACCENT, Styles.BUTTON_OUTLINED);
|
|
||||||
content.getChildren().add(accentTag);
|
|
||||||
|
|
||||||
var successTag = new Tag("success");
|
|
||||||
successTag.getStyleClass().addAll(Styles.SUCCESS, Styles.BUTTON_OUTLINED);
|
|
||||||
content.getChildren().add(successTag);
|
|
||||||
|
|
||||||
var dangerTag = new Tag("danger");
|
|
||||||
dangerTag.getStyleClass().addAll(Styles.DANGER, Styles.BUTTON_OUTLINED);
|
|
||||||
content.getChildren().add(dangerTag);
|
|
||||||
|
|
||||||
return new SampleBlock("Outlined", content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock closeableTagSample() {
|
|
||||||
var content = new FlowPane(BLOCK_HGAP, BLOCK_VGAP);
|
|
||||||
content.setPrefWidth(PREF_WIDTH);
|
|
||||||
|
|
||||||
var basicTag = new Tag("basic", new FontIcon(Material2AL.CLOSE));
|
|
||||||
basicTag.setContentDisplay(ContentDisplay.RIGHT);
|
|
||||||
content.getChildren().add(basicTag);
|
|
||||||
|
|
||||||
var accentTag = new Tag("accent", new FontIcon(Material2AL.CANCEL));
|
|
||||||
accentTag.setContentDisplay(ContentDisplay.RIGHT);
|
|
||||||
accentTag.getStyleClass().add(Styles.ACCENT);
|
|
||||||
content.getChildren().add(accentTag);
|
|
||||||
|
|
||||||
var successTag = new Tag("success", new FontIcon(Material2AL.CANCEL));
|
|
||||||
successTag.setContentDisplay(ContentDisplay.RIGHT);
|
|
||||||
successTag.getStyleClass().add(Styles.SUCCESS);
|
|
||||||
content.getChildren().add(successTag);
|
|
||||||
|
|
||||||
var dangerTag = new Tag("danger", new FontIcon(Material2AL.CANCEL));
|
|
||||||
dangerTag.setContentDisplay(ContentDisplay.RIGHT);
|
|
||||||
dangerTag.getStyleClass().add(Styles.DANGER);
|
|
||||||
content.getChildren().add(dangerTag);
|
|
||||||
|
|
||||||
return new SampleBlock("Removable", content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SampleBlock customColorTagSample() {
|
|
||||||
var content = new FlowPane(BLOCK_HGAP, BLOCK_VGAP);
|
|
||||||
content.setPrefWidth(PREF_WIDTH);
|
|
||||||
new CSSFragment("""
|
|
||||||
.brand {
|
|
||||||
-color-button-fg: -color-fg-emphasis;
|
|
||||||
-color-button-bg-hover: -color-button-bg;
|
|
||||||
-color-button-bg-pressed: -color-button-bg;
|
|
||||||
}
|
|
||||||
.twitter {
|
|
||||||
-color-button-bg: rgb(85, 172, 238);
|
|
||||||
-color-button-border: rgb(85, 172, 238);
|
|
||||||
}
|
|
||||||
.youtube {
|
|
||||||
-color-button-bg: rgb(205, 32, 31);
|
|
||||||
-color-button-border: rgb(205, 32, 31);
|
|
||||||
}
|
|
||||||
.facebook {
|
|
||||||
-color-button-bg: rgb(59, 89, 153);
|
|
||||||
-color-button-border: rgb(59, 89, 153);
|
|
||||||
}
|
|
||||||
""").addTo(content);
|
|
||||||
|
|
||||||
var twitterTag = new Tag("Twitter", new FontIcon(Feather.TWITTER));
|
|
||||||
twitterTag.getStyleClass().addAll("brand", "twitter");
|
|
||||||
content.getChildren().add(twitterTag);
|
|
||||||
|
|
||||||
var youtubeTag = new Tag("YouTube", new FontIcon(Feather.YOUTUBE));
|
|
||||||
youtubeTag.getStyleClass().addAll("brand", "youtube");
|
|
||||||
content.getChildren().add(youtubeTag);
|
|
||||||
|
|
||||||
var facebookTag = new Tag("Facebook", new FontIcon(Feather.FACEBOOK));
|
|
||||||
facebookTag.getStyleClass().addAll("brand", "facebook");
|
|
||||||
content.getChildren().add(facebookTag);
|
|
||||||
|
|
||||||
return new SampleBlock("Custom Color", content);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
|
|
||||||
package atlantafx.sampler.page.showcase.widget;
|
|
||||||
|
|
||||||
import atlantafx.base.theme.Styles;
|
|
||||||
import atlantafx.base.theme.Tweaks;
|
|
||||||
import atlantafx.sampler.page.Page;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Parent;
|
|
||||||
import javafx.scene.control.ListCell;
|
|
||||||
import javafx.scene.control.ListView;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
|
|
||||||
// JavaFX Skin API is very complex and almost undocumented. In many cases it's much simpler
|
|
||||||
// to create a small widget that just do the job than wasting hours to debug control behaviour.
|
|
||||||
// Consider this as a cookbook of those widgets.
|
|
||||||
public class WidgetCollectionPage extends BorderPane implements Page {
|
|
||||||
|
|
||||||
public static final String NAME = "Widgets";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ListView<Example> toc = new ListView<>();
|
|
||||||
private final VBox widgetWrapper = new VBox(PAGE_HGAP);
|
|
||||||
private boolean isRendered = false;
|
|
||||||
|
|
||||||
public WidgetCollectionPage() {
|
|
||||||
super();
|
|
||||||
createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createView() {
|
|
||||||
widgetWrapper.getStyleClass().add("widget");
|
|
||||||
widgetWrapper.setAlignment(Pos.TOP_CENTER);
|
|
||||||
widgetWrapper.setFillWidth(false);
|
|
||||||
|
|
||||||
toc.setCellFactory(c -> new TocCell());
|
|
||||||
toc.getStyleClass().addAll("toc", Styles.DENSE, Tweaks.EDGE_TO_EDGE);
|
|
||||||
toc.getItems().setAll(Example.values());
|
|
||||||
toc.getSelectionModel().selectedItemProperty().addListener((obs, old, val) -> {
|
|
||||||
if (val == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
widgetWrapper.getChildren().setAll(val.getSupplier().get());
|
|
||||||
});
|
|
||||||
|
|
||||||
// ~
|
|
||||||
|
|
||||||
setCenter(widgetWrapper);
|
|
||||||
setRight(toc);
|
|
||||||
BorderPane.setMargin(toc, new Insets(0, 0, 0, PAGE_HGAP));
|
|
||||||
getStyleClass().setAll("page", "widget-collection");
|
|
||||||
|
|
||||||
toc.getSelectionModel().selectFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Parent getView() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canDisplaySourceCode() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canChangeThemeSettings() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void layoutChildren() {
|
|
||||||
super.layoutChildren();
|
|
||||||
if (isRendered) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isRendered = true;
|
|
||||||
toc.getSelectionModel().selectFirst();
|
|
||||||
toc.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@SuppressWarnings("ImmutableEnumChecker")
|
|
||||||
public enum Example {
|
|
||||||
CARD("Card", CardSample::new),
|
|
||||||
MESSAGE("Message", MessageSample::new),
|
|
||||||
STEPPER("Stepper", StepperSample::new),
|
|
||||||
TAG("Tag", TagSample::new);
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final Supplier<Pane> supplier;
|
|
||||||
|
|
||||||
Example(String name, Supplier<Pane> supplier) {
|
|
||||||
this.name = name;
|
|
||||||
this.supplier = supplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Supplier<Pane> getSupplier() {
|
|
||||||
return supplier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TocCell extends ListCell<Example> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateItem(Example item, boolean empty) {
|
|
||||||
super.updateItem(item, empty);
|
|
||||||
if (empty || item == null) {
|
|
||||||
setText(null);
|
|
||||||
} else {
|
|
||||||
setText(item.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,18 +4,11 @@ package atlantafx.sampler.util;
|
|||||||
|
|
||||||
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
import static atlantafx.base.theme.Styles.BUTTON_ICON;
|
||||||
|
|
||||||
import atlantafx.sampler.event.BrowseEvent;
|
|
||||||
import atlantafx.sampler.event.DefaultEventBus;
|
|
||||||
import java.net.URI;
|
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Hyperlink;
|
|
||||||
import javafx.scene.control.MenuItem;
|
|
||||||
import javafx.scene.control.ToggleButton;
|
|
||||||
import javafx.scene.control.ToggleGroup;
|
|
||||||
import javafx.scene.input.KeyCombination;
|
|
||||||
import org.kordamp.ikonli.Ikon;
|
import org.kordamp.ikonli.Ikon;
|
||||||
import org.kordamp.ikonli.javafx.FontIcon;
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public final class Controls {
|
public final class Controls {
|
||||||
|
|
||||||
public static Button iconButton(Ikon icon, boolean disable) {
|
public static Button iconButton(Ikon icon, boolean disable) {
|
||||||
@ -31,48 +24,4 @@ public final class Controls {
|
|||||||
button.getStyleClass().addAll(styleClasses);
|
button.getStyleClass().addAll(styleClasses);
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MenuItem menuItem(String text, Ikon graphic, KeyCombination accelerator) {
|
|
||||||
return menuItem(text, graphic, accelerator, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MenuItem menuItem(String text, Ikon graphic, KeyCombination accelerator, boolean disable) {
|
|
||||||
var item = new MenuItem(text);
|
|
||||||
|
|
||||||
if (graphic != null) {
|
|
||||||
item.setGraphic(new FontIcon(graphic));
|
|
||||||
}
|
|
||||||
if (accelerator != null) {
|
|
||||||
item.setAccelerator(accelerator);
|
|
||||||
}
|
|
||||||
item.setDisable(disable);
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ToggleButton toggleButton(String text,
|
|
||||||
Ikon icon,
|
|
||||||
ToggleGroup group,
|
|
||||||
boolean selected,
|
|
||||||
String... styleClasses) {
|
|
||||||
var toggleButton = new ToggleButton(text);
|
|
||||||
if (icon != null) {
|
|
||||||
toggleButton.setGraphic(new FontIcon(icon));
|
|
||||||
}
|
|
||||||
if (group != null) {
|
|
||||||
toggleButton.setToggleGroup(group);
|
|
||||||
}
|
|
||||||
toggleButton.setSelected(selected);
|
|
||||||
toggleButton.getStyleClass().addAll(styleClasses);
|
|
||||||
|
|
||||||
return toggleButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Hyperlink hyperlink(String text, URI uri) {
|
|
||||||
var hyperlink = new Hyperlink(text);
|
|
||||||
if (uri != null) {
|
|
||||||
hyperlink.setOnAction(event -> DefaultEventBus.getInstance().publish(new BrowseEvent(uri)));
|
|
||||||
}
|
|
||||||
return hyperlink;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
39
sampler/src/main/java/atlantafx/sampler/util/Lazy.java
Normal file
39
sampler/src/main/java/atlantafx/sampler/util/Lazy.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.util;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auxiliary object wrapper to support lazy initialization.
|
||||||
|
* DO NOT override {@code hashCode()} / {@code equals()}, because each instance
|
||||||
|
* of this object must remain unique.
|
||||||
|
*/
|
||||||
|
public class Lazy<T> implements Supplier<T> {
|
||||||
|
|
||||||
|
protected final Supplier<T> supplier;
|
||||||
|
protected @Nullable T value = null;
|
||||||
|
|
||||||
|
public Lazy(Supplier<T> supplier) {
|
||||||
|
this.supplier = Objects.requireNonNull(supplier, "supplier");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
if (value == null) {
|
||||||
|
value = supplier.get();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean initialized() {
|
||||||
|
return value != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ module atlantafx.sampler {
|
|||||||
requires javafx.swing;
|
requires javafx.swing;
|
||||||
requires javafx.media;
|
requires javafx.media;
|
||||||
requires javafx.web;
|
requires javafx.web;
|
||||||
|
requires javafx.fxml;
|
||||||
|
|
||||||
requires org.kordamp.ikonli.core;
|
requires org.kordamp.ikonli.core;
|
||||||
requires org.kordamp.ikonli.javafx;
|
requires org.kordamp.ikonli.javafx;
|
||||||
@ -20,13 +21,13 @@ module atlantafx.sampler {
|
|||||||
requires datafaker;
|
requires datafaker;
|
||||||
|
|
||||||
exports atlantafx.sampler;
|
exports atlantafx.sampler;
|
||||||
exports atlantafx.sampler.fake;
|
|
||||||
exports atlantafx.sampler.fake.domain;
|
exports atlantafx.sampler.fake.domain;
|
||||||
exports atlantafx.sampler.event;
|
exports atlantafx.sampler.event;
|
||||||
exports atlantafx.sampler.layout;
|
exports atlantafx.sampler.layout;
|
||||||
exports atlantafx.sampler.page;
|
exports atlantafx.sampler.page;
|
||||||
exports atlantafx.sampler.page.general;
|
exports atlantafx.sampler.page.general;
|
||||||
exports atlantafx.sampler.page.components;
|
exports atlantafx.sampler.page.components;
|
||||||
|
exports atlantafx.sampler.page.extras;
|
||||||
exports atlantafx.sampler.page.showcase;
|
exports atlantafx.sampler.page.showcase;
|
||||||
exports atlantafx.sampler.theme;
|
exports atlantafx.sampler.theme;
|
||||||
exports atlantafx.sampler.util;
|
exports atlantafx.sampler.util;
|
||||||
@ -39,4 +40,5 @@ module atlantafx.sampler {
|
|||||||
opens atlantafx.sampler.assets.styles;
|
opens atlantafx.sampler.assets.styles;
|
||||||
opens atlantafx.sampler.images;
|
opens atlantafx.sampler.images;
|
||||||
opens atlantafx.sampler.images.modena;
|
opens atlantafx.sampler.images.modena;
|
||||||
|
opens atlantafx.sampler.page.general;
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user