initial commit
This commit is contained in:
+39
@@ -0,0 +1,39 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
.kotlin
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
Generated
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>cz.jzitnik</groupId>
|
||||||
|
<artifactId>jecnaclient</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-maven-plugin</artifactId>
|
||||||
|
<version>0.0.8</version>
|
||||||
|
<configuration>
|
||||||
|
<!-- Make sure this matches your exact package and class name -->
|
||||||
|
<mainClass>cz.jzitnik.Main</mainClass>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- JavaFX Controls -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-controls</artifactId>
|
||||||
|
<version>21.0.1</version> <!-- Use the version matching your JDK -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-fxml</artifactId>
|
||||||
|
<version>21.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- AtlantaFX Theme -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.mkpaz</groupId>
|
||||||
|
<artifactId>atlantafx-base</artifactId>
|
||||||
|
<version>2.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Reflections -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.reflections</groupId>
|
||||||
|
<artifactId>reflections</artifactId>
|
||||||
|
<version>0.10.2</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package cz.jzitnik;
|
||||||
|
|
||||||
|
import atlantafx.base.theme.PrimerDark;
|
||||||
|
import cz.jzitnik.state.StateManager;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.shape.Rectangle;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
public class Main extends Application {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage stage) throws Exception {
|
||||||
|
Application.setUserAgentStylesheet(new PrimerDark().getUserAgentStylesheet());
|
||||||
|
|
||||||
|
// 1. Initialize State Manager
|
||||||
|
// This will find all @State annotated classes inside the cz.jzitnik package and create singletons
|
||||||
|
StateManager.getInstance().initialize("cz.jzitnik");
|
||||||
|
|
||||||
|
// 2. Set up routing container (this is our root)
|
||||||
|
StackPane root = new StackPane();
|
||||||
|
root.setStyle("-fx-background-color: -color-bg-default;");
|
||||||
|
|
||||||
|
// Ensure contents never bleed out and cause weird window scaling
|
||||||
|
Rectangle clip = new Rectangle();
|
||||||
|
clip.widthProperty().bind(root.widthProperty());
|
||||||
|
clip.heightProperty().bind(root.heightProperty());
|
||||||
|
root.setClip(clip);
|
||||||
|
|
||||||
|
// 3. Initialize Router
|
||||||
|
cz.jzitnik.router.Router router = cz.jzitnik.router.Router.getInstance();
|
||||||
|
router.setRootContainer(root);
|
||||||
|
router.registerRoute("/login", cz.jzitnik.controllers.LoginController.class);
|
||||||
|
router.registerRoute("/home", cz.jzitnik.controllers.HomeController.class);
|
||||||
|
|
||||||
|
// Initial screen
|
||||||
|
router.navigate("/login");
|
||||||
|
|
||||||
|
// 4. Display
|
||||||
|
Scene scene = new Scene(root, 800, 600);
|
||||||
|
stage.setTitle("JecnaClient");
|
||||||
|
stage.setScene(scene);
|
||||||
|
stage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package cz.jzitnik.controllers;
|
||||||
|
|
||||||
|
import cz.jzitnik.router.Routable;
|
||||||
|
import cz.jzitnik.router.Route;
|
||||||
|
import cz.jzitnik.router.Router;
|
||||||
|
import cz.jzitnik.state.AppState;
|
||||||
|
import cz.jzitnik.state.InjectState;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Route(fxml = "/home.fxml")
|
||||||
|
public class HomeController implements Routable {
|
||||||
|
|
||||||
|
@InjectState
|
||||||
|
private AppState appState;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label welcomeLabel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNavigate(Map<String, Object> props) {
|
||||||
|
// Because of dependency injection, appState is already fully populated
|
||||||
|
String username = appState.getUsername();
|
||||||
|
if (username == null) username = "User";
|
||||||
|
|
||||||
|
welcomeLabel.setText("Welcome, " + username + "!");
|
||||||
|
System.out.println("Global Auth Token is: " + appState.getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onLogoutClick() {
|
||||||
|
// Clear global state
|
||||||
|
appState.clear();
|
||||||
|
|
||||||
|
// Redirect
|
||||||
|
Router.getInstance().navigate("/login");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package cz.jzitnik.controllers;
|
||||||
|
|
||||||
|
import cz.jzitnik.router.Routable;
|
||||||
|
import cz.jzitnik.router.Route;
|
||||||
|
import cz.jzitnik.router.Router;
|
||||||
|
import cz.jzitnik.state.AppState;
|
||||||
|
import cz.jzitnik.state.InjectState;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.PasswordField;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Route(fxml = "/login.fxml")
|
||||||
|
public class LoginController implements Routable {
|
||||||
|
|
||||||
|
@InjectState
|
||||||
|
private AppState appState;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField usernameField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private PasswordField passwordField;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNavigate(Map<String, Object> props) {
|
||||||
|
// Here you can use props if the screen was opened with them
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onLoginClick() {
|
||||||
|
String username = usernameField.getText();
|
||||||
|
|
||||||
|
// Save the username globally into the app state
|
||||||
|
if (username != null && !username.isEmpty()) {
|
||||||
|
appState.setUsername(username);
|
||||||
|
appState.setToken("dummy_token_123");
|
||||||
|
} else {
|
||||||
|
appState.setUsername("Guest");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to the home screen (no longer need to pass username in props, it's global now!)
|
||||||
|
Router.getInstance().navigate("/home");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package cz.jzitnik.router;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for screen controllers that can receive properties when navigated to.
|
||||||
|
*/
|
||||||
|
public interface Routable {
|
||||||
|
default void onNavigate(Map<String, Object> props) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package cz.jzitnik.router;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to map a controller class to an FXML file.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface Route {
|
||||||
|
String fxml();
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package cz.jzitnik.router;
|
||||||
|
|
||||||
|
import cz.jzitnik.state.StateManager;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scalable API for managing screens.
|
||||||
|
*/
|
||||||
|
public class Router {
|
||||||
|
private static Router instance;
|
||||||
|
private Pane rootContainer;
|
||||||
|
private final Map<String, Class<?>> routes = new HashMap<>();
|
||||||
|
|
||||||
|
private Router() {}
|
||||||
|
|
||||||
|
public static Router getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new Router();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRootContainer(Pane rootContainer) {
|
||||||
|
this.rootContainer = rootContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerRoute(String path, Class<?> controllerClass) {
|
||||||
|
if (!controllerClass.isAnnotationPresent(Route.class)) {
|
||||||
|
throw new IllegalArgumentException("Controller " + controllerClass.getName() + " must be annotated with @Route");
|
||||||
|
}
|
||||||
|
routes.put(path, controllerClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void navigate(String path) {
|
||||||
|
navigate(path, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void navigate(String path, Map<String, Object> props) {
|
||||||
|
if (rootContainer == null) {
|
||||||
|
throw new IllegalStateException("Root container not set in Router.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> controllerClass = routes.get(path);
|
||||||
|
if (controllerClass == null) {
|
||||||
|
throw new IllegalArgumentException("Route not found: " + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Route routeAnnotation = controllerClass.getAnnotation(Route.class);
|
||||||
|
String fxmlPath = routeAnnotation.fxml();
|
||||||
|
|
||||||
|
try {
|
||||||
|
FXMLLoader loader = new FXMLLoader(controllerClass.getResource(fxmlPath));
|
||||||
|
Parent view = loader.load();
|
||||||
|
Object controller = loader.getController();
|
||||||
|
|
||||||
|
// Inject global state into the controller if it uses @InjectState
|
||||||
|
StateManager.getInstance().injectStates(controller);
|
||||||
|
|
||||||
|
if (controller instanceof Routable routable) {
|
||||||
|
routable.onNavigate(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
rootContainer.getChildren().clear();
|
||||||
|
rootContainer.getChildren().add(view);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to load screen for route: " + path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package cz.jzitnik.state;
|
||||||
|
|
||||||
|
@State
|
||||||
|
public class AppState {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
private String token; // Example for some auth token
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLoggedIn() {
|
||||||
|
return username != null && !username.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
this.username = null;
|
||||||
|
this.token = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package cz.jzitnik.state;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects a singleton state object into a controller field.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface InjectState {
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package cz.jzitnik.state;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a class as a managed state object.
|
||||||
|
* The StateManager will instantiate and hold a singleton of this class.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface State {
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package cz.jzitnik.state;
|
||||||
|
|
||||||
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages @State annotated classes, storing them as singletons and injecting them.
|
||||||
|
*/
|
||||||
|
public class StateManager {
|
||||||
|
|
||||||
|
private static StateManager instance;
|
||||||
|
private final Map<Class<?>, Object> states = new HashMap<>();
|
||||||
|
|
||||||
|
private StateManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StateManager getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new StateManager();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans the specified base package for @State annotated classes and instantiates them.
|
||||||
|
* @param basePackage the base package to scan (e.g. "cz.jzitnik")
|
||||||
|
*/
|
||||||
|
public void initialize(String basePackage) {
|
||||||
|
Reflections reflections = new Reflections(basePackage);
|
||||||
|
Set<Class<?>> stateClasses = reflections.getTypesAnnotatedWith(State.class);
|
||||||
|
|
||||||
|
for (Class<?> stateClass : stateClasses) {
|
||||||
|
try {
|
||||||
|
Object stateInstance = stateClass.getDeclaredConstructor().newInstance();
|
||||||
|
states.put(stateClass, stateInstance);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed to instantiate state class: " + stateClass.getName());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects initialized states into fields annotated with @InjectState in the given target object.
|
||||||
|
* @param target the controller or object to inject state into
|
||||||
|
*/
|
||||||
|
public void injectStates(Object target) {
|
||||||
|
if (target == null) return;
|
||||||
|
|
||||||
|
Class<?> targetClass = target.getClass();
|
||||||
|
for (Field field : targetClass.getDeclaredFields()) {
|
||||||
|
if (field.isAnnotationPresent(InjectState.class)) {
|
||||||
|
Class<?> fieldType = field.getType();
|
||||||
|
Object stateInstance = states.get(fieldType);
|
||||||
|
|
||||||
|
if (stateInstance == null) {
|
||||||
|
throw new RuntimeException("No @State found for type: " + fieldType.getName() + " in " + targetClass.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(target, stateInstance);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException("Failed to inject state into field: " + field.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package cz.jzitnik.ui;
|
||||||
|
|
||||||
|
import javafx.animation.Animation;
|
||||||
|
import javafx.animation.KeyFrame;
|
||||||
|
import javafx.animation.KeyValue;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.scene.effect.BlendMode;
|
||||||
|
import javafx.scene.effect.GaussianBlur;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reusable animated background component.
|
||||||
|
*/
|
||||||
|
public class AuroraBackground extends StackPane {
|
||||||
|
|
||||||
|
public AuroraBackground() {
|
||||||
|
// LOWER OPACITY: Dropped from 0.3 to 0.1 for a subtle background feel
|
||||||
|
Color color1 = Color.web("#00FFFF", 0.10); // Cyan
|
||||||
|
Color color2 = Color.web("#FF00FF", 0.10); // Magenta
|
||||||
|
Color color3 = Color.web("#7D3CFF", 0.10); // Deep Purple
|
||||||
|
|
||||||
|
// MASSIVE BLOBS: Increased radius so they fill large screens beautifully
|
||||||
|
Circle blob1 = new Circle(400, color1);
|
||||||
|
Circle blob2 = new Circle(350, color2);
|
||||||
|
Circle blob3 = new Circle(450, color3);
|
||||||
|
|
||||||
|
// HEAVY BLUR: Increased from 100 to 200 for maximum softness
|
||||||
|
GaussianBlur blur = new GaussianBlur(200);
|
||||||
|
blob1.setEffect(blur);
|
||||||
|
blob2.setEffect(blur);
|
||||||
|
blob3.setEffect(blur);
|
||||||
|
|
||||||
|
blob1.setBlendMode(BlendMode.ADD);
|
||||||
|
blob2.setBlendMode(BlendMode.ADD);
|
||||||
|
blob3.setBlendMode(BlendMode.ADD);
|
||||||
|
|
||||||
|
// Because this is a StackPane, all circles start perfectly centered
|
||||||
|
getChildren().addAll(blob1, blob2, blob3);
|
||||||
|
|
||||||
|
// Animate them drifting away from the center using translation
|
||||||
|
setupAnimation(blob1, -150, -100, 150, 100, 20);
|
||||||
|
setupAnimation(blob2, 150, 150, -150, -150, 25);
|
||||||
|
setupAnimation(blob3, 0, -200, 0, 200, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates a node shifting back and forth from a center point.
|
||||||
|
*/
|
||||||
|
private void setupAnimation(Circle node, double startX, double startY, double endX, double endY, int durationSeconds) {
|
||||||
|
Timeline timeline = new Timeline(
|
||||||
|
new KeyFrame(Duration.ZERO,
|
||||||
|
new KeyValue(node.translateXProperty(), startX),
|
||||||
|
new KeyValue(node.translateYProperty(), startY)
|
||||||
|
),
|
||||||
|
new KeyFrame(Duration.seconds(durationSeconds),
|
||||||
|
new KeyValue(node.translateXProperty(), endX),
|
||||||
|
new KeyValue(node.translateYProperty(), endY)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
timeline.setAutoReverse(true);
|
||||||
|
timeline.setCycleCount(Animation.INDEFINITE);
|
||||||
|
timeline.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
|
||||||
|
<!-- Home screen with a solid transparent/default background (NO aurora background here) -->
|
||||||
|
<StackPane xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="cz.jzitnik.controllers.HomeController">
|
||||||
|
|
||||||
|
<VBox alignment="CENTER" spacing="15.0" maxWidth="350.0"
|
||||||
|
style="-fx-background-color: transparent;">
|
||||||
|
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="40.0" left="40.0" right="40.0" top="40.0"/>
|
||||||
|
</padding>
|
||||||
|
|
||||||
|
<Label text="Home Screen" styleClass="title-1"/>
|
||||||
|
<Label fx:id="welcomeLabel" styleClass="text-muted"/>
|
||||||
|
|
||||||
|
<Button text="Logout" onAction="#onLogoutClick" maxWidth="Infinity" styleClass="danger"/>
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
</StackPane>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.PasswordField?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import cz.jzitnik.ui.AuroraBackground?>
|
||||||
|
|
||||||
|
<StackPane xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="cz.jzitnik.controllers.LoginController">
|
||||||
|
|
||||||
|
<!-- Screen Specific Background -->
|
||||||
|
<AuroraBackground />
|
||||||
|
|
||||||
|
<!-- Actual Content -->
|
||||||
|
<VBox alignment="CENTER" spacing="15.0" maxWidth="350.0"
|
||||||
|
style="-fx-background-color: transparent;">
|
||||||
|
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="40.0" left="40.0" right="40.0" top="40.0"/>
|
||||||
|
</padding>
|
||||||
|
|
||||||
|
<VBox alignment="CENTER" spacing="5.0">
|
||||||
|
<Label text="Sign In" styleClass="title-2"/>
|
||||||
|
<Label text="Welcome back to the portal" styleClass="text-muted"/>
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
<Label prefHeight="15.0"/>
|
||||||
|
<TextField fx:id="usernameField" promptText="Username or Email"/>
|
||||||
|
<PasswordField fx:id="passwordField" promptText="Password"/>
|
||||||
|
|
||||||
|
<Button text="Login" onAction="#onLoginClick" maxWidth="Infinity" styleClass="accent"/>
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
</StackPane>
|
||||||
Reference in New Issue
Block a user