mirror of
https://github.com/celisej567/abaddon.git
synced 2026-09-19 20:11:59 +03:00
add ability to create replies
This commit is contained in:
@@ -31,6 +31,11 @@ Glib::RefPtr<Gtk::TextBuffer> ChatInput::GetBuffer() {
|
||||
|
||||
// this isnt connected directly so that the chat window can handle stuff like the completer first
|
||||
bool ChatInput::ProcessKeyPress(GdkEventKey *event) {
|
||||
if (event->keyval == GDK_KEY_Escape) {
|
||||
m_signal_escape.emit();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event->keyval == GDK_KEY_Return) {
|
||||
if (event->state & GDK_SHIFT_MASK)
|
||||
return false;
|
||||
@@ -48,6 +53,14 @@ bool ChatInput::ProcessKeyPress(GdkEventKey *event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ChatInput::on_grab_focus() {
|
||||
m_textview.grab_focus();
|
||||
}
|
||||
|
||||
ChatInput::type_signal_submit ChatInput::signal_submit() {
|
||||
return m_signal_submit;
|
||||
}
|
||||
|
||||
ChatInput::type_signal_escape ChatInput::signal_escape() {
|
||||
return m_signal_escape;
|
||||
}
|
||||
|
||||
@@ -9,15 +9,20 @@ public:
|
||||
Glib::RefPtr<Gtk::TextBuffer> GetBuffer();
|
||||
bool ProcessKeyPress(GdkEventKey *event);
|
||||
|
||||
private:
|
||||
protected:
|
||||
void on_grab_focus() override;
|
||||
|
||||
private:
|
||||
Gtk::TextView m_textview;
|
||||
|
||||
public:
|
||||
typedef sigc::signal<void, Glib::ustring> type_signal_submit;
|
||||
typedef sigc::signal<void> type_signal_escape;
|
||||
|
||||
type_signal_submit signal_submit();
|
||||
type_signal_escape signal_escape();
|
||||
|
||||
private:
|
||||
type_signal_submit m_signal_submit;
|
||||
type_signal_escape m_signal_escape;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <filesystem>
|
||||
#include "typingindicator.hpp"
|
||||
#include "chatinputindicator.hpp"
|
||||
#include "../abaddon.hpp"
|
||||
#include "../util.hpp"
|
||||
|
||||
constexpr static const int MaxUsersInIndicator = 4;
|
||||
|
||||
TypingIndicator::TypingIndicator()
|
||||
ChatInputIndicator::ChatInputIndicator()
|
||||
: Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) {
|
||||
m_label.set_text("");
|
||||
m_label.set_ellipsize(Pango::ELLIPSIZE_END);
|
||||
@@ -13,8 +13,8 @@ TypingIndicator::TypingIndicator()
|
||||
m_img.set_margin_right(5);
|
||||
get_style_context()->add_class("typing-indicator");
|
||||
|
||||
Abaddon::Get().GetDiscordClient().signal_typing_start().connect(sigc::mem_fun(*this, &TypingIndicator::OnUserTypingStart));
|
||||
Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &TypingIndicator::OnMessageCreate));
|
||||
Abaddon::Get().GetDiscordClient().signal_typing_start().connect(sigc::mem_fun(*this, &ChatInputIndicator::OnUserTypingStart));
|
||||
Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &ChatInputIndicator::OnMessageCreate));
|
||||
|
||||
add(m_img);
|
||||
add(m_label);
|
||||
@@ -36,7 +36,7 @@ TypingIndicator::TypingIndicator()
|
||||
} catch (const std::exception &) {}
|
||||
}
|
||||
|
||||
void TypingIndicator::AddUser(Snowflake channel_id, const UserData &user, int timeout) {
|
||||
void ChatInputIndicator::AddUser(Snowflake channel_id, const UserData &user, int timeout) {
|
||||
auto current_connection_it = m_typers[channel_id].find(user.ID);
|
||||
if (current_connection_it != m_typers.at(channel_id).end()) {
|
||||
current_connection_it->second.disconnect();
|
||||
@@ -53,12 +53,22 @@ void TypingIndicator::AddUser(Snowflake channel_id, const UserData &user, int ti
|
||||
ComputeTypingString();
|
||||
}
|
||||
|
||||
void TypingIndicator::SetActiveChannel(Snowflake id) {
|
||||
void ChatInputIndicator::SetActiveChannel(Snowflake id) {
|
||||
m_active_channel = id;
|
||||
ComputeTypingString();
|
||||
}
|
||||
|
||||
void TypingIndicator::OnUserTypingStart(Snowflake user_id, Snowflake channel_id) {
|
||||
void ChatInputIndicator::SetCustomMarkup(const Glib::ustring &str) {
|
||||
m_custom_markup = str;
|
||||
ComputeTypingString();
|
||||
}
|
||||
|
||||
void ChatInputIndicator::ClearCustom() {
|
||||
m_custom_markup = "";
|
||||
ComputeTypingString();
|
||||
}
|
||||
|
||||
void ChatInputIndicator::OnUserTypingStart(Snowflake user_id, Snowflake channel_id) {
|
||||
const auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto user = discord.GetUser(user_id);
|
||||
if (!user.has_value()) return;
|
||||
@@ -66,14 +76,14 @@ void TypingIndicator::OnUserTypingStart(Snowflake user_id, Snowflake channel_id)
|
||||
AddUser(channel_id, *user, 10);
|
||||
}
|
||||
|
||||
void TypingIndicator::OnMessageCreate(Snowflake message_id) {
|
||||
void ChatInputIndicator::OnMessageCreate(Snowflake message_id) {
|
||||
const auto msg = Abaddon::Get().GetDiscordClient().GetMessage(message_id);
|
||||
if (!msg.has_value()) return;
|
||||
m_typers[msg->ChannelID].erase(msg->Author.ID);
|
||||
ComputeTypingString();
|
||||
}
|
||||
|
||||
void TypingIndicator::SetTypingString(const Glib::ustring &str) {
|
||||
void ChatInputIndicator::SetTypingString(const Glib::ustring &str) {
|
||||
m_label.set_text(str);
|
||||
if (str == "")
|
||||
m_img.hide();
|
||||
@@ -81,7 +91,13 @@ void TypingIndicator::SetTypingString(const Glib::ustring &str) {
|
||||
m_img.show();
|
||||
}
|
||||
|
||||
void TypingIndicator::ComputeTypingString() {
|
||||
void ChatInputIndicator::ComputeTypingString() {
|
||||
if (m_custom_markup != "") {
|
||||
m_label.set_markup(m_custom_markup);
|
||||
m_img.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
std::vector<UserData> typers;
|
||||
for (const auto &[id, conn] : m_typers[m_active_channel]) {
|
||||
@@ -4,10 +4,12 @@
|
||||
#include "../discord/snowflake.hpp"
|
||||
#include "../discord/user.hpp"
|
||||
|
||||
class TypingIndicator : public Gtk::Box {
|
||||
class ChatInputIndicator : public Gtk::Box {
|
||||
public:
|
||||
TypingIndicator();
|
||||
ChatInputIndicator();
|
||||
void SetActiveChannel(Snowflake id);
|
||||
void SetCustomMarkup(const Glib::ustring &str);
|
||||
void ClearCustom();
|
||||
|
||||
private:
|
||||
void AddUser(Snowflake channel_id, const UserData &user, int timeout);
|
||||
@@ -19,6 +21,8 @@ private:
|
||||
Gtk::Image m_img;
|
||||
Gtk::Label m_label;
|
||||
|
||||
Glib::ustring m_custom_markup;
|
||||
|
||||
Snowflake m_active_channel;
|
||||
std::unordered_map<Snowflake, std::unordered_map<Snowflake, sigc::connection>> m_typers; // channel id -> [user id -> connection]
|
||||
};
|
||||
@@ -29,6 +29,10 @@ ChatMessageItemContainer::ChatMessageItemContainer() {
|
||||
m_menu_copy_content->signal_activate().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::on_menu_copy_content));
|
||||
m_menu.append(*m_menu_copy_content);
|
||||
|
||||
m_menu_reply_to = Gtk::manage(new Gtk::MenuItem("Reply To"));
|
||||
m_menu_reply_to->signal_activate().connect(sigc::mem_fun(*this, &ChatMessageItemContainer::on_menu_reply_to));
|
||||
m_menu.append(*m_menu_reply_to);
|
||||
|
||||
m_menu.show_all();
|
||||
|
||||
m_link_menu_copy = Gtk::manage(new Gtk::MenuItem("Copy Link"));
|
||||
@@ -919,6 +923,10 @@ void ChatMessageItemContainer::on_menu_copy_content() {
|
||||
Gtk::Clipboard::get()->set_text(msg->Content);
|
||||
}
|
||||
|
||||
void ChatMessageItemContainer::on_menu_reply_to() {
|
||||
m_signal_action_reply_to.emit(ID);
|
||||
}
|
||||
|
||||
ChatMessageItemContainer::type_signal_action_delete ChatMessageItemContainer::signal_action_delete() {
|
||||
return m_signal_action_delete;
|
||||
}
|
||||
@@ -939,6 +947,10 @@ ChatMessageItemContainer::type_signal_action_reaction_remove ChatMessageItemCont
|
||||
return m_signal_action_reaction_remove;
|
||||
}
|
||||
|
||||
ChatMessageItemContainer::type_signal_action_reply_to ChatMessageItemContainer::signal_action_reply_to() {
|
||||
return m_signal_action_reply_to;
|
||||
}
|
||||
|
||||
void ChatMessageItemContainer::AttachEventHandlers(Gtk::Widget &widget) {
|
||||
const auto on_button_press_event = [this](GdkEventButton *event) -> bool {
|
||||
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) {
|
||||
|
||||
@@ -60,11 +60,13 @@ protected:
|
||||
Gtk::MenuItem *m_menu_copy_content;
|
||||
Gtk::MenuItem *m_menu_delete_message;
|
||||
Gtk::MenuItem *m_menu_edit_message;
|
||||
Gtk::MenuItem *m_menu_reply_to;
|
||||
|
||||
void on_menu_copy_id();
|
||||
void on_menu_delete_message();
|
||||
void on_menu_edit_message();
|
||||
void on_menu_copy_content();
|
||||
void on_menu_reply_to();
|
||||
|
||||
Gtk::EventBox *m_ev;
|
||||
Gtk::Box *m_main;
|
||||
@@ -80,6 +82,7 @@ public:
|
||||
typedef sigc::signal<void, Snowflake> type_signal_channel_click;
|
||||
typedef sigc::signal<void, Glib::ustring> type_signal_action_reaction_add;
|
||||
typedef sigc::signal<void, Glib::ustring> type_signal_action_reaction_remove;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_action_reply_to;
|
||||
typedef sigc::signal<void> type_signal_enter;
|
||||
typedef sigc::signal<void> type_signal_leave;
|
||||
|
||||
@@ -88,12 +91,15 @@ public:
|
||||
type_signal_channel_click signal_action_channel_click();
|
||||
type_signal_action_reaction_add signal_action_reaction_add();
|
||||
type_signal_action_reaction_remove signal_action_reaction_remove();
|
||||
type_signal_action_reply_to signal_action_reply_to();
|
||||
|
||||
private:
|
||||
type_signal_action_delete m_signal_action_delete;
|
||||
type_signal_action_edit m_signal_action_edit;
|
||||
type_signal_channel_click m_signal_action_channel_click;
|
||||
type_signal_action_reaction_add m_signal_action_reaction_add;
|
||||
type_signal_action_reaction_remove m_signal_action_reaction_remove;
|
||||
type_signal_action_reply_to m_signal_action_reply_to;
|
||||
};
|
||||
|
||||
class ChatMessageHeader : public Gtk::ListBoxRow {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "chatwindow.hpp"
|
||||
#include "chatmessage.hpp"
|
||||
#include "../abaddon.hpp"
|
||||
#include "typingindicator.hpp"
|
||||
#include "chatinputindicator.hpp"
|
||||
#include "chatinput.hpp"
|
||||
|
||||
ChatWindow::ChatWindow() {
|
||||
@@ -9,10 +9,10 @@ ChatWindow::ChatWindow() {
|
||||
m_list = Gtk::manage(new Gtk::ListBox);
|
||||
m_scroll = Gtk::manage(new Gtk::ScrolledWindow);
|
||||
m_input = Gtk::manage(new ChatInput);
|
||||
m_typing_indicator = Gtk::manage(new TypingIndicator);
|
||||
m_input_indicator = Gtk::manage(new ChatInputIndicator);
|
||||
|
||||
m_typing_indicator->set_valign(Gtk::ALIGN_END);
|
||||
m_typing_indicator->show();
|
||||
m_input_indicator->set_valign(Gtk::ALIGN_END);
|
||||
m_input_indicator->show();
|
||||
|
||||
m_main->get_style_context()->add_class("messages");
|
||||
m_list->get_style_context()->add_class("messages");
|
||||
@@ -43,9 +43,10 @@ ChatWindow::ChatWindow() {
|
||||
m_list->set_focus_vadjustment(m_scroll->get_vadjustment());
|
||||
m_list->show();
|
||||
|
||||
m_input->signal_submit().connect([this](const Glib::ustring &text) {
|
||||
if (m_active_channel.IsValid())
|
||||
m_signal_action_chat_submit.emit(text, m_active_channel);
|
||||
m_input->signal_submit().connect(sigc::mem_fun(*this, &ChatWindow::OnInputSubmit));
|
||||
m_input->signal_escape().connect([this]() {
|
||||
if (m_is_replying)
|
||||
StopReplying();
|
||||
});
|
||||
m_input->signal_key_press_event().connect(sigc::mem_fun(*this, &ChatWindow::OnKeyPressEvent), false);
|
||||
m_input->show();
|
||||
@@ -87,7 +88,7 @@ ChatWindow::ChatWindow() {
|
||||
m_main->add(*m_scroll);
|
||||
m_main->add(m_completer);
|
||||
m_main->add(*m_input);
|
||||
m_main->add(*m_typing_indicator);
|
||||
m_main->add(*m_input_indicator);
|
||||
m_main->show();
|
||||
}
|
||||
|
||||
@@ -118,7 +119,9 @@ void ChatWindow::SetMessages(const std::set<Snowflake> &msgs) {
|
||||
|
||||
void ChatWindow::SetActiveChannel(Snowflake id) {
|
||||
m_active_channel = id;
|
||||
m_typing_indicator->SetActiveChannel(id);
|
||||
m_input_indicator->SetActiveChannel(id);
|
||||
if (m_is_replying)
|
||||
StopReplying();
|
||||
}
|
||||
|
||||
void ChatWindow::AddNewMessage(Snowflake id) {
|
||||
@@ -178,6 +181,13 @@ Snowflake ChatWindow::GetActiveChannel() const {
|
||||
return m_active_channel;
|
||||
}
|
||||
|
||||
void ChatWindow::OnInputSubmit(const Glib::ustring &text) {
|
||||
if (m_active_channel.IsValid())
|
||||
m_signal_action_chat_submit.emit(text, m_active_channel, m_replying_to); // m_replying_to is checked for invalid in the handler
|
||||
if (m_is_replying)
|
||||
StopReplying();
|
||||
}
|
||||
|
||||
bool ChatWindow::OnKeyPressEvent(GdkEventKey *e) {
|
||||
if (m_completer.ProcessKeyPress(e))
|
||||
return true;
|
||||
@@ -253,6 +263,7 @@ void ChatWindow::ProcessNewMessage(Snowflake id, bool prepend) {
|
||||
content->signal_action_channel_click().connect([this](const Snowflake &id) {
|
||||
m_signal_action_channel_click.emit(id);
|
||||
});
|
||||
content->signal_action_reply_to().connect(sigc::mem_fun(*this, &ChatWindow::StartReplying));
|
||||
}
|
||||
|
||||
header->set_margin_left(5);
|
||||
@@ -266,6 +277,27 @@ void ChatWindow::ProcessNewMessage(Snowflake id, bool prepend) {
|
||||
}
|
||||
}
|
||||
|
||||
void ChatWindow::StartReplying(Snowflake message_id) {
|
||||
const auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto message = *discord.GetMessage(message_id);
|
||||
const auto author = discord.GetUser(message.Author.ID);
|
||||
m_replying_to = message_id;
|
||||
m_is_replying = true;
|
||||
m_input->grab_focus();
|
||||
m_input->get_style_context()->add_class("replying");
|
||||
if (author.has_value())
|
||||
m_input_indicator->SetCustomMarkup("Replying to " + author->GetEscapedBoldString<false>());
|
||||
else
|
||||
m_input_indicator->SetCustomMarkup("Replying...");
|
||||
}
|
||||
|
||||
void ChatWindow::StopReplying() {
|
||||
m_is_replying = false;
|
||||
m_replying_to = Snowflake::Invalid;
|
||||
m_input->get_style_context()->remove_class("replying");
|
||||
m_input_indicator->ClearCustom();
|
||||
}
|
||||
|
||||
void ChatWindow::OnScrollEdgeOvershot(Gtk::PositionType pos) {
|
||||
if (pos == Gtk::POS_TOP)
|
||||
m_signal_action_chat_load_history.emit(m_active_channel);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "completer.hpp"
|
||||
|
||||
class ChatInput;
|
||||
class TypingIndicator;
|
||||
class ChatInputIndicator;
|
||||
class ChatWindow {
|
||||
public:
|
||||
ChatWindow();
|
||||
@@ -30,11 +30,19 @@ protected:
|
||||
ChatMessageItemContainer *CreateMessageComponent(Snowflake id); // to be inserted into header's content box
|
||||
void ProcessNewMessage(Snowflake id, bool prepend); // creates and adds components
|
||||
|
||||
bool m_is_replying = false;
|
||||
Snowflake m_replying_to;
|
||||
|
||||
void StartReplying(Snowflake message_id);
|
||||
void StopReplying();
|
||||
|
||||
int m_num_rows = 0;
|
||||
std::unordered_map<Snowflake, Gtk::Widget *> m_id_to_widget;
|
||||
|
||||
Snowflake m_active_channel;
|
||||
|
||||
void OnInputSubmit(const Glib::ustring &text);
|
||||
|
||||
bool OnKeyPressEvent(GdkEventKey *e);
|
||||
void OnScrollEdgeOvershot(Gtk::PositionType pos);
|
||||
|
||||
@@ -48,12 +56,12 @@ protected:
|
||||
ChatInput *m_input;
|
||||
|
||||
Completer m_completer;
|
||||
TypingIndicator *m_typing_indicator;
|
||||
ChatInputIndicator *m_input_indicator;
|
||||
|
||||
public:
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_action_message_delete;
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_action_message_edit;
|
||||
typedef sigc::signal<void, std::string, Snowflake> type_signal_action_chat_submit;
|
||||
typedef sigc::signal<void, std::string, Snowflake, Snowflake> type_signal_action_chat_submit;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_action_chat_load_history;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_action_channel_click;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_action_insert_mention;
|
||||
|
||||
Reference in New Issue
Block a user