Add InputGroup layout
This commit is contained in:
parent
baac4d9153
commit
e86c95af29
65
base/src/main/java/atlantafx/base/layout/InputGroup.java
Normal file
65
base/src/main/java/atlantafx/base/layout/InputGroup.java
Normal file
@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
package atlantafx.base.layout;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
/**
|
||||
* InputGroup is a layout that helps combine multiple controls into a group
|
||||
* that looks like a single control. Without it, you would have to manually
|
||||
* add the "left-pill", "center-pill," and "right-pill" styles classes to
|
||||
* each control in such combination. The InputGroup removes this ceremony.
|
||||
* Since it inherits from HBox, you can use the same API.
|
||||
*/
|
||||
public class InputGroup extends HBox {
|
||||
|
||||
/**
|
||||
* See {@link HBox#HBox()}.
|
||||
*/
|
||||
public InputGroup() {
|
||||
super();
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link HBox#HBox(Node...)}.
|
||||
*/
|
||||
public InputGroup(Node... children) {
|
||||
super(children);
|
||||
init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
getStyleClass().add("input-group");
|
||||
|
||||
updateStyles();
|
||||
getChildren().addListener((InvalidationListener) o -> updateStyles());
|
||||
}
|
||||
|
||||
// We don't clean up style classes if a control is removed from the input group.
|
||||
// However, they will be fixed if the same control is added to the input group again.
|
||||
protected void updateStyles() {
|
||||
for (int i = 0; i < getChildren().size(); i++) {
|
||||
Node n = getChildren().get(i);
|
||||
|
||||
n.getStyleClass().removeAll(
|
||||
Styles.LEFT_PILL, Styles.CENTER_PILL, Styles.RIGHT_PILL
|
||||
);
|
||||
|
||||
if (i == getChildren().size() - 1) {
|
||||
if (i != 0) {
|
||||
n.getStyleClass().add(Styles.RIGHT_PILL);
|
||||
}
|
||||
} else if (i == 0) {
|
||||
n.getStyleClass().add(Styles.LEFT_PILL);
|
||||
} else {
|
||||
n.getStyleClass().add(Styles.CENTER_PILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
97
base/src/test/java/atlantafx/base/layout/InputGroupTest.java
Normal file
97
base/src/test/java/atlantafx/base/layout/InputGroupTest.java
Normal file
@ -0,0 +1,97 @@
|
||||
package atlantafx.base.layout;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import javafx.scene.layout.Pane;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class InputGroupTest {
|
||||
|
||||
@Test
|
||||
public void testInitSingleNode() {
|
||||
var g = new InputGroup(
|
||||
new Pane()
|
||||
);
|
||||
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(1);
|
||||
Assertions.assertThat(g.getChildren().get(0).getStyleClass()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitTwoNodes() {
|
||||
var g = new InputGroup(
|
||||
new Pane(), new Pane()
|
||||
);
|
||||
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(2);
|
||||
assertStyle(g, 0, Styles.LEFT_PILL);
|
||||
assertStyle(g, 1, Styles.RIGHT_PILL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitMultipleNodes() {
|
||||
var g = new InputGroup(
|
||||
new Pane(), new Pane(), new Pane(), new Pane()
|
||||
);
|
||||
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(4);
|
||||
assertStyle(g, 0, Styles.LEFT_PILL);
|
||||
assertStyle(g, 1, Styles.CENTER_PILL);
|
||||
assertStyle(g, 2, Styles.CENTER_PILL);
|
||||
assertStyle(g, 3, Styles.RIGHT_PILL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNodes() {
|
||||
var g = new InputGroup();
|
||||
Assertions.assertThat(g.getChildren()).isEmpty();
|
||||
|
||||
g.getChildren().add(new Pane());
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(1);
|
||||
Assertions.assertThat(g.getChildren().get(0).getStyleClass()).isEmpty();
|
||||
|
||||
g.getChildren().add(new Pane());
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(2);
|
||||
assertStyle(g, 0, Styles.LEFT_PILL);
|
||||
assertStyle(g, 1, Styles.RIGHT_PILL);
|
||||
|
||||
g.getChildren().add(new Pane());
|
||||
g.getChildren().add(new Pane());
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(4);
|
||||
assertStyle(g, 0, Styles.LEFT_PILL);
|
||||
assertStyle(g, 1, Styles.CENTER_PILL);
|
||||
assertStyle(g, 2, Styles.CENTER_PILL);
|
||||
assertStyle(g, 3, Styles.RIGHT_PILL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNodes() {
|
||||
var g = new InputGroup(
|
||||
new Pane(), new Pane(), new Pane(), new Pane()
|
||||
);
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(4);
|
||||
assertStyle(g, 0, Styles.LEFT_PILL);
|
||||
assertStyle(g, 1, Styles.CENTER_PILL);
|
||||
assertStyle(g, 2, Styles.CENTER_PILL);
|
||||
assertStyle(g, 3, Styles.RIGHT_PILL);
|
||||
|
||||
g.getChildren().remove(0);
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(3);
|
||||
assertStyle(g, 0, Styles.LEFT_PILL);
|
||||
assertStyle(g, 1, Styles.CENTER_PILL);
|
||||
assertStyle(g, 2, Styles.RIGHT_PILL);
|
||||
|
||||
g.getChildren().remove(0);
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(2);
|
||||
assertStyle(g, 0, Styles.LEFT_PILL);
|
||||
assertStyle(g, 1, Styles.RIGHT_PILL);
|
||||
|
||||
g.getChildren().remove(0);
|
||||
Assertions.assertThat(g.getChildren().size()).isEqualTo(1);
|
||||
Assertions.assertThat(g.getChildren().get(0).getStyleClass()).isEmpty();
|
||||
}
|
||||
|
||||
private void assertStyle(InputGroup g, int index, String style) {
|
||||
Assertions.assertThat(g.getChildren().get(index).getStyleClass()).containsExactly(style);
|
||||
}
|
||||
}
|
@ -163,6 +163,7 @@ public class MainModel {
|
||||
NAV_TREE.get(CardPage.class),
|
||||
NAV_TREE.get(ContextMenuPage.class),
|
||||
NAV_TREE.get(DeckPanePage.class),
|
||||
NAV_TREE.get(InputGroupPage.class),
|
||||
NAV_TREE.get(ModalPanePage.class),
|
||||
NAV_TREE.get(ScrollPanePage.class),
|
||||
NAV_TREE.get(SeparatorPage.class),
|
||||
@ -200,7 +201,6 @@ public class MainModel {
|
||||
NAV_TREE.get(ComboBoxPage.class),
|
||||
NAV_TREE.get(CustomTextFieldPage.class),
|
||||
NAV_TREE.get(DatePickerPage.class),
|
||||
NAV_TREE.get(InputGroupPage.class),
|
||||
NAV_TREE.get(HtmlEditorPage.class),
|
||||
NAV_TREE.get(MenuButtonPage.class),
|
||||
NAV_TREE.get(RadioButtonPage.class),
|
||||
@ -259,7 +259,6 @@ public class MainModel {
|
||||
);
|
||||
|
||||
// components
|
||||
map.put(InputGroupPage.class, NavTree.Item.page(InputGroupPage.NAME, InputGroupPage.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(ButtonPage.class, NavTree.Item.page(ButtonPage.NAME, ButtonPage.class));
|
||||
@ -285,6 +284,7 @@ public class MainModel {
|
||||
map.put(DeckPanePage.class, NavTree.Item.page(DeckPanePage.NAME, DeckPanePage.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(InputGroupPage.class, NavTree.Item.page(InputGroupPage.NAME, InputGroupPage.class));
|
||||
map.put(ListViewPage.class, NavTree.Item.page(ListViewPage.NAME, ListViewPage.class));
|
||||
map.put(MenuBarPage.class, NavTree.Item.page(MenuBarPage.NAME, MenuBarPage.class));
|
||||
map.put(MenuButtonPage.class, NavTree.Item.page(
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
package atlantafx.sampler.page.components;
|
||||
|
||||
import atlantafx.base.layout.InputGroup;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import atlantafx.base.util.BBCodeParser;
|
||||
import atlantafx.sampler.page.ExampleBox;
|
||||
@ -19,13 +20,12 @@ import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.kordamp.ikonli.feather.Feather;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
public final class InputGroupPage extends OutlinePage {
|
||||
|
||||
public static final String NAME = "Input Group";
|
||||
public static final String NAME = "InputGroup";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
@ -33,8 +33,8 @@ public final class InputGroupPage extends OutlinePage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable URI getJavadocUri() {
|
||||
return null;
|
||||
public URI getJavadocUri() {
|
||||
return URI.create(String.format(AFX_JAVADOC_URI_TEMPLATE, "layout/" + getName()));
|
||||
}
|
||||
|
||||
public InputGroupPage() {
|
||||
@ -42,10 +42,12 @@ public final class InputGroupPage extends OutlinePage {
|
||||
|
||||
addPageHeader();
|
||||
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."""
|
||||
[i]InputGroup[/i] is a layout that helps combine various controls into a group \
|
||||
that allow them to appear as a single control. Without it, you would have \
|
||||
to manually add the [font=monospace].left-pill[/font], [font=monospace].center-pill[/font], \
|
||||
and [font=monospace].right-pill[/font] styles classes to each control in such combination. \
|
||||
You still can, but the [i]InputGroup[/i] removes this ceremony. And since it inherits \
|
||||
from [i]HBox[/i], you can use the same API."""
|
||||
);
|
||||
addSection("ComboBox", comboBoxExample());
|
||||
addSection("Button", buttonExample());
|
||||
@ -58,16 +60,15 @@ public final class InputGroupPage extends OutlinePage {
|
||||
//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);
|
||||
|
||||
var group = new InputGroup(leftCmb, rightTfd);
|
||||
//snippet_1:end
|
||||
|
||||
var box = new HBox(leftCmb, rightTfd);
|
||||
box.setAlignment(Pos.CENTER_LEFT);
|
||||
var box = new HBox(group);
|
||||
box.setMinWidth(400);
|
||||
box.setMaxWidth(400);
|
||||
|
||||
@ -82,7 +83,6 @@ public final class InputGroupPage extends OutlinePage {
|
||||
//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(
|
||||
@ -92,11 +92,11 @@ public final class InputGroupPage extends OutlinePage {
|
||||
rightBtn.setOnAction(
|
||||
e -> leftTfd.setText(FAKER.internet().password())
|
||||
);
|
||||
rightBtn.getStyleClass().add(Styles.RIGHT_PILL);
|
||||
|
||||
var group = new InputGroup(leftTfd, rightBtn);
|
||||
//snippet_2:end
|
||||
|
||||
var box = new HBox(leftTfd, rightBtn);
|
||||
box.setAlignment(Pos.CENTER_LEFT);
|
||||
var box = new HBox(group);
|
||||
box.setMinWidth(400);
|
||||
box.setMaxWidth(400);
|
||||
|
||||
@ -110,18 +110,16 @@ public final class InputGroupPage extends OutlinePage {
|
||||
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);
|
||||
|
||||
var group = new InputGroup(leftTfd, centerTfd, rightTfd);
|
||||
//snippet_3:end
|
||||
|
||||
var box = new HBox(leftTfd, centerTfd, rightTfd);
|
||||
box.setAlignment(Pos.CENTER_LEFT);
|
||||
var box = new HBox(group);
|
||||
box.setMinWidth(400);
|
||||
box.setMaxWidth(400);
|
||||
|
||||
@ -135,7 +133,6 @@ public final class InputGroupPage extends OutlinePage {
|
||||
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");
|
||||
@ -155,11 +152,11 @@ public final class InputGroupPage extends OutlinePage {
|
||||
|
||||
var leftMenu = new MenuButton("Dropdown");
|
||||
leftMenu.getItems().addAll(spellItem, characterItem, locationItem);
|
||||
leftMenu.getStyleClass().add(Styles.LEFT_PILL);
|
||||
|
||||
var group = new InputGroup(leftMenu, rightTfd);
|
||||
//snippet_4:end
|
||||
|
||||
var box = new HBox(leftMenu, rightTfd);
|
||||
box.setAlignment(Pos.CENTER_LEFT);
|
||||
var box = new HBox(group);
|
||||
box.setMinWidth(400);
|
||||
box.setMaxWidth(400);
|
||||
|
||||
@ -173,43 +170,33 @@ public final class InputGroupPage extends OutlinePage {
|
||||
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 sample1 = new InputGroup(leftLbl1, rightTfd1);
|
||||
|
||||
// ~
|
||||
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 sample2 = new InputGroup(leftTfd2, centerLbl2, rightTfd2);
|
||||
|
||||
// ~
|
||||
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);
|
||||
var sample3 = new InputGroup(leftTfd3, rightLbl3);
|
||||
//snippet_5:end
|
||||
|
||||
sample1.setMinWidth(400);
|
||||
|
@ -6,6 +6,7 @@ import static javafx.scene.control.TabPane.TabClosingPolicy;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import atlantafx.base.controls.ToggleSwitch;
|
||||
import atlantafx.base.layout.InputGroup;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import atlantafx.base.util.BBCodeParser;
|
||||
import atlantafx.sampler.page.ExampleBox;
|
||||
@ -346,7 +347,6 @@ public final class TabPanePage extends OutlinePage {
|
||||
defaultStyleToggle.setUserData(
|
||||
List.of("whatever", Styles.TABS_FLOATING, Styles.TABS_CLASSIC)
|
||||
);
|
||||
defaultStyleToggle.getStyleClass().add(Styles.LEFT_PILL);
|
||||
defaultStyleToggle.setSelected(true);
|
||||
|
||||
var floatingStyleToggle = new ToggleButton("Floating");
|
||||
@ -354,14 +354,12 @@ public final class TabPanePage extends OutlinePage {
|
||||
floatingStyleToggle.setUserData(
|
||||
List.of(Styles.TABS_FLOATING, "whatever", Styles.TABS_CLASSIC)
|
||||
);
|
||||
floatingStyleToggle.getStyleClass().add(Styles.CENTER_PILL);
|
||||
|
||||
var classicStyleToggle = new ToggleButton("Classic");
|
||||
classicStyleToggle.setToggleGroup(styleToggleGroup);
|
||||
classicStyleToggle.setUserData(
|
||||
List.of(Styles.TABS_CLASSIC, "whatever", Styles.TABS_FLOATING)
|
||||
);
|
||||
classicStyleToggle.getStyleClass().add(Styles.RIGHT_PILL);
|
||||
|
||||
styleToggleGroup.selectedToggleProperty().addListener((obs, old, val) -> {
|
||||
if (val != null) {
|
||||
@ -370,7 +368,9 @@ public final class TabPanePage extends OutlinePage {
|
||||
}
|
||||
});
|
||||
|
||||
var styleBox = new HBox(defaultStyleToggle, floatingStyleToggle, classicStyleToggle);
|
||||
var styleBox = new InputGroup(
|
||||
defaultStyleToggle, floatingStyleToggle, classicStyleToggle
|
||||
);
|
||||
styleBox.setAlignment(Pos.CENTER);
|
||||
|
||||
// == LAYOUT ==
|
||||
|
Loading…
Reference in New Issue
Block a user