diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index b931840..e7acc9d 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
- java: [11, 15, 16-ea]
+ java: [15, 16-ea]
fail-fast: false
max-parallel: 4
name: Test JDK ${{ matrix.java }}, ${{ matrix.os }}
diff --git a/README.md b/README.md
index 72a33c7..dfe473e 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
# World Clock - A JavaFX World Clock
-Welcome to the World Clock application! This project is for a series of blogs at foojay.io
-https://foojay.io/today/creating-a-javafx-world-clock-from-scratch-part-1/
+Welcome to the World Clock application! This project is for a series of blog entries at https://foojay.io
+https://foojay.io/today/creating-a-javafx-world-clock-from-scratch-part-1
This is a standard Maven JavaFX modular (JPMS) application.
## Requirements:
-- Maven
-- Java 15 w/JavaFX (ZuluFX) https://www.azul.com/downloads/zulu-community/?package=jdk-fx
+- Maven 3.6.3 or greater
+- Java 15+
## Run World Clock
diff --git a/pom.xml b/pom.xml
index f7e4f0f..8c21d22 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
1.0-SNAPSHOT
UTF-8
- 11
+ 15
15
@@ -27,9 +27,14 @@
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
-
- ${maven.compiler.release}
-
+
+
+ default-compile
+
+ ${maven.compiler.release}
+
+
+
org.openjfx
diff --git a/src/main/java/com/carlfx/worldclock/App.java b/src/main/java/com/carlfx/worldclock/App.java
index 44b7a57..9512a29 100644
--- a/src/main/java/com/carlfx/worldclock/App.java
+++ b/src/main/java/com/carlfx/worldclock/App.java
@@ -7,28 +7,35 @@ import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
-import javafx.event.ActionEvent;
-import javafx.event.EventHandler;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D;
+import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
-
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
/**
* JavaFX World Clock
+ *
+ * Creative commons attribution:
+ * Font Awesome was used for buttons https://fontawesome.com/license
*/
public class App extends Application {
@@ -41,6 +48,7 @@ public class App extends Application {
"Roboto-Medium.ttf",
"RobotoMono-Medium.ttf"
};
+
@Override
public void init() throws Exception {
super.init();
@@ -53,18 +61,23 @@ public class App extends Application {
T childNode = (T) node.lookup("#"+id);
return childNode;
}
- Location[] locations = new Location[] {
- new USLocation("GMT-5", "Pasadena, MD", "US", 32.0f, Location.TEMP_STD.FAHRENHEIT),
- new USLocation("GMT-8", "Sunnyvale, CA", "US", 60.0f, Location.TEMP_STD.FAHRENHEIT),
- new Location("GMT+1", "Amsterdam", "NL", 4.0f, Location.TEMP_STD.CELSIUS),
- new Location("GMT+1", "Münster", "DE",5.0f, Location.TEMP_STD.CELSIUS)
- };
+ static ObservableList locations = FXCollections.observableArrayList();
+
+
@Override
public void start(Stage stage) throws IOException {
stage.initStyle(StageStyle.TRANSPARENT);
stage.setOpacity(.75);
this.stage = stage;
+ // fake data
+ locations.addAll(
+ new USLocation("GMT-5", "Pasadena, MD", "US", 32.0f, Location.TEMP_STD.FAHRENHEIT),
+ new USLocation("GMT-8", "Sunnyvale, CA", "US", 60.0f, Location.TEMP_STD.FAHRENHEIT),
+ new Location("GMT+1", "Amsterdam", "NL", 4.0f, Location.TEMP_STD.CELSIUS),
+ new Location("GMT+1", "Münster", "DE",5.0f, Location.TEMP_STD.CELSIUS)
+ );
+
SimpleLongProperty epochTime = new SimpleLongProperty(new Date().getTime());
// each tick update epoch property
Timeline timeline = new Timeline(
@@ -79,9 +92,14 @@ public class App extends Application {
// each controller will attach a listener when epochTime changes.
clocks.add(loadClockFXML(location, epochTime));
}
-
+ BorderPane windowContainer = new BorderPane();
+ windowContainer.getStyleClass().add("window-container");
VBox clockList = new VBox();
-
+ Parent windowBar = loadWindowControlsFXML(locations);
+ BorderPane.setAlignment(windowBar, Pos.CENTER_RIGHT);
+ windowContainer.setTop(windowBar);
+ windowContainer.setCenter(clockList);
+ makeDraggable(windowContainer);
// fake map
ImageView mapImage = new ImageView(new Image(App.class.getResourceAsStream("Mapimage.png")));
@@ -90,9 +108,8 @@ public class App extends Application {
.addAll(clocks);
clockList.getChildren()
.add(mapImage);
- makeDraggable(clockList);
- scene = new Scene(clockList);
+ scene = new Scene(windowContainer);
scene.getStylesheets()
.add(getClass()
.getResource("styles.css")
@@ -140,6 +157,19 @@ public class App extends Application {
controller.init(location, epochTime);
return parent;
}
+ private static Parent loadWindowControlsFXML(ObservableList locations) throws IOException {
+ FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("window-controls.fxml"));
+ Parent parent = fxmlLoader.load();
+ WindowController controller = fxmlLoader.getController();
+ controller.init(locations);
+ return parent;
+ }
+
+ @Override
+ public void stop() {
+ Platform.exit();
+ System.exit(0);
+ }
public static void main(String[] args) {
System.setProperty("prism.lcdtext", "false");
diff --git a/src/main/java/com/carlfx/worldclock/Location.java b/src/main/java/com/carlfx/worldclock/Location.java
index 7eabf91..9465f15 100644
--- a/src/main/java/com/carlfx/worldclock/Location.java
+++ b/src/main/java/com/carlfx/worldclock/Location.java
@@ -48,10 +48,20 @@ public class Location {
CELSIUS,
FAHRENHEIT
}
- public Location(String timezone, String city, String countryCode, float temp, TEMP_STD tempType) {
+
+ public Location(String timezone, String city, String countryCode) {
this.timezone = timezone;
this.city = city;
this.countryCode = countryCode;
+ }
+
+ public Location(String timezone, String city, String countryCode, double [] latLong) {
+ this(timezone, city, countryCode);
+ this.latLong = latLong;
+ }
+
+ public Location(String timezone, String city, String countryCode, float temp, TEMP_STD tempType) {
+ this(timezone, city, countryCode);
this.temperature = temp;
this.tempType = tempType;
}
@@ -101,9 +111,19 @@ public class Location {
}
public void setLatLong(double[] latLong) {
+ if (latLong == null || latLong.length != 2) {
+ throw new IllegalArgumentException("Latitude and Longitude is a two element array of type double.");
+ }
this.latLong = latLong;
}
+ public double getLatitude() {
+ return this.latLong[0];
+ }
+ public double getLongitude() {
+ return this.latLong[1];
+ }
+
public Image getWeatherImage() {
return weatherImage;
}
diff --git a/src/main/java/com/carlfx/worldclock/WindowController.java b/src/main/java/com/carlfx/worldclock/WindowController.java
new file mode 100644
index 0000000..1e7af4d
--- /dev/null
+++ b/src/main/java/com/carlfx/worldclock/WindowController.java
@@ -0,0 +1,49 @@
+package com.carlfx.worldclock;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.input.MouseEvent;
+import javafx.stage.Stage;
+
+public class WindowController {
+ @FXML
+ private Button closeButton;
+
+ @FXML
+ private Button configButton;
+
+ private ObservableList locations;
+
+ public void init(ObservableList locations) {
+ this.locations = locations;
+ }
+
+ private Object targetButton;
+
+ @FXML
+ private void handleEnterIngnoreAction(MouseEvent mouseEvent) {
+ targetButton = mouseEvent.getTarget();
+ }
+ @FXML
+ private void handleExitIngnoreAction(MouseEvent mouseEvent) {
+ targetButton = null;
+ }
+
+ @FXML
+ private void handleCloseWindowAction(MouseEvent mouseEvent) {
+ if (closeButton.equals(targetButton)) {
+ Stage stage = (Stage) closeButton
+ .getScene()
+ .getWindow();
+ stage.close();
+ }
+ }
+
+ @FXML
+ private void handleConfigWorldClockAction(MouseEvent mouseEvent) {
+ if (configButton.equals(targetButton)) {
+ System.out.println("config button hit " + mouseEvent);
+ }
+ }
+}
diff --git a/src/main/resources/com/carlfx/worldclock/styles.css b/src/main/resources/com/carlfx/worldclock/styles.css
index b4f441b..e2d0686 100644
--- a/src/main/resources/com/carlfx/worldclock/styles.css
+++ b/src/main/resources/com/carlfx/worldclock/styles.css
@@ -1,5 +1,5 @@
.root {
- -fx-background-color:rgba(0,0,0,0.75);
+ -fx-background-color: transparent;
}
/* Not working look at App.java init() method */
@font-face {
@@ -24,7 +24,9 @@
font-family: 'RobotoMono Medium';
src: url('RobotoMono-Medium.ttf');
}
-
+.window-container {
+ -fx-background-color: transparent;
+}
.clock-background {
-fx-background-color: rgba(0,0,0,.90);
}
diff --git a/src/main/resources/com/carlfx/worldclock/window-controls.css b/src/main/resources/com/carlfx/worldclock/window-controls.css
new file mode 100644
index 0000000..9b5cd39
--- /dev/null
+++ b/src/main/resources/com/carlfx/worldclock/window-controls.css
@@ -0,0 +1,31 @@
+
+
+.window-control-bar {
+ -fx-background-color: linear-gradient(from 0% 0% to 100% 100%, black 0%, #de752f 5%, black 100%);
+
+}
+
+.config-gear {
+ -fx-shape: "M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z";
+ -fx-background-color: white;
+ -fx-min-height: 15;
+ -fx-min-width: 15;
+ -fx-max-height: 15;
+ -fx-max-width: 15;
+}
+.config-gear:hover {
+ -fx-background-color: "#2fdae0";
+}
+
+.window-close {
+ -fx-shape: "M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z";
+ -fx-background-color: white;
+ -fx-min-height: 15;
+ -fx-min-width: 15;
+ -fx-max-height: 15;
+ -fx-max-width: 15;
+}
+
+.window-close:hover {
+ -fx-background-color: "#2fdae0";
+}
\ No newline at end of file
diff --git a/src/main/resources/com/carlfx/worldclock/window-controls.fxml b/src/main/resources/com/carlfx/worldclock/window-controls.fxml
new file mode 100644
index 0000000..a47b28b
--- /dev/null
+++ b/src/main/resources/com/carlfx/worldclock/window-controls.fxml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+