Skip to content

Commit

Permalink
More drawer greatness.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlemmermann committed Aug 29, 2024
1 parent 207eb6d commit 3875409
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,9 @@
{
"name":"java.util.Date"
},
{
"name":"javafx.animation.KeyValue"
},
{
"name":"javafx.scene.Camera"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.dlsc.jfxcentral2.components.CustomToggleButton;
import com.dlsc.jfxcentral2.components.SizeSupport;
import com.dlsc.jfxcentral2.events.MobileLinkEvent;
import com.dlsc.jfxcentral2.mobile.pages.MainPage;
import com.dlsc.jfxcentral2.mobile.pages.MobileHomePage;
import com.dlsc.jfxcentral2.model.Size;
import com.dlsc.jfxcentral2.utils.EventBusUtil;
Expand All @@ -28,7 +29,7 @@ public class BottomMenuBar extends HBox {
private final SizeSupport sizeSupport = new SizeSupport(this);
private final ToggleGroup toggleGroup;

public BottomMenuBar() {
public BottomMenuBar(Runnable closeDrawerCallback) {
getStyleClass().add("bottom-menu-bar");
EventBusUtil.register(this);

Expand All @@ -39,6 +40,7 @@ public BottomMenuBar() {
homeButton.setMaxWidth(Double.MAX_VALUE);
homeButton.setUserData(PagePath.HOME);
homeButton.setOnMousePressed(evt -> {
closeDrawerCallback.run();
if (homeButton.isSelected()) {
// If the home page is being displayed, clicking homeButton will hide the search view and display the normal content.
MobileHomePage.getInstance().setContentType(MobileHomePage.ContentType.NORMAL);
Expand All @@ -54,6 +56,7 @@ public BottomMenuBar() {
linksWeekButton.setGraphic(new FontIcon(IkonUtil.getModelIkon(LinksOfTheWeek.class)));
linksWeekButton.setMaxWidth(Double.MAX_VALUE);
linksWeekButton.setUserData(PagePath.LINKS);
linksWeekButton.setOnMouseClicked(evt -> closeDrawerCallback.run());
MobileLinkUtil.setLink(linksWeekButton, PagePath.LINKS);
HBox.setHgrow(linksWeekButton, Priority.ALWAYS);

Expand All @@ -63,6 +66,7 @@ public BottomMenuBar() {
showcasesButton.setGraphic(new FontIcon(IkonUtil.getModelIkon(RealWorldApp.class)));
showcasesButton.setMaxWidth(Double.MAX_VALUE);
showcasesButton.setUserData(PagePath.SHOWCASES);
showcasesButton.setOnMouseClicked(evt -> closeDrawerCallback.run());
MobileLinkUtil.setLink(showcasesButton, PagePath.SHOWCASES);
HBox.setHgrow(showcasesButton, Priority.ALWAYS);

Expand All @@ -72,6 +76,7 @@ public BottomMenuBar() {
libraryButton.setGraphic(new FontIcon(IkonUtil.getModelIkon(Library.class)));
libraryButton.setMaxWidth(Double.MAX_VALUE);
libraryButton.setUserData(PagePath.LIBRARIES);
libraryButton.setOnMouseClicked(evt -> closeDrawerCallback.run());
MobileLinkUtil.setLink(libraryButton, PagePath.LIBRARIES);
HBox.setHgrow(libraryButton, Priority.ALWAYS);

Expand All @@ -81,6 +86,7 @@ public BottomMenuBar() {
peopleButton.setGraphic(new FontIcon(IkonUtil.getModelIkon(Person.class)));
peopleButton.setMaxWidth(Double.MAX_VALUE);
peopleButton.setUserData(PagePath.PEOPLE);
peopleButton.setOnMouseClicked(evt -> closeDrawerCallback.run());
MobileLinkUtil.setLink(peopleButton, PagePath.PEOPLE);
HBox.setHgrow(peopleButton, Priority.ALWAYS);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
import com.dlsc.jfxcentral2.mobile.home.WeekLinksView;
import com.dlsc.jfxcentral2.utils.PagePath;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ToggleButton;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
Expand Down Expand Up @@ -87,10 +87,11 @@ private MobileHomePage() {
searchView.searchTextProperty().bindBidirectional(searchTextField.textProperty());

HBox.setHgrow(searchTextField, Priority.ALWAYS);

HBox searchWrapper = new HBox(searchTextField);
searchWrapper.getStyleClass().add("search-wrapper");

getChildren().addAll(header, searchWrapper, normalView, searchView);
getChildren().addAll(searchWrapper, normalView, searchView);

setViewWillAppear(()-> setContentType(ContentType.NORMAL));
}
Expand All @@ -104,6 +105,7 @@ private Button createSearchCancelButton() {
return "Search";
}
}, contentTypeProperty()));

button.setOnAction(event -> {
if (getContentType() == ContentType.SEARCH) {
searchTextField.clear();
Expand All @@ -112,6 +114,7 @@ private Button createSearchCancelButton() {
setContentType(ContentType.SEARCH);
}
});

return button;
}

Expand Down Expand Up @@ -163,38 +166,7 @@ private Node createNormalView() {
scrollPane.getStyleClass().add("mobile");
VBox.setVgrow(scrollPane, Priority.ALWAYS);

Node drawerContent = createDrawerContent();
StackPane.setAlignment(drawerContent, Pos.BOTTOM_CENTER);

return new StackPane(scrollPane, drawerContent);
}

private Node createDrawerContent() {
ToggleButton title = new ToggleButton("Content");
title.setMaxWidth(Double.MAX_VALUE);
title.getStyleClass().add("title");

final int HEIGHT = 200;

CategoriesPane content = new CategoriesPane();
content.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
content.setAlignment(Pos.CENTER);
content.setPrefHeight(HEIGHT);
VBox.setVgrow(content, Priority.ALWAYS);

VBox drawer = new VBox(title, content);
drawer.getStyleClass().add("drawer");
drawer.setMaxHeight(Region.USE_PREF_SIZE);

drawer.translateYProperty().bind(Bindings.createDoubleBinding(() -> title.isSelected() ? 0d: HEIGHT, title.selectedProperty()));

Rectangle clip = new Rectangle();
clip.widthProperty().bind(drawer.widthProperty());
clip.heightProperty().bind(drawer.heightProperty().add(50));
clip.setLayoutY(-50);
drawer.setClip(clip);

return drawer;
return scrollPane;
}

private <T extends ModelObject> List<T> getRandomSample(List<T> list, int sampleSize) {
Expand Down Expand Up @@ -222,5 +194,4 @@ public final ObjectProperty<ContentType> contentTypeProperty() {
public final void setContentType(ContentType contentType) {
this.contentType.set(contentType);
}

}
140 changes: 127 additions & 13 deletions mobile/src/main/java/com/dlsc/jfxcentral2/mobile/skin/MainPageSkin.java
Original file line number Diff line number Diff line change
@@ -1,52 +1,166 @@
package com.dlsc.jfxcentral2.mobile.skin;

import com.dlsc.gemsfx.GlassPane;
import com.dlsc.jfxcentral2.components.MobilePageBase;
import com.dlsc.jfxcentral2.events.MobileResponseEvent;
import com.dlsc.jfxcentral2.events.RepositoryUpdatedEvent;
import com.dlsc.jfxcentral2.mobile.components.BottomMenuBar;
import com.dlsc.jfxcentral2.mobile.pages.CategoriesPane;
import com.dlsc.jfxcentral2.mobile.pages.MainPage;
import com.dlsc.jfxcentral2.mobile.utils.PreferredFocusedNodeProvider;
import com.dlsc.jfxcentral2.utils.EventBusUtil;
import com.dlsc.jfxcentral2.utils.Subscribe;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.BorderPane;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;

import java.util.Optional;
import java.util.function.Function;

public class MainPageSkin extends SkinBase<MainPage> {

private final BorderPane borderPane = new BorderPane();
private final BottomMenuBar bottomMenuBar = new BottomMenuBar();
private final StackPane centerPane = new StackPane();
private final BottomMenuBar bottomMenuBar;
private final StackPane contentPane = new StackPane();
private final GlassPane glassPane = new GlassPane();
private final Node drawer;
private StackPane drawerHandle;

private final DoubleProperty drawerHeightPercentage = new SimpleDoubleProperty();
private CategoriesPane content;
private Timeline timeline;

public MainPageSkin(MainPage control) {
super(control);

EventBusUtil.register(this);

bottomMenuBar.managedProperty().bind(bottomMenuBar.visibleProperty());
bottomMenuBar = new BottomMenuBar(() -> hideDrawer());
bottomMenuBar.setVisible(false);

centerPane.getStyleClass().add("content-pane");
drawer = createDrawerContent();

contentPane.getStyleClass().add("content-pane");

contentPane.setManaged(false);
bottomMenuBar.setManaged(false);
glassPane.setManaged(false);
drawer.setManaged(false);

glassPane.setOnMouseClicked(evt -> hideDrawer());
glassPane.hideProperty().bind(drawerHeightPercentage.isEqualTo(0));

getChildren().setAll(contentPane, bottomMenuBar, glassPane, drawer);

drawerHeightPercentage.addListener(it -> getSkinnable().requestLayout());
}

@Override
protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
double barHeight = bottomMenuBar.prefHeight(contentWidth);
bottomMenuBar.resizeRelocate(contentX, contentY + contentHeight - barHeight, contentWidth, barHeight);
double drawerHeight = drawerHeightPercentage.get() * content.prefHeight(-1) + drawerHandle.prefHeight(-1);
drawer.resizeRelocate(contentX, contentY + contentHeight - barHeight - drawerHeight, contentWidth, drawerHeight);
contentPane.resizeRelocate(contentX, contentY, contentWidth, contentHeight - barHeight - drawerHeight);
glassPane.resizeRelocate(contentX, contentY, contentWidth, contentHeight - barHeight - drawerHeight);
}

private Node createDrawerContent() {
Region region = new Region();
region.getStyleClass().add("region");
region.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);

StackPane.setAlignment(region, Pos.CENTER);

drawerHandle = new StackPane(region);
drawerHandle.setMaxWidth(Double.MAX_VALUE);
drawerHandle.setMinHeight(Region.USE_PREF_SIZE);
drawerHandle.getStyleClass().add("handle");
drawerHandle.setOnMouseClicked(evt -> {
if (drawerHeightPercentage.get() > 0) {
hideDrawer();
} else {
showDrawer();
}
});

content = new CategoriesPane();
content.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
content.setAlignment(Pos.CENTER);
content.setMouseTransparent(false);
VBox.setVgrow(content, Priority.ALWAYS);

installCloseDrawerHandler(content);

VBox drawer = new VBox(drawerHandle, content);
drawer.getStyleClass().add("drawer");
drawer.setMaxHeight(Region.USE_PREF_SIZE);

borderPane.setCenter(centerPane);
borderPane.setBottom(bottomMenuBar);
getChildren().add(borderPane);
Rectangle clip = new Rectangle();
clip.widthProperty().bind(drawer.widthProperty());
clip.heightProperty().bind(drawer.heightProperty().add(50));
clip.setLayoutY(-50);
drawer.setClip(clip);

return drawer;
}

private void installCloseDrawerHandler(Node node) {
node.addEventFilter(MouseEvent.MOUSE_CLICKED, evt -> {
if (evt.getButton().equals(MouseButton.PRIMARY) && evt.isStillSincePress()) {
hideDrawer();
}
});
}

private void hideDrawer() {
animateDrawer(0);
}

private void showDrawer() {
animateDrawer(1);
}

private void animateDrawer(double value) {
if (timeline != null && timeline.getStatus().equals(Animation.Status.RUNNING)) {
timeline.stop();
}

KeyValue keyValue = new KeyValue(drawerHeightPercentage, value);
KeyFrame keyFrame = new KeyFrame(Duration.millis(100), keyValue);
timeline = new Timeline(keyFrame);
timeline.play();
}

@Subscribe
public void onMobileResponseEvent(MobileResponseEvent event) {
Node oldView = null;

// Old view will disappear
Node oldView = borderPane.getCenter();
invokeLifecycleMethod(oldView, MobilePageBase::getViewWillDisappear);
if (!contentPane.getChildren().isEmpty()) {
oldView = contentPane.getChildren().get(0);
invokeLifecycleMethod(oldView, MobilePageBase::getViewWillDisappear);
}

// New view will appear
Node newView = event.mobileResponse().getView();
invokeLifecycleMethod(newView, MobilePageBase::getViewWillAppear);

centerPane.getChildren().setAll(newView);
contentPane.getChildren().setAll(newView);

// Old view did disappear
invokeLifecycleMethod(oldView, MobilePageBase::getViewDidDisappear);
Expand All @@ -62,7 +176,7 @@ public void onMobileResponseEvent(MobileResponseEvent event) {
}
}
}

private void invokeLifecycleMethod(Node view, Function<MobilePageBase, Runnable> eventFunction) {
if (view instanceof MobilePageBase mobilePage) {
Optional.ofNullable(eventFunction.apply(mobilePage)).ifPresent(Runnable::run);
Expand Down
29 changes: 17 additions & 12 deletions mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/mobile.css
Original file line number Diff line number Diff line change
Expand Up @@ -553,34 +553,39 @@
-fx-alignment: center-left;
}

.mobile-home-page .drawer {
/** ----------------------------------
* MainPage
*/
.main-page .drawer {
-fx-background-color: -background;
-fx-background-radius: 32px 32px 0px 0px;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.25), 30, 0.2, 0, 0); /* 20% alpha */;
}

.mobile-home-page .drawer > .title {
-fx-background-radius: 32px 32px 0px 0px;
.main-page .drawer > .handle {
-fx-background-color: -background;
-fx-padding: 10px;
-fx-font-size: 1.2em;
-fx-font-family: "Roboto Condensed";
-fx-font-weight: bold;
-fx-padding: 6px 0px;
}

.mobile-home-page .drawer > .categories-pane {
.main-page .drawer > .handle > .region {
-fx-pref-width: 50px;
-fx-pref-height: 4px;
-fx-background-radius: 8px;
-fx-background-color: -grey-30;
}

.main-page .drawer > .categories-pane {
-fx-font-size: 12px;
}

.mobile-home-page .drawer > .categories-pane > .button {
.main-page .drawer > .categories-pane > .button {
-fx-content-display: top;
-fx-background-color: transparent;
-fx-text-fill: -grey-60;
-fx-padding: 0 0 5px 0;
-fx-padding: 10px 0px;
-fx-graphic-text-gap: 0px;
}

.mobile-home-page .drawer > .categories-pane > .button .ikonli-font-icon {
.main-page .drawer > .categories-pane > .button .ikonli-font-icon {
-fx-icon-size: 24px;
-fx-icon-color: -grey-60;
}
Expand Down

0 comments on commit 3875409

Please sign in to comment.