├── .github ├── theme.png ├── themes │ ├── backify-dark.png │ ├── backify-light.png │ ├── standard-dark.png │ └── standard-light.png └── thumbnail.png ├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── jannikbuscha │ └── dashboard │ ├── Main.java │ ├── controller │ └── DashboardController.java │ ├── tab │ ├── Builder.java │ ├── Home.java │ ├── Options.java │ └── Users.java │ ├── user │ ├── LocalUserData.java │ └── User.java │ └── util │ ├── FXUtil.java │ ├── ResizeListener.java │ └── Theme.java └── resources └── com └── jannikbuscha └── dashboard ├── css ├── font.css ├── style.css └── theme │ ├── backify │ ├── dark.css │ ├── light.css │ └── theme.css │ └── standard │ ├── dark.css │ ├── light.css │ └── theme.css ├── font ├── barlow │ └── Barlow-Regular.ttf └── proxima-nova-alt │ ├── Proxima-Nova-Alt-Bold.ttf │ ├── Proxima-Nova-Alt-Regular.ttf │ └── Proxima-Nova-Alt-Semibold.ttf └── fxml └── dashboard.fxml /.github/theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/.github/theme.png -------------------------------------------------------------------------------- /.github/themes/backify-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/.github/themes/backify-dark.png -------------------------------------------------------------------------------- /.github/themes/backify-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/.github/themes/backify-light.png -------------------------------------------------------------------------------- /.github/themes/standard-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/.github/themes/standard-dark.png -------------------------------------------------------------------------------- /.github/themes/standard-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/.github/themes/standard-light.png -------------------------------------------------------------------------------- /.github/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/.github/thumbnail.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | *.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-present - Jannik Buscha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaFX Dashboard 2 | 3 | This JavaFX Dashboard is a flexible and versatile template, designed to be used as a starting point for your own custom dashboard projects. It features a theme changer with both dark and light modes, a customizable table builder, and a user-friendly interface. 4 | 5 | **[Features](#-features) • [Themes](#-themes) • [Theme Changer](#-theme-changer) • [Table Builder](#-table-builder) • [Adding Tabs](#-adding-tabs) • [License](#-license)** 6 | 7 | ![thumbnail](.github/thumbnail.png) 8 | 9 | ## 💫 Features 10 | 11 | - Custom title bar 12 | - Rounded movable window 13 | - Resizeable 14 | - [Theme Changer](#-theme-changer) (with local storage) 15 | - [Table Builder](#-table-builder) 16 | 17 | ## 🎨 Themes 18 | 19 | | Theme | Light | Dark | 20 | |----------|------------------------------------------------------|---------------------------------------------------------| 21 | | Standard | ![standard-light](.github/themes/standard-light.png) | ![json-standard-dark](.github/themes/standard-dark.png) | 22 | | Backify | ![backify-light](.github/themes/backify-light.png) | ![backify-dark](.github/themes/backify-dark.png) | 23 | 24 | ## 🌗 Theme Changer 25 | 26 | ![theme](.github/theme.png) 27 | 28 | To add a new theme, you need to create a new enumerated value in the [Theme](src/main/java/com/jannikbuscha/dashboard/util/Theme.java) enum: 29 | 30 | ```java 31 | public enum Theme { 32 | STANDARD, BACKIFY, /* YOUR_NEW_THEME */; 33 | // ... 34 | } 35 | ``` 36 | 37 | Then create the corresponding CSS files: 38 | 39 | - `dark.css` (Color variables for the dark theme) 40 | - `light.css` (Color variables for the light theme) 41 | - `theme.css` (Semantic color variables and font for the theme) 42 | 43 | in the [theme](src/main/resources/com/jannikbuscha/dashboard/css/theme) directory: 44 | 45 | ``` 46 | 📦javafx-dashboard 47 | ┗ 📂src 48 | ┃ ┗ 📂main 49 | ┃ ┃ ┣ 📂java 50 | ┃ ┃ ┗ 📂resources 51 | ┃ ┃ ┃ ┗ 📂com 52 | ┃ ┃ ┃ ┃ ┗ 📂jannikbuscha 53 | ┃ ┃ ┃ ┃ ┃ ┗ 📂dashboard 54 | ┃ ┃ ┃ ┃ ┃ ┃ ┗ 📂css 55 | ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┗ 📂theme 56 | ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┣ 📂backify 57 | ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┣ 📂standard 58 | ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┗ 📂your_new_theme 59 | ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┣ 📜dark.css 60 | ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┣ 📜light.css 61 | ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┗ 📜theme.css 62 | ┗ ... 63 | ``` 64 | 65 | How to structure the content of these files can be found in the [standard theme](src/main/resources/com/jannikbuscha/dashboard/css/theme/standard). 66 | 67 | After changing the theme in the dashboard, it is stored locally in `java.io.tmpdir` properties using the [LocalUserData](src/main/java/com/jannikbuscha/dashboard/user/LocalUserData.java) class. 68 | 69 | ## 🛠 Table Builder 70 | 71 | The table methods in FXUtils simplify the process of building tables and populating the TableView, making it easier to create a well-formatted table of data from the ObservableList. 72 | 73 | Example usage: 74 | 75 | ```java 76 | final ObservableList data = FXCollections.observableArrayList( 77 | new User("Violet", 56, "USA"), 78 | new User("Brianna", 44, "Germany"), 79 | // ... 80 | ); 81 | 82 | List> columns = Arrays.asList( 83 | FXUtil.createColumn("Name", User::getName), 84 | FXUtil.createColumn("Age", User::getAge), 85 | FXUtil.createColumn("Country", User::getCountry) 86 | ); 87 | 88 | TableView table = FXUtil.createTable(data, columns); 89 | ``` 90 | 91 | More context can be found for this in the [Options](src/main/java/com/jannikbuscha/dashboard/tab/Options.java)-Tab class. 92 | 93 | ## 🗂 Adding Tabs 94 | 95 | To create a new tab, you must first create a new `HBox` in the [Dashboard FXML](src/main/resources/com/jannikbuscha/dashboard/fxml/dashboard.fxml) as follows in the `VBox fx:id="vbxMenuNavigation"`: 96 | 97 | ```xml 98 | 99 | 100 | 101 | 102 | 104 | ``` 105 | 106 | Then the Tab class in the [Tab](src/main/java/com/jannikbuscha/dashboard/tab) package must be created as follows: 107 | 108 | ```java 109 | public class YourNewTab extends StackPane { 110 | public YourNewTab() { 111 | this.getChildren().add(new Label(this.getClass().getSimpleName())); 112 | } 113 | } 114 | ``` 115 | 116 | Finally, create an object in the `initialize()` method of the [Dashboard Controller](src/main/java/com/jannikbuscha/dashboard/controller/DashboardController.java) inside the tabs Pane array: 117 | 118 | ```java 119 | Pane[] tabs = {new Home(), new Users(), new Builder(), new Options(), /*new YourNewTab()*/}; 120 | ``` 121 | 122 | ## 📝 License 123 | 124 | [MIT](LICENSE) 125 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.jannikbuscha 8 | dashboard 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | org.projectlombok 27 | lombok 28 | 1.18.12 29 | provided 30 | 31 | 32 | 33 | 34 | org.fxmisc.cssfx 35 | cssfx 36 | 1.1.1 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/Main.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard; 2 | 3 | import com.jannikbuscha.dashboard.user.LocalUserData; 4 | import com.jannikbuscha.dashboard.util.FXUtil; 5 | import com.jannikbuscha.dashboard.util.Theme; 6 | import javafx.application.Application; 7 | import javafx.fxml.FXMLLoader; 8 | import javafx.scene.Parent; 9 | import javafx.scene.Scene; 10 | import javafx.scene.paint.Color; 11 | import javafx.stage.Stage; 12 | import javafx.stage.StageStyle; 13 | import lombok.*; 14 | import org.fxmisc.cssfx.CSSFX; 15 | 16 | import java.io.IOException; 17 | 18 | @Getter 19 | public class Main extends Application { 20 | 21 | private final String name = "JavaFX Dashboard", version = "1.0"; 22 | 23 | @Getter 24 | private static Main instance; 25 | 26 | private Stage stage; 27 | 28 | @Setter 29 | private Scene scene; 30 | 31 | public void start(Stage stage) throws Exception { 32 | instance = this; 33 | 34 | startCSSFX(); 35 | createScene(stage); 36 | } 37 | 38 | private void startCSSFX() { 39 | CSSFX.start(); 40 | } 41 | 42 | private void createScene(Stage stage) throws IOException { 43 | this.stage = stage; 44 | 45 | Parent root = FXMLLoader.load(getClass().getResource("fxml/dashboard.fxml")); 46 | 47 | scene = new Scene(root); 48 | 49 | loadTheme(); 50 | scene.getStylesheets().add(getClass().getResource("css/style.css").toExternalForm()); 51 | 52 | scene.setRoot(root); 53 | scene.setFill(Color.TRANSPARENT); 54 | 55 | stage.setTitle(name + " " + version); 56 | stage.setScene(scene); 57 | 58 | stage.initStyle(StageStyle.TRANSPARENT); 59 | FXUtil.resizable(stage); 60 | 61 | stage.show(); 62 | } 63 | 64 | private void loadTheme() { 65 | // Ignore CSS warnings 66 | com.sun.javafx.util.Logging.getCSSLogger().setLevel(sun.util.logging.PlatformLogger.Level.OFF); 67 | 68 | if (!LocalUserData.existsFolder()) { 69 | LocalUserData.setProperty("theme", Theme.STANDARD.ordinal() + ""); 70 | LocalUserData.setProperty("dark_mode", String.valueOf(false)); 71 | } 72 | 73 | LocalUserData.getProperty("theme").ifPresent(theme -> Theme.setCurrentTheme(Theme.values()[Integer.parseInt(theme)], LocalUserData.getProperty("dark_mode").map(Boolean::valueOf).orElse(false))); 74 | } 75 | 76 | @Override 77 | public void stop() throws Exception { 78 | super.stop(); 79 | System.exit(0); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/controller/DashboardController.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.controller; 2 | 3 | import com.jannikbuscha.dashboard.Main; 4 | import com.jannikbuscha.dashboard.tab.*; 5 | import com.jannikbuscha.dashboard.util.FXUtil; 6 | import javafx.application.Platform; 7 | import javafx.fxml.FXML; 8 | import javafx.fxml.Initializable; 9 | import javafx.scene.control.Label; 10 | import javafx.scene.layout.*; 11 | import javafx.scene.shape.SVGPath; 12 | 13 | import java.net.URL; 14 | import java.util.ResourceBundle; 15 | 16 | public class DashboardController implements Initializable { 17 | 18 | @FXML 19 | private BorderPane root; 20 | 21 | @FXML 22 | private StackPane stckTopBar, stckMin, stckClose; 23 | 24 | @FXML 25 | private VBox vbxMenuNavigation, vbxMenuTabs; 26 | 27 | @FXML 28 | private Label lblVersion; 29 | 30 | @Override 31 | public void initialize(URL location, ResourceBundle resources) { 32 | lblVersion.setText(Main.getInstance().getVersion()); 33 | FXUtil.movable(Main.getInstance().getStage(), stckTopBar); 34 | 35 | FXUtil.windowActions(Main.getInstance().getStage(), stckMin, stckClose); 36 | 37 | Pane[] tabs = {new Home(), new Users(), new Builder(), new Options()}; 38 | 39 | vbxMenuTabs.getChildren().add(tabs[0]); 40 | 41 | for (int i = 0; i < tabs.length; ++i) { 42 | VBox.setVgrow(tabs[i], Priority.ALWAYS); 43 | FXUtil.tabSwitch(vbxMenuNavigation.getChildren().get(i), tabs[i], vbxMenuNavigation, vbxMenuTabs); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/tab/Builder.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.tab; 2 | 3 | import javafx.scene.control.Label; 4 | import javafx.scene.layout.StackPane; 5 | 6 | public class Builder extends StackPane { 7 | 8 | public Builder() { 9 | this.getChildren().add(new Label(this.getClass().getSimpleName())); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/tab/Home.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.tab; 2 | 3 | import javafx.scene.control.Label; 4 | import javafx.scene.layout.StackPane; 5 | 6 | public class Home extends StackPane { 7 | 8 | public Home() { 9 | this.getChildren().add(new Label(this.getClass().getSimpleName())); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/tab/Options.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.tab; 2 | 3 | import com.jannikbuscha.dashboard.user.LocalUserData; 4 | import com.jannikbuscha.dashboard.util.Theme; 5 | import javafx.collections.FXCollections; 6 | import javafx.event.ActionEvent; 7 | import javafx.event.EventHandler; 8 | import javafx.geometry.Pos; 9 | import javafx.scene.control.CheckBox; 10 | import javafx.scene.control.ComboBox; 11 | import javafx.scene.layout.HBox; 12 | import javafx.scene.layout.StackPane; 13 | import javafx.util.StringConverter; 14 | 15 | import java.util.Arrays; 16 | 17 | public class Options extends StackPane { 18 | 19 | public Options() { 20 | ComboBox cbxTheme = new ComboBox(); 21 | CheckBox ccxDarkMode = new CheckBox("Dark Mode"); 22 | 23 | // Prepare click event for theme change 24 | EventHandler themeClickEvent = e -> { 25 | LocalUserData.setProperty("theme", String.valueOf(cbxTheme.getSelectionModel().getSelectedIndex())); 26 | LocalUserData.setProperty("dark_mode", String.valueOf(ccxDarkMode.isSelected())); 27 | Theme.setCurrentTheme(Theme.values()[cbxTheme.getSelectionModel().getSelectedIndex()], ccxDarkMode.isSelected()); 28 | }; 29 | 30 | // Prepare theme combo box 31 | cbxTheme.setItems(FXCollections.observableArrayList(Theme.values())); 32 | cbxTheme.setOnAction(themeClickEvent); 33 | cbxTheme.setConverter(new StringConverter() { 34 | @Override 35 | public String toString(Theme object) { 36 | return object.getName(); 37 | } 38 | 39 | @Override 40 | public Theme fromString(String string) { 41 | return Arrays.stream(Theme.values()).filter(m -> m.getName().equals(string)).findFirst().orElse(null); 42 | } 43 | }); 44 | 45 | // Prepare dark mode selector 46 | ccxDarkMode.setOnAction(themeClickEvent); 47 | 48 | // Set current theme 49 | cbxTheme.getSelectionModel().select(Integer.parseInt(LocalUserData.getProperty("theme").get())); 50 | ccxDarkMode.setSelected(LocalUserData.getProperty("dark_mode").map(Boolean::valueOf).orElse(false)); 51 | 52 | HBox hbxTheme = new HBox(8, cbxTheme, ccxDarkMode); 53 | hbxTheme.setAlignment(Pos.BASELINE_LEFT); 54 | 55 | this.getChildren().addAll(hbxTheme); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/tab/Users.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.tab; 2 | 3 | import com.jannikbuscha.dashboard.user.User; 4 | import com.jannikbuscha.dashboard.util.FXUtil; 5 | import javafx.collections.*; 6 | import javafx.scene.control.*; 7 | import javafx.scene.layout.StackPane; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | public class Users extends StackPane { 13 | 14 | public Users() { 15 | final ObservableList data = FXCollections.observableArrayList( 16 | new User("Violet", 56, "USA"), 17 | new User("Brianna", 44, "Germany"), 18 | new User("Freddie", 12, "Belgium"), 19 | new User("James", 35, "Denmark"), 20 | new User("Alina", 22, "New Zealand")); 21 | 22 | List> columns = Arrays.asList( 23 | FXUtil.createColumn("Name", User::getName), 24 | FXUtil.createColumn("Age", User::getAge), 25 | FXUtil.createColumn("Country", User::getCountry) 26 | ); 27 | 28 | TableView table = FXUtil.createTable(data, columns); 29 | 30 | this.getChildren().add(table); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/user/LocalUserData.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.user; 2 | 3 | import com.jannikbuscha.dashboard.Main; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.util.Optional; 10 | import java.util.Properties; 11 | 12 | public class LocalUserData { 13 | 14 | private static final String TEMP_PATH = System.getProperty("java.io.tmpdir"); 15 | 16 | private static final File FOLDER = new File(TEMP_PATH + "/" + Main.getInstance().getName()), 17 | PROPERTIES_FILE = new File(FOLDER, "data.properties"); 18 | 19 | private static Properties properties = null; 20 | 21 | public static boolean existsFolder() { 22 | return FOLDER.exists(); 23 | } 24 | 25 | public static Optional getProperty(String key) { 26 | initProperties(); 27 | 28 | if (!properties.containsKey(key)) { 29 | return Optional.empty(); 30 | } 31 | 32 | return Optional.of(properties.getProperty(key)); 33 | } 34 | 35 | public static void setProperty(String key, String value) { 36 | initProperties(); 37 | 38 | properties.setProperty(key, value); 39 | 40 | try { 41 | properties.store(new FileOutputStream(PROPERTIES_FILE), "Do not change anything here!"); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | private static void initProperties() { 48 | if (properties != null) return; 49 | 50 | properties = new Properties(); 51 | 52 | if (!existsFolder()) { 53 | FOLDER.mkdir(); 54 | } 55 | 56 | if (!PROPERTIES_FILE.exists()) { 57 | try { 58 | properties.store(new FileOutputStream(PROPERTIES_FILE), "Do not change anything here!"); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | try { 65 | properties.load(new FileInputStream(PROPERTIES_FILE)); 66 | } catch (IOException e) { 67 | e.printStackTrace(); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/user/User.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.user; 2 | 3 | import javafx.beans.property.IntegerProperty; 4 | import javafx.beans.property.SimpleIntegerProperty; 5 | import javafx.beans.property.SimpleStringProperty; 6 | import javafx.beans.property.StringProperty; 7 | import lombok.Getter; 8 | 9 | @Getter 10 | public class User { 11 | 12 | private final StringProperty name, country; 13 | private final IntegerProperty age; 14 | 15 | public User(String name, int age, String country) { 16 | this.name = new SimpleStringProperty(name); 17 | this.age = new SimpleIntegerProperty(age); 18 | this.country = new SimpleStringProperty(country); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/util/FXUtil.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.util; 2 | 3 | import com.sun.javafx.scene.control.skin.TableHeaderRow; 4 | import javafx.beans.property.Property; 5 | import javafx.collections.ObservableList; 6 | import javafx.event.EventType; 7 | import javafx.scene.Node; 8 | import javafx.scene.Scene; 9 | import javafx.scene.control.Label; 10 | import javafx.scene.control.TableColumn; 11 | import javafx.scene.control.TableView; 12 | import javafx.scene.layout.Pane; 13 | import javafx.stage.Stage; 14 | 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.concurrent.atomic.AtomicReference; 18 | import java.util.function.Function; 19 | 20 | import static javafx.scene.input.MouseEvent.*; 21 | 22 | public class FXUtil { 23 | private static int winWidth = 1240, winHeight = 704; 24 | 25 | public static void movable(Stage stage, Pane pane) { 26 | AtomicReference xOffset = new AtomicReference<>(0D); 27 | AtomicReference yOffset = new AtomicReference<>(0D); 28 | 29 | pane.setOnMousePressed(e -> { 30 | xOffset.set(e.getSceneX()); 31 | yOffset.set(e.getSceneY()); 32 | }); 33 | 34 | pane.setOnMouseDragged(e -> { 35 | stage.setX(e.getScreenX() - xOffset.get()); 36 | stage.setY(e.getScreenY() - yOffset.get()); 37 | }); 38 | } 39 | 40 | public static void windowActions(Stage stage, Node min, Node close) { 41 | min.setOnMouseClicked(e -> stage.setIconified(true)); 42 | close.setOnMouseClicked(e -> System.exit(0)); 43 | } 44 | 45 | public static void resizable(Stage stage) { 46 | ResizeListener resizeListener = new ResizeListener(stage, winWidth, winHeight); 47 | Scene scene = stage.getScene(); 48 | 49 | EventType[] mouseEvents = new EventType[]{MOUSE_MOVED, MOUSE_PRESSED, MOUSE_DRAGGED, MOUSE_EXITED, MOUSE_EXITED_TARGET}; 50 | Arrays.stream(mouseEvents).forEach(type -> scene.addEventHandler(type, resizeListener)); 51 | } 52 | 53 | public static void tabSwitch(Node navigation, Pane tab, Pane navigationContainer, Pane tabContainer) { 54 | navigation.setOnMouseClicked(e -> { 55 | if (e.getClickCount() == 1) { 56 | navigation.getStyleClass().add("selected"); 57 | 58 | navigationContainer.getChildren().stream().filter(n -> !n.equals(navigation)).forEach(n -> 59 | n.getStyleClass().remove("selected")); 60 | 61 | tabContainer.getChildren().clear(); 62 | 63 | tabContainer.getChildren().add(tab); 64 | } 65 | }); 66 | } 67 | 68 | public static TableView createTable(ObservableList data, List> columns) { 69 | TableView table = new TableView<>(data); 70 | table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); 71 | 72 | table.getColumns().addAll(columns); 73 | table.setPlaceholder(new Label("Empty")); 74 | 75 | table.skinProperty().addListener((obs, oldSkin, newSkin) -> 76 | { 77 | TableHeaderRow header = (TableHeaderRow) table.lookup("TableHeaderRow"); 78 | header.reorderingProperty().addListener((o, oldVal, newVal) -> header.setReordering(false)); 79 | }); 80 | 81 | return table; 82 | } 83 | 84 | public static TableColumn createColumn(String name, Function> property) { 85 | TableColumn column = new TableColumn<>(name); 86 | column.setCellValueFactory(cellData -> property.apply(cellData.getValue())); 87 | column.setSortable(true); 88 | 89 | return column; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/util/ResizeListener.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.util; 2 | 3 | import javafx.event.*; 4 | import javafx.scene.*; 5 | import javafx.scene.input.MouseEvent; 6 | import javafx.stage.Stage; 7 | import lombok.Setter; 8 | 9 | @Setter 10 | public class ResizeListener implements EventHandler { 11 | private final Stage stage; 12 | private final Scene scene; 13 | 14 | private Cursor cursorEvent = Cursor.DEFAULT; 15 | 16 | private double startX = 0, startY = 0; 17 | private double sceneOffsetX = 0, sceneOffsetY = 0; 18 | 19 | public ResizeListener(Stage stage, int width, int height) { 20 | this.stage = stage; 21 | this.scene = stage.getScene(); 22 | 23 | stage.setMinWidth(width); 24 | stage.setMinHeight(height); 25 | } 26 | 27 | @Override 28 | public void handle(MouseEvent mouseEvent) { 29 | EventType mouseEventType = mouseEvent.getEventType(); 30 | 31 | double mouseEventX = mouseEvent.getSceneX(), mouseEventY = mouseEvent.getSceneY(); 32 | double viewWidth = stage.getWidth(), viewHeight = stage.getHeight(); 33 | 34 | int border = 4; 35 | 36 | if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) { 37 | if (mouseEventX < border) { 38 | cursorEvent = Cursor.W_RESIZE; 39 | } else if (mouseEventX > viewWidth - border) { 40 | cursorEvent = Cursor.E_RESIZE; 41 | } else if (mouseEventY < border) { 42 | cursorEvent = Cursor.N_RESIZE; 43 | } else if (mouseEventY > viewHeight - border) { 44 | cursorEvent = Cursor.S_RESIZE; 45 | } else { 46 | cursorEvent = Cursor.DEFAULT; 47 | } 48 | 49 | scene.setCursor(cursorEvent); 50 | } else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) { 51 | scene.setCursor(Cursor.DEFAULT); 52 | } else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) { 53 | startX = viewWidth - mouseEventX; 54 | startY = viewHeight - mouseEventY; 55 | 56 | sceneOffsetX = mouseEvent.getSceneX(); 57 | sceneOffsetY = mouseEvent.getSceneY(); 58 | } else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && !Cursor.DEFAULT.equals(cursorEvent)) { 59 | if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) { 60 | double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2); 61 | 62 | if (Cursor.N_RESIZE.equals(cursorEvent)) { 63 | if (stage.getHeight() > minHeight || mouseEventY < 0) { 64 | double height = stage.getY() - mouseEvent.getScreenY() + stage.getHeight() + sceneOffsetY; 65 | double y = mouseEvent.getScreenY() - sceneOffsetY; 66 | 67 | stage.setHeight(height); 68 | stage.setY(y); 69 | } 70 | } else { 71 | if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) { 72 | stage.setHeight(mouseEventY + startY); 73 | } 74 | } 75 | } 76 | 77 | if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) { 78 | double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2); 79 | 80 | if (Cursor.W_RESIZE.equals(cursorEvent)) { 81 | if (stage.getWidth() > minWidth || mouseEventX < 0) { 82 | double width = stage.getX() - mouseEvent.getScreenX() + stage.getWidth() + sceneOffsetX; 83 | double x = mouseEvent.getScreenX() - sceneOffsetX; 84 | 85 | stage.setWidth(width); 86 | stage.setX(x); 87 | } 88 | } else { 89 | if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) { 90 | stage.setWidth(mouseEventX + startX); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /src/main/java/com/jannikbuscha/dashboard/util/Theme.java: -------------------------------------------------------------------------------- 1 | package com.jannikbuscha.dashboard.util; 2 | 3 | import com.jannikbuscha.dashboard.Main; 4 | import javafx.application.Platform; 5 | import lombok.*; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | @Getter 11 | @AllArgsConstructor 12 | public enum Theme { 13 | 14 | STANDARD, BACKIFY; 15 | 16 | public String getThemeFile() { 17 | return Main.class.getResource("css/theme/" + this.name().toLowerCase() + "/theme.css").toExternalForm(); 18 | } 19 | 20 | public String getDarkFile() { 21 | return Main.class.getResource("css/theme/" + this.name().toLowerCase() + "/dark.css").toExternalForm(); 22 | } 23 | 24 | public String getLightFile() { 25 | return Main.class.getResource("css/theme/" + this.name().toLowerCase() + "/light.css").toExternalForm(); 26 | } 27 | 28 | public String getName() { 29 | return this.name().substring(0, 1).toUpperCase() + this.name().substring(1).toLowerCase().replace("_", " "); 30 | } 31 | 32 | public static void setCurrentTheme(Theme theme, boolean dark) { 33 | Main.getInstance().getScene().getStylesheets().removeIf(s -> s.contains("theme.css") || s.contains("dark.css") || s.contains("light.css")); 34 | Main.getInstance().getScene().getStylesheets().addAll(theme.getThemeFile(), dark ? theme.getDarkFile() : theme.getLightFile()); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/css/font.css: -------------------------------------------------------------------------------- 1 | /* Barlow */ 2 | @font-face { 3 | font-family: "Barlow Regular"; 4 | src: url("../font/barlow/Barlow-Regular.ttf"); 5 | } 6 | 7 | /*@font-face {*/ 8 | /* font-family: "Barlow ExtraBold";*/ 9 | /* src: url("../font/barlow/Barlow-ExtraBold.ttf");*/ 10 | /*}*/ 11 | 12 | /* Proxima Nova */ 13 | @font-face { 14 | font-family: "Proxima Nova Alt Regular"; 15 | src: url("../font/proxima-nova-alt/Proxima-Nova-Alt-Regular.ttf"); 16 | } 17 | 18 | @font-face { 19 | font-family: "Proxima Nova Alt Semibold"; 20 | src: url("../font/proxima-nova-alt/Proxima-Nova-Alt-Semibold.ttf"); 21 | } 22 | 23 | @font-face { 24 | font-family: "Proxima Nova Alt Bold"; 25 | src: url("../font/proxima-nova-alt/Proxima-Nova-Alt-Bold.ttf"); 26 | } -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/css/style.css: -------------------------------------------------------------------------------- 1 | @import url("font.css"); 2 | 3 | * { 4 | -fx-effect: none !important; 5 | 6 | -fx-font-smoothing-type: gray; 7 | -fx-smooth: true; 8 | } 9 | 10 | .root { 11 | -fx-background-color: transparent; 12 | } 13 | 14 | .root Label { 15 | -fx-text-fill: -fx-primary-text-color; 16 | } 17 | 18 | .button, .window-actions, .sidebar-tab, .top-bar-icon, .combo-box, .check-box { 19 | -fx-cursor: hand; 20 | } 21 | 22 | .sidebar { 23 | -fx-pref-width: 230px; 24 | -fx-background-color: -fx-sidebar-color; 25 | -fx-background-radius: 16px 0 0 16px; 26 | } 27 | 28 | .sidebar-tab Label { 29 | -fx-text-fill: -fx-secondary-text-color; 30 | } 31 | 32 | .logo { 33 | -fx-text-fill: -fx-primary-color; 34 | -fx-font-size: 34px; 35 | -fx-font-weight: bold; 36 | } 37 | 38 | .icon-selected { 39 | -fx-fill: -fx-primary-color; 40 | } 41 | 42 | .icon-1, .icon-2 { 43 | -fx-fill: -fx-secondary-text-color; 44 | } 45 | 46 | .icon-2 { 47 | -fx-opacity: .4; 48 | } 49 | 50 | .selected Label { 51 | -fx-text-fill: -fx-primary-text-color; 52 | } 53 | 54 | .selected SVGPath { 55 | -fx-fill: -fx-primary-color; 56 | } 57 | 58 | .background { 59 | -fx-background-color: -fx-bg-color; 60 | -fx-background-radius: 0 16px 16px 0; 61 | } 62 | 63 | .menu-tab { 64 | -fx-background-color: -fx-card-color; 65 | -fx-background-radius: 6px; 66 | } 67 | 68 | .window-actions { 69 | -fx-stroke: none; 70 | -fx-fill: -fx-bg-color; 71 | } 72 | 73 | .table-view .column-header, .table-view .column-header .filler, .table-view .column-header-background .filler, .nested-column-header, .table-view .column-header, .column-header-background { 74 | -fx-background-color: -fx-separator-color; 75 | -fx-border-width: 0; 76 | -fx-background-radius: 6px 6px 0 0; 77 | } 78 | 79 | .table-view .filler, .table-view .column-header { 80 | -fx-size: 40px; 81 | } 82 | 83 | .table-view { 84 | -fx-table-cell-border-color: transparent; 85 | } 86 | 87 | .table-row-cell .text, .table-row-cell:hover .text { 88 | -fx-fill: -fx-primary-text-color; 89 | } 90 | 91 | .table-row-cell:hover .text { 92 | -fx-fill: -fx-secondary-text-color; 93 | } 94 | 95 | .table-row-cell:selected .text, .icon { 96 | -fx-fill: -fx-primary-color; 97 | } 98 | 99 | .table-column { 100 | -fx-padding: 0 0 0 5; 101 | -fx-alignment: CENTER_LEFT; 102 | } 103 | 104 | .table-row-cell { 105 | -fx-cell-size: 44px; 106 | -fx-background-color: -fx-card-color; 107 | -fx-background-insets: 0; 108 | -fx-table-cell-border-color: transparent; 109 | } 110 | 111 | .table-view, .scroll-bar { 112 | -fx-background-color: transparent; 113 | } 114 | 115 | .top-bar-icon SVGPath { 116 | -fx-fill: -fx-secondary-text-color; 117 | } 118 | 119 | .combo-box-base .arrow, .combo-box-base:focused .arrow { 120 | -fx-shape: "M2462.656-371.929l-4.267-4.268-4.268,4.268a1,1,0,0,1-1.414,0,1,1,0,0,1,0-1.415l4.95-4.949a1,1,0,0,1,.732-.293,1,1,0,0,1,.732.293l4.95,4.949a1,1,0,0,1,0,1.415,1,1,0,0,1-.707.293A1,1,0,0,1,2462.656-371.929Z"; 121 | -fx-background-color: -fx-primary-text-color; 122 | } 123 | 124 | .combo-box-base:focused .arrow { 125 | -fx-rotate: 180; 126 | } 127 | 128 | .combo-box-popup .list-view .list-cell, .combo-box-popup .list-view { 129 | -fx-background-color: -fx-bg-color; 130 | -fx-border-width: 0; 131 | -fx-padding: 5; 132 | } 133 | 134 | .combo-box-popup > .list-view { 135 | -fx-padding: 0; 136 | } 137 | 138 | .combo-box-popup .list-view .list-cell:filled:selected, .dialog-container .combo-box-popup .list-view .list-cell:filled:selected { 139 | -fx-background-color: -fx-primary-color; 140 | } 141 | 142 | .combo-box .cell, .combo-box-popup .list-view .list-cell:hover { 143 | -fx-text-fill: -fx-primary-text-color; 144 | } 145 | 146 | .combo-box-popup .list-view .list-cell { 147 | -fx-text-fill: -fx-secondary-text-color; 148 | } 149 | 150 | .combo-box-popup .list-view .list-cell:filled:selected { 151 | -fx-text-fill: -fx-primary-text-color; 152 | } 153 | 154 | .combo-box, .check-box .box { 155 | -fx-background-color: -fx-separator-color; 156 | } 157 | 158 | .check-box { 159 | -fx-text-fill: -fx-primary-text-color; 160 | } 161 | 162 | .check-box .box { 163 | -fx-border-color: transparent; 164 | -fx-border-radius: 0; 165 | } 166 | 167 | .check-box .mark { 168 | -fx-shape: "M1.73,12.91 8.1,19.28 22.79,4.59 22.79,1.59 8.1,16.28 1.73,9.91"; 169 | } 170 | 171 | .check-box:selected .mark { 172 | -fx-background-color: -fx-primary-text-color; 173 | } 174 | 175 | .check-box:selected .box { 176 | -fx-background-color: -fx-primary-color; 177 | } -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/css/theme/backify/dark.css: -------------------------------------------------------------------------------- 1 | * { 2 | -fx-primary-color: #95F39B; 3 | 4 | -fx-separator-color: -fx-bg-color; 5 | 6 | -fx-card-color: #100F15; 7 | -fx-bg-color: #06050B; 8 | 9 | -fx-sidebar-color: -fx-bg-color; 10 | 11 | -fx-primary-text-color: #ffffff; 12 | -fx-secondary-text-color: #708090; 13 | } -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/css/theme/backify/light.css: -------------------------------------------------------------------------------- 1 | * { 2 | -fx-primary-color: #95F39B; 3 | 4 | -fx-separator-color: -fx-bg-color; 5 | 6 | -fx-card-color: #ffffff; 7 | -fx-bg-color: #F8F9FA; 8 | 9 | -fx-sidebar-color: -fx-bg-color; 10 | 11 | -fx-primary-text-color: #06050B; 12 | -fx-secondary-text-color: #708090; 13 | } -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/css/theme/backify/theme.css: -------------------------------------------------------------------------------- 1 | * { 2 | -fx-success-color: #C8F362; 3 | -fx-error-color: #EF2D54; 4 | -fx-info-color: #8D62F3; 5 | -fx-warning-color: #F1C9AC; 6 | } 7 | 8 | .regular { 9 | -fx-font-family: "Proxima Nova Alt Regular"; 10 | } 11 | 12 | .semibold { 13 | -fx-font-family: "Proxima Nova Alt Semibold"; 14 | } 15 | 16 | .bold { 17 | -fx-font-family: "Proxima Nova Alt Bold"; 18 | } -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/css/theme/standard/dark.css: -------------------------------------------------------------------------------- 1 | * { 2 | -fx-primary-color: #e76086; 3 | 4 | -fx-separator-color: #282e3f; 5 | 6 | -fx-card-color: #1f2632; 7 | -fx-bg-color: derive(-fx-card-color, -15%); 8 | 9 | -fx-sidebar-color: linear-gradient(to bottom, derive(-fx-separator-color, -6%), -fx-bg-color); 10 | 11 | -fx-primary-text-color: #ffffff; 12 | -fx-secondary-text-color: #9999aa; 13 | } -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/css/theme/standard/light.css: -------------------------------------------------------------------------------- 1 | * { 2 | -fx-primary-color: #3506EF; 3 | 4 | -fx-separator-color: -fx-bg-color; 5 | 6 | -fx-card-color: #FFFFFF; 7 | -fx-bg-color: #F2F3F7; 8 | 9 | -fx-sidebar-color: linear-gradient(to bottom, derive(-fx-separator-color, -6%), -fx-bg-color); 10 | 11 | -fx-primary-text-color: #2E2E3A; 12 | -fx-secondary-text-color: #7E7E8F; 13 | } -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/css/theme/standard/theme.css: -------------------------------------------------------------------------------- 1 | * { 2 | -fx-success-color: #77dfa4; 3 | -fx-error-color: #f95054; 4 | -fx-info-color: #789efc; 5 | -fx-warning-color: #fab859; 6 | } 7 | 8 | .regular { 9 | -fx-font-family: "Proxima Nova Alt Regular"; 10 | } 11 | 12 | .semibold { 13 | -fx-font-family: "Proxima Nova Alt Semibold"; 14 | } 15 | 16 | .bold { 17 | -fx-font-family: "Proxima Nova Alt Bold"; 18 | } -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/font/barlow/Barlow-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/src/main/resources/com/jannikbuscha/dashboard/font/barlow/Barlow-Regular.ttf -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/font/proxima-nova-alt/Proxima-Nova-Alt-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/src/main/resources/com/jannikbuscha/dashboard/font/proxima-nova-alt/Proxima-Nova-Alt-Bold.ttf -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/font/proxima-nova-alt/Proxima-Nova-Alt-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/src/main/resources/com/jannikbuscha/dashboard/font/proxima-nova-alt/Proxima-Nova-Alt-Regular.ttf -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/font/proxima-nova-alt/Proxima-Nova-Alt-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fntp/javafx-common-dashboard/3e437376bb0847bf7dd463d761fe3c4cb8dcaed8/src/main/resources/com/jannikbuscha/dashboard/font/proxima-nova-alt/Proxima-Nova-Alt-Semibold.ttf -------------------------------------------------------------------------------- /src/main/resources/com/jannikbuscha/dashboard/fxml/dashboard.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 63 | 64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
97 |
98 | --------------------------------------------------------------------------------