Chat page UI implemented.

This commit is contained in:
Asal Lotfi
2025-08-23 15:48:12 +03:30
parent f80fd60a48
commit 221f093324
33 changed files with 855 additions and 69 deletions

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -58,3 +58,121 @@
-fx-border-width: 0 1px 0 1px; /* 1px vertical line */
-fx-padding: 0 0 0 0.5px; /* big invisible hitbox for the mouse */
}
.placeholder-label {
-fx-text-fill: #aaa; /* text color */
-fx-font-size: 13px;
-fx-font-weight: bold;
-fx-background-color: #2a2a2a; /* bubble background */
-fx-padding: 8 10 8 10; /* top/right/bottom/left spacing */
-fx-background-radius: 20; /* rounded corners */
}
/* ===== Chat page (dark) ===== */
.chat-root { -fx-background-color: #0f1c26; }
/* Header bar */
.chat-header {
-fx-background-color: #0f1c26;
-fx-border-color: #2e3948;
-fx-border-width: 0 0 1 0;
}
.chat-title { -fx-text-fill: #e8f1f8; -fx-font-size: 14px; -fx-font-weight: bold; }
.chat-status { -fx-text-fill: #8ea1b2; -fx-font-size: 12px; }
/* Icon-only buttons */
.icon-btn {
-fx-background-color: transparent;
-fx-padding: 6 8 6 8;
-fx-cursor: hand;
}
.icon-btn:hover {
-fx-background-color: rgba(255,255,255,0.06);
-fx-background-radius: 6;
}
/* Messages area */
.chat-scroll { -fx-background-color: transparent; }
/* Input bar */
.chat-input-bar {
-fx-background-color: #15222d;
-fx-border-color: #2e3948;
-fx-border-width: 1 0 0 0;
}
/* Send button (telegram-ish blue) */
.send-btn {
-fx-background-color: #2ca4ff;
-fx-background-radius: 18;
-fx-padding: 8 12 8 12;
-fx-cursor: hand;
}
.send-btn:hover { -fx-background-color: #1996f3; }
/* Active/empty states (matches controllers styleClass) */
.send-btn.send-empty {
-fx-opacity: 0.5; /* look inactive */
}
.send-btn.send-ready {
-fx-opacity: 1.0; /* fully active */
}
.send-btn:disabled {
-fx-opacity: 0.4;
-fx-cursor: default;
}
/* Input bubble */
.msg-input {
-fx-background-color: white;
-fx-background-radius: 18;
-fx-padding: 6 12 6 12;
-fx-text-fill: #0f141a;
-fx-prompt-text-fill: #9aa6b2;
-fx-border-color: transparent;
-fx-control-inner-background: white;
-fx-focus-color: transparent;
-fx-faint-focus-color: transparent;
}
.msg-input .scroll-pane {
-fx-vbar-policy: never;
-fx-hbar-policy: never;
-fx-background-color: transparent;
}
.msg-input .scroll-pane .corner {
-fx-background-color: transparent;
}
.context-menu {
-fx-background-color: #1b2735;
-fx-background-radius: 6;
-fx-padding: 4;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.4), 8, 0, 0, 2);
}
.menu-item {
-fx-font-size: 13px;
-fx-text-fill: #e8f1f8;
-fx-padding: 6 12;
}
.menu-item:focused {
-fx-background-color: rgba(255,255,255,0.08);
-fx-background-radius: 4;
}
/* ===== Search panel ===== */
.chat-search-pane {
-fx-background-color: #1b2735; /* matches sidebar */
}
.chat-search-bar {
-fx-background-color: transparent;
-fx-padding: 8 10;
}
.search-placeholder {
-fx-text-fill: #8ea1b2;
-fx-font-size: 13px;
}
@@ -58,3 +58,121 @@
-fx-border-width: 0 1px 0 1px; /* 1px vertical line */
-fx-padding: 0 0 0 0.5px; /* big invisible hitbox for the mouse */
}
.placeholder-label {
-fx-text-fill: #2e3948; /* text color */
-fx-font-size: 13px;
-fx-font-weight: bold;
-fx-background-color: #f1f1f1; /* bubble background */
-fx-padding: 8 10 8 10; /* top/right/bottom/left spacing */
-fx-background-radius: 20; /* rounded corners */
}
/* ===== Chat page (light) ===== */
.chat-root { -fx-background-color: #ffffff; }
/* Header bar */
.chat-header {
-fx-background-color: #ffffff;
-fx-border-color: #e6e6e6;
-fx-border-width: 0 0 1 0;
}
.chat-title { -fx-text-fill: #0f141a; -fx-font-size: 14px; -fx-font-weight: bold; }
.chat-status { -fx-text-fill: #7e8a97; -fx-font-size: 12px; }
/* Icon-only buttons */
.icon-btn {
-fx-background-color: transparent;
-fx-padding: 6 8 6 8;
-fx-cursor: hand;
}
.icon-btn:hover {
-fx-background-color: rgba(0,0,0,0.05);
-fx-background-radius: 6;
}
/* Messages area */
.chat-scroll { -fx-background-color: transparent; }
/* Input bar */
.chat-input-bar {
-fx-background-color: #f5f5f5;
-fx-border-color: #e6e6e6;
-fx-border-width: 1 0 0 0;
}
/* Send button (telegram-ish blue) */
.send-btn {
-fx-background-color: #2ca4ff;
-fx-background-radius: 18;
-fx-padding: 8 12 8 12;
-fx-cursor: hand;
}
.send-btn:hover { -fx-background-color: #1996f3; }
/* Active/empty states (matches controllers styleClass) */
.send-btn.send-empty {
-fx-opacity: 0.5; /* look inactive */
}
.send-btn.send-ready {
-fx-opacity: 1.0; /* fully active */
}
.send-btn:disabled {
-fx-opacity: 0.4;
-fx-cursor: default;
}
/* Input bubble */
.msg-input {
-fx-background-color: white;
-fx-background-radius: 18;
-fx-padding: 6 12 6 12;
-fx-text-fill: #0f141a;
-fx-prompt-text-fill: #9aa6b2;
-fx-border-color: transparent;
-fx-control-inner-background: white;
-fx-focus-color: transparent;
-fx-faint-focus-color: transparent;
}
.msg-input .scroll-pane {
-fx-vbar-policy: never;
-fx-hbar-policy: never;
-fx-background-color: transparent;
}
.msg-input .scroll-pane .corner {
-fx-background-color: transparent;
}
.context-menu {
-fx-background-color: white;
-fx-background-radius: 6;
-fx-padding: 4;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0, 0, 2);
}
.menu-item {
-fx-font-size: 13px;
-fx-text-fill: #0f141a;
-fx-padding: 6 12;
}
.menu-item:focused {
-fx-background-color: #e6e6e6;
-fx-background-radius: 4;
}
/* ===== Search panel ===== */
.chat-search-pane {
-fx-background-color: #f5f5f5;
}
.chat-search-bar {
-fx-background-color: transparent;
-fx-padding: 8 10;
}
.search-placeholder {
-fx-text-fill: #7e8a97;
-fx-font-size: 13px;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<VBox xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.to.telegramfinalproject.UI.ChatPageController"
spacing="0"
styleClass="chat-root">
<!-- HEADER: avatar + name/status (left) | search + more (right) -->
<HBox fx:id="chatHeader" spacing="10" styleClass="chat-header">
<padding>
<Insets top="8" right="12" bottom="8" left="12"/>
</padding>
<!-- Avatar -->
<ImageView fx:id="userAvatar" fitWidth="36" fitHeight="36" preserveRatio="true"/>
<!-- Title + status -->
<VBox alignment="CENTER_LEFT">
<Label fx:id="chatTitle" text="User name" styleClass="chat-title"/>
<Label fx:id="chatStatus" text="last seen recently" styleClass="chat-status"/>
</VBox>
<Region HBox.hgrow="ALWAYS"/>
<!-- Search-in-chat -->
<Button fx:id="searchInChatButton" styleClass="icon-btn" onAction="#openSearchPanel">
<graphic>
<ImageView fx:id="searchIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
<image>
<Image url="@/org/to/telegramfinalproject/Icons/search_light.png"/>
</image>
</ImageView>
</graphic>
</Button>
<!-- More / overflow -->
<Button fx:id="moreButton" styleClass="icon-btn">
<graphic>
<ImageView fx:id="moreIcon" fitWidth="18" fitHeight="18" preserveRatio="true">
<image>
<Image url="@/org/to/telegramfinalproject/Icons/more_light.png"/>
</image>
</ImageView>
</graphic>
<contextMenu>
<ContextMenu fx:id="moreMenu">
<items>
<MenuItem fx:id="viewProfileItem" text="View profile">
<graphic>
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
<image>
<Image url="@/org/to/telegramfinalproject/Icons/view_profile_light.png"/>
</image>
</ImageView>
</graphic>
</MenuItem>
<MenuItem fx:id="deleteChatItem" text="Delete chat" style="-fx-text-fill: red;">
<graphic>
<ImageView fitWidth="16" fitHeight="16" preserveRatio="true">
<image>
<Image url="@/org/to/telegramfinalproject/Icons/delete_red.png"/>
</image>
</ImageView>
</graphic>
</MenuItem>
</items>
</ContextMenu>
</contextMenu>
</Button>
</HBox>
<!-- MESSAGES -->
<ScrollPane fx:id="messageScrollPane"
fitToWidth="true"
vbarPolicy="AS_NEEDED"
hbarPolicy="NEVER"
VBox.vgrow="ALWAYS"
styleClass="chat-scroll">
<content>
<VBox fx:id="messageContainer" spacing="10">
<padding>
<Insets top="10" right="10" bottom="10" left="10"/>
</padding>
</VBox>
</content>
</ScrollPane>
<!-- INPUT BAR -->
<HBox fx:id="inputBar" spacing="8" styleClass="chat-input-bar">
<padding>
<Insets top="8" right="12" bottom="8" left="12"/>
</padding>
<!-- Attachment -->
<Button fx:id="attachmentButton" styleClass="icon-btn">
<graphic>
<ImageView fx:id="attachmentIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
<image>
<Image url="@/org/to/telegramfinalproject/Icons/attachment_light.png"/>
</image>
</ImageView>
</graphic>
</Button>
<!-- Expanding input -->
<TextArea fx:id="messageInput"
HBox.hgrow="ALWAYS"
promptText="Write a message..."
styleClass="msg-input"
wrapText="true"
prefRowCount="1"
minHeight="40"
maxHeight="120"
VBox.vgrow="NEVER" />
<!-- Send -->
<Button fx:id="sendButton" styleClass="send-btn">
<graphic>
<ImageView fx:id="sendIcon" fitWidth="20" fitHeight="20" preserveRatio="true">
<image>
<Image url="@/org/to/telegramfinalproject/Icons/send_cyan2.png"/>
</image>
</ImageView>
</graphic>
</Button>
</HBox>
</VBox>
@@ -1,69 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<StackPane fx:id="mainRoot" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.to.telegramfinalproject.UI.MainController">
<StackPane fx:id="mainRoot"
xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.to.telegramfinalproject.UI.MainController">
<children>
<!-- SplitPane: left (chat list / search) + right (chat panel) -->
<SplitPane fx:id="mainSplitPane"
dividerPositions="0.32"
orientation="HORIZONTAL"
prefHeight="700" prefWidth="1100"
styleClass="thin-splitpane">
<items>
<!-- Use SplitPane so the two sides are resizable -->
<SplitPane fx:id="mainSplitPane" dividerPositions="0.32" orientation="HORIZONTAL" prefHeight="700" prefWidth="1100" styleClass="thin-splitpane">
<items>
<!-- LEFT: Chat list -->
<VBox fx:id="leftPane" minWidth="72" spacing="10" style="-fx-background-color: #FFFFFF;"> <!-- keep at least avatar width when collapsed -->
<children>
<HBox spacing="10">
<padding>
<Insets bottom="10" left="10" right="10" top="10" />
</padding>
<children>
<Button fx:id="menuButton" minHeight="29.0" minWidth="40" onAction="#toggleSidebar" prefHeight="29.0" prefWidth="40.0" style="-fx-background-color: transparent;">
<graphic>
<ImageView fx:id="menuIcon" fitHeight="24" fitWidth="17" preserveRatio="true" smooth="true">
<image>
<Image url="@/org/to/telegramfinalproject/Icons/menu_dark.png" />
</image>
</ImageView>
</graphic>
</Button>
<TextField fx:id="searchBar" prefHeight="36" prefWidth="430.0" promptText="Search" style="-fx-background-color: #f5f5f5; -fx-background-radius: 15;" />
</children>
</HBox>
<ScrollPane fx:id="scrollPane" fitToWidth="true" hbarPolicy="NEVER" style="-fx-background-color: transparent;" vbarPolicy="AS_NEEDED">
<content>
<VBox fx:id="chatListContainer" spacing="5" style="-fx-background-color: transparent;">
<padding>
<Insets bottom="10" left="10" right="10" top="10" />
</padding>
</VBox>
</content>
</ScrollPane>
</children>
</VBox>
<!-- RIGHT: Chat panel -->
<StackPane fx:id="chatDisplayArea" minWidth="360" style="-fx-background-color: #FAFAFA;"> <!-- don't let chat page collapse -->
<children>
<Label style="-fx-text-fill: #888888;" text="Select a chat to start messaging">
<font>
<Font size="15" />
</font>
</Label>
</children>
</StackPane>
</items>
<!-- LEFT: Sidebar -->
<VBox fx:id="leftPane" minWidth="72" style="-fx-background-color: #FFFFFF;">
<!-- Top bar (always visible) -->
<HBox spacing="10">
<padding>
<Insets bottom="10" left="10" right="10" top="10"/>
</padding>
<children>
<Button fx:id="menuButton" minHeight="29.0" minWidth="40"
onAction="#toggleSidebar"
prefHeight="29.0" prefWidth="40.0"
style="-fx-background-color: transparent;">
<graphic>
<ImageView fx:id="menuIcon" fitHeight="24" fitWidth="17"
preserveRatio="true" smooth="true">
<image>
<Image url="@/org/to/telegramfinalproject/Icons/menu_dark.png"/>
</image>
</ImageView>
</graphic>
</Button>
<TextField fx:id="searchBar"
prefHeight="36" prefWidth="430.0"
promptText="Search"
style="-fx-background-color: #f5f5f5; -fx-background-radius: 15;"/>
</children>
</HBox>
<!-- Content area (chat list vs search) -->
<StackPane VBox.vgrow="ALWAYS">
<!-- Chat list (default) -->
<ScrollPane fx:id="scrollPane" fitToWidth="true" hbarPolicy="NEVER"
style="-fx-background-color: transparent;"
vbarPolicy="AS_NEEDED"
visible="true" managed="true">
<content>
<VBox fx:id="chatListContainer" spacing="5"
style="-fx-background-color: transparent;">
<padding>
<Insets bottom="10" left="10" right="10" top="10"/>
</padding>
</VBox>
</content>
</ScrollPane>
<!-- Search panel (hidden initially) -->
<VBox fx:id="chatSearchPane" visible="false" managed="false"
styleClass="chat-search-pane">
<!-- Section label -->
<Label text="Search messages in"
style="-fx-text-fill: #7e8a97; -fx-font-size: 11px; -fx-padding: 10 0 5 10;"/>
<!-- Tiny separator line -->
<Separator style="-fx-background-color: #e6e6e6;" prefHeight="0.5"/>
<!-- User profile + "This chat" + close -->
<HBox spacing="10" alignment="CENTER_LEFT" style="-fx-padding: 8 10; -fx-border-color: #e6e6e6; -fx-border-width: 0 0 1 0;">
<!-- Avatar -->
<ImageView fx:id="searchChatAvatar" fitWidth="32" fitHeight="32" preserveRatio="true">
<image>
<Image url="@/org/to/telegramfinalproject/Avatars/profile_test.png"/>
</image>
</ImageView>
<!-- scope -->
<VBox alignment="CENTER_LEFT">
<Label text="This chat" style="-fx-text-fill: #7e8a97; -fx-font-size: 11px;"/>
</VBox>
<Region HBox.hgrow="ALWAYS"/>
<!-- Close button -->
<Button text="✕" onAction="#closeSearchPanel"
style="-fx-background-color: transparent; -fx-cursor: hand;"/>
</HBox>
<!-- Placeholder -->
<VBox alignment="CENTER" spacing="10" prefHeight="300">
<ImageView fx:id="searchIconLarge" fitWidth="64" fitHeight="64" preserveRatio="true"/>
<Label text="Search for messages" styleClass="search-placeholder"/>
</VBox>
<!-- Results -->
<ListView fx:id="chatSearchResults" visible="false" managed="false"/>
</VBox>
</StackPane>
</VBox>
<!-- RIGHT: Chat panel -->
<StackPane fx:id="chatDisplayArea" minWidth="360" style="-fx-background-color: #FAFAFA;">
<children>
<Label fx:id="placeholderLabel" alignment="CENTER"
prefHeight="21.0" prefWidth="248.0"
styleClass="placeholder-label"
text="Select a chat to start messaging">
<font><Font size="15"/></font>
</Label>
</children>
</StackPane>
</items>
</SplitPane>
<!-- Overlay (for sidebar) -->
<Pane fx:id="overlay" mouseTransparent="true" onMouseClicked="#closeSidebar" style="-fx-background-color: rgba(0,0,0,0.3);" visible="false" />
<Pane fx:id="overlay"
mouseTransparent="true"
onMouseClicked="#closeSidebar"
style="-fx-background-color: rgba(0,0,0,0.3);"
visible="false"/>
</children>
</StackPane>
Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B