From 5fab9a3a2cf048330f687dda48c76c95a3a67d98 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 9 Nov 2018 09:46:43 +0100 Subject: Add room joining --- src/ChatWindow.cpp | 306 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 231 insertions(+), 75 deletions(-) (limited to 'src/ChatWindow.cpp') 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 #include #include #include +#include #include 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) @@ -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, std::shared_ptr 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(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()) -- cgit v1.2.3