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