Merge branch 'feat/chat-visual-improvement' into 'main'
Feat/chat visual improvement Closes #107 See merge request cs108-fs26/Gruppe-13!159
This commit was merged in pull request #315.
This commit is contained in:
+44
-22
@@ -1,6 +1,7 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui;
|
package ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatController;
|
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatController;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatController.ChatKey;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatModel;
|
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatModel;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatType;
|
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatType;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message;
|
import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message;
|
||||||
@@ -20,7 +21,6 @@ import javafx.scene.control.MenuButton;
|
|||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TabPane;
|
import javafx.scene.control.TabPane;
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
@@ -30,16 +30,10 @@ public class ChatBoxController {
|
|||||||
|
|
||||||
private final ChatController chatController;
|
private final ChatController chatController;
|
||||||
|
|
||||||
@FXML private VBox chatBox;
|
|
||||||
|
|
||||||
@FXML private TabPane chatTabPane;
|
@FXML private TabPane chatTabPane;
|
||||||
|
|
||||||
@FXML private MenuButton addWhisperChatButton;
|
@FXML private MenuButton addWhisperChatButton;
|
||||||
|
|
||||||
@FXML private HBox menuBox;
|
|
||||||
|
|
||||||
private FXMLLoader fxmlLoader;
|
|
||||||
|
|
||||||
private final List<String> activeWhisperChats;
|
private final List<String> activeWhisperChats;
|
||||||
|
|
||||||
@FXML private final Map<String, Tab> usernameTabMap;
|
@FXML private final Map<String, Tab> usernameTabMap;
|
||||||
@@ -76,7 +70,7 @@ public class ChatBoxController {
|
|||||||
ChatModel globalChatModel = new ChatModel(global, username, -1, null);
|
ChatModel globalChatModel = new ChatModel(global, username, -1, null);
|
||||||
chatController.getChatModelMap().put(new ChatController.ChatKey(global), globalChatModel);
|
chatController.getChatModelMap().put(new ChatController.ChatKey(global), globalChatModel);
|
||||||
addChatTab("GLOBAL", globalChatModel, global);
|
addChatTab("GLOBAL", globalChatModel, global);
|
||||||
addWhisperChatButton.setOnAction(event -> addWhisperChatButton.show());
|
addWhisperChatButton.setOnAction(_ -> addWhisperChatButton.show());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,7 +90,7 @@ public class ChatBoxController {
|
|||||||
MenuItem menuItem = new MenuItem(targetUserName);
|
MenuItem menuItem = new MenuItem(targetUserName);
|
||||||
addWhisperChatButton.getItems().add(menuItem);
|
addWhisperChatButton.getItems().add(menuItem);
|
||||||
menuItem.setOnAction(
|
menuItem.setOnAction(
|
||||||
event ->
|
_ ->
|
||||||
addWhisperChat(
|
addWhisperChat(
|
||||||
targetUserName,
|
targetUserName,
|
||||||
new ChatModel(
|
new ChatModel(
|
||||||
@@ -130,7 +124,7 @@ public class ChatBoxController {
|
|||||||
if (oldUsername.equals(item.getText())) {
|
if (oldUsername.equals(item.getText())) {
|
||||||
item.setText(newUsername);
|
item.setText(newUsername);
|
||||||
item.setOnAction(
|
item.setOnAction(
|
||||||
event ->
|
_ ->
|
||||||
addWhisperChat(
|
addWhisperChat(
|
||||||
newUsername,
|
newUsername,
|
||||||
new ChatModel(
|
new ChatModel(
|
||||||
@@ -165,11 +159,21 @@ public class ChatBoxController {
|
|||||||
if (!activeWhisperChats.contains(target)) {
|
if (!activeWhisperChats.contains(target)) {
|
||||||
activeWhisperChats.add(target);
|
activeWhisperChats.add(target);
|
||||||
ChatType whisper = ChatType.WHISPER;
|
ChatType whisper = ChatType.WHISPER;
|
||||||
chatController
|
chatController.getChatModelMap().put(new ChatKey(whisper, target), chatModel);
|
||||||
.getChatModelMap()
|
ChatViewController chatViewController = addChatTab(target, chatModel, whisper);
|
||||||
.put(new ChatController.ChatKey(whisper, target), chatModel);
|
Tab chatTab = chatViewController.getChatTab();
|
||||||
addChatTab(target, chatModel, whisper);
|
chatTab.setOnCloseRequest(
|
||||||
|
_ -> {
|
||||||
|
chatViewController.tabIsOpen = false;
|
||||||
|
chatTabPane.getTabs().remove(chatTab);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
|
ChatViewController chatViewController =
|
||||||
|
chatController.activeChatControllers.get(new ChatKey(ChatType.WHISPER, target));
|
||||||
|
if (!chatViewController.tabIsOpen) {
|
||||||
|
reopenChatTab(chatViewController.getChatTab());
|
||||||
|
chatViewController.tabIsOpen = true;
|
||||||
|
}
|
||||||
chatTabPane.getSelectionModel().select(usernameTabMap.get(target));
|
chatTabPane.getSelectionModel().select(usernameTabMap.get(target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,30 +187,40 @@ public class ChatBoxController {
|
|||||||
* @param chatModel The {@link ChatModel} containing the data and logic for this specific chat.
|
* @param chatModel The {@link ChatModel} containing the data and logic for this specific chat.
|
||||||
* @throws RuntimeException If the FXML resource for the chat tab cannot be loaded.
|
* @throws RuntimeException If the FXML resource for the chat tab cannot be loaded.
|
||||||
*/
|
*/
|
||||||
public void addChatTab(String title, ChatModel chatModel, ChatType chatType) {
|
public ChatViewController addChatTab(String title, ChatModel chatModel, ChatType chatType) {
|
||||||
String ressource = "/ui-structure/components/chatui/chattab.fxml";
|
String ressource = "/ui-structure/components/chatui/chattab.fxml";
|
||||||
URL resource = getClass().getResource(ressource);
|
URL resource = getClass().getResource(ressource);
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(resource);
|
FXMLLoader fxmlLoader = new FXMLLoader(resource);
|
||||||
runOnPlatformSynchronized(
|
return runOnPlatformSynchronized(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
ChatViewController chatViewController =
|
ChatViewController chatViewController =
|
||||||
new ChatViewController(
|
new ChatViewController(
|
||||||
this.chatController, chatModel, this.username);
|
this.chatController, chatModel, this);
|
||||||
chatController.activeChatControllers.put(
|
|
||||||
new ChatController.ChatKey(chatType), chatViewController);
|
ChatKey chatKey;
|
||||||
|
if (chatType.equals(ChatType.WHISPER)) {
|
||||||
|
chatKey = new ChatKey(chatType, title);
|
||||||
|
} else {
|
||||||
|
chatKey = new ChatKey(chatType);
|
||||||
|
}
|
||||||
|
chatController.activeChatControllers.put(chatKey, chatViewController);
|
||||||
fxmlLoader.setController(chatViewController);
|
fxmlLoader.setController(chatViewController);
|
||||||
Node load = fxmlLoader.load();
|
Node load = fxmlLoader.load();
|
||||||
VBox.setVgrow(load, Priority.ALWAYS);
|
VBox.setVgrow(load, Priority.ALWAYS);
|
||||||
VBox vbox = new VBox();
|
VBox vbox = new VBox();
|
||||||
VBox.setVgrow(vbox, Priority.ALWAYS);
|
VBox.setVgrow(vbox, Priority.ALWAYS);
|
||||||
vbox.getChildren().add(load);
|
vbox.getChildren().add(load);
|
||||||
chatModel.addListener((msg) -> chatViewController.showMessage(msg));
|
chatModel.addListener(chatViewController::showMessage);
|
||||||
Tab newChat = new Tab(title, vbox);
|
Tab newChat = new Tab(title, vbox);
|
||||||
usernameTabMap.put(title, newChat);
|
usernameTabMap.put(title, newChat);
|
||||||
this.chatTabPane.getTabs().add(newChat);
|
chatTabPane.getTabs().add(newChat);
|
||||||
this.chatTabPane.getSelectionModel().select(newChat);
|
this.chatTabPane.getSelectionModel().select(newChat);
|
||||||
return newChat;
|
chatViewController.setChatTab(newChat);
|
||||||
|
if (!chatType.equals(ChatType.WHISPER)) {
|
||||||
|
newChat.setClosable(false);
|
||||||
|
}
|
||||||
|
return chatViewController;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -267,4 +281,12 @@ public class ChatBoxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reopenChatTab(Tab chatTab) {
|
||||||
|
Platform.runLater(
|
||||||
|
() -> {
|
||||||
|
chatTabPane.getTabs().add(chatTab);
|
||||||
|
chatTabPane.getSelectionModel().select(chatTab);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-11
@@ -11,37 +11,48 @@ import javafx.fxml.Initializable;
|
|||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
/** Responsible for the presentation of the ChatModel to the Client */
|
/** Responsible for the presentation of the ChatModel to the Client */
|
||||||
public class ChatViewController implements Initializable {
|
public class ChatViewController implements Initializable {
|
||||||
|
|
||||||
|
private final ChatBoxController chatBoxController;
|
||||||
@FXML private Button sendButton;
|
@FXML private Button sendButton;
|
||||||
|
|
||||||
@FXML private TextField inputField;
|
@FXML private TextField inputField;
|
||||||
|
|
||||||
@FXML private VBox chatInterfaceVBox;
|
|
||||||
|
|
||||||
@FXML private HBox controlBar;
|
|
||||||
|
|
||||||
@FXML private ScrollPane scrollPane;
|
@FXML private ScrollPane scrollPane;
|
||||||
|
|
||||||
@FXML private VBox chat;
|
@FXML private VBox chat;
|
||||||
|
|
||||||
private final ChatModel chatModel;
|
public Tab getChatTab() {
|
||||||
|
return chatTab;
|
||||||
|
}
|
||||||
|
|
||||||
private final String username;
|
public void setChatTab(Tab chatTab) {
|
||||||
|
this.chatTab = chatTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML private Tab chatTab;
|
||||||
|
|
||||||
|
public Boolean tabIsOpen;
|
||||||
|
|
||||||
|
private final ChatModel chatModel;
|
||||||
|
|
||||||
private final ChatController controller;
|
private final ChatController controller;
|
||||||
|
|
||||||
private static final int CHAT_PADDING = 20;
|
private static final int CHAT_PADDING = 20;
|
||||||
|
|
||||||
public ChatViewController(ChatController chatController, ChatModel chatModel, String username) {
|
public ChatViewController(
|
||||||
|
ChatController chatController,
|
||||||
|
ChatModel chatModel,
|
||||||
|
ChatBoxController chatBoxController) {
|
||||||
this.controller = chatController;
|
this.controller = chatController;
|
||||||
this.username = username;
|
|
||||||
this.chatModel = chatModel;
|
this.chatModel = chatModel;
|
||||||
|
this.chatBoxController = chatBoxController;
|
||||||
|
tabIsOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,8 +65,8 @@ public class ChatViewController implements Initializable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resourceBundle) {
|
public void initialize(URL location, ResourceBundle resourceBundle) {
|
||||||
inputField.setOnAction(event -> sendMessage());
|
inputField.setOnAction(_ -> sendMessage());
|
||||||
sendButton.setOnAction(event -> sendMessage());
|
sendButton.setOnAction(_ -> sendMessage());
|
||||||
scrollPane.vvalueProperty().bind(chat.heightProperty());
|
scrollPane.vvalueProperty().bind(chat.heightProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +99,10 @@ public class ChatViewController implements Initializable {
|
|||||||
* @param msg The {@link Message} object containing the content and metadata to display.
|
* @param msg The {@link Message} object containing the content and metadata to display.
|
||||||
*/
|
*/
|
||||||
public void showMessage(Message msg) {
|
public void showMessage(Message msg) {
|
||||||
|
if (!tabIsOpen) {
|
||||||
|
tabIsOpen = true;
|
||||||
|
chatBoxController.reopenChatTab(chatTab);
|
||||||
|
}
|
||||||
Platform.runLater(
|
Platform.runLater(
|
||||||
() -> {
|
() -> {
|
||||||
String msgText =
|
String msgText =
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.skin.TabPaneSkin.TabContentRegion?>
|
||||||
<VBox xmlns="http://javafx.com/javafx/21"
|
<VBox xmlns="http://javafx.com/javafx/21"
|
||||||
xmlns:fx="http://javafx.com/fxml/1"
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
fx:id="chatBox"
|
fx:id="chatBox"
|
||||||
VBox.vgrow="ALWAYS"
|
VBox.vgrow="ALWAYS"
|
||||||
stylesheets="@chatui.css"
|
stylesheets="@chatui.css"
|
||||||
styleClass="chat-box"
|
styleClass="chat-box"
|
||||||
spacing="10"
|
spacing="20"
|
||||||
maxWidth="Infinity">
|
maxWidth="Infinity">
|
||||||
|
|
||||||
<HBox spacing="10"
|
<HBox spacing="10"
|
||||||
@@ -17,14 +18,14 @@
|
|||||||
|
|
||||||
<Label text="== CHAT ==" styleClass="chat-header" alignment="CENTER" maxWidth="Infinity"/>
|
<Label text="== CHAT ==" styleClass="chat-header" alignment="CENTER" maxWidth="Infinity"/>
|
||||||
<Region HBox.hgrow="ALWAYS"/>
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
<MenuButton fx:id="addWhisperChatButton" styleClass="yellow-button" text="WHISPER CHAT"
|
<MenuButton fx:id="addWhisperChatButton" styleClass="menu-button" text="WHISPER CHAT"
|
||||||
alignment="CENTER_RIGHT">
|
alignment="CENTER_RIGHT">
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<TabPane fx:id="chatTabPane"
|
<TabPane fx:id="chatTabPane"
|
||||||
VBox.vgrow="ALWAYS"
|
VBox.vgrow="ALWAYS"
|
||||||
styleClass="tab-header-area">
|
styleClass="tab-pane">
|
||||||
|
|
||||||
</TabPane>
|
</TabPane>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<VBox fx:id="chatInterfaceVBox"
|
<VBox fx:id="chatInterfaceVBox"
|
||||||
maxWidth="Infinity"
|
maxWidth="Infinity"
|
||||||
spacing="10"
|
spacing="10"
|
||||||
styleClass="chat-box"
|
styleClass="tab-chat-box"
|
||||||
stylesheets="@chatui.css"
|
stylesheets="@chatui.css"
|
||||||
VBox.vgrow="ALWAYS"
|
VBox.vgrow="ALWAYS"
|
||||||
xmlns="http://javafx.com/javafx/17.0.12"
|
xmlns="http://javafx.com/javafx/17.0.12"
|
||||||
|
|||||||
@@ -5,7 +5,17 @@
|
|||||||
-fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10;
|
-fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10;
|
||||||
-fx-border-width: 12;
|
-fx-border-width: 12;
|
||||||
-fx-padding: 20;
|
-fx-padding: 20;
|
||||||
/*-fx-effect: dropshadow(one-pass-box, rgba(0,0,0,1), 0, 0, 15, 15);*/
|
-fx-effect: dropshadow(one-pass-box, rgba(0,0,0,1), 0, 0, 15, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-chat-box {
|
||||||
|
|
||||||
|
-fx-background-color: #0d9e3b;
|
||||||
|
-fx-background-radius: 58;
|
||||||
|
-fx-border-radius: 50;
|
||||||
|
-fx-border-color: #0d763b #0d623b #0d623b #0d763b;
|
||||||
|
-fx-border-width: 12;
|
||||||
|
-fx-padding: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-header {
|
.chat-header {
|
||||||
@@ -163,25 +173,25 @@
|
|||||||
-fx-border-width: 5;
|
-fx-border-width: 5;
|
||||||
-fx-padding: 10;
|
-fx-padding: 10;
|
||||||
-fx-effect: dropshadow(one-pass-box, rgba(0,0,0,1), 0, 0, 15, 15);
|
-fx-effect: dropshadow(one-pass-box, rgba(0,0,0,1), 0, 0, 15, 15);
|
||||||
-fx-max-height: 10;
|
-fx-min-height: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab {
|
.tab-pane .tab {
|
||||||
-fx-background-color: #0d9e3b;
|
-fx-background-color: #0d983b;
|
||||||
-fx-background-radius: 18;
|
-fx-background-radius: 28;
|
||||||
-fx-border-radius: 10;
|
-fx-border-radius: 10;
|
||||||
-fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10;
|
-fx-border-color: #0d873b #0d733b #0d733b #0d873b;
|
||||||
-fx-border-width: 5;
|
-fx-border-width: 5;
|
||||||
-fx-padding: 10;
|
-fx-padding: 6 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab:selected {
|
.tab-pane .tab:selected {
|
||||||
-fx-background-color: #0d763b;
|
-fx-background-color: #0d873b;
|
||||||
-fx-background-radius: 18;
|
-fx-background-radius: 18;
|
||||||
-fx-border-radius: 10;
|
-fx-border-radius: 10;
|
||||||
-fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10;
|
-fx-border-color: #0d763b #0d623b #0d623b #0d763b;
|
||||||
-fx-border-width: 5;
|
-fx-border-width: 5;
|
||||||
-fx-padding: 10;
|
-fx-padding: 6 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab .tab-close-button {
|
.tab-pane .tab .tab-close-button {
|
||||||
@@ -192,7 +202,6 @@
|
|||||||
-fx-background-color: #0d9e3b;
|
-fx-background-color: #0d9e3b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.tab .tab-label {
|
.tab .tab-label {
|
||||||
-fx-alignment: CENTER;
|
-fx-alignment: CENTER;
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
@@ -204,3 +213,36 @@
|
|||||||
-fx-alignment: CENTER;
|
-fx-alignment: CENTER;
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-button {
|
||||||
|
-fx-background-color: #b8860b;
|
||||||
|
-fx-border-color: #ffd700;
|
||||||
|
-fx-text-fill: #ffffff;
|
||||||
|
-fx-background-radius: 12;
|
||||||
|
-fx-border-radius: 12;
|
||||||
|
-fx-font-family: "Courier New";
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-border-width: 2;
|
||||||
|
-fx-padding: 6 15;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-button .context-menu {
|
||||||
|
-fx-background-color: #b8860b;
|
||||||
|
-fx-background-radius: 4;
|
||||||
|
-fx-padding: 6 15;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-button:hover {
|
||||||
|
-fx-translate-y: -3;
|
||||||
|
-fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-button .menu-item {
|
||||||
|
-fx-background-color: #b8860b;
|
||||||
|
-fx-border-color: #ffd700;
|
||||||
|
-fx-text-fill: #ffffff
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user