Added custom cell factory

This commit is contained in:
Rohan Sircar 2020-05-26 12:59:04 +05:30
parent 640b2507eb
commit 125fcb51b3
3 changed files with 121 additions and 152 deletions

View File

@ -5,6 +5,44 @@ import javafx.scene.control.Label
import scalafx.Includes._ import scalafx.Includes._
import wow.doge.chatto.controller.ChatData import wow.doge.chatto.controller.ChatData
import com.sfxcode.sapphire.core.value.FXBean import com.sfxcode.sapphire.core.value.FXBean
import wow.doge.chatto.controller.Message
import com.sandec.mdfx.MDFXNode
import javafx.geometry.Pos
import javafx.scene.layout.Priority
class MessageBox(val sender: String, val receiver: String, val message: String) class MessageBox() extends HBox() {
extends HBox() {} import MessageBox._
private var mdfxNode = new MDFXNode("");
mdfxNode
.getStylesheets()
.add(markdownStyleSheet)
setAlignment(Pos.CENTER_RIGHT)
maxWidth(500)
this.hgrow = Priority.ALWAYS
this.vgrow = Priority.ALWAYS
this.children ++= Seq(mdfxNode)
this.fillHeight = true
def setItem(message: Message): Unit = {
// val index = this.children.indexOf(mdfxNode)
val mdfxNode2 = new MDFXNode(message.message)
mdfxNode2
.getStylesheets()
.add(markdownStyleSheet)
mdfxNode2.setMaxWidth(500)
mdfxNode2.vgrow = Priority.ALWAYS
mdfxNode2.setAlignment(Pos.CENTER)
mdfxNode2.styleClass = Seq("chat-message-box")
this.children -= mdfxNode
this.children += mdfxNode2
mdfxNode = mdfxNode2
// this.children.updated(index, mdfxNode2)
}
}
object MessageBox {
lazy val markdownStyleSheet =
getClass().getResource("/styles/markdown.css").toExternalForm()
}

View File

@ -61,6 +61,8 @@ import javafx.scene.input.DataFormat
import wow.doge.chatto.control.MessageBox import wow.doge.chatto.control.MessageBox
import javafx.scene.control.SelectionMode import javafx.scene.control.SelectionMode
import scalafx.beans.property.BooleanProperty import scalafx.beans.property.BooleanProperty
import javafx.scene.control.ListCell
import java.time.Instant
class ChatController @Inject() ( class ChatController @Inject() (
userService: UserService, userService: UserService,
@ -76,14 +78,14 @@ class ChatController @Inject() (
// @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[ActiveUser] = _
@FXML var chatListView: JFXListView[MessageBox] = _ @FXML var chatListView: JFXListView[Message] = _
@FXML private var curUsr: Label = _ @FXML private var curUsr: Label = _
@FXML private var lastActiveLabel: Label = _ @FXML private var lastActiveLabel: Label = _
@FXML private var isOnlineLabel: Label = _ @FXML private var isOnlineLabel: Label = _
@FXML private var selectedUserBox: HBox = _ @FXML private var selectedUserBox: HBox = _
private val usersBuffer = ObservableBuffer.empty[UserBox2] private val usersBuffer = ObservableBuffer.empty[ActiveUser]
private val usersListProperty = BufferProperty(usersBuffer) private val usersListProperty = BufferProperty(usersBuffer)
/** /**
@ -91,7 +93,7 @@ class ChatController @Inject() (
* Synchronized with the internal users list property. * Synchronized with the internal users list property.
* Attemping to modify the internal list will throw an exception * Attemping to modify the internal list will throw an exception
*/ */
val usersListROProp: ReadOnlyListProperty[UserBox2] = BufferProperty( val usersListROProp: ReadOnlyListProperty[ActiveUser] = BufferProperty(
FXCollections.unmodifiableObservableList(usersListProperty()) FXCollections.unmodifiableObservableList(usersListProperty())
) )
@ -103,7 +105,9 @@ class ChatController @Inject() (
super.didGainVisibilityFirstTime() super.didGainVisibilityFirstTime()
this.stage.resizable = true this.stage.resizable = true
chatMainPane.hgrow = Priority.ALWAYS chatMainPane.hgrow = Priority.ALWAYS
chatListView.selectionModel().selectionMode = SelectionMode.MULTIPLE chatListView.selectionModel().selectionMode = SelectionMode.MULTIPLE
chatListView.setCellFactory(_ => new ChatListCell())
chatDataAdapter.set(FXBean(ChatData.empty)) chatDataAdapter.set(FXBean(ChatData.empty))
@ -114,6 +118,8 @@ class ChatController @Inject() (
.isNull() .isNull()
}) })
// usersListView2.setCellFactory(lv => {})
// 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()}")
@ -134,7 +140,7 @@ class ChatController @Inject() (
.addListener((_, _, newValue) => { .addListener((_, _, newValue) => {
Option(newValue).foreach(nv => { Option(newValue).foreach(nv => {
val maybeCDP = chatDataStore val maybeCDP = chatDataStore
.get(nv.username) .get(nv.userName)
val chatDataBean = maybeCDP val chatDataBean = maybeCDP
.map(_.bean) .map(_.bean)
.getOrElse { .getOrElse {
@ -143,9 +149,9 @@ class ChatController @Inject() (
} }
chatDataAdapter.set(chatDataBean) chatDataAdapter.set(chatDataBean)
maybeCDP.foreach(cdp => { maybeCDP.foreach(cdp => {
// lastActiveLabel.text <== cdp.lastActive lastActiveLabel.text <== cdp.lastActive
isOnlineLabel.text <== cdp.isActive.asString() isOnlineLabel.text <== cdp.isActive.asString()
chatListView.items <== cdp.messageBubbles chatListView.items <== cdp.messages
}) })
}) })
}) })
@ -155,32 +161,12 @@ class ChatController @Inject() (
new KeyCodeCombination(KeyCode.C, KeyCombination.ControlDown) new KeyCodeCombination(KeyCode.C, KeyCombination.ControlDown)
copyMessageMenuItem.onAction = _ => { copyMessageMenuItem.onAction = _ => {
val content = new ClipboardContent() val content = new ClipboardContent()
val x = chatListView.getSelectionModel().getSelectedItems().map(_.message) val messages = chatListView.selectionModel().selectedItems.map(_.message)
// chatListView.selectionModel().selectedItem().message content.putString(messages.mkString("\n"))
Option(x).foreach(message => {
content.putString(message.mkString("\n"))
Clipboard.getSystemClipboard().setContent(content) 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()
val chatListMenu = new ContextMenu()
chatListMenu.items += copyMessageMenuItem chatListMenu.items += copyMessageMenuItem
chatListView.contextMenu = chatListMenu chatListView.contextMenu = chatListMenu
@ -191,19 +177,18 @@ class ChatController @Inject() (
submitButton.disable <== validator.containsErrorsProperty() submitButton.disable <== validator.containsErrorsProperty()
submitButton.onAction = (e) => { submitButton.onAction = (e) => {
// val msgBox = ChatDataProperty.createMdMessageBox2(chatInput.text())
// chatListView.items() += msgBox
if (!chatInput.text().equals("") && if (!chatInput.text().equals("") &&
!chatInput.text().equals(" ") && !chatInput.text().equals(" ") &&
!chatInput.text().equals("\n")) { !chatInput.text().equals("\n")) {
val maybeCDP = chatDataStore.get(chatDataAdapter.get.bean.userName) val maybeCDP = chatDataStore.get(chatDataAdapter.get.bean.userName)
maybeCDP.foreach(cdp => { maybeCDP.foreach(cdp => {
cdp.messageBubbles += ChatDataProperty.createMdMessageBox3( // cdp.messageBubbles += ChatDataProperty.createMdMessageBox3(
appDataHandler.appData.credentials.username, // appDataHandler.appData.credentials.username,
cdp.username(), // cdp.username(),
chatInput.text() // chatInput.text()
) // )
cdp.messages += Message.empty.copy(message = chatInput.text())
}) })
} }
} }
@ -236,49 +221,44 @@ class ChatController @Inject() (
val maybeUserBoxes = maybeActiveUsers.map(users => { val maybeUserBoxes = maybeActiveUsers.map(users => {
users.map(user => { users.map(user => {
val chatData = val chatData =
ChatData(user.userName, user, ObservableBuffer.empty[String]) ChatData(user.userName, user, ObservableBuffer.empty[Message])
chatDataStore.put(user.userName, new ChatDataProperty(chatData)) chatDataStore.put(user.userName, new ChatDataProperty(chatData))
new UserBox2(user.userName, chatData) { new UserBox2(user.userName, chatData) {
this.styleClass ++= Seq("text-white") this.styleClass ++= Seq("text-white")
} }
}) })
}) })
val messageBox = ChatDataProperty.createMdMessageBox2( // val messageBox = ChatDataProperty.createMdMessageBox2(
"""**Hello world qefwew yeeehay bwergqwevqcqe** // """**Hello world qefwew yeeehay bwergqwevqcqe**
|**Hello world qefwew yeeehay bwergqwevqcqe** // |**Hello world qefwew yeeehay bwergqwevqcqe**
| // |
| Hello World // | Hello World
""".stripMargin // """.stripMargin
) // )
onFX { onFX {
maybeUserBoxes.foreach(userBoxes => { // maybeUserBoxes.foreach(userBoxes => {
usersBuffer ++= userBoxes // usersBuffer ++= userBoxes
})
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) maybeActiveUsers.foreach(users => usersBuffer ++= users)
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
// )
)
} }
chatDataStore chatDataStore
.map { case (key, value) => value } .map { case (key, value) => value }
.foreach(cdp => { .foreach(cdp => {
cdp.messageBubbles ++= Seq( cdp.messages ++= Seq(
ChatDataProperty.createMdMessageBox2("hi"), Message.empty.copy(message = "hi"),
ChatDataProperty.createMdMessageBox2("hello"), Message.empty.copy(message = "hello"),
ChatDataProperty.createMdMessageBox2("bye") Message.empty.copy(message = "bye")
) )
// .map(ChatDataProperty.createMdMessageBox)
}) })
} }
} }
@ -304,21 +284,12 @@ class ChatController @Inject() (
this.stage.maximized = false this.stage.maximized = false
applicationController.logout() applicationController.logout()
} }
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 ChatData( final case class ChatData(
userName: String, userName: String,
activeUser: ActiveUser, activeUser: ActiveUser,
messages: ObservableBuffer[String] messages: ObservableBuffer[Message]
) { ) {
lazy val lastActiveString = lazy val lastActiveString =
activeUser.lastActive activeUser.lastActive
@ -328,69 +299,33 @@ final case class ChatData(
} }
object ChatData { object ChatData {
def empty = { def empty = {
ChatData("empty", ActiveUser.empty, ObservableBuffer.empty[String]) ChatData("empty", ActiveUser.empty, ObservableBuffer.empty[Message])
} }
} }
class ChatDataProperty(chatData: ChatData) { class ChatDataProperty(chatData: ChatData) {
import ChatDataProperty._
val bean = FXBean(chatData) val bean = FXBean(chatData)
val username = bean.getStringProperty("userName") val username = bean.getStringProperty("userName")
val isActive = bean.getBooleanProperty("activeUser.online") val isActive = bean.getBooleanProperty("activeUser.online")
val lastActive = bean.getStringProperty("lastActiveString") val lastActive = bean.getStringProperty("lastActiveString")
lazy val messageBubbles = BufferProperty( val messages = BufferProperty(chatData.messages)
chatData.messages.map(ChatDataProperty.createMdMessageBox2)
)
def messageList = messageBubbles().map(_.message)
// lazy val messages = messagesBubbleProperty
// .get()
// .map(_.messageText)
} }
object ChatDataProperty {
final case class Message(
fromUser: String,
toUser: String,
message: String,
messageTime: Instant
)
object Message {
lazy val markdownStyleSheet = lazy val markdownStyleSheet =
getClass().getResource("/styles/markdown.css").toExternalForm() getClass().getResource("/styles/markdown.css").toExternalForm()
def createMdMessageBox(mdfxText: String) = { def empty = Message("", "", "", Instant.MIN)
val mdfxNode = new BubbledMDFXNode(mdfxText);
mdfxNode
.getStylesheets()
.add(markdownStyleSheet);
mdfxNode.setBubbleSpec(BubbleSpec.FACE_RIGHT_CENTER);
mdfxNode.setBackground(
new Background(new BackgroundFill(Color.LIGHTSTEELBLUE, null, null))
);
val box = new HBox();
mdfxNode.setMinWidth(100.0);
box.setAlignment(Pos.TOP_RIGHT);
box.children += mdfxNode;
box
}
def createMdMessageBox2(mdfxText: String) = { def createMdMessageBox(
val mdfxNode = new MDFXNode(mdfxText); message: Message
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); val mdfxNode = new MDFXNode(message.message);
mdfxNode mdfxNode
.getStylesheets() .getStylesheets()
.add(markdownStyleSheet) .add(markdownStyleSheet)
@ -399,7 +334,7 @@ object ChatDataProperty {
mdfxNode.setAlignment(Pos.CENTER) mdfxNode.setAlignment(Pos.CENTER)
mdfxNode.styleClass = Seq("chat-message-box") mdfxNode.styleClass = Seq("chat-message-box")
val box = new MessageBox(sender, receiver, mdfxText) val box = new HBox()
box.setAlignment(Pos.CENTER_RIGHT) box.setAlignment(Pos.CENTER_RIGHT)
// box.maxWidth(500) // box.maxWidth(500)
box.hgrow = Priority.ALWAYS box.hgrow = Priority.ALWAYS
@ -408,5 +343,19 @@ object ChatDataProperty {
box.fillHeight = true box.fillHeight = true
box box
} }
}
final class ChatListCell extends ListCell[Message] {
private val messageBox = new MessageBox()
override def updateItem(item: Message, empty: Boolean): Unit = {
super.updateItem(item, empty)
if (empty || item == null) {
setText(null)
setGraphic(null)
} else {
// messageBox.setItem(item)
// setGraphic(messageBox)
setGraphic(Message.createMdMessageBox(item))
}
}
} }

View File

@ -7,7 +7,6 @@ import com.jfoenix.controls.JFXButton
import com.jfoenix.controls.JFXTextField import com.jfoenix.controls.JFXTextField
import com.jfoenix.controls.JFXPasswordField import com.jfoenix.controls.JFXPasswordField
import scalafx.Includes._ import scalafx.Includes._
// import scalafx.application.Platform
import scalafx.event.ActionEvent import scalafx.event.ActionEvent
import com.sfxcode.sapphire.core.value.KeyBindings import com.sfxcode.sapphire.core.value.KeyBindings
import scalafx.scene.layout.VBox import scalafx.scene.layout.VBox
@ -54,25 +53,6 @@ class LoginController @Inject() (
override def didGainVisibilityFirstTime(): Unit = { override def didGainVisibilityFirstTime(): Unit = {
super.didGainVisibilityFirstTime() super.didGainVisibilityFirstTime()
this.stage.resizable = false this.stage.resizable = false
// usernameTextField.requestFocus()
// submitButton.setOnAction(actionLogin)
val bindings = KeyBindings("username", "password")
// Expression Binding Example
// bindings.add(
// "usernameTextField",
// "${sf:i18n('personText', _self.usernameTextField(), _self.passwordTextField())})"
// )
val box = new VBox()
val adapter = FXBeanAdapter[Person](this)
val bean = FXBean[Person](Person("0", "username", "password"))
adapter.addBindings(bindings)
adapter.set(bean)
// adapter.addIntConverter("age")
// adapter.hasBeanProperty
// adapter.revert()
Array(usernameTextField, passwordTextField, submitButton) Array(usernameTextField, passwordTextField, submitButton)
.foreach(_.onKeyPressed = (keyEvent) => { .foreach(_.onKeyPressed = (keyEvent) => {
@ -83,6 +63,8 @@ class LoginController @Inject() (
override def didGainVisibility(): Unit = { override def didGainVisibility(): Unit = {
usernameTextField.requestFocus() usernameTextField.requestFocus()
usernameTextField.text() = "username"
passwordTextField.text() = "password"
} }
def actionLogin() = { def actionLogin() = {