diff --git a/pom.xml b/pom.xml
index 4ddcd58a3fa2195be7a56a3221def65b913133ea..882fede3a350c0b121878919e98fbe45bad37825 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,10 +9,10 @@
1.0-SNAPSHOT
- 20
- 20
+ 21
+ 21
UTF-8
- 20.0.2
+ 21
2.0.1
@@ -106,6 +106,12 @@
atlantafx-base
${atlantafx.version}
+
+ org.jetbrains
+ annotations
+ 23.0.0
+ compile
+
diff --git a/src/main/java/com/light/TutorialApplication.java b/src/main/java/com/light/GitManagerApp.java
similarity index 30%
rename from src/main/java/com/light/TutorialApplication.java
rename to src/main/java/com/light/GitManagerApp.java
index 97bb58e37528e65876828189de53250a4bb3e66d..6cccd4f8092502dfb5bc898aa0d42497f3696136 100644
--- a/src/main/java/com/light/TutorialApplication.java
+++ b/src/main/java/com/light/GitManagerApp.java
@@ -1,45 +1,55 @@
package com.light;
+import atlantafx.base.theme.PrimerLight;
+import com.light.layout.ContentPane;
+import com.light.layout.MenuPane;
+import com.light.util.NodeUtils;
import javafx.application.Application;
+import javafx.geometry.Insets;
import javafx.scene.Scene;
-import javafx.scene.control.Label;
import javafx.scene.image.Image;
-import javafx.scene.layout.*;
-import javafx.scene.paint.Color;
-import javafx.scene.text.Font;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
import javafx.stage.Stage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-/**
- * TODO 例子,后续移除
- */
-public class TutorialApplication extends Application {
+public class GitManagerApp extends Application {
- public static void main(String[] args) {
- launch(args);
- }
+ public static final Logger LOGGER = LoggerFactory.getLogger(GitManagerApp.class);
@Override
public void start(Stage stage) throws Exception {
- // label
- Label l = new Label("Hello");
- l.setFont(Font.font(20));
-
- BorderPane root = new BorderPane(l);
-
- root.setTop(getPane("blue"));
- root.setLeft(getPane("green"));
-
- Scene scene = new Scene(root, 1096, 768);
+ // 主题
+ Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet());
+
+ // 根节点
+ AnchorPane root = new AnchorPane();
+
+ // 左右布局
+ HBox container = new HBox();
+ // 内容
+ ContentPane contentPane = new ContentPane();
+ // 菜单
+ MenuPane menuPane = new MenuPane(contentPane);
+ container.getChildren().addAll(menuPane, contentPane);
+ HBox.setHgrow(contentPane, Priority.ALWAYS);
+
+ root.getChildren().add(container);
+ NodeUtils.setAnchors(root, Insets.EMPTY);
+
+ // 场景
+ Scene scene = new Scene(root, 1000, 600);
stage.setTitle("Git批量管理工具");
stage.getIcons().add(new Image(this.getClass().getResource("/icons/git.png").toExternalForm()));
stage.setScene(scene);
stage.show();
+
+ LOGGER.info("项目启动完成。。。");
}
- public Pane getPane(String color) {
- Pane pane = new Pane();
- pane.setPrefSize(100.0, 100.0);
- pane.setStyle("-fx-background-color: " + color);
- return pane;
+ public static void main(String[] args) {
+ launch(args);
}
}
diff --git a/src/main/java/com/light/LogApplication.java b/src/main/java/com/light/LogApplication.java
deleted file mode 100644
index da176e66f31983caad2cbb46cb4701255940f264..0000000000000000000000000000000000000000
--- a/src/main/java/com/light/LogApplication.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.light;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-/**
- * 日志使用示例,后续删除
- */
-public class LogApplication {
-
- public static final Logger LOGGER = LoggerFactory.getLogger(LogApplication.class);
- private static final Logger COMMON_LOGGER = LoggerFactory.getLogger("COMMON");
-
- public static void main(String[] args) {
- for (int i = 0; i < 10; i++) {
- LogService logService = new LogService();
- LOGGER.info("这是info日志");
- LOGGER.error("这是error日志");
- LOGGER.warn("这是warn日志");
- LOGGER.debug("这是debug日志");
- COMMON_LOGGER.info("这是common的info日志");
- COMMON_LOGGER.warn("这是common的warn日志");
- COMMON_LOGGER.debug("这是common的debug日志");
- }
-
- }
-}
diff --git a/src/main/java/com/light/LogService.java b/src/main/java/com/light/LogService.java
deleted file mode 100644
index eef769868211c2bf4ba91f8504807a2fc24c3890..0000000000000000000000000000000000000000
--- a/src/main/java/com/light/LogService.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.light;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * 日志使用示例,后续删除
- */
-public class LogService {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(LogService.class);
-
- public LogService() {
- LOGGER.info("这是com.leo.service中的info日志");
- LOGGER.warn("这是com.leo.service中的warn日志");
- LOGGER.debug("这是com.leo.service中的debug日志");
- }
-}
diff --git a/src/main/java/com/light/layout/ContentPane.java b/src/main/java/com/light/layout/ContentPane.java
new file mode 100644
index 0000000000000000000000000000000000000000..d08b986e6d6065f759a836007e6e324062d42246
--- /dev/null
+++ b/src/main/java/com/light/layout/ContentPane.java
@@ -0,0 +1,14 @@
+package com.light.layout;
+
+import javafx.scene.Node;
+import javafx.scene.layout.BorderPane;
+
+public class ContentPane extends BorderPane {
+
+ public ContentPane() {
+ }
+
+ public void setContent(Node content) {
+ setCenter(content);
+ }
+}
diff --git a/src/main/java/com/light/layout/MenuPane.java b/src/main/java/com/light/layout/MenuPane.java
new file mode 100644
index 0000000000000000000000000000000000000000..b56ba7ded12124791ed1d43b67e5d18dbb8feb6f
--- /dev/null
+++ b/src/main/java/com/light/layout/MenuPane.java
@@ -0,0 +1,66 @@
+package com.light.layout;
+
+import javafx.geometry.Orientation;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
+import javafx.scene.control.Separator;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.StackPane;
+import javafx.scene.layout.VBox;
+import org.kordamp.ikonli.antdesignicons.AntDesignIconsFilled;
+import org.kordamp.ikonli.antdesignicons.AntDesignIconsOutlined;
+import org.kordamp.ikonli.javafx.FontIcon;
+
+import java.util.Optional;
+
+public class MenuPane extends StackPane {
+
+ private static final String STYLE_SHEET = MenuPane.class.getResource("/css/menu.css").toExternalForm();
+
+ // 顶部导航栏
+ private final NavItem setting = new NavItem(new FontIcon(AntDesignIconsFilled.SETTING), "设置", null);
+ private final NavItem notification = new NavItem(new FontIcon(AntDesignIconsFilled.NOTIFICATION), "通知", null);
+ private final VBox topMenu = new VBox(setting, notification);
+
+ // 动态导航栏
+ private final ListView dynamicMenu = new ListView<>();
+
+ // 底部导航栏
+ private final Label userLabel = new Label("test");
+ private final Label emailLabel = new Label("test@163.com");
+ private final VBox bottomMenu = new VBox(userLabel, emailLabel);
+
+ private VBox asideContainer = new VBox(topMenu, new Separator(Orientation.HORIZONTAL), dynamicMenu, new Separator(Orientation.HORIZONTAL), bottomMenu);
+
+ private final ContentPane contentPane;
+
+ public MenuPane(ContentPane contentPane) {
+ this.contentPane = contentPane;
+ initialize();
+
+ // 模拟数据
+ dynamicMenu.getItems().addAll(
+ new NavItem(new FontIcon(AntDesignIconsOutlined.HOME), "首页", null),
+ new NavItem(new FontIcon(AntDesignIconsOutlined.PARTITION), "管理", null),
+ new NavItem(new FontIcon(AntDesignIconsOutlined.EDIT), "笔记", null)
+ );
+ }
+
+ private void initialize() {
+ // 加载样式
+ getStylesheets().add(STYLE_SHEET);
+
+ getChildren().addAll(asideContainer);
+ VBox.setVgrow(dynamicMenu, Priority.ALWAYS);
+
+ // 添加样式
+ this.dynamicMenu.getStyleClass().addAll("menu");
+
+ // 加上监听
+ dynamicMenu.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
+ Optional.ofNullable(newValue).ifPresent(menuItem -> {
+ contentPane.setContent(menuItem.getContent());//设置内容
+ });
+ });
+ }
+}
diff --git a/src/main/java/com/light/layout/NavItem.java b/src/main/java/com/light/layout/NavItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..76a946da92e3386ff24035d38cd63b7c42c4c4ae
--- /dev/null
+++ b/src/main/java/com/light/layout/NavItem.java
@@ -0,0 +1,51 @@
+package com.light.layout;
+
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
+import org.kordamp.ikonli.javafx.FontIcon;
+
+/**
+ * 导航组件
+ */
+public class NavItem extends HBox {
+
+ /**
+ * 图标
+ */
+ private Label iconLabel = new Label();
+
+ /**
+ * 标题
+ */
+ private Label titleLabel = new Label();
+
+ /**
+ * 内容界面
+ */
+ private Node content;
+
+ public NavItem(FontIcon fontIcon, String title, Node content) {
+ this.iconLabel.setGraphic(fontIcon);
+ this.titleLabel.setText(title);
+ this.content = content;
+ initialize();
+ }
+
+ private void initialize() {
+ getChildren().addAll(iconLabel, titleLabel);
+ // 标题占满剩余空间
+ titleLabel.setMaxSize(Double.MAX_VALUE, USE_PREF_SIZE);
+ HBox.setHgrow(titleLabel, Priority.ALWAYS);
+
+ getStyleClass().add("nav-item");
+ iconLabel.getStyleClass().add("icon-label");
+ titleLabel.getStyleClass().add("name-label");
+ prefWidthProperty().bind(this.widthProperty().subtract(1));
+ }
+
+ public Node getContent() {
+ return content;
+ }
+}
diff --git a/src/main/java/com/light/util/FxUtil.java b/src/main/java/com/light/util/FxUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8bb44005a2cf1b27a72aa086fdc4a1252c9baab
--- /dev/null
+++ b/src/main/java/com/light/util/FxUtil.java
@@ -0,0 +1,114 @@
+package com.light.util;
+
+import javafx.beans.binding.Bindings;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.geometry.Bounds;
+import javafx.scene.Node;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.Region;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+import javafx.stage.Window;
+
+import java.util.concurrent.Callable;
+
+/**
+ * @author ChenFei
+ * @date 2022/12/9
+ */
+public class FxUtil {
+
+ public static String getResource(String resources) {
+ return FxUtil.class.getResource(resources).toExternalForm();
+ }
+
+ public static Image getImage(String resources) {
+ return new Image(getResource(resources));
+ }
+
+ public static ImageView getImageView(String resources, double size) {
+ return getImageView(resources, size, size);
+ }
+
+ public static ImageView getImageView(String resources, double height, double width) {
+ ImageView imageView = new ImageView(getResource(resources));
+ imageView.setFitHeight(height);
+ imageView.setFitWidth(width);
+ return imageView;
+ }
+
+ /**
+ * 获取组件在屏幕中的位置
+ *
+ * @param node
+ * @return
+ */
+ public static Bounds localToScreen(Node node) {
+ return node.localToScreen(node.getLayoutBounds());
+ }
+
+ /**
+ * 获取窗口
+ *
+ * @param node
+ * @return
+ */
+ public static Stage getStage(Node node) {
+ return (Stage) getWindow(node);
+ }
+
+
+ /**
+ * 获取当前被聚焦的第一个窗口,没有则返回null。
+ *
+ * @return
+ */
+ public static Window getFocusedWindow() {
+ return Stage.getWindows().stream().filter(Window::isFocused).findFirst().orElse(null);
+ }
+
+ /**
+ * 获取窗口
+ *
+ * @param node
+ * @return
+ */
+ public static Window getWindow(Node node) {
+ return node.getParent().getScene().getWindow();
+ }
+
+ /**
+ * 矩形裁剪
+ *
+ * @param region :裁剪区域
+ * @return Rectangle
+ */
+ public static Rectangle clipRect(Region region) {
+ return clipRect(region, new SimpleDoubleProperty(0));
+ }
+
+ /**
+ * 矩形裁剪
+ *
+ * @param node :裁剪区域
+ * @param bindArc :裁剪圆角
+ * @return Rectangle
+ */
+ public static Rectangle clipRect(Node node, DoubleProperty bindArc) {
+ Rectangle rectangle = new Rectangle();
+ //rectangle.setSmooth(false);
+ rectangle.widthProperty().bind(Bindings
+ .createObjectBinding((Callable) () ->
+ node.getLayoutBounds().getWidth(), node.layoutBoundsProperty()));
+ rectangle.heightProperty().bind(Bindings
+ .createObjectBinding((Callable) () ->
+ node.getLayoutBounds().getHeight(), node.layoutBoundsProperty()));
+ rectangle.arcWidthProperty().bind(bindArc);
+ rectangle.arcHeightProperty().bind(bindArc);
+ node.setClip(rectangle);
+ return rectangle;
+ }
+
+}
diff --git a/src/main/java/com/light/util/H2Utils.java b/src/main/java/com/light/util/H2Utils.java
new file mode 100644
index 0000000000000000000000000000000000000000..186e4c22cf1359e05f25b96eabb51cdeeb893661
--- /dev/null
+++ b/src/main/java/com/light/util/H2Utils.java
@@ -0,0 +1,4 @@
+package com.light.util;
+
+public class H2Utils {
+}
diff --git a/src/main/java/com/light/util/NodeUtils.java b/src/main/java/com/light/util/NodeUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..eeebc219c3912d50da8b0a2ea4655a0bf3b8e073
--- /dev/null
+++ b/src/main/java/com/light/util/NodeUtils.java
@@ -0,0 +1,29 @@
+package com.light.util;
+
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.layout.AnchorPane;
+
+public final class NodeUtils {
+
+ /**
+ * 设置子节点距AnchorPane锚点布局四边的距离
+ *
+ * @param node
+ * @param insets
+ */
+ public static void setAnchors(Node node, Insets insets) {
+ if (insets.getTop() >= 0) {
+ AnchorPane.setTopAnchor(node, insets.getTop());
+ }
+ if (insets.getRight() >= 0) {
+ AnchorPane.setRightAnchor(node, insets.getRight());
+ }
+ if (insets.getBottom() >= 0) {
+ AnchorPane.setBottomAnchor(node, insets.getBottom());
+ }
+ if (insets.getLeft() >= 0) {
+ AnchorPane.setLeftAnchor(node, insets.getLeft());
+ }
+ }
+}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index a0fa134ecba79c1c0f47f9c587e4d3dcb591ac6b..fbb190d889513c5c1b4e6f956a5319cddb9b4dac 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -7,4 +7,7 @@ open module com.leo {
requires java.sql;
requires com.h2database;
requires atlantafx.base;
+ requires org.kordamp.ikonli.javafx;
+ requires org.jetbrains.annotations;
+ requires org.kordamp.ikonli.antdesignicons;
}
diff --git a/src/main/resources/css/menu.css b/src/main/resources/css/menu.css
new file mode 100644
index 0000000000000000000000000000000000000000..5f1ad56b661f13f8e1d8b63eb2e466dbd3d14add
--- /dev/null
+++ b/src/main/resources/css/menu.css
@@ -0,0 +1,56 @@
+/*导航栏Item样式*/
+.nav-item{
+ -fx-background-radius: 3px;
+ -fx-min-height: 40px;
+ -fx-pref-height: 40px;
+ -fx-alignment: center-left;
+ -fx-spacing: 10px;
+ -fx-padding: 0 10px;
+ -fx-cursor: hand;
+}
+.nav-item > .icon-label > .ikonli-font-icon{
+ -fx-icon-size: 20px;
+ -fx-icon-color: derive(#303133,30%);
+}
+.nav-item > .name-label{
+ -fx-font-size: 14px;
+ -fx-pref-height: 30px;
+ -fx-font-weight: bolder;
+ -fx-text-fill: derive(#303133,30%);
+}
+.nav-item:hover > .name-label{
+ -fx-text-fill: #6690FF;
+}
+.nav-item:hover > .icon-label > .ikonli-font-icon{
+ -fx-icon-color: #6690FF;
+}
+/*动态导航栏*/
+.menu{
+ -fx-background-insets: 0px;
+ -fx-background-color:transparent;
+ -fx-padding:5px 0 5px 0;
+}
+.menu .scroll-bar{
+ -fx-opacity:0;
+}
+.menu:hover .scroll-bar{
+ -fx-opacity:1;
+}
+.menu .cell.indexed-cell.list-cell{
+ -fx-background-color:transparent;
+ -fx-padding:0px;
+}
+.menu .cell.indexed-cell.list-cell:empty:hover{
+ -fx-background-color:transparent;
+}
+.menu .cell.indexed-cell.list-cell:hover{}
+.menu .cell.indexed-cell.list-cell:selected{}
+.menu .cell.indexed-cell.list-cell:selected .nav-item{
+ -fx-background-color: rgba(0,0,0,0.1);
+}
+.menu .cell.indexed-cell.list-cell:selected .name-label{
+ -fx-text-fill: #6690FF;
+}
+.menu .cell.indexed-cell.list-cell:selected .icon-label > .ikonli-font-icon{
+ -fx-icon-color: #6690FF;
+}
\ No newline at end of file
diff --git a/src/main/resources/css/test.css b/src/main/resources/css/test.css
deleted file mode 100644
index 4e55547a516794e8361f5147ff191b83c419ae27..0000000000000000000000000000000000000000
--- a/src/main/resources/css/test.css
+++ /dev/null
@@ -1 +0,0 @@
-/*例子后续移除*/
\ No newline at end of file