diff --git a/.launchers/Install on iOS Device.run.xml b/.launchers/Install on iOS Device.run.xml
new file mode 100644
index 00000000..8c344a5e
--- /dev/null
+++ b/.launchers/Install on iOS Device.run.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.launchers/JFXCentral2MobileApp (develop).run.xml b/.launchers/JFXCentral2MobileApp (develop).run.xml
index 77e6416a..ecc11bd4 100644
--- a/.launchers/JFXCentral2MobileApp (develop).run.xml
+++ b/.launchers/JFXCentral2MobileApp (develop).run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.launchers/JFXCentral2MobileApp.run.xml b/.launchers/JFXCentral2MobileApp.run.xml
index 3986b032..1275ac12 100644
--- a/.launchers/JFXCentral2MobileApp.run.xml
+++ b/.launchers/JFXCentral2MobileApp.run.xml
@@ -2,6 +2,7 @@
+
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 47a05e63..41b2ebca 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -1,18 +1,2 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.8/apache-maven-3.8.8-bin.zip
-wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
\ No newline at end of file
diff --git a/app/src/main/java/com/dlsc/jfxcentral2/app/JFXCentral2MobileApp.java b/app/src/main/java/com/dlsc/jfxcentral2/app/JFXCentral2MobileApp.java
index 16316b99..710c8374 100644
--- a/app/src/main/java/com/dlsc/jfxcentral2/app/JFXCentral2MobileApp.java
+++ b/app/src/main/java/com/dlsc/jfxcentral2/app/JFXCentral2MobileApp.java
@@ -18,7 +18,6 @@
import com.dlsc.jfxcentral2.app.pages.MobileRefreshPage;
import com.dlsc.jfxcentral2.events.OpenWebLinkEvent;
import com.dlsc.jfxcentral2.events.RepositoryUpdatedEvent;
-import com.dlsc.jfxcentral2.mobile.components.BottomMenuBar;
import com.dlsc.jfxcentral2.mobile.components.MobileDevelopToolBar;
import com.dlsc.jfxcentral2.mobile.pages.MainPage;
import com.dlsc.jfxcentral2.mobile.pages.MobileHomePage;
@@ -43,7 +42,7 @@
import com.dlsc.jfxcentral2.mobile.pages.details.MobileLearnDetailsPage;
import com.dlsc.jfxcentral2.mobile.pages.details.MobileLibraryDetailsPage;
import com.dlsc.jfxcentral2.mobile.pages.details.MobilePersonDetailsPage;
-import com.dlsc.jfxcentral2.mobile.pages.details.MobileShowcaseMobileDetailsPage;
+import com.dlsc.jfxcentral2.mobile.pages.details.MobileShowcaseDetailsPage;
import com.dlsc.jfxcentral2.mobile.pages.details.MobileTipDetailsPage;
import com.dlsc.jfxcentral2.mobile.pages.details.MobileToolDetailsPage;
import com.dlsc.jfxcentral2.mobile.pages.details.MobileTutorialDetailsPage;
@@ -135,7 +134,7 @@ public void start(Stage stage) {
scene.setFill(Color.web("#070B32"));
scene.widthProperty().addListener((it -> updateSizeProperty(scene)));
scene.getStylesheets().add(Objects.requireNonNull(NodeUtil.class.getResource("/com/dlsc/jfxcentral2/theme.css")).toExternalForm());
- scene.getStylesheets().add(Objects.requireNonNull(BottomMenuBar.class.getResource("/com/dlsc/jfxcentral2/mobile/mobile.css")).toExternalForm());
+ scene.getStylesheets().add(Objects.requireNonNull(MainPage.class.getResource("/com/dlsc/jfxcentral2/mobile/mobile.css")).toExternalForm());
// scene.focusOwnerProperty().addListener(it -> System.out.println("focus owner: " + scene.getFocusOwner()));
updateSizeProperty(scene);
@@ -200,8 +199,8 @@ private MobileRouter createMobileRouter() {
.and(MobileRoute.redirect("/home", PagePath.HOME))
.and(MobileRoute.get(PagePath.LINKS, r -> MobileResponse.view(r, new MobileLinksOfTheWeekPage(size))))
.and(MobileRoute.get(PagePath.DOCUMENTATION, r -> MobileResponse.view(r, new MobileDocPage(size))))
- .and(createCategoryOrDetailRoute(PagePath.SHOWCASES, RealWorldApp.class, () -> new MobileShowcasesCategoryPage(size), id -> new MobileShowcaseMobileDetailsPage(size, id)))
- .and(createCategoryOrDetailRoute(PagePath.REAL_WORLD, RealWorldApp.class, () -> new MobileShowcasesCategoryPage(size), id -> new MobileShowcaseMobileDetailsPage(size, id)))
+ .and(createCategoryOrDetailRoute(PagePath.SHOWCASES, RealWorldApp.class, () -> new MobileShowcasesCategoryPage(size), id -> new MobileShowcaseDetailsPage(size, id)))
+ .and(createCategoryOrDetailRoute(PagePath.REAL_WORLD, RealWorldApp.class, () -> new MobileShowcasesCategoryPage(size), id -> new MobileShowcaseDetailsPage(size, id)))
.and(createCategoryOrDetailRoute(PagePath.LIBRARIES, Library.class, () -> new MobileLibrariesCategoryPage(size), id -> new MobileLibraryDetailsPage(size, id)))
.and(createCategoryOrDetailRoute(PagePath.PEOPLE, Person.class, () -> new MobilePeopleCategoryPage(size), id -> new MobilePersonDetailsPage(size, id)))
.and(createCategoryOrDetailRoute(PagePath.BLOGS, Blog.class, () -> new MobileBlogsCategoryPage(size), id -> new MobileBlogDetailsPage(size, id)))
diff --git a/app/src/main/java/com/dlsc/jfxcentral2/app/pages/MobileRefreshPage.java b/app/src/main/java/com/dlsc/jfxcentral2/app/pages/MobileRefreshPage.java
index 4d7b6a49..eb30887b 100644
--- a/app/src/main/java/com/dlsc/jfxcentral2/app/pages/MobileRefreshPage.java
+++ b/app/src/main/java/com/dlsc/jfxcentral2/app/pages/MobileRefreshPage.java
@@ -15,6 +15,7 @@
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Priority;
+import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.TextAlignment;
@@ -35,6 +36,7 @@ public class MobileRefreshPage extends StackPane {
private final WeakInvalidationListener weakInvalidationListener = new WeakInvalidationListener(invalidationListener);
private final RepositoryUpdater repositoryUpdater = new RepositoryUpdater();
+ private final CustomImageView logo;
public MobileRefreshPage(ObjectProperty size) {
getStyleClass().add(DEFAULT_STYLE_CLASS);
@@ -44,10 +46,19 @@ public MobileRefreshPage(ObjectProperty size) {
invalidationListener.invalidated(null);
});
- // top part (logo)
- CustomImageView logo = new CustomImageView();
+ logo = new CustomImageView();
logo.getStyleClass().addAll("jfx-central-logo", "color");
+ boolean firstTimeSetup = RepositoryManager.isFirstTimeSetup();
+
+ if (firstTimeSetup) {
+ setupFirstTimeUI();
+ } else {
+ setupUpdateUI();
+ }
+ }
+
+ private void setupFirstTimeUI() {
// center part (intro pane)
IntroPane introPane = new IntroPane();
VBox.setVgrow(introPane, Priority.ALWAYS);
@@ -72,8 +83,8 @@ public MobileRefreshPage(ObjectProperty size) {
// bottom part
Button startButton = new Button("Get Started");
startButton.getStyleClass().add("start-button");
- startButton.setVisible(RepositoryManager.isFirstTimeSetup());
- startButton.setVisible(RepositoryManager.isFirstTimeSetup());
+ startButton.setVisible(true);
+ startButton.setVisible(true);
startButton.setOnAction(evt -> {
startButton.setVisible(false);
startButton.setManaged(false);
@@ -88,10 +99,22 @@ public MobileRefreshPage(ObjectProperty size) {
VBox content = new VBox(logo, introPane, bottomBox);
content.getStyleClass().add("content-box");
getChildren().add(content);
+ }
- if (!RepositoryManager.isFirstTimeSetup()) {
- repositoryUpdater.performUpdate(true);
- }
+ private void setupUpdateUI() {
+ Label tipsLabel = new Label("Checking for updates ...");
+
+ Region dividingLine = new Region();
+ dividingLine.getStyleClass().add("dividing-line");
+
+ VBox updateContentBox = new VBox(logo, dividingLine, tipsLabel);
+ updateContentBox.getStyleClass().add("update-content-box");
+ updateContentBox.setMaxHeight(Region.USE_PREF_SIZE);
+
+ getChildren().add(updateContentBox);
+
+ // start the update process
+ repositoryUpdater.performUpdate(true);
}
}
diff --git a/app/src/main/resources/META-INF/native-image/reflect-config.json b/app/src/main/resources/META-INF/native-image/reflect-config.json
index ea342a69..7fb3b753 100644
--- a/app/src/main/resources/META-INF/native-image/reflect-config.json
+++ b/app/src/main/resources/META-INF/native-image/reflect-config.json
@@ -651,6 +651,9 @@
{
"name":"java.util.Date"
},
+{
+ "name":"javafx.animation.KeyValue"
+},
{
"name":"javafx.scene.Camera"
},
diff --git a/components/src/main/java/com/dlsc/jfxcentral2/components/CustomMarkdownView.java b/components/src/main/java/com/dlsc/jfxcentral2/components/CustomMarkdownView.java
index 084c937b..2eed3976 100644
--- a/components/src/main/java/com/dlsc/jfxcentral2/components/CustomMarkdownView.java
+++ b/components/src/main/java/com/dlsc/jfxcentral2/components/CustomMarkdownView.java
@@ -1,6 +1,7 @@
package com.dlsc.jfxcentral2.components;
import com.dlsc.jfxcentral2.utils.ExternalLinkUtil;
+import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
@@ -8,13 +9,18 @@
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Node;
+import javafx.scene.Parent;
+import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
+import javafx.scene.web.WebView;
+import one.jpro.jproutils.treeshowing.TreeShowing;
import one.jpro.platform.mdfx.MarkdownView;
import one.jpro.platform.mdfx.extensions.ImageExtension;
import one.jpro.platform.mdfx.extensions.YoutubeExtension;
import org.apache.commons.lang3.StringUtils;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -32,6 +38,41 @@ public CustomMarkdownView() {
getStyleClass().add("custom-markdown-view");
getStylesheets().add(Objects.requireNonNull(CustomMarkdownView.class.getResource("markdown.css")).toExternalForm());
+
+ TreeShowing.treeShowing(this).addListener(it -> setupWorkAroundForWebViewLayout());
+ Platform.runLater(this::setupWorkAroundForWebViewLayout);
+ mdStringProperty().addListener(it -> Platform.runLater(this::setupWorkAroundForWebViewLayout));
+ }
+
+ private void setupWorkAroundForWebViewLayout() {
+ List webViews = new ArrayList<>();
+ getChildrenUnmodifiable().forEach(child -> collectWebViews(child, webViews));
+ webViews.forEach(view -> {
+ fixIt(view);
+ view.localToSceneTransformProperty().addListener((obs, oldV, newV) -> fixIt(view));
+ });
+ }
+
+ boolean fixing = false;
+
+ private void fixIt(WebView view) {
+ if (!fixing) {
+ fixing = true;
+ double width = view.getWidth();
+ double height = view.getHeight();
+ view.resize(width + 1, height + 1);
+ view.resize(width, height);
+ fixing = false;
+ }
+ }
+
+ private void collectWebViews(Node node, List webViews) {
+ if (node instanceof WebView) {
+ webViews.add((WebView) node);
+ } else if (node instanceof Parent) {
+ Parent parent = (Parent) node;
+ parent.getChildrenUnmodifiable().forEach(child -> collectWebViews(child, webViews));
+ }
}
public CustomMarkdownView(String mdString) {
diff --git a/components/src/main/java/com/dlsc/jfxcentral2/components/headers/LinksOfTheWeekHeader.java b/components/src/main/java/com/dlsc/jfxcentral2/components/headers/LinksOfTheWeekHeader.java
index 030b36d3..a1fe68ba 100644
--- a/components/src/main/java/com/dlsc/jfxcentral2/components/headers/LinksOfTheWeekHeader.java
+++ b/components/src/main/java/com/dlsc/jfxcentral2/components/headers/LinksOfTheWeekHeader.java
@@ -7,6 +7,9 @@
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.TextAlignment;
import one.jpro.platform.routing.LinkUtil;
import org.kordamp.ikonli.javafx.FontIcon;
import scala.Option;
@@ -36,14 +39,21 @@ public LinksOfTheWeekHeader() {
CustomImageView rssImageView = new CustomImageView();
rssImageView.setImage(RSS_IMAGE);
LinkUtil.setLinkInternalNoPush(rssImageView, "/lotw/rss.xml", Option.empty(), false);
- //rssImageView.setVisible(false);
- //rssImageView.setManaged(false);
HBox box = new HBox(label, rssImageView);
box.getStyleClass().add("rss-box");
box.setAlignment(Pos.CENTER);
- setContent(box);
+ Label descriptionLabel = new Label("Do you have JavaFX news you want to share with the community? Let us know!\nSend a mail to links@jfx-central.com");
+ descriptionLabel.getStyleClass().add("description-label");
+ descriptionLabel.setWrapText(true);
+ descriptionLabel.setMinHeight(Region.USE_PREF_SIZE);
+ descriptionLabel.setTextAlignment(TextAlignment.CENTER);
+
+ VBox vbox = new VBox(20, box, descriptionLabel);
+ vbox.setAlignment(Pos.CENTER);
+
+ setContent(vbox);
setTitle("Links of the Week");
setIkon(IkonUtil.getModelIkon(LinksOfTheWeek.class));
setBackgroundImage(BACKGROUND_IMAGE);
diff --git a/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/OverviewBox.java b/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/OverviewBox.java
index 75b573f6..d2d9f72c 100644
--- a/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/OverviewBox.java
+++ b/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/OverviewBox.java
@@ -5,6 +5,7 @@
import com.dlsc.jfxcentral2.components.CustomMarkdownView;
import com.dlsc.jfxcentral2.components.PaneBase;
import com.dlsc.jfxcentral2.model.NameProvider;
+import com.dlsc.jfxcentral2.utils.OSUtil;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
@@ -50,6 +51,9 @@ public OverviewBox(T model) {
VBox bodyBox = new VBox(topWrapper, markdownView, bottomWrapper);
bodyBox.getStyleClass().add("body-box");
+ if (OSUtil.isAndroidOrIOS()) {
+ bodyBox.getStyleClass().add("no-top-padding");
+ }
VBox contentBox = new VBox();
contentBox.getStyleClass().add("content-box");
diff --git a/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/ShowcaseOverviewBox.java b/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/ShowcaseOverviewBox.java
index c8d66ed2..1d470e3a 100644
--- a/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/ShowcaseOverviewBox.java
+++ b/components/src/main/java/com/dlsc/jfxcentral2/components/overviewbox/ShowcaseOverviewBox.java
@@ -2,6 +2,7 @@
import com.dlsc.jfxcentral.data.DataRepository2;
import com.dlsc.jfxcentral.data.model.RealWorldApp;
+import com.dlsc.jfxcentral2.utils.OSUtil;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
@@ -49,6 +50,7 @@ protected Node createTopNode() {
if (!isSmall()) {
GridPane gridPane = new GridPane();
+ gridPane.setVisible(!OSUtil.isAndroidOrIOS());
gridPane.getStyleClass().add("top-grid");
for (int i = 0; i < 4; i++) {
ColumnConstraints columnConstraints = new ColumnConstraints();
@@ -89,6 +91,7 @@ protected Node createTopNode() {
createdOnGroup.getHeader(),
createdOnLabel
);
+ topBox.setVisible(!OSUtil.isAndroidOrIOS());
createdOnLabel.getStyleClass().add("last");
topBox.getStyleClass().add("top-box");
return topBox;
diff --git a/components/src/main/java/module-info.java b/components/src/main/java/module-info.java
index dc92c03c..a2df9fba 100644
--- a/components/src/main/java/module-info.java
+++ b/components/src/main/java/module-info.java
@@ -98,6 +98,7 @@
requires org.kordamp.ikonli.zondicons;
requires com.rometools.rome;
requires java.prefs;
+ requires jpro.utils.treeshowing;
// ikonli icon packs END
exports com.dlsc.jfxcentral2.components;
diff --git a/components/src/main/resources/com/dlsc/jfxcentral2/theme.css b/components/src/main/resources/com/dlsc/jfxcentral2/theme.css
index 8cb636f4..870deb39 100644
--- a/components/src/main/resources/com/dlsc/jfxcentral2/theme.css
+++ b/components/src/main/resources/com/dlsc/jfxcentral2/theme.css
@@ -3273,6 +3273,11 @@
-fx-fit-height: 32px;
}
+.links-of-the-week-header .description-label {
+ -fx-padding: 20px;
+ -fx-text-fill: -white;
+}
+
/** ------------------------------------
* PersonDetailHeader
*/
@@ -6851,10 +6856,18 @@
-fx-padding: 20px 30px;
}
+.overview-box:md-lg .body-box.no-top-padding {
+ -fx-padding: 0px 30px 20px 30px;
+}
+
.overview-box:sm .body-box {
-fx-padding: 20px;
}
+.overview-box:sm .body-box.no-top-padding {
+ -fx-padding: 0px 20px 20px 20px;
+}
+
.overview-box > .content-box > .body-box {
-fx-spacing: 15px;
}
diff --git a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/BottomMenuBar.java b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/BottomMenuBar.java
deleted file mode 100644
index 64653dec..00000000
--- a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/BottomMenuBar.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package com.dlsc.jfxcentral2.mobile.components;
-
-import com.dlsc.jfxcentral.data.model.Library;
-import com.dlsc.jfxcentral.data.model.LinksOfTheWeek;
-import com.dlsc.jfxcentral.data.model.Person;
-import com.dlsc.jfxcentral.data.model.RealWorldApp;
-import com.dlsc.jfxcentral2.components.CustomToggleButton;
-import com.dlsc.jfxcentral2.components.SizeSupport;
-import com.dlsc.jfxcentral2.events.MobileLinkEvent;
-import com.dlsc.jfxcentral2.mobile.pages.MobileHomePage;
-import com.dlsc.jfxcentral2.model.Size;
-import com.dlsc.jfxcentral2.utils.EventBusUtil;
-import com.dlsc.jfxcentral2.utils.IkonUtil;
-import com.dlsc.jfxcentral2.utils.MobileLinkUtil;
-import com.dlsc.jfxcentral2.utils.PagePath;
-import com.dlsc.jfxcentral2.utils.Subscribe;
-import javafx.beans.property.ObjectProperty;
-import javafx.scene.control.ContentDisplay;
-import javafx.scene.control.ToggleGroup;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Priority;
-import javafx.scene.layout.Region;
-import org.kordamp.ikonli.javafx.FontIcon;
-import org.kordamp.ikonli.materialdesign.MaterialDesign;
-
-public class BottomMenuBar extends HBox {
-
- private final SizeSupport sizeSupport = new SizeSupport(this);
- private final ToggleGroup toggleGroup;
-
- public BottomMenuBar() {
- getStyleClass().add("bottom-menu-bar");
- EventBusUtil.register(this);
-
- CustomToggleButton homeButton = new CustomToggleButton();
- homeButton.setText("Home");
- homeButton.setGraphic(new FontIcon(MaterialDesign.MDI_HOME));
- homeButton.setMaxWidth(Double.MAX_VALUE);
- homeButton.setUserData(PagePath.HOME);
- homeButton.setOnMousePressed(evt -> {
- 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);
- } else {
- MobileLinkUtil.getToPage(PagePath.HOME);
- }
- });
- HBox.setHgrow(homeButton, Priority.ALWAYS);
-
- CustomToggleButton linksWeekButton = new CustomToggleButton();
- linksWeekButton.setText("News");
- linksWeekButton.setGraphic(new FontIcon(IkonUtil.getModelIkon(LinksOfTheWeek.class)));
- linksWeekButton.setMaxWidth(Double.MAX_VALUE);
- linksWeekButton.setUserData(PagePath.LINKS);
- MobileLinkUtil.setLink(linksWeekButton, PagePath.LINKS);
- HBox.setHgrow(linksWeekButton, Priority.ALWAYS);
-
- CustomToggleButton showcasesButton = new CustomToggleButton();
- showcasesButton.setText("Apps");
- showcasesButton.setGraphic(new FontIcon(IkonUtil.getModelIkon(RealWorldApp.class)));
- showcasesButton.setMaxWidth(Double.MAX_VALUE);
- showcasesButton.setUserData(PagePath.SHOWCASES);
- MobileLinkUtil.setLink(showcasesButton, PagePath.SHOWCASES);
- HBox.setHgrow(showcasesButton, Priority.ALWAYS);
-
- CustomToggleButton libraryButton = new CustomToggleButton();
- libraryButton.setText("Libs");
- libraryButton.setGraphic(new FontIcon(IkonUtil.getModelIkon(Library.class)));
- libraryButton.setMaxWidth(Double.MAX_VALUE);
- libraryButton.setUserData(PagePath.LIBRARIES);
- MobileLinkUtil.setLink(libraryButton, PagePath.LIBRARIES);
- HBox.setHgrow(libraryButton, Priority.ALWAYS);
-
- CustomToggleButton peopleButton = new CustomToggleButton();
- peopleButton.setText("People");
- peopleButton.setGraphic(new FontIcon(IkonUtil.getModelIkon(Person.class)));
- peopleButton.setMaxWidth(Double.MAX_VALUE);
- peopleButton.setUserData(PagePath.PEOPLE);
- MobileLinkUtil.setLink(peopleButton, PagePath.PEOPLE);
- HBox.setHgrow(peopleButton, Priority.ALWAYS);
-
- getChildren().addAll(homeButton, linksWeekButton, showcasesButton, libraryButton, peopleButton);
-
- toggleGroup = new ToggleGroup();
- toggleGroup.getToggles().addAll(homeButton, linksWeekButton, showcasesButton, libraryButton, peopleButton);
-
- setMaxHeight(Region.USE_PREF_SIZE);
-
- // select home button by default
- // homeButton.fire();
- }
-
- public final ObjectProperty sizeProperty() {
- return sizeSupport.sizeProperty();
- }
-
- public final void setSize(Size size) {
- sizeSupport.setSize(size);
- }
-
- public final Size getSize() {
- return sizeSupport.getSize();
- }
-
- @Subscribe
- public void handleNavigation(MobileLinkEvent linkEvent) {
- toggleGroup.getToggles().stream()
- .filter(toggle -> toggle.getUserData().equals(linkEvent.link()))
- .findFirst()
- .ifPresentOrElse(
- toggle -> toggle.setSelected(true),
- () -> toggleGroup.selectToggle(null)
- );
- }
-
-}
\ No newline at end of file
diff --git a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/CategorySelectionView.java b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/CategorySelectionView.java
new file mode 100644
index 00000000..6892b916
--- /dev/null
+++ b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/CategorySelectionView.java
@@ -0,0 +1,184 @@
+package com.dlsc.jfxcentral2.mobile.components;
+
+import com.dlsc.jfxcentral.data.model.Blog;
+import com.dlsc.jfxcentral.data.model.Book;
+import com.dlsc.jfxcentral.data.model.Company;
+import com.dlsc.jfxcentral.data.model.Documentation;
+import com.dlsc.jfxcentral.data.model.LearnJavaFX;
+import com.dlsc.jfxcentral.data.model.LearnMobile;
+import com.dlsc.jfxcentral.data.model.LearnRaspberryPi;
+import com.dlsc.jfxcentral.data.model.Library;
+import com.dlsc.jfxcentral.data.model.LinksOfTheWeek;
+import com.dlsc.jfxcentral.data.model.Person;
+import com.dlsc.jfxcentral.data.model.RealWorldApp;
+import com.dlsc.jfxcentral.data.model.Tip;
+import com.dlsc.jfxcentral.data.model.Tool;
+import com.dlsc.jfxcentral.data.model.Tutorial;
+import com.dlsc.jfxcentral.data.model.Video;
+import com.dlsc.jfxcentral2.components.CustomToggleButton;
+import com.dlsc.jfxcentral2.mobile.pages.MobileHomePage;
+import com.dlsc.jfxcentral2.utils.IkonUtil;
+import com.dlsc.jfxcentral2.utils.MobileLinkUtil;
+import com.dlsc.jfxcentral2.utils.PagePath;
+import javafx.geometry.HPos;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.geometry.VPos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.image.ImageView;
+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.layout.VBox;
+import org.kordamp.ikonli.Ikon;
+import org.kordamp.ikonli.javafx.FontIcon;
+import org.kordamp.ikonli.materialdesign.MaterialDesign;
+
+import java.util.Objects;
+
+public class CategorySelectionView extends GridPane {
+
+ private final ToggleGroup toggleGroup = new ToggleGroup();
+
+ public CategorySelectionView(Runnable closeDrawer) {
+ getStyleClass().add("categories-selection-view");
+
+ setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+
+ CustomToggleButton homeButton = createButton("Home", MaterialDesign.MDI_HOME, PagePath.HOME);
+ CustomToggleButton linksWeekButton = createButton("News", IkonUtil.getModelIkon(LinksOfTheWeek.class), PagePath.LINKS);
+ CustomToggleButton showcasesButton = createButton("Apps", IkonUtil.getModelIkon(RealWorldApp.class), PagePath.SHOWCASES);
+ CustomToggleButton libraryButton = createButton("Libs", IkonUtil.getModelIkon(Library.class), PagePath.LIBRARIES);
+ CustomToggleButton peopleButton = createButton("People", IkonUtil.getModelIkon(Person.class), PagePath.PEOPLE);
+ CustomToggleButton booksButton = createButton("Books", IkonUtil.getModelIkon(Book.class), PagePath.BOOKS);
+ CustomToggleButton blogsButton = createButton("Blogs", IkonUtil.getModelIkon(Blog.class), PagePath.BLOGS);
+ CustomToggleButton companiesButton = createButton("Companies", IkonUtil.getModelIkon(Company.class), PagePath.COMPANIES);
+ CustomToggleButton docsButton = createButton("Docs", IkonUtil.getModelIkon(Documentation.class), PagePath.DOCUMENTATION);
+ CustomToggleButton tutorialsButton = createButton("Tutorials", IkonUtil.getModelIkon(Tutorial.class), PagePath.TUTORIALS);
+ CustomToggleButton toolsButton = createButton("Tools", IkonUtil.getModelIkon(Tool.class), PagePath.TOOLS);
+ CustomToggleButton tipsButton = createButton("Tips", IkonUtil.getModelIkon(Tip.class), PagePath.TIPS);
+ CustomToggleButton videosButton = createButton("Videos", IkonUtil.getModelIkon(Video.class), PagePath.VIDEOS);
+ CustomToggleButton learnJavaFXButton = createButton("Learn JFX", IkonUtil.getModelIkon(LearnJavaFX.class), PagePath.LEARN_JAVAFX);
+ CustomToggleButton learnMobileButton = createButton("Learn Mobile", IkonUtil.getModelIkon(LearnMobile.class), PagePath.LEARN_MOBILE);
+ CustomToggleButton learnRaspberryPiButton = createButton("Learn Pi", IkonUtil.getModelIkon(LearnRaspberryPi.class), PagePath.LEARN_RASPBERRYPI);
+
+ Node senaptNode = createNode("Sponsored by", "Senapt Ltd.", "senapt.png");
+ Node gluonNode = createNode("Powered by", "Gluon", "gluon.png");
+
+ senaptNode.setOnMousePressed(evt -> MobileLinkUtil.openWebLink("https://senapt.co.uk"));
+ gluonNode.setOnMousePressed(evt -> MobileLinkUtil.openWebLink("https://gluonhq.com"));
+
+ HBox logoBox = new HBox(10, senaptNode, gluonNode);
+ logoBox.setMaxHeight(Region.USE_PREF_SIZE);
+ logoBox.getStyleClass().add("logo-box");
+ GridPane.setMargin(logoBox, new Insets(30, 0, 0, 0));
+
+ for (int i = 0; i < 5; i++) {
+ ColumnConstraints constraint = new ColumnConstraints();
+ constraint.setPercentWidth(20);
+ constraint.setHgrow(Priority.ALWAYS);
+ getColumnConstraints().add(constraint);
+ }
+
+ GridPane.setColumnSpan(logoBox, 4);
+ GridPane.setHalignment(logoBox, HPos.RIGHT);
+ GridPane.setValignment(logoBox, VPos.CENTER);
+
+ GridPane.setHgrow(videosButton, Priority.ALWAYS);
+ GridPane.setHgrow(toolsButton, Priority.ALWAYS);
+ GridPane.setHgrow(docsButton, Priority.ALWAYS);
+ GridPane.setHgrow(tipsButton, Priority.ALWAYS);
+ GridPane.setHgrow(tutorialsButton, Priority.ALWAYS);
+ GridPane.setHgrow(booksButton, Priority.ALWAYS);
+ GridPane.setHgrow(blogsButton, Priority.ALWAYS);
+ GridPane.setHgrow(companiesButton, Priority.ALWAYS);
+ GridPane.setHgrow(learnJavaFXButton, Priority.ALWAYS);
+ GridPane.setHgrow(learnMobileButton, Priority.ALWAYS);
+ GridPane.setHgrow(learnRaspberryPiButton, Priority.ALWAYS);
+
+ GridPane.setVgrow(videosButton, Priority.ALWAYS);
+ GridPane.setVgrow(toolsButton, Priority.ALWAYS);
+ GridPane.setVgrow(docsButton, Priority.ALWAYS);
+ GridPane.setVgrow(tipsButton, Priority.ALWAYS);
+ GridPane.setVgrow(tutorialsButton, Priority.ALWAYS);
+ GridPane.setVgrow(booksButton, Priority.ALWAYS);
+ GridPane.setVgrow(blogsButton, Priority.ALWAYS);
+ GridPane.setVgrow(companiesButton, Priority.ALWAYS);
+ GridPane.setVgrow(learnJavaFXButton, Priority.ALWAYS);
+ GridPane.setVgrow(learnMobileButton, Priority.ALWAYS);
+ GridPane.setVgrow(learnRaspberryPiButton, Priority.ALWAYS);
+
+ videosButton.setPrefWidth(0);
+ toolsButton.setPrefWidth(0);
+ docsButton.setPrefWidth(0);
+ tipsButton.setPrefWidth(0);
+ tutorialsButton.setPrefWidth(0);
+ booksButton.setPrefWidth(0);
+ blogsButton.setPrefWidth(0);
+ companiesButton.setPrefWidth(0);
+ learnJavaFXButton.setPrefWidth(0);
+ learnMobileButton.setPrefWidth(0);
+ learnRaspberryPiButton.setPrefWidth(0);
+
+ add(homeButton, 0, 0);
+ add(linksWeekButton, 1, 0);
+ add(showcasesButton, 2, 0);
+ add(libraryButton, 3, 0);
+ add(peopleButton, 4, 0);
+
+ add(videosButton, 0, 1);
+ add(toolsButton, 1, 1);
+ add(docsButton, 2, 1);
+ add(tipsButton, 3, 1);
+ add(tutorialsButton, 4, 1);
+
+ add(learnJavaFXButton, 0, 2);
+ add(learnRaspberryPiButton, 1, 2);
+ add(learnMobileButton, 2, 2);
+ add(booksButton, 3, 2);
+ add(blogsButton, 4, 2);
+
+ add(companiesButton, 0, 3);
+
+ add(logoBox, 1, 3);
+
+ toggleGroup.selectToggle(homeButton);
+ toggleGroup.selectedToggleProperty().addListener(it -> closeDrawer.run());
+ }
+
+ private Node createNode(String text1, String text2, String imageFileName) {
+ ImageView logo = new ImageView(Objects.requireNonNull(CategorySelectionView.class.getResource(imageFileName)).toExternalForm());
+ logo.setFitHeight(24);
+ logo.setPreserveRatio(true);
+
+ Label label1 = new Label(text1);
+ Label label2 = new Label(text2);
+
+ VBox vBox = new VBox(label1, label2);
+ vBox.getStyleClass().add("vbox");
+
+ HBox hBox = new HBox(logo, vBox);
+ hBox.getStyleClass().add("hbox");
+
+ return hBox;
+ }
+
+ private CustomToggleButton createButton(String text, Ikon icon, String path) {
+ CustomToggleButton button = new CustomToggleButton();
+ button.setPrefWidth(0);
+ button.setText(text);
+ button.setGraphic(new FontIcon(icon));
+ button.setMaxWidth(Double.MAX_VALUE);
+ button.setUserData(path);
+ button.setOnMousePressed(evt -> {
+ MobileHomePage.getInstance().setContentType(MobileHomePage.ContentType.NORMAL);
+ MobileLinkUtil.getToPage(path);
+ });
+ toggleGroup.getToggles().add(button);
+ return button;
+ }
+}
\ No newline at end of file
diff --git a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/IntroPane.java b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/IntroPane.java
index 9e74c806..8f4a800b 100644
--- a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/IntroPane.java
+++ b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/components/IntroPane.java
@@ -24,16 +24,16 @@ public record IntroCardData(String title, String description, String imageUrl) {
}
public final List introCardData = new ArrayList<>(List.of(
- new IntroCardData("JavaFX Books", "Explore our JavaFX book collection, from beginner guides to advanced tutorials, including game development and use-cases like JavaFX for Raspberry Pi.", "books.png"),
- new IntroCardData("Videos", "Watch JavaFX videos, conference talks, demos, and tutorials for all expertise levels.", "videos.png"),
- new IntroCardData("Libraries", "Explore JavaFX third-party libraries, featuring components, game engines, styles, 3D graphics, and frameworks to enhance your projects.", "libraries.png"),
- new IntroCardData("JavaFX Tutorials", "Browse tutorials ranging from JavaFX basics to advanced application and game development.", "tutorials.png"),
- new IntroCardData("Tools", "Explore JavaFX tools including plugins, layout, CSS tools, testing, and packaging.", "tools.png"),
+ new IntroCardData("Books", "Beginner guides, advanced tutorials, game development.", "books.png"),
+ new IntroCardData("Videos", "Watch videos of conference talks, demos, and tutorials.", "videos.png"),
+ new IntroCardData("Libraries", "Explore third-party libraries that you can add to your own applications.", "libraries.png"),
+ new IntroCardData("Tutorials", "See a listing of places where you can find good online tutorials.", "tutorials.png"),
+ new IntroCardData("Tools", "Find out which tools are available for styling, testing, packaging, etc...", "tools.png"),
new IntroCardData("Links of The Week", "Miscellaneous stuff found on the web that is related to JavaFX.", "news.png"),
- new IntroCardData("Tips & Tricks", "Discover practical JavaFX tips in this section, featuring articles that share real-world techniques and tricks for effective JavaFX development.", "tips.png"),
- new IntroCardData("JavaFX Blogs", "Visit our blog section for articles from JavaFX experts, featuring practical tips, cutting-edge insights, and more from the world of JavaFX.", "blogs.png"),
- new IntroCardData("People", "A curated list of people connected to JavaFX. They develop libraries, applications, tools or they present at conferences and evangelise JavaFX.", "people.png"),
- new IntroCardData("Companies", "Explore companies in JavaFX, especially those with significant influence and contributions to the JavaFX community, shaping and advancing the ecosystem.", "companies.png")
+ new IntroCardData("Tips & Tricks", "Real-world tips and tricks for effective user interface development.", "tips.png"),
+ new IntroCardData("Blogs", "A collection of blogs related to JavaFX", "blogs.png"),
+ new IntroCardData("People", "People that influence the JavaFX ecosystem.", "people.png"),
+ new IntroCardData("Companies", "Companies that contribute to the JavaFX ecosystem.", "companies.png")
));
public IntroPane() {
diff --git a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/MobileHomePage.java b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/MobileHomePage.java
index 24fe5605..ad1589aa 100644
--- a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/MobileHomePage.java
+++ b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/MobileHomePage.java
@@ -19,16 +19,26 @@
import com.dlsc.jfxcentral2.mobile.home.CategoryPreviewView;
import com.dlsc.jfxcentral2.mobile.home.HomePageHeader;
import com.dlsc.jfxcentral2.mobile.home.WeekLinksView;
+import com.dlsc.jfxcentral2.utils.OSUtil;
import com.dlsc.jfxcentral2.utils.PagePath;
+import com.dlsc.jfxcentral2.utils.StringUtil;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
+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;
+import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
+import javafx.scene.shape.Rectangle;
+import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -38,8 +48,6 @@ public class MobileHomePage extends MobilePageBase {
private static MobileHomePage instance;
- private final MobileSearchTextField searchTextField;
-
public enum ContentType {
NORMAL, SEARCH
}
@@ -56,13 +64,13 @@ private MobileHomePage() {
// content
Node normalView = createNormalView();
+ normalView.visibleProperty().bind(contentTypeProperty().isEqualTo(ContentType.NORMAL));
normalView.managedProperty().bind(normalView.visibleProperty());
VBox.setVgrow(normalView, Priority.ALWAYS);
MobileSearchView searchView = new MobileSearchView(sizeProperty());
+ searchView.visibleProperty().bind(contentTypeProperty().isEqualTo(ContentType.SEARCH));
searchView.managedProperty().bind(searchView.visibleProperty());
- searchView.visibleProperty().bind(Bindings.createBooleanBinding(() -> getContentType() == ContentType.SEARCH, contentTypeProperty()));
- normalView.visibleProperty().bind(searchView.visibleProperty().not());
VBox.setVgrow(searchView, Priority.ALWAYS);
// header
@@ -70,39 +78,39 @@ private MobileHomePage() {
header.sizeProperty().bind(sizeProperty());
// search field
- searchTextField = new MobileSearchTextField();
+ MobileSearchTextField searchTextField = new MobileSearchTextField();
searchTextField.setRight(createSearchCancelButton());
searchTextField.setPromptText("Search for anything...");
- searchView.searchTextProperty().bindBidirectional(searchTextField.textProperty());
searchTextField.setOnMousePressed(event -> setContentType(ContentType.SEARCH));
- searchTextField.textProperty().addListener(it -> setContentType(ContentType.SEARCH));
+ searchTextField.setOnTouchPressed(event -> setContentType(ContentType.SEARCH));
+ searchTextField.textProperty().addListener((obs, oldV, newV) -> {
+ if (StringUtils.isNotBlank(newV)) {
+ setContentType(ContentType.SEARCH);
+ }
+ });
+
+ 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));
- }
+ setViewWillAppear(() -> setContentType(ContentType.NORMAL));
- private Button createSearchCancelButton() {
- Button button = new Button("Search");
- button.textProperty().bind(Bindings.createStringBinding(() -> {
- if (getContentType() == ContentType.SEARCH) {
- return "Cancel";
- } else {
- return "Search";
- }
- }, contentTypeProperty()));
- button.setOnAction(event -> {
- if (getContentType() == ContentType.SEARCH) {
+ contentTypeProperty().addListener(it -> {
+ if (getContentType().equals(ContentType.NORMAL)) {
searchTextField.clear();
- setContentType(ContentType.NORMAL);
- } else {
- setContentType(ContentType.SEARCH);
}
});
+ }
+
+ private Button createSearchCancelButton() {
+ Button button = new Button("Cancel");
+ button.visibleProperty().bind(contentTypeProperty().isEqualTo(ContentType.SEARCH));
+ button.setOnMouseClicked(evt -> setContentType(ContentType.NORMAL));
return button;
}
@@ -112,12 +120,6 @@ private Node createNormalView() {
LearnCategoryBox learnCategoryBox = new LearnCategoryBox();
- List linksOfTheWeek = DataRepository2.getInstance().getLinksOfTheWeek();
-
- WeekLinksView weekLinksView = new WeekLinksView();
- weekLinksView.sizeProperty().bind(sizeProperty());
- weekLinksView.setLinksOfTheWeek(linksOfTheWeek.get(linksOfTheWeek.size() - 1));
-
List randomApps = getRandomSample(DataRepository2.getInstance().getRealWorldApps(), 3);
CategoryPreviewView showCasePreviewView = CategoryPreviewView.createShowCasePreviewView(randomApps, PagePath.SHOWCASES);
showCasePreviewView.sizeProperty().bind(sizeProperty());
@@ -146,14 +148,14 @@ private Node createNormalView() {
CategoryPreviewView blogPreviewView = CategoryPreviewView.createBlogPreviewView(randomBlogs, PagePath.BLOGS);
blogPreviewView.sizeProperty().bind(sizeProperty());
- // VBox normalView = new VBox(categoryAdvancedView, weekLinksView, showCasePreviewView, peoplePreviewView, libraryPreviewView, booksPreviewView, videoPreviewView, blogPreviewView, tipsPreviewView, learnCategoryBox);
VBox normalView = new VBox(showCasePreviewView, peoplePreviewView, libraryPreviewView, booksPreviewView, videoPreviewView, blogPreviewView, tipsPreviewView, learnCategoryBox);
normalView.getStyleClass().add("content-box");
- ScrollPane ScrollPane = new ScrollPane(normalView);
- ScrollPane.getStyleClass().add("mobile");
- VBox.setVgrow(ScrollPane, Priority.ALWAYS);
- return ScrollPane;
+ ScrollPane scrollPane = new ScrollPane(normalView);
+ scrollPane.getStyleClass().add("mobile");
+ VBox.setVgrow(scrollPane, Priority.ALWAYS);
+
+ return scrollPane;
}
private List getRandomSample(List list, int sampleSize) {
@@ -181,5 +183,4 @@ public final ObjectProperty contentTypeProperty() {
public final void setContentType(ContentType contentType) {
this.contentType.set(contentType);
}
-
}
diff --git a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/details/MobileShowcaseMobileDetailsPage.java b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/details/MobileShowcaseDetailsPage.java
similarity index 89%
rename from mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/details/MobileShowcaseMobileDetailsPage.java
rename to mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/details/MobileShowcaseDetailsPage.java
index 68e71ea0..6f5e6d0f 100644
--- a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/details/MobileShowcaseMobileDetailsPage.java
+++ b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/pages/details/MobileShowcaseDetailsPage.java
@@ -13,9 +13,9 @@
import java.util.List;
-public class MobileShowcaseMobileDetailsPage extends MobileDetailsPageBase {
+public class MobileShowcaseDetailsPage extends MobileDetailsPageBase {
- public MobileShowcaseMobileDetailsPage(ObjectProperty size, String itemId) {
+ public MobileShowcaseDetailsPage(ObjectProperty size, String itemId) {
super(size, RealWorldApp.class, itemId);
}
diff --git a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/skin/MainPageSkin.java b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/skin/MainPageSkin.java
index 956e519d..b0c8adc3 100644
--- a/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/skin/MainPageSkin.java
+++ b/mobile/src/main/java/com/dlsc/jfxcentral2/mobile/skin/MainPageSkin.java
@@ -1,47 +1,202 @@
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.components.CategorySelectionView;
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.SkinBase;
-import javafx.scene.layout.BorderPane;
+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 {
- private final BorderPane borderPane = new BorderPane();
- private final BottomMenuBar bottomMenuBar = new BottomMenuBar();
+ public static final double CLOSED_DRAWER_HEIGHT = .26;
+ public static final int DRAWER_ANIMATION_DURATION = 150;
+
+ private final StackPane contentPane = new StackPane();
+ private final GlassPane glassPane = new GlassPane();
+ private final Node drawer;
+ private StackPane drawerHandle;
+
+ // value between 0 and 1
+ private final DoubleProperty drawerHeight = new SimpleDoubleProperty(CLOSED_DRAWER_HEIGHT);
+
+ private CategorySelectionView content;
+ private Timeline timeline;
+ private double startY = -1;
public MainPageSkin(MainPage control) {
super(control);
+
EventBusUtil.register(this);
- bottomMenuBar.managedProperty().bind(bottomMenuBar.visibleProperty());
- bottomMenuBar.setVisible(false);
+ drawer = createDrawerContent();
+
+ contentPane.getStyleClass().add("content-pane");
+
+ contentPane.setManaged(false);
+ glassPane.setManaged(false);
+ drawer.setManaged(false);
+ drawer.setVisible(false);
+
+ glassPane.setOnMouseClicked(evt -> hideDrawer());
+ glassPane.setBlockingOpacity(0);
+ glassPane.hideProperty().bind(drawerHeight.lessThanOrEqualTo(CLOSED_DRAWER_HEIGHT));
+
+ getChildren().setAll(contentPane, glassPane, drawer);
+
+ drawerHeight.addListener(it -> getSkinnable().requestLayout());
+ }
+
+ @Override
+ protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
+ double drawerHeight = 0;
+ if (drawer.isVisible()) {
+ drawerHeight = this.drawerHeight.get() * content.prefHeight(-1) + drawerHandle.prefHeight(-1);
+ drawer.resizeRelocate(contentX, contentY + contentHeight - drawerHeight, contentWidth, drawerHeight);
+ }
+ contentPane.resizeRelocate(contentX, contentY, contentWidth, contentHeight - drawerHeight);
+ glassPane.resizeRelocate(contentX, contentY, contentWidth, contentHeight - 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");
+
+ content = new CategorySelectionView(this::hideDrawer);
+ 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);
+
+ Rectangle clip = new Rectangle();
+ clip.widthProperty().bind(drawer.widthProperty());
+ clip.heightProperty().bind(drawer.heightProperty().add(50));
+ clip.setLayoutY(-50);
+ drawer.setClip(clip);
+
+ drawer.addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> startY = evt.getScreenY());
- borderPane.setBottom(bottomMenuBar);
- getChildren().add(borderPane);
+ drawer.addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> {
+ startY = 0;
+
+ if (drawerHeight.get() > CLOSED_DRAWER_HEIGHT) {
+ if (drawerHeight.get() < .75) {
+ hideDrawer();
+ } else {
+ showDrawer();
+ }
+ }
+ });
+
+ drawer.addEventFilter(MouseEvent.MOUSE_DRAGGED, evt -> {
+ if (startY != -1) {
+ double deltaY = startY - evt.getScreenY();
+ double height = drawerHeight.get();
+ height += (deltaY / drawer.prefHeight(-1));
+ if (height > CLOSED_DRAWER_HEIGHT && height <= 1) {
+ height = Math.max(CLOSED_DRAWER_HEIGHT, Math.min(1, height));
+ drawerHeight.set(height);
+ startY = evt.getScreenY();
+ }
+ }
+ });
+
+// drawer.addEventFilter(MouseEvent.MOUSE_CLICKED, evt -> {
+// if (evt.isConsumed()) {
+// return;
+// }
+// if (drawerHeight.get() > CLOSED_DRAWER_HEIGHT) {
+// hideDrawer();
+// } else if (evt.getClickCount() == 2) {
+// showDrawer();
+// }
+// });
+
+ drawer.setOnSwipeDown(evt -> hideDrawer());
+ drawer.setOnSwipeUp(evt -> showDrawer());
+
+ return drawer;
+ }
+
+ private void installCloseDrawerHandler(Node node) {
+// node.addEventFilter(MouseEvent.MOUSE_CLICKED, evt -> {
+// if (evt.getButton().equals(MouseButton.PRIMARY) && evt.isStillSincePress()) {
+// System.out.println("closing bc of handler");
+// hideDrawer();
+// }
+// });
+ }
+
+ private void hideDrawer() {
+ animateDrawer(CLOSED_DRAWER_HEIGHT);
+ }
+
+ 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(drawerHeight, value);
+ KeyFrame keyFrame = new KeyFrame(Duration.millis(DRAWER_ANIMATION_DURATION), 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);
- borderPane.setCenter(newView);
+ contentPane.getChildren().setAll(newView);
// Old view did disappear
invokeLifecycleMethod(oldView, MobilePageBase::getViewDidDisappear);
@@ -57,7 +212,7 @@ public void onMobileResponseEvent(MobileResponseEvent event) {
}
}
}
-
+
private void invokeLifecycleMethod(Node view, Function eventFunction) {
if (view instanceof MobilePageBase mobilePage) {
Optional.ofNullable(eventFunction.apply(mobilePage)).ifPresent(Runnable::run);
@@ -66,7 +221,6 @@ private void invokeLifecycleMethod(Node view, Function
@Subscribe
public void onHideMenuBarEvent(RepositoryUpdatedEvent event) {
- bottomMenuBar.setVisible(event.isUpdated());
+ drawer.setVisible(event.isUpdated());
}
-
}
diff --git a/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/components/gluon.png b/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/components/gluon.png
new file mode 100644
index 00000000..c9292a77
Binary files /dev/null and b/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/components/gluon.png differ
diff --git a/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/components/senapt.png b/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/components/senapt.png
new file mode 100644
index 00000000..0977e064
Binary files /dev/null and b/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/components/senapt.png differ
diff --git a/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/mobile.css b/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/mobile.css
index c59bcf83..5d766eb1 100644
--- a/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/mobile.css
+++ b/mobile/src/main/resources/com/dlsc/jfxcentral2/mobile/mobile.css
@@ -2,11 +2,11 @@
* notch-pane
*/
.notch-pane.mobile {
- -fx-background-color: #F7F9FFFF;
+ -fx-background-color: -white;
}
/** ----------------------------------
- *PrettyScrollPane
+ * PrettyScrollPane
*/
.scroll-pane.mobile {
-fx-fit-to-width: true;
@@ -16,43 +16,6 @@
-fx-background-color: -white;
}
-/** ----------------------------------
- * BottomMenuBar
- */
-.bottom-menu-bar {
- -fx-alignment: top-center;
- -fx-background-color: -background;
- -fx-padding: 8px 0 25px 0;
- -fx-border-color: -grey-10;
- -fx-border-width: 1 0 0 0;
- -fx-font-size: 12px;
-}
-
-.bottom-menu-bar .toggle-button {
- -fx-content-display: top;
- -fx-background-color: transparent;
- -fx-text-fill: -grey-60;
- -fx-padding: 0 0 5px 0;
- -fx-graphic-text-gap: 0px;
-}
-
-.bottom-menu-bar .toggle-button .ikonli-font-icon {
- -fx-icon-size: 24px;
- -fx-icon-color: -grey-60;
-}
-
-.bottom-menu-bar:md-lg .toggle-button .ikonli-font-icon {
- -fx-icon-size: 24px;
-}
-
-.bottom-menu-bar .toggle-button:selected {
- -fx-text-fill: -bright-blue;
-}
-
-.bottom-menu-bar .toggle-button:selected .ikonli-font-icon {
- -fx-icon-color: -bright-blue;
-}
-
/** ----------------------------------
* BackButton
*/
@@ -535,8 +498,12 @@
-fx-background-color: -white;
}
+.mobile-home-page .mobile-search-text-field {
+ -fx-padding: 0px 10px;
+}
+
.mobile-home-page .content-box {
- -fx-padding: 5px 15px 10px 15px;
+ -fx-padding: 5px 15px 50px 15px;
-fx-spacing: 30px;
}
@@ -545,16 +512,89 @@
}
.mobile-home-page > .search-wrapper {
- -fx-padding: 0 15px 5px 15px;
+ -fx-padding: 10px 15px 10px 15px;
-fx-alignment: center-left;
}
+/** ----------------------------------
+ * MainPage
+ */
+.main-page .drawer {
+ -fx-border-color: transparent transparent transparent transparent;
+ -fx-background-color: -background;
+ -fx-background-radius: 32px 32px 0px 0px;
+ -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.2), 10, 0.2, 0, 0); /* 20% alpha */;
+}
+
+.main-page .drawer > .handle {
+ -fx-background-color: -background;
+ -fx-padding: 6px 0px;
+}
+
+.main-page .drawer > .handle > .region {
+ -fx-pref-width: 50px;
+ -fx-pref-height: 4px;
+ -fx-background-radius: 8px;
+ -fx-background-color: -grey-30;
+}
+
+.categories-selection-view {
+ -fx-font-size: 12px;
+ -fx-padding: 0px 10px 35px 10px;
+}
+
+.categories-selection-view > .logo-box {
+ -fx-padding: 0px 15px 0px 0px;
+ -fx-alignment: bottom-right;
+ -fx-spacing: 30px;
+ -fx-fit-to-height: false;
+}
+
+.categories-selection-view > .logo-box > .hbox {
+ -fx-fit-to-height: false;
+ -fx-alignment: bottom-left;
+ -fx-spacing: 10px;
+}
+
+.categories-selection-view > .logo-box > .hbox > .vbox {
+ -fx-alignment: bottom-left;
+}
+
+.categories-selection-view > .logo-box > .hbox > .vbox > .label {
+ -fx-text-fill: -grey-60;
+ -fx-font-family: "Spline Sans";
+ -fx-font-size: 10px;
+}
+
+.categories-selection-view > .toggle-button {
+ -fx-content-display: top;
+ -fx-background-color: transparent;
+ -fx-text-fill: -grey-60;
+ -fx-padding: 10px 0px;
+ -fx-graphic-text-gap: 0px;
+ -fx-font-family: "Spline Sans";
+ -fx-font-size: 12px;
+}
+
+.categories-selection-view > .toggle-button .ikonli-font-icon {
+ -fx-icon-size: 24px;
+ -fx-icon-color: -grey-60;
+}
+
+.categories-selection-view > .toggle-button:selected {
+ -fx-text-fill: -bright-blue;
+}
+
+.categories-selection-view > .toggle-button:selected .ikonli-font-icon {
+ -fx-icon-color: -bright-blue;
+}
+
/** ----------------------------------
* MobileSearchTextField
*/
.mobile-search-text-field {
-fx-background-radius: 1000px;
- -fx-padding: 0px 10px;
+ -fx-padding: 6px 10px;
}
.mobile-search-text-field > .ikonli-font-icon {
@@ -572,7 +612,8 @@
-fx-border-insets: 4px 0px;
-fx-text-fill: -bright-blue;
-fx-padding: 4 5px 4 10px;
- -fx-background-color: transparent;
+ -fx-background-color: white;
+ -fx-background-insets: 4px;
}
.home-page-header {
@@ -688,7 +729,7 @@
* MobilePageHeader
*/
.mobile-page-header {
- -fx-background-color: linear-gradient(from 0% 0% to 0% 100%, rgba(247, 249, 255, 1) 0%, rgba(247, 249, 255, 0) 100%);
+ -fx-background-color: -white;
-fx-alignment: center;
}
@@ -699,7 +740,7 @@
.mobile-page-header > .top-box .button {
-fx-padding: 8px;
-fx-background-radius: 50px;
- -fx-background-color: transparent;
+ -fx-background-color: white;
}
.mobile-page-header > .top-box .button:pressed {
@@ -903,10 +944,6 @@
-fx-background-color: -white;
}
-.model-list-view .search-wrapper .search-text-field .left-pane {
- -fx-padding: 8px;
-}
-
.model-list-view .list-view .model-list-cell {
-fx-cell-size: 80px;
-fx-padding: 10px 5px;
@@ -1056,6 +1093,28 @@
-fx-border-width: 0 0 1px 0;
}
+.mobile-refresh-page .update-content-box {
+ -fx-alignment: center;
+ -fx-padding: 0 50px;
+ -fx-spacing: 15px;
+}
+
+.mobile-refresh-page .update-content-box .custom-image-view {
+ -fx-fit-width: 400px;
+}
+
+.mobile-refresh-page .update-content-box .dividing-line {
+ -fx-border-color: #75767660;
+ -fx-border-width: 0 0 1px 0;
+ -fx-padding: 0 0 10px 0;
+}
+
+.mobile-refresh-page .update-content-box > .label {
+ -fx-font-family: "Roboto Condensed";
+ -fx-font-size: 1.3em;
+ -fx-text-fill: -grey-30;
+}
+
/** ----------------------------------
* IntroPane
*/
diff --git a/pom.xml b/pom.xml
index c4c6c110..c0d71682 100644
--- a/pom.xml
+++ b/pom.xml
@@ -278,7 +278,7 @@
com.dlsc.gemsfx
gemsfx
- 2.23.0
+ 2.47.0
diff --git a/sampler/src/main/java/com/dlsc/jfxcentral2/demo/JFXCentralSampleBase.java b/sampler/src/main/java/com/dlsc/jfxcentral2/demo/JFXCentralSampleBase.java
index d037ccd4..1904a946 100644
--- a/sampler/src/main/java/com/dlsc/jfxcentral2/demo/JFXCentralSampleBase.java
+++ b/sampler/src/main/java/com/dlsc/jfxcentral2/demo/JFXCentralSampleBase.java
@@ -5,7 +5,7 @@
*/
package com.dlsc.jfxcentral2.demo;
-import com.dlsc.jfxcentral2.mobile.components.BottomMenuBar;
+import com.dlsc.jfxcentral2.mobile.pages.MainPage;
import com.dlsc.jfxcentral2.utils.NodeUtil;
import fr.brouillard.oss.cssfx.CSSFX;
import fxsampler.SampleBase;
@@ -18,6 +18,8 @@
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
+import java.util.Objects;
+
public abstract class JFXCentralSampleBase extends SampleBase {
private boolean stylesheetsAdded;
@@ -56,10 +58,9 @@ public final Node getPanel(Stage stage) {
}
- scene.getStylesheets().add(NodeUtil.class.getResource("/com/dlsc/jfxcentral2/theme.css").toExternalForm());
- scene.getStylesheets().add(BottomMenuBar.class.getResource("/com/dlsc/jfxcentral2/mobile/mobile.css").toExternalForm());
-
- scene.getStylesheets().add(JFXCentralSampleBase.class.getResource("/com/dlsc/jfxcentral2/demo/components/test.css").toExternalForm());
+ scene.getStylesheets().add(Objects.requireNonNull(NodeUtil.class.getResource("/com/dlsc/jfxcentral2/theme.css")).toExternalForm());
+ scene.getStylesheets().add(Objects.requireNonNull(MainPage.class.getResource("/com/dlsc/jfxcentral2/mobile/mobile.css")).toExternalForm());
+ scene.getStylesheets().add(Objects.requireNonNull(JFXCentralSampleBase.class.getResource("/com/dlsc/jfxcentral2/demo/components/test.css")).toExternalForm());
stylesheetsAdded = true;
});
diff --git a/sampler/src/main/java/com/dlsc/jfxcentral2/demo/mobile/HelloBottomMenuBar.java b/sampler/src/main/java/com/dlsc/jfxcentral2/demo/mobile/HelloBottomMenuBar.java
deleted file mode 100644
index 5d77e8db..00000000
--- a/sampler/src/main/java/com/dlsc/jfxcentral2/demo/mobile/HelloBottomMenuBar.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.dlsc.jfxcentral2.demo.mobile;
-
-import com.dlsc.jfxcentral2.components.SizeComboBox;
-import com.dlsc.jfxcentral2.demo.JFXCentralSampleBase;
-import com.dlsc.jfxcentral2.mobile.components.BottomMenuBar;
-import javafx.scene.Node;
-import javafx.scene.layout.Region;
-import javafx.scene.layout.StackPane;
-
-public class HelloBottomMenuBar extends JFXCentralSampleBase {
-
- private BottomMenuBar bottomMenuBar;
-
- @Override
- protected Region createControl() {
- bottomMenuBar = new BottomMenuBar();
- return new StackPane(bottomMenuBar);
- }
-
- @Override
- public Node getControlPanel() {
- SizeComboBox sizeComboBox = new SizeComboBox();
- bottomMenuBar.sizeProperty().bind(sizeComboBox.sizeProperty());
- return sizeComboBox;
- }
-
- @Override
- public String getSampleName() {
- return "BottomMenuBar";
- }
-}
diff --git a/sampler/src/main/java/com/dlsc/jfxcentral2/demo/mobile/HelloCategorySelectionView.java b/sampler/src/main/java/com/dlsc/jfxcentral2/demo/mobile/HelloCategorySelectionView.java
new file mode 100644
index 00000000..d5eb7ef0
--- /dev/null
+++ b/sampler/src/main/java/com/dlsc/jfxcentral2/demo/mobile/HelloCategorySelectionView.java
@@ -0,0 +1,24 @@
+package com.dlsc.jfxcentral2.demo.mobile;
+
+import com.dlsc.jfxcentral2.demo.JFXCentralSampleBase;
+import com.dlsc.jfxcentral2.mobile.components.CategorySelectionView;
+import javafx.geometry.Pos;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.StackPane;
+
+public class HelloCategorySelectionView extends JFXCentralSampleBase {
+
+ @Override
+ protected Region createControl() {
+ CategorySelectionView view = new CategorySelectionView(null);
+ view.setPrefWidth(400);
+ view.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+ StackPane.setAlignment(view, Pos.CENTER);
+ return new StackPane(view);
+ }
+
+ @Override
+ public String getSampleName() {
+ return "CategoriesSelectionView";
+ }
+}