diff options
Diffstat (limited to 'src/components/channellist/classic')
6 files changed, 472 insertions, 0 deletions
diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp new file mode 100644 index 0000000..d756c6f --- /dev/null +++ b/src/components/channellist/classic/guildlist.cpp @@ -0,0 +1,177 @@ +#include "guildlist.hpp" + +#include "abaddon.hpp" +#include "guildlistfolderitem.hpp" + +class GuildListDMsButton : public Gtk::EventBox { +public: + GuildListDMsButton() { + set_size_request(48, 48); + + m_img.property_icon_name() = "user-available-symbolic"; // meh + m_img.property_icon_size() = Gtk::ICON_SIZE_DND; + add(m_img); + show_all_children(); + } + +private: + Gtk::Image m_img; +}; + +GuildList::GuildList() + : m_menu_guild_copy_id("_Copy ID", true) + , m_menu_guild_settings("View _Settings", true) + , m_menu_guild_leave("_Leave", true) + , m_menu_guild_mark_as_read("Mark as _Read", true) { + get_style_context()->add_class("classic-guild-list"); + set_selection_mode(Gtk::SELECTION_NONE); + show_all_children(); + + m_menu_guild_copy_id.signal_activate().connect([this] { + Gtk::Clipboard::get()->set_text(std::to_string(m_menu_guild_target)); + }); + m_menu_guild_settings.signal_activate().connect([this] { + m_signal_action_guild_settings.emit(m_menu_guild_target); + }); + m_menu_guild_leave.signal_activate().connect([this] { + m_signal_action_guild_leave.emit(m_menu_guild_target); + }); + m_menu_guild_mark_as_read.signal_activate().connect([this] { + Abaddon::Get().GetDiscordClient().MarkGuildAsRead(m_menu_guild_target, [](...) {}); + }); + m_menu_guild_toggle_mute.signal_activate().connect([this] { + const auto id = m_menu_guild_target; + auto &discord = Abaddon::Get().GetDiscordClient(); + if (discord.IsGuildMuted(id)) + discord.UnmuteGuild(id, NOOP_CALLBACK); + else + discord.MuteGuild(id, NOOP_CALLBACK); + }); + m_menu_guild.append(m_menu_guild_mark_as_read); + m_menu_guild.append(m_menu_guild_settings); + m_menu_guild.append(m_menu_guild_leave); + m_menu_guild.append(m_menu_guild_toggle_mute); + m_menu_guild.append(m_menu_guild_copy_id); + m_menu_guild.show_all(); +} + +void GuildList::UpdateListing() { + auto &discord = Abaddon::Get().GetDiscordClient(); + + Clear(); + + auto *dms = Gtk::make_managed<GuildListDMsButton>(); + dms->show(); + dms->signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { + if (ev->type == GDK_BUTTON_PRESS && ev->button == GDK_BUTTON_PRIMARY) { + m_signal_dms_selected.emit(); + } + return false; + }); + add(*dms); + + // does this function still even work ??lol + const auto folders = discord.GetUserSettings().GuildFolders; + const auto guild_ids = discord.GetUserSortedGuilds(); + + // same logic from ChannelListTree + + std::set<Snowflake> foldered_guilds; + for (const auto &group : folders) { + foldered_guilds.insert(group.GuildIDs.begin(), group.GuildIDs.end()); + } + + for (auto iter = guild_ids.crbegin(); iter != guild_ids.crend(); iter++) { + if (foldered_guilds.find(*iter) == foldered_guilds.end()) { + AddGuild(*iter); + } + } + + for (const auto &group : folders) { + AddFolder(group); + } +} + +void GuildList::AddGuild(Snowflake id) { + if (auto item = CreateGuildWidget(id)) { + item->show(); + add(*item); + } +} + +GuildListGuildItem *GuildList::CreateGuildWidget(Snowflake id) { + const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id); + if (!guild.has_value()) return nullptr; + + auto *item = Gtk::make_managed<GuildListGuildItem>(*guild); + item->signal_button_press_event().connect([this, id](GdkEventButton *event) -> bool { + if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { + m_signal_guild_selected.emit(id); + } else if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) { + m_menu_guild_target = id; + OnGuildSubmenuPopup(); + m_menu_guild.popup_at_pointer(reinterpret_cast<GdkEvent *>(event)); + } + return true; + }); + + return item; +} + +void GuildList::AddFolder(const UserSettingsGuildFoldersEntry &folder) { + // groups with no ID arent actually folders + if (!folder.ID.has_value()) { + if (!folder.GuildIDs.empty()) { + AddGuild(folder.GuildIDs[0]); + } + return; + } + + auto *folder_widget = Gtk::make_managed<GuildListFolderItem>(folder); + for (const auto guild_id : folder.GuildIDs) { + if (auto *guild_widget = CreateGuildWidget(guild_id)) { + guild_widget->show(); + folder_widget->AddGuildWidget(guild_widget); + } + } + + folder_widget->show(); + add(*folder_widget); +} + +void GuildList::Clear() { + const auto children = get_children(); + for (auto *child : children) { + delete child; + } +} + +void GuildList::OnGuildSubmenuPopup() { + const auto id = m_menu_guild_target; + auto &discord = Abaddon::Get().GetDiscordClient(); + if (discord.IsGuildMuted(id)) { + m_menu_guild_toggle_mute.set_label("Unmute"); + } else { + m_menu_guild_toggle_mute.set_label("Mute"); + } + + const auto guild = discord.GetGuild(id); + const auto self_id = discord.GetUserData().ID; + m_menu_guild_leave.set_sensitive(!(guild.has_value() && guild->OwnerID == self_id)); +} + +GuildList::type_signal_guild_selected GuildList::signal_guild_selected() { + return m_signal_guild_selected; +} + +GuildList::type_signal_dms_selected GuildList::signal_dms_selected() { + return m_signal_dms_selected; +} + +GuildList::type_signal_action_guild_leave GuildList::signal_action_guild_leave() { + return m_signal_action_guild_leave; +} + +GuildList::type_signal_action_guild_settings GuildList::signal_action_guild_settings() { + return m_signal_action_guild_settings; +} diff --git a/src/components/channellist/classic/guildlist.hpp b/src/components/channellist/classic/guildlist.hpp new file mode 100644 index 0000000..72e88e8 --- /dev/null +++ b/src/components/channellist/classic/guildlist.hpp @@ -0,0 +1,48 @@ +#pragma once +#include <gtkmm/listbox.h> +#include "discord/snowflake.hpp" +#include "discord/usersettings.hpp" + +class GuildListGuildItem; + +class GuildList : public Gtk::ListBox { +public: + GuildList(); + + void UpdateListing(); + +private: + void AddGuild(Snowflake id); + void AddFolder(const UserSettingsGuildFoldersEntry &folder); + void Clear(); + + GuildListGuildItem *CreateGuildWidget(Snowflake id); + + // todo code duplication not good no sir + Gtk::Menu m_menu_guild; + Gtk::MenuItem m_menu_guild_copy_id; + Gtk::MenuItem m_menu_guild_settings; + Gtk::MenuItem m_menu_guild_leave; + Gtk::MenuItem m_menu_guild_mark_as_read; + Gtk::MenuItem m_menu_guild_toggle_mute; + Snowflake m_menu_guild_target; + + void OnGuildSubmenuPopup(); + +public: + using type_signal_guild_selected = sigc::signal<void, Snowflake>; + using type_signal_dms_selected = sigc::signal<void>; + using type_signal_action_guild_leave = sigc::signal<void, Snowflake>; + using type_signal_action_guild_settings = sigc::signal<void, Snowflake>; + + type_signal_guild_selected signal_guild_selected(); + type_signal_dms_selected signal_dms_selected(); + type_signal_action_guild_leave signal_action_guild_leave(); + type_signal_action_guild_settings signal_action_guild_settings(); + +private: + type_signal_guild_selected m_signal_guild_selected; + type_signal_dms_selected m_signal_dms_selected; + type_signal_action_guild_leave m_signal_action_guild_leave; + type_signal_action_guild_settings m_signal_action_guild_settings; +}; diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp new file mode 100644 index 0000000..e062d42 --- /dev/null +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -0,0 +1,129 @@ +#include "guildlistfolderitem.hpp" + +#include "abaddon.hpp" +#include "guildlistguilditem.hpp" +#include "util.hpp" + +// doing my best to copy discord here + +const int FolderGridButtonSize = 48; +const int FolderGridImageSize = 24; + +GuildListFolderButton::GuildListFolderButton() { + set_size_request(FolderGridButtonSize, FolderGridButtonSize); +} + +void GuildListFolderButton::SetGuilds(const std::vector<Snowflake> &guild_ids) { + for (int y = 0; y < 2; y++) { + for (int x = 0; x < 2; x++) { + const size_t i = y * 2 + x; + auto &widget = m_images[x][y]; + widget.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(FolderGridImageSize); + attach(widget, x, y, 1, 1); + + if (i < guild_ids.size()) { + widget.show(); + + if (const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(guild_ids[i]); guild.has_value() && guild->HasIcon()) { + const auto cb = [&widget](const Glib::RefPtr<Gdk::Pixbuf> &pb) { + widget.property_pixbuf() = pb->scale_simple(FolderGridImageSize, FolderGridImageSize, Gdk::INTERP_BILINEAR); + }; + Abaddon::Get().GetImageManager().LoadFromURL(guild->GetIconURL("png", "32"), sigc::track_obj(cb, *this)); + } + } + } + } +} + +GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder) { + m_guild_ids = folder.GuildIDs; + + get_style_context()->add_class("classic-guild-list-folder"); + + if (folder.Name.has_value()) { + set_tooltip_text(*folder.Name); + } + + m_revealer.add(m_box); + m_revealer.set_reveal_child(false); + + m_image.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(48); + + m_ev.signal_button_press_event().connect([this](GdkEventButton *event) -> bool { + if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { + m_revealer.set_reveal_child(!m_revealer.get_reveal_child()); + if (!Abaddon::Get().GetSettings().FolderIconOnly) { + if (m_revealer.get_reveal_child()) { + m_stack.set_visible_child("icon", Gtk::STACK_TRANSITION_TYPE_SLIDE_DOWN); + } else { + m_stack.set_visible_child("grid", Gtk::STACK_TRANSITION_TYPE_SLIDE_UP); + } + } + } + + return false; + }); + + m_grid.SetGuilds(folder.GuildIDs); + m_grid.show(); + + m_icon.property_icon_name() = "folder-symbolic"; + m_icon.property_icon_size() = Gtk::ICON_SIZE_DND; + if (folder.Color.has_value()) { + m_icon.override_color(IntToRGBA(*folder.Color)); + } + m_icon.show(); + + m_stack.add(m_grid, "grid"); + m_stack.add(m_icon, "icon"); + m_stack.set_visible_child(Abaddon::Get().GetSettings().FolderIconOnly ? "icon" : "grid"); + m_stack.show(); + + m_ev.add(m_stack); + add(m_ev); + add(m_revealer); + + m_ev.show(); + m_revealer.show(); + m_box.show(); + m_image.show(); + show(); + + Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &GuildListFolderItem::OnMessageCreate)); + Abaddon::Get().GetDiscordClient().signal_message_ack().connect(sigc::mem_fun(*this, &GuildListFolderItem::OnMessageAck)); + + CheckUnreadStatus(); +} + +void GuildListFolderItem::AddGuildWidget(GuildListGuildItem *widget) { + m_box.add(*widget); +} + +void GuildListFolderItem::OnMessageCreate(const Message &msg) { + if (msg.GuildID.has_value() && std::find(m_guild_ids.begin(), m_guild_ids.end(), *msg.GuildID) != m_guild_ids.end()) CheckUnreadStatus(); +} + +void GuildListFolderItem::OnMessageAck(const MessageAckData &data) { + CheckUnreadStatus(); +} + +void GuildListFolderItem::CheckUnreadStatus() { + auto &discord = Abaddon::Get().GetDiscordClient(); + if (!Abaddon::Get().GetSettings().Unreads) return; + + bool has_any_unreads = false; + + for (auto guild_id : m_guild_ids) { + int mentions; + if (!discord.IsGuildMuted(guild_id) && discord.GetUnreadStateForGuild(guild_id, mentions)) { + has_any_unreads = true; + break; + } + } + + if (has_any_unreads) { + get_style_context()->add_class("has-unread"); + } else { + get_style_context()->remove_class("has-unread"); + } +} diff --git a/src/components/channellist/classic/guildlistfolderitem.hpp b/src/components/channellist/classic/guildlistfolderitem.hpp new file mode 100644 index 0000000..e5772c0 --- /dev/null +++ b/src/components/channellist/classic/guildlistfolderitem.hpp @@ -0,0 +1,44 @@ +#pragma once +#include <gtkmm/box.h> +#include <gtkmm/eventbox.h> +#include <gtkmm/grid.h> +#include <gtkmm/image.h> +#include <gtkmm/revealer.h> +#include <gtkmm/stack.h> + +#include "guildlistguilditem.hpp" +#include "discord/usersettings.hpp" + +class GuildListGuildItem; + +class GuildListFolderButton : public Gtk::Grid { +public: + GuildListFolderButton(); + void SetGuilds(const std::vector<Snowflake> &guild_ids); + +private: + Gtk::Image m_images[2][2]; +}; + +class GuildListFolderItem : public Gtk::VBox { +public: + GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder); + + void AddGuildWidget(GuildListGuildItem *widget); + +private: + void OnMessageCreate(const Message &msg); + void OnMessageAck(const MessageAckData &data); + void CheckUnreadStatus(); + + std::vector<Snowflake> m_guild_ids; + + Gtk::Stack m_stack; + GuildListFolderButton m_grid; + Gtk::Image m_icon; + + Gtk::EventBox m_ev; + Gtk::Image m_image; + Gtk::Revealer m_revealer; + Gtk::VBox m_box; +}; diff --git a/src/components/channellist/classic/guildlistguilditem.cpp b/src/components/channellist/classic/guildlistguilditem.cpp new file mode 100644 index 0000000..5b578be --- /dev/null +++ b/src/components/channellist/classic/guildlistguilditem.cpp @@ -0,0 +1,52 @@ +#include "guildlistguilditem.hpp" + +#include "abaddon.hpp" + +GuildListGuildItem::GuildListGuildItem(const GuildData &guild) + : ID(guild.ID) { + get_style_context()->add_class("classic-guild-list-guild"); + + m_image.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(48); + + add(m_box); + m_box.pack_start(m_image); + show_all_children(); + + set_tooltip_text(guild.Name); + + UpdateIcon(); + + Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &GuildListGuildItem::OnMessageCreate)); + Abaddon::Get().GetDiscordClient().signal_message_ack().connect(sigc::mem_fun(*this, &GuildListGuildItem::OnMessageAck)); + + CheckUnreadStatus(); +} + +void GuildListGuildItem::UpdateIcon() { + const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(ID); + if (!guild.has_value() || !guild->HasIcon()) return; + Abaddon::Get().GetImageManager().LoadFromURL(guild->GetIconURL("png", "64"), sigc::mem_fun(*this, &GuildListGuildItem::OnIconFetched)); +} + +void GuildListGuildItem::OnIconFetched(const Glib::RefPtr<Gdk::Pixbuf> &pb) { + m_image.property_pixbuf() = pb->scale_simple(48, 48, Gdk::INTERP_BILINEAR); +} + +void GuildListGuildItem::OnMessageCreate(const Message &msg) { + if (msg.GuildID.has_value() && *msg.GuildID == ID) CheckUnreadStatus(); +} + +void GuildListGuildItem::OnMessageAck(const MessageAckData &data) { + CheckUnreadStatus(); +} + +void GuildListGuildItem::CheckUnreadStatus() { + auto &discord = Abaddon::Get().GetDiscordClient(); + if (!Abaddon::Get().GetSettings().Unreads) return; + int mentions; + if (!discord.IsGuildMuted(ID) && discord.GetUnreadStateForGuild(ID, mentions)) { + get_style_context()->add_class("has-unread"); + } else { + get_style_context()->remove_class("has-unread"); + } +} diff --git a/src/components/channellist/classic/guildlistguilditem.hpp b/src/components/channellist/classic/guildlistguilditem.hpp new file mode 100644 index 0000000..6e2b241 --- /dev/null +++ b/src/components/channellist/classic/guildlistguilditem.hpp @@ -0,0 +1,22 @@ +#pragma once +#include <gtkmm/box.h> +#include <gtkmm/eventbox.h> +#include <gtkmm/image.h> +#include "discord/guild.hpp" + +class GuildListGuildItem : public Gtk::EventBox { +public: + GuildListGuildItem(const GuildData &guild); + + Snowflake ID; + +private: + void UpdateIcon(); + void OnIconFetched(const Glib::RefPtr<Gdk::Pixbuf> &pb); + void OnMessageCreate(const Message &msg); + void OnMessageAck(const MessageAckData &data); + void CheckUnreadStatus(); + + Gtk::Box m_box; + Gtk::Image m_image; +}; |