diff --git a/Project 1/.gitignore b/Project 1/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5bfb48ed0b155fac605c25bfac13ca2115525da1 --- /dev/null +++ b/Project 1/.gitignore @@ -0,0 +1,20 @@ +contracts/ +.classpath +.project +.history/ +.idea +.jqwik-database +.lib/ +.worksheet +.settings/ +*.iml +*.ipr +*.iws +*.log +project/boot/ +project/plugins/project/ +project/project/ +project/*-shim.sbt +project/target/ +target/ +openjfx/ diff --git a/Project 1/.gitlab-ci.yml b/Project 1/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..4b3553b42ff83c2ef7e7ea37ffa030aaaf554898 --- /dev/null +++ b/Project 1/.gitlab-ci.yml @@ -0,0 +1,13 @@ +image: maven:latest + +stages: + - build + - test + +build: + script: + - mvn compile + +test: + script: + - mvn test \ No newline at end of file diff --git a/Project 1/README.md b/Project 1/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2907fd126e89f04e9141a21287b3d0e75094be52 --- /dev/null +++ b/Project 1/README.md @@ -0,0 +1,39 @@ +# Project description + +Simple template for projects that make use of JavaFX and FXML (Scene Builder). +Requires Java 11 or later. Compatible with +Eclipse and IntelliJ IDEA. Minor issues with Netbeans. Automatically +integrates with Gitlab CI. + +## Installation + +Maven: + +```bash +$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx + +$ cd template-javafx + +$ mvn compile exec:java +``` + +SBT: + +```bash +$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx + +$ cd template-javafx + +$ sbt compile run +``` + +## Further instructions + + * Java platform: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/jvm-platform + * Maven: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/maven-misc + * SBT: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/sbt-misc + +External sources: + + * JavaFX: https://openjfx.io/javadoc/11/ + * Scene Builder: https://docs.gluonhq.com/scenebuilder/ diff --git a/Project 1/build.sbt b/Project 1/build.sbt new file mode 100644 index 0000000000000000000000000000000000000000..e703e9051c964d2e35d6f297a81b241437860648 --- /dev/null +++ b/Project 1/build.sbt @@ -0,0 +1,236 @@ +// Project template + +// Supported operating systems: Windows, Mac, Linux +// Supported JDKs: 8, 10+ + +// Project name +name := "template-javafx" + +// organization name +organization := "fi.utu.tech" + +version := "1.0" + +// project description +description := "JavaFX project template" + +// main class +Compile/mainClass := Some("fi.utu.tech.gui.javafx.Main") + +// force the java version by typing it here (remove the comment) +val force_javaVersion = None // Some(13) + +// force the javafx version by typing it here (remove the comment) +val force_javaFxVersion = None // Some(13) + +val useJavaFX = true + +val useScalaOrScalaFX = true + +// END_OF_SIMPLE_CONFIGURATION +// you can copy the rest for each new project +// --- --- --- + +def fail(msg: String) = { + println("Error :-/") + println + println(msg) + System.exit(1) + null +} + +val detectedJDK = System.getProperty("java.version").replace("-ea","").split('.').dropWhile(_.toInt<8).head.toInt + +val javaVersionNum = force_javaVersion.getOrElse(detectedJDK) + +val javaVersionString = javaVersionNum match { + case 7 => "1.7" + case 8 => "1.8" + case x if x > 8 => x.toString +} + +val lts = 11 +val dev = 13 + +val supported = javaVersionNum match { + case x if x < 8 => fail("Your Java installation is obsolete. Please upgrade to Java " + lts + "LTS") + case 9 => fail("Your Java installation is unsupported and has known issues. Please upgrade to Java " + lts + "LTS") + case x if x < lts => println("Consider upgrading to Java " + lts + " LTS"); true + case x if x > lts && x < dev => println("Consider upgrading to Java " + dev); true + case x if x > dev => println("Unsupported early access version. Consider switching back to Java " + dev); true + case _ => true +} + +javacOptions ++= Seq("-source", javaVersionString, "-target", javaVersionString, "-encoding", "utf8", "-Xlint:unchecked", "-Xlint:deprecation") +javacOptions in doc := Seq("-source", javaVersionString) + +enablePlugins(JShellPlugin) + +compileOrder := CompileOrder.JavaThenScala + +// Enables publishing to maven repo +publishMavenStyle := true + +// Do not append Scala versions to the generated artifacts +crossPaths := false + +// This forbids including Scala related libraries into the dependency +autoScalaLibrary := false + +assemblyMergeStrategy in assembly := { + case PathList("META-INF", xs @ _*) => MergeStrategy.discard + case _ => MergeStrategy.first +} + +// contains libraries provided by utu/ft dep +resolvers += "ftdev" at "https://ftdev.utu.fi/maven2" + +fork in Global := true + +val javaVersion = taskKey[Unit]("Prints the Java version.") + +javaVersion := { println("SBT uses Java SDK located at "+System.getProperty("java.home")) } + +publishTo := Some(Resolver.file("file", new File("/tmp/repository"))) + +val oomkit = "fi.utu.tech" % "oomkit" % "1.15" + +libraryDependencies ++= Seq() + +//// +//// JQWIK / JUNIT configuration +//// + +resolvers in ThisBuild += Resolver.jcenterRepo + +val junit_version = "5.5.2" + +// library dependencies. (orginization name) % (project name) % (version) +libraryDependencies ++= Seq( + "net.aichler" % "jupiter-interface" % JupiterKeys.jupiterVersion.value % Test, + "org.junit.platform" % "junit-platform-commons" % ("1"+junit_version.tail) % Test, + "org.junit.platform" % "junit-platform-runner" % ("1"+junit_version.tail) % Test, + "org.junit.jupiter" % "junit-jupiter-engine" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-api" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-migrationsupport" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-params" % junit_version % Test, + "net.jqwik" % "jqwik" % "1.2.0" % Test, + "org.scalatest" %% "scalatest" % "3.0.8" % Test, +) + +testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-c") + +//// +//// JAVAFX configuration +//// + +val javafx_versions = if (!useJavaFX) (0,"-","-") else (force_javaFxVersion getOrElse javaVersionNum) match { + case 7 => (7, "7", "8.0.181-R13") + case 8 => (8, "8", "8.0.181-R13") + case 10 => (11, "11.0.2", "11-R16") + case x if x>10 => (13, "13", "12.0.2-R18") + case _ => fail("Unsupported Java version for JavaFX") +} + +// JAVA_HOME location +val javaHomeDir = { + val path = try { + if (scala.sys.env("JAVA_HOME").trim.isEmpty) throw new Exception("Empty JAVA_HOME") else scala.sys.env("JAVA_HOME") + } catch { + case _: Throwable => System.getProperty("java.home") // not set -> ask from current JVM + } + + val f = file(path) + if (!f.exists()) fail("The environment variable JAVA_HOME points to a non-existent directory!\nSolution: Edit your system settings (Windows control panel / *nix .bashrc) and fix the JAVA_HOME location.") + f +} + +val osName: SettingKey[String] = SettingKey[String]("osName") + +osName := (System.getProperty("os.name") match { + case n if n.startsWith("Linux") => "linux" + case n if n.startsWith("Mac") => "mac" + case n if n.startsWith("Windows") => "win" + case _ => throw new Exception("Unknown platform!") +}) + +def legacyJavaFX() = { + val searchDirs = Seq( + "/jre/lib/jfxrt.jar", // OpenJDK 7 + "/jre/lib/ext/jfxrt.jar", // OpenJDK 8 + "/lib/ext/jfxrt.jar" // Windows & Oracle Java 8 + ) + + if (detectedJDK > 8) fail(s"Trying to use legacy non-modular JavaFX with a modern JDK [$detectedJDK].\nSolution: Check the line 'val force_javaFxVersion =' in build.sbt.") + + val javaFxJAR = searchDirs.map{ searchDir => file(javaHomeDir + searchDir) }.find{ _.exists() } + + javaFxJAR.getOrElse { + fail(s"Java FX runtime not installed in [${javaHomeDir.toString}]!\nSolution: Install JavaFX or consider upgrading your JDK so that JavaFX can be installed automatically.") + } +} + +val jfx_sdk_version = javafx_versions._2 +val jfx_scalafx_version = javafx_versions._3 + +val javaFxPath = Def.taskKey[File]("OpenJFX fetcher") +javaFxPath := { + val javaFxHome = + try { + val envHome = file(scala.sys.env("JAVAFX_HOME")) + if (envHome.toString.trim.isEmpty) throw new Exception("Empty JAVAFX_HOME") + println("Using OpenJFX from " + envHome) + envHome + } + catch { case _: Throwable => + println("Using local OpenJFX") + baseDirectory.value / "openjfx" + } + + if (!javaFxHome.exists()) java.nio.file.Files.createDirectory(javaFxHome.toPath) + + val jfx_os = osName.value match { + case "linux" => "linux" + case "mac" => "osx" + case "win" => "windows" + } + + val sdkURL = "http://download2.gluonhq.com/openjfx/" + jfx_sdk_version + "/openjfx-" + jfx_sdk_version + "_" + jfx_os + "-x64_bin-sdk.zip" + + try { + val testDir = javaFxHome / "all.ok" + if (!testDir.exists()) { + println("Fetching OpenJFX from "+sdkURL+"..") + IO.unzipURL(new URL(sdkURL), javaFxHome) + java.nio.file.Files.createDirectory(testDir.toPath) + println("Fetching OpenJFX done.") + } else { + println("Using OpenJFX from "+javaFxHome) + } + + javaFxHome + } + catch { + case t: Throwable => fail("Could not load OpenJFX! Reason:" + t.getMessage) + } +} + +val jfxModules = Seq("base","controls","fxml","graphics","media","swing","web") + + +if (!useJavaFX) Seq() else javafx_versions._1 match { + case 7 => + // TODO libraryDependencies + Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX())) + case 8 => + (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" %% "scalafx" % jfx_scalafx_version) else Seq()) ++ + Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX())) + case _ => + Seq( + javaOptions in run ++= Seq( + "--module-path", (javaFxPath.value / ("javafx-sdk-" + jfx_sdk_version) / "lib").toString, + "--add-modules=" + jfxModules.map("javafx."+_).mkString(",")) + ) ++ + (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" % "scalafx_2.13" % jfx_scalafx_version) else Seq()) ++ + jfxModules.map(module => libraryDependencies += "org.openjfx" % ("javafx-"+module) % jfx_sdk_version classifier osName.value) +} diff --git a/Project 1/pom.xml b/Project 1/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..40e1bb8e9bf40fbf9a984a7b5bb3b2ff7177fd54 --- /dev/null +++ b/Project 1/pom.xml @@ -0,0 +1,452 @@ +<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> + + <!-- + + Welcome! + This pom.xml is *THE* file that defines your Maven style Java project. + + Eclipse, IDEA and other development environments with Maven support + or plugins can *import* this project by reading this file. + + It usually contains tons of barely readable configuration. Luckily + this basic pom.xml is somewhat readable. All necessary configuration + for customizing your project if located here on top before the + 'END OF SIMPLE CONFIGURATION' line below. + + Some basics: + + - Maven is a build system for Java/JVM + - the Maven projects define an artifact that has a three part id: + groupId - artifactId - version + + - For example, this project is called: + fi.utu.tech - oomkit - 1.15 + + - The group id is usually a web domain in reverse order. + + - You can use these id parts to search for projects at + https://search.maven.org/ + + - If your version is not final and you have plans to modify the + source code at some point, please use a version id that ends with + -SNAPSHOT, e.g 1.0-SNAPSHOT. Maven may cache the project jar + and refuse to overwrite old cached versions with new ones unless + you remember this convention. + + - This file supports + compiling the project with 'mvn compile' (see target/) + cleaning the class files with 'mvn clean' + executing the main routine with 'mvn exec:java' + executing the unit tests with 'mvn test' + packaging the application with 'mvn package' (see target/) + + - the Maven project structure is as follows: + https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html + + --> + + <!-- ==== START OF SIMPLE CONFIGURATION ==== --> + + <!-- the three parts of the artifact name --> + <groupId>fi.utu.tech</groupId> + <artifactId>template-javafx</artifactId> + <version>1.0</version> + + <!-- additional information about the project --> + <name>JavaFX project template</name> + <url>https://gitlab.utu.fi/tech/education/gui/template-javafx</url> + + <packaging>jar</packaging> + + <!-- HINT: More configuration here! --> + <properties> + <!-- Configures this project to use 'fi.utu.tech.AppMain' as its main class --> + <project.mainclass>fi.utu.tech.gui.javafx.Main</project.mainclass> + + + <!-- Don't touch these unless you know what you're doing! + + For example, the source encoding should always be utf-8. + You're probably doing something stupid if you think it + should be a 8-bit code page in 2019. --> + <jdk.version>11</jdk.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <jqwik.version>1.2.0</jqwik.version> + <junit.version>5.5.2</junit.version> + <junitplatform.version>1.5.2</junitplatform.version> + <javafx.version>13</javafx.version> + </properties> + + <!-- ==== END OF SIMPLE CONFIGURATION ==== --> + + <repositories> + <repository> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>central</id> + <name>Central Repository</name> + <url>https://repo.maven.apache.org/maven2</url> + </repository> + <repository> + <id>jcenter</id> + <name>jcenter</name> + <url>https://jcenter.bintray.com/</url> + </repository> + <!-- UTU repository --> + <repository> + <id>ftdev</id> + <name>ftdev</name> + <url>https://ftdev.utu.fi/maven2</url> + </repository> + </repositories> + + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-commons</artifactId> + <version>${junitplatform.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.jqwik</groupId> + <artifactId>jqwik</artifactId> + <version>${jqwik.version}</version> + <scope>test</scope> + </dependency><!-- + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-migrationsupport</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-runner</artifactId> + <version>${junitplatform.version}</version> + <scope>test</scope> + </dependency> --> + + <!-- JavaFX (remove if not needed to speed up dep downloads)--> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-base</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-controls</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-fxml</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-graphics</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-media</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-swing</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-web</artifactId> + <version>${javafx.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- Make a 'fat' jar, that is, jar that contains all its dependencies and runs as is. + See: https://stackoverflow.com/a/57691362 --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.3</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>module-info.class</exclude> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + </excludes> + </filter> + </filters> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>${project.mainclass}</mainClass> + </transformer> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + + <!-- Run this app with exec:java --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.6.0</version> + <executions> + <execution> + <goals> + <goal>java</goal> + </goals> + </execution> + </executions> + <configuration> + <mainClass>${project.mainclass}</mainClass> + <arguments> + <argument>arg1</argument> + <argument>arg2</argument> + </arguments> + </configuration> + </plugin> + + <!-- Make the packaged jar executable --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <!-- DO NOT include log4j.properties file in your Jar --> + <excludes> + <exclude>**/log4j.properties</exclude> + </excludes> + <archive> + <manifest> + <!-- Jar file entry point --> + <mainClass>${project.mainclass}</mainClass> + </manifest> + </archive> + </configuration> + </plugin> + + <!-- download source code in Eclipse, best practice --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-eclipse-plugin</artifactId> + <version>2.10</version> + <configuration> + <downloadSources>true</downloadSources> + <downloadJavadocs>false</downloadJavadocs> + </configuration> + </plugin> + + <!-- JDK source/target versions --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <source>${jdk.version}</source> + <target>${jdk.version}</target> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.1.1</version> + <executions> + <execution> + <id>attach-javadocs</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + <configuration> + <tags> + <tag> + <name>toDo</name> + <placement>a</placement> + <head>To do:</head> + </tag> + <tag> + <name>classInvariant</name> + <placement>t</placement> + <head>Class invariant:</head> + </tag> + <tag> + <name>classInvariantProtected</name> + <placement>t</placement> + <head>Protected class invariant:</head> + </tag> + <tag> + <name>classInvariantPrivate</name> + <placement>t</placement> + <head>Private class invariant:</head> + </tag> + <tag> + <name>abstractionFunction</name> + <placement>t</placement> + <head>Abstraction function:</head> + </tag> + <tag> + <name>pre</name> + <placement>cm</placement> + <head>Precondition:</head> + </tag> + <tag> + <name>post</name> + <placement>cm</placement> + <head>Postcondition:</head> + </tag> + <tag> + <name>postProtected</name> + <placement>cm</placement> + <head>Protected postcondition:</head> + </tag> + <tag> + <name>postPrivate</name> + <placement>cm</placement> + <head>Private postcondition:</head> + </tag> + <tag> + <name>time</name> + <placement>cmf</placement> + <head>Time complexity:</head> + </tag> + <tag> + <name>space</name> + <placement>cmf</placement> + <head>Space complexity:</head> + </tag> + <tag> + <name>correspondence</name> + <placement>a</placement> + <head>Correspondence:</head> + </tag> + <tag> + <name>download</name> + <placement>a</placement> + <head>Download:</head> + </tag> + </tags> + <show>protected</show> + <failOnError>false</failOnError> + <sourceFileExcludes> + <sourceFileExclude>**/module-info.java</sourceFileExclude> + </sourceFileExcludes> + </configuration> + </plugin> + + <!-- JUnit & JQwik test integration --> + + <!-- junit-platform-maven-plugin: supports modular tests + maven-surefire-plugin: non-modular tests + + Modular testing works via command line mvn, but is + still broken in Eclipse due to this + https://bugs.eclipse.org/bugs/show_bug.cgi?id=520667 + --> + <plugin> + <groupId>de.sormuras.junit</groupId> + <artifactId>junit-platform-maven-plugin</artifactId> + <version>1.0.0-M5</version> + <extensions>true</extensions> + <configuration> + <executor>JAVA</executor> + </configuration> + </plugin> + + <!-- + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.0.0-M3</version> + </plugin> + <plugin> + <artifactId>maven-failsafe-plugin</artifactId> + <version>3.0.0-M3</version> + </plugin> --> + + <!-- javafx:jlink: + https://github.com/openjfx/javafx-maven-plugin + Use 'mvn package' instead if you don't need jlink. + + <plugin> + <groupId>org.openjfx</groupId> + <artifactId>javafx-maven-plugin</artifactId> + <version>0.0.3</version> + <configuration> + <mainClass>${project.mainclass}</mainClass> + </configuration> + </plugin> --> + + </plugins> + <extensions> + <!-- Enables the use of SSH for deployments --> + <extension> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-ssh-external</artifactId> + <version>3.3.3</version> + </extension> + <extension> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-ssh</artifactId> + <version>3.3.3</version> + </extension> + </extensions> + </build> + <distributionManagement> + <repository> + <id>ftdev</id> + <name>UTU tech ftdev repository</name> + <url>scp://localhost:2222/var/www/maven2</url> + </repository> + </distributionManagement> +</project> diff --git a/Project 1/project/build.properties b/Project 1/project/build.properties new file mode 100644 index 0000000000000000000000000000000000000000..6adcdc753fdca1b4b851d8b75426dee24044bf6d --- /dev/null +++ b/Project 1/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.3.3 diff --git a/Project 1/project/plugins.sbt b/Project 1/project/plugins.sbt new file mode 100644 index 0000000000000000000000000000000000000000..37ca261463b6a1800a62a0d5a5279fbb22915638 --- /dev/null +++ b/Project 1/project/plugins.sbt @@ -0,0 +1,7 @@ +resolvers += Resolver.jcenterRepo + +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") +addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3") +addSbtPlugin("com.github.xuwei-k" % "sbt-jshell" % "0.1.2") +//addSbtPlugin("org.xerial.sbt" % "sbt-sql-sqlite" % "0.8") +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.0") diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java new file mode 100644 index 0000000000000000000000000000000000000000..a4d0180639373080225fee7cfa3de4b85d5f05f3 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java @@ -0,0 +1,13 @@ +package fi.utu.tech.gui.javafx; + +import javafx.scene.control.Alert; + +public class Dialogs { + public static void warning(String title, String header, String content) { + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle(title); + alert.setHeaderText(header); + alert.setContentText(content); + alert.showAndWait(); + } +} \ No newline at end of file diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java new file mode 100644 index 0000000000000000000000000000000000000000..7777d6696e7c286beead79605114a62997be9867 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java @@ -0,0 +1,63 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Platform; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; + +import java.util.function.Supplier; + +public class FXMLController { + private IntegerProperty clicks = new SimpleIntegerProperty(0); + + private Supplier<Parent> supplier; + + @FXML + private Label clicksLabel; + + protected void updateClicks() { + if (!clicksLabel.textProperty().isBound()) + clicksLabel.textProperty().bind(clicks.asString().concat(" clicks.")); + + clicks.setValue(clicks.getValue() + 1); + } + + protected void setLabel(String text) { + if (!clicksLabel.textProperty().isBound()) + clicksLabel.setText(text); + } + + @FXML + protected void handleDialogButton(ActionEvent event) { + updateClicks(); + Dialogs.warning("Example dialog", "Content header", "Content"); + } + + @FXML + protected void handleExitButton(ActionEvent event) { + System.out.println("Closing app."); + Platform.exit(); + } + + @FXML + protected void handleWindowButton(ActionEvent event) { + updateClicks(); + Scene other = new Scene(supplier.get()); + Stage otherStage = new Stage(); + otherStage.setScene(other); + otherStage.show(); + } + + protected void setWindowFactory(Supplier<Parent> supplier) { + this.supplier = supplier; + } + + public void initialize() { + System.out.println("2-A"); + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController2.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController2.java new file mode 100644 index 0000000000000000000000000000000000000000..11ce009829cb4914c8324600b306da95237c7c07 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController2.java @@ -0,0 +1,83 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Platform; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; + +import java.util.function.Supplier; + +public class FXMLController2 { + private IntegerProperty clicks = new SimpleIntegerProperty(0); + + private Supplier<Parent> supplier; + private Parent parent; + private int ikkunoita_avattu = 0; + private Scene other; + private Stage otherStage; + + + private Label clicksLabel; + + protected void updateClicks() { + if (!clicksLabel.textProperty().isBound()) + clicksLabel.textProperty().bind(clicks.asString().concat(" clicks.")); + + clicks.setValue(clicks.getValue() + 1); + } + + protected void setLabel(Label l) {clicksLabel = l;} + + + protected void setLabel(String text) { + if (!clicksLabel.textProperty().isBound()) + clicksLabel.setText(text); + } + + protected void handleDialogButton(ActionEvent event) { + updateClicks(); + Dialogs.warning("Example dialog", "Content header", "Content"); + } + + + protected void handleExitButton(ActionEvent event) { + System.out.println("Closing app."); + Platform.exit(); + } + + protected void handleWindowButton(ActionEvent event) { + if(ikkunoita_avattu < 1) { + setLabel("Welcome!"); + updateClicks(); + ResourceLoader<Parent, FXMLController2> loader = new ResourceLoader<>("otherwindow.fxml"); + other = new Scene(loader.root); + otherStage = new Stage(); + otherStage.setScene(other); + otherStage.show(); + System.out.println("Uusi ikkuna avattu"); + ikkunoita_avattu += 1; + } + else if(otherStage.isShowing()){ + otherStage.hide(); + } + else if (!otherStage.isShowing() || !otherStage.isFocused()) { + otherStage.show(); + otherStage.toFront(); + } + } + + protected void setWindowFactory(Supplier<Parent> supplier) { + this.supplier = supplier; + } + + protected void setParent(Parent p) {parent = p;System.out.println(parent + " parent asetettu");} + + public void initialize() { + System.out.println("2-A"); + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/Main.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..f3f36884092e60cce6b4089131d57c7be407ddaf --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Main.java @@ -0,0 +1,29 @@ +package fi.utu.tech.gui.javafx; + +public class Main { + /** + * The main() method is ignored in correctly deployed JavaFX application. + * main() serves only as fallback in case the application can not be + * launched through deployment artifacts, e.g., in IDEs with limited FX + * support. NetBeans ignores main(). + * + * @param args the command line arguments + */ + public static void main(String[] args) { + System.out.println("Stage A"); + if (args.length == 1 && args[0].equals("--test")) return; + + int exercise = 1; + if (args.length == 1) exercise = Integer.parseInt(args[0]); + switch(exercise) { + case 1: + case 2: + case 3: + case 4: MainApp.launch(MainApp4.class, args); break; + case 5: MainApp.launch(MainApp5.class, args); break; + case 10: MainApp.launch(MainApp10.class, args); break; + default: MainApp.launch(MainApp.class, args); break; + } + } + +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp.java new file mode 100644 index 0000000000000000000000000000000000000000..6dc8ba36d119ed7103cff242ce1cb5da8d0e99a1 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new OtherWindow(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp10.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp10.java new file mode 100644 index 0000000000000000000000000000000000000000..3875e03e085c1d98c77a35c01b8b90c2a89a4490 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp10.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp10 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new OtherWindow10(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java new file mode 100644 index 0000000000000000000000000000000000000000..3833d30dd5740f188e84d8cad62179d88490eaaa --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp4 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new OtherWindow4(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java new file mode 100644 index 0000000000000000000000000000000000000000..921f7d7a79c5ed5855d97d728ee84d85d34a43ad --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java @@ -0,0 +1,62 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp5 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new Teht_5_ikkuna(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + + //Teht_5_ikkuna loader = new Teht_5_ikkuna(); + + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java new file mode 100644 index 0000000000000000000000000000000000000000..3f644487c5d3f683f77bde383e689e2d4c5d83db --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java @@ -0,0 +1,47 @@ +package fi.utu.tech.gui.javafx; + +import java.io.IOException; + +import javafx.fxml.FXMLLoader; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.layout.StackPane; + + +public class OtherWindow extends BorderPane { + public OtherWindow() { + Circle c = new Circle(200, Color.NAVY); + setLeft(new ImageView(ResourceLoader.image("hmm.png"))); + + Label terve = new Label("Terve"); + setAlignment(terve,Pos.CENTER); + setTop(terve); + + Label maailma = new Label("Maailma"); + + StackPane stackPane = new StackPane(); + stackPane.getChildren().addAll(c, maailma); + setCenter(stackPane); + + TextField textField1 = new TextField(); + TextField textField2 = new TextField(); + TextField textField3 = new TextField(); + HBox hBox = new HBox(textField1,textField2,textField3); + setBottom(hBox); + + + + + + + + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow10.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow10.java new file mode 100644 index 0000000000000000000000000000000000000000..158757f90a822bd89a15f169554bb59c9150af51 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow10.java @@ -0,0 +1,48 @@ +package fi.utu.tech.gui.javafx; + +import java.io.IOException; + +import javafx.fxml.FXMLLoader; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.layout.StackPane; + + +public class OtherWindow10 extends BorderPane { + public OtherWindow10() { + Circle c = new Circle(200, Color.NAVY); + setLeft(new ImageView(ResourceLoader.image("hmm.png"))); + + Label terve = new Label("Terve"); + setAlignment(terve,Pos.CENTER); + setTop(terve); + + Label maailma = new Label("Maailma"); + + StackPane stackPane = new StackPane(); + stackPane.getChildren().addAll(c, maailma); + setCenter(stackPane); + + TextField textField1 = new TextField(); + TextField textField2 = new TextField(); + TextField textField3 = new TextField(); + HBox hBox = new HBox(textField1,textField2,textField3); + setBottom(hBox); + + + textField1.textProperty().bindBidirectional(textField2.textProperty()); + textField2.textProperty().bindBidirectional(textField3.textProperty()); + + + + + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow4.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow4.java new file mode 100644 index 0000000000000000000000000000000000000000..58a71d1e9da4eef5a2a9b952cb4c35b897103945 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow4.java @@ -0,0 +1,50 @@ +package fi.utu.tech.gui.javafx; + +import java.io.IOException; + +import javafx.fxml.FXMLLoader; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.layout.StackPane; + + +public class OtherWindow4 extends BorderPane { + public OtherWindow4() { + + getStylesheets().add(getClass().getResource("styles2.css").toExternalForm()); + + Circle c = new Circle(200, Color.NAVY); + setLeft(new ImageView(ResourceLoader.image("hmm.png"))); + + Label terve = new Label("Terve"); + setAlignment(terve,Pos.CENTER); + setTop(terve); + + Label maailma = new Label("Maailma"); + + StackPane stackPane = new StackPane(); + stackPane.getChildren().addAll(c, maailma); + setCenter(stackPane); + + TextField textField1 = new TextField(); + TextField textField2 = new TextField(); + TextField textField3 = new TextField(); + HBox hBox = new HBox(textField1,textField2,textField3); + setBottom(hBox); + + + + + + + + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..f917bd339f01757b5c81f9a4252f1adf2d3a9da4 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java @@ -0,0 +1,42 @@ +package fi.utu.tech.gui.javafx; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; + +public class ResourceLoader<N extends Parent, C> { + protected final N root; + protected final C controller; + + public ResourceLoader(String contentPath) { + N root_ = null; + C controller_ = null; + try { + // determines where to look for the resources (the root path) + Class resourceRootClass = getClass(); + + FXMLLoader loader = new FXMLLoader(resourceRootClass.getResource(contentPath)); + root_ = loader.load(); + controller_ = loader.getController(); + System.out.println("DEBUG: " + contentPath + " loaded."); + } catch (Exception e) { + Dialogs.warning( + "Internal error", + "Could not open FXML file: " + contentPath, + "Reason: " + e.getMessage() + ); + System.exit(1); + } + root = root_; + controller = controller_; + } + + // finds images both outside and inside jars + public static String image(String fileName) { + return ResourceLoader.class.getResource(fileName).toExternalForm(); + } + + // finds stylesheets both outside and inside jars + public static String stylesheet(String fileName) { + return ResourceLoader.class.getResource(fileName).toExternalForm(); + } +} diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/Teht_5_ikkuna.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Teht_5_ikkuna.java new file mode 100644 index 0000000000000000000000000000000000000000..30402f333f5e15549cb69f223fc68636d774ced3 --- /dev/null +++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Teht_5_ikkuna.java @@ -0,0 +1,80 @@ +package fi.utu.tech.gui.javafx; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.VBox; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.text.Font; + +public class Teht_5_ikkuna extends BorderPane{ + + Label label2; + + public Teht_5_ikkuna(){ + + setLeft(new ImageView(ResourceLoader.image("hmm.png"))); + + FXMLController2 ctrl = new FXMLController2(); + BorderPane bp = new BorderPane(); + setAlignment(bp, Pos.CENTER); + bp.setPadding(new Insets(16, 16, 16, 16)); + //setPrefHeight(bp); + //setMargin(bp, new Insets(16.0, 16.0, 16.0, 16.0)); + bp.setTop(new Label("What should we do?")); + Button button1 = new Button("Open a new window"); + Button button2 = new Button("Display Dialog"); + Button button3 = new Button("Exit"); + Label label1 = new Label("Loading..."); + + ctrl.setLabel(label1); + label2 = new Label("Welcome!"); + + EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() { + public void handle(ActionEvent e) + { + ctrl.handleWindowButton(e); + } + }; + EventHandler<ActionEvent> event2 = new EventHandler<ActionEvent>() { + public void handle(ActionEvent e) + { + ctrl.handleDialogButton(e); + } + }; + EventHandler<ActionEvent> event3 = new EventHandler<ActionEvent>() { + public void handle(ActionEvent e) + { + ctrl.handleExitButton(e); + } + }; + button1.setOnAction(event); + button2.setOnAction(event2); + button3.setOnAction(event3); + + button1.setPadding(new Insets(5, 110, 5, 110)); + button2.setPadding(new Insets(5, 122, 5, 122)); + button3.setPadding(new Insets(5, 154, 5, 154)); + + VBox vbox = new VBox(button1, button2, button3, label1); + vbox.setAlignment(Pos.CENTER); + vbox.setPadding(new Insets(16, 16, 16, 16)); + + Label otsikko = new Label("What should we do?"); + bp.setTop(otsikko); + bp.setAlignment(otsikko, Pos.CENTER); + bp.setMargin(otsikko,new Insets(15, 0, 15, 0)); + otsikko.setFont(new Font(28)); + + bp.setCenter(vbox); + setAlignment(bp ,Pos.CENTER); + bp.setMargin(vbox,new Insets(5, 40, 5, 40)); + + setCenter(bp); + } + +} \ No newline at end of file diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/hmm.png b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/hmm.png new file mode 100644 index 0000000000000000000000000000000000000000..25e8eb650f6752e716570cfb3c590bf09dff41ee Binary files /dev/null and b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/hmm.png differ diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/otherwindow.fxml b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/otherwindow.fxml new file mode 100644 index 0000000000000000000000000000000000000000..cfaf34b85a6c38bde7658f287deff1f27f9aa521 --- /dev/null +++ b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/otherwindow.fxml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.TextField?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.BorderPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.StackPane?> +<?import javafx.scene.shape.Circle?> + + +<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="550.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.FXMLController2"> + <left> + <ImageView fitHeight="379.0" fitWidth="163.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@hmm.png" /> + </image> + <BorderPane.margin> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </BorderPane.margin> + </ImageView> + </left> + <top> + <Label text="Terve" BorderPane.alignment="CENTER" /> + </top> + <center> + <StackPane prefHeight="358.0" prefWidth="388.0" BorderPane.alignment="CENTER"> + <children> + <Circle fill="#0e166f" radius="178.0" smooth="false" stroke="BLACK" strokeType="INSIDE" /> + <Label text="Maailma" /> + </children> + </StackPane> + </center> + <bottom> + <HBox alignment="BOTTOM_RIGHT" prefHeight="14.0" prefWidth="600.0" BorderPane.alignment="CENTER"> + <children> + <TextField prefHeight="25.0" prefWidth="103.0" /> + <TextField prefHeight="25.0" prefWidth="108.0" /> + <TextField prefHeight="25.0" prefWidth="113.0" /> + </children> + </HBox> + </bottom> +</BorderPane> diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml new file mode 100644 index 0000000000000000000000000000000000000000..434e233d182a2a3205228268795bbedbb6104185 --- /dev/null +++ b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.BorderPane?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> + +<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.FXMLController"> + <left> + <ImageView fitHeight="368.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@hmm.png" /> + </image> + <BorderPane.margin> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </BorderPane.margin> + </ImageView> + </left> + <center> + <BorderPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER"> + <padding> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </padding> + <center> + <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="16.0" BorderPane.alignment="CENTER"> + <padding> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </padding> + <children> + <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleWindowButton" text="Open a new window" /> + <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleDialogButton" text="Display a dialog" /> + <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleExitButton" text="Exit" /> + <Label fx:id="clicksLabel" text="Loading..." /> + </children></VBox> + </center> + <top> + <Label text="What should we do?" BorderPane.alignment="CENTER"> + <font> + <Font size="26.0" /> + </font></Label> + </top> + </BorderPane> + </center> +</BorderPane> diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles.css b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..3ab643a3ea2d9be4b2f210cd57132b6fc8569a82 --- /dev/null +++ b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles.css @@ -0,0 +1,3 @@ +.button { + -fx-font-weight: bold; +} diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles2.css b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles2.css new file mode 100644 index 0000000000000000000000000000000000000000..6444aafc9f6d8834244b305e5defcf52c1acb37c --- /dev/null +++ b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles2.css @@ -0,0 +1,7 @@ +.root { + -fx-font-weight: bold; + -fx-background-color: black; +} +.root .label { + -fx-text-fill: white; +} diff --git a/Project 2/.gitignore b/Project 2/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2b186553c37a0083b6357ae6d311b05e880e4052 --- /dev/null +++ b/Project 2/.gitignore @@ -0,0 +1,21 @@ +bin/ +contracts/ +.classpath +.project +.history/ +.idea +.jqwik-database +.lib/ +.worksheet +.settings/ +*.iml +*.ipr +*.iws +*.log +project/boot/ +project/plugins/project/ +project/project/ +project/*-shim.sbt +project/target/ +target/ +openjfx/ diff --git a/Project 2/.gitlab-ci.yml b/Project 2/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..4b3553b42ff83c2ef7e7ea37ffa030aaaf554898 --- /dev/null +++ b/Project 2/.gitlab-ci.yml @@ -0,0 +1,13 @@ +image: maven:latest + +stages: + - build + - test + +build: + script: + - mvn compile + +test: + script: + - mvn test \ No newline at end of file diff --git a/Project 2/README.md b/Project 2/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2907fd126e89f04e9141a21287b3d0e75094be52 --- /dev/null +++ b/Project 2/README.md @@ -0,0 +1,39 @@ +# Project description + +Simple template for projects that make use of JavaFX and FXML (Scene Builder). +Requires Java 11 or later. Compatible with +Eclipse and IntelliJ IDEA. Minor issues with Netbeans. Automatically +integrates with Gitlab CI. + +## Installation + +Maven: + +```bash +$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx + +$ cd template-javafx + +$ mvn compile exec:java +``` + +SBT: + +```bash +$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx + +$ cd template-javafx + +$ sbt compile run +``` + +## Further instructions + + * Java platform: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/jvm-platform + * Maven: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/maven-misc + * SBT: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/sbt-misc + +External sources: + + * JavaFX: https://openjfx.io/javadoc/11/ + * Scene Builder: https://docs.gluonhq.com/scenebuilder/ diff --git a/Project 2/build.sbt b/Project 2/build.sbt new file mode 100644 index 0000000000000000000000000000000000000000..e703e9051c964d2e35d6f297a81b241437860648 --- /dev/null +++ b/Project 2/build.sbt @@ -0,0 +1,236 @@ +// Project template + +// Supported operating systems: Windows, Mac, Linux +// Supported JDKs: 8, 10+ + +// Project name +name := "template-javafx" + +// organization name +organization := "fi.utu.tech" + +version := "1.0" + +// project description +description := "JavaFX project template" + +// main class +Compile/mainClass := Some("fi.utu.tech.gui.javafx.Main") + +// force the java version by typing it here (remove the comment) +val force_javaVersion = None // Some(13) + +// force the javafx version by typing it here (remove the comment) +val force_javaFxVersion = None // Some(13) + +val useJavaFX = true + +val useScalaOrScalaFX = true + +// END_OF_SIMPLE_CONFIGURATION +// you can copy the rest for each new project +// --- --- --- + +def fail(msg: String) = { + println("Error :-/") + println + println(msg) + System.exit(1) + null +} + +val detectedJDK = System.getProperty("java.version").replace("-ea","").split('.').dropWhile(_.toInt<8).head.toInt + +val javaVersionNum = force_javaVersion.getOrElse(detectedJDK) + +val javaVersionString = javaVersionNum match { + case 7 => "1.7" + case 8 => "1.8" + case x if x > 8 => x.toString +} + +val lts = 11 +val dev = 13 + +val supported = javaVersionNum match { + case x if x < 8 => fail("Your Java installation is obsolete. Please upgrade to Java " + lts + "LTS") + case 9 => fail("Your Java installation is unsupported and has known issues. Please upgrade to Java " + lts + "LTS") + case x if x < lts => println("Consider upgrading to Java " + lts + " LTS"); true + case x if x > lts && x < dev => println("Consider upgrading to Java " + dev); true + case x if x > dev => println("Unsupported early access version. Consider switching back to Java " + dev); true + case _ => true +} + +javacOptions ++= Seq("-source", javaVersionString, "-target", javaVersionString, "-encoding", "utf8", "-Xlint:unchecked", "-Xlint:deprecation") +javacOptions in doc := Seq("-source", javaVersionString) + +enablePlugins(JShellPlugin) + +compileOrder := CompileOrder.JavaThenScala + +// Enables publishing to maven repo +publishMavenStyle := true + +// Do not append Scala versions to the generated artifacts +crossPaths := false + +// This forbids including Scala related libraries into the dependency +autoScalaLibrary := false + +assemblyMergeStrategy in assembly := { + case PathList("META-INF", xs @ _*) => MergeStrategy.discard + case _ => MergeStrategy.first +} + +// contains libraries provided by utu/ft dep +resolvers += "ftdev" at "https://ftdev.utu.fi/maven2" + +fork in Global := true + +val javaVersion = taskKey[Unit]("Prints the Java version.") + +javaVersion := { println("SBT uses Java SDK located at "+System.getProperty("java.home")) } + +publishTo := Some(Resolver.file("file", new File("/tmp/repository"))) + +val oomkit = "fi.utu.tech" % "oomkit" % "1.15" + +libraryDependencies ++= Seq() + +//// +//// JQWIK / JUNIT configuration +//// + +resolvers in ThisBuild += Resolver.jcenterRepo + +val junit_version = "5.5.2" + +// library dependencies. (orginization name) % (project name) % (version) +libraryDependencies ++= Seq( + "net.aichler" % "jupiter-interface" % JupiterKeys.jupiterVersion.value % Test, + "org.junit.platform" % "junit-platform-commons" % ("1"+junit_version.tail) % Test, + "org.junit.platform" % "junit-platform-runner" % ("1"+junit_version.tail) % Test, + "org.junit.jupiter" % "junit-jupiter-engine" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-api" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-migrationsupport" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-params" % junit_version % Test, + "net.jqwik" % "jqwik" % "1.2.0" % Test, + "org.scalatest" %% "scalatest" % "3.0.8" % Test, +) + +testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-c") + +//// +//// JAVAFX configuration +//// + +val javafx_versions = if (!useJavaFX) (0,"-","-") else (force_javaFxVersion getOrElse javaVersionNum) match { + case 7 => (7, "7", "8.0.181-R13") + case 8 => (8, "8", "8.0.181-R13") + case 10 => (11, "11.0.2", "11-R16") + case x if x>10 => (13, "13", "12.0.2-R18") + case _ => fail("Unsupported Java version for JavaFX") +} + +// JAVA_HOME location +val javaHomeDir = { + val path = try { + if (scala.sys.env("JAVA_HOME").trim.isEmpty) throw new Exception("Empty JAVA_HOME") else scala.sys.env("JAVA_HOME") + } catch { + case _: Throwable => System.getProperty("java.home") // not set -> ask from current JVM + } + + val f = file(path) + if (!f.exists()) fail("The environment variable JAVA_HOME points to a non-existent directory!\nSolution: Edit your system settings (Windows control panel / *nix .bashrc) and fix the JAVA_HOME location.") + f +} + +val osName: SettingKey[String] = SettingKey[String]("osName") + +osName := (System.getProperty("os.name") match { + case n if n.startsWith("Linux") => "linux" + case n if n.startsWith("Mac") => "mac" + case n if n.startsWith("Windows") => "win" + case _ => throw new Exception("Unknown platform!") +}) + +def legacyJavaFX() = { + val searchDirs = Seq( + "/jre/lib/jfxrt.jar", // OpenJDK 7 + "/jre/lib/ext/jfxrt.jar", // OpenJDK 8 + "/lib/ext/jfxrt.jar" // Windows & Oracle Java 8 + ) + + if (detectedJDK > 8) fail(s"Trying to use legacy non-modular JavaFX with a modern JDK [$detectedJDK].\nSolution: Check the line 'val force_javaFxVersion =' in build.sbt.") + + val javaFxJAR = searchDirs.map{ searchDir => file(javaHomeDir + searchDir) }.find{ _.exists() } + + javaFxJAR.getOrElse { + fail(s"Java FX runtime not installed in [${javaHomeDir.toString}]!\nSolution: Install JavaFX or consider upgrading your JDK so that JavaFX can be installed automatically.") + } +} + +val jfx_sdk_version = javafx_versions._2 +val jfx_scalafx_version = javafx_versions._3 + +val javaFxPath = Def.taskKey[File]("OpenJFX fetcher") +javaFxPath := { + val javaFxHome = + try { + val envHome = file(scala.sys.env("JAVAFX_HOME")) + if (envHome.toString.trim.isEmpty) throw new Exception("Empty JAVAFX_HOME") + println("Using OpenJFX from " + envHome) + envHome + } + catch { case _: Throwable => + println("Using local OpenJFX") + baseDirectory.value / "openjfx" + } + + if (!javaFxHome.exists()) java.nio.file.Files.createDirectory(javaFxHome.toPath) + + val jfx_os = osName.value match { + case "linux" => "linux" + case "mac" => "osx" + case "win" => "windows" + } + + val sdkURL = "http://download2.gluonhq.com/openjfx/" + jfx_sdk_version + "/openjfx-" + jfx_sdk_version + "_" + jfx_os + "-x64_bin-sdk.zip" + + try { + val testDir = javaFxHome / "all.ok" + if (!testDir.exists()) { + println("Fetching OpenJFX from "+sdkURL+"..") + IO.unzipURL(new URL(sdkURL), javaFxHome) + java.nio.file.Files.createDirectory(testDir.toPath) + println("Fetching OpenJFX done.") + } else { + println("Using OpenJFX from "+javaFxHome) + } + + javaFxHome + } + catch { + case t: Throwable => fail("Could not load OpenJFX! Reason:" + t.getMessage) + } +} + +val jfxModules = Seq("base","controls","fxml","graphics","media","swing","web") + + +if (!useJavaFX) Seq() else javafx_versions._1 match { + case 7 => + // TODO libraryDependencies + Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX())) + case 8 => + (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" %% "scalafx" % jfx_scalafx_version) else Seq()) ++ + Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX())) + case _ => + Seq( + javaOptions in run ++= Seq( + "--module-path", (javaFxPath.value / ("javafx-sdk-" + jfx_sdk_version) / "lib").toString, + "--add-modules=" + jfxModules.map("javafx."+_).mkString(",")) + ) ++ + (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" % "scalafx_2.13" % jfx_scalafx_version) else Seq()) ++ + jfxModules.map(module => libraryDependencies += "org.openjfx" % ("javafx-"+module) % jfx_sdk_version classifier osName.value) +} diff --git a/Project 2/pom.xml b/Project 2/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..40e1bb8e9bf40fbf9a984a7b5bb3b2ff7177fd54 --- /dev/null +++ b/Project 2/pom.xml @@ -0,0 +1,452 @@ +<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> + + <!-- + + Welcome! + This pom.xml is *THE* file that defines your Maven style Java project. + + Eclipse, IDEA and other development environments with Maven support + or plugins can *import* this project by reading this file. + + It usually contains tons of barely readable configuration. Luckily + this basic pom.xml is somewhat readable. All necessary configuration + for customizing your project if located here on top before the + 'END OF SIMPLE CONFIGURATION' line below. + + Some basics: + + - Maven is a build system for Java/JVM + - the Maven projects define an artifact that has a three part id: + groupId - artifactId - version + + - For example, this project is called: + fi.utu.tech - oomkit - 1.15 + + - The group id is usually a web domain in reverse order. + + - You can use these id parts to search for projects at + https://search.maven.org/ + + - If your version is not final and you have plans to modify the + source code at some point, please use a version id that ends with + -SNAPSHOT, e.g 1.0-SNAPSHOT. Maven may cache the project jar + and refuse to overwrite old cached versions with new ones unless + you remember this convention. + + - This file supports + compiling the project with 'mvn compile' (see target/) + cleaning the class files with 'mvn clean' + executing the main routine with 'mvn exec:java' + executing the unit tests with 'mvn test' + packaging the application with 'mvn package' (see target/) + + - the Maven project structure is as follows: + https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html + + --> + + <!-- ==== START OF SIMPLE CONFIGURATION ==== --> + + <!-- the three parts of the artifact name --> + <groupId>fi.utu.tech</groupId> + <artifactId>template-javafx</artifactId> + <version>1.0</version> + + <!-- additional information about the project --> + <name>JavaFX project template</name> + <url>https://gitlab.utu.fi/tech/education/gui/template-javafx</url> + + <packaging>jar</packaging> + + <!-- HINT: More configuration here! --> + <properties> + <!-- Configures this project to use 'fi.utu.tech.AppMain' as its main class --> + <project.mainclass>fi.utu.tech.gui.javafx.Main</project.mainclass> + + + <!-- Don't touch these unless you know what you're doing! + + For example, the source encoding should always be utf-8. + You're probably doing something stupid if you think it + should be a 8-bit code page in 2019. --> + <jdk.version>11</jdk.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <jqwik.version>1.2.0</jqwik.version> + <junit.version>5.5.2</junit.version> + <junitplatform.version>1.5.2</junitplatform.version> + <javafx.version>13</javafx.version> + </properties> + + <!-- ==== END OF SIMPLE CONFIGURATION ==== --> + + <repositories> + <repository> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>central</id> + <name>Central Repository</name> + <url>https://repo.maven.apache.org/maven2</url> + </repository> + <repository> + <id>jcenter</id> + <name>jcenter</name> + <url>https://jcenter.bintray.com/</url> + </repository> + <!-- UTU repository --> + <repository> + <id>ftdev</id> + <name>ftdev</name> + <url>https://ftdev.utu.fi/maven2</url> + </repository> + </repositories> + + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-commons</artifactId> + <version>${junitplatform.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.jqwik</groupId> + <artifactId>jqwik</artifactId> + <version>${jqwik.version}</version> + <scope>test</scope> + </dependency><!-- + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-migrationsupport</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-runner</artifactId> + <version>${junitplatform.version}</version> + <scope>test</scope> + </dependency> --> + + <!-- JavaFX (remove if not needed to speed up dep downloads)--> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-base</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-controls</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-fxml</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-graphics</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-media</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-swing</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-web</artifactId> + <version>${javafx.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- Make a 'fat' jar, that is, jar that contains all its dependencies and runs as is. + See: https://stackoverflow.com/a/57691362 --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.3</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>module-info.class</exclude> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + </excludes> + </filter> + </filters> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>${project.mainclass}</mainClass> + </transformer> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + + <!-- Run this app with exec:java --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.6.0</version> + <executions> + <execution> + <goals> + <goal>java</goal> + </goals> + </execution> + </executions> + <configuration> + <mainClass>${project.mainclass}</mainClass> + <arguments> + <argument>arg1</argument> + <argument>arg2</argument> + </arguments> + </configuration> + </plugin> + + <!-- Make the packaged jar executable --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <!-- DO NOT include log4j.properties file in your Jar --> + <excludes> + <exclude>**/log4j.properties</exclude> + </excludes> + <archive> + <manifest> + <!-- Jar file entry point --> + <mainClass>${project.mainclass}</mainClass> + </manifest> + </archive> + </configuration> + </plugin> + + <!-- download source code in Eclipse, best practice --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-eclipse-plugin</artifactId> + <version>2.10</version> + <configuration> + <downloadSources>true</downloadSources> + <downloadJavadocs>false</downloadJavadocs> + </configuration> + </plugin> + + <!-- JDK source/target versions --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <source>${jdk.version}</source> + <target>${jdk.version}</target> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.1.1</version> + <executions> + <execution> + <id>attach-javadocs</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + <configuration> + <tags> + <tag> + <name>toDo</name> + <placement>a</placement> + <head>To do:</head> + </tag> + <tag> + <name>classInvariant</name> + <placement>t</placement> + <head>Class invariant:</head> + </tag> + <tag> + <name>classInvariantProtected</name> + <placement>t</placement> + <head>Protected class invariant:</head> + </tag> + <tag> + <name>classInvariantPrivate</name> + <placement>t</placement> + <head>Private class invariant:</head> + </tag> + <tag> + <name>abstractionFunction</name> + <placement>t</placement> + <head>Abstraction function:</head> + </tag> + <tag> + <name>pre</name> + <placement>cm</placement> + <head>Precondition:</head> + </tag> + <tag> + <name>post</name> + <placement>cm</placement> + <head>Postcondition:</head> + </tag> + <tag> + <name>postProtected</name> + <placement>cm</placement> + <head>Protected postcondition:</head> + </tag> + <tag> + <name>postPrivate</name> + <placement>cm</placement> + <head>Private postcondition:</head> + </tag> + <tag> + <name>time</name> + <placement>cmf</placement> + <head>Time complexity:</head> + </tag> + <tag> + <name>space</name> + <placement>cmf</placement> + <head>Space complexity:</head> + </tag> + <tag> + <name>correspondence</name> + <placement>a</placement> + <head>Correspondence:</head> + </tag> + <tag> + <name>download</name> + <placement>a</placement> + <head>Download:</head> + </tag> + </tags> + <show>protected</show> + <failOnError>false</failOnError> + <sourceFileExcludes> + <sourceFileExclude>**/module-info.java</sourceFileExclude> + </sourceFileExcludes> + </configuration> + </plugin> + + <!-- JUnit & JQwik test integration --> + + <!-- junit-platform-maven-plugin: supports modular tests + maven-surefire-plugin: non-modular tests + + Modular testing works via command line mvn, but is + still broken in Eclipse due to this + https://bugs.eclipse.org/bugs/show_bug.cgi?id=520667 + --> + <plugin> + <groupId>de.sormuras.junit</groupId> + <artifactId>junit-platform-maven-plugin</artifactId> + <version>1.0.0-M5</version> + <extensions>true</extensions> + <configuration> + <executor>JAVA</executor> + </configuration> + </plugin> + + <!-- + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.0.0-M3</version> + </plugin> + <plugin> + <artifactId>maven-failsafe-plugin</artifactId> + <version>3.0.0-M3</version> + </plugin> --> + + <!-- javafx:jlink: + https://github.com/openjfx/javafx-maven-plugin + Use 'mvn package' instead if you don't need jlink. + + <plugin> + <groupId>org.openjfx</groupId> + <artifactId>javafx-maven-plugin</artifactId> + <version>0.0.3</version> + <configuration> + <mainClass>${project.mainclass}</mainClass> + </configuration> + </plugin> --> + + </plugins> + <extensions> + <!-- Enables the use of SSH for deployments --> + <extension> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-ssh-external</artifactId> + <version>3.3.3</version> + </extension> + <extension> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-ssh</artifactId> + <version>3.3.3</version> + </extension> + </extensions> + </build> + <distributionManagement> + <repository> + <id>ftdev</id> + <name>UTU tech ftdev repository</name> + <url>scp://localhost:2222/var/www/maven2</url> + </repository> + </distributionManagement> +</project> diff --git a/Project 2/project/build.properties b/Project 2/project/build.properties new file mode 100644 index 0000000000000000000000000000000000000000..6adcdc753fdca1b4b851d8b75426dee24044bf6d --- /dev/null +++ b/Project 2/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.3.3 diff --git a/Project 2/project/plugins.sbt b/Project 2/project/plugins.sbt new file mode 100644 index 0000000000000000000000000000000000000000..37ca261463b6a1800a62a0d5a5279fbb22915638 --- /dev/null +++ b/Project 2/project/plugins.sbt @@ -0,0 +1,7 @@ +resolvers += Resolver.jcenterRepo + +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") +addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3") +addSbtPlugin("com.github.xuwei-k" % "sbt-jshell" % "0.1.2") +//addSbtPlugin("org.xerial.sbt" % "sbt-sql-sqlite" % "0.8") +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.0") diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/BMI.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/BMI.java new file mode 100644 index 0000000000000000000000000000000000000000..c6c72d39efd3065afb24c7b1e26f78a10a3e5eab --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/BMI.java @@ -0,0 +1,46 @@ +package fi.utu.tech.gui.javafx; + +import javafx.event.EventHandler; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; + +public class BMI extends BorderPane{ + + public BMI() { + Label label1 = new Label("Pituus: "); + Label label2 = new Label("Paino: "); + Label tulos = new Label("Virhellinen syöttö"); + Label BMI = new Label("BMI: "); + TextField tf1 = new TextField(); + TextField tf2 = new TextField(); + + VBox vb1 = new VBox(label1, tf1); + VBox vb2 = new VBox(label2, tf2); + + HBox hb = new HBox(vb1, vb2); + HBox hb2 = new HBox(BMI, tulos); + + EventHandler<KeyEvent> BMIKaava = e -> { + try { + float pituus = Float.valueOf(tf1.getText()); + float paino = Float.valueOf(tf2.getText()); + tulos.setText(String.valueOf((paino/(pituus*pituus)))); + } catch(Exception a) { + tulos.setText("Virheellinen syöttö"); + } + }; + + + tf1.setOnKeyTyped(BMIKaava); + tf2.setOnKeyTyped(BMIKaava); + + + setCenter(hb); + setBottom(hb2); + + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/CanvasWindow.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/CanvasWindow.java new file mode 100644 index 0000000000000000000000000000000000000000..c01c54dbe9a2c858dde59959dd454dfad746621a --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/CanvasWindow.java @@ -0,0 +1,84 @@ +package fi.utu.tech.gui.javafx; + +import java.util.Random; + +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.canvas.Canvas; +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.paint.Color; + +public class CanvasWindow extends BorderPane{ + + public CanvasWindow() { + + Canvas canvas = new Canvas(500, 500); + final GraphicsContext graphicsContext = canvas.getGraphicsContext2D(); + initDraw(graphicsContext); + Random r = new Random(); + + + canvas.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>(){ + + @Override + public void handle(MouseEvent event) { + if (event.getButton() == MouseButton.PRIMARY) { + graphicsContext.beginPath(); + graphicsContext.moveTo(event.getX(), event.getY()); + graphicsContext.stroke(); + } else if (event.getButton() == MouseButton.SECONDARY) { + + } + } + }); + + canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>(){ + + @Override + public void handle(MouseEvent event) { + if (event.getButton() == MouseButton.PRIMARY) { + graphicsContext.lineTo(event.getX(), event.getY()); + graphicsContext.stroke(); + } else if (event.getButton() == MouseButton.SECONDARY) { + graphicsContext.fillRect(event.getX()+ (r.nextInt(20)-10), event.getY()+ (r.nextInt(20)-10), 2, 2); + graphicsContext.stroke(); + } + } + }); + + canvas.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>(){ + + @Override + public void handle(MouseEvent event) { + + } + }); + + setCenter(canvas); + } + + private void initDraw(GraphicsContext gc){ + double canvasWidth = gc.getCanvas().getWidth(); + double canvasHeight = gc.getCanvas().getHeight(); + + gc.setFill(Color.BLACK); + gc.setStroke(Color.BLACK); + gc.setLineWidth(5); + + gc.fill(); + gc.strokeRect( + 0, //x of the upper left corner + 0, //y of the upper left corner + canvasWidth, //width of the rectangle + canvasHeight); //height of the rectangle + + gc.setFill(Color.BLACK); + gc.setStroke(Color.BLACK); + gc.setLineWidth(1); + + } + +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java new file mode 100644 index 0000000000000000000000000000000000000000..a4d0180639373080225fee7cfa3de4b85d5f05f3 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java @@ -0,0 +1,13 @@ +package fi.utu.tech.gui.javafx; + +import javafx.scene.control.Alert; + +public class Dialogs { + public static void warning(String title, String header, String content) { + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle(title); + alert.setHeaderText(header); + alert.setContentText(content); + alert.showAndWait(); + } +} \ No newline at end of file diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java new file mode 100644 index 0000000000000000000000000000000000000000..7777d6696e7c286beead79605114a62997be9867 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java @@ -0,0 +1,63 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Platform; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; + +import java.util.function.Supplier; + +public class FXMLController { + private IntegerProperty clicks = new SimpleIntegerProperty(0); + + private Supplier<Parent> supplier; + + @FXML + private Label clicksLabel; + + protected void updateClicks() { + if (!clicksLabel.textProperty().isBound()) + clicksLabel.textProperty().bind(clicks.asString().concat(" clicks.")); + + clicks.setValue(clicks.getValue() + 1); + } + + protected void setLabel(String text) { + if (!clicksLabel.textProperty().isBound()) + clicksLabel.setText(text); + } + + @FXML + protected void handleDialogButton(ActionEvent event) { + updateClicks(); + Dialogs.warning("Example dialog", "Content header", "Content"); + } + + @FXML + protected void handleExitButton(ActionEvent event) { + System.out.println("Closing app."); + Platform.exit(); + } + + @FXML + protected void handleWindowButton(ActionEvent event) { + updateClicks(); + Scene other = new Scene(supplier.get()); + Stage otherStage = new Stage(); + otherStage.setScene(other); + otherStage.show(); + } + + protected void setWindowFactory(Supplier<Parent> supplier) { + this.supplier = supplier; + } + + public void initialize() { + System.out.println("2-A"); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/Main.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..8039f74d6f63866f01415316911afc470cb27731 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/Main.java @@ -0,0 +1,29 @@ +package fi.utu.tech.gui.javafx; + +public class Main { + /** + * The main() method is ignored in correctly deployed JavaFX application. + * main() serves only as fallback in case the application can not be + * launched through deployment artifacts, e.g., in IDEs with limited FX + * support. NetBeans ignores main(). + * + * @param args the command line arguments + */ + public static void main(String[] args) { + System.out.println("Stage A"); + if (args.length == 1 && args[0].equals("--test")) return; + + int exercise = 1; + if (args.length == 1) exercise = Integer.parseInt(args[0]); + switch(exercise) { + case 1: MainApp1.launch(MainApp1.class, args); break; + case 2: MainApp2.launch(MainApp2.class, args); break; + case 3: MainApp3.launch(MainApp3.class, args); break; + case 4: MainApp4.launch(MainApp4.class, args); break; + case 5: MainApp5.launch(MainApp5.class, args); break; + case 6: MainApp6.launch(MainApp6.class, args); break; + default: MainApp.launch(MainApp.class, args); break; + } + } + +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp.java new file mode 100644 index 0000000000000000000000000000000000000000..6dc8ba36d119ed7103cff242ce1cb5da8d0e99a1 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new OtherWindow(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp1.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp1.java new file mode 100644 index 0000000000000000000000000000000000000000..5b8c8b3ba81acac1c4b6161d0f51a2f0dc730aa0 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp1.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp1 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new PasswordWindow(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp2.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp2.java new file mode 100644 index 0000000000000000000000000000000000000000..f86a01d070d49e8c9d9cd3b4e29373e06b2e3a9b --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp2.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp2 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new BMI(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp3.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp3.java new file mode 100644 index 0000000000000000000000000000000000000000..58ea02f48431f1486e8c193e58aa18979f00db51 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp3.java @@ -0,0 +1,76 @@ +package fi.utu.tech.gui.javafx; + +import javafx.animation.Animation; +import javafx.animation.Interpolator; +import javafx.animation.RotateTransition; +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import javafx.util.Duration; + +public class MainApp3 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new OtherWindow(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + + RotateTransition rt = new RotateTransition(Duration.millis(5000), loader.root); + + rt.setCycleCount(Animation.INDEFINITE); + rt.setFromAngle(0); + rt.setToAngle(360); + rt.setInterpolator(Interpolator.LINEAR); + rt.play(); + + loader.root.setOnMouseClicked(mouseEvent -> { + rt.setRate(-1*rt.getRate()); + }); + + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java new file mode 100644 index 0000000000000000000000000000000000000000..253aaaef03b38950ef53687da04bc30c35f9f1b6 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp4 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new WebSelain(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java new file mode 100644 index 0000000000000000000000000000000000000000..73acf69c905707e3e37664e330cd2af2a9dccb81 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp5 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new StackPaneWindow(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp6.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp6.java new file mode 100644 index 0000000000000000000000000000000000000000..fefcf2d57a73eb6fb3448ea8e80d819bbf3d7b55 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp6.java @@ -0,0 +1,59 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp6 extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + System.out.println("Stage B"); + } + + @Override + public void stop() { + System.out.println("Stage E"); + } + + protected Parent createWindow() { + return new CanvasWindow(); + } + + protected String createStyle() { + return ResourceLoader.stylesheet("styles.css"); + } + + @Override + public void start(Stage stage) throws Exception { + System.out.println("Stage C"); + ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml"); + + loader.controller.setLabel("Welcome!"); + + // configure the menu to create windows with createWindow + loader.controller.setWindowFactory(this::createWindow); + + Scene scene = new Scene(loader.root); + scene.getStylesheets().add(createStyle()); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + System.out.println("Stage D"); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java new file mode 100644 index 0000000000000000000000000000000000000000..5328b153fd5aab1989df1f54049401893272a439 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java @@ -0,0 +1,14 @@ +package fi.utu.tech.gui.javafx; + +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; + +public class OtherWindow extends BorderPane { + public OtherWindow() { + Circle c = new Circle(200, Color.NAVY); + setCenter(c); + setLeft(new ImageView(ResourceLoader.image("hmm.png"))); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/PasswordWindow.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/PasswordWindow.java new file mode 100644 index 0000000000000000000000000000000000000000..9c799984583388219eab1f153b6ee4cd02c5c7ef --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/PasswordWindow.java @@ -0,0 +1,91 @@ +package fi.utu.tech.gui.javafx; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Formatter; + +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; + +public class PasswordWindow extends BorderPane { + + public PasswordWindow() { + PasswordField pf = new PasswordField(); + Label label = new Label("Aseta salasana"); + Button b1 = new Button("Tyhjennä"); + Button b2 = new Button("Hyväksy"); + HBox hb = new HBox(b1, b2); + Alert alert = new Alert(Alert.AlertType.NONE); + + + EventHandler<KeyEvent> salasanaPituus = e -> { + if(pf.getLength() < 8) { + label.setText("Salasana liian lyhyt"); + }else { + label.setText("Salasana on riittävän pitkä"); + } + }; + + + EventHandler<ActionEvent> tyhjennys = e -> { + pf.clear(); + label.setText("Aseta salasana"); + }; + + EventHandler<ActionEvent> hyvaksy = e -> { + Dialogs.warning("SHA1", "Salasana SHA1 muodossa", encryptPassword(pf.getText()) ); + }; + + pf.setOnKeyTyped(salasanaPituus); + b1.setOnAction(tyhjennys); + b2.setOnAction(hyvaksy); + + setTop(label); + setCenter(pf); + setBottom(hb); + + } + + + private static String encryptPassword(String password) + { + String sha1 = ""; + try + { + MessageDigest crypt = MessageDigest.getInstance("SHA-1"); + crypt.reset(); + crypt.update(password.getBytes("UTF-8")); + sha1 = byteToHex(crypt.digest()); + } + catch(NoSuchAlgorithmException e) + { + e.printStackTrace(); + } + catch(UnsupportedEncodingException e) + { + e.printStackTrace(); + } + return sha1; + } + + private static String byteToHex(final byte[] hash) + { + Formatter formatter = new Formatter(); + for (byte b : hash) + { + formatter.format("%02x", b); + } + String result = formatter.toString(); + formatter.close(); + return result; + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..f917bd339f01757b5c81f9a4252f1adf2d3a9da4 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java @@ -0,0 +1,42 @@ +package fi.utu.tech.gui.javafx; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; + +public class ResourceLoader<N extends Parent, C> { + protected final N root; + protected final C controller; + + public ResourceLoader(String contentPath) { + N root_ = null; + C controller_ = null; + try { + // determines where to look for the resources (the root path) + Class resourceRootClass = getClass(); + + FXMLLoader loader = new FXMLLoader(resourceRootClass.getResource(contentPath)); + root_ = loader.load(); + controller_ = loader.getController(); + System.out.println("DEBUG: " + contentPath + " loaded."); + } catch (Exception e) { + Dialogs.warning( + "Internal error", + "Could not open FXML file: " + contentPath, + "Reason: " + e.getMessage() + ); + System.exit(1); + } + root = root_; + controller = controller_; + } + + // finds images both outside and inside jars + public static String image(String fileName) { + return ResourceLoader.class.getResource(fileName).toExternalForm(); + } + + // finds stylesheets both outside and inside jars + public static String stylesheet(String fileName) { + return ResourceLoader.class.getResource(fileName).toExternalForm(); + } +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/StackPaneWindow.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/StackPaneWindow.java new file mode 100644 index 0000000000000000000000000000000000000000..a0cadac53d210ed521d80414ec8830a84f8af137 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/StackPaneWindow.java @@ -0,0 +1,41 @@ +package fi.utu.tech.gui.javafx; + +import javafx.event.EventHandler; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Rectangle; + +public class StackPaneWindow extends BorderPane { + + public StackPaneWindow() { + + Rectangle r = new Rectangle(300,300, Paint.valueOf("#ff0000")); + + StackPane sp1 = new StackPane(r); + StackPane sp2 = new StackPane(sp1); + + EventHandler<MouseEvent> rKlikkaus = e -> { + System.out.println(r.toString()); + e.consume(); + }; + + EventHandler<MouseEvent> sp1Klikkaus = e -> { + System.out.println(sp1.toString()); + }; + + EventHandler<MouseEvent> sp2Klikkaus = e -> { + System.out.println(sp2.toString()); + }; + + r.setOnMouseClicked(rKlikkaus); + sp1.setOnMouseClicked(sp1Klikkaus); + sp2.setOnMouseClicked(sp2Klikkaus); + + setCenter(sp2); + + } + +} diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/WebSelain.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/WebSelain.java new file mode 100644 index 0000000000000000000000000000000000000000..ae0bb790d003f68de3ccb62b9b8a71a14b1e4792 --- /dev/null +++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/WebSelain.java @@ -0,0 +1,40 @@ +package fi.utu.tech.gui.javafx; + +import javafx.event.EventHandler; +import javafx.scene.Scene; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; + +public class WebSelain extends BorderPane { + + public WebSelain() { + + TextField osoiteRivi = new TextField(); + + WebView webView = new WebView(); + setCenter(webView); + + setTop(osoiteRivi); + + osoiteRivi.setOnKeyPressed(new EventHandler<KeyEvent>(){ + @Override + public void handle(KeyEvent ke) + { + if (ke.getCode().equals(KeyCode.ENTER)) + { + System.out.println("moi"); + + webView.getEngine().load(osoiteRivi.getText()); + + } + } + }); + + } +} diff --git a/Project 2/src/main/resources/fi/utu/tech/gui/javafx/hmm.png b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/hmm.png new file mode 100644 index 0000000000000000000000000000000000000000..25e8eb650f6752e716570cfb3c590bf09dff41ee Binary files /dev/null and b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/hmm.png differ diff --git a/Project 2/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml new file mode 100644 index 0000000000000000000000000000000000000000..434e233d182a2a3205228268795bbedbb6104185 --- /dev/null +++ b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.BorderPane?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> + +<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.FXMLController"> + <left> + <ImageView fitHeight="368.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@hmm.png" /> + </image> + <BorderPane.margin> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </BorderPane.margin> + </ImageView> + </left> + <center> + <BorderPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER"> + <padding> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </padding> + <center> + <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="16.0" BorderPane.alignment="CENTER"> + <padding> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </padding> + <children> + <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleWindowButton" text="Open a new window" /> + <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleDialogButton" text="Display a dialog" /> + <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleExitButton" text="Exit" /> + <Label fx:id="clicksLabel" text="Loading..." /> + </children></VBox> + </center> + <top> + <Label text="What should we do?" BorderPane.alignment="CENTER"> + <font> + <Font size="26.0" /> + </font></Label> + </top> + </BorderPane> + </center> +</BorderPane> diff --git a/Project 2/src/main/resources/fi/utu/tech/gui/javafx/styles.css b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..3ab643a3ea2d9be4b2f210cd57132b6fc8569a82 --- /dev/null +++ b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/styles.css @@ -0,0 +1,3 @@ +.button { + -fx-font-weight: bold; +} diff --git a/Project 3/.gitignore b/Project 3/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5bfb48ed0b155fac605c25bfac13ca2115525da1 --- /dev/null +++ b/Project 3/.gitignore @@ -0,0 +1,20 @@ +contracts/ +.classpath +.project +.history/ +.idea +.jqwik-database +.lib/ +.worksheet +.settings/ +*.iml +*.ipr +*.iws +*.log +project/boot/ +project/plugins/project/ +project/project/ +project/*-shim.sbt +project/target/ +target/ +openjfx/ diff --git a/Project 3/.gitlab-ci.yml b/Project 3/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..4b3553b42ff83c2ef7e7ea37ffa030aaaf554898 --- /dev/null +++ b/Project 3/.gitlab-ci.yml @@ -0,0 +1,13 @@ +image: maven:latest + +stages: + - build + - test + +build: + script: + - mvn compile + +test: + script: + - mvn test \ No newline at end of file diff --git a/Project 3/README.md b/Project 3/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2907fd126e89f04e9141a21287b3d0e75094be52 --- /dev/null +++ b/Project 3/README.md @@ -0,0 +1,39 @@ +# Project description + +Simple template for projects that make use of JavaFX and FXML (Scene Builder). +Requires Java 11 or later. Compatible with +Eclipse and IntelliJ IDEA. Minor issues with Netbeans. Automatically +integrates with Gitlab CI. + +## Installation + +Maven: + +```bash +$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx + +$ cd template-javafx + +$ mvn compile exec:java +``` + +SBT: + +```bash +$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx + +$ cd template-javafx + +$ sbt compile run +``` + +## Further instructions + + * Java platform: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/jvm-platform + * Maven: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/maven-misc + * SBT: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/sbt-misc + +External sources: + + * JavaFX: https://openjfx.io/javadoc/11/ + * Scene Builder: https://docs.gluonhq.com/scenebuilder/ diff --git a/Project 3/build.sbt b/Project 3/build.sbt new file mode 100644 index 0000000000000000000000000000000000000000..e703e9051c964d2e35d6f297a81b241437860648 --- /dev/null +++ b/Project 3/build.sbt @@ -0,0 +1,236 @@ +// Project template + +// Supported operating systems: Windows, Mac, Linux +// Supported JDKs: 8, 10+ + +// Project name +name := "template-javafx" + +// organization name +organization := "fi.utu.tech" + +version := "1.0" + +// project description +description := "JavaFX project template" + +// main class +Compile/mainClass := Some("fi.utu.tech.gui.javafx.Main") + +// force the java version by typing it here (remove the comment) +val force_javaVersion = None // Some(13) + +// force the javafx version by typing it here (remove the comment) +val force_javaFxVersion = None // Some(13) + +val useJavaFX = true + +val useScalaOrScalaFX = true + +// END_OF_SIMPLE_CONFIGURATION +// you can copy the rest for each new project +// --- --- --- + +def fail(msg: String) = { + println("Error :-/") + println + println(msg) + System.exit(1) + null +} + +val detectedJDK = System.getProperty("java.version").replace("-ea","").split('.').dropWhile(_.toInt<8).head.toInt + +val javaVersionNum = force_javaVersion.getOrElse(detectedJDK) + +val javaVersionString = javaVersionNum match { + case 7 => "1.7" + case 8 => "1.8" + case x if x > 8 => x.toString +} + +val lts = 11 +val dev = 13 + +val supported = javaVersionNum match { + case x if x < 8 => fail("Your Java installation is obsolete. Please upgrade to Java " + lts + "LTS") + case 9 => fail("Your Java installation is unsupported and has known issues. Please upgrade to Java " + lts + "LTS") + case x if x < lts => println("Consider upgrading to Java " + lts + " LTS"); true + case x if x > lts && x < dev => println("Consider upgrading to Java " + dev); true + case x if x > dev => println("Unsupported early access version. Consider switching back to Java " + dev); true + case _ => true +} + +javacOptions ++= Seq("-source", javaVersionString, "-target", javaVersionString, "-encoding", "utf8", "-Xlint:unchecked", "-Xlint:deprecation") +javacOptions in doc := Seq("-source", javaVersionString) + +enablePlugins(JShellPlugin) + +compileOrder := CompileOrder.JavaThenScala + +// Enables publishing to maven repo +publishMavenStyle := true + +// Do not append Scala versions to the generated artifacts +crossPaths := false + +// This forbids including Scala related libraries into the dependency +autoScalaLibrary := false + +assemblyMergeStrategy in assembly := { + case PathList("META-INF", xs @ _*) => MergeStrategy.discard + case _ => MergeStrategy.first +} + +// contains libraries provided by utu/ft dep +resolvers += "ftdev" at "https://ftdev.utu.fi/maven2" + +fork in Global := true + +val javaVersion = taskKey[Unit]("Prints the Java version.") + +javaVersion := { println("SBT uses Java SDK located at "+System.getProperty("java.home")) } + +publishTo := Some(Resolver.file("file", new File("/tmp/repository"))) + +val oomkit = "fi.utu.tech" % "oomkit" % "1.15" + +libraryDependencies ++= Seq() + +//// +//// JQWIK / JUNIT configuration +//// + +resolvers in ThisBuild += Resolver.jcenterRepo + +val junit_version = "5.5.2" + +// library dependencies. (orginization name) % (project name) % (version) +libraryDependencies ++= Seq( + "net.aichler" % "jupiter-interface" % JupiterKeys.jupiterVersion.value % Test, + "org.junit.platform" % "junit-platform-commons" % ("1"+junit_version.tail) % Test, + "org.junit.platform" % "junit-platform-runner" % ("1"+junit_version.tail) % Test, + "org.junit.jupiter" % "junit-jupiter-engine" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-api" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-migrationsupport" % junit_version % Test, + "org.junit.jupiter" % "junit-jupiter-params" % junit_version % Test, + "net.jqwik" % "jqwik" % "1.2.0" % Test, + "org.scalatest" %% "scalatest" % "3.0.8" % Test, +) + +testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-c") + +//// +//// JAVAFX configuration +//// + +val javafx_versions = if (!useJavaFX) (0,"-","-") else (force_javaFxVersion getOrElse javaVersionNum) match { + case 7 => (7, "7", "8.0.181-R13") + case 8 => (8, "8", "8.0.181-R13") + case 10 => (11, "11.0.2", "11-R16") + case x if x>10 => (13, "13", "12.0.2-R18") + case _ => fail("Unsupported Java version for JavaFX") +} + +// JAVA_HOME location +val javaHomeDir = { + val path = try { + if (scala.sys.env("JAVA_HOME").trim.isEmpty) throw new Exception("Empty JAVA_HOME") else scala.sys.env("JAVA_HOME") + } catch { + case _: Throwable => System.getProperty("java.home") // not set -> ask from current JVM + } + + val f = file(path) + if (!f.exists()) fail("The environment variable JAVA_HOME points to a non-existent directory!\nSolution: Edit your system settings (Windows control panel / *nix .bashrc) and fix the JAVA_HOME location.") + f +} + +val osName: SettingKey[String] = SettingKey[String]("osName") + +osName := (System.getProperty("os.name") match { + case n if n.startsWith("Linux") => "linux" + case n if n.startsWith("Mac") => "mac" + case n if n.startsWith("Windows") => "win" + case _ => throw new Exception("Unknown platform!") +}) + +def legacyJavaFX() = { + val searchDirs = Seq( + "/jre/lib/jfxrt.jar", // OpenJDK 7 + "/jre/lib/ext/jfxrt.jar", // OpenJDK 8 + "/lib/ext/jfxrt.jar" // Windows & Oracle Java 8 + ) + + if (detectedJDK > 8) fail(s"Trying to use legacy non-modular JavaFX with a modern JDK [$detectedJDK].\nSolution: Check the line 'val force_javaFxVersion =' in build.sbt.") + + val javaFxJAR = searchDirs.map{ searchDir => file(javaHomeDir + searchDir) }.find{ _.exists() } + + javaFxJAR.getOrElse { + fail(s"Java FX runtime not installed in [${javaHomeDir.toString}]!\nSolution: Install JavaFX or consider upgrading your JDK so that JavaFX can be installed automatically.") + } +} + +val jfx_sdk_version = javafx_versions._2 +val jfx_scalafx_version = javafx_versions._3 + +val javaFxPath = Def.taskKey[File]("OpenJFX fetcher") +javaFxPath := { + val javaFxHome = + try { + val envHome = file(scala.sys.env("JAVAFX_HOME")) + if (envHome.toString.trim.isEmpty) throw new Exception("Empty JAVAFX_HOME") + println("Using OpenJFX from " + envHome) + envHome + } + catch { case _: Throwable => + println("Using local OpenJFX") + baseDirectory.value / "openjfx" + } + + if (!javaFxHome.exists()) java.nio.file.Files.createDirectory(javaFxHome.toPath) + + val jfx_os = osName.value match { + case "linux" => "linux" + case "mac" => "osx" + case "win" => "windows" + } + + val sdkURL = "http://download2.gluonhq.com/openjfx/" + jfx_sdk_version + "/openjfx-" + jfx_sdk_version + "_" + jfx_os + "-x64_bin-sdk.zip" + + try { + val testDir = javaFxHome / "all.ok" + if (!testDir.exists()) { + println("Fetching OpenJFX from "+sdkURL+"..") + IO.unzipURL(new URL(sdkURL), javaFxHome) + java.nio.file.Files.createDirectory(testDir.toPath) + println("Fetching OpenJFX done.") + } else { + println("Using OpenJFX from "+javaFxHome) + } + + javaFxHome + } + catch { + case t: Throwable => fail("Could not load OpenJFX! Reason:" + t.getMessage) + } +} + +val jfxModules = Seq("base","controls","fxml","graphics","media","swing","web") + + +if (!useJavaFX) Seq() else javafx_versions._1 match { + case 7 => + // TODO libraryDependencies + Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX())) + case 8 => + (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" %% "scalafx" % jfx_scalafx_version) else Seq()) ++ + Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX())) + case _ => + Seq( + javaOptions in run ++= Seq( + "--module-path", (javaFxPath.value / ("javafx-sdk-" + jfx_sdk_version) / "lib").toString, + "--add-modules=" + jfxModules.map("javafx."+_).mkString(",")) + ) ++ + (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" % "scalafx_2.13" % jfx_scalafx_version) else Seq()) ++ + jfxModules.map(module => libraryDependencies += "org.openjfx" % ("javafx-"+module) % jfx_sdk_version classifier osName.value) +} diff --git a/Project 3/pom.xml b/Project 3/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..d5e648244ca21b15feef4d86023c7ce15e3b51c6 --- /dev/null +++ b/Project 3/pom.xml @@ -0,0 +1,452 @@ +<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> + + <!-- + + Welcome! + This pom.xml is *THE* file that defines your Maven style Java project. + + Eclipse, IDEA and other development environments with Maven support + or plugins can *import* this project by reading this file. + + It usually contains tons of barely readable configuration. Luckily + this basic pom.xml is somewhat readable. All necessary configuration + for customizing your project if located here on top before the + 'END OF SIMPLE CONFIGURATION' line below. + + Some basics: + + - Maven is a build system for Java/JVM + - the Maven projects define an artifact that has a three part id: + groupId - artifactId - version + + - For example, this project is called: + fi.utu.tech - oomkit - 1.15 + + - The group id is usually a web domain in reverse order. + + - You can use these id parts to search for projects at + https://search.maven.org/ + + - If your version is not final and you have plans to modify the + source code at some point, please use a version id that ends with + -SNAPSHOT, e.g 1.0-SNAPSHOT. Maven may cache the project jar + and refuse to overwrite old cached versions with new ones unless + you remember this convention. + + - This file supports + compiling the project with 'mvn compile' (see target/) + cleaning the class files with 'mvn clean' + executing the main routine with 'mvn exec:java' + executing the unit tests with 'mvn test' + packaging the application with 'mvn package' (see target/) + + - the Maven project structure is as follows: + https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html + + --> + + <!-- ==== START OF SIMPLE CONFIGURATION ==== --> + + <!-- the three parts of the artifact name --> + <groupId>fi.utu.tech</groupId> + <artifactId>template-javafx</artifactId> + <version>1.0</version> + + <!-- additional information about the project --> + <name>JavaFX project template</name> + <url>https://gitlab.utu.fi/tech/education/gui/template-javafx</url> + + <packaging>jar</packaging> + + <!-- HINT: More configuration here! --> + <properties> + <!-- Configures this project to use 'fi.utu.tech.AppMain' as its main class --> + <project.mainclass>fi.utu.tech.gui.javafx.Main</project.mainclass> + + + <!-- Don't touch these unless you know what you're doing! + + For example, the source encoding should always be utf-8. + You're probably doing something stupid if you think it + should be a 8-bit code page in 2019. --> + <jdk.version>11</jdk.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <jqwik.version>1.2.0</jqwik.version> + <junit.version>5.5.2</junit.version> + <junitplatform.version>1.5.2</junitplatform.version> + <javafx.version>13.0.2</javafx.version> + </properties> + + <!-- ==== END OF SIMPLE CONFIGURATION ==== --> + + <repositories> + <repository> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>central</id> + <name>Central Repository</name> + <url>https://repo.maven.apache.org/maven2</url> + </repository> + <repository> + <id>jcenter</id> + <name>jcenter</name> + <url>https://jcenter.bintray.com/</url> + </repository> + <!-- UTU repository --> + <repository> + <id>ftdev</id> + <name>ftdev</name> + <url>https://ftdev.utu.fi/maven2</url> + </repository> + </repositories> + + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-commons</artifactId> + <version>${junitplatform.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.jqwik</groupId> + <artifactId>jqwik</artifactId> + <version>${jqwik.version}</version> + <scope>test</scope> + </dependency><!-- + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-migrationsupport</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-runner</artifactId> + <version>${junitplatform.version}</version> + <scope>test</scope> + </dependency> --> + + <!-- JavaFX (remove if not needed to speed up dep downloads)--> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-base</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-controls</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-fxml</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-graphics</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-media</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-swing</artifactId> + <version>${javafx.version}</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-web</artifactId> + <version>${javafx.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- Make a 'fat' jar, that is, jar that contains all its dependencies and runs as is. + See: https://stackoverflow.com/a/57691362 --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.3</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>module-info.class</exclude> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + </excludes> + </filter> + </filters> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>${project.mainclass}</mainClass> + </transformer> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + + <!-- Run this app with exec:java --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.6.0</version> + <executions> + <execution> + <goals> + <goal>java</goal> + </goals> + </execution> + </executions> + <configuration> + <mainClass>${project.mainclass}</mainClass> + <arguments> + <argument>arg1</argument> + <argument>arg2</argument> + </arguments> + </configuration> + </plugin> + + <!-- Make the packaged jar executable --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <!-- DO NOT include log4j.properties file in your Jar --> + <excludes> + <exclude>**/log4j.properties</exclude> + </excludes> + <archive> + <manifest> + <!-- Jar file entry point --> + <mainClass>${project.mainclass}</mainClass> + </manifest> + </archive> + </configuration> + </plugin> + + <!-- download source code in Eclipse, best practice --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-eclipse-plugin</artifactId> + <version>2.10</version> + <configuration> + <downloadSources>true</downloadSources> + <downloadJavadocs>false</downloadJavadocs> + </configuration> + </plugin> + + <!-- JDK source/target versions --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <source>${jdk.version}</source> + <target>${jdk.version}</target> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.1.1</version> + <executions> + <execution> + <id>attach-javadocs</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + <configuration> + <tags> + <tag> + <name>toDo</name> + <placement>a</placement> + <head>To do:</head> + </tag> + <tag> + <name>classInvariant</name> + <placement>t</placement> + <head>Class invariant:</head> + </tag> + <tag> + <name>classInvariantProtected</name> + <placement>t</placement> + <head>Protected class invariant:</head> + </tag> + <tag> + <name>classInvariantPrivate</name> + <placement>t</placement> + <head>Private class invariant:</head> + </tag> + <tag> + <name>abstractionFunction</name> + <placement>t</placement> + <head>Abstraction function:</head> + </tag> + <tag> + <name>pre</name> + <placement>cm</placement> + <head>Precondition:</head> + </tag> + <tag> + <name>post</name> + <placement>cm</placement> + <head>Postcondition:</head> + </tag> + <tag> + <name>postProtected</name> + <placement>cm</placement> + <head>Protected postcondition:</head> + </tag> + <tag> + <name>postPrivate</name> + <placement>cm</placement> + <head>Private postcondition:</head> + </tag> + <tag> + <name>time</name> + <placement>cmf</placement> + <head>Time complexity:</head> + </tag> + <tag> + <name>space</name> + <placement>cmf</placement> + <head>Space complexity:</head> + </tag> + <tag> + <name>correspondence</name> + <placement>a</placement> + <head>Correspondence:</head> + </tag> + <tag> + <name>download</name> + <placement>a</placement> + <head>Download:</head> + </tag> + </tags> + <show>protected</show> + <failOnError>false</failOnError> + <sourceFileExcludes> + <sourceFileExclude>**/module-info.java</sourceFileExclude> + </sourceFileExcludes> + </configuration> + </plugin> + + <!-- JUnit & JQwik test integration --> + + <!-- junit-platform-maven-plugin: supports modular tests + maven-surefire-plugin: non-modular tests + + Modular testing works via command line mvn, but is + still broken in Eclipse due to this + https://bugs.eclipse.org/bugs/show_bug.cgi?id=520667 + --> + <plugin> + <groupId>de.sormuras.junit</groupId> + <artifactId>junit-platform-maven-plugin</artifactId> + <version>1.0.0-M5</version> + <extensions>true</extensions> + <configuration> + <executor>JAVA</executor> + </configuration> + </plugin> + + <!-- + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.0.0-M3</version> + </plugin> + <plugin> + <artifactId>maven-failsafe-plugin</artifactId> + <version>3.0.0-M3</version> + </plugin> --> + + <!-- javafx:jlink: + https://github.com/openjfx/javafx-maven-plugin + Use 'mvn package' instead if you don't need jlink. + + <plugin> + <groupId>org.openjfx</groupId> + <artifactId>javafx-maven-plugin</artifactId> + <version>0.0.3</version> + <configuration> + <mainClass>${project.mainclass}</mainClass> + </configuration> + </plugin> --> + + </plugins> + <extensions> + <!-- Enables the use of SSH for deployments --> + <extension> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-ssh-external</artifactId> + <version>3.3.3</version> + </extension> + <extension> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-ssh</artifactId> + <version>3.3.3</version> + </extension> + </extensions> + </build> + <distributionManagement> + <repository> + <id>ftdev</id> + <name>UTU tech ftdev repository</name> + <url>scp://localhost:2222/var/www/maven2</url> + </repository> + </distributionManagement> +</project> diff --git a/Project 3/project/build.properties b/Project 3/project/build.properties new file mode 100644 index 0000000000000000000000000000000000000000..6adcdc753fdca1b4b851d8b75426dee24044bf6d --- /dev/null +++ b/Project 3/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.3.3 diff --git a/Project 3/project/plugins.sbt b/Project 3/project/plugins.sbt new file mode 100644 index 0000000000000000000000000000000000000000..37ca261463b6a1800a62a0d5a5279fbb22915638 --- /dev/null +++ b/Project 3/project/plugins.sbt @@ -0,0 +1,7 @@ +resolvers += Resolver.jcenterRepo + +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") +addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3") +addSbtPlugin("com.github.xuwei-k" % "sbt-jshell" % "0.1.2") +//addSbtPlugin("org.xerial.sbt" % "sbt-sql-sqlite" % "0.8") +addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.0") diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java new file mode 100644 index 0000000000000000000000000000000000000000..a4d0180639373080225fee7cfa3de4b85d5f05f3 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java @@ -0,0 +1,13 @@ +package fi.utu.tech.gui.javafx; + +import javafx.scene.control.Alert; + +public class Dialogs { + public static void warning(String title, String header, String content) { + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle(title); + alert.setHeaderText(header); + alert.setContentText(content); + alert.showAndWait(); + } +} \ No newline at end of file diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/Main.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..ae8c3e30808aa3075a967653268563c6aebc9d1e --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/Main.java @@ -0,0 +1,31 @@ +package fi.utu.tech.gui.javafx; + +import fi.utu.tech.gui.javafx.gimmick.MainApp2; +import fi.utu.tech.gui.javafx.zipper.MainApp1; + +public class Main { + /** + * The main() method is ignored in correctly deployed JavaFX application. + * main() serves only as fallback in case the application can not be + * launched through deployment artifacts, e.g., in IDEs with limited FX + * support. NetBeans ignores main(). + * + * @param args the command line arguments + */ + public static void main(String[] args) { + System.out.println("Launching.."); + if (args.length == 1 && args[0].equals("--test")) return; + + int exercise = 2; + if (args.length == 1) exercise = Integer.parseInt(args[0]); + switch (exercise) { + case 1: + MainApp1.launch(MainApp1.class, args); + break; + default: + MainApp2.launch(MainApp2.class, args); + break; + } + } + +} diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/MainApp.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/MainApp.java new file mode 100644 index 0000000000000000000000000000000000000000..519b92e02723bce49a0ca7133868c3ca4a792215 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/MainApp.java @@ -0,0 +1,40 @@ +package fi.utu.tech.gui.javafx; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.layout.BorderPane; +import javafx.scene.shape.Rectangle; +import javafx.stage.Stage; + +public class MainApp extends Application { + + // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html + + // The JavaFX runtime does the following, in order, whenever an application is launched: + + // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information) + // 2. Constructs an instance of the specified Application class + // 3. Calls the Application's init() method + // 4. Calls the Application's start(javafx.stage.Stage) method + // 5. Waits for the application to finish, which happens when either of the following occur: + // a) the application calls Platform.exit() + // b) the last window has been closed and the implicitExit attribute on Platform is true + // 6. Calls the Application's stop() method + + @Override + public void init() { + } + + @Override + public void stop() { + } + + @Override + public void start(Stage stage) { + Scene scene = new Scene(new BorderPane(new Rectangle(300, 300))); + + stage.setTitle("JavaFX template"); + stage.setScene(scene); + stage.show(); + } +} diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..e5c2d1dca12f8fbaa9afd81f5ebdfa5e1d0c1a09 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java @@ -0,0 +1,42 @@ +package fi.utu.tech.gui.javafx; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; + +public class ResourceLoader<N extends Parent, C> { + public final N root; + public final C controller; + + public ResourceLoader(String contentPath) { + N root_ = null; + C controller_ = null; + try { + // determines where to look for the resources (the root path) + Class resourceRootClass = getClass(); + + FXMLLoader loader = new FXMLLoader(resourceRootClass.getResource(contentPath)); + root_ = loader.load(); + controller_ = loader.getController(); + System.out.println("DEBUG: " + contentPath + " loaded."); + } catch (Exception e) { + Dialogs.warning( + "Internal error", + "Could not open FXML file: " + contentPath, + "Reason: " + e.getMessage() + ); + System.exit(1); + } + root = root_; + controller = controller_; + } + + // finds images both outside and inside jars + public static String image(String fileName) { + return ResourceLoader.class.getResource(fileName).toExternalForm(); + } + + // finds stylesheets both outside and inside jars + public static String stylesheet(String fileName) { + return ResourceLoader.class.getResource(fileName).toExternalForm(); + } +} diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/GimmickController.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/GimmickController.java new file mode 100644 index 0000000000000000000000000000000000000000..871c432972cd3e4f759610d540eedcfbddd93505 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/GimmickController.java @@ -0,0 +1,27 @@ +package fi.utu.tech.gui.javafx.gimmick; + +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.scene.layout.TilePane; + +public class GimmickController { + @FXML + private TilePane tilePane; + + public void initialize() { + tilePane.getChildren().clear(); + + new Thread(() -> { + + for (int i = 0; i < 20; i++) { + final int j = i; + try { Thread.sleep(500); } + catch(Exception e) { + } + View c = j % 2 == 0 ? new View1(150, 150) : new View2(150, 150); + Platform.runLater(() -> tilePane.getChildren().add(c)); + new Thread(c).start(); + }}).start(); + + } +} diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/MainApp2.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/MainApp2.java new file mode 100644 index 0000000000000000000000000000000000000000..353ad8da1ca64146f8f0bfdc1e5e25c92dbe6ca6 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/MainApp2.java @@ -0,0 +1,20 @@ +package fi.utu.tech.gui.javafx.gimmick; + +import fi.utu.tech.gui.javafx.MainApp; +import fi.utu.tech.gui.javafx.ResourceLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class MainApp2 extends MainApp { + @Override + public void start(Stage stage) { + ResourceLoader<Parent, GimmickController> loader = new ResourceLoader<>("gimmick.fxml"); + + Scene scene = new Scene(loader.root); + + stage.setTitle("The Gimmick Show"); + stage.setScene(scene); + stage.show(); + } +} \ No newline at end of file diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View.java new file mode 100644 index 0000000000000000000000000000000000000000..666104e6edb0012fc800c3818671c35da247f8a6 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View.java @@ -0,0 +1,17 @@ +package fi.utu.tech.gui.javafx.gimmick; + +import javafx.scene.canvas.Canvas; + +abstract class View extends Canvas implements Runnable { + protected boolean active = true; + + public View(double width, double height) { + super(width, height); + + setOnMouseClicked(e -> { + active = false; + setVisible(false); + setManaged(false); + }); + } +} \ No newline at end of file diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View1.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View1.java new file mode 100644 index 0000000000000000000000000000000000000000..2fb6b9e47592231bb38251ec0153fc4cf228d8df --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View1.java @@ -0,0 +1,63 @@ +package fi.utu.tech.gui.javafx.gimmick; + +import javafx.scene.image.PixelFormat; +import javafx.scene.image.WritableImage; + +import java.util.Random; +import java.util.Timer; +import java.util.TimerTask; + + +class View1 extends View { + protected final int width, height; + private final WritableImage buffer; + private final int[] data; + + View1(int width, int height) { + super(width, height); + this.width = width; + this.height = height; + buffer = new WritableImage(width, height); + data = new int[width * height]; + } + + public void run() { + launchTimer(); + // uncomment to test if it fails (java.lang.InternalError). + // it should work if you managed to program this correctly! + //launchTimer(); + } + + void render() { + // uncomment to test if it fails + // it should work if you managed to program this correctly! + //if (!Platform.isFxApplicationThread()) throw new Error("Wrong thread!"); + + for (int pixelIdx = 0; pixelIdx < data.length; pixelIdx++) { + final int idx = pixelIdx; + new Thread(() -> { + data[idx] = new Random().nextInt(200) | ((new Random().nextInt(16) * 16) << 24); + }).run(); + } + + buffer.getPixelWriter().setPixels( + 0, 0, + width, height, + PixelFormat.getIntArgbPreInstance(), data, 0, width); + + getGraphicsContext2D().clearRect(0.0, 0.0, width, height); + getGraphicsContext2D().drawImage(buffer, 0.0, 0.0); + } + + void launchTimer() { + final Thread t = new Thread(() -> { + render(); + }); + new Timer().schedule(new TimerTask() { + @Override + public void run() { + t.run(); + } + }, 0l, 1000l); + } +} \ No newline at end of file diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View2.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View2.java new file mode 100644 index 0000000000000000000000000000000000000000..be806677ae20f6942a7971a76799869d29b080d7 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View2.java @@ -0,0 +1,36 @@ +package fi.utu.tech.gui.javafx.gimmick; + +import javafx.scene.paint.Color; + +import java.util.Random; + +class View2 extends View { + protected final int width, height; + + View2(int width, int height) { + super(width, height); + this.width = width; + this.height = height; + } + + public void run() { + while (true) { + render(); + try { + Thread.sleep(200 + new Random().nextInt(10) * 100); + } catch (Exception e) { + } + } + } + + void render() { + // uncomment to test if it fails + // it should work if you managed to program this correctly! + //if (!Platform.isFxApplicationThread()) throw new Error("Wrong thread!"); + + getGraphicsContext2D().setFill(new Color(0.5,0,0,0.5)); + getGraphicsContext2D().fillRect(0.0, 0.0, width, height); + getGraphicsContext2D().setFill(new Color(0,0,1,0.9)); + getGraphicsContext2D().fillOval(new Random().nextDouble() * width, new Random().nextDouble() * height, 20, 20); + } +} \ No newline at end of file diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/MainApp1.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/MainApp1.java new file mode 100644 index 0000000000000000000000000000000000000000..9f4449e54d23008d8da87a4062789640c25f6384 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/MainApp1.java @@ -0,0 +1,27 @@ +package fi.utu.tech.gui.javafx.zipper; + +import fi.utu.tech.gui.javafx.MainApp; +import fi.utu.tech.gui.javafx.ResourceLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; + +public class MainApp1 extends MainApp { + @Override + public void start(Stage stage) { + ResourceLoader<Parent, ZipperController> loader = new ResourceLoader<>("zipper.fxml"); + + + + loader.controller.setLabel("Welcome!"); + loader.controller.setStage(stage); + + Scene scene = new Scene(loader.root); + + stage.setTitle("Zip Master 2020"); + stage.setScene(scene); + stage.show(); + + } +} \ No newline at end of file diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/Zipper.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/Zipper.java new file mode 100644 index 0000000000000000000000000000000000000000..11ac8a08ab764a7f90024b2e82eec2db03011cb7 --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/Zipper.java @@ -0,0 +1,134 @@ +package fi.utu.tech.gui.javafx.zipper; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import javafx.application.Platform; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.concurrent.Task; +import javafx.scene.control.Label; + +public class Zipper extends Task<Void> { + + String sourceDir; + String destinationFile; + StringProperty currentFile = new SimpleStringProperty(); + StringProperty zipItButtonText = new SimpleStringProperty(); + FileOutputStream fos; + ZipOutputStream zipOut; + BooleanProperty currentlyZipping = new SimpleBooleanProperty(); + //Boolean currentlyZipping; + + public Zipper (String sourceDir, String destinationFile) { + this.sourceDir = sourceDir; + this.destinationFile = destinationFile; + try { + this.fos = new FileOutputStream(destinationFile); + this.zipOut = new ZipOutputStream(fos); + } + catch(Exception e) {} + + } + + //runlater + public Void call() { + + //if(currentlyZipping) { + // cancelZip(); + //} + //currentlyZipping.set(true); + //currentlyZipping = true; + //System.out.println("zipper "+ currentlyZipping.get()); + + try{ + + //Platform.runLater(() -> {zipItButtonText.set("Cancel"); + //}); + + zip(); + + //currentlyZipping.set(false); + //currentlyZipping = false; + + }catch(Exception e){} + return null; + } + + public void cancelZip() { + try { + + fos.close(); + + } catch(Exception e) {} + + Platform.runLater(() -> {zipItButtonText.set("Zip It!"); + }); + + Platform.runLater(() -> {currentFile.set("Zippaus peruutettu"); + }); + + } + + public void zip() throws IOException { + { + + + File fileToZip = new File(sourceDir); + + //currentFileLabel.setText(fileToZip.getName()); + + + + //System.out.println(fileToZip.getName()); + + zipFile(fileToZip, fileToZip.getName(), zipOut); + + } + } + + private void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException { + Platform.runLater(() -> {currentFile.set("Zipataan tiedostoa: " + fileToZip.getName()); + }); + if (fileToZip.isHidden()) { + return; + } + if (fileToZip.isDirectory()) { + if (fileName.endsWith("/")) { + zipOut.putNextEntry(new ZipEntry(fileName)); + zipOut.closeEntry(); + } else { + zipOut.putNextEntry(new ZipEntry(fileName + "/")); + zipOut.closeEntry(); + } + File[] children = fileToZip.listFiles(); + for (File childFile : children) { + zipFile(childFile, fileName + "/" + childFile.getName(), zipOut); + } + + Platform.runLater(() -> {currentFile.set("Valamis"); + }); + + return; + } + try (FileInputStream fis = new FileInputStream(fileToZip)) { + ZipEntry zipEntry = new ZipEntry(fileName); + zipOut.putNextEntry(zipEntry); + byte[] bytes = new byte[1024]; + int length; + while ((length = fis.read(bytes)) >= 0) { + zipOut.write(bytes, 0, length); + } + + Platform.runLater(() -> {currentFile.set("Valamis"); + }); + + } + } +} \ No newline at end of file diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/ZipperController.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/ZipperController.java new file mode 100644 index 0000000000000000000000000000000000000000..8293271d15314910ff2526e37ec66d0397bc6e3e --- /dev/null +++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/ZipperController.java @@ -0,0 +1,112 @@ +package fi.utu.tech.gui.javafx.zipper; + +import javafx.application.Platform; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.stage.DirectoryChooser; +import javafx.stage.FileChooser; +import javafx.stage.Stage; + +import java.io.File; +import java.io.IOException; + +public class ZipperController { + private Stage stage; + + public void setStage(Stage stage) { + this.stage = stage; + } + + @FXML + private TextField sourceField; + + @FXML + private TextField destField; + + @FXML + private Button zipItButton; + + public Label statusLabel; + + public Zipper zipper; + + public BooleanProperty zipping = new SimpleBooleanProperty(); + + public void setLabel(String text) { + if (!statusLabel.textProperty().isBound()) + statusLabel.setText(text); + } + + @FXML + void chooseDest(ActionEvent event) { + FileChooser chooser = new FileChooser(); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Zip files (.zip)", "*.zip")); + chooser.setTitle("Destination zip"); + File selected = chooser.showSaveDialog(stage); + if (selected == null) return; + destField.setText(selected.getAbsolutePath()); + if (!destField.getText().endsWith(".zip")) + destField.setText(destField.getText() + ".zip"); + setButtonState(null); + } + + @FXML + void chooseSource(ActionEvent event) { + DirectoryChooser chooser = new DirectoryChooser(); + chooser.setTitle("Source folder"); + File selected = chooser.showDialog(stage); + if (selected == null) return; + sourceField.setText(selected.getAbsolutePath()); + } + + @FXML + //yksisuuntanen bind statuslabelille, zipper status + void zipIt(ActionEvent event) { + try { + + + + zipper = new Zipper(sourceField.getText(), destField.getText()); + + //zipping.bind(zipper.currentlyZipping); + //zipper.currentlyZipping.bind(zipping); + + //zipping.set(true); + //System.out.println(zipping.get()); + + if(zipper.currentlyZipping.get()) { + zipper.cancelZip(); + + }else { + + new Thread(zipper).start(); + + //setLabel(zipper.currentFile); + + statusLabel.textProperty().bind(zipper.currentFile); + + zipItButton.textProperty().bind(zipper.zipItButtonText); + + + + } + + } catch (Exception e) { + setLabel(e.getMessage()); + } + } + + <T> void setButtonState(T t) { + zipItButton.setDisable(!destField.getText().endsWith(".zip")); + } + + public void initialize() { + setButtonState(null); + destField.setOnKeyTyped(this::setButtonState); + } +} \ No newline at end of file diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/alien.png b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/alien.png new file mode 100644 index 0000000000000000000000000000000000000000..e94c775d601e2ae42159af0b4f86b7394ff3c47b Binary files /dev/null and b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/alien.png differ diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/gimmick.fxml b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/gimmick.fxml new file mode 100644 index 0000000000000000000000000000000000000000..f42f9b6f9d3cadc20095990b5feae828446b10f3 --- /dev/null +++ b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/gimmick.fxml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.ScrollPane?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.BorderPane?> +<?import javafx.scene.layout.StackPane?> +<?import javafx.scene.layout.TilePane?> +<?import javafx.scene.shape.Rectangle?> +<?import javafx.scene.text.Font?> + +<StackPane maxHeight="580.0" maxWidth="770.0" stylesheets="@styles.css" xmlns="http://javafx.com/javafx/11.0.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.gimmick.GimmickController"> + <children> + <ImageView fitHeight="580.0" fitWidth="770.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@alien.png" /> + </image> + </ImageView> + <BorderPane> + <center> + <ScrollPane fitToHeight="true" fitToWidth="true" vbarPolicy="ALWAYS" BorderPane.alignment="CENTER"> + <content> + <TilePane fx:id="tilePane" alignment="CENTER" hgap="16.0" opacity="0.85" prefColumns="3" vgap="16.0"> + <padding> + <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" /> + </padding> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" /> + </children> + </TilePane> + </content> + </ScrollPane> + </center> + <top> + <Label text="The Gimmick Show" BorderPane.alignment="CENTER"> + <font> + <Font size="32.0" /> + </font> + </Label> + </top> + </BorderPane> + </children> +</StackPane> diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/hmm.png b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/hmm.png new file mode 100644 index 0000000000000000000000000000000000000000..cb06d8873e15f2f9f690347c2298dea696cac12e Binary files /dev/null and b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/hmm.png differ diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/package.png b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/package.png new file mode 100644 index 0000000000000000000000000000000000000000..643eff6d60113e5e16ab4426c1f3a47ac4878c01 Binary files /dev/null and b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/package.png differ diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/styles.css b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..d1e250b8df11265e6345bd142497f6feee8630e8 --- /dev/null +++ b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/styles.css @@ -0,0 +1,7 @@ +.scroll-pane { + -fx-background-color:transparent; +} + +.scroll-pane > .viewport { + -fx-background-color: transparent; +} diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/zipper.fxml b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/zipper.fxml new file mode 100644 index 0000000000000000000000000000000000000000..9a011d163b0ec3090860b42f5c8681417ac75f25 --- /dev/null +++ b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/zipper.fxml @@ -0,0 +1,70 @@ +<?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.TextField?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.BorderPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> + +<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.zipper.ZipperController"> + <left> + <ImageView fitHeight="368.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER"> + <image> + <Image url="@package.png" /> + </image> + <BorderPane.margin> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </BorderPane.margin> + </ImageView> + </left> + <center> + <BorderPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER"> + <padding> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </padding> + <center> + <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="16.0" BorderPane.alignment="CENTER"> + <padding> + <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" /> + </padding> + <children> + <HBox alignment="CENTER_LEFT" prefHeight="30.0" prefWidth="200.0"> + <children> + <Label prefHeight="30.0" text="Source: " /> + <TextField fx:id="sourceField" prefHeight="30.0" HBox.hgrow="ALWAYS"> + <HBox.margin> + <Insets left="8.0" right="8.0" /> + </HBox.margin> + </TextField> + <Button maxHeight="30.0" maxWidth="30.0" mnemonicParsing="false" onAction="#chooseSource" prefHeight="30.0" prefWidth="30.0" text="..." /> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" prefHeight="30.0" prefWidth="200.0"> + <children> + <Label prefHeight="30.0" text="Destination:" /> + <TextField fx:id="destField" prefHeight="30.0" HBox.hgrow="ALWAYS"> + <HBox.margin> + <Insets left="8.0" right="8.0" /> + </HBox.margin> + </TextField> + <Button maxHeight="30.0" maxWidth="30.0" mnemonicParsing="false" onAction="#chooseDest" prefHeight="30.0" prefWidth="30.0" text="..." /> + </children> + </HBox> + <Button fx:id="zipItButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#zipIt" text="Zip it!" /> + <Label fx:id="statusLabel" text="Loading..." /> + </children></VBox> + </center> + <top> + <Label text="Zip Master 2020" BorderPane.alignment="CENTER"> + <font> + <Font size="26.0" /> + </font></Label> + </top> + </BorderPane> + </center> +</BorderPane>