From 219284316668476047b17be36f6d8b762d8f0a48 Mon Sep 17 00:00:00 2001 From: Izn432 Date: Mon, 21 Oct 2024 23:32:17 +0800 Subject: [PATCH] Add Calendar functionality --- .../java/keycontacts/ui/CalendarView.java | 215 +++++++++++++++++- src/main/resources/view/Calendar.fxml | 66 ++---- 2 files changed, 222 insertions(+), 59 deletions(-) diff --git a/src/main/java/keycontacts/ui/CalendarView.java b/src/main/java/keycontacts/ui/CalendarView.java index 675dc7f4c6b..af5f697b01b 100644 --- a/src/main/java/keycontacts/ui/CalendarView.java +++ b/src/main/java/keycontacts/ui/CalendarView.java @@ -1,9 +1,17 @@ package keycontacts.ui; import javafx.fxml.FXML; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Pane; +import javafx.scene.Node; +import javafx.scene.layout.HBox; import javafx.scene.layout.Region; +import javafx.scene.layout.RowConstraints; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; /** * The calendar view @@ -12,23 +20,212 @@ public class CalendarView extends UiPart { private static final String FXML = "Calendar.fxml"; private static final int NUM_ROWS = 7; - private static final int NUM_COLS = 24; + private static final int NUM_MINUTES = 24 * 60; + + @SuppressWarnings("unchecked") + private final List[] timeBlocks = (ArrayList[]) new ArrayList[NUM_ROWS]; + + private final List dayHBoxes = new ArrayList<>(); + + @FXML + private VBox grid; + + @FXML + private HBox mon; + + @FXML + private HBox tue; @FXML - private GridPane grid; + private HBox wed; + + @FXML + private HBox thu; + + @FXML + private HBox fri; + + @FXML + private HBox sat; + + @FXML + private HBox sun; public CalendarView() { super(FXML); + addHBoxes(); + generateGrid(); update(); + } - public void update() { + private void addHBoxes() { + dayHBoxes.add(mon); + dayHBoxes.add(tue); + dayHBoxes.add(wed); + dayHBoxes.add(thu); + dayHBoxes.add(fri); + dayHBoxes.add(sat); + dayHBoxes.add(sun); + } + + private void generateGrid() { for (int i = 0; i < NUM_ROWS; i++) { - for (int j = 0; j < NUM_COLS; j++) { - Pane pane = new Pane(); - pane.setStyle("-fx-background-color: white; -fx-border-color: black; -fx-border-width: 1;"); - grid.add(pane, j, i); + timeBlocks[i] = new ArrayList<>(); + } + } + + private void createBlock(int day, LocalTime startTime, LocalTime endTime, Color color) { + assert endTime.isAfter(startTime); + + TimeBlock timeBlock = new TimeBlock(startTime, endTime); + + List currentDayBlocks = timeBlocks[day]; + + TimeBlock previousTimeBlock = new TimeBlock(0, 0); + for (int i = 0; i <= currentDayBlocks.size(); i++) { + if (i == currentDayBlocks.size()) { + // attach + dayHBoxes.get(day).getChildren().add(i * 2, + timeBlock.createPadding(previousTimeBlock)); + dayHBoxes.get(day).getChildren().add(i * 2 + 1, + timeBlock.createBlock(color)); + currentDayBlocks.add(i, timeBlock); + + return; + } + + TimeBlock currentTimeBlock = currentDayBlocks.get(i); + assert !currentTimeBlock.isIntersecting(timeBlock); + if (currentTimeBlock.isAfter(timeBlock)) { + // attach + dayHBoxes.get(day).getChildren().add(i * 2, + timeBlock.createPadding(previousTimeBlock)); + dayHBoxes.get(day).getChildren().add(i * 2 + 1, + timeBlock.createBlock(color)); + currentDayBlocks.add(i, timeBlock); + + // fix padding + currentTimeBlock.subtractPaddingWidth(timeBlock.getTotalWidth()); + + return; + } + previousTimeBlock = currentTimeBlock; + } + } + + public void update() { + createBlock(0, LocalTime.of(1, 0, 0), LocalTime.of(1, 30, 0), + Color.BLUE); + createBlock(0, LocalTime.of(3, 0, 0), LocalTime.of(3, 30, 0), + Color.BLUE); + createBlock(0, LocalTime.of(2, 0, 0), LocalTime.of(2, 30, 0), + Color.RED); + } + + class TimeBlock { + + private final int startTimeMinutes; + private final int endTimeMinutes; + + private Rectangle block; + private Rectangle padding; + + private int paddingWidth; + private int blockWidth; + + public TimeBlock(LocalTime startTime, LocalTime endTime) { + this(startTime.getHour() * 60 + startTime.getMinute(), endTime.getHour() * 60 + endTime.getMinute()); + } + + private TimeBlock(int startTimeMinutes, int endTimeMinutes) { + this.startTimeMinutes = startTimeMinutes; + this.endTimeMinutes = endTimeMinutes; + } + + /** + * Returns the duration of the time block + */ + public int getDuration() { + return endTimeMinutes - startTimeMinutes; + } + + /** + * Returns the distance between this time block and the other + */ + public int getDistance(TimeBlock other) { + if (isIntersecting(other)) { + return 0; } + + if (isAfter(other)) { + return startTimeMinutes - other.endTimeMinutes; + } + + return other.startTimeMinutes - endTimeMinutes; + } + + /** + * Returns true if the given time block starts at or after this time block's end + */ + public boolean isAfter(TimeBlock other) { + return startTimeMinutes >= other.startTimeMinutes; + } + + /** + * Returns true if the given time block intersects with this time block + */ + public boolean isIntersecting(TimeBlock other) { + return startTimeMinutes <= other.endTimeMinutes && other.startTimeMinutes <= endTimeMinutes; + } + + /** + * Subtracts {@code width} from the padding width + */ + public void subtractPaddingWidth(int width) { + this.paddingWidth -= width; + updatePaddingBinding(); + } + + /** + * Updates the binding to the new padding width + */ + private void updatePaddingBinding() { + this.padding.widthProperty().bind(grid.widthProperty().multiply((double) paddingWidth / NUM_MINUTES)); + this.padding.heightProperty().bind(grid.heightProperty().divide(NUM_ROWS)); + } + + /** + * Updates the binding to the new block width + */ + private void updateBlockBinding() { + this.block.widthProperty().bind(grid.widthProperty().multiply((double) blockWidth / NUM_MINUTES)); + this.block.heightProperty().bind(grid.heightProperty().divide(NUM_ROWS)); + } + + /** + * Returns the total width this block takes, including padding + */ + public int getTotalWidth() { + return paddingWidth + blockWidth; + } + + public Node createPadding(TimeBlock previousTimeBlock) { + this.paddingWidth = getDistance(previousTimeBlock); + this.padding = new Rectangle(); + this.padding.setStyle("-fx-opacity: 0"); + updatePaddingBinding(); + return this.padding; + } + + public Node createBlock(Color color) { + this.blockWidth = getDuration(); + this.block = new Rectangle(); + this.block.setFill(color); + updateBlockBinding(); + return this.block; } } } + + diff --git a/src/main/resources/view/Calendar.fxml b/src/main/resources/view/Calendar.fxml index 32749ea9782..aad160f36b4 100644 --- a/src/main/resources/view/Calendar.fxml +++ b/src/main/resources/view/Calendar.fxml @@ -2,15 +2,12 @@ - - - - + @@ -76,7 +73,21 @@ - + + + + + + + + + + + + + + + @@ -151,51 +162,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -