aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2018-11-09 09:46:43 +0100
committerdec05eba <dec05eba@protonmail.com>2018-11-09 09:46:47 +0100
commit5fab9a3a2cf048330f687dda48c76c95a3a67d98 (patch)
tree1be0385ef62f5fbf0794d98a697e0ff11fe7a510
parent118277121c8b1767a78f76d19c44aea496121b34 (diff)
Add room joining
-rw-r--r--css/style.css25
m---------depends/dchat_core0
-rw-r--r--images/messages_icon.pngbin0 -> 817 bytes
-rw-r--r--include/ChatWindow.hpp25
-rw-r--r--include/InputDialog.hpp6
-rw-r--r--include/RoomNotificationsWindow.hpp36
-rw-r--r--include/RoomSettingsWindow.hpp30
-rw-r--r--include/Topbar.hpp22
-rwxr-xr-xrun.sh3
-rw-r--r--src/ChatMessage.cpp2
-rw-r--r--src/ChatWindow.cpp306
-rw-r--r--src/InputDialog.cpp38
-rw-r--r--src/RoomNotificationsWindow.cpp105
-rw-r--r--src/RoomSettingsWindow.cpp88
-rw-r--r--src/Topbar.cpp44
-rw-r--r--src/Window.cpp16
16 files changed, 637 insertions, 109 deletions
diff --git a/css/style.css b/css/style.css
index b48451f..1c34440 100644
--- a/css/style.css
+++ b/css/style.css
@@ -68,6 +68,11 @@ separator {
background-size: 0px;
}
+treeview {
+ background-color: #36393e;
+ color: #f7f7f7;
+}
+
.separator-horizontal-margin {
margin-top: 10px;
margin-bottom: 10px;
@@ -91,7 +96,12 @@ separator {
border: 1px solid black;
}
-#side-panels separator {
+.side-panels {
+ border: none;
+ outline: none;
+}
+
+.side-panels separator {
border: 1px solid #2f3136;
background-color: #2f3136;
}
@@ -123,14 +133,15 @@ separator {
font-weight: bold;
}
-#left-panel {
+.left-panel {
background-color: #36393e;
padding: 10px 10px 10px 10px;
border: 1px solid #36393e;
}
-#left-panel separator {
+.left-panel separator {
border: 1px solid #36393e;
+ border-top: 3px solid #444444;
background-color: #36393e;
}
@@ -233,6 +244,14 @@ textview text {
font-size: 14px;
}
+.chat-input-focused text {
+ color: #f7f7f7;
+}
+
+.chat-input-unfocused text {
+ color: #a7a7a7;
+}
+
.window-notification {
background-color: #aa3030;
}
diff --git a/depends/dchat_core b/depends/dchat_core
-Subproject fff32050deb68c10118afd98e1f0f45cfe28993
+Subproject b37a6fa4c2fb73376b2be701127b6dbf7f7bdb8
diff --git a/images/messages_icon.png b/images/messages_icon.png
new file mode 100644
index 0000000..24337f4
--- /dev/null
+++ b/images/messages_icon.png
Binary files differ
diff --git a/include/ChatWindow.hpp b/include/ChatWindow.hpp
index d457b7d..48bbff5 100644
--- a/include/ChatWindow.hpp
+++ b/include/ChatWindow.hpp
@@ -1,6 +1,8 @@
#pragma once
#include "ImageButton.hpp"
+#include "RoomSettingsWindow.hpp"
+#include "RoomNotificationsWindow.hpp"
#include <dchat/Room.hpp>
#include <gtkmm/label.h>
#include <gtkmm/togglebutton.h>
@@ -17,6 +19,7 @@ namespace dchat
{
class ChatMessage;
class Window;
+ class Topbar;
class ChatWindow : public Gtk::Grid
{
@@ -25,24 +28,31 @@ namespace dchat
~ChatWindow();
void addRoom(std::shared_ptr<Room> room);
void addMessage(const RoomAddMessageRequest &request);
- void addUser(std::shared_ptr<Room> room, std::shared_ptr<User> user);
+ void addUser(const RoomAddUserRequest &request);
void setUserNickname(const UserChangeNicknameRequest &request);
void changeRoomName(const RoomChangeNameRequest &request);
+ void addInviteRequest(const InviteUserRequest &request);
void scrollToBottom();
+
+ Topbar *topbar;
+ Gtk::Stack stack;
+ Gtk::Grid chatPage;
+ RoomSettingsWindow roomSettingsWindow;
+ RoomNotificationsWindow roomNotificationsWindow;
+ Window *window;
private:
- void setupTopBar();
+ void setupTopbar();
void setupLeftPanel(Gtk::Paned *sidePanels);
void setupMessageArea(Gtk::Grid *rightPanel);
void setupChatInput(Gtk::Grid *rightPanel);
void setCurrentRoom(std::shared_ptr<Room> room);
private:
- Gtk::Grid topbar;
- Gtk::Entry topbarSearchBar;
Gtk::Grid leftPanelChannels;
Gtk::Stack leftPanelUsersStack;
- ImageButton addRoomButton;
- Gtk::Label currentChannelTitle;
+ ImageButton createRoomButton;
+ ImageButton joinRoomButton;
+ ImageButton userSettingsButton;
Gtk::ScrolledWindow messageArea;
Gtk::Stack messageAreaStack;
Gtk::TextView chatInput;
@@ -60,6 +70,7 @@ namespace dchat
int roomCount;
RoomData *currentRoomData;
std::shared_ptr<Room> currentRoom;
- Window *window;
+ bool chatInputShowPlaceholder;
+ bool chatInputChangeByPlaceholder;
};
} \ No newline at end of file
diff --git a/include/InputDialog.hpp b/include/InputDialog.hpp
index 4b2b02a..ae06de2 100644
--- a/include/InputDialog.hpp
+++ b/include/InputDialog.hpp
@@ -2,15 +2,15 @@
#include <gtkmm/dialog.h>
#include <gtkmm/entry.h>
+#include <vector>
namespace dchat
{
class InputDialog : public Gtk::Dialog
{
public:
- InputDialog(const char *title, const char *text, const char *acceptText = "Create", const char *cancelText = "Cancel");
- Glib::ustring getInput() const;
+ InputDialog(const char *title, const std::vector<const char*> &texts, const char *acceptText = "Create", const char *cancelText = "Cancel");
- Gtk::Entry entry;
+ std::vector<Gtk::Entry*> entries;
};
} \ No newline at end of file
diff --git a/include/RoomNotificationsWindow.hpp b/include/RoomNotificationsWindow.hpp
new file mode 100644
index 0000000..e4e29f5
--- /dev/null
+++ b/include/RoomNotificationsWindow.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <dchat/Room.hpp>
+#include <gtkmm/stack.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/liststore.h>
+#include <unordered_map>
+
+namespace dchat
+{
+ class ChatWindow;
+
+ class RoomNotificationsWindow : public Gtk::Stack
+ {
+ public:
+ RoomNotificationsWindow(ChatWindow *chatWindow);
+
+ void addInviteRequest(const InviteUserRequest &request);
+ private:
+ class RoomNotifications : public Gtk::ScrolledWindow
+ {
+ public:
+ Glib::RefPtr<Gtk::ListStore> listStore;
+ Gtk::TreeModelColumn<Glib::ustring> userPublicKeyColumn;
+ Gtk::TreeModelColumn<Glib::ustring> messageColumn;
+ Gtk::TreeModel::ColumnRecord columns;
+ std::unordered_map<std::string, InviteUserRequest> inviteRequests;
+ };
+
+ RoomNotifications* createRoomNotifications();
+
+ std::unordered_map<Room*, RoomNotifications*> roomNotificationsMap;
+
+ ChatWindow *chatWindow;
+ };
+} \ No newline at end of file
diff --git a/include/RoomSettingsWindow.hpp b/include/RoomSettingsWindow.hpp
new file mode 100644
index 0000000..9ffd808
--- /dev/null
+++ b/include/RoomSettingsWindow.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <gtkmm/grid.h>
+#include <gtkmm/entry.h>
+#include <gtkmm/label.h>
+#include <dchat/Room.hpp>
+
+namespace Gtk
+{
+ class Paned;
+}
+
+namespace dchat
+{
+ class ChatWindow;
+
+ class RoomSettingsWindow : public Gtk::Grid
+ {
+ public:
+ RoomSettingsWindow(ChatWindow *chatWindow);
+ void selectRoom(std::shared_ptr<Room> room);
+
+ void setupLeftPanel(Gtk::Paned *sidePanels);
+ void setupRightPanel(Gtk::Paned *sidePanels);
+ private:
+ ChatWindow *chatWindow;
+ Gtk::Entry roomNameEntry;
+ Gtk::Label inviteKey;
+ };
+} \ No newline at end of file
diff --git a/include/Topbar.hpp b/include/Topbar.hpp
new file mode 100644
index 0000000..ebb7729
--- /dev/null
+++ b/include/Topbar.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "ImageButton.hpp"
+#include <gtkmm/grid.h>
+#include <gtkmm/searchentry.h>
+#include <gtkmm/label.h>
+
+namespace dchat
+{
+ class Topbar : public Gtk::Grid
+ {
+ public:
+ Topbar();
+ void setTitle(const Glib::ustring &title);
+
+ ImageButton roomSettingsButton;
+ ImageButton roomNotificationsButton;
+ private:
+ Gtk::SearchEntry topbarSearchBar;
+ Gtk::Label currentRoomTitle;
+ };
+} \ No newline at end of file
diff --git a/run.sh b/run.sh
index 542f03f..b78f4e8 100755
--- a/run.sh
+++ b/run.sh
@@ -13,4 +13,5 @@ if [ ! -f ~/.local/share/fonts/Lato-Bold.ttf ]; then
cp ./fonts/Lato-Bold.ttf ~/.local/share/fonts/Lato-Bold.ttf
fc-cache
fi
-env GTK_THEME="css/style.css" ./sibs-build/debug/dchat
+platform=`sibs platform`
+env GTK_THEME="css/style.css" ./sibs-build/$platform/debug/dchat
diff --git a/src/ChatMessage.cpp b/src/ChatMessage.cpp
index fe3afdc..98d7f88 100644
--- a/src/ChatMessage.cpp
+++ b/src/ChatMessage.cpp
@@ -10,7 +10,7 @@ namespace dchat
avatar.set_halign(Gtk::ALIGN_START);
avatar.set_valign(Gtk::ALIGN_START);
avatar.set_size_request(50, 50);
- avatar.url = "https://discordemoji.com/assets/emoji/7752_PepePOOGERSFAST.gif";
+ //avatar.url = "https://discordemoji.com/assets/emoji/7752_PepePOOGERSFAST.gif";
username.set_selectable(true);
username.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);
diff --git a/src/ChatWindow.cpp b/src/ChatWindow.cpp
index a991e57..473b5d1 100644
--- a/src/ChatWindow.cpp
+++ b/src/ChatWindow.cpp
@@ -2,10 +2,12 @@
#include "../include/ChatMessage.hpp"
#include "../include/InputDialog.hpp"
#include "../include/Window.hpp"
+#include "../include/Topbar.hpp"
#include <gtkmm/alignment.h>
#include <gtkmm/viewport.h>
#include <gtkmm/scrollbar.h>
#include <gtkmm/eventbox.h>
+#include <giomm/notification.h>
#include <assert.h>
namespace dchat
@@ -14,20 +16,33 @@ namespace dchat
const int MERGE_MESSAGE_TIMESTAMP_DIFF_SEC = 60;
ChatWindow::ChatWindow(Window *_window) :
- addRoomButton("images/add_button_small.png", " Add room"),
+ roomSettingsWindow(this),
+ roomNotificationsWindow(this),
+ createRoomButton("images/add_button_small.png", " Create room"),
+ joinRoomButton("images/add_button_small.png", " Join room"),
+ userSettingsButton("images/settings-icon.png", " User settings"),
roomCount(0),
currentRoomData(nullptr),
- window(_window)
+ window(_window),
+ chatInputShowPlaceholder(true),
+ chatInputChangeByPlaceholder(false)
{
assert(window);
+ stack.set_homogeneous(false);
+ stack.set_transition_type(Gtk::StackTransitionType::STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
+ stack.set_transition_duration(250);
leftPanelUsersStack.set_homogeneous(false);
messageAreaStack.set_homogeneous(false);
- setupTopBar();
+ setupTopbar();
+ attach(stack, 0, 1, 2, 2);
- Gtk::Paned *sidePanels = Gtk::manage(new Gtk::Paned());
- sidePanels->set_name("side-panels");
- sidePanels->set_border_width(0);
- attach(*sidePanels, 0, 1, 1, 2);
+ stack.add(chatPage, "chat");
+ stack.add(roomSettingsWindow, "settings");
+ stack.add(roomNotificationsWindow, "notifications");
+
+ Gtk::Paned *sidePanels = Gtk::manage(new Gtk::Paned(Gtk::ORIENTATION_HORIZONTAL));
+ sidePanels->get_style_context()->add_class("side-panels");
+ chatPage.attach(*sidePanels, 0, 1, 1, 2);
setupLeftPanel(sidePanels);
@@ -41,6 +56,9 @@ namespace dchat
set_vexpand(true);
set_hexpand(true);
+ chatPage.show_all();
+ stack.show();
+ stack.set_visible_child("chat");
}
ChatWindow::~ChatWindow()
@@ -54,37 +72,48 @@ namespace dchat
}
}
- void ChatWindow::setupTopBar()
+ void ChatWindow::setupTopbar()
{
- topbar.set_name("top-bar");
- topbar.set_hexpand(true);
- attach(topbar, 0, 0, 2, 1);
-
- Gtk::Grid *topbarLeft = Gtk::manage(new Gtk::Grid());
- topbarLeft->set_name("top-bar-left");
- topbarLeft->set_size_request(180);
- topbarLeft->set_valign(Gtk::ALIGN_CENTER);
- topbarLeft->set_halign(Gtk::ALIGN_CENTER);
- topbar.attach(*topbarLeft, 0, 0, 1, 1);
-
- topbarSearchBar.set_name("top-bar-search");
- topbarSearchBar.set_placeholder_text("Search...");
- topbarSearchBar.set_size_request(180);
- topbarLeft->attach(topbarSearchBar, 0, 0, 1, 1);
-
- Gtk::Grid *topbarRight = Gtk::manage(new Gtk::Grid());
- topbarRight->set_name("top-bar-right");
- topbarRight->set_hexpand(true);
- topbarRight->set_valign(Gtk::ALIGN_CENTER);
- topbar.attach_next_to(*topbarRight, *topbarLeft, Gtk::POS_RIGHT, 1, 1);
-
- currentChannelTitle.set_name("current-room-title");
- currentChannelTitle.set_hexpand(true);
- currentChannelTitle.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
- topbarRight->attach(currentChannelTitle, 0, 0, 1, 1);
-
- ImageButton *channelSettings = Gtk::manage(new ImageButton("images/settings-icon.png", nullptr));
- topbarRight->attach_next_to(*channelSettings, currentChannelTitle, Gtk::POS_RIGHT, 1, 1);
+ topbar = Gtk::manage(new Topbar());
+ topbar->show_all();
+ attach(*topbar, 0, 0, 2, 1);
+
+ topbar->roomSettingsButton.signal_clicked().connect([this]
+ {
+ if(!currentRoom)
+ {
+ window->windowNotification->show("You need to be inside a room to go to settings");
+ return;
+ }
+
+ if(!currentRoom->localUser)
+ {
+ window->windowNotification->show("You need to be a member of the room to go to room settings");
+ return;
+ }
+
+ roomSettingsWindow.show_all();
+ roomSettingsWindow.selectRoom(currentRoom);
+ stack.set_visible_child("settings");
+ });
+
+ topbar->roomNotificationsButton.signal_clicked().connect([this]
+ {
+ if(!currentRoom)
+ {
+ window->windowNotification->show("You need to be inside a room to go to room notifications");
+ return;
+ }
+
+ if(!currentRoom->localUser)
+ {
+ window->windowNotification->show("You need to be a member of the room to go to room notifications");
+ return;
+ }
+
+ roomNotificationsWindow.show_all();
+ stack.set_visible_child("notifications");
+ });
}
void ChatWindow::setupLeftPanel(Gtk::Paned *sidePanels)
@@ -92,53 +121,110 @@ namespace dchat
Gtk::Grid *leftPanelLayout = Gtk::manage(new Gtk::Grid());
leftPanelLayout->set_vexpand(true);
leftPanelLayout->set_size_request(200);
- leftPanelLayout->set_name("left-panel");
+ leftPanelLayout->get_style_context()->add_class("left-panel");
sidePanels->add1(*leftPanelLayout);
Gtk::Paned *leftPanel = Gtk::manage(new Gtk::Paned(Gtk::ORIENTATION_VERTICAL));
leftPanel->set_vexpand(true);
leftPanelLayout->attach(*leftPanel, 0, 0, 1, 2);
- leftPanelChannels.set_vexpand(true);
- leftPanel->add1(leftPanelChannels);
+ Gtk::Grid *channelsLayout = Gtk::manage(new Gtk::Grid());
+ channelsLayout->set_orientation(Gtk::ORIENTATION_VERTICAL);
+ channelsLayout->set_vexpand(true);
+ channelsLayout->set_hexpand(true);
+ leftPanel->add1(*channelsLayout);
Gtk::Label *channelsTitle = Gtk::manage(new Gtk::Label());
channelsTitle->set_name("channels-title");
channelsTitle->set_text("Channels");
channelsTitle->set_halign(Gtk::ALIGN_START);
- leftPanelChannels.attach(*channelsTitle, 0, 0, 1, 1);
+ channelsLayout->attach(*channelsTitle, 0, 0, 1, 1);
+
+ Gtk::ScrolledWindow *channelsScrollWindow = Gtk::manage(new Gtk::ScrolledWindow());
+ channelsScrollWindow->set_vexpand(true);
+ channelsScrollWindow->set_hexpand(true);
+ channelsScrollWindow->set_overlay_scrolling(false);
+ channelsLayout->attach_next_to(*channelsScrollWindow, *channelsTitle, Gtk::POS_BOTTOM, 1, 2);
+
+ leftPanelChannels.set_vexpand(true);
+ leftPanelChannels.set_hexpand(true);
+ channelsScrollWindow->add(leftPanelChannels);
////
- leftPanelUsersStack.set_vexpand(true);
- leftPanel->add2(leftPanelUsersStack);
+ Gtk::Grid *userLayout = Gtk::manage(new Gtk::Grid());
+ leftPanel->add2(*userLayout);
- addRoomButton.set_halign(Gtk::ALIGN_START);
- leftPanelLayout->attach_next_to(addRoomButton, *leftPanel, Gtk::POS_BOTTOM, 1, 1);
- addRoomButton.signal_clicked().connect([this]()
+ Gtk::Label *usersTitle = Gtk::manage(new Gtk::Label());
+ usersTitle->get_style_context()->add_class("users-title");
+ usersTitle->set_text("Users");
+ usersTitle->set_halign(Gtk::ALIGN_START);
+ userLayout->attach(*usersTitle, 0, 0, 1, 1);
+
+ //leftPanelUsersStack.set_vexpand(true);
+ userLayout->attach_next_to(leftPanelUsersStack, *usersTitle, Gtk::POS_BOTTOM, 1, 2);
+
+ createRoomButton.set_halign(Gtk::ALIGN_START);
+ createRoomButton.set_valign(Gtk::ALIGN_END);
+ leftPanelLayout->attach_next_to(createRoomButton, *leftPanel, Gtk::POS_BOTTOM, 1, 1);
+ createRoomButton.signal_clicked().connect([this]()
{
- InputDialog createRoomDialog("Create a new room", "Room name");
+ InputDialog createRoomDialog("Create a new room", { "Room name" });
switch(createRoomDialog.run())
{
case Gtk::RESPONSE_ACCEPT:
{
// TODO: Show error inline in the create room dialog
- Glib::ustring roomName = createRoomDialog.getInput();
+ Glib::ustring roomName = createRoomDialog.entries[0]->get_text();
if(roomName.size() == 0 || roomName.size() > 32)
window->windowNotification->show("Room name has to be between 1 and 32 characters");
else
- window->rooms->createRoom(roomName);
+ {
+ auto room = window->rooms->createRoom(roomName);
+ setCurrentRoom(room);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ });
+
+ joinRoomButton.set_halign(Gtk::ALIGN_START);
+ leftPanelLayout->attach_next_to(joinRoomButton, createRoomButton, Gtk::POS_BOTTOM, 1, 1);
+ joinRoomButton.signal_clicked().connect([this]()
+ {
+ InputDialog joinRoomDialog("Join room", { "Invite key", "Message to send to admin" });
+ switch(joinRoomDialog.run())
+ {
+ case Gtk::RESPONSE_ACCEPT:
+ {
+ // TODO: Show error inline in the join room dialog
+ Glib::ustring inviteKey = joinRoomDialog.entries[0]->get_text();
+ Glib::ustring message = joinRoomDialog.entries[1]->get_text();
+ if(inviteKey.size() != 130)
+ window->windowNotification->show("Invite key has to be 130 characters");
+ else
+ window->rooms->requestJoinRoom(inviteKey, message);
break;
}
default:
break;
}
});
+
+ userSettingsButton.set_halign(Gtk::ALIGN_START);
+ leftPanelLayout->attach_next_to(userSettingsButton, joinRoomButton, Gtk::POS_BOTTOM, 1, 1);
+ userSettingsButton.signal_clicked().connect([this]()
+ {
+ printf("user settings!\n");
+ });
}
void ChatWindow::setupMessageArea(Gtk::Grid *rightPanel)
{
//messageArea.set_valign(Gtk::ALIGN_START);
messageArea.set_vexpand(true);
+ messageArea.set_overlay_scrolling(false);
messageArea.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC);
rightPanel->attach(messageArea, 0, 0, 1, 2);
@@ -153,6 +239,7 @@ namespace dchat
Gtk::ScrolledWindow *chatScrollWindow = Gtk::manage(new Gtk::ScrolledWindow());
chatScrollWindow->set_hexpand(true);
+ chatScrollWindow->set_overlay_scrolling(false);
chatScrollWindow->set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_NEVER);
chatScrollWindow->set_name("chat-scroll-view");
chatArea->attach(*chatScrollWindow, 0, 0, 1, 1);
@@ -161,8 +248,35 @@ namespace dchat
chatInput.set_editable(false);
chatInput.set_name("chat-input");
chatInput.set_wrap_mode(Gtk::WrapMode::WRAP_WORD_CHAR);
+ chatInput.get_buffer()->set_text("Type a message...");
+ chatInput.get_style_context()->add_class("chat-input-unfocused");
+ chatInputShowPlaceholder = true;
chatScrollWindow->add(chatInput);
+ chatInput.signal_focus_in_event().connect([this](GdkEventFocus *event)
+ {
+ if(chatInputShowPlaceholder)
+ {
+ chatInputChangeByPlaceholder = true;
+ chatInput.get_buffer()->set_text("");
+ }
+ chatInput.get_style_context()->remove_class("chat-input-unfocused");
+ chatInput.get_style_context()->add_class("chat-input-focused");
+ return false;
+ });
+
+ chatInput.signal_focus_out_event().connect([this](GdkEventFocus *event)
+ {
+ if(chatInputShowPlaceholder)
+ {
+ chatInputChangeByPlaceholder = true;
+ chatInput.get_buffer()->set_text("Type a message...");
+ chatInput.get_style_context()->remove_class("chat-input-focused");
+ chatInput.get_style_context()->add_class("chat-input-unfocused");
+ }
+ return false;
+ });
+
double fontSize = 18.5;//PANGO_PIXELS(chatInput.get_style_context()->get_font().get_size());
chatPrevNumLines = 1;
chatInput.signal_key_press_event().connect([this](GdkEventKey *event)
@@ -178,17 +292,27 @@ namespace dchat
}
return false;
}, false);
+
chatInput.get_buffer()->signal_changed().connect([this, chatScrollWindow, fontSize]
{
+ if(chatInputChangeByPlaceholder)
+ {
+ chatInputChangeByPlaceholder = false;
+ }
+ else
+ {
+ chatInputShowPlaceholder = chatInput.get_buffer()->get_char_count() == 0;
+ chatInputChangeByPlaceholder = false;
+ }
+
int numLines = chatInput.get_buffer()->get_line_count();
- numLines = std::min(numLines, 10);
if(numLines != chatPrevNumLines)
{
- chatPrevNumLines = numLines;
- chatScrollWindow->set_min_content_height(fontSize * numLines);
+ if(numLines <= 10)
+ chatScrollWindow->set_min_content_height(fontSize * std::min(numLines, 10));
auto adj = chatScrollWindow->get_vadjustment();
- if(chatInput.get_buffer()->get_line_count() <= 10)
+ if(chatInput.get_buffer()->get_line_count() <= 11)
{
chatScrollWindow->set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_NEVER);
adj->set_value(0);
@@ -198,7 +322,9 @@ namespace dchat
chatScrollWindow->set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_ALWAYS);
}
}
+ chatPrevNumLines = numLines;
});
+
chatScrollWindow->get_vadjustment()->signal_value_changed().connect([this, chatScrollWindow]()
{
auto adj = chatScrollWindow->get_vadjustment();
@@ -222,13 +348,6 @@ namespace dchat
leftPanelUsersLayout->show();
leftPanelUsersStack.add(*leftPanelUsersLayout, roomIdStr);
- Gtk::Label *usersTitle = Gtk::manage(new Gtk::Label());
- usersTitle->get_style_context()->add_class("users-title");
- usersTitle->set_text("Users");
- usersTitle->set_halign(Gtk::ALIGN_START);
- usersTitle->show();
- leftPanelUsersLayout->attach(*usersTitle, 0, 0, 1, 1);
-
Gtk::Grid *messageAreaLayout = new Gtk::Grid();
messageAreaLayout->show();
messageAreaStack.add(*messageAreaLayout, roomIdStr);
@@ -245,15 +364,22 @@ namespace dchat
{
setCurrentRoom(room);
});
- leftPanelChannels.attach(*roomButton, 0, 1 + roomCount, 1, 1);
+ leftPanelChannels.attach(*roomButton, 0, roomCount, 1, 1);
++roomCount;
- currentRoomData = new RoomData { leftPanelUsersLayout, messageAreaLayout, roomButton };
- roomDataById[*room->id] = currentRoomData;
- currentRoom = room;
- chatInput.set_editable(true);
- leftPanelUsersStack.set_visible_child(roomIdStr);
- messageAreaStack.set_visible_child(roomIdStr);
+ RoomData *roomData = new RoomData { leftPanelUsersLayout, messageAreaLayout, roomButton };
+ roomDataById[*room->id] = roomData;
+ if(!currentRoom)
+ {
+ currentRoom = room;
+ currentRoomData = roomData;
+ if(room->localUser)
+ chatInput.set_editable(true);
+ else
+ chatInput.set_editable(false);
+ leftPanelUsersStack.set_visible_child(roomIdStr);
+ messageAreaStack.set_visible_child(roomIdStr);
+ }
}
void ChatWindow::setCurrentRoom(std::shared_ptr<Room> room)
@@ -261,10 +387,13 @@ namespace dchat
std::string roomIdStr = room->id->toString();
leftPanelUsersStack.set_visible_child(roomIdStr);
messageAreaStack.set_visible_child(roomIdStr);
- currentChannelTitle.set_text(room->name);
+ topbar->setTitle(room->name);
currentRoom = room;
currentRoomData = roomDataById[*room->id];
- chatInput.set_editable(true);
+ if(room->localUser)
+ chatInput.set_editable(true);
+ else
+ chatInput.set_editable(false);
// TODO: Instead of scrolling to bottom, remember scroll position (even after restarting application).
// We want to show oldest unread message first
@@ -315,16 +444,31 @@ namespace dchat
}
}
- void ChatWindow::addUser(std::shared_ptr<Room> room, std::shared_ptr<User> user)
+ void ChatWindow::addUser(const RoomAddUserRequest &request)
{
Gtk::Label *username = Gtk::manage(new Gtk::Label("Anonymous"));
username->set_halign(Gtk::ALIGN_START);
username->show();
username->get_style_context()->add_class("username-list-username");
- user->userdata = username;
- RoomData *roomData = roomDataById[*room->id];
- roomData->leftPanelUsersLayout->attach(*username, 0, room->userByPublicKey.size(), 1, 1);
- fprintf(stderr, "Added user %s\n", user->publicKey.toString().c_str());
+ request.user->userdata = username;
+ RoomData *roomData = roomDataById[*request.room->id];
+ roomData->leftPanelUsersLayout->attach(*username, 0, request.room->userByPublicKey.size() - 1, 1, 1);
+ fprintf(stderr, "Added user %s\n", request.user->publicKey.toString().c_str());
+
+ if(roomData == currentRoomData)
+ {
+ if(request.room->localUser)
+ chatInput.set_editable(true);
+ else
+ chatInput.set_editable(false);
+ }
+
+ if(!request.loadedFromCache && request.isLocalUser)
+ {
+ Glib::ustring msg = "You were added to room ";
+ msg += request.room->name + " by " + request.addedByUser->nickname;
+ window->windowNotification->show(msg);
+ }
}
void ChatWindow::setUserNickname(const UserChangeNicknameRequest &request)
@@ -339,10 +483,22 @@ namespace dchat
Gtk::Button *button = roomDataById[*request.room->id]->button;
static_cast<Gtk::Label*>(button->get_child())->set_text(request.newName);
if(*request.room->id == *currentRoom->id)
- currentChannelTitle.set_text(request.newName);
+ topbar->setTitle(request.newName);
fprintf(stderr, "Changed room %s name to %s\n", request.room->id->toString().c_str(), request.newName.c_str());
}
+ void ChatWindow::addInviteRequest(const InviteUserRequest &request)
+ {
+ auto notification = Gio::Notification::create("Invite");
+ Glib::ustring body = "User ";
+ body += request.userPublicKey.toString();
+ body += " wants to join your room " + request.room->name + ". Message from user: ";
+ body += request.message;
+ notification->set_body(body);
+ window->get_application()->send_notification(notification);
+ roomNotificationsWindow.addInviteRequest(request);
+ }
+
void ChatWindow::scrollToBottom()
{
while(gtk_events_pending())
diff --git a/src/InputDialog.cpp b/src/InputDialog.cpp
index 520bfa2..16c4d01 100644
--- a/src/InputDialog.cpp
+++ b/src/InputDialog.cpp
@@ -1,22 +1,37 @@
#include "../include/InputDialog.hpp"
#include <gtkmm/label.h>
+#include <gtkmm/grid.h>
namespace dchat
{
- InputDialog::InputDialog(const char *title, const char *text, const char *acceptText, const char *cancelText)
+ InputDialog::InputDialog(const char *title, const std::vector<const char*> &texts, const char *acceptText, const char *cancelText)
{
set_title(title);
Gtk::Box *box = get_content_area();
- Gtk::Label *label = Gtk::manage(new Gtk::Label(text));
- label->set_valign(Gtk::ALIGN_END);
- label->set_halign(Gtk::ALIGN_CENTER);
- box->pack_start(*label, true, true);
+ //box->set_halign(Gtk::ALIGN_CENTER);
+ //box->set_valign(Gtk::ALIGN_CENTER);
+ Gtk::Grid *grid = Gtk::manage(new Gtk::Grid());
+ grid->set_halign(Gtk::ALIGN_CENTER);
+ grid->set_valign(Gtk::ALIGN_CENTER);
+ box->pack_start(*grid, true, true);
+ int i = 0;
+ for(const char *text : texts)
+ {
+ Gtk::Label *label = Gtk::manage(new Gtk::Label(text));
+ label->set_valign(Gtk::ALIGN_END);
+ label->set_halign(Gtk::ALIGN_CENTER);
+ grid->attach(*label, 0, i, 1, 1);
- entry.set_valign(Gtk::ALIGN_CENTER);
- entry.set_halign(Gtk::ALIGN_CENTER);
- entry.set_hexpand(true);
- box->pack_end(entry, true, true);
+ Gtk::Entry *entry = Gtk::manage(new Gtk::Entry());
+ entry->set_valign(Gtk::ALIGN_CENTER);
+ entry->set_halign(Gtk::ALIGN_CENTER);
+ entry->set_hexpand(true);
+ grid->attach(*entry, 0, i + 1, 2, 1);
+ entries.push_back(entry);
+
+ i += 2;
+ }
add_button(acceptText, Gtk::RESPONSE_ACCEPT);
add_button(cancelText, Gtk::RESPONSE_CANCEL);
@@ -24,9 +39,4 @@ namespace dchat
set_size_request(300, 150);
}
-
- Glib::ustring InputDialog::getInput() const
- {
- return entry.get_text();
- }
} \ No newline at end of file
diff --git a/src/RoomNotificationsWindow.cpp b/src/RoomNotificationsWindow.cpp
new file mode 100644
index 0000000..a491840
--- /dev/null
+++ b/src/RoomNotificationsWindow.cpp
@@ -0,0 +1,105 @@
+#include "../include/RoomNotificationsWindow.hpp"
+#include "../include/ChatWindow.hpp"
+#include "../include/Window.hpp"
+#include <gtkmm/treeview.h>
+#include <gtkmm/dialog.h>
+#include <assert.h>
+
+namespace dchat
+{
+ RoomNotificationsWindow::RoomNotificationsWindow(ChatWindow *_chatWindow) :
+ chatWindow(_chatWindow)
+ {
+ set_vexpand(true);
+ set_hexpand(true);
+ set_border_width(25);
+ }
+
+ RoomNotificationsWindow::RoomNotifications* RoomNotificationsWindow::createRoomNotifications()
+ {
+ RoomNotifications *roomNotifications = Gtk::manage(new RoomNotifications());
+ roomNotifications->columns.add(roomNotifications->userPublicKeyColumn);
+ roomNotifications->columns.add(roomNotifications->messageColumn);
+ roomNotifications->listStore = Gtk::ListStore::create(roomNotifications->columns);
+
+ Gtk::TreeView *treeView = Gtk::manage(new Gtk::TreeView(roomNotifications->listStore));
+ treeView->append_column("Public key", roomNotifications->userPublicKeyColumn);
+ treeView->append_column("Message", roomNotifications->messageColumn);
+ treeView->set_reorderable();
+ //treeView->set_rules_hint();
+ treeView->set_headers_visible();
+ treeView->set_headers_clickable();
+ treeView->set_activate_on_single_click(false);
+ treeView->get_selection()->set_mode(Gtk::SelectionMode::SELECTION_SINGLE);
+ treeView->signal_row_activated().connect([roomNotifications, treeView, this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column)
+ {
+ if(path.empty()) return;
+ auto selectedRow = treeView->get_selection()->get_selected();
+ Glib::ustring userPublicKeyStr = selectedRow->get_value(roomNotifications->userPublicKeyColumn);
+ assert(roomNotifications->inviteRequests.find(userPublicKeyStr) != roomNotifications->inviteRequests.end());
+ InviteUserRequest &request = roomNotifications->inviteRequests[userPublicKeyStr];
+
+ Gtk::Dialog dialog;
+ dialog.set_title("Add user to room");
+ Glib::ustring msg = "Are you sure you want to add the user ";
+ msg += userPublicKeyStr;
+ msg += " to the room ";
+ msg += request.room->name + " (" + request.room->id->toString() + ") ?";
+ dialog.get_content_area()->pack_start(*Gtk::manage(new Gtk::Label(msg)));
+ dialog.add_button("Yes", Gtk::RESPONSE_YES);
+ dialog.add_button("No", Gtk::RESPONSE_NO);
+ switch(dialog.run())
+ {
+ case Gtk::RESPONSE_YES:
+ {
+ assert(!request.room->groups.empty());
+ // TODO: Add user to a guest group instead
+ try
+ {
+ request.room->addUser(request.userPublicKey, request.room->groups[0]);
+ roomNotifications->listStore->erase(selectedRow);
+ }
+ catch(std::exception &e)
+ {
+ chatWindow->window->windowNotification->show(Glib::ustring("Failed to add user to room, reason: ") + e.what());
+ }
+ }
+ default:
+ break;
+ }
+ });
+ roomNotifications->add(*treeView);
+
+ roomNotifications->set_overlay_scrolling(false);
+ //roomNotifications->set_size_request(640, 480);
+ //roomNotifications->set_valign(Gtk::ALIGN_START);
+ //roomNotifications->set_halign(Gtk::ALIGN_CENTER);
+ return roomNotifications;
+ }
+
+ void RoomNotificationsWindow::addInviteRequest(const InviteUserRequest &request)
+ {
+ RoomNotifications *roomNotifications = nullptr;
+ auto it = roomNotificationsMap.find(request.room.get());
+ if(it == roomNotificationsMap.end())
+ {
+ roomNotifications = createRoomNotifications();
+ add(*roomNotifications, request.room->id->toString());
+ roomNotificationsMap[request.room.get()] = roomNotifications;
+ }
+ else
+ roomNotifications = it->second;
+
+ std::string userPublicKeyStr = request.userPublicKey.toString();
+ if(roomNotifications->inviteRequests.find(userPublicKeyStr) != roomNotifications->inviteRequests.end())
+ {
+ fprintf(stderr, "Got duplicate invite request from user %s for room %s\n", userPublicKeyStr.c_str(), request.room->id->toString().c_str());
+ return;
+ }
+ roomNotifications->inviteRequests[userPublicKeyStr] = request;
+
+ Gtk::TreeModel::Row row = *roomNotifications->listStore->append();
+ row[roomNotifications->userPublicKeyColumn] = userPublicKeyStr;
+ row[roomNotifications->messageColumn] = request.message;
+ }
+} \ No newline at end of file
diff --git a/src/RoomSettingsWindow.cpp b/src/RoomSettingsWindow.cpp
new file mode 100644
index 0000000..e20e36e
--- /dev/null
+++ b/src/RoomSettingsWindow.cpp
@@ -0,0 +1,88 @@
+#include "../include/RoomSettingsWindow.hpp"
+#include "../include/ChatWindow.hpp"
+#include <gtkmm/button.h>
+#include <gtkmm/togglebutton.h>
+#include <gtkmm/clipboard.h>
+#include <gtkmm/paned.h>
+#include <assert.h>
+
+namespace dchat
+{
+ RoomSettingsWindow::RoomSettingsWindow(ChatWindow *_chatWindow) :
+ chatWindow(_chatWindow)
+ {
+ assert(chatWindow);
+ Gtk::Paned *sidePanels = Gtk::manage(new Gtk::Paned(Gtk::ORIENTATION_HORIZONTAL));
+ sidePanels->get_style_context()->add_class("side-panels");
+ sidePanels->set_vexpand(true);
+ sidePanels->set_hexpand(true);
+ attach(*sidePanels, 0, 0, 1, 1);
+
+ setupLeftPanel(sidePanels);
+ setupRightPanel(sidePanels);
+
+ set_vexpand(true);
+ set_hexpand(true);
+ }
+
+ void RoomSettingsWindow::selectRoom(std::shared_ptr<Room> room)
+ {
+ roomNameEntry.set_text(room->name);
+ inviteKey.set_text(room->inviteKey);
+ }
+
+ void RoomSettingsWindow::setupLeftPanel(Gtk::Paned *sidePanels)
+ {
+ Gtk::Grid *leftPanel = Gtk::manage(new Gtk::Grid());
+ leftPanel->set_vexpand(true);
+ leftPanel->set_valign(Gtk::ALIGN_START);
+ leftPanel->set_halign(Gtk::ALIGN_START);
+ leftPanel->set_size_request(200);
+ leftPanel->get_style_context()->add_class("left-panel");
+ sidePanels->add1(*leftPanel);
+
+ Gtk::Label *settingsLabel = Gtk::manage(new Gtk::Label("Settings"));
+ leftPanel->attach(*settingsLabel, 0, 0, 1, 1);
+
+ Gtk::ToggleButton *generalButton = Gtk::manage(new Gtk::ToggleButton("General"));
+ leftPanel->attach_next_to(*generalButton, *settingsLabel, Gtk::POS_BOTTOM, 1, 1);
+
+ Gtk::ToggleButton *returnToChatButton = Gtk::manage(new Gtk::ToggleButton("Return to chat"));
+ returnToChatButton->signal_clicked().connect([this]
+ {
+ chatWindow->chatPage.show_all();
+ chatWindow->stack.set_visible_child("chat");
+ });
+ leftPanel->attach_next_to(*returnToChatButton, *generalButton, Gtk::POS_BOTTOM, 1, 1);
+ }
+
+ void RoomSettingsWindow::setupRightPanel(Gtk::Paned *sidePanels)
+ {
+ Gtk::Grid *rightPanel = Gtk::manage(new Gtk::Grid());
+ rightPanel->set_vexpand(true);
+ rightPanel->set_valign(Gtk::ALIGN_START);
+ rightPanel->set_halign(Gtk::ALIGN_START);
+ sidePanels->add2(*rightPanel);
+
+ Gtk::Label *roomNameLabel = Gtk::manage(new Gtk::Label("Room name"));
+ rightPanel->attach(*roomNameLabel, 0, 0, 1, 1);
+
+ roomNameEntry.set_editable(false);
+ rightPanel->attach_next_to(roomNameEntry, *roomNameLabel, Gtk::POS_BOTTOM, 1, 1);
+
+ Gtk::Label *inviteKeyLabel = Gtk::manage(new Gtk::Label("Invite key"));
+ rightPanel->attach_next_to(*inviteKeyLabel, roomNameEntry, Gtk::POS_BOTTOM, 1, 1);
+
+ inviteKey.set_selectable(true);
+ inviteKey.set_line_wrap(true);
+ inviteKey.set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
+ rightPanel->attach_next_to(inviteKey, *inviteKeyLabel, Gtk::POS_BOTTOM, 1, 1);
+
+ Gtk::Button *copyInviteKeyButton = Gtk::manage(new Gtk::Button("_Copy", true));
+ copyInviteKeyButton->signal_clicked().connect([this]
+ {
+ Gtk::Clipboard::get()->set_text(inviteKey.get_text());
+ });
+ rightPanel->attach_next_to(*copyInviteKeyButton, inviteKey, Gtk::POS_RIGHT, 1, 1);
+ }
+} \ No newline at end of file
diff --git a/src/Topbar.cpp b/src/Topbar.cpp
new file mode 100644
index 0000000..6339761
--- /dev/null
+++ b/src/Topbar.cpp
@@ -0,0 +1,44 @@
+#include "../include/Topbar.hpp"
+
+namespace dchat
+{
+ Topbar::Topbar() :
+ roomSettingsButton("images/settings-icon.png", nullptr),
+ roomNotificationsButton("images/messages_icon.png", nullptr)
+ {
+ set_name("top-bar");
+ set_hexpand(true);
+
+ Gtk::Grid *topbarLeft = Gtk::manage(new Gtk::Grid());
+ topbarLeft->set_name("top-bar-left");
+ topbarLeft->set_size_request(180);
+ topbarLeft->set_valign(Gtk::ALIGN_CENTER);
+ topbarLeft->set_halign(Gtk::ALIGN_CENTER);
+ attach(*topbarLeft, 0, 0, 1, 1);
+
+ topbarSearchBar.set_name("top-bar-search");
+ topbarSearchBar.set_placeholder_text("Search...");
+ topbarSearchBar.set_size_request(180);
+ topbarLeft->attach(topbarSearchBar, 0, 0, 1, 1);
+
+ Gtk::Grid *topbarRight = Gtk::manage(new Gtk::Grid());
+ topbarRight->set_name("top-bar-right");
+ topbarRight->set_hexpand(true);
+ topbarRight->set_valign(Gtk::ALIGN_CENTER);
+ attach_next_to(*topbarRight, *topbarLeft, Gtk::POS_RIGHT, 1, 1);
+
+ currentRoomTitle.set_name("current-room-title");
+ currentRoomTitle.set_selectable(true);
+ currentRoomTitle.set_hexpand(true);
+ currentRoomTitle.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
+ topbarRight->attach(currentRoomTitle, 0, 0, 1, 1);
+
+ topbarRight->attach_next_to(roomSettingsButton, currentRoomTitle, Gtk::POS_RIGHT, 1, 1);
+ topbarRight->attach_next_to(roomNotificationsButton, roomSettingsButton, Gtk::POS_RIGHT, 1, 1);
+ }
+
+ void Topbar::setTitle(const Glib::ustring &title)
+ {
+ currentRoomTitle.set_text(title);
+ }
+} \ No newline at end of file
diff --git a/src/Window.cpp b/src/Window.cpp
index 22e5501..adad21e 100644
--- a/src/Window.cpp
+++ b/src/Window.cpp
@@ -20,6 +20,7 @@ namespace dchat
overlay.add(stack);
add(overlay);
+ stack.set_homogeneous(false);
stack.set_transition_type(Gtk::StackTransitionType::STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
stack.set_transition_duration(250);
stack.add(loginWindow, "login");
@@ -27,6 +28,7 @@ namespace dchat
overlay.show();
windowNotification->show_all();
+ stack.set_visible_child("login");
stack.show();
//chatWindow.show_all();
//loginWindow.show();
@@ -45,7 +47,7 @@ namespace dchat
rooms->loginUser(username.raw(), password.raw());
//windowNotification->show(Glib::ustring("Successfully logged in as ") + username);
drawBackgroundConnection.disconnect();
- chatWindow.show_all();
+ chatWindow.show();
stack.set_visible_child(chatWindow);
chatWindow.scrollToBottom();
}
@@ -71,7 +73,7 @@ namespace dchat
rooms->registerUser(username.raw(), password.raw());
windowNotification->show(Glib::ustring("Successfully registered user ") + username);
drawBackgroundConnection.disconnect();
- chatWindow.show_all();
+ chatWindow.show();
stack.set_visible_child(chatWindow);
}
catch(std::exception &e)
@@ -109,11 +111,11 @@ namespace dchat
};
roomCallbackFuncs.createRoomCallbackFunc = [this](std::shared_ptr<Room> room)
{
- chatWindow.addRoom(room);
+ chatWindow.addRoom(room);
};
- roomCallbackFuncs.addUserCallbackFunc = [this](std::shared_ptr<Room> room, std::shared_ptr<User> user)
+ roomCallbackFuncs.addUserCallbackFunc = [this](const RoomAddUserRequest &request)
{
- chatWindow.addUser(room, user);
+ chatWindow.addUser(request);
};
roomCallbackFuncs.addMessageCallbackFunc = [this](const RoomAddMessageRequest &request)
{
@@ -127,6 +129,10 @@ namespace dchat
{
chatWindow.changeRoomName(request);
};
+ roomCallbackFuncs.receiveInviteUserCallbackFunc = [this](const InviteUserRequest &request)
+ {
+ chatWindow.addInviteRequest(request);
+ };
windowNotification->show("Connecting to 83.252.53.188:27130");
Rooms::connect("83.252.53.188", 27130, roomCallbackFuncs);