Skip to content

Commit

Permalink
Merge pull request #206 from dlsc-software-consulting-gmbh/enhancemen…
Browse files Browse the repository at this point in the history
…t-selected-month-pseudo-class

Implement Pseudo-Class Style for Selected Month in YearMonthView Component
  • Loading branch information
dlemmermann authored Sep 12, 2024
2 parents f751a0c + a598225 commit 377a932
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 45 deletions.
123 changes: 79 additions & 44 deletions gemsfx/src/main/java/com/dlsc/gemsfx/skins/YearMonthViewSkin.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.css.PseudoClass;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.ColumnConstraints;
Expand All @@ -20,18 +20,22 @@
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;

import java.time.LocalDate;
import java.time.Month;
import java.time.YearMonth;

public class YearMonthViewSkin extends SkinBase<YearMonthView> {

private static final PseudoClass SELECTED_MONTH_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
private static final PseudoClass CURRENT_MONTH_PSEUDO_CLASS = PseudoClass.getPseudoClass("current");

private final ObjectProperty<Integer> year = new SimpleObjectProperty<>(this, "year");
private boolean updatingMonthBox;

public YearMonthViewSkin(YearMonthView control) {
super(control);

year.set(control.getValue().getYear());
control.valueProperty().addListener(it -> year.set(control.getValue().getYear()));

Label yearLabel = new Label();
yearLabel.getStyleClass().add("year-label");
Expand Down Expand Up @@ -60,18 +64,7 @@ public YearMonthViewSkin(YearMonthView control) {

GridPane gridPane = new GridPane();
gridPane.getStyleClass().add("grid-pane");
gridPane.add(createMonth(Month.JANUARY), 0, 0);
gridPane.add(createMonth(Month.FEBRUARY), 2, 0);
gridPane.add(createMonth(Month.MARCH), 0, 1);
gridPane.add(createMonth(Month.APRIL), 2, 1);
gridPane.add(createMonth(Month.MAY), 0, 2);
gridPane.add(createMonth(Month.JUNE), 2, 2);
gridPane.add(createMonth(Month.JULY), 0, 3);
gridPane.add(createMonth(Month.AUGUST), 2, 3);
gridPane.add(createMonth(Month.SEPTEMBER), 0, 4);
gridPane.add(createMonth(Month.OCTOBER), 2, 4);
gridPane.add(createMonth(Month.NOVEMBER), 0, 5);
gridPane.add(createMonth(Month.DECEMBER), 2, 5);
addMonthBoxToGridPane(gridPane, control);

Region divider = new Region();
divider.getStyleClass().add("divider");
Expand Down Expand Up @@ -112,39 +105,81 @@ public YearMonthViewSkin(YearMonthView control) {
container.setClip(clip);

getChildren().add(container);
}

private Node createMonth(Month month) {
Label monthLabel = new Label(getSkinnable().getConverter().toString(month));
monthLabel.getStyleClass().add("month-label");
monthLabel.setMinWidth(Region.USE_PREF_SIZE);
monthLabel.setMaxWidth(Region.USE_PREF_SIZE);

YearMonthView view = getSkinnable();

Region selectionIndicator = new Region();
selectionIndicator.visibleProperty().bind(Bindings.createBooleanBinding(() -> view.getValue().equals(YearMonth.of(year.get(), month)), view.valueProperty(), year));
selectionIndicator.getStyleClass().add("selection-indicator");

VBox box = new VBox(monthLabel, selectionIndicator);
box.setMaxWidth(Region.USE_PREF_SIZE);
box.setAlignment(Pos.CENTER);
box.getStyleClass().add("month-box");
box.setOnMouseClicked(evt -> view.setValue(YearMonth.of(year.get(), month.getValue())));
box.disableProperty().bind(Bindings.createObjectBinding(() -> {
YearMonth earliestMonth = view.getEarliestMonth();
if (earliestMonth != null && YearMonth.of(view.getValue().getYear(), month.getValue()).isBefore(earliestMonth)) {
return true;
}
YearMonth latestMonth = view.getLatestMonth();
if (latestMonth != null && YearMonth.of(view.getValue().getYear(), month.getValue()).isAfter(latestMonth)) {
return true;
control.valueProperty().subscribe(value -> {
updatingMonthBox = true;
year.set(value.getYear());
updateMonthBoxes(value, gridPane);
updatingMonthBox = false;
});

year.addListener(it -> {
if (!updatingMonthBox) {
updateMonthBoxes(control.getValue(), gridPane);
}
return false;
}, view.earliestMonthProperty(), view.latestMonthProperty(), view.valueProperty()));
});
}

private void addMonthBoxToGridPane(GridPane gridPane, YearMonthView control) {
for (Month month : Month.values()) {
int columnIndex = (month.getValue() % 2 == 0) ? 2 : 0;
int rowIndex = (month.getValue() - 1) / 2;
gridPane.add(new MonthBox(month, control), columnIndex, rowIndex);
}
}

/**
* Updates the pseudo-class state of the MonthBoxes.
*/
private void updateMonthBoxes(YearMonth value, GridPane gridPane) {
Month selectedMonth = value.getMonth();
int currentYear = LocalDate.now().getYear();
Month currentMonth = LocalDate.now().getMonth();

gridPane.getChildren().stream()
.filter(node -> node instanceof MonthBox)
.map(node -> (MonthBox) node)
.forEach(box -> {
box.pseudoClassStateChanged(SELECTED_MONTH_PSEUDO_CLASS, box.getMonth() == selectedMonth);
box.pseudoClassStateChanged(CURRENT_MONTH_PSEUDO_CLASS, box.getMonth() == currentMonth && year.get() == currentYear);
});
}

GridPane.setMargin(box, new Insets(10, 30, 10, 30));
private class MonthBox extends VBox {

return box;
private final Month month;

public MonthBox(Month month, YearMonthView view) {
getStyleClass().add("month-box");

this.month = month;

Label monthLabel = new Label(view.getConverter().toString(month));
monthLabel.getStyleClass().add("month-label");
monthLabel.setMinWidth(Region.USE_PREF_SIZE);
monthLabel.setMaxWidth(Region.USE_PREF_SIZE);

Region indicator = new Region();
indicator.getStyleClass().add("indicator");

getChildren().setAll(monthLabel, indicator);
GridPane.setMargin(this, new Insets(10, 30, 10, 30));

setMaxWidth(Region.USE_PREF_SIZE);
setAlignment(Pos.CENTER);
setOnMouseClicked(evt -> view.setValue(YearMonth.of(year.get(), month.getValue())));
disableProperty().bind(Bindings.createObjectBinding(() -> {
YearMonth earliestMonth = view.getEarliestMonth();
if (earliestMonth != null && YearMonth.of(view.getValue().getYear(), month.getValue()).isBefore(earliestMonth)) {
return true;
}
YearMonth latestMonth = view.getLatestMonth();
return latestMonth != null && YearMonth.of(view.getValue().getYear(), month.getValue()).isAfter(latestMonth);
}, view.earliestMonthProperty(), view.latestMonthProperty(), view.valueProperty()));
}

public final Month getMonth() {
return month;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@
-fx-cursor: hand;
}

.year-month-view > .container > .grid-pane .month-box .selection-indicator {
.year-month-view > .container > .grid-pane .month-box > .indicator {
visibility: hidden;
-fx-pref-height: 2px;
-fx-background-color: -fx-text-background-color;
-fx-background-insets: 0px -2px 0px -2px;
}

.year-month-view > .container > .grid-pane .month-box:selected > .indicator {
visibility: visible;
}

.year-month-view > .container > .grid-pane .divider {
-fx-pref-width: 1px;
-fx-background-color: #e0e0e0;
Expand Down

0 comments on commit 377a932

Please sign in to comment.