aboutsummaryrefslogtreecommitdiff
path: root/src/ChatWindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ChatWindow.cpp')
-rw-r--r--src/ChatWindow.cpp306
1 files changed, 231 insertions, 75 deletions
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())