diff --git a/CalendarFXSampler/src/main/java/com/calendarfx/demo/views/resources/HelloResourcesView.java b/CalendarFXSampler/src/main/java/com/calendarfx/demo/views/resources/HelloResourcesView.java index 30c604b3..d6513fe0 100644 --- a/CalendarFXSampler/src/main/java/com/calendarfx/demo/views/resources/HelloResourcesView.java +++ b/CalendarFXSampler/src/main/java/com/calendarfx/demo/views/resources/HelloResourcesView.java @@ -18,10 +18,13 @@ package com.calendarfx.demo.views.resources; import com.calendarfx.demo.CalendarFXDateControlSample; +import com.calendarfx.model.Calendar; import com.calendarfx.model.Calendar.Style; +import com.calendarfx.model.Entry; import com.calendarfx.view.DateControl; import com.calendarfx.view.DayViewBase.AvailabilityEditingEntryBehaviour; import com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy; +import com.calendarfx.view.DayViewBase.GridType; import com.calendarfx.view.resources.Resource; import com.calendarfx.view.resources.ResourcesView; import javafx.scene.Node; @@ -33,6 +36,9 @@ import javafx.scene.control.ToggleButton; import javafx.scene.layout.VBox; +import java.time.LocalDate; +import java.time.LocalTime; + public class HelloResourcesView extends CalendarFXDateControlSample { private ResourcesView resourcesView; @@ -74,6 +80,10 @@ public Node getControlPanel() { behaviourBox.getItems().setAll(AvailabilityEditingEntryBehaviour.values()); behaviourBox.valueProperty().bindBidirectional(resourcesView.entryViewAvailabilityEditingBehaviourProperty()); + ChoiceBox gridTypeBox = new ChoiceBox<>(); + gridTypeBox.getItems().setAll(GridType.values()); + gridTypeBox.valueProperty().bindBidirectional(resourcesView.gridTypeProperty()); + CheckBox adjustBox = new CheckBox("Adjust first day of week"); adjustBox.selectedProperty().bindBidirectional(resourcesView.adjustToFirstDayOfWeekProperty()); @@ -82,13 +92,14 @@ public Node getControlPanel() { slider.setMax(1); slider.valueProperty().bindBidirectional(resourcesView.entryViewAvailabilityEditingOpacityProperty()); - return new VBox(10, availabilityButton, datePicker, adjustBox, daysBox, new Label("Availability Behaviour"), behaviourBox, new Label("Availability Opacity"), slider); + return new VBox(10, availabilityButton, datePicker, adjustBox, daysBox, new Label("Availability Behaviour"), behaviourBox, new Label("Availability Opacity"), slider, new Label("Grid Type"), gridTypeBox); } @Override protected DateControl createControl() { resourcesView = new ResourcesView(); resourcesView.setNumberOfDays(5); + resourcesView.setGridType(GridType.CUSTOM); resourcesView.setEarlyLateHoursStrategy(EarlyLateHoursStrategy.HIDE); resourcesView.getResources().addAll(create("Dirk", Style.STYLE1), create("Katja", Style.STYLE2), create("Philip", Style.STYLE3)); //, create("Jule", Style.STYLE4), create("Armin", Style.STYLE5)); return resourcesView; @@ -98,11 +109,27 @@ private Resource create(String name, Style style) { Resource resource = new Resource(name); resource.getAvailabilityCalendar().setName("Availability of " + name); resource.getCalendar().setStyle(style); - resource.getCalendar().addEventHandler(evt -> System.out.println(evt)); - resource.getAvailabilityCalendar().addEventHandler(evt -> System.out.println(evt)); + fillAvailabilities(resource.getAvailabilityCalendar()); return resource; } + private void fillAvailabilities(Calendar calendar) { + LocalDate date = LocalDate.now(); + for (int i = 0; i < 14; i++) { + // fourteen days is enough for this demo + Entry morning = new Entry("Morning"); + morning.setInterval(date, LocalTime.MIN, date, LocalTime.of(8, 0)); + calendar.addEntry(morning); + Entry noon = new Entry("Noon"); + noon.setInterval(date, LocalTime.of(12, 0), date, LocalTime.of(13, 0)); + calendar.addEntry(noon); + Entry evening = new Entry("Evening"); + evening.setInterval(date, LocalTime.of(18, 0), date, LocalTime.MAX); + calendar.addEntry(evening); + date = date.plusDays(1); + } + } + public static void main(String[] args) { launch(args); } diff --git a/CalendarFXView/src/main/java/com/calendarfx/view/AllDayView.java b/CalendarFXView/src/main/java/com/calendarfx/view/AllDayView.java index c554257d..21dbe4c2 100644 --- a/CalendarFXView/src/main/java/com/calendarfx/view/AllDayView.java +++ b/CalendarFXView/src/main/java/com/calendarfx/view/AllDayView.java @@ -68,13 +68,11 @@ public class AllDayView extends DateControl implements ZonedDateTimeProvider { /** * Constructs a new view for the given number of days. * - * @param numberOfDays - * the number of days to be shown by this view + * @param numberOfDays the number of days to be shown by this view */ public AllDayView(int numberOfDays) { if (numberOfDays <= 0) { - throw new IllegalArgumentException( - "number of days must be larger than zero"); + throw new IllegalArgumentException("number of days must be larger than zero"); } getStyleClass().add(ALL_DAY_VIEW); @@ -122,8 +120,7 @@ protected Skin createDefaultSkin() { */ public final ObjectProperty extraPaddingProperty() { if (extraPadding == null) { - extraPadding = new StyleableObjectProperty(new Insets(2, 0, - 9, 0)) { + extraPadding = new StyleableObjectProperty<>(new Insets(2, 0, 9, 0)) { @Override public CssMetaData getCssMetaData() { @@ -157,8 +154,7 @@ public final Insets getExtraPadding() { /** * Sets the value of {@link #extraPaddingProperty()}. * - * @param padding - * padding insets + * @param padding padding insets */ public final void setExtraPadding(Insets padding) { requireNonNull(padding); @@ -209,8 +205,7 @@ public final double getRowHeight() { /** * Sets the value of the {@link #rowHeightProperty()}. * - * @param height - * the new row height + * @param height the new row height */ public final void setRowHeight(double height) { rowHeightProperty().set(height); @@ -259,13 +254,11 @@ public final double getRowSpacing() { /** * Sets the value of {@link #rowSpacingProperty()}. * - * @param space - * the space between rows in pixel + * @param space the space between rows in pixel */ public final void setRowSpacing(double space) { if (space < 0) { - throw new IllegalArgumentException( - "row spacing can not be smaller than zero"); + throw new IllegalArgumentException("row spacing can not be smaller than zero"); } rowSpacingProperty().set(space); } @@ -313,15 +306,13 @@ public final double getColumnSpacing() { /** * Sets the value of {@link #columnSpacingProperty()}. * - * @param space - * the space between columns in pixel + * @param space the space between columns in pixel */ public final void setColumnSpacing(double space) { columnSpacingProperty().set(space); } - private final BooleanProperty adjustToFirstDayOfWeek = new SimpleBooleanProperty( - this, "adjustToFirstDayOfWeek", true); + private final BooleanProperty adjustToFirstDayOfWeek = new SimpleBooleanProperty(this, "adjustToFirstDayOfWeek", true); /** * A flag used to indicate that the view should always show the first day of @@ -349,15 +340,13 @@ public final boolean isAdjustToFirstDayOfWeek() { /** * Sets the value of {@link #adjustToFirstDayOfWeekProperty()}. * - * @param adjust - * if true the view will always show the first day of the week + * @param adjust if true the view will always show the first day of the week */ public final void setAdjustToFirstDayOfWeek(boolean adjust) { adjustToFirstDayOfWeekProperty().set(adjust); } - private final IntegerProperty numberOfDays = new SimpleIntegerProperty( - this, "numberOfDays"); + private final IntegerProperty numberOfDays = new SimpleIntegerProperty(this, "numberOfDays"); /** * Stores the number of days that will be shown by this view. This value @@ -382,21 +371,17 @@ public final int getNumberOfDays() { /** * Sets the value of {@link #numberOfDaysProperty()}. * - * @param number - * the new number of days shown by the view + * @param number the new number of days shown by the view */ public final void setNumberOfDays(int number) { if (number < 1) { - throw new IllegalArgumentException( - "invalid number of days, must be larger than 0 but was " - + number); + throw new IllegalArgumentException("invalid number of days, must be larger than 0 but was " + number); } numberOfDaysProperty().set(number); } - private final ObjectProperty, AllDayEntryView>> entryViewFactory = new SimpleObjectProperty<>( - this, "entryViewFactory", AllDayEntryView::new); + private final ObjectProperty, AllDayEntryView>> entryViewFactory = new SimpleObjectProperty<>(this, "entryViewFactory", AllDayEntryView::new); /** * A callback used for producing views for entries. The views have to be of @@ -422,12 +407,35 @@ public final Callback, AllDayEntryView> getEntryViewFactory() { * * @param factory the new entry view factory */ - public final void setEntryViewFactory( - Callback, AllDayEntryView> factory) { + public final void setEntryViewFactory(Callback, AllDayEntryView> factory) { requireNonNull(factory); entryViewFactoryProperty().set(factory); } + private final ObjectProperty> separatorFactory = new SimpleObjectProperty<>(this, "separatorFactory", it -> { + Region region = new Region(); + region.getStyleClass().add("weekday-separator"); + return region; + }); + + + public final Callback getSeparatorFactory() { + return separatorFactory.get(); + } + + /** + * A factory used for creating (optional) vertical separators between the all day view. + * + * @return the separator factory + */ + public final ObjectProperty> separatorFactoryProperty() { + return separatorFactory; + } + + public final void setSeparatorFactory(Callback separatorFactory) { + this.separatorFactory.set(separatorFactory); + } + private static class StyleableProperties { private static final List> STYLEABLES; diff --git a/CalendarFXView/src/main/java/com/calendarfx/view/DayViewBase.java b/CalendarFXView/src/main/java/com/calendarfx/view/DayViewBase.java index f622b3c2..1ad178f5 100644 --- a/CalendarFXView/src/main/java/com/calendarfx/view/DayViewBase.java +++ b/CalendarFXView/src/main/java/com/calendarfx/view/DayViewBase.java @@ -107,6 +107,43 @@ public DayViewBase() { setMinWidth(0); // important, so that multi day views apply same width for all day views } + /** + * A list of possible grid types supported by the day view. + * + * @see #gridTypeProperty() + * @see #gridLinesProperty() + * @see #gridLineColorProperty() + */ + public enum GridType { + STANDARD, + CUSTOM + } + + private final ObjectProperty gridType = new SimpleObjectProperty<>(this, "gridType", GridType.STANDARD); + + public final GridType getGridType() { + return gridType.get(); + } + + /** + * Determines the type of grid / grid lines will be used for full hours, + * half hours, and so on. The {@link GridType#STANDARD} only supports grid lines + * for full hours and half hours. The {@link GridType#CUSTOM} can be configured + * via a {@link VirtualGrid} to show any kind of grid lines. + * + * @return the grid type + * + * @see #gridLinesProperty() + * @see #gridLineColorProperty() + */ + public final ObjectProperty gridTypeProperty() { + return gridType; + } + + public final void setGridType(GridType gridType) { + this.gridType.set(gridType); + } + private final ObjectProperty gridLines = new SimpleObjectProperty<>(this, "virtualGrid", new VirtualGrid("Grid Lines", "Grid", ChronoUnit.MINUTES, 30)); public final VirtualGrid getGridLines() { @@ -1055,6 +1092,7 @@ public final void bind(DayViewBase otherControl, boolean bindDate) { Bindings.bindBidirectional(otherControl.entryViewAvailabilityEditingOpacityProperty(), entryViewAvailabilityEditingOpacityProperty()); Bindings.bindBidirectional(otherControl.gridLinesProperty(), gridLinesProperty()); Bindings.bindBidirectional(otherControl.gridLineColorProperty(), gridLineColorProperty()); + Bindings.bindBidirectional(otherControl.gridTypeProperty(), gridTypeProperty()); } public final void unbind(DayViewBase otherControl) { @@ -1080,6 +1118,7 @@ public final void unbind(DayViewBase otherControl) { Bindings.unbindBidirectional(otherControl.entryViewAvailabilityEditingOpacityProperty(), entryViewAvailabilityEditingOpacityProperty()); Bindings.unbindBidirectional(otherControl.gridLinesProperty(), gridLinesProperty()); Bindings.unbindBidirectional(otherControl.gridLineColorProperty(), gridLineColorProperty()); + Bindings.unbindBidirectional(otherControl.gridTypeProperty(), gridTypeProperty()); } private static final String DAY_VIEW_BASE_CATEGORY = "Date View Base"; @@ -1686,6 +1725,49 @@ public boolean isEditable() { } }); + items.add(new Item() { + + @Override + public Optional> getObservableValue() { + return Optional.of(gridTypeProperty()); + } + + @Override + public void setValue(Object value) { + setGridType((GridType) value); + } + + @Override + public Object getValue() { + return getGridType(); + } + + @Override + public Class getType() { + return GridType.class; + } + + @Override + public String getName() { + return "Grid Type"; + } + + @Override + public String getDescription() { + return "Specifies the grid type."; + } + + @Override + public String getCategory() { + return DAY_VIEW_BASE_CATEGORY; + } + + @Override + public boolean isEditable() { + return true; + } + }); + items.add(new Item() { @Override diff --git a/CalendarFXView/src/main/java/com/calendarfx/view/WeekDayHeaderView.java b/CalendarFXView/src/main/java/com/calendarfx/view/WeekDayHeaderView.java index e9ce09ba..a464c89d 100644 --- a/CalendarFXView/src/main/java/com/calendarfx/view/WeekDayHeaderView.java +++ b/CalendarFXView/src/main/java/com/calendarfx/view/WeekDayHeaderView.java @@ -31,6 +31,7 @@ import javafx.scene.control.Label; import javafx.scene.control.Skin; import javafx.scene.input.MouseButton; +import javafx.scene.layout.Region; import javafx.util.Callback; import org.controlsfx.control.PropertySheet.Item; @@ -106,14 +107,36 @@ public final Callback getCellFactory() { * @param factory * the cell factory */ - public final void setCellFactory( - Callback factory) { + public final void setCellFactory(Callback factory) { requireNonNull(factory); cellFactoryProperty().set(factory); } - private final IntegerProperty numberOfDays = new SimpleIntegerProperty( - this, "numberOfDays", 7); + private final ObjectProperty> separatorFactory = new SimpleObjectProperty<>(this, "separatorFactory", it-> { + Region region = new Region(); + region.getStyleClass().add("weekday-separator"); + return region; + }); + + + public final Callback getSeparatorFactory() { + return separatorFactory.get(); + } + + /** + * A factory used for creating (optional) vertical separators between the week day headers. + * + * @return the separator factory + */ + public final ObjectProperty> separatorFactoryProperty() { + return separatorFactory; + } + + public final void setSeparatorFactory(Callback separatorFactory) { + this.separatorFactory.set(separatorFactory); + } + + private final IntegerProperty numberOfDays = new SimpleIntegerProperty(this, "numberOfDays", 7); /** * Stores the number of days that will be shown by this view. This value diff --git a/CalendarFXView/src/main/java/com/calendarfx/view/WeekView.java b/CalendarFXView/src/main/java/com/calendarfx/view/WeekView.java index 6d5db9a6..11c535cc 100644 --- a/CalendarFXView/src/main/java/com/calendarfx/view/WeekView.java +++ b/CalendarFXView/src/main/java/com/calendarfx/view/WeekView.java @@ -31,6 +31,7 @@ import javafx.collections.ObservableList; import javafx.geometry.Bounds; import javafx.scene.control.Skin; +import javafx.scene.layout.Region; import javafx.util.Callback; import org.controlsfx.control.PropertySheet; @@ -310,6 +311,30 @@ public final LocalDate getEndDate() { return endDate.get(); } + private final ObjectProperty> separatorFactory = new SimpleObjectProperty<>(this, "separatorFactory", it-> { + Region region = new Region(); + region.getStyleClass().add("weekday-separator"); + return region; + }); + + + public final Callback getSeparatorFactory() { + return separatorFactory.get(); + } + + /** + * A factory used for creating (optional) vertical separators between the week days. + * + * @return the separator factory + */ + public final ObjectProperty> separatorFactoryProperty() { + return separatorFactory; + } + + public final void setSeparatorFactory(Callback separatorFactory) { + this.separatorFactory.set(separatorFactory); + } + private static final String WEEK_VIEW_CATEGORY = "Week View"; @Override diff --git a/CalendarFXView/src/main/java/com/calendarfx/view/resources/ResourcesView.java b/CalendarFXView/src/main/java/com/calendarfx/view/resources/ResourcesView.java index 968b591c..2cbaeed3 100644 --- a/CalendarFXView/src/main/java/com/calendarfx/view/resources/ResourcesView.java +++ b/CalendarFXView/src/main/java/com/calendarfx/view/resources/ResourcesView.java @@ -197,7 +197,7 @@ public final void setNumberOfDays(int number) { private final ObjectProperty> resourceHeaderFactory = new SimpleObjectProperty<>(this,"headerFactory", resource -> { Label label = new Label(resource.toString()); label.setAlignment(Pos.CENTER); - label.getStyleClass().add("resource-header-label"); + label.getStyleClass().add("resource-header"); label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); return label; }); diff --git a/CalendarFXView/src/main/java/impl/com/calendarfx/view/AllDayViewSkin.java b/CalendarFXView/src/main/java/impl/com/calendarfx/view/AllDayViewSkin.java index 935586ee..e39c0189 100644 --- a/CalendarFXView/src/main/java/impl/com/calendarfx/view/AllDayViewSkin.java +++ b/CalendarFXView/src/main/java/impl/com/calendarfx/view/AllDayViewSkin.java @@ -32,8 +32,7 @@ import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.Control; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.util.Callback; @@ -57,16 +56,16 @@ public class AllDayViewSkin extends DateControlSkin implements LoadD private static final String ALL_DAY_BACKGROUND_REGION_WEEKEND = "weekend"; private final DataLoader dataLoader; - private final GridPane pane; + private final HBox container; public AllDayViewSkin(AllDayView view) { super(view); view.setFocusTraversable(true); - pane = new GridPane(); - pane.getStyleClass().add("container"); - getChildren().add(pane); + container = new HBox(); + container.getStyleClass().add("container"); + getChildren().add(container); // update backgrounds InvalidationListener updateBackgroundsListener = evt -> updateBackgrounds(); @@ -296,8 +295,6 @@ protected void entryIntervalChanged(CalendarEvent evt) { } } - private final TimeBoundsResolver resolver = new TimeBoundsResolver(); - @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { @@ -397,34 +394,35 @@ protected void layoutChildren(double contentX, double contentY, double contentWi } private void updateBackgrounds() { - // the day views + container.getChildren().clear(); - pane.getChildren().clear(); - List constraints = new ArrayList<>(); + AllDayView allDayView = getSkinnable(); + Callback separatorFactory = allDayView.getSeparatorFactory(); - int numberOfDays = getSkinnable().getNumberOfDays(); + int numberOfDays = allDayView.getNumberOfDays(); for (int i = 0; i < numberOfDays; i++) { - ColumnConstraints con = new ColumnConstraints(); - con.setPercentWidth((double) 100 / (double) numberOfDays); - constraints.add(con); Region region = new Region(); + region.setPrefWidth(1); // equal width distribution region.setMaxWidth(Double.MAX_VALUE); region.getStyleClass().add(ALL_DAY_BACKGROUND_REGION); - GridPane.setHgrow(region, Priority.ALWAYS); - GridPane.setVgrow(region, Priority.ALWAYS); - GridPane.setFillHeight(region, true); - GridPane.setFillWidth(region, true); final int day = i; - getSkinnable().dateProperty().addListener(evt -> updateRegion(region, day)); + allDayView.dateProperty().addListener(evt -> updateRegion(region, day)); updateRegion(region, day); - pane.add(region, i, 0); - } + HBox.setHgrow(region, Priority.ALWAYS); + container.getChildren().add(region); - pane.getColumnConstraints().setAll(constraints); + if (separatorFactory != null && i < numberOfDays - 1) { + Region separator = separatorFactory.call(allDayView); + if (separator != null) { + container.getChildren().add(separator); + HBox.setHgrow(separator, Priority.NEVER); + } + } + } - getSkinnable().requestLayout(); + allDayView.requestLayout(); } private void updateRegion(Region region, int day) { diff --git a/CalendarFXView/src/main/java/impl/com/calendarfx/view/DayViewSkin.java b/CalendarFXView/src/main/java/impl/com/calendarfx/view/DayViewSkin.java index 76b7b692..3602fb92 100644 --- a/CalendarFXView/src/main/java/impl/com/calendarfx/view/DayViewSkin.java +++ b/CalendarFXView/src/main/java/impl/com/calendarfx/view/DayViewSkin.java @@ -27,6 +27,7 @@ import com.calendarfx.view.DayEntryView; import com.calendarfx.view.DayView; import com.calendarfx.view.DayViewBase.EarlyLateHoursStrategy; +import com.calendarfx.view.DayViewBase.GridType; import com.calendarfx.view.DayViewBase.OverlapResolutionStrategy; import com.calendarfx.view.DraggedEntry; import com.calendarfx.view.EntryViewBase; @@ -199,6 +200,7 @@ public DayViewSkin(T view) { view.startTimeProperty().addListener(styleLinesListener); view.endTimeProperty().addListener(styleLinesListener); view.earlyLateHoursStrategyProperty().addListener(styleLinesListener); + view.gridTypeProperty().addListener(styleLinesListener); loadData("initial data loading"); @@ -229,6 +231,7 @@ public DayViewSkin(T view) { view.gridLinesProperty().addListener(drawBackgroundCanvasListener); view.gridLineColorProperty().addListener(drawBackgroundCanvasListener); + view.gridTypeProperty().addListener(drawBackgroundCanvasListener); } @@ -342,7 +345,7 @@ private void createLine(String styleClass) { line.getStyleClass().add(styleClass); } lines.add(line); - //getChildren().add(line); + getChildren().add(line); } private void updateLineStyling() { @@ -385,24 +388,28 @@ private void updateLineStyling() { } } - switch (dayView.getEarlyLateHoursStrategy()) { - case HIDE: - /* - * We do not show ... a) lines before the start time and after - * the end time b) lines directly on the start time or end time - * because they make the UI look messy - */ - line.setVisible(!time.isBefore(startTime) && !time.equals(startTime) && !time.isAfter(endTime) && !time.equals(endTime)); - break; - case SHOW: - line.setVisible(true); - break; - case SHOW_COMPRESSED: - line.setVisible(!halfHourLine); - break; - default: - break; + if (dayView.getGridType().equals(GridType.STANDARD)) { + switch (dayView.getEarlyLateHoursStrategy()) { + case HIDE: + /* + * We do not show ... a) lines before the start time and after + * the end time b) lines directly on the start time or end time + * because they make the UI look messy + */ + line.setVisible(!time.isBefore(startTime) && !time.equals(startTime) && !time.isAfter(endTime) && !time.equals(endTime)); + break; + case SHOW: + line.setVisible(true); + break; + case SHOW_COMPRESSED: + line.setVisible(!halfHourLine); + break; + default: + break; + } + } else { + line.setVisible(false); } } } @@ -1094,31 +1101,32 @@ public void draw() { } } - gc.setStroke(dayView.getGridLineColor()); + if (dayView.getGridType().equals(GridType.CUSTOM)) { + gc.setStroke(dayView.getGridLineColor()); - ZonedDateTime startTime = dayView.getZonedDateTimeMin(); - ZonedDateTime endTime = dayView.getZonedDateTimeMax(); + ZonedDateTime startTime = dayView.getZonedDateTimeMin(); + ZonedDateTime endTime = dayView.getZonedDateTimeMax(); - if (dayView.getEarlyLateHoursStrategy().equals(EarlyLateHoursStrategy.HIDE)) { - startTime = dayView.getZonedDateTimeStart(); - endTime = dayView.getZonedDateTimeEnd(); - } + if (dayView.getEarlyLateHoursStrategy().equals(EarlyLateHoursStrategy.HIDE)) { + startTime = dayView.getZonedDateTimeStart(); + endTime = dayView.getZonedDateTimeEnd(); + } - VirtualGrid virtualGrid = dayView.getGridLines(); + VirtualGrid virtualGrid = dayView.getGridLines(); - do { - double y = ViewHelper.getTimeLocation(dayView, startTime); - if (startTime.toLocalTime().getMinute() == 0) { - gc.setLineDashes(null); - } else { - gc.setLineDashes(2, 2); - } - gc.strokeLine(0, y, getWidth(), y); - startTime = startTime.plus(virtualGrid.getAmount(), virtualGrid.getUnit()); - } while (startTime.isBefore(endTime)); + do { + double y = ViewHelper.getTimeLocation(dayView, startTime); + if (startTime.toLocalTime().getMinute() == 0) { + gc.setLineDashes(null); + } else { + gc.setLineDashes(2, 2); + } + gc.strokeLine(0, y, getWidth(), y); + startTime = startTime.plus(virtualGrid.getAmount(), virtualGrid.getUnit()); + } while (startTime.isBefore(endTime)); + } gc.setLineDashes(null); - gc.strokeLine(getWidth() - gc.getLineWidth(), 0, getWidth() - gc.getLineWidth(), getHeight() - 1); } } } diff --git a/CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekDayHeaderViewSkin.java b/CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekDayHeaderViewSkin.java index e8bebbcf..7786e0b9 100644 --- a/CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekDayHeaderViewSkin.java +++ b/CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekDayHeaderViewSkin.java @@ -20,12 +20,14 @@ import com.calendarfx.view.WeekDayHeaderView.WeekDayCell; import javafx.beans.InvalidationListener; import javafx.scene.control.SkinBase; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; import javafx.util.Callback; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import static java.time.temporal.ChronoField.DAY_OF_WEEK; @@ -33,15 +35,15 @@ public class WeekDayHeaderViewSkin extends SkinBase { private static final String TODAY = "today"; - private final GridPane pane; + private final HBox hbox; public WeekDayHeaderViewSkin(WeekDayHeaderView view) { super(view); - pane = new GridPane(); - pane.getStyleClass().add("container"); + hbox = new HBox(); + hbox.getStyleClass().add("container"); - getChildren().add(pane); + getChildren().add(hbox); final InvalidationListener updateListener = it -> updateControl(); @@ -54,33 +56,41 @@ public WeekDayHeaderViewSkin(WeekDayHeaderView view) { updateControl(); } - private void updateControl() { - pane.getChildren().clear(); - pane.getColumnConstraints().clear(); + private final List listeners = new ArrayList<>(); + private void updateControl() { + hbox.getChildren().clear(); // the day views WeekDayHeaderView view = getSkinnable(); + listeners.forEach(l -> view.dateProperty().removeListener(l)); + final int numberOfDays = view.getNumberOfDays(); + Callback separatorFactory = view.getSeparatorFactory(); Callback cellFactory = view.getCellFactory(); + for (int i = 0; i < numberOfDays; i++) { - ColumnConstraints con = new ColumnConstraints(); - con.setPercentWidth((double) 100 / (double) numberOfDays); - pane.getColumnConstraints().add(con); WeekDayCell cell = cellFactory.call(view); - GridPane.setHgrow(cell, Priority.ALWAYS); - GridPane.setVgrow(cell, Priority.ALWAYS); - GridPane.setFillHeight(cell, true); - GridPane.setFillWidth(cell, true); + cell.setPrefWidth(1); // equal width distribution final int dayCount = i; - /* - * TODO: listener must be removed when number of days change. - */ - view.dateProperty().addListener(evt -> updateCell(cell, dayCount)); + + InvalidationListener invalidationListener = evt -> updateCell(cell, dayCount); + listeners.add(invalidationListener); + + view.dateProperty().addListener(invalidationListener); updateCell(cell, dayCount); - pane.add(cell, i, 0); + HBox.setHgrow(cell, Priority.ALWAYS); + hbox.getChildren().add(cell); + + if (separatorFactory != null && i < numberOfDays - 1) { + Region separator = separatorFactory.call(view); + if (separator != null) { + HBox.setHgrow(separator, Priority.NEVER); + hbox.getChildren().add(separator); + } + } } } diff --git a/CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekViewSkin.java b/CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekViewSkin.java index df5c9d00..c52c6bde 100644 --- a/CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekViewSkin.java +++ b/CalendarFXView/src/main/java/impl/com/calendarfx/view/WeekViewSkin.java @@ -21,9 +21,9 @@ import com.calendarfx.view.WeekView.WeekDayParameter; import javafx.beans.InvalidationListener; import javafx.scene.control.SkinBase; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; import javafx.scene.shape.Rectangle; import javafx.util.Callback; @@ -35,7 +35,7 @@ public class WeekViewSkin extends SkinBase { - private final GridPane dayGridPane = new GridPane(); + private final HBox container = new HBox(); public WeekViewSkin(WeekView view) { super(view); @@ -55,13 +55,13 @@ public WeekViewSkin(WeekView view) { new DayViewEditController(view); - getChildren().add(dayGridPane); + getChildren().add(container); } @Override protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) { - final double height = dayGridPane.prefHeight(-1); - dayGridPane.resizeRelocate(contentX, contentY, contentWidth, height); + final double height = container.prefHeight(-1); + container.resizeRelocate(contentX, contentY, contentWidth, height); } private void buildDays() { @@ -71,8 +71,7 @@ private void buildDays() { // before rebuilding make sure to unbind previous day view children weekDayViews.forEach(view -> getSkinnable().unbind(view)); - dayGridPane.getChildren().clear(); - dayGridPane.getColumnConstraints().clear(); + container.getChildren().clear(); weekDayViews.clear(); @@ -80,13 +79,13 @@ private void buildDays() { int numberOfDays = weekView.getNumberOfDays(); - for (int i = 0; i < numberOfDays; i++) { - ColumnConstraints con = new ColumnConstraints(); - con.setPercentWidth((double) 100 / (double) numberOfDays); - dayGridPane.getColumnConstraints().add(con); + Callback separatorFactory = weekView.getSeparatorFactory(); + for (int i = 0; i < numberOfDays; i++) { WeekDayParameter param = new WeekDayParameter(weekView); + WeekDayView weekDayView = weekDayViewFactory.call(param); + weekDayView.setPrefWidth(1); // equal width distribution weekDayView.getProperties().put("week.view", weekView); weekDayView.earliestTimeUsedProperty().addListener(it -> updateUsedTimes()); weekDayView.latestTimeUsedProperty().addListener(it -> updateUsedTimes()); @@ -98,9 +97,6 @@ private void buildDays() { weekDayView.getStyleClass().add("last-day"); } - GridPane.setHgrow(weekDayView, Priority.ALWAYS); - GridPane.setVgrow(weekDayView, Priority.ALWAYS); - final int dayCount = i; final InvalidationListener updateListener = it -> updateDate(weekDayView, dayCount); @@ -111,7 +107,15 @@ private void buildDays() { getSkinnable().bind(weekDayView, false); - dayGridPane.add(weekDayView, i, 0); + HBox.setHgrow(weekDayView, Priority.ALWAYS); + container.getChildren().add(weekDayView); + + if (separatorFactory != null && i < numberOfDays - 1) { + Region separator = separatorFactory.call(weekView); + if (separator != null) { + container.getChildren().add(separator); + } + } weekDayViews.add(weekDayView); } diff --git a/CalendarFXView/src/main/java/impl/com/calendarfx/view/resources/ResourcesContainerSkin.java b/CalendarFXView/src/main/java/impl/com/calendarfx/view/resources/ResourcesContainerSkin.java index 99fadd1f..662b6c83 100644 --- a/CalendarFXView/src/main/java/impl/com/calendarfx/view/resources/ResourcesContainerSkin.java +++ b/CalendarFXView/src/main/java/impl/com/calendarfx/view/resources/ResourcesContainerSkin.java @@ -41,6 +41,20 @@ private void updateView() { WeekView weekView = container.getWeekViewFactory().call(resource); + weekView.getStyleClass().removeAll("only", "first", "middle", "last"); + + if (resources.size() == 1) { + weekView.getStyleClass().add("only"); + } else { + if (i == 0) { + weekView.getStyleClass().add("first"); + } else if (i == resources.size() - 1) { + weekView.getStyleClass().add("last"); + } else { + weekView.getStyleClass().add("middle"); + } + } + weekView.setPrefWidth(0); // so they all end up with the same percentage width // bind day view to container but remove bindings that interfere diff --git a/CalendarFXView/src/main/java/impl/com/calendarfx/view/resources/ResourcesViewSkin.java b/CalendarFXView/src/main/java/impl/com/calendarfx/view/resources/ResourcesViewSkin.java index e8369d75..1380049d 100644 --- a/CalendarFXView/src/main/java/impl/com/calendarfx/view/resources/ResourcesViewSkin.java +++ b/CalendarFXView/src/main/java/impl/com/calendarfx/view/resources/ResourcesViewSkin.java @@ -120,6 +120,14 @@ private void updateView() { gridPane.add(timeScaleScrollPane, 0, 1); } + Region upperLeftCorner = new Region(); + upperLeftCorner.getStyleClass().add("upper-left-corner"); + gridPane.add(upperLeftCorner, 0, 0); + + Region upperRightCorner = new Region(); + upperRightCorner.getStyleClass().add("upper-right-corner"); + gridPane.add(upperRightCorner, 2, 0); + HBox headerBox = new HBox(); headerBox.getStyleClass().add("header-box"); @@ -131,8 +139,23 @@ private void updateView() { ObservableList resources = view.getResources(); for (int i = 0; i < resources.size(); i++) { T resource = resources.get(i); + Node headerNode = resourceHeaderFactory.call(resource); + VBox resourceHeader = new VBox(headerNode); + resourceHeader.getStyleClass().removeAll("only", "first", "middle", "last"); + + if (resources.size() == 1) { + resourceHeader.getStyleClass().add("only"); + } else { + if (i == 0) { + resourceHeader.getStyleClass().add("first"); + } else if (i == resources.size() - 1) { + resourceHeader.getStyleClass().add("last"); + } else { + resourceHeader.getStyleClass().add("middle"); + } + } if (view.isShowAllDayView()) { AllDayView allDayView = new AllDayView(); @@ -158,7 +181,6 @@ private void updateView() { } resourceHeader.getStyleClass().add("resource-header-view"); - HBox.setHgrow(resourceHeader, Priority.ALWAYS); WeekDayHeaderView weekDayHeaderView = view.getWeekDayHeaderViewFactory().call(resource); weekDayHeaderView.adjustToFirstDayOfWeekProperty().bind(view.adjustToFirstDayOfWeekProperty()); @@ -169,6 +191,18 @@ private void updateView() { resourceHeader.getChildren().add(weekDayHeaderView); headerBox.getChildren().add(resourceHeader); + HBox.setHgrow(resourceHeader, Priority.ALWAYS); + + if (i < resources.size() - 1) { + Callback separatorFactory = view.getSeparatorFactory(); + if (separatorFactory != null) { + Region separator = separatorFactory.call(resource); + if (separator != null) { + headerBox.getChildren().add(separator); + HBox.setHgrow(separator, Priority.NEVER); + } + } + } } ColumnConstraints dayViewsConstraints = new ColumnConstraints(); diff --git a/CalendarFXView/src/main/resources/com/calendarfx/view/calendar.css b/CalendarFXView/src/main/resources/com/calendarfx/view/calendar.css index 73d054c5..60c3ca31 100644 --- a/CalendarFXView/src/main/resources/com/calendarfx/view/calendar.css +++ b/CalendarFXView/src/main/resources/com/calendarfx/view/calendar.css @@ -600,6 +600,20 @@ -fx-alignment: center-right; } +/* + * -------------------------------------------------------------------------------------------------------------- + * Styles needed by the week day separators. + */ +.week-view .weekday-separator, +.weekday-header-view .weekday-separator, +.all-day-view .weekday-separator { + -size: 0px; + -fx-pref-width: -size; + -fx-min-width: -size; + -fx-max-width: -size; + -fx-background-color: lightgrey; +} + /* * -------------------------------------------------------------------------------------------------------------- * Styles needed by the day view. @@ -2038,13 +2052,100 @@ .resources-view { } -.resources-view .header-box .resource-header-view .resource-header-label { +.resources-view .header-box .resource-header-view .resource-header { -fx-padding: 5px; -fx-font-weight: bold; -fx-alignment: center; } -.resources-view .resources-view-container .resource-separator { - -fx-pref-width: 5px; +.resources-view .header-box .resource-header-view .resource-header, +.resources-view .header-box .resource-header-view .all-day-view, +.resources-view .header-box .resource-header-view .weekday-header-view { + -fx-border-color: lightgrey; +} + +.resources-view .header-box .resource-header-view.first .resource-header { + -fx-border-width: 1px 0px 1px 1px; +} + +.resources-view .header-box .resource-header-view.first .weekday-header-view { + -fx-border-width: 0px 0px 0px 1px; +} + +.resources-view .header-box .resource-header-view.first .all-day-view { + -fx-border-width: 0px 0px 1px 1px; +} + +.resources-view .header-box .resource-header-view.last .resource-header { + -fx-border-width: 1px 1px 1px 0px; +} + +.resources-view .header-box .resource-header-view.last .all-day-view { + -fx-border-width: 0px 1px 1px 0px; +} + +.resources-view .header-box .resource-header-view.last .weekday-header-view { + -fx-border-width: 0px 1px 0px 0px; +} + +.resources-view .header-box .resource-header-view.only .resource-header, +.resources-view .header-box .resource-header-view.middle .resource-header { + -fx-border-width: 1px; +} + +.resources-view .header-box .resource-header-view.only .weekday-header-view, +.resources-view .header-box .resource-header-view.middle .weekday-header-view { + -fx-border-width: 0px 1px 0px 1px; +} + +.resources-view .header-box .resource-header-view.only .all-day-view, +.resources-view .header-box .resource-header-view.middle .all-day-view { + -fx-border-width: 0px 1px 1px 1px; +} + +.resources-view .resources-view-container .week-view { + -fx-border-color: lightgrey; +} + +.resources-view .resources-view-container .week-view.first { + -fx-border-width: 0px 0px 1px 1px; +} + +.resources-view .resources-view-container .week-view.only { + -fx-border-width: 1px; +} + +.resources-view .resources-view-container .week-view.middle { + -fx-border-width: 0px 0px 1px 0px; +} + +.resources-view .resources-view-container .week-view.last { + -fx-border-width: 0px 1px 1px 0px; +} + +.resources-view .upper-left-corner { + -fx-border-color: lightgrey; + -fx-border-width: 1px 0px 1px 1px; +} + +.resources-view .upper-right-corner { + -fx-border-color: lightgrey; + -fx-border-width: 1px 1px 1px 0px; +} + +.resources-view .time-scale { + -fx-border-color: lightgrey; + -fx-border-width: 0px 0px 1px 1px; +} + +.resources-view .weekday-separator { + -size: 1px; +} + +.resources-view .resource-separator { + -size: 4px; + -fx-pref-width: -size; + -fx-min-width: -size; + -fx-max-width: -size; -fx-background-color: lightgrey; } \ No newline at end of file