Browse Source

updated UI

master
Rohan Sircar 4 years ago
parent
commit
640b2507eb
  1. 12
      build.sbt
  2. 61
      src/main/resources/fxml/Chat.fxml
  3. 13
      src/main/resources/fxml/Login.fxml
  4. 54
      src/main/resources/fxml/MainView.fxml
  5. 29
      src/main/resources/fxml/default.css
  6. 173
      src/main/resources/styles/chat.css
  7. 85
      src/main/resources/styles/default.css
  8. 5
      src/main/resources/styles/login.css
  9. 57
      src/main/scala/wow/doge/chatto/ApplicationController.scala
  10. 114
      src/main/scala/wow/doge/chatto/control/JFXSmoothScroll.java
  11. 10
      src/main/scala/wow/doge/chatto/control/MessageBox.scala
  12. 5
      src/main/scala/wow/doge/chatto/control/UserBox2.scala
  13. 388
      src/main/scala/wow/doge/chatto/controller/ChatController.scala
  14. 15
      src/main/scala/wow/doge/chatto/controller/LoginController.scala
  15. 19
      src/main/scala/wow/doge/chatto/controller/MainViewController.scala
  16. 4
      src/main/scala/wow/doge/chatto/controller/NavigationController.scala
  17. 2
      src/main/scala/wow/doge/chatto/controller/WorkspaceController.scala
  18. 69
      src/main/scala/wow/doge/chatto/service/CustomSerializer.scala
  19. 14
      src/main/scala/wow/doge/chatto/service/UserService.scala

12
build.sbt

@ -45,13 +45,14 @@ libraryDependencies ++= Seq(
"org.json4s" %% "json4s-jackson" % "3.6.8", "org.json4s" %% "json4s-jackson" % "3.6.8",
"org.scala-lang.modules" %% "scala-async" % "0.10.0", "org.scala-lang.modules" %% "scala-async" % "0.10.0",
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided, "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
// "org.kordamp.ikonli" %% "ikonli-javafx" % "11.4.0",
// "org.kordamp.ikonli" %% "ikonli-fontawesome-pack" % "11.4.0",
// "org.kordamp.ikonli" %% "ikonli-fontawesome5-pack" % "11.4.0",
"org.kordamp.ikonli" % "ikonli-javafx" % "11.4.0",
"org.kordamp.ikonli" % "ikonli-fontawesome-pack" % "11.4.0",
"org.kordamp.ikonli" % "ikonli-fontawesome5-pack" % "11.4.0",
"org.jsoup" % "jsoup" % "1.13.1", "org.jsoup" % "jsoup" % "1.13.1",
"com.sandec" % "mdfx" % "0.1.6", "com.sandec" % "mdfx" % "0.1.6",
"com.softwaremill.sttp.client" %% "async-http-client-backend-future" % "2.1.1", "com.softwaremill.sttp.client" %% "async-http-client-backend-future" % "2.1.1",
"com.softwaremill.quicklens" %% "quicklens" % "1.5.0"
"com.softwaremill.quicklens" %% "quicklens" % "1.5.0",
"net.synedra" % "validatorfx" % "0.1.11"
) )
libraryDependencies += "org.asynchttpclient" % "async-http-client" % "2.12.1" libraryDependencies += "org.asynchttpclient" % "async-http-client" % "2.12.1"
libraryDependencies += "com.softwaremill.macwire" %% "macros" % "2.3.3" libraryDependencies += "com.softwaremill.macwire" %% "macros" % "2.3.3"
@ -66,6 +67,9 @@ libraryDependencies += "org.kordamp.bootstrapfx" % "bootstrapfx-core" % "0.2.4"
// https://mvnrepository.com/artifact/org.json4s/json4s-ext // https://mvnrepository.com/artifact/org.json4s/json4s-ext
libraryDependencies += "org.json4s" %% "json4s-ext" % "3.6.8" libraryDependencies += "org.json4s" %% "json4s-ext" % "3.6.8"
// https://mvnrepository.com/artifact/org.jsoup/jsoup
libraryDependencies += "org.jsoup" % "jsoup" % "1.13.1"
enablePlugins(BuildInfoPlugin) enablePlugins(BuildInfoPlugin)
buildInfoPackage := "wow.doge.chatto" buildInfoPackage := "wow.doge.chatto"

61
src/main/resources/fxml/Chat.fxml

@ -1,49 +1,54 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXListView ?>
<?import javafx.geometry.Insets ?>
<?import javafx.scene.control.Button ?>
<?import javafx.scene.control.Label ?>
<?import javafx.scene.control.TextArea ?>
<?import javafx.scene.layout.BorderPane ?>
<?import javafx.scene.layout.FlowPane ?>
<?import javafx.scene.layout.HBox ?>
<?import javafx.scene.layout.VBox ?>
<?import com.jfoenix.controls.JFXListView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import org.kordamp.ikonli.javafx.FontIcon?>
<!-- <?import com.example.javafx.control.UserBox?> --> <!-- <?import com.example.javafx.control.UserBox?> -->
<!-- fx:controller="com.example.javafx.controller.SimpleUiController" --> <!-- fx:controller="com.example.javafx.controller.SimpleUiController" -->
<BorderPane minHeight="533.0" minWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="wow.doge.chatto.controller.ChatController">
<BorderPane fx:id="chatMainPane" minWidth="800.0" prefHeight="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="wow.doge.chatto.controller.ChatController">
<left> <left>
<VBox fx:id="usersVBox" alignment="TOP_CENTER" prefHeight="200.0" prefWidth="175.0" BorderPane.alignment="CENTER">
<VBox fx:id="usersVBox" alignment="CENTER" prefHeight="200.0" prefWidth="175.0" BorderPane.alignment="CENTER">
<children> <children>
<JFXListView fx:id="usersListView" prefHeight="554.0" prefWidth="175.0" styleClass="my-list-view" />
<JFXListView fx:id="usersListView" prefHeight="561.0" prefWidth="175.0" styleClass="my-list-view" VBox.vgrow="ALWAYS" />
</children> </children>
</VBox> </VBox>
</left> </left>
<center> <center>
<VBox prefHeight="200.0" prefWidth="100.0" spacing="5.0" BorderPane.alignment="CENTER">
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="5.0" BorderPane.alignment="CENTER">
<children> <children>
<BorderPane prefHeight="41.0" prefWidth="620.0"> <BorderPane prefHeight="41.0" prefWidth="620.0">
<center> <center>
<HBox alignment="CENTER" spacing="5.0">
<HBox fx:id="selectedUserBox" alignment="CENTER" spacing="5.0" styleClass="chat-background">
<children> <children>
<Label id="currentUser" fx:id="curUsr" text="User1" />
<Label id="lastActive" fx:id="dataContent" text="User2" />
<Label id="online" text="User2" />
<Label id="currentUser" fx:id="curUsr" styleClass="text-white" text="User1" />
<Label id="lastActive" fx:id="lastActiveLabel" styleClass="text-white" text="User2" />
<Label id="online" fx:id="isOnlineLabel" styleClass="text-white" text="User2" />
</children> </children>
</HBox> </HBox>
</center> </center>
</BorderPane> </BorderPane>
<JFXListView fx:id="chatListView" prefHeight="463.0" prefWidth="610.0" />
<HBox prefHeight="50.0" prefWidth="790.0" spacing="2.0">
<JFXListView fx:id="chatListView" minWidth="100.0" prefHeight="477.0" prefWidth="608.0" styleClass="my-list-view" VBox.vgrow="ALWAYS" />
<HBox prefHeight="50.0" prefWidth="790.0" spacing="2.0" styleClass="chat-background">
<children> <children>
<TextArea fx:id="chatInput" prefHeight="15.0" prefWidth="250.0" HBox.hgrow="ALWAYS" /> <TextArea fx:id="chatInput" prefHeight="15.0" prefWidth="250.0" HBox.hgrow="ALWAYS" />
<FlowPane alignment="CENTER" hgap="2.0" prefHeight="47.0" prefWidth="221.0">
<FlowPane alignment="CENTER" hgap="2.0" prefHeight="50.0" prefWidth="181.0">
<children> <children>
<Button fx:id="logoutButton" styleClass="btn, btn-primary" text="Logout" onAction="#actionLogout" />
<Button fx:id="submitButton" styleClass="btn, btn-primary" text="Submit" />
<Button fx:id="logoutButton" onAction="#actionLogout" styleClass="btn, btn-primary" text="Logout" />
<Button fx:id="submitButton" style="-fx-background-color: transparent;">
<graphic>
<FontIcon accessibleText="Submit" fill="#2f91c9" iconLiteral="fa-chevron-circle-right" iconSize="40" />
</graphic>
</Button>
</children> </children>
</FlowPane> </FlowPane>
@ -55,8 +60,8 @@
</BorderPane.margin> </BorderPane.margin>
</VBox> </VBox>
</center> </center>
<stylesheets>
<!-- <URL value="@../styles/ui.css" />
<URL value="@../styles/bootstrapfx.css" /> -->
</stylesheets>
</BorderPane>
<!-- <stylesheets>
<URL value="@../styles/chat.css" />
<URL value="@../styles/bootstrapfx.css" />
</stylesheets> -->
</BorderPane>

13
src/main/resources/fxml/Login.fxml

@ -15,8 +15,7 @@
<!-- minHeight="533.0" minWidth="800" maxHeight="533.0" maxWidth="800" --> <!-- minHeight="533.0" minWidth="800" maxHeight="533.0" maxWidth="800" -->
<!-- fx:controller="com.example.javafx.controller.LoginController" --> <!-- fx:controller="com.example.javafx.controller.LoginController" -->
<GridPane prefHeight="533.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="wow.doge.chatto.controller.LoginController">
<GridPane id="rootPane" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="wow.doge.chatto.controller.LoginController">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="257.0" minWidth="10.0" prefWidth="122.0" /> <ColumnConstraints hgrow="SOMETIMES" maxWidth="257.0" minWidth="10.0" prefWidth="122.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="667.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="667.0" />
@ -43,14 +42,14 @@
<Font size="14.0" /> <Font size="14.0" />
</font> </font>
</Label> </Label>
<JFXTextField fx:id="usernameTextField" focusColor="#d30699" labelFloat="true" minWidth="196.0" prefHeight="31.0" prefWidth="215.0" id="username" promptText="Username" />
<JFXPasswordField fx:id="passwordTextField" focusColor="#fb06d2" labelFloat="true" minWidth="196.0" prefHeight="31.0" prefWidth="215.0" id="password" promptText="Password" />
<JFXTextField id="username" fx:id="usernameTextField" focusColor="#d30699" labelFloat="true" minWidth="196.0" prefHeight="31.0" prefWidth="215.0" promptText="Username" />
<JFXPasswordField id="password" fx:id="passwordTextField" focusColor="#fb06d2" labelFloat="true" minWidth="196.0" prefHeight="31.0" prefWidth="215.0" promptText="Password" />
<!-- <JFXButton fx:id="submitButton" buttonType="RAISED" prefHeight="37.0" prefWidth="110.0" ripplerFill="WHITE" style="-fx-background-color: #fb06d2; -fx-background-radius: 50px;" text="Get started" textFill="WHITE" /> --> <!-- <JFXButton fx:id="submitButton" buttonType="RAISED" prefHeight="37.0" prefWidth="110.0" ripplerFill="WHITE" style="-fx-background-color: #fb06d2; -fx-background-radius: 50px;" text="Get started" textFill="WHITE" /> -->
<JFXButton fx:id="submitButton" styleClass="btn, btn-primary" text="Submit" onAction="#actionLogin" />
<JFXButton fx:id="submitButton" onAction="#actionLogin" styleClass="btn, btn-primary" text="Submit" />
<!-- style="-fx-background-radius: 50px;-fx-background-color: #fb06d2" --> <!-- style="-fx-background-radius: 50px;-fx-background-color: #fb06d2" -->
<Label fx:id="errorLabel" alignment="CENTER" prefHeight="37.0" prefWidth="324.0" text="" textAlignment="CENTER" textFill="#727070" wrapText="true">
<Label fx:id="errorLabel" alignment="CENTER" prefHeight="37.0" prefWidth="324.0" style="-fx-text-fill: RED;" textAlignment="CENTER" wrapText="true">
<font> <font>
<Font size="14.0" /> <Font size="14.0" />
</font> </font>
@ -70,7 +69,7 @@
</VBox> </VBox>
</children> </children>
<stylesheets> <stylesheets>
<!-- <URL value="@../styles/style2.css" /> -->
<URL value="@../styles/login.css" />
<URL value="@../styles/bootstrapfx.css" /> <URL value="@../styles/bootstrapfx.css" />
</stylesheets> </stylesheets>
</GridPane> </GridPane>

54
src/main/resources/fxml/MainView.fxml

@ -4,8 +4,27 @@
<?import javafx.scene.layout.* ?> <?import javafx.scene.layout.* ?>
<?import javafx.scene.control.* ?> <?import javafx.scene.control.* ?>
<AnchorPane id="rootPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="wow.doge.chatto.controller.MainViewController">
<children>
<BorderPane id="rootPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="wow.doge.chatto.controller.MainViewController">
<!-- <children>
<BorderPane prefHeight="600.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<top>
<AnchorPane fx:id="navigationPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
prefHeight="40.0" styleClass="navigation"/>
</top>
<center>
<AnchorPane fx:id="workspacePane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
prefHeight="200.0"/>
</center>
<bottom>
<AnchorPane fx:id="statusPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
prefHeight="30.0" styleClass="status"/>
</bottom>
</BorderPane>
<AnchorPane fx:id="mainPane"></AnchorPane>
<AnchorPane fx:id="chatPane"></AnchorPane>
</children> -->
<top>
<MenuBar VBox.vgrow="NEVER" fx:id="menuBar"> <MenuBar VBox.vgrow="NEVER" fx:id="menuBar">
<menus> <menus>
<Menu mnemonicParsing="false" text="File"> <Menu mnemonicParsing="false" text="File">
@ -45,28 +64,13 @@
</Menu> </Menu>
</menus> </menus>
</MenuBar> </MenuBar>
<!-- <BorderPane prefHeight="600.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<top>
<AnchorPane fx:id="navigationPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
prefHeight="40.0" styleClass="navigation"/>
</top>
<center>
<AnchorPane fx:id="workspacePane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
prefHeight="200.0"/>
</center>
<bottom>
<AnchorPane fx:id="statusPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
prefHeight="30.0" styleClass="status"/>
</bottom>
</BorderPane> -->
<AnchorPane fx:id="mainPane"></AnchorPane>
<!-- <AnchorPane fx:id="chatPane"></AnchorPane> -->
</children>
</top>
<center>
<HBox fx:id="mainPane"></HBox>
</center>
<stylesheets> <stylesheets>
<!-- <URL value="@default.css" />
<URL value="@../styles/ui.css" />
<URL value="@../styles/style2.css" /> -->
<URL value="@../styles/default.css" />
<URL value="@../styles/bootstrapfx.css" />
<URL value="@../styles/chat.css" />
</stylesheets> </stylesheets>
</AnchorPane>
</BorderPane>

29
src/main/resources/fxml/default.css

@ -1,29 +0,0 @@
.navigation {
-fx-background-color: derive(cadetblue, 60%);
-fx-font-size: 14px;
}
.status {
-fx-background-color: derive(lightgray, 30%);
-fx-font-size: 12px;
}
.workspace2 {
-fx-background-color: azure;
-fx-font-size: 16px;
}
.workspace {
-fx-background-color: beige;
-fx-font-size: 13px;
}
.personPane {
-fx-font-size: 11px;
}

173
src/main/resources/styles/chat.css

@ -0,0 +1,173 @@
/* .ikonli-font-icon {
-fx-icon-size: 100px;
-fx-icon-color: blue;
} */
.ikonli-font-icon:hover {
-fx-icon-color: #1976a8;
}
#chatMainPane {
-fx-padding: 5px;
}
.my-list-view .scroll-bar:horizontal .track,
.my-list-view .scroll-bar:vertical .track {
-fx-background-color: transparent;
-fx-border-color: transparent;
-fx-background-radius: 0em;
-fx-border-radius: 2em;
}
.my-list-view .scroll-bar:horizontal .increment-button,
.my-list-view .scroll-bar:horizontal .decrement-button {
-fx-background-color: transparent;
-fx-background-radius: 0em;
-fx-padding: 0 0 10 0;
}
.my-list-view .scroll-bar:vertical .increment-button,
.my-list-view .scroll-bar:vertical .decrement-button {
-fx-background-color: transparent;
-fx-background-radius: 0em;
-fx-padding: 0 10 0 0;
}
.my-list-view .scroll-bar .increment-arrow,
.my-list-view .scroll-bar .decrement-arrow {
-fx-shape: " ";
-fx-padding: 0;
}
.my-list-view .scroll-bar:horizontal .thumb,
.my-list-view .scroll-bar:vertical .thumb {
-fx-background-color: derive(black, 90%);
-fx-background-insets: 2, 0, 0;
-fx-background-radius: 2em;
}
.scroll-pane .scroll-bar:horizontal .track,
.scroll-pane .scroll-bar:vertical .track {
-fx-background-color: transparent;
-fx-border-color: transparent;
-fx-background-radius: 0em;
-fx-border-radius: 2em;
}
.scroll-pane .scroll-bar:horizontal .increment-button,
.scroll-pane .scroll-bar:horizontal .decrement-button {
-fx-background-color: transparent;
-fx-background-radius: 0em;
-fx-padding: 0 0 10 0;
}
.scroll-pane .scroll-bar:vertical .increment-button,
.scroll-pane .scroll-bar:vertical .decrement-button {
-fx-background-color: transparent;
-fx-background-radius: 0em;
-fx-padding: 0 10 0 0;
}
.scroll-pane .scroll-bar .increment-arrow,
.scroll-pane .scroll-bar .decrement-arrow {
-fx-shape: " ";
-fx-padding: 0;
}
.scroll-pane .scroll-bar:horizontal .thumb,
.scroll-pane .scroll-bar:vertical .thumb {
-fx-background-color: derive(black, 90%);
-fx-background-insets: 2, 0, 0;
-fx-background-radius: 2em;
}
.scroll-pane {
-fx-background-color: transparent;
}
.scroll-pane > .viewport {
-fx-background-color: transparent;
}
.chat-message-box {
-fx-text-fill: white;
/* -fx-background-color: LIGHTGREEN; */
-fx-background-color: #82ccdd;
-fx-background-radius: 30px;
-fx-padding: 20px;
}
.chat-background {
-fx-background-color: rgba(0, 0, 0, 0.4);
/* -fx-selection-bar: rgba(0, 0, 0, 0.5); */
-fx-text-fill: white;
-fx-background-radius: 20px;
-fx-padding: 5px 10px;
}
.my-list-view {
-fx-background-color: rgba(0, 0, 0, 0.4);
/* -fx-selection-bar: rgba(0, 0, 0, 0.5); */
-fx-text-fill: white;
-fx-background-radius: 20px;
-fx-padding: 5px 10px;
}
.list-cell {
-fx-background-color: transparent;
-fx-text-fill: white;
}
.list-cell:selected {
-fx-background-color: rgba(0, 0, 0, 0.2);
-fx-text-fill: white;
-fx-background-radius: 20px;
}
.list-cell:hover {
-fx-background-color: rgba(0, 0, 0, 0.1);
-fx-text-fill: white;
-fx-background-radius: 20px;
}
.list-cell:selected:hover {
-fx-background-color: rgba(0, 0, 0, 0.3);
-fx-text-fill: white;
-fx-background-radius: 20px;
}
.list-view:focused .list-cell:selected {
-fx-background-color: rgba(0, 0, 0, 0.3);
-fx-text-fill: white;
-fx-background-radius: 20px;
}
.text-area {
-fx-text-fill: white;
-fx-border-radius: 20px;
}
.text-area .content {
-fx-background-color: rgb(116, 116, 116);
-fx-background-radius: 20px;
}
.text-area {
-fx-background-color: rgba(53, 89, 119, 0.4);
}
.text-area .scroll-pane {
-fx-background-color: transparent;
}
.text-area .scroll-pane .viewport {
-fx-background-color: transparent;
}
.text-area .scroll-pane .content {
-fx-background-color: transparent;
}
#chatInput {
-fx-text-fill: white;
-fx-background-radius: 20px;
-fx-padding: 5px 10px;
}

85
src/main/resources/styles/default.css

@ -0,0 +1,85 @@
.navigation {
-fx-background-color: derive(cadetblue, 60%);
-fx-font-size: 14px;
}
.status {
-fx-background-color: derive(lightgray, 30%);
-fx-font-size: 12px;
}
.workspace2 {
-fx-background-color: azure;
-fx-font-size: 16px;
}
.workspace {
-fx-background-color: beige;
-fx-font-size: 13px;
}
.personPane {
-fx-font-size: 11px;
}
.root {
-fx-padding: 5 5 5 5;
}
.scroll-bar:horizontal .track,
.scroll-bar:vertical .track {
-fx-background-color: transparent;
-fx-border-color: transparent;
-fx-background-radius: 0em;
-fx-border-radius: 2em;
}
.scroll-bar:horizontal .increment-button,
.scroll-bar:horizontal .decrement-button {
-fx-background-color: transparent;
-fx-background-radius: 0em;
-fx-padding: 0 0 10 0;
}
.scroll-bar:vertical .increment-button,
.scroll-bar:vertical .decrement-button {
-fx-background-color: transparent;
-fx-background-radius: 0em;
-fx-padding: 0 10 0 0;
}
.scroll-bar .increment-arrow,
.scroll-bar .decrement-arrow {
-fx-shape: " ";
-fx-padding: 0.15em 0;
}
.scroll-bar:vertical .increment-arrow,
.scroll-bar:vertical .decrement-arrow {
-fx-shape: " ";
-fx-padding: 0 0.15em;
}
.scroll-bar:horizontal .thumb,
.scroll-bar:vertical .thumb {
-fx-background-color: derive(black, 90%);
-fx-background-insets: 2, 0, 0;
-fx-background-radius: 2em;
}
.scroll-bar:horizontal .thumb:hover,
.scroll-bar:vertical .thumb:hover {
-fx-background-color: derive(#4d4c4f, 10%);
-fx-background-insets: 2, 0, 0;
-fx-background-radius: 2em;
}
.root {
-fx-background-color: linear-gradient(to right, #91eae4, #86a8e7, #7f7fd5);
}
/* linear-gradient(to right, #91EAE4, #86A8E7, #7F7FD5); */
/* linear-gradient(from 25% 25% to 100% 100%, #dc143c, #661a33)"); */
.text-white {
-fx-text-fill: white;
}

5
src/main/resources/styles/login.css

@ -0,0 +1,5 @@
#rootPane {
-fx-background-image: url("../images/backgroung.jpg");
-fx-background-size: 1920 1080;
-fx-background-position: center center;
}

57
src/main/scala/wow/doge/chatto/ApplicationController.scala

@ -38,19 +38,20 @@ class ApplicationController extends DefaultWindowController {
// override def width: Int = 400 // override def width: Int = 400
@Inject
private var appDataHandler: AppDataHandler = _
// @Inject
// private var appDataHandler: AppDataHandler = _
def applicationDidLaunch() = { def applicationDidLaunch() = {
logger.info("start " + this) logger.info("start " + this)
applicationEnvironment.loadResourceBundle("bundles/application") applicationEnvironment.loadResourceBundle("bundles/application")
replaceSceneContent(mainViewController)
replaceSceneContent(mainViewController.loginController)
} }
@Produces @Produces
def applicationName: ApplicationName = { def applicationName: ApplicationName = {
ApplicationName(configStringValue("application.name")) ApplicationName(configStringValue("application.name"))
} }
@Produces @Produces
def httpBackend = backend def httpBackend = backend
@ -65,15 +66,15 @@ class ApplicationController extends DefaultWindowController {
replaceSceneContent(newMainViewController) replaceSceneContent(newMainViewController)
} }
def testListener(@Observes event: SceneControllerWillChangeEvent) = {
logger.info("test success 1")
// logger.info(s"${event.newController}")
}
// def testListener(@Observes event: SceneControllerWillChangeEvent) = {
// logger.info("test success 1")
// // logger.info(s"${event.newController}")
// }
def testListener2(@Observes event: SceneControllerDidChangeEvent) = {
logger.info("test success 2")
// logger.info(s"${event.newController}")
}
// def testListener2(@Observes event: SceneControllerDidChangeEvent) = {
// logger.info("test success 2")
// // logger.info(s"${event.newController}")
// }
override def applicationWillStop(): Unit = async { override def applicationWillStop(): Unit = async {
super.applicationWillStop() super.applicationWillStop()
@ -84,42 +85,17 @@ class ApplicationController extends DefaultWindowController {
} }
def showLoginPane() = onFX { def showLoginPane() = onFX {
offFX { appDataHandler.clearCredentials() }
// replaceSceneContent(mainViewController.loginController)
// mainViewController.mainManager.updatePaneContent(
// mainViewController.loginController
// )
// offFX { appDataHandler.clearCredentials() }
replaceSceneContent(mainViewController.loginController) replaceSceneContent(mainViewController.loginController)
} }
def logout() = {
def logout() = onFX {
val newMainViewController = getController[MainViewController]() val newMainViewController = getController[MainViewController]()
replaceSceneContent(newMainViewController)
replaceSceneContent(mainViewController.loginController)
} }
def showChatPane(): Unit = onFX { def showChatPane(): Unit = onFX {
// import org.scalafx.extras._
replaceSceneContent(mainViewController.chatController, true)
// mainViewController.mainManager.updatePaneContent(
// mainViewController.chatController
// )
// httpBackend.send(basicRequest.get(uri""))
// val willBeResponse = basicRequest
// .get(uri"https://httpbin.org/get")
// .response(asJson[HttpBinResponse])
// .send()
// async {
// val r = await { willBeResponse }
// r.body.map(println)
// }
// willBeResponse onComplete {
// case Success(x) => { x.body }
// case Failure(x) => {}
// }
// val body = for {
// r <- willBeResponse
// } yield (r.body)
replaceSceneContent(mainViewController, true)
} }
} }
@ -142,7 +118,6 @@ class AppDataHandler {
def appData = _appData def appData = _appData
def updateCredentials(credentials: UserCredentials): Unit = offFX { def updateCredentials(credentials: UserCredentials): Unit = offFX {
println(credentials)
appData = appData.copy(credentials = credentials) appData = appData.copy(credentials = credentials)
} }

114
src/main/scala/wow/doge/chatto/control/JFXSmoothScroll.java

@ -0,0 +1,114 @@
package wow.doge.chatto.control;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.DefaultProperty;
import javafx.beans.property.DoubleProperty;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ListView;
import javafx.geometry.Orientation;
import javafx.scene.control.ScrollBar;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Transform;
import javafx.util.Duration;
import java.util.function.Function;
public class JFXSmoothScroll {
private static ScrollBar getScrollbarComponent(ListView<?> control, Orientation orientation) {
Node n = control.lookup(".scroll-bar");
if (n instanceof ScrollBar) {
final ScrollBar bar = (ScrollBar) n;
if (bar.getOrientation().equals(orientation)) {
return bar;
}
}
return null;
}
public static void smoothScrollingListView(ListView<?> listView, double speed) {
smoothScrollingListView(listView, speed, Orientation.VERTICAL, bounds -> bounds.getHeight());
}
public static void smoothHScrollingListView(ListView<?> listView, double speed) {
smoothScrollingListView(listView, speed, Orientation.HORIZONTAL, bounds -> bounds.getHeight());
}
private static void smoothScrollingListView(ListView<?> listView, double speed, Orientation orientation,
Function<Bounds, Double> sizeFunc) {
ScrollBar scrollBar = getScrollbarComponent(listView, orientation);
if (scrollBar == null) {
return;
}
scrollBar.setUnitIncrement(5);
final double[] frictions = { 0.99, 0.1, 0.05, 0.04, 0.03, 0.02, 0.01, 0.04, 0.01, 0.008, 0.008, 0.008, 0.008,
0.0006, 0.0005, 0.00003, 0.00001 };
final double[] pushes = { speed };
final double[] derivatives = new double[frictions.length];
final double[] lastVPos = { 0 };
Timeline timeline = new Timeline();
final EventHandler<MouseEvent> dragHandler = event -> timeline.stop();
final EventHandler<ScrollEvent> scrollHandler = event -> {
scrollBar.valueProperty().set(lastVPos[0]);
if (event.getEventType() == ScrollEvent.SCROLL) {
double direction = event.getDeltaY() > 0 ? -1 : 1;
for (int i = 0; i < pushes.length; i++) {
derivatives[i] += direction * pushes[i];
}
if (timeline.getStatus() == Animation.Status.STOPPED) {
timeline.play();
}
}
event.consume();
};
if (scrollBar.getParent() != null) {
scrollBar.getParent().addEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
scrollBar.getParent().addEventHandler(ScrollEvent.ANY, scrollHandler);
}
scrollBar.parentProperty().addListener((o, oldVal, newVal) -> {
if (oldVal != null) {
oldVal.removeEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
oldVal.removeEventHandler(ScrollEvent.ANY, scrollHandler);
}
if (newVal != null) {
newVal.addEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
newVal.addEventHandler(ScrollEvent.ANY, scrollHandler);
}
});
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(3), (event) -> {
for (int i = 0; i < derivatives.length; i++) {
derivatives[i] *= frictions[i];
}
for (int i = 1; i < derivatives.length; i++) {
derivatives[i] += derivatives[i - 1];
}
double dy = derivatives[derivatives.length - 1];
double size = sizeFunc.apply(scrollBar.getLayoutBounds());
scrollBar.valueProperty().set(Math.min(Math.max(scrollBar.getValue() + dy / size, 0), 1));
lastVPos[0] = scrollBar.getValue();
if (Math.abs(dy) < 1) {
if (Math.abs(dy) < 0.001) {
timeline.stop();
}
}
}));
timeline.setCycleCount(Animation.INDEFINITE);
}
}

10
src/main/scala/wow/doge/chatto/control/MessageBox.scala

@ -0,0 +1,10 @@
package wow.doge.chatto.control
import javafx.scene.layout.HBox
import javafx.scene.control.Label
import scalafx.Includes._
import wow.doge.chatto.controller.ChatData
import com.sfxcode.sapphire.core.value.FXBean
class MessageBox(val sender: String, val receiver: String, val message: String)
extends HBox() {}

5
src/main/scala/wow/doge/chatto/control/UserBox2.scala

@ -7,7 +7,10 @@ import wow.doge.chatto.controller.ChatData
import com.sfxcode.sapphire.core.value.FXBean import com.sfxcode.sapphire.core.value.FXBean
class UserBox2(val username: String, val chatData: ChatData) extends HBox() { class UserBox2(val username: String, val chatData: ChatData) extends HBox() {
val usernameLabel = new Label(username)
val usernameLabel = new Label(username) {
// this.style = "-fx-text-fill: white"
this.styleClass ++= Seq("text-white")
}
// lazy val chatDataBean = FXBean[ChatData](chatData) // lazy val chatDataBean = FXBean[ChatData](chatData)
this.children ++= Seq { this.children ++= Seq {
usernameLabel usernameLabel

388
src/main/scala/wow/doge/chatto/controller/ChatController.scala

@ -18,7 +18,6 @@ import wow.doge.chatto.service.UserService
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import com.typesafe.scalalogging.LazyLogging import com.typesafe.scalalogging.LazyLogging
// import wow.doge.chatto.controller.LoginController.Person
import com.sfxcode.sapphire.core.value.FXBean import com.sfxcode.sapphire.core.value.FXBean
import wow.doge.chatto.AppDataHandler import wow.doge.chatto.AppDataHandler
import com.jfoenix.controls.JFXListView import com.jfoenix.controls.JFXListView
@ -41,6 +40,27 @@ import wow.doge.chatto.messagebuble.BubbleSpec
import javafx.scene.layout.Background import javafx.scene.layout.Background
import javafx.scene.layout.BackgroundFill import javafx.scene.layout.BackgroundFill
import javafx.geometry.Pos import javafx.geometry.Pos
import scalafx.beans.property.ReadOnlyBufferProperty
import scalafx.beans.property.ReadOnlyBufferWrapper
import javafx.beans.property.ReadOnlyListProperty
import scalafx.beans.property.BufferProperty
import javafx.collections.FXCollections
import com.sandec.mdfx.MDFXNode
import javafx.scene.layout.BorderPane
import javafx.scene.layout.Priority
import net.synedra.validatorfx.Validator
import wow.doge.chatto.control.JFXSmoothScroll
import javafx.scene.control.ContextMenu
import javafx.scene.control.MenuItem
import javafx.scene.input.Clipboard
import javafx.scene.input.ClipboardContent
import scalafx.scene.input.KeyCodeCombination
import scalafx.scene.input.KeyCode
import scalafx.scene.input.KeyCombination
import javafx.scene.input.DataFormat
import wow.doge.chatto.control.MessageBox
import javafx.scene.control.SelectionMode
import scalafx.beans.property.BooleanProperty
class ChatController @Inject() ( class ChatController @Inject() (
userService: UserService, userService: UserService,
@ -49,71 +69,54 @@ class ChatController @Inject() (
with LazyLogging { with LazyLogging {
// @FXML private var label: Label = _ // @FXML private var label: Label = _
@FXML private var flowPane: FlowPane = _
@FXML var chatMainPane: BorderPane = _
// @FXML private var flowPane: FlowPane = _
@FXML private var submitButton: Button = _ @FXML private var submitButton: Button = _
@FXML private var logoutButton: Button = _ @FXML private var logoutButton: Button = _
// @FXML private var chatTextArea: TextArea = _ // @FXML private var chatTextArea: TextArea = _
@FXML private var chatInput: TextArea = _ @FXML private var chatInput: TextArea = _
@FXML private var usersVBox: VBox = _ @FXML private var usersVBox: VBox = _
@FXML var usersListView: JFXListView[UserBox2] = _ @FXML var usersListView: JFXListView[UserBox2] = _
@FXML var chatListView: JFXListView[HBox] = _
@FXML var chatListView: JFXListView[MessageBox] = _
@FXML private var curUsr: Label = _ @FXML private var curUsr: Label = _
@FXML private var dataContent: Label = _
// applicationController.show
private val usersListProperty = new SimpleListProperty(
ObservableBuffer[UserBox2]()
@FXML private var lastActiveLabel: Label = _
@FXML private var isOnlineLabel: Label = _
@FXML private var selectedUserBox: HBox = _
private val usersBuffer = ObservableBuffer.empty[UserBox2]
private val usersListProperty = BufferProperty(usersBuffer)
/**
* Readonly property wrapping an unmodifiable list.
* Synchronized with the internal users list property.
* Attemping to modify the internal list will throw an exception
*/
val usersListROProp: ReadOnlyListProperty[UserBox2] = BufferProperty(
FXCollections.unmodifiableObservableList(usersListProperty())
) )
private val chatDataBuffer = ObservableBuffer[UserBox2]()
private val chatDataStore: mutable.Map[String, ChatDataProperty] =
TrieMap()
// private lazy val curUserKeys = KeyBindings("curUser")
// bindings.add("person", "Person ${_self.name()} with age of ${_self.age()} is active: ${_self.isActive()}")
// private lazy val curUserAdapter = FXBeanAdapter[User](this)
private val chatDataStore = TrieMap.empty[String, ChatDataProperty]
private lazy val chatDataAdapter = FXBeanAdapter[ChatData](this) private lazy val chatDataAdapter = FXBeanAdapter[ChatData](this)
override def didGainVisibilityFirstTime(): Unit = { override def didGainVisibilityFirstTime(): Unit = {
super.didGainVisibilityFirstTime() super.didGainVisibilityFirstTime()
// val ub = new UserBox()
this.stage.resizable = true this.stage.resizable = true
// ub.messageLabel.text = "Hi there"
// ub.messageLabel.id =
// ub.userRadioButton.text = "User 1"
// usersVBox.children.clear()
// usersVBox.children += ub
// println("test")
// println(s"Result = ${func()}")
// offFX(println("hello from new thread"))
// chatTextArea.visible <== !chatInput.text.isEmpty
// chatTextArea.text <== chatInput.text
// for (r <- userService.func2()) yield (logger.info(s"${r.body}"))
// val person = Person(0, 10, "Billy")
// val bean = FXBean[Person](person)
// ub.messageLabel.text <== bean.getStringProperty("name")
// bean.getStringProperty("name") <== chatInput.text
// bean.getStringProperty("name")() = "Lester"
// println(bean.getValue("name"))
// println(bean.getStringProperty("name")())
// bean.
// bean.updateValue("name", "Lester")
// println(bean.bean)
// val curUserBean = FXBean[User](User("None"))
// logoutButton.onAction = (e) =>
// usersListView
// .getSelectionModel()
// .selectedItemProperty()
// .addListener((_, _, newValue) => {
// curUsr.text() = newValue.usernameLabel.text()
// })
chatMainPane.hgrow = Priority.ALWAYS
chatListView.selectionModel().selectionMode = SelectionMode.MULTIPLE
chatDataAdapter.set(FXBean(ChatData.empty))
Array(submitButton, chatInput).foreach(n => {
n.disableProperty() <== usersListView
.selectionModel()
.selectedItemProperty()
.isNull()
})
// curUserKeys.add("content", "${_self.data().content()}") // curUserKeys.add("content", "${_self.data().content()}")
val chatDataAdapterKeys = KeyBindings() val chatDataAdapterKeys = KeyBindings()
// chatDataAdapterKeys.add("currentUser", "${_self.userName()}")
chatDataAdapterKeys.add("currentUser", "${_self.userName()}")
// chatDataAdapterKeys.add( // chatDataAdapterKeys.add(
// "lastActive", // "lastActive",
// "${_self.lastActiveString()}" // "${_self.lastActiveString()}"
@ -125,39 +128,97 @@ class ChatController @Inject() (
// curUserAdapter.addDateConverter() // curUserAdapter.addDateConverter()
// curUserAdapter.addBindings(curUserKeys) // curUserAdapter.addBindings(curUserKeys)
chatDataAdapter.addBindings(chatDataAdapterKeys) chatDataAdapter.addBindings(chatDataAdapterKeys)
// curUsr.text <== chatDataAdapter.beanProperty
// .getOrElse(FXBean(ChatData.empty))
// .getStringProperty("lastActiveString")
usersListView usersListView
.getSelectionModel()
.selectionModel()
.selectedItemProperty() .selectedItemProperty()
.addListener((_, _, newValue) => { .addListener((_, _, newValue) => {
if (newValue != null) {
// val dataBean =
// FXBean(User(newValue.usernameLabel.text(), Data("test data")))
// curUserAdapter
// .set(dataBean)
// chatDataAdapter.set(newValue.chatDataBean)
val y = chatDataStore
.get(newValue.username)
val x = y
Option(newValue).foreach(nv => {
val maybeCDP = chatDataStore
.get(nv.username)
val chatDataBean = maybeCDP
.map(_.bean) .map(_.bean)
.getOrElse { .getOrElse {
logger.error("Error null") logger.error("Error null")
FXBean(ChatData.empty) FXBean(ChatData.empty)
} }
chatDataAdapter.set(x)
y.map(z => chatListView.items <== z.prop)
}
chatDataAdapter.set(chatDataBean)
maybeCDP.foreach(cdp => {
// lastActiveLabel.text <== cdp.lastActive
isOnlineLabel.text <== cdp.isActive.asString()
chatListView.items <== cdp.messageBubbles
})
})
})
val copyMessageMenuItem = new MenuItem("Copy Message")
copyMessageMenuItem.accelerator =
new KeyCodeCombination(KeyCode.C, KeyCombination.ControlDown)
copyMessageMenuItem.onAction = _ => {
val content = new ClipboardContent()
val x = chatListView.getSelectionModel().getSelectedItems().map(_.message)
// chatListView.selectionModel().selectedItem().message
Option(x).foreach(message => {
content.putString(message.mkString("\n"))
Clipboard.getSystemClipboard().setContent(content)
}) })
// val message = cdp.messageList(selectedIndex)
// val maybeCDP = chatDataStore.get(chatDataAdapter.get.bean.userName)
// maybeCDP.foreach(cdp => {
// // val message = Option(cdp.messageList().get(selectedIndex)).toRight {
// // "Unexpected error - message not found"
// // }
// // message.map(msg => {
// // // content.putString(msg)
// // // clipboard.setContent(content)
// // // val content = clipboard.content
// // // content.putString(msg)
// // // clipboard.content = content
// // clipboard.putString(msg)
// // })
// // message.left.map(err => logger.error(err))
// })
}
val chatListMenu = new ContextMenu()
// curUsr.text <== usersListView
// .getSelectionModel()
// .getSelectedItem()
// .usernameLabel
// .text
chatListMenu.items += copyMessageMenuItem
chatListView.contextMenu = chatListMenu
usersListView.items <== usersListProperty usersListView.items <== usersListProperty
val validator = new Validator()
submitButton.disable <== validator.containsErrorsProperty()
submitButton.onAction = (e) => {
// val msgBox = ChatDataProperty.createMdMessageBox2(chatInput.text())
// chatListView.items() += msgBox
if (!chatInput.text().equals("") &&
!chatInput.text().equals(" ") &&
!chatInput.text().equals("\n")) {
val maybeCDP = chatDataStore.get(chatDataAdapter.get.bean.userName)
maybeCDP.foreach(cdp => {
cdp.messageBubbles += ChatDataProperty.createMdMessageBox3(
appDataHandler.appData.credentials.username,
cdp.username(),
chatInput.text()
)
})
}
}
validator
.createCheck()
.withMethod(c => {
val userName = chatInput.text()
if (!userName.toLowerCase().equals(userName)) {
c.error("Please use only lowercase letters.")
}
})
.dependsOn("chatInput", chatInput.text)
.decorates(chatInput)
.immediate()
} }
override def didGainVisibility(): Unit = { override def didGainVisibility(): Unit = {
@ -165,70 +226,60 @@ class ChatController @Inject() (
chatInput.requestFocus() chatInput.requestFocus()
async { async {
val willBeUsers = userService
.getUsers(appDataHandler.appData.credentials)
.map(_.body)
val willBeActiveUsers = userService val willBeActiveUsers = userService
.getActiveUsers(appDataHandler.appData.credentials) .getActiveUsers(appDataHandler.appData.credentials)
.map(_.body) .map(_.body)
val maybeUsers = await(willBeUsers)
val maybeActiveUsers = await(willBeActiveUsers) val maybeActiveUsers = await(willBeActiveUsers)
// maybeActiveUsers.foreach(println)
logger.debug(s"$maybeActiveUsers")
logger.debug(s"Received Users: $maybeActiveUsers")
val maybeUserBoxes = maybeActiveUsers.map(users => { val maybeUserBoxes = maybeActiveUsers.map(users => {
// usersBuffer ++= users
users.map(user => { users.map(user => {
// usersBuffer += FXBean(User(user))
// val ub = new UserBox()
// ub.messageLabel.text = "Hi there"
// ub.userRadioButton.text = user
// ub
// new Rectangle {
// fill <== when(hover) choose (Color.Red)
// }
// val hb = new HBox
val chatData = val chatData =
ChatData(user.userName, user, ObservableBuffer.empty[String]) ChatData(user.userName, user, ObservableBuffer.empty[String])
// chatDataStore.updateWith(user.userName)(maybeCD =>
// maybeCD.map(cd => {
// cd.messagesStringProperty +=
// })
// )
chatDataStore.put(user.userName, new ChatDataProperty(chatData)) chatDataStore.put(user.userName, new ChatDataProperty(chatData))
chatDataStore
.get(user.userName)
.map(cdp => {
cdp.prop ++= Seq("hi", "hello", "bye")
.map(s => ChatDataProperty.createMdMessageBox(s))
})
new UserBox2(user.userName, chatData) { new UserBox2(user.userName, chatData) {
// this.children += new Label {
// textProperty() = user
// // textFillProperty <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE)
// }
// fill <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE)
this.styleClass ++= Seq("text-white")
} }
}) })
}) })
// maybeUserBoxes.map(userBoxes => {
// // chatDataBuffer ++= userBoxes
// usersListProperty ++= userBoxes
// })
val messageBox = ChatDataProperty.createMdMessageBox("*hello!*")
val messageBox = ChatDataProperty.createMdMessageBox2(
"""**Hello world qefwew yeeehay bwergqwevqcqe**
|**Hello world qefwew yeeehay bwergqwevqcqe**
|
| Hello World
""".stripMargin
)
onFX { onFX {
// maybeUserBoxes.map(userBoxes => {
// chatDataBuffer ++= userBoxes
// // usersListView.items() ++= userBoxes
// // val x = new SimpleListProperty(ObservableBuffer(userBoxes))
// // usersListView.items <== x
// })
maybeUserBoxes.map(userBoxes => {
usersListProperty ++= userBoxes
maybeUserBoxes.foreach(userBoxes => {
usersBuffer ++= userBoxes
}) })
chatListView.items() += messageBox
chatListView.items() ++= Seq(
messageBox,
ChatDataProperty.createMdMessageBox2("hello"),
ChatDataProperty.createMdMessageBox2(
""" 1. Hello world qefwew yeeehay bwergqwevqcqe
|1. Hello world qefwew yeeehay bwergqwevqcqe
|1. Hello world qefwew yeeehay bwergqwevqcqe
|1. Hello world qefwew yeeehay bwergqwevqcqe""".stripMargin
)
)
// .map(node => {
// node.prefWidthProperty <== (chatListView.prefWidthProperty - 200)
// node
// })
// JFXSmoothScroll.smoothScrollingListView(chatListView, 0.1)
} }
chatDataStore
.map { case (key, value) => value }
.foreach(cdp => {
cdp.messageBubbles ++= Seq(
ChatDataProperty.createMdMessageBox2("hi"),
ChatDataProperty.createMdMessageBox2("hello"),
ChatDataProperty.createMdMessageBox2("bye")
)
// .map(ChatDataProperty.createMdMessageBox)
})
} }
} }
def func() = { def func() = {
@ -241,40 +292,29 @@ class ChatController @Inject() (
def actionLogout = onFX { def actionLogout = onFX {
offFXAndWait { offFXAndWait {
appDataHandler.clearCredentials() appDataHandler.clearCredentials()
println(appDataHandler.appData)
} }
// curUserAdapter.set(User.empty) // curUserAdapter.set(User.empty)
logger.debug(s"Logout - clearing credentials - ${appDataHandler.appData}")
chatDataAdapter.set(FXBean(ChatData.empty)) chatDataAdapter.set(FXBean(ChatData.empty))
usersListView.items().clear() usersListView.items().clear()
chatListView.items().clear() chatListView.items().clear()
chatDataBuffer.clear()
chatDataStore.clear() chatDataStore.clear()
usersListProperty.clear()
usersBuffer.clear()
chatInput.clear()
this.stage.maximized = false
applicationController.logout() applicationController.logout()
println(appDataHandler.appData)
} }
}
object ChatController {
lazy val markdownStyleSheet =
getClass().getResource("/styles/markdown.css").toExternalForm()
implicit class MyClipboardExtension(clipboard: Clipboard) {
def putString(string: String) = {
// val content = Option(clipboard.getContent(DataFormat.PLAIN_TEXT))
// .getOrElse(new ClipboardContent())
// content.putString(string)
// clipboard.setContent(content)
}
}
} }
final case class Person(id: Int, age: Int, name: String)
final case class Data(content: String)
final case class User(curUser: String, data: Data)
object User {
def empty = User("", Data(""))
// Bindings.createObjectBinding()
}
// final case class ActiveUser(
// userName: String,
// online: Boolean,
// lastActive: String
// )
// object ActiveUser {
// def empty = ActiveUser("empty", false, "empty")
// }
final case class ChatData( final case class ChatData(
userName: String, userName: String,
activeUser: ActiveUser, activeUser: ActiveUser,
@ -294,21 +334,26 @@ object ChatData {
class ChatDataProperty(chatData: ChatData) { class ChatDataProperty(chatData: ChatData) {
import ChatDataProperty._ import ChatDataProperty._
val bean = FXBean(chatData) val bean = FXBean(chatData)
val usernameProperty = bean.getStringProperty("userName")
val isActiveProperty = bean.getBooleanProperty("activeUser.online")
val lastActiveProperty = bean.getStringProperty("lastActiveString")
val messagesStringProperty = new SimpleListProperty(chatData.messages)
val prop = new SimpleListProperty(ObservableBuffer.empty[HBox])
// val x = messagesStringProperty.map(message => createMdMessageBox(message))
// prop <== messagesStringProperty.map(message => createMdMessageBox(message))
// prop <== new SimpleListProperty(x)
val username = bean.getStringProperty("userName")
val isActive = bean.getBooleanProperty("activeUser.online")
val lastActive = bean.getStringProperty("lastActiveString")
lazy val messageBubbles = BufferProperty(
chatData.messages.map(ChatDataProperty.createMdMessageBox2)
)
def messageList = messageBubbles().map(_.message)
// lazy val messages = messagesBubbleProperty
// .get()
// .map(_.messageText)
} }
object ChatDataProperty { object ChatDataProperty {
lazy val markdownStyleSheet =
getClass().getResource("/styles/markdown.css").toExternalForm()
def createMdMessageBox(mdfxText: String) = { def createMdMessageBox(mdfxText: String) = {
val mdfxNode = new BubbledMDFXNode(mdfxText); val mdfxNode = new BubbledMDFXNode(mdfxText);
mdfxNode mdfxNode
.getStylesheets() .getStylesheets()
.add(ChatController.markdownStyleSheet);
.add(markdownStyleSheet);
mdfxNode.setBubbleSpec(BubbleSpec.FACE_RIGHT_CENTER); mdfxNode.setBubbleSpec(BubbleSpec.FACE_RIGHT_CENTER);
mdfxNode.setBackground( mdfxNode.setBackground(
new Background(new BackgroundFill(Color.LIGHTSTEELBLUE, null, null)) new Background(new BackgroundFill(Color.LIGHTSTEELBLUE, null, null))
@ -319,4 +364,49 @@ object ChatDataProperty {
box.children += mdfxNode; box.children += mdfxNode;
box box
} }
def createMdMessageBox2(mdfxText: String) = {
val mdfxNode = new MDFXNode(mdfxText);
mdfxNode
.getStylesheets()
.add(markdownStyleSheet)
mdfxNode.setMaxWidth(500)
mdfxNode.vgrow = Priority.ALWAYS
mdfxNode.setAlignment(Pos.CENTER)
mdfxNode.styleClass = Seq("chat-message-box")
val box = new MessageBox("", "", mdfxText)
box.setAlignment(Pos.CENTER_RIGHT)
// box.maxWidth(500)
box.hgrow = Priority.ALWAYS
box.vgrow = Priority.ALWAYS
box.children ++= Seq(mdfxNode)
box.fillHeight = true
box
}
def createMdMessageBox3(
sender: String,
receiver: String,
mdfxText: String
) = {
val mdfxNode = new MDFXNode(mdfxText);
mdfxNode
.getStylesheets()
.add(markdownStyleSheet)
mdfxNode.setMaxWidth(500)
mdfxNode.vgrow = Priority.ALWAYS
mdfxNode.setAlignment(Pos.CENTER)
mdfxNode.styleClass = Seq("chat-message-box")
val box = new MessageBox(sender, receiver, mdfxText)
box.setAlignment(Pos.CENTER_RIGHT)
// box.maxWidth(500)
box.hgrow = Priority.ALWAYS
box.vgrow = Priority.ALWAYS
box.children ++= Seq(mdfxNode)
box.fillHeight = true
box
}
} }

15
src/main/scala/wow/doge/chatto/controller/LoginController.scala

@ -31,6 +31,8 @@ import wow.doge.chatto.types.AppTypes
import org.scalafx.extras._ import org.scalafx.extras._
import wow.doge.chatto.AppDataHandler import wow.doge.chatto.AppDataHandler
import com.sfxcode.sapphire.core.value.BeanConversions import com.sfxcode.sapphire.core.value.BeanConversions
import javafx.scene.layout.StackPane
import com.jfoenix.controls.JFXSpinner
class LoginController @Inject() ( class LoginController @Inject() (
userService: UserService, userService: UserService,
@ -87,33 +89,31 @@ class LoginController @Inject() (
val inputUserName = usernameTextField.text() val inputUserName = usernameTextField.text()
val inputPassword = passwordTextField.text() val inputPassword = passwordTextField.text()
submitButton.disable = true
login(inputUserName, inputPassword) onComplete { login(inputUserName, inputPassword) onComplete {
case Success(maybeToken) => { case Success(maybeToken) => {
maybeToken.foreach(println)
maybeToken match { maybeToken match {
case Some(token) => case Some(token) =>
async { async {
val credentials = val credentials =
UserCredentials(inputUserName, inputPassword, token) UserCredentials(inputUserName, inputPassword, token)
appDataHandler.updateCredentials(credentials) appDataHandler.updateCredentials(credentials)
// await {
// userService.getUsers(credentials).map(_.body.foreach(println))
// }
updateErrorLabel("")
applicationController.showChatPane() applicationController.showChatPane()
onFX { submitButton.disable = false }
} }
case None => { case None => {
updateErrorLabel("Error logging in - please check your password") updateErrorLabel("Error logging in - please check your password")
logger.warn("Login unsuccessful wrong password") logger.warn("Login unsuccessful wrong password")
onFX { submitButton.disable = false }
} }
} }
// applicationController.showChatPane()
} }
case Failure(exception) => { case Failure(exception) => {
logger.error(s"${exception.getMessage()}") logger.error(s"${exception.getMessage()}")
logger.warn("Login unsuccessful network problem") logger.warn("Login unsuccessful network problem")
updateErrorLabel("Error logging in - Please check your network") updateErrorLabel("Error logging in - Please check your network")
// applicationController.showChatPane()
onFX { submitButton.disable = false }
} }
} }
} }
@ -141,5 +141,4 @@ class LoginController @Inject() (
username: String, username: String,
password: String password: String
) )
} }

19
src/main/scala/wow/doge/chatto/controller/MainViewController.scala

@ -11,11 +11,13 @@ import com.typesafe.scalalogging.LazyLogging
import wow.doge.chatto.messagebuble.BubbledMDFXNode import wow.doge.chatto.messagebuble.BubbledMDFXNode
import scalafx.scene.layout.GridPane import scalafx.scene.layout.GridPane
import scalafx.Includes._ import scalafx.Includes._
// import wow.doge.chatto.
import javafx.scene.layout.HBox
import javafx.scene.layout.Priority
import wow.doge.chatto.control.UserBox
class MainViewController extends ViewController with LazyLogging { class MainViewController extends ViewController with LazyLogging {
@FXML var menuBar: MenuBar = _
@FXML private var menuBar: MenuBar = _
// @FXML // @FXML
// var workspacePane: Pane = _ // var workspacePane: Pane = _
// @FXML // @FXML
@ -23,9 +25,7 @@ class MainViewController extends ViewController with LazyLogging {
// @FXML // @FXML
// var navigationPane: Pane = _ // var navigationPane: Pane = _
@FXML var mainPane: Pane = _
// @FXML var chatPane: Pane = _
@FXML private var mainPane: HBox = _
lazy val workspaceController = getController[WorkspaceController]() lazy val workspaceController = getController[WorkspaceController]()
lazy val navigationController = getController[NavigationController]() lazy val navigationController = getController[NavigationController]()
@ -33,22 +33,21 @@ class MainViewController extends ViewController with LazyLogging {
lazy val loginController = getController[LoginController]() lazy val loginController = getController[LoginController]()
lazy val chatController = getController[ChatController]() lazy val chatController = getController[ChatController]()
// val bubbleNode = new BubbledMDFXNode("Wow")
var workspaceManager: ContentManager = _ var workspaceManager: ContentManager = _
var navigationManager: ContentManager = _ var navigationManager: ContentManager = _
var statusBarManager: ContentManager = _ var statusBarManager: ContentManager = _
var mainManager: ContentManager = _ var mainManager: ContentManager = _
override def didGainVisibilityFirstTime() {
// menuBar.setUseSystemMenuBar(true)
override def didGainVisibilityFirstTime() = {
menuBar.setUseSystemMenuBar(true)
// menuBar.setVisible(false) // menuBar.setVisible(false)
// navigationManager = // navigationManager =
// ContentManager(navigationPane, this, navigationController) // ContentManager(navigationPane, this, navigationController)
// statusBarManager = ContentManager(statusPane, this, statusBarController) // statusBarManager = ContentManager(statusPane, this, statusBarController)
// workspaceManager = ContentManager(workspacePane, this, workspaceController) // workspaceManager = ContentManager(workspacePane, this, workspaceController)
mainManager = ContentManager(mainPane, this, loginController)
mainManager = ContentManager(mainPane, this, chatController)
chatController.chatMainPane.hgrow = Priority.ALWAYS
} }
} }

4
src/main/scala/wow/doge/chatto/controller/NavigationController.scala

@ -5,12 +5,12 @@ import javafx.scene.control.Button
class NavigationController extends AbstractViewController { class NavigationController extends AbstractViewController {
def actionClickButton(event: ActionEvent) {
def actionClickButton(event: ActionEvent) = {
logger.debug(event.toString) logger.debug(event.toString)
statusBarController.updateLabel(event.getSource.asInstanceOf[Button]) statusBarController.updateLabel(event.getSource.asInstanceOf[Button])
} }
def actionHotReload(event: ActionEvent) {
def actionHotReload(event: ActionEvent) = {
applicationController.replacePrimarySceneContent() applicationController.replacePrimarySceneContent()
logger.debug("Hot Reload Succeeded") logger.debug("Hot Reload Succeeded")
statusBarController.updateLabel(event.getSource.asInstanceOf[Button]) statusBarController.updateLabel(event.getSource.asInstanceOf[Button])

2
src/main/scala/wow/doge/chatto/controller/WorkspaceController.scala

@ -15,7 +15,7 @@ class WorkspaceController @Inject() (applicationName: ApplicationName)
@FXML @FXML
var infoLabel: Label = _ var infoLabel: Label = _
override def didGainVisibilityFirstTime() {
override def didGainVisibilityFirstTime() = {
infoLabel.setText(applicationName.name) infoLabel.setText(applicationName.name)
} }
} }

69
src/main/scala/wow/doge/chatto/service/CustomSerializer.scala

@ -1,69 +0,0 @@
package wow.doge.chatto.service
import org.json4s.CustomSerializer
import org.json4s._
import java.time.Instant
// import org.json4s.JsonAST._
import org.json4s.JsonDSL._
// class ParentSerializer extends Serializer[Parent] {
// private val ParentClass = classOf[Parent]
// implicit val formats = DefaultFormats
// def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Parent] = {
// case (TypeInfo(ParentClass, _), json) => json match {
// case JObject(JField("kind", JString(kind)) :: _) => kind match {
// case "first_type" => json.extract[ChildClassOne]
// case "second_type" => json.extract[ChildClassTwo]
// }
// case _ => throw new MappingException("Invalid kind")
// }
// }
// def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Map()
// }
// class MyJInstantSerializer extends Serializer[Instant] {
// implicit val formats = DefaultFormats
// def deserialize(
// implicit format: Formats
// ): PartialFunction[(TypeInfo, JValue), Instant] = {
// case (_, JString(d)) => Instant.parse(d)
// case (_, JNull) => null
// }
// def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
// case d: Instant => JString(d.toString())
// }
// }
// class MyJInstantSerializer
// extends CustomSerializer[Instant](implicit format =>
// (
// {
// // case JInt(d) => Instant.ofEpochMilli(d.toLong)
// case JString(d) => Instant.parse(d)
// case JNull => null
// }, {
// // case d: Instant => JInt(d.toEpochMilli)
// case d: Instant => JString(d.toString())
// }
// )
// )
// import java.time.ZonedDateTime
// import java.time.format.DateTimeFormatter
// a custom serializer has two partial functions, one
// for serializing and one for deserializing
// case object ZDTSerializer
// extends CustomSerializer[ZonedDateTime](format =>
// ({
// case JString(s) => ZonedDateTime.parse(s)
// }, {
// case zdt: ZonedDateTime =>
// JString(
// zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"))
// )
// })
// )

14
src/main/scala/wow/doge/chatto/service/UserService.scala

@ -52,12 +52,12 @@ class UserService @Inject() (appDataHandler: AppDataHandler)(
private def endpoint(uri: String) = uri"$baseUrl/$uri" private def endpoint(uri: String) = uri"$baseUrl/$uri"
def getUsers(credentials: UserCredentials) = async { def getUsers(credentials: UserCredentials) = async {
logger.debug(s"${appDataHandler.appData}")
println(
write[ActiveUser](
ActiveUser("hmm what is it", true, Some(ZonedDateTime.now()))
)
)
// logger.debug(s"${appDataHandler.appData}")
// println(
// write[ActiveUser](
// ActiveUser("hmm what is it", true, Some(ZonedDateTime.now()))
// )
// )
await { await {
authBasicRequest(credentials) authBasicRequest(credentials)
.get(uri"http://localhost:8080/api/chat/get/users") .get(uri"http://localhost:8080/api/chat/get/users")
@ -67,7 +67,7 @@ class UserService @Inject() (appDataHandler: AppDataHandler)(
} }
def getMessages(credentials: UserCredentials) = async { def getMessages(credentials: UserCredentials) = async {
logger.debug(s"${appDataHandler.appData}")
// logger.debug(s"${appDataHandler.appData}")
await { await {
authBasicRequest(credentials) authBasicRequest(credentials)
.get(uri"http://localhost:8080/api/chat/get/users") .get(uri"http://localhost:8080/api/chat/get/users")

Loading…
Cancel
Save