allow multiple components to display shared user menu

This commit is contained in:
ouwou 2020-11-08 00:44:26 -05:00
parent 0c442a423e
commit f124626a6d
12 changed files with 143 additions and 105 deletions

View File

@ -58,6 +58,22 @@ int Abaddon::StartGTK() {
m_main_window->show();
m_main_window->UpdateComponents();
// crashes for some stupid reason if i put it somewhere else
m_user_menu = Gtk::manage(new Gtk::Menu);
m_user_menu_insert_mention = Gtk::manage(new Gtk::MenuItem("Insert Mention"));
m_user_menu_ban = Gtk::manage(new Gtk::MenuItem("Ban"));
m_user_menu_kick = Gtk::manage(new Gtk::MenuItem("Kick"));
m_user_menu_copy_id = Gtk::manage(new Gtk::MenuItem("Copy ID"));
m_user_menu_insert_mention->signal_activate().connect(sigc::mem_fun(*this, &Abaddon::on_user_menu_insert_mention));
m_user_menu_ban->signal_activate().connect(sigc::mem_fun(*this, &Abaddon::on_user_menu_ban));
m_user_menu_kick->signal_activate().connect(sigc::mem_fun(*this, &Abaddon::on_user_menu_kick));
m_user_menu_copy_id->signal_activate().connect(sigc::mem_fun(*this, &Abaddon::on_user_menu_copy_id));
m_user_menu->append(*m_user_menu_insert_mention);
m_user_menu->append(*m_user_menu_ban);
m_user_menu->append(*m_user_menu_kick);
m_user_menu->append(*m_user_menu_copy_id);
m_user_menu->show_all();
m_main_window->signal_action_connect().connect(sigc::mem_fun(*this, &Abaddon::ActionConnect));
m_main_window->signal_action_disconnect().connect(sigc::mem_fun(*this, &Abaddon::ActionDisconnect));
m_main_window->signal_action_set_token().connect(sigc::mem_fun(*this, &Abaddon::ActionSetToken));
@ -65,6 +81,8 @@ int Abaddon::StartGTK() {
m_main_window->signal_action_join_guild().connect(sigc::mem_fun(*this, &Abaddon::ActionJoinGuildDialog));
m_main_window->signal_action_set_status().connect(sigc::mem_fun(*this, &Abaddon::ActionSetStatus));
m_main_window->signal_action_show_user_menu().connect(sigc::mem_fun(*this, &Abaddon::ShowUserMenu));
m_main_window->GetChannelList()->signal_action_channel_item_select().connect(sigc::mem_fun(*this, &Abaddon::ActionChannelOpened));
m_main_window->GetChannelList()->signal_action_guild_move_up().connect(sigc::mem_fun(*this, &Abaddon::ActionMoveGuildUp));
m_main_window->GetChannelList()->signal_action_guild_move_down().connect(sigc::mem_fun(*this, &Abaddon::ActionMoveGuildDown));
@ -77,10 +95,6 @@ int Abaddon::StartGTK() {
m_main_window->GetChatWindow()->signal_action_channel_click().connect(sigc::mem_fun(*this, &Abaddon::ActionChannelOpened));
m_main_window->GetChatWindow()->signal_action_insert_mention().connect(sigc::mem_fun(*this, &Abaddon::ActionInsertMention));
m_main_window->GetMemberList()->signal_action_insert_mention().connect(sigc::mem_fun(*this, &Abaddon::ActionInsertMention));
m_main_window->GetMemberList()->signal_action_kick().connect(sigc::mem_fun(*this, &Abaddon::ActionKickMember));
m_main_window->GetMemberList()->signal_action_ban().connect(sigc::mem_fun(*this, &Abaddon::ActionBanMember));
ActionReloadCSS();
m_gtk_app->signal_shutdown().connect([&]() {
@ -186,6 +200,37 @@ const SettingsManager &Abaddon::GetSettings() const {
return m_settings;
}
void Abaddon::ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_id) {
m_shown_user_menu_id = id;
m_shown_user_menu_guild_id = guild_id;
const auto me = m_discord.GetUserData().ID;
const bool has_kick = m_discord.HasGuildPermission(me, guild_id, Permission::KICK_MEMBERS);
const bool has_ban = m_discord.HasGuildPermission(me, guild_id, Permission::BAN_MEMBERS);
const bool can_manage = m_discord.CanManageMember(guild_id, me, id);
m_user_menu_kick->set_sensitive(has_kick && can_manage);
m_user_menu_ban->set_sensitive(has_ban && can_manage);
m_user_menu->popup_at_pointer(event);
}
void Abaddon::on_user_menu_insert_mention() {
ActionInsertMention(m_shown_user_menu_id);
}
void Abaddon::on_user_menu_ban() {
ActionBanMember(m_shown_user_menu_id, m_shown_user_menu_guild_id);
}
void Abaddon::on_user_menu_kick() {
ActionKickMember(m_shown_user_menu_id, m_shown_user_menu_guild_id);
}
void Abaddon::on_user_menu_copy_id() {
Gtk::Clipboard::get()->set_text(std::to_string(m_shown_user_menu_id));
}
void Abaddon::ActionConnect() {
if (!m_discord.IsStarted())
StartDiscord();

View File

@ -71,6 +71,22 @@ public:
const SettingsManager &GetSettings() const;
protected:
Snowflake m_shown_user_menu_id;
Snowflake m_shown_user_menu_guild_id;
void ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_id);
Gtk::Menu *m_user_menu;
Gtk::MenuItem *m_user_menu_insert_mention;
Gtk::MenuItem *m_user_menu_ban;
Gtk::MenuItem *m_user_menu_kick;
Gtk::MenuItem *m_user_menu_copy_id;
void on_user_menu_insert_mention();
void on_user_menu_ban();
void on_user_menu_kick();
void on_user_menu_copy_id();
private:
DiscordClient m_discord;
std::string m_discord_token;

View File

@ -757,9 +757,10 @@ ChatMessageHeader::ChatMessageHeader(const Message *data) {
m_main_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
m_content_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
m_meta_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
m_author_ev = Gtk::manage(new Gtk::EventBox);
m_meta_ev = Gtk::manage(new Gtk::EventBox);
m_author = Gtk::manage(new Gtk::Label);
m_timestamp = Gtk::manage(new Gtk::Label);
m_avatar_ev = Gtk::manage(new Gtk::EventBox);
auto buf = Abaddon::Get().GetImageManager().GetFromURLIfCached(data->Author.GetAvatarURL());
if (buf)
@ -781,7 +782,8 @@ ChatMessageHeader::ChatMessageHeader(const Message *data) {
m_author->set_ellipsize(Pango::ELLIPSIZE_END);
m_author->set_xalign(0.f);
m_author->set_can_focus(false);
m_author_ev->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageHeader::on_author_button_press));
m_meta_ev->signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageHeader::on_author_button_press));
if (data->WebhookID.has_value()) {
m_extra = Gtk::manage(new Gtk::Label);
@ -815,13 +817,15 @@ ChatMessageHeader::ChatMessageHeader(const Message *data) {
m_content_box->set_can_focus(false);
m_author_ev->add(*m_author);
m_meta_box->add(*m_author_ev);
m_meta_box->add(*m_author);
if (m_extra != nullptr)
m_meta_box->add(*m_extra);
m_meta_box->add(*m_timestamp);
m_content_box->add(*m_meta_box);
m_main_box->add(*m_avatar);
m_meta_ev->add(*m_meta_box);
m_content_box->add(*m_meta_ev);
m_avatar_ev->add(*m_avatar);
m_main_box->add(*m_avatar_ev);
m_main_box->add(*m_content_box);
add(*m_main_box);
@ -830,6 +834,8 @@ ChatMessageHeader::ChatMessageHeader(const Message *data) {
show_all();
UpdateNameColor();
AttachUserMenuHandler(*m_meta_ev);
AttachUserMenuHandler(*m_avatar_ev);
}
void ChatMessageHeader::UpdateNameColor() {
@ -849,9 +855,20 @@ void ChatMessageHeader::UpdateNameColor() {
m_author->set_markup(md);
}
void ChatMessageHeader::AttachUserMenuHandler(Gtk::Widget &widget) {
widget.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool {
if (ev->type == GDK_BUTTON_PRESS && ev->button == GDK_BUTTON_SECONDARY) {
m_signal_action_open_user_menu.emit(reinterpret_cast<GdkEvent *>(ev));
return true;
}
return false;
});
}
bool ChatMessageHeader::on_author_button_press(GdkEventButton *ev) {
if (ev->button == GDK_BUTTON_PRIMARY && (ev->state & GDK_SHIFT_MASK)) {
m_signal_action_insert_mention.emit(UserID);
m_signal_action_insert_mention.emit();
return true;
}
@ -862,6 +879,10 @@ ChatMessageHeader::type_signal_action_insert_mention ChatMessageHeader::signal_a
return m_signal_action_insert_mention;
}
ChatMessageHeader::type_signal_action_open_user_menu ChatMessageHeader::signal_action_open_user_menu() {
return m_signal_action_open_user_menu;
}
void ChatMessageHeader::AddContent(Gtk::Widget *widget, bool prepend) {
m_content_box->add(*widget);
if (prepend)

View File

@ -106,20 +106,27 @@ public:
void UpdateNameColor();
protected:
void AttachUserMenuHandler(Gtk::Widget &widget);
bool on_author_button_press(GdkEventButton *ev);
Gtk::Box *m_main_box;
Gtk::Box *m_content_box;
Gtk::Box *m_meta_box;
Gtk::EventBox *m_author_ev;
Gtk::EventBox *m_meta_ev;
Gtk::Label *m_author;
Gtk::Label *m_timestamp;
Gtk::Label *m_extra = nullptr;
Gtk::Image *m_avatar;
Gtk::EventBox *m_avatar_ev;
typedef sigc::signal<void> type_signal_action_insert_mention;
typedef sigc::signal<void, const GdkEvent *> type_signal_action_open_user_menu;
typedef sigc::signal<void, Snowflake> type_signal_action_insert_mention;
type_signal_action_insert_mention m_signal_action_insert_mention;
type_signal_action_open_user_menu m_signal_action_open_user_menu;
public:
type_signal_action_insert_mention signal_action_insert_mention();
type_signal_action_open_user_menu signal_action_open_user_menu();
};

View File

@ -177,14 +177,20 @@ void ChatWindow::ProcessNewMessage(Snowflake id, bool prepend) {
if (should_attach) {
header = last_row;
} else {
const auto guild_id = client.GetChannel(m_active_channel)->GuildID;
const auto user_id = data->Author.ID;
const auto *user = client.GetUser(user_id);
if (user == nullptr) return;
header = Gtk::manage(new ChatMessageHeader(data));
header->signal_action_insert_mention().connect([this](const Snowflake &id) {
m_signal_action_insert_mention.emit(id);
header->signal_action_insert_mention().connect([this, user_id]() {
m_signal_action_insert_mention.emit(user_id);
});
header->signal_action_open_user_menu().connect([this, user_id, guild_id](const GdkEvent *event) {
m_signal_action_open_user_menu.emit(event, user_id, guild_id);
});
m_num_rows++;
Abaddon::Get().GetImageManager().LoadFromURL(user->GetAvatarURL("png", "32"), [this, user_id](Glib::RefPtr<Gdk::Pixbuf> buf) {
Glib::signal_idle().connect([this, buf, user_id]() -> bool {
@ -341,3 +347,7 @@ ChatWindow::type_signal_action_channel_click ChatWindow::signal_action_channel_c
ChatWindow::type_signal_action_insert_mention ChatWindow::signal_action_insert_mention() {
return m_signal_action_insert_mention;
}
ChatWindow::type_signal_action_open_user_menu ChatWindow::signal_action_open_user_menu() {
return m_signal_action_open_user_menu;
}

View File

@ -71,6 +71,7 @@ public:
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;
typedef sigc::signal<void, const GdkEvent *, Snowflake, Snowflake> type_signal_action_open_user_menu;
type_signal_action_message_delete signal_action_message_delete();
type_signal_action_message_edit signal_action_message_edit();
@ -78,6 +79,7 @@ public:
type_signal_action_chat_load_history signal_action_chat_load_history();
type_signal_action_channel_click signal_action_channel_click();
type_signal_action_insert_mention signal_action_insert_mention();
type_signal_action_open_user_menu signal_action_open_user_menu();
private:
type_signal_action_message_delete m_signal_action_message_delete;
@ -86,4 +88,5 @@ private:
type_signal_action_chat_load_history m_signal_action_chat_load_history;
type_signal_action_channel_click m_signal_action_channel_click;
type_signal_action_insert_mention m_signal_action_insert_mention;
type_signal_action_open_user_menu m_signal_action_open_user_menu;
};

View File

@ -56,24 +56,6 @@ MemberList::MemberList() {
m_listbox->set_selection_mode(Gtk::SELECTION_NONE);
m_menu_copy_id = Gtk::manage(new Gtk::MenuItem("_Copy ID", true));
m_menu_copy_id->signal_activate().connect(sigc::mem_fun(*this, &MemberList::on_copy_id_activate));
m_menu.append(*m_menu_copy_id);
m_menu_insert_mention = Gtk::manage(new Gtk::MenuItem("Insert _Mention", true));
m_menu_insert_mention->signal_activate().connect(sigc::mem_fun(*this, &MemberList::on_insert_mention_activate));
m_menu.append(*m_menu_insert_mention);
m_menu_kick = Gtk::manage(new Gtk::MenuItem("_Kick User", true));
m_menu_kick->signal_activate().connect(sigc::mem_fun(*this, &MemberList::on_kick_activate));
m_menu.append(*m_menu_kick);
m_menu_ban = Gtk::manage(new Gtk::MenuItem("_Ban User", true));
m_menu_ban->signal_activate().connect(sigc::mem_fun(*this, &MemberList::on_ban_activate));
m_menu.append(*m_menu_ban);
m_menu.show_all();
m_main->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
m_main->add(*m_listbox);
m_main->show_all();
@ -229,43 +211,10 @@ void MemberList::UpdateMemberListInternal() {
}
}
void MemberList::on_copy_id_activate() {
auto *row = dynamic_cast<MemberListUserRow *>(m_row_menu_target);
if (row == nullptr) return;
Gtk::Clipboard::get()->set_text(std::to_string(row->ID));
}
void MemberList::on_insert_mention_activate() {
auto *row = dynamic_cast<MemberListUserRow *>(m_row_menu_target);
if (row == nullptr) return;
m_signal_action_insert_mention.emit(row->ID);
}
void MemberList::on_kick_activate() {
auto *row = dynamic_cast<MemberListUserRow *>(m_row_menu_target);
if (row == nullptr) return;
m_signal_action_kick.emit(row->ID, m_guild_id);
}
void MemberList::on_ban_activate() {
auto *row = dynamic_cast<MemberListUserRow *>(m_row_menu_target);
if (row == nullptr) return;
m_signal_action_ban.emit(row->ID, m_guild_id);
}
void MemberList::AttachUserMenuHandler(Gtk::ListBoxRow *row, Snowflake id) {
row->signal_button_press_event().connect([this, row, id](GdkEventButton *e) -> bool {
if (e->type == GDK_BUTTON_PRESS && e->button == GDK_BUTTON_SECONDARY) {
const auto &discord = Abaddon::Get().GetDiscordClient();
const auto me = discord.GetUserData().ID;
const bool has_kick = discord.HasGuildPermission(me, m_guild_id, Permission::KICK_MEMBERS);
const bool has_ban = discord.HasGuildPermission(me, m_guild_id, Permission::BAN_MEMBERS);
const bool can_manage = discord.CanManageMember(m_chan_id, discord.GetUserData().ID, id);
m_row_menu_target = row;
m_menu_kick->set_sensitive(has_kick && can_manage);
m_menu_ban->set_sensitive(has_ban && can_manage);
m_menu.popup_at_pointer(reinterpret_cast<const GdkEvent *>(e));
m_signal_action_show_user_menu.emit(reinterpret_cast<const GdkEvent *>(e), id, m_guild_id);
return true;
}
@ -273,14 +222,6 @@ void MemberList::AttachUserMenuHandler(Gtk::ListBoxRow *row, Snowflake id) {
});
}
MemberList::type_signal_action_insert_mention MemberList::signal_action_insert_mention() {
return m_signal_action_insert_mention;
}
MemberList::type_signal_action_kick MemberList::signal_action_kick() {
return m_signal_action_kick;
}
MemberList::type_signal_action_ban MemberList::signal_action_ban() {
return m_signal_action_ban;
MemberList::type_signal_action_show_user_menu MemberList::signal_action_show_user_menu() {
return m_signal_action_show_user_menu;
}

View File

@ -28,21 +28,9 @@ public:
void SetActiveChannel(Snowflake id);
private:
void on_copy_id_activate();
void on_insert_mention_activate();
void on_kick_activate();
void on_ban_activate();
void UpdateMemberListInternal();
void AttachUserMenuHandler(Gtk::ListBoxRow *row, Snowflake id);
Gtk::Menu m_menu;
Gtk::MenuItem *m_menu_copy_id;
Gtk::MenuItem *m_menu_insert_mention;
Gtk::MenuItem *m_menu_kick;
Gtk::MenuItem *m_menu_ban;
Gtk::ListBoxRow *m_row_menu_target = nullptr; // maybe hacky
std::mutex m_mutex;
Glib::Dispatcher m_update_member_list_dispatcher;
@ -55,16 +43,10 @@ private:
std::unordered_map<Snowflake, Gtk::ListBoxRow *> m_id_to_row;
public:
typedef sigc::signal<void, Snowflake> type_signal_action_insert_mention;
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_action_kick; // user_id, guild_id
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_action_ban; // ^
typedef sigc::signal<void, const GdkEvent *, Snowflake, Snowflake> type_signal_action_show_user_menu;
type_signal_action_insert_mention signal_action_insert_mention();
type_signal_action_kick signal_action_kick();
type_signal_action_ban signal_action_ban();
type_signal_action_show_user_menu signal_action_show_user_menu();
private:
type_signal_action_insert_mention m_signal_action_insert_mention;
type_signal_action_kick m_signal_action_kick;
type_signal_action_ban m_signal_action_ban;
type_signal_action_show_user_menu m_signal_action_show_user_menu;
};

View File

@ -330,13 +330,11 @@ Permission DiscordClient::ComputeOverwrites(Permission base, Snowflake member_id
return perms;
}
bool DiscordClient::CanManageMember(Snowflake channel_id, Snowflake actor, Snowflake target) const {
const auto *channel = GetChannel(channel_id);
if (channel == nullptr) return false;
const auto *guild = GetGuild(channel->GuildID);
bool DiscordClient::CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const {
const auto *guild = GetGuild(guild_id);
if (guild != nullptr && guild->OwnerID == target) return false;
const auto actor_highest_id = GetMemberHighestRole(channel->GuildID, actor);
const auto target_highest_id = GetMemberHighestRole(channel->GuildID, target);
const auto actor_highest_id = GetMemberHighestRole(guild_id, actor);
const auto target_highest_id = GetMemberHighestRole(guild_id, target);
const auto *actor_highest = GetRole(actor_highest_id);
const auto *target_highest = GetRole(target_highest_id);
if (actor_highest == nullptr) return false;

View File

@ -93,7 +93,7 @@ public:
bool HasChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const;
Permission ComputePermissions(Snowflake member_id, Snowflake guild_id) const;
Permission ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const;
bool CanManageMember(Snowflake channel_id, Snowflake actor, Snowflake target) const; // kick, ban, edit nickname (cant think of a better name)
bool CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const; // kick, ban, edit nickname (cant think of a better name)
void SendChatMessage(std::string content, Snowflake channel);
void DeleteMessage(Snowflake channel_id, Snowflake id);

View File

@ -68,6 +68,14 @@ MainWindow::MainWindow()
auto *member_list = m_members.GetRoot();
auto *chat = m_chat.GetRoot();
m_members.signal_action_show_user_menu().connect([this](const GdkEvent *event, Snowflake id, Snowflake guild_id) {
m_signal_action_show_user_menu.emit(event, id, guild_id);
});
m_chat.signal_action_open_user_menu().connect([this](const GdkEvent *event, Snowflake id, Snowflake guild_id) {
m_signal_action_show_user_menu.emit(event, id, guild_id);
});
chat->set_vexpand(true);
chat->set_hexpand(true);
@ -238,3 +246,7 @@ MainWindow::type_signal_action_join_guild MainWindow::signal_action_join_guild()
MainWindow::type_signal_action_set_status MainWindow::signal_action_set_status() {
return m_signal_action_set_status;
}
MainWindow::type_signal_action_show_user_menu MainWindow::signal_action_show_user_menu() {
return m_signal_action_show_user_menu;
}

View File

@ -38,6 +38,7 @@ public:
typedef sigc::signal<void> type_signal_action_reload_css;
typedef sigc::signal<void> type_signal_action_join_guild;
typedef sigc::signal<void> type_signal_action_set_status;
typedef sigc::signal<void, const GdkEvent *, Snowflake, Snowflake> type_signal_action_show_user_menu;
type_signal_action_connect signal_action_connect();
type_signal_action_disconnect signal_action_disconnect();
@ -45,6 +46,7 @@ public:
type_signal_action_reload_css signal_action_reload_css();
type_signal_action_join_guild signal_action_join_guild();
type_signal_action_set_status signal_action_set_status();
type_signal_action_show_user_menu signal_action_show_user_menu();
protected:
type_signal_action_connect m_signal_action_connect;
@ -53,6 +55,7 @@ protected:
type_signal_action_reload_css m_signal_action_reload_css;
type_signal_action_join_guild m_signal_action_join_guild;
type_signal_action_set_status m_signal_action_set_status;
type_signal_action_show_user_menu m_signal_action_show_user_menu;
protected:
Gtk::Box m_main_box;