Skip to content

Commit

Permalink
Merge pull request #158 from dlsc-software-consulting-gmbh/enhance-in…
Browse files Browse the repository at this point in the history
…focenter-view-fixes

Implement i18n, optimize property initialization, and fix focus loss in InfoCenterView
  • Loading branch information
dlemmermann authored Apr 17, 2024
2 parents 9c5f3c6 + 6317767 commit 5b17b4c
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
*/
public class InfoCenterPane extends Control {

private static final Duration DEFAULT_SLIDE_IN_DURATION = Duration.millis(200);

/*
* The managed info center instance.
*/
Expand Down Expand Up @@ -161,7 +163,7 @@ public void handle(long now) {
}
};

TreeShowing.treeShowing(this).addListener((p,o,n) -> {
TreeShowing.treeShowing(this).addListener((p, o, n) -> {
if (n) {
timer.start();
} else {
Expand Down Expand Up @@ -231,9 +233,8 @@ public final boolean isAutoHide() {
* A flag that determines if the info center view should automatically disappear again
* after a certain timeout duration.
*
* @see #autoHideDuration
*
* @return true if the info center hides automatically after a certain period of time
* @see #autoHideDuration
*/
public final BooleanProperty autoHideProperty() {
return autoHide;
Expand Down Expand Up @@ -269,24 +270,27 @@ public final void setContent(Node content) {

// slide in / slide out duration

private final ObjectProperty<Duration> slideInDuration = new SimpleObjectProperty<>(this, "slideDuration", Duration.millis(200)); //$NON-NLS-1$
private ObjectProperty<Duration> slideInDuration; //$NON-NLS-1$

/**
* The duration used for the "slide in" / "slide out" animation when the info center view
* gets shown or hidden.
*
* @return animation duration for the sliding in and out of the info center view
*/
public final ObjectProperty<Duration> slideInDuration() {
public final ObjectProperty<Duration> slideInDurationProperty() {
if (slideInDuration == null) {
slideInDuration = new SimpleObjectProperty<>(this, "slideInDuration", DEFAULT_SLIDE_IN_DURATION);
}
return slideInDuration;
}

public final Duration getSlideInDuration() {
return slideInDuration.get();
return slideInDuration == null ? DEFAULT_SLIDE_IN_DURATION : slideInDuration.get();
}

public final void setSlideInDuration(Duration duration) {
slideInDuration.set(duration);
slideInDurationProperty().set(duration);
}

private final BooleanProperty showInfoCenter = new SimpleBooleanProperty(this, "showInfoCenter", false);
Expand Down
129 changes: 113 additions & 16 deletions gemsfx/src/main/java/com/dlsc/gemsfx/infocenter/InfoCenterView.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.css.StyleableDoubleProperty;
import javafx.css.StyleableProperty;
import javafx.css.converter.SizeConverter;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.util.Duration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
Expand All @@ -31,6 +38,11 @@
public class InfoCenterView extends Control {

private static final String TRANSPARENT = "transparent";
private static final double DEFAULT_NOTIFICATION_SPACING = 10.0;
private static final Duration DEFAULT_EXPAND_DURATION = Duration.millis(300);
private static final Duration DEFAULT_SLIDE_IN_DURATION = Duration.millis(500);
private static final boolean DEFAULT_AUTO_OPEN_GROUP = false;
private static final boolean DEFAULT_TRANSPARENT = false;

private final InvalidationListener updateNotificationsListener = it -> updateNotificationsList();

Expand Down Expand Up @@ -70,8 +82,6 @@ public InfoCenterView() {
getGroups().addListener(groupListListener);

setFocusTraversable(false);

transparentProperty().addListener(it -> updateStyle());
}

@Override
Expand Down Expand Up @@ -171,8 +181,6 @@ private void updatePinnedAndUnpinnedGroupsList() {
* notifications of that group. Applications can choose to display the notifications in
* a different place, e.g. in a dialog.
*
* @see NotificationGroup#maximumNumberOfNotificationsProperty()
*
* @return the callback that gets invoked when requesting to see all notifications of a group
*/
public final ObjectProperty<Consumer<NotificationGroup<?, ?>>> onShowAllGroupNotificationsProperty() {
Expand All @@ -183,10 +191,10 @@ public final void setOnShowAllGroupNotifications(Consumer<NotificationGroup<?, ?
this.onShowAllGroupNotifications.set(onShowAllGroupNotifications);
}

private final BooleanProperty autoOpenGroup = new SimpleBooleanProperty(this, "autoOpenGroup", false);
private BooleanProperty autoOpenGroup;

public final boolean isAutoOpenGroup() {
return autoOpenGroup.get();
return autoOpenGroup == null ? DEFAULT_AUTO_OPEN_GROUP : autoOpenGroup.get();
}

/**
Expand All @@ -196,11 +204,14 @@ public final boolean isAutoOpenGroup() {
* @return true if the group of a newly added notification should be automatically expanded
*/
public final BooleanProperty autoOpenGroupProperty() {
if (autoOpenGroup == null) {
autoOpenGroup = new SimpleBooleanProperty(this, "autoOpenGroup", DEFAULT_AUTO_OPEN_GROUP);
}
return autoOpenGroup;
}

public final void setAutoOpenGroup(boolean autoOpenGroup) {
this.autoOpenGroup.set(autoOpenGroup);
autoOpenGroupProperty().set(autoOpenGroup);
}

private final ObservableList<Notification<?>> notifications = FXCollections.observableArrayList();
Expand Down Expand Up @@ -281,10 +292,10 @@ public final void setShowAllFadeDuration(Duration showAllFadeDuration) {
this.showAllFadeDuration.set(showAllFadeDuration);
}

private final ObjectProperty<Duration> expandDuration = new SimpleObjectProperty<>(this, "expandDuration", Duration.millis(300));
private ObjectProperty<Duration> expandDuration;

public final Duration getExpandDuration() {
return expandDuration.get();
return expandDuration == null ? DEFAULT_EXPAND_DURATION : expandDuration.get();
}

/**
Expand All @@ -294,17 +305,20 @@ public final Duration getExpandDuration() {
* @return the expand / collapse animation duration
*/
public final ObjectProperty<Duration> expandDurationProperty() {
if (expandDuration == null) {
expandDuration = new SimpleObjectProperty<>(this, "expandDuration", DEFAULT_EXPAND_DURATION);
}
return expandDuration;
}

public final void setExpandDuration(Duration expandDuration) {
this.expandDuration.set(expandDuration);
expandDurationProperty().set(expandDuration);
}

private final ObjectProperty<Duration> slideInDuration = new SimpleObjectProperty<>(this, "slideInDuration", Duration.millis(500));
private ObjectProperty<Duration> slideInDuration;

public final Duration getSlideInDuration() {
return slideInDuration.get();
return slideInDuration == null ? DEFAULT_SLIDE_IN_DURATION : slideInDuration.get();
}

/**
Expand All @@ -313,17 +327,20 @@ public final Duration getSlideInDuration() {
* @return the slide-in duration of a new notification
*/
public final ObjectProperty<Duration> slideInDurationProperty() {
if (slideInDuration == null) {
slideInDuration = new SimpleObjectProperty<>(this, "slideInDuration", DEFAULT_SLIDE_IN_DURATION);
}
return slideInDuration;
}

public final void setSlideInDuration(Duration slideInDuration) {
this.slideInDuration.set(slideInDuration);
slideInDurationProperty().set(slideInDuration);
}

private final BooleanProperty transparent = new SimpleBooleanProperty(this, "transparent", false);
private BooleanProperty transparent;

public final boolean isTransparent() {
return transparent.get();
return transparent == null ? DEFAULT_TRANSPARENT : transparent.get();
}

/**
Expand All @@ -333,11 +350,91 @@ public final boolean isTransparent() {
* @return true if the control is not transparent
*/
public final BooleanProperty transparentProperty() {
if (transparent == null) {
transparent = new SimpleBooleanProperty(this, "transparent", DEFAULT_TRANSPARENT) {
@Override
protected void invalidated() {
updateStyle();
}
};
}
return transparent;
}

public final void setTransparent(boolean transparent) {
this.transparent.set(transparent);
transparentProperty().set(transparent);
}

private DoubleProperty notificationSpacing;

/**
* Represents the spacing between individual notification views within the same notification group.
* This property allows adjustment of the vertical gap depending on the layout of the notification container.
* The default value is 10.0.
*
* @return the DoubleProperty that controls the spacing between notifications in the same group.
*/
public final DoubleProperty notificationSpacingProperty() {
if (notificationSpacing == null) {
notificationSpacing = new StyleableDoubleProperty(DEFAULT_NOTIFICATION_SPACING) {
@Override
public Object getBean() {
return InfoCenterView.this;
}

@Override
public String getName() {
return "notificationSpacing";
}

@Override
public CssMetaData<? extends Styleable, Number> getCssMetaData() {
return StyleableProperties.NOTIFICATION_SPACING;
}
};
}
return notificationSpacing;
}

public final double getNotificationSpacing() {
return notificationSpacing == null ? DEFAULT_NOTIFICATION_SPACING : notificationSpacing.get();
}

public final void setNotificationSpacing(double notificationSpacing) {
notificationSpacingProperty().set(notificationSpacing);
}

private static class StyleableProperties {

private static final CssMetaData<InfoCenterView, Number> NOTIFICATION_SPACING =
new CssMetaData<>("-fx-notification-spacing", SizeConverter.getInstance(), DEFAULT_NOTIFICATION_SPACING) {
@Override
public boolean isSettable(InfoCenterView view) {
return view.notificationSpacing == null || !view.notificationSpacing.isBound();
}

@Override
public StyleableProperty<Number> getStyleableProperty(InfoCenterView view) {
return (StyleableProperty<Number>) view.notificationSpacingProperty();
}
};

private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;

static {
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData());
styleables.add(NOTIFICATION_SPACING);
STYLEABLES = Collections.unmodifiableList(styleables);
}
}

@Override
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
return getClassCssMetaData();
}

public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return InfoCenterView.StyleableProperties.STYLEABLES;
}

public void clearAll() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.dlsc.gemsfx.infocenter;

import com.dlsc.gemsfx.infocenter.Notification.OnClickBehaviour;
import com.dlsc.gemsfx.util.ResourceBundleManager;
import javafx.animation.FadeTransition;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
Expand Down Expand Up @@ -45,6 +46,8 @@
public class NotificationView<T, S extends Notification<T>> extends StackPane {

private static final PseudoClass PSEUDO_CLASS_EXPANDED = PseudoClass.getPseudoClass("expanded");
private static final DateTimeFormatter SHORT_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);

private final S notification;
private final ContentPane contentPane;
private final StackPane stackNotification1;
Expand Down Expand Up @@ -211,23 +214,22 @@ public final void setShowContent(boolean showContent) {
private static final StringConverter<ZonedDateTime> DEFAULT_TIME_CONVERTER = new StringConverter<>() {
@Override
public String toString(ZonedDateTime dateTime) {
System.out.println(">> 0");
if (dateTime != null) {
Duration between = Duration.between(dateTime, ZonedDateTime.now());
if (between.toDays() == 0) {
if (between.toHours() > 2) {
return DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(dateTime.toLocalTime());
} else if (between.toHours() > 0) {
return MessageFormat.format("{0}h ago", between.toHours());
return MessageFormat.format("{0}{1}", between.toHours(), ResourceBundleManager.getString(ResourceBundleManager.Type.NOTIFICATION_VIEW, "time.hours.ago"));
} else if (between.toMinutes() > 0) {
return MessageFormat.format("{0}m ago", between.toMinutes());
return MessageFormat.format("{0}{1}", between.toMinutes(), ResourceBundleManager.getString(ResourceBundleManager.Type.NOTIFICATION_VIEW, "time.minutes.ago"));
} else {
return "now";
return ResourceBundleManager.getString(ResourceBundleManager.Type.NOTIFICATION_VIEW, "time.now");
}
} else if (between.toDays() == 1) {
return MessageFormat.format("Yesterday, {0}", DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(dateTime.toLocalTime()));
return MessageFormat.format("{0}, {1}", ResourceBundleManager.getString(ResourceBundleManager.Type.NOTIFICATION_VIEW,"time.yesterday"), SHORT_TIME_FORMATTER.format(dateTime.toLocalTime()));
} else if (between.toDays() < 7) {
return MessageFormat.format("{0} days ago", between.toDays());
return MessageFormat.format("{0} {1}", between.toDays(), ResourceBundleManager.getString(ResourceBundleManager.Type.NOTIFICATION_VIEW, "time.days.ago"));
} else {
return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).format(dateTime);
}
Expand Down Expand Up @@ -346,7 +348,7 @@ public ContentPane() {
contentProperty().addListener(it -> updateCenterNode(center));
showContentProperty().addListener(it -> updateCenterNode(center));

Label clearAllLabel = new Label("Clear All");
Label clearAllLabel = new Label(ResourceBundleManager.getString(ResourceBundleManager.Type.NOTIFICATION_VIEW, "group.clear.all"));
clearAllLabel.getStyleClass().add("clear-all");
clearAllLabel.setMouseTransparent(true);

Expand Down
Loading

0 comments on commit 5b17b4c

Please sign in to comment.