diff options
-rw-r--r-- | css/style.css | 29 | ||||
m--------- | depends/dchat_core | 0 | ||||
-rw-r--r-- | images/add_button.png | bin | 0 -> 1568 bytes | |||
-rw-r--r-- | images/add_button_small.png | bin | 0 -> 1122 bytes | |||
-rw-r--r-- | include/ChatWindow.hpp | 15 | ||||
-rw-r--r-- | include/ImageButton.hpp | 12 | ||||
-rw-r--r-- | include/InputDialog.hpp | 16 | ||||
-rw-r--r-- | src/ChatWindow.cpp | 156 | ||||
-rw-r--r-- | src/ImageButton.cpp | 14 | ||||
-rw-r--r-- | src/InputDialog.cpp | 32 | ||||
-rw-r--r-- | src/Window.cpp | 13 | ||||
-rw-r--r-- | src/main.cpp | 1 |
12 files changed, 232 insertions, 56 deletions
diff --git a/css/style.css b/css/style.css index 65cc83d..7b82820 100644 --- a/css/style.css +++ b/css/style.css @@ -6,13 +6,17 @@ window { background-color: #2f3136; } +dialog { + background-color: #2f3136; +} + entry { caret-color: #f7f7f7; background-color: #444444; border-color: #444444; border-radius: 10px; color: #f7f7f7; - box-shadow: 0px 0px 0px 0px; + box-shadow: none; } label { @@ -39,12 +43,12 @@ scrollbar { button { background-image: none; - background-color: #2f3136; + background-color: #36393e; color: #f7f7f7; border-style: none; outline-style: none; - box-shadow: 0px 0px 0px transparent; - text-shadow: 0px 0px 0px transparent; + box-shadow: none; + text-shadow: none; } .confirm-button { @@ -96,7 +100,7 @@ separator { padding: 10px 10px 10px 10px; } -#current-channel-title { +#current-room-title { color: #f7f7f7; font-size: 20px; font-weight: bold; @@ -123,11 +127,11 @@ separator { font-size: 16px; } -.channel-button { - background-color: transparent; +.room-button { + background: none; } -#users-title { +.users-title { color: #f7f7f7; font-weight: bold; font-size: 16px; @@ -175,6 +179,15 @@ textview text { padding: 2em; } +.image-button { + background-image: none; + background: none; + border-style: none; + outline-style: none; + box-shadow: none; + text-shadow: none; +} + /* #chat-input { border-style: none; diff --git a/depends/dchat_core b/depends/dchat_core -Subproject 391e0cb6a667fa384d61a9ef7909482d2e1bbb5 +Subproject c5b916157ae111da514481899d53f3e2bf5be98 diff --git a/images/add_button.png b/images/add_button.png Binary files differnew file mode 100644 index 0000000..2efa8a0 --- /dev/null +++ b/images/add_button.png diff --git a/images/add_button_small.png b/images/add_button_small.png Binary files differnew file mode 100644 index 0000000..d6c0a6c --- /dev/null +++ b/images/add_button_small.png diff --git a/include/ChatWindow.hpp b/include/ChatWindow.hpp index 988b225..7a61d91 100644 --- a/include/ChatWindow.hpp +++ b/include/ChatWindow.hpp @@ -1,5 +1,6 @@ #pragma once +#include "ImageButton.hpp" #include <dchat/Room.hpp> #include <gtkmm/label.h> #include <gtkmm/togglebutton.h> @@ -20,35 +21,43 @@ namespace dchat { public: ChatWindow(); + ~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 setUserNickname(const UserChangeNicknameRequest &request); + void changeRoomName(const RoomChangeNameRequest &request); void scrollToBottom(); private: 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::Grid leftPanelUsers; + Gtk::Stack leftPanelUsersStack; + ImageButton addRoomButton; Gtk::Label currentChannelTitle; Gtk::ScrolledWindow messageArea; - Gtk::Grid messageAreaLayout; + Gtk::Stack messageAreaStack; Gtk::TextView chatInput; struct RoomData { + Gtk::Grid *leftPanelUsersLayout; + Gtk::Grid *messageAreaLayout; Gtk::ToggleButton *button; }; - odhtdb::MapHash<RoomData> roomDataById; + odhtdb::MapHash<RoomData*> roomDataById; odhtdb::MapHash<ChatMessage*> messageById; int chatPrevNumLines; int roomCount; + RoomData *currentRoomData; std::shared_ptr<Room> currentRoom; }; }
\ No newline at end of file diff --git a/include/ImageButton.hpp b/include/ImageButton.hpp new file mode 100644 index 0000000..16b0840 --- /dev/null +++ b/include/ImageButton.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include <gtkmm/button.h> + +namespace dchat +{ + class ImageButton : public Gtk::Button + { + public: + ImageButton(const char *filepath, const char *text); + }; +}
\ No newline at end of file diff --git a/include/InputDialog.hpp b/include/InputDialog.hpp new file mode 100644 index 0000000..4b2b02a --- /dev/null +++ b/include/InputDialog.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include <gtkmm/dialog.h> +#include <gtkmm/entry.h> + +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; + + Gtk::Entry entry; + }; +}
\ No newline at end of file diff --git a/src/ChatWindow.cpp b/src/ChatWindow.cpp index c82e7fa..273f0e0 100644 --- a/src/ChatWindow.cpp +++ b/src/ChatWindow.cpp @@ -1,5 +1,6 @@ #include "../include/ChatWindow.hpp" #include "../include/ChatMessage.hpp" +#include "../include/InputDialog.hpp" #include <gtkmm/alignment.h> #include <gtkmm/viewport.h> #include <gtkmm/scrollbar.h> @@ -12,8 +13,12 @@ namespace dchat const int MERGE_MESSAGE_TIMESTAMP_DIFF_SEC = 60; ChatWindow::ChatWindow() : - roomCount(0) + addRoomButton("images/add_button_small.png", " Add room"), + roomCount(0), + currentRoomData(nullptr) { + leftPanelUsersStack.set_homogeneous(false); + messageAreaStack.set_homogeneous(false); setupTopBar(); Gtk::Paned *sidePanels = Gtk::manage(new Gtk::Paned()); @@ -24,6 +29,7 @@ namespace dchat setupLeftPanel(sidePanels); Gtk::Grid *rightPanel = Gtk::manage(new Gtk::Grid()); + rightPanel->set_vexpand(true); rightPanel->set_hexpand(true); sidePanels->add2(*rightPanel); @@ -34,6 +40,17 @@ namespace dchat set_hexpand(true); } + ChatWindow::~ChatWindow() + { + for(auto &it : roomDataById) + { + delete it.second->leftPanelUsersLayout; + delete it.second->messageAreaLayout; + delete it.second->button; + delete it.second; + } + } + void ChatWindow::setupTopBar() { topbar.set_name("top-bar"); @@ -49,18 +66,21 @@ namespace dchat topbarSpacer->set_size_request(50); topbar.attach_next_to(*topbarSpacer, topbarSearchBar, Gtk::POS_RIGHT, 1, 1); - currentChannelTitle.set_text("Linux"); - currentChannelTitle.set_name("current-channel-title"); + currentChannelTitle.set_name("current-room-title"); topbar.attach_next_to(currentChannelTitle, *topbarSpacer, Gtk::POS_RIGHT, 1, 1); } void ChatWindow::setupLeftPanel(Gtk::Paned *sidePanels) { + Gtk::Grid *leftPanelLayout = Gtk::manage(new Gtk::Grid()); + leftPanelLayout->set_vexpand(true); + leftPanelLayout->set_size_request(200); + leftPanelLayout->set_name("left-panel"); + sidePanels->add1(*leftPanelLayout); + Gtk::Paned *leftPanel = Gtk::manage(new Gtk::Paned(Gtk::ORIENTATION_VERTICAL)); - leftPanel->set_size_request(200); - leftPanel->set_name("left-panel"); leftPanel->set_vexpand(true); - sidePanels->add1(*leftPanel); + leftPanelLayout->attach(*leftPanel, 0, 0, 1, 2); leftPanelChannels.set_vexpand(true); leftPanel->add1(leftPanelChannels); @@ -72,14 +92,26 @@ namespace dchat leftPanelChannels.attach(*channelsTitle, 0, 0, 1, 1); //// - leftPanelUsers.set_vexpand(true); - leftPanel->add2(leftPanelUsers); + leftPanelUsersStack.set_vexpand(true); + leftPanel->add2(leftPanelUsersStack); - Gtk::Label *usersTitle = Gtk::manage(new Gtk::Label()); - usersTitle->set_name("users-title"); - usersTitle->set_text("Users"); - usersTitle->set_halign(Gtk::ALIGN_START); - leftPanelUsers.attach(*usersTitle, 0, 0, 1, 1); + addRoomButton.set_halign(Gtk::ALIGN_START); + leftPanelLayout->attach_next_to(addRoomButton, *leftPanel, Gtk::POS_BOTTOM, 1, 1); + addRoomButton.signal_clicked().connect([this]() + { + InputDialog createRoomDialog("Create a new room", "Room name"); + switch(createRoomDialog.run()) + { + case Gtk::RESPONSE_ACCEPT: + { + // TODO: Do not allow if room name size is == 0 or > 32 + currentRoom->rooms->createRoom(createRoomDialog.getInput()); + break; + } + default: + break; + } + }); } void ChatWindow::setupMessageArea(Gtk::Grid *rightPanel) @@ -89,8 +121,8 @@ namespace dchat messageArea.set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC); rightPanel->attach(messageArea, 0, 0, 1, 2); - messageAreaLayout.set_name("chat-area-layout"); - messageArea.add(messageAreaLayout); + messageAreaStack.set_name("chat-area-layout"); + messageArea.add(messageAreaStack); } void ChatWindow::setupChatInput(Gtk::Grid *rightPanel) @@ -159,17 +191,57 @@ namespace dchat void ChatWindow::addRoom(std::shared_ptr<Room> room) { + std::string roomIdStr = room->id->toString(); + Gtk::Grid *leftPanelUsersLayout = new Gtk::Grid(); + leftPanelUsersLayout->set_vexpand(true); + 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); + fprintf(stderr, "Added channel %s\n", room->id->toString().c_str()); - Gtk::ToggleButton *channelButton = Gtk::manage(new Gtk::ToggleButton("Channel name")); - channelButton->set_active(true); - channelButton->get_style_context()->add_class("channel-button"); - channelButton->set_hexpand(true); - channelButton->get_child()->set_halign(Gtk::ALIGN_START); - channelButton->show(); - leftPanelChannels.attach(*channelButton, 0, 1 + roomCount, 1, 1); + Gtk::ToggleButton *roomButton = new Gtk::ToggleButton("Room name"); + roomButton->set_active(true); + roomButton->get_style_context()->add_class("room-button"); + roomButton->set_hexpand(true); + roomButton->set_halign(Gtk::ALIGN_START); + roomButton->get_child()->set_halign(Gtk::ALIGN_START); + roomButton->show(); + roomButton->signal_clicked().connect([this, room] + { + setCurrentRoom(room); + }); + leftPanelChannels.attach(*roomButton, 0, 1 + roomCount, 1, 1); ++roomCount; - roomDataById[*room->id] = { channelButton }; + currentRoomData = new RoomData { leftPanelUsersLayout, messageAreaLayout, roomButton }; + roomDataById[*room->id] = currentRoomData; + currentRoom = room; + + leftPanelUsersStack.set_visible_child(roomIdStr); + messageAreaStack.set_visible_child(roomIdStr); + } + + void ChatWindow::setCurrentRoom(std::shared_ptr<Room> room) + { + std::string roomIdStr = room->id->toString(); + leftPanelUsersStack.set_visible_child(roomIdStr); + messageAreaStack.set_visible_child(roomIdStr); + currentChannelTitle.set_text(room->name); currentRoom = room; + currentRoomData = roomDataById[*room->id]; + + // TODO: Instead of scrolling to bottom, remember scroll position (even after restarting application). + // We want to show oldest unread message first + scrollToBottom(); } void ChatWindow::addMessage(const RoomAddMessageRequest &request) @@ -190,42 +262,41 @@ namespace dchat messageById[request.message.id] = message; if(!request.loadedFromCache && *request.room->id == *currentRoom->id) { - auto adj = messageArea.get_vadjustment(); - adj->set_value(adj->get_upper()); - messageAreaLayout.queue_draw(); - while(gtk_events_pending()) - gtk_main_iteration_do(FALSE); + currentRoomData->messageAreaLayout->queue_draw(); + scrollToBottom(); } return; } } - ChatMessage *message = Gtk::manage(new ChatMessage(request.message.creator->nickname, request.message.text, request.message.timestampSeconds)); + std::string userNickname = request.message.creator->nickname; + if(userNickname.empty()) + userNickname = "Anonymous"; + ChatMessage *message = Gtk::manage(new ChatMessage(userNickname, request.message.text, request.message.timestampSeconds)); message->set_valign(Gtk::Align::ALIGN_START); message->set_hexpand(true); message->show_all(); messageById[request.message.id] = message; - messageAreaLayout.attach(*message, 0, roomMessages.size(), 1, 1); + RoomData *roomData = roomDataById[*request.room->id]; + roomData->messageAreaLayout->attach(*message, 0, roomMessages.size(), 1, 1); // TODO: When we get a message in the current room we scroll to the bottom, but this should only be done if we are not manually scrolling to view old messages if(!request.loadedFromCache && *request.room->id == *currentRoom->id) { - auto adj = messageArea.get_vadjustment(); - adj->set_value(adj->get_upper()); - messageAreaLayout.queue_draw(); - while(gtk_events_pending()) - gtk_main_iteration_do(FALSE); + roomData->messageAreaLayout->queue_draw(); + scrollToBottom(); } } void ChatWindow::addUser(std::shared_ptr<Room> room, std::shared_ptr<User> user) { - Gtk::Label *username = Gtk::manage(new Gtk::Label("NoName")); + 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; - leftPanelUsers.attach(*username, 0, room->userByPublicKey.size(), 1, 1); + 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()); } @@ -236,8 +307,19 @@ namespace dchat fprintf(stderr, "Set nickname for user %s to %s\n", request.user->publicKey.toString().c_str(), request.newNickname.c_str()); } + void ChatWindow::changeRoomName(const RoomChangeNameRequest &request) + { + 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); + fprintf(stderr, "Changed room %s name to %s\n", request.room->id->toString().c_str(), request.newName.c_str()); + } + void ChatWindow::scrollToBottom() { + while(gtk_events_pending()) + gtk_main_iteration_do(FALSE); auto adj = messageArea.get_vadjustment(); adj->set_value(adj->get_upper()); } diff --git a/src/ImageButton.cpp b/src/ImageButton.cpp new file mode 100644 index 0000000..17939f7 --- /dev/null +++ b/src/ImageButton.cpp @@ -0,0 +1,14 @@ +#include "../include/ImageButton.hpp" +#include <gtkmm/image.h> + +namespace dchat +{ + ImageButton::ImageButton(const char *filepath, const char *text) : + Gtk::Button(text) + { + Gtk::Image *image = Gtk::manage(new Gtk::Image(filepath)); + set_always_show_image(true); + set_image(*image); + get_style_context()->add_class("image-button"); + } +}
\ No newline at end of file diff --git a/src/InputDialog.cpp b/src/InputDialog.cpp new file mode 100644 index 0000000..520bfa2 --- /dev/null +++ b/src/InputDialog.cpp @@ -0,0 +1,32 @@ +#include "../include/InputDialog.hpp" +#include <gtkmm/label.h> + +namespace dchat +{ + InputDialog::InputDialog(const char *title, const char *text, 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); + + entry.set_valign(Gtk::ALIGN_CENTER); + entry.set_halign(Gtk::ALIGN_CENTER); + entry.set_hexpand(true); + box->pack_end(entry, true, true); + + add_button(acceptText, Gtk::RESPONSE_ACCEPT); + add_button(cancelText, Gtk::RESPONSE_CANCEL); + show_all(); + + set_size_request(300, 150); + } + + Glib::ustring InputDialog::getInput() const + { + return entry.get_text(); + } +}
\ No newline at end of file diff --git a/src/Window.cpp b/src/Window.cpp index b9e1490..e8129f0 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -5,7 +5,6 @@ #include <sibs/SafeDeserializer.hpp> #include <math.h> #include <chrono> -#include <gtkmm.h> namespace dchat { @@ -48,13 +47,7 @@ namespace dchat drawBackgroundConnection.disconnect(); chatWindow.show_all(); stack.set_visible_child(chatWindow); - - Glib::signal_timeout().connect([this] - { - printf("scroll to bottom!\n"); - chatWindow.scrollToBottom(); - return false; - }, 100); + chatWindow.scrollToBottom(); } catch(std::exception &e) { @@ -130,6 +123,10 @@ namespace dchat { chatWindow.setUserNickname(request); }; + roomCallbackFuncs.changeRoomNameCallbackFunc = [this](const RoomChangeNameRequest &request) + { + chatWindow.changeRoomName(request); + }; windowNotification->show("Connecting to 83.252.53.188:27130"); Rooms::connect("83.252.53.188", 27130, roomCallbackFuncs); diff --git a/src/main.cpp b/src/main.cpp index 6c7dec6..71e54d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include "../include/Window.hpp" #include <gtkmm/application.h> #include <gtkmm/cssprovider.h> +#include <gtkmm/settings.h> int main (int argc, char *argv[]) { |