Refactor and improve Card
This commit is contained in:
parent
34acefa8f8
commit
24a2e096ad
@ -20,6 +20,7 @@ public class Card extends Control {
|
|||||||
// Default constructor
|
// Default constructor
|
||||||
public Card() {
|
public Card() {
|
||||||
super();
|
super();
|
||||||
|
getStyleClass().add("card");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,14 +3,22 @@
|
|||||||
package atlantafx.base.controls;
|
package atlantafx.base.controls;
|
||||||
|
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
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;
|
||||||
|
|
||||||
public class CardSkin implements Skin<Card> {
|
public class CardSkin implements Skin<Card> {
|
||||||
|
|
||||||
|
protected static final PseudoClass HAS_HEADER = PseudoClass.getPseudoClass("has-header");
|
||||||
|
protected static final PseudoClass HAS_SUBHEADER = PseudoClass.getPseudoClass("has-subheader");
|
||||||
|
protected static final PseudoClass HAS_BODY = PseudoClass.getPseudoClass("has-body");
|
||||||
|
protected static final PseudoClass HAS_FOOTER = PseudoClass.getPseudoClass("has-footer");
|
||||||
|
protected static final PseudoClass HAS_IMAGE = PseudoClass.getPseudoClass("has-image");
|
||||||
|
|
||||||
protected final Card control;
|
protected final Card control;
|
||||||
protected final VBox root = new VBox();
|
protected final VBox root = new VBox();
|
||||||
|
|
||||||
@ -31,30 +39,42 @@ public class CardSkin implements Skin<Card> {
|
|||||||
|
|
||||||
headerSlot = new StackPane();
|
headerSlot = new StackPane();
|
||||||
headerSlot.getStyleClass().add("header");
|
headerSlot.getStyleClass().add("header");
|
||||||
headerSlotListener = new SlotListener(headerSlot);
|
headerSlotListener = new SlotListener(
|
||||||
|
headerSlot, (n, active) -> getSkinnable().pseudoClassStateChanged(HAS_HEADER, active)
|
||||||
|
);
|
||||||
control.headerProperty().addListener(headerSlotListener);
|
control.headerProperty().addListener(headerSlotListener);
|
||||||
headerSlotListener.changed(control.headerProperty(), null, control.getHeader());
|
headerSlotListener.changed(control.headerProperty(), null, control.getHeader());
|
||||||
|
|
||||||
subHeaderSlot = new StackPane();
|
subHeaderSlot = new StackPane();
|
||||||
subHeaderSlot.getStyleClass().add("sub-header");
|
subHeaderSlot.getStyleClass().add("sub-header");
|
||||||
subHeaderSlotListener = new SlotListener(subHeaderSlot);
|
subHeaderSlotListener = new SlotListener(
|
||||||
|
subHeaderSlot,
|
||||||
|
(n, active) -> {
|
||||||
|
getSkinnable().pseudoClassStateChanged(HAS_SUBHEADER, active);
|
||||||
|
getSkinnable().pseudoClassStateChanged(HAS_IMAGE, n instanceof ImageView);
|
||||||
|
}
|
||||||
|
);
|
||||||
control.subHeaderProperty().addListener(subHeaderSlotListener);
|
control.subHeaderProperty().addListener(subHeaderSlotListener);
|
||||||
subHeaderSlotListener.changed(control.subHeaderProperty(), null, control.getSubHeader());
|
subHeaderSlotListener.changed(control.subHeaderProperty(), null, control.getSubHeader());
|
||||||
|
|
||||||
bodySlot = new StackPane();
|
bodySlot = new StackPane();
|
||||||
bodySlot.getStyleClass().add("body");
|
bodySlot.getStyleClass().add("body");
|
||||||
VBox.setVgrow(bodySlot, Priority.ALWAYS);
|
VBox.setVgrow(bodySlot, Priority.ALWAYS);
|
||||||
bodySlotListener = new SlotListener(bodySlot);
|
bodySlotListener = new SlotListener(
|
||||||
|
bodySlot, (n, active) -> getSkinnable().pseudoClassStateChanged(HAS_BODY, active)
|
||||||
|
);
|
||||||
control.bodyProperty().addListener(bodySlotListener);
|
control.bodyProperty().addListener(bodySlotListener);
|
||||||
bodySlotListener.changed(control.bodyProperty(), null, control.getBody());
|
bodySlotListener.changed(control.bodyProperty(), null, control.getBody());
|
||||||
|
|
||||||
footerSlot = new StackPane();
|
footerSlot = new StackPane();
|
||||||
footerSlot.getStyleClass().add("footer");
|
footerSlot.getStyleClass().add("footer");
|
||||||
footerSlotListener = new SlotListener(footerSlot);
|
footerSlotListener = new SlotListener(
|
||||||
|
footerSlot, (n, active) -> getSkinnable().pseudoClassStateChanged(HAS_FOOTER, active)
|
||||||
|
);
|
||||||
control.footerProperty().addListener(footerSlotListener);
|
control.footerProperty().addListener(footerSlotListener);
|
||||||
footerSlotListener.changed(control.footerProperty(), null, control.getFooter());
|
footerSlotListener.changed(control.footerProperty(), null, control.getFooter());
|
||||||
|
|
||||||
root.getStyleClass().add("card");
|
root.getStyleClass().add("container");
|
||||||
root.getChildren().setAll(headerSlot, subHeaderSlot, bodySlot, footerSlot);
|
root.getChildren().setAll(headerSlot, subHeaderSlot, bodySlot, footerSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import javafx.scene.layout.StackPane;
|
|||||||
|
|
||||||
public class MessageSkin extends TileSkinBase<Message> {
|
public class MessageSkin extends TileSkinBase<Message> {
|
||||||
|
|
||||||
private static final PseudoClass CLOSEABLE = PseudoClass.getPseudoClass("closeable");
|
protected static final PseudoClass CLOSEABLE = PseudoClass.getPseudoClass("closeable");
|
||||||
|
|
||||||
protected final StackPane closeButton = new StackPane();
|
protected final StackPane closeButton = new StackPane();
|
||||||
protected final StackPane closeButtonIcon = new StackPane();
|
protected final StackPane closeButtonIcon = new StackPane();
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package atlantafx.sampler.page.components;
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
import atlantafx.base.controls.Card;
|
import atlantafx.base.controls.Card;
|
||||||
|
import atlantafx.base.controls.CustomTextField;
|
||||||
import atlantafx.base.controls.Spacer;
|
import atlantafx.base.controls.Spacer;
|
||||||
import atlantafx.base.controls.Tile;
|
import atlantafx.base.controls.Tile;
|
||||||
import atlantafx.base.theme.Styles;
|
import atlantafx.base.theme.Styles;
|
||||||
import atlantafx.base.util.BBCodeParser;
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.Resources;
|
||||||
import atlantafx.sampler.page.ExampleBox;
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
import atlantafx.sampler.page.OutlinePage;
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
import atlantafx.sampler.page.Snippet;
|
import atlantafx.sampler.page.Snippet;
|
||||||
@ -16,10 +18,16 @@ import javafx.scene.Node;
|
|||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.image.WritableImage;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.scene.text.TextFlow;
|
import javafx.scene.text.TextFlow;
|
||||||
|
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;
|
||||||
import org.kordamp.ikonli.material2.Material2MZ;
|
import org.kordamp.ikonli.material2.Material2MZ;
|
||||||
@ -45,13 +53,14 @@ public class CardPage extends OutlinePage {
|
|||||||
addFormattedText("""
|
addFormattedText("""
|
||||||
The [i]Card[/i] is a versatile container that can used in various contexts \
|
The [i]Card[/i] is a versatile container that can used in various contexts \
|
||||||
such as headings, text, dialogs and more. It includes a header to provide a \
|
such as headings, text, dialogs and more. It includes a header to provide a \
|
||||||
brief overview or context of the information. The sub-header and body sections \
|
brief overview or context of the information. The subheader and body sections \
|
||||||
provide more detailed content, while the footer may include additional actions \
|
provide more detailed content, while the footer may include additional actions \
|
||||||
or information."""
|
or information."""
|
||||||
);
|
);
|
||||||
addNode(skeleton());
|
addNode(skeleton());
|
||||||
addSection("Usage", usageExample());
|
addSection("Usage", usageExample());
|
||||||
addSection("Elevation", elevationExample());
|
addSection("Elevation", elevationExample());
|
||||||
|
addSection("Subheader", subHeaderExample());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node skeleton() {
|
private Node skeleton() {
|
||||||
@ -76,7 +85,7 @@ public class CardPage extends OutlinePage {
|
|||||||
box.setMaxWidth(500);
|
box.setMaxWidth(500);
|
||||||
box.getChildren().setAll(
|
box.getChildren().setAll(
|
||||||
cellBuilder.apply("header"),
|
cellBuilder.apply("header"),
|
||||||
cellBuilder.apply("sub-header"),
|
cellBuilder.apply("subheader"),
|
||||||
body,
|
body,
|
||||||
cellBuilder.apply("footer")
|
cellBuilder.apply("footer")
|
||||||
);
|
);
|
||||||
@ -133,7 +142,7 @@ public class CardPage extends OutlinePage {
|
|||||||
var box = new HBox(HGAP_20, tweetCard, dialogCard);
|
var box = new HBox(HGAP_20, tweetCard, dialogCard);
|
||||||
var description = BBCodeParser.createFormattedText("""
|
var description = BBCodeParser.createFormattedText("""
|
||||||
The [i]Card[/i] pairs well with the [i]Tile[/i] component. \
|
The [i]Card[/i] pairs well with the [i]Tile[/i] component. \
|
||||||
You can use the [i]Tile[/i] as either a header or body for the [i]Card[/i]. \
|
You can use the [i]Tiles[/i] as either a header or body for the [i]Card[/i]. \
|
||||||
It’s also suitable for building more complex dialogs as well."""
|
It’s also suitable for building more complex dialogs as well."""
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -160,17 +169,95 @@ public class CardPage extends OutlinePage {
|
|||||||
"This is a title",
|
"This is a title",
|
||||||
"This is a description"
|
"This is a description"
|
||||||
));
|
));
|
||||||
card2.setBody(new Label("This is content"));
|
card2.setBody(new Label("This is a content"));
|
||||||
//snippet_2:end
|
//snippet_2:end
|
||||||
|
|
||||||
var box = new HBox(HGAP_20, card1, card2);
|
var box = new HBox(HGAP_20, card1, card2);
|
||||||
box.setPadding(new Insets(0, 0, 10, 0));
|
box.setPadding(new Insets(0, 0, 10, 0));
|
||||||
|
|
||||||
var description = BBCodeParser.createFormattedText("""
|
var description = BBCodeParser.createFormattedText("""
|
||||||
With [code]Styles.ELEVATED_N[/code] or [code]Styles.INTERACTIVE[/code] styles classes \
|
To add the raised effect to the [i]Card[/i], use the [code]Styles.ELEVATED_N[/code] \
|
||||||
you can add raised shadow effect to the [i]Card[/i]."""
|
or [code]Styles.INTERACTIVE[/code] style classes."""
|
||||||
);
|
);
|
||||||
|
|
||||||
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ExampleBox subHeaderExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var card1 = new Card();
|
||||||
|
card1.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
card1.setMinWidth(300);
|
||||||
|
card1.setMaxWidth(300);
|
||||||
|
|
||||||
|
var avatar1 = new Image(
|
||||||
|
Resources.getResourceAsStream("images/avatars/avatar3.png")
|
||||||
|
);
|
||||||
|
var btn1 = new Button(null, new FontIcon(Feather.MORE_VERTICAL));
|
||||||
|
btn1.getStyleClass().addAll(Styles.BUTTON_CIRCLE, Styles.FLAT);
|
||||||
|
var header1 = new Tile(
|
||||||
|
"Title",
|
||||||
|
"This is a description",
|
||||||
|
new ImageView(avatar1)
|
||||||
|
);
|
||||||
|
header1.setAction(btn1);
|
||||||
|
card1.setHeader(header1);
|
||||||
|
|
||||||
|
var image1 = new WritableImage(
|
||||||
|
new Image(
|
||||||
|
Resources.getResourceAsStream("images/pattern.jpg")
|
||||||
|
).getPixelReader(), 0, 0, 298, 150
|
||||||
|
);
|
||||||
|
card1.setSubHeader(new ImageView(image1));
|
||||||
|
|
||||||
|
var text1 = new TextFlow(new Text(FAKER.lorem().sentence(15)));
|
||||||
|
text1.setMaxWidth(260);
|
||||||
|
card1.setBody(text1);
|
||||||
|
|
||||||
|
// ~
|
||||||
|
|
||||||
|
var card2 = new Card();
|
||||||
|
card2.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
card2.setMinWidth(300);
|
||||||
|
card2.setMaxWidth(300);
|
||||||
|
|
||||||
|
var header2 = new Tile(
|
||||||
|
"Reviewers",
|
||||||
|
"Request up to 10 reviewers"
|
||||||
|
);
|
||||||
|
card2.setHeader(header2);
|
||||||
|
|
||||||
|
var tf2 = new CustomTextField();
|
||||||
|
tf2.setPromptText("Search people");
|
||||||
|
tf2.setLeft(new FontIcon(Material2MZ.SEARCH));
|
||||||
|
card2.setSubHeader(tf2);
|
||||||
|
|
||||||
|
var body2 = new VBox(10);
|
||||||
|
card2.setBody(body2);
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
var cb = new CheckBox();
|
||||||
|
var lbl = new Label(FAKER.name().fullName());
|
||||||
|
var circle = new Circle(
|
||||||
|
8, Color.web(FAKER.color().hex(true))
|
||||||
|
);
|
||||||
|
|
||||||
|
var row = new HBox(10, circle, cb, lbl);
|
||||||
|
row.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
body2.getChildren().add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new HBox(HGAP_20, card1, card2);
|
||||||
|
box.setPadding(new Insets(0, 0, 10, 0));
|
||||||
|
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The subheader slot is an optional space for interactive controls. Use \
|
||||||
|
it to display a search field, filter menu, or local navigation component. \
|
||||||
|
It also has special support for the [i]ImageView[/i]. If you place an image \
|
||||||
|
inside the subheader, it will remove its horizontal padding."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,11 +339,11 @@ public final class ModalPanePage extends OutlinePage {
|
|||||||
VBox.setVgrow(ta, Priority.ALWAYS);
|
VBox.setVgrow(ta, Priority.ALWAYS);
|
||||||
|
|
||||||
var content = new VBox(
|
var content = new VBox(
|
||||||
20,
|
16,
|
||||||
new Tile("Example Dialog", FAKER.lorem().sentence(10)),
|
new Tile("Example Dialog", FAKER.lorem().sentence(10)),
|
||||||
ta
|
ta
|
||||||
);
|
);
|
||||||
content.setPadding(new Insets(20));
|
content.setPadding(new Insets(16));
|
||||||
dialog.addContent(content);
|
dialog.addContent(content);
|
||||||
AnchorPane.setTopAnchor(content, 0d);
|
AnchorPane.setTopAnchor(content, 0d);
|
||||||
AnchorPane.setRightAnchor(content, 0d);
|
AnchorPane.setRightAnchor(content, 0d);
|
||||||
|
@ -7,55 +7,74 @@ $color-bg: -color-bg-default !default;
|
|||||||
$color-border: -color-border-default !default;
|
$color-border: -color-border-default !default;
|
||||||
$padding-x: 0.75em !default;
|
$padding-x: 0.75em !default;
|
||||||
$padding-y: 1em !default;
|
$padding-y: 1em !default;
|
||||||
$spacing: 10px !default;
|
$spacing: 1em !default;
|
||||||
|
|
||||||
|
$title-font-size: cfg.$font-title-4;
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
-fx-background-color: $color-bg;
|
>.container {
|
||||||
-fx-alignment: TOP_LEFT;
|
-fx-background-color: $color-bg;
|
||||||
-fx-padding: $padding-y $padding-x $padding-y $padding-x;
|
|
||||||
-fx-spacing: $spacing;
|
|
||||||
|
|
||||||
-fx-border-color: $color-border;
|
|
||||||
-fx-border-width: cfg.$border-width;
|
|
||||||
-fx-border-radius: cfg.$border-radius;
|
|
||||||
|
|
||||||
>.header {
|
|
||||||
-fx-alignment: TOP_LEFT;
|
-fx-alignment: TOP_LEFT;
|
||||||
}
|
-fx-padding: $padding-y 0 $padding-y 0;
|
||||||
|
-fx-spacing: $spacing;
|
||||||
|
|
||||||
>.sub-header {
|
-fx-border-color: $color-border;
|
||||||
-fx-alignment: TOP_LEFT;
|
-fx-border-width: cfg.$border-width;
|
||||||
}
|
-fx-border-radius: cfg.$border-radius;
|
||||||
|
|
||||||
>.body {
|
>.header {
|
||||||
// double spacing for body
|
-fx-alignment: TOP_LEFT;
|
||||||
-fx-padding: $spacing 0 $spacing 0;
|
-fx-padding: 0 $padding-x 0 $padding-x 0;
|
||||||
-fx-alignment: TOP_LEFT;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
>.footer {
|
>.sub-header {
|
||||||
-fx-alignment: TOP_LEFT;
|
-fx-alignment: TOP_LEFT;
|
||||||
}
|
-fx-padding: 0 $padding-x 0 $padding-x 0;
|
||||||
|
}
|
||||||
|
|
||||||
@each $level, $radius in cfg.$elevation {
|
>.body {
|
||||||
&.elevated-#{$level} {
|
// double vertical spacing for body
|
||||||
@include effects.shadow(cfg.$elevation-color, $radius);
|
-fx-padding: 0 $padding-x 0 $padding-x;
|
||||||
|
-fx-alignment: TOP_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.footer {
|
||||||
|
-fx-alignment: TOP_LEFT;
|
||||||
|
-fx-padding: 0 $padding-x 0 $padding-x 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $level, $radius in cfg.$elevation {
|
||||||
|
&.elevated-#{$level} {
|
||||||
|
@include effects.shadow(cfg.$elevation-color, $radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.interactive:hover {
|
||||||
|
@include effects.shadow(cfg.$elevation-color, cfg.$elevation-interactive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.interactive:hover {
|
&:has-image {
|
||||||
@include effects.shadow(cfg.$elevation-color, cfg.$elevation-interactive);
|
>.container >.sub-header {
|
||||||
|
-fx-padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.edge-to-edge>.container {
|
||||||
|
-fx-border-width: 0;
|
||||||
|
-fx-border-radius: 0;
|
||||||
|
-fx-effect: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile {
|
.tile {
|
||||||
// prevent double indentation
|
>.container {
|
||||||
-fx-padding: 0;
|
// prevent double indentation
|
||||||
-fx-background-radius: 0;
|
-fx-padding: 0;
|
||||||
|
-fx-background-radius: 0;
|
||||||
|
|
||||||
|
>.header >.title {
|
||||||
|
-fx-font-size: $title-font-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edge-to-edge>.card {
|
|
||||||
-fx-border-width: 0;
|
|
||||||
-fx-border-radius: 0;
|
|
||||||
-fx-effect: none;
|
|
||||||
}
|
|
||||||
|
@ -46,5 +46,11 @@ $close-button-icon-size: 0.3em !default;
|
|||||||
// prevent double indentation inside dialog
|
// prevent double indentation inside dialog
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
-fx-background-radius: 0;
|
-fx-background-radius: 0;
|
||||||
|
|
||||||
|
>.container {
|
||||||
|
// prevent double indentation
|
||||||
|
-fx-padding: 0;
|
||||||
|
-fx-background-radius: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user