From 3e0cd83dd6026dc701c548dc40b41f5accbc29db Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 16 Oct 2023 21:31:28 -0400 Subject: [PATCH 01/48] start channel list refactor for server list --- .../cellrendererchannels.cpp} | 2 +- .../cellrendererchannels.hpp} | 0 .../channellist.cpp} | 91 ++++++------------- .../channellist.hpp} | 6 +- .../channellist/classic/guildlist.cpp | 23 +++++ .../channellist/classic/guildlist.hpp | 12 +++ .../classic/guildlistfolderitem.cpp | 18 ++++ .../classic/guildlistfolderitem.hpp | 18 ++++ .../classic/guildlistguilditem.cpp | 29 ++++++ .../classic/guildlistguilditem.hpp | 17 ++++ src/windows/mainwindow.cpp | 1 + src/windows/mainwindow.hpp | 2 +- 12 files changed, 152 insertions(+), 67 deletions(-) rename src/components/{channelscellrenderer.cpp => channellist/cellrendererchannels.cpp} (99%) rename src/components/{channelscellrenderer.hpp => channellist/cellrendererchannels.hpp} (100%) rename src/components/{channels.cpp => channellist/channellist.cpp} (95%) rename src/components/{channels.hpp => channellist/channellist.hpp} (97%) create mode 100644 src/components/channellist/classic/guildlist.cpp create mode 100644 src/components/channellist/classic/guildlist.hpp create mode 100644 src/components/channellist/classic/guildlistfolderitem.cpp create mode 100644 src/components/channellist/classic/guildlistfolderitem.hpp create mode 100644 src/components/channellist/classic/guildlistguilditem.cpp create mode 100644 src/components/channellist/classic/guildlistguilditem.hpp diff --git a/src/components/channelscellrenderer.cpp b/src/components/channellist/cellrendererchannels.cpp similarity index 99% rename from src/components/channelscellrenderer.cpp rename to src/components/channellist/cellrendererchannels.cpp index ac98512d..e0079cd1 100644 --- a/src/components/channelscellrenderer.cpp +++ b/src/components/channellist/cellrendererchannels.cpp @@ -1,4 +1,4 @@ -#include "channelscellrenderer.hpp" +#include "cellrendererchannels.hpp" constexpr static int MentionsRightPad = 7; #ifndef M_PI diff --git a/src/components/channelscellrenderer.hpp b/src/components/channellist/cellrendererchannels.hpp similarity index 100% rename from src/components/channelscellrenderer.hpp rename to src/components/channellist/cellrendererchannels.hpp diff --git a/src/components/channels.cpp b/src/components/channellist/channellist.cpp similarity index 95% rename from src/components/channels.cpp rename to src/components/channellist/channellist.cpp index 9fd4abd7..51e8f336 100644 --- a/src/components/channels.cpp +++ b/src/components/channellist/channellist.cpp @@ -1,6 +1,6 @@ -#include "channels.hpp" +#include "channellist.hpp" #include "imgmanager.hpp" -#include "statusindicator.hpp" +#include "components/statusindicator.hpp" #include #include #include @@ -8,6 +8,7 @@ ChannelList::ChannelList() : Glib::ObjectBase(typeid(ChannelList)) , m_model(Gtk::TreeStore::create(m_columns)) + , m_filter_model(Gtk::TreeModelFilter::create(m_model)) , m_menu_guild_copy_id("_Copy ID", true) , m_menu_guild_settings("View _Settings", true) , m_menu_guild_leave("_Leave", true) @@ -36,9 +37,9 @@ ChannelList::ChannelList() , m_menu_thread_mark_as_read("Mark as _Read", true) { get_style_context()->add_class("channel-list"); - // todo: move to method + // Filter iters const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) { - auto row = *m_model->get_iter(path); + auto row = *m_filter_model->get_iter(path); const auto type = row[m_columns.m_type]; // text channels should not be allowed to be collapsed // maybe they should be but it seems a little difficult to handle expansion to permit this @@ -72,13 +73,22 @@ ChannelList::ChannelList() m_view.set_show_expanders(false); m_view.set_enable_search(false); m_view.set_headers_visible(false); - m_view.set_model(m_model); + m_view.set_model(m_filter_model); m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING); m_model->signal_row_inserted().connect([this](const Gtk::TreeModel::Path &path, const Gtk::TreeModel::iterator &iter) { if (m_updating_listing) return; - if (auto parent = iter->parent(); parent && (*parent)[m_columns.m_expanded]) - m_view.expand_row(m_model->get_path(parent), false); + if (auto parent = iter->parent(); parent && (*parent)[m_columns.m_expanded]) { + const auto filter_path = m_filter_model->convert_child_path_to_path(m_model->get_path(parent)); + m_view.expand_row(filter_path, false); + } + }); + + m_filter_model->set_visible_func([this](const Gtk::TreeModel::const_iterator &iter) -> bool { + if ((*iter)[m_columns.m_type] == RenderType::Guild) { + return (*iter)[m_columns.m_id] == 754921263616753776ULL; + } + return true; }); m_view.show(); @@ -594,9 +604,9 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { const auto channel_iter = GetIteratorForRowFromID(id); if (channel_iter) { if (expand_to) { - m_view.expand_to_path(m_model->get_path(channel_iter)); + m_view.expand_to_path(m_filter_model->convert_child_path_to_path(m_model->get_path(channel_iter))); } - m_view.get_selection()->select(channel_iter); + m_view.get_selection()->select(m_filter_model->convert_child_iter_to_iter(channel_iter)); } else { m_view.get_selection()->unselect_all(); const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); @@ -604,64 +614,17 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { auto parent_iter = GetIteratorForRowFromID(*channel->ParentID); if (!parent_iter) return; m_temporary_thread_row = CreateThreadRow(parent_iter->children(), *channel); - m_view.get_selection()->select(m_temporary_thread_row); + m_view.get_selection()->select(m_filter_model->convert_child_iter_to_iter(m_temporary_thread_row)); } } void ChannelList::UseExpansionState(const ExpansionStateRoot &root) { - auto recurse = [this](auto &self, const ExpansionStateRoot &root) -> void { - for (const auto &[id, state] : root.Children) { - Gtk::TreeModel::iterator row_iter; - if (const auto map_iter = m_tmp_row_map.find(id); map_iter != m_tmp_row_map.end()) { - row_iter = map_iter->second; - } else if (const auto map_iter = m_tmp_guild_row_map.find(id); map_iter != m_tmp_guild_row_map.end()) { - row_iter = map_iter->second; - } - - if (row_iter) { - if (state.IsExpanded) - m_view.expand_row(m_model->get_path(row_iter), false); - else - m_view.collapse_row(m_model->get_path(row_iter)); - } - - self(self, state.Children); - } - }; - - for (const auto &[id, state] : root.Children) { - if (const auto iter = GetIteratorForTopLevelFromID(id)) { - if (state.IsExpanded) - m_view.expand_row(m_model->get_path(iter), false); - else - m_view.collapse_row(m_model->get_path(iter)); - } - - recurse(recurse, state.Children); - } - m_tmp_row_map.clear(); } ExpansionStateRoot ChannelList::GetExpansionState() const { ExpansionStateRoot r; - auto recurse = [this](auto &self, const Gtk::TreeRow &row) -> ExpansionState { - ExpansionState r; - - r.IsExpanded = row[m_columns.m_expanded]; - for (const auto &child : row.children()) - r.Children.Children[static_cast(child[m_columns.m_id])] = self(self, child); - - return r; - }; - - for (const auto &child : m_model->children()) { - const auto id = static_cast(child[m_columns.m_id]); - if (static_cast(id) == 0ULL) continue; // dont save DM header - r.Children[id] = recurse(recurse, child); - } - return r; } @@ -969,7 +932,7 @@ void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk: // restore previous expansion for (auto it = iter->children().begin(); it != iter->children().end(); it++) { if ((*it)[m_columns.m_expanded]) - m_view.expand_row(m_model->get_path(it), false); + m_view.expand_row(m_filter_model->get_path(it), false); } // try and restore selection if previous collapsed @@ -981,11 +944,13 @@ void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk: } bool ChannelList::SelectionFunc(const Glib::RefPtr &model, const Gtk::TreeModel::Path &path, bool is_currently_selected) { - if (auto selection = m_view.get_selection()) - if (auto row = selection->get_selected()) - m_last_selected = m_model->get_path(row); + if (auto selection = m_view.get_selection()) { + if (auto row = selection->get_selected()) { + m_last_selected = m_filter_model->get_path(row); + } + } - auto type = (*m_model->get_iter(path))[m_columns.m_type]; + auto type = (*model->get_iter(path))[m_columns.m_type]; return type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread; } @@ -1139,7 +1104,7 @@ void ChannelList::OnMessageCreate(const Message &msg) { bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) { if (ev->button == GDK_BUTTON_SECONDARY && ev->type == GDK_BUTTON_PRESS) { if (m_view.get_path_at_pos(static_cast(ev->x), static_cast(ev->y), m_path_for_menu)) { - auto row = (*m_model->get_iter(m_path_for_menu)); + auto row = (*m_filter_model->get_iter(m_path_for_menu)); switch (static_cast(row[m_columns.m_type])) { case RenderType::Guild: OnGuildSubmenuPopup(); diff --git a/src/components/channels.hpp b/src/components/channellist/channellist.hpp similarity index 97% rename from src/components/channels.hpp rename to src/components/channellist/channellist.hpp index 9d449e4c..df7dbac1 100644 --- a/src/components/channels.hpp +++ b/src/components/channellist/channellist.hpp @@ -8,11 +8,12 @@ #include #include #include +#include #include #include #include "discord/discord.hpp" #include "state.hpp" -#include "channelscellrenderer.hpp" +#include "cellrendererchannels.hpp" constexpr static int GuildIconSize = 24; constexpr static int DMIconSize = 20; @@ -82,6 +83,7 @@ class ChannelList : public Gtk::ScrolledWindow { ModelColumns m_columns; Glib::RefPtr m_model; + Glib::RefPtr m_filter_model; Gtk::TreeModel::iterator AddFolder(const UserSettingsGuildFoldersEntry &folder); Gtk::TreeModel::iterator AddGuild(const GuildData &guild, const Gtk::TreeNodeChildren &root); @@ -116,7 +118,7 @@ class ChannelList : public Gtk::ScrolledWindow { void UpdateCreateDMChannel(const ChannelData &channel); void SetDMChannelIcon(Gtk::TreeIter iter, const ChannelData &dm); - void RedrawUnreadIndicatorsForChannel(const ChannelData& channel); + void RedrawUnreadIndicatorsForChannel(const ChannelData &channel); void OnMessageAck(const MessageAckData &data); void OnMessageCreate(const Message &msg); diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp new file mode 100644 index 00000000..0d5737b4 --- /dev/null +++ b/src/components/channellist/classic/guildlist.cpp @@ -0,0 +1,23 @@ +#include "guildlist.hpp" +#include "guildlistfolderitem.hpp" + +GuildList::GuildList() { + set_selection_mode(Gtk::SELECTION_NONE); + show_all_children(); +} + +void GuildList::AddGuild(Snowflake id) { + const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id); + if (!guild.has_value()) return; + + auto *item = Gtk::make_managed(*guild); + item->show(); + add(*item); +} + +void GuildList::Clear() { + const auto children = get_children(); + for (auto child : children) { + delete child; + } +} diff --git a/src/components/channellist/classic/guildlist.hpp b/src/components/channellist/classic/guildlist.hpp new file mode 100644 index 00000000..244313fb --- /dev/null +++ b/src/components/channellist/classic/guildlist.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include "discord/snowflake.hpp" + +class GuildList : public Gtk::ListBox { +public: + GuildList(); + + void AddGuild(Snowflake id); + + void Clear(); +}; diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp new file mode 100644 index 00000000..36d5a5a1 --- /dev/null +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -0,0 +1,18 @@ +#include "guildlistfolderitem.hpp" + +GuildListFolderItem::GuildListFolderItem() { + m_revealer.add(m_box); + m_revealer.set_reveal_child(true); + + 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()); + } + return false; + }); + + m_ev.add(m_image); + add(m_ev); + add(m_revealer); + show_all_children(); +} diff --git a/src/components/channellist/classic/guildlistfolderitem.hpp b/src/components/channellist/classic/guildlistfolderitem.hpp new file mode 100644 index 00000000..3506969d --- /dev/null +++ b/src/components/channellist/classic/guildlistfolderitem.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include +#include +#include + +#include "guildlistguilditem.hpp" + +class GuildListFolderItem : public Gtk::VBox { +public: + GuildListFolderItem(); + +private: + 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 00000000..88ed2a96 --- /dev/null +++ b/src/components/channellist/classic/guildlistguilditem.cpp @@ -0,0 +1,29 @@ +#include "guildlistguilditem.hpp" + +GuildListGuildItem::GuildListGuildItem(const GuildData &guild) + : ID(guild.ID) { + m_image.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(48); + add(m_image); + show_all_children(); + + signal_button_press_event().connect([this](GdkEventButton *event) -> bool { + if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { + printf("Click %llu\n", (uint64_t)ID); + } + return true; + }); + + set_tooltip_text(guild.Name); + + UpdateIcon(); +} + +void GuildListGuildItem::UpdateIcon() { + const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(ID); + if (!guild.has_value()) return; + Abaddon::Get().GetImageManager().LoadFromURL(guild->GetIconURL("png", "64"), sigc::mem_fun(*this, &GuildListGuildItem::OnIconFetched)); +} + +void GuildListGuildItem::OnIconFetched(const Glib::RefPtr &pb) { + m_image.property_pixbuf() = pb->scale_simple(48, 48, Gdk::INTERP_BILINEAR); +} diff --git a/src/components/channellist/classic/guildlistguilditem.hpp b/src/components/channellist/classic/guildlistguilditem.hpp new file mode 100644 index 00000000..3114a05b --- /dev/null +++ b/src/components/channellist/classic/guildlistguilditem.hpp @@ -0,0 +1,17 @@ +#pragma once +#include +#include +#include "discord/guild.hpp" + +class GuildListGuildItem : public Gtk::EventBox { +public: + GuildListGuildItem(const GuildData &guild); + + Snowflake ID; + +private: + void UpdateIcon(); + void OnIconFetched(const Glib::RefPtr &pb); + + Gtk::Image m_image; +}; diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index 8e030edc..8156659a 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -1,4 +1,5 @@ #include "mainwindow.hpp" +#include "components/channellist/channellist.hpp" MainWindow::MainWindow() : m_main_box(Gtk::ORIENTATION_VERTICAL) diff --git a/src/windows/mainwindow.hpp b/src/windows/mainwindow.hpp index ce2e6366..37c1b87a 100644 --- a/src/windows/mainwindow.hpp +++ b/src/windows/mainwindow.hpp @@ -1,5 +1,5 @@ #pragma once -#include "components/channels.hpp" +#include "components/channellist/channellist.hpp" #include "components/chatwindow.hpp" #include "components/memberlist.hpp" #include "components/friendslist.hpp" From 2646da31a1a396b5c9d4c9537a5f04f8a210883c Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 16 Oct 2023 21:32:18 -0400 Subject: [PATCH 02/48] check filter model conversions --- src/components/channellist/channellist.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 51e8f336..236513eb 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -604,9 +604,15 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { const auto channel_iter = GetIteratorForRowFromID(id); if (channel_iter) { if (expand_to) { - m_view.expand_to_path(m_filter_model->convert_child_path_to_path(m_model->get_path(channel_iter))); + const auto filter_path = m_filter_model->convert_child_path_to_path(m_model->get_path(channel_iter)); + if (filter_path) { + m_view.expand_to_path(filter_path); + } + } + const auto filter_iter = m_filter_model->convert_child_iter_to_iter(channel_iter); + if (filter_iter) { + m_view.get_selection()->select(filter_iter); } - m_view.get_selection()->select(m_filter_model->convert_child_iter_to_iter(channel_iter)); } else { m_view.get_selection()->unselect_all(); const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); @@ -614,7 +620,10 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { auto parent_iter = GetIteratorForRowFromID(*channel->ParentID); if (!parent_iter) return; m_temporary_thread_row = CreateThreadRow(parent_iter->children(), *channel); - m_view.get_selection()->select(m_filter_model->convert_child_iter_to_iter(m_temporary_thread_row)); + const auto filter_iter = m_filter_model->convert_child_iter_to_iter(m_temporary_thread_row); + if (filter_iter) { + m_view.get_selection()->select(filter_iter); + } } } From 4906775edeb96dc3f9194d43252ab2b47bc11e4e Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 16 Oct 2023 21:35:44 -0400 Subject: [PATCH 03/48] remove tmp row map from channel list --- src/components/channellist/channellist.cpp | 11 +++-------- src/components/channellist/channellist.hpp | 5 ----- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 236513eb..ca731c98 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -628,7 +628,6 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { } void ChannelList::UseExpansionState(const ExpansionStateRoot &root) { - m_tmp_row_map.clear(); } ExpansionStateRoot ChannelList::GetExpansionState() const { @@ -650,7 +649,6 @@ Gtk::TreeModel::iterator ChannelList::AddFolder(const UserSettingsGuildFoldersEn auto folder_row = *m_model->append(); folder_row[m_columns.m_type] = RenderType::Folder; folder_row[m_columns.m_id] = *folder.ID; - m_tmp_row_map[*folder.ID] = folder_row; if (folder.Name.has_value()) { folder_row[m_columns.m_name] = Glib::Markup::escape_text(*folder.Name); } else { @@ -684,7 +682,6 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk guild_row[m_columns.m_id] = guild.ID; guild_row[m_columns.m_name] = "" + Glib::Markup::escape_text(guild.Name) + ""; guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize); - m_tmp_guild_row_map[guild.ID] = guild_row; if (Abaddon::Get().GetSettings().ShowAnimations && guild.HasAnimatedIcon()) { const auto cb = [this, id = guild.ID](const Glib::RefPtr &pb) { @@ -735,8 +732,9 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk const auto it = threads.find(channel.ID); if (it == threads.end()) return; - for (const auto &thread : it->second) - m_tmp_row_map[thread.ID] = CreateThreadRow(row.children(), thread); + for (const auto &thread : it->second) { + CreateThreadRow(row.children(), thread); + } }; #ifdef WITH_VOICE @@ -766,7 +764,6 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset; channel_row[m_columns.m_nsfw] = channel.NSFW(); add_threads(channel, channel_row); - m_tmp_row_map[channel.ID] = channel_row; } for (const auto &[category_id, channels] : categories) { @@ -778,7 +775,6 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk cat_row[m_columns.m_name] = Glib::Markup::escape_text(*category->Name); cat_row[m_columns.m_sort] = *category->Position; cat_row[m_columns.m_expanded] = true; - m_tmp_row_map[category_id] = cat_row; // m_view.expand_row wont work because it might not have channels for (const auto &channel : channels) { @@ -798,7 +794,6 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk channel_row[m_columns.m_sort] = *channel.Position; channel_row[m_columns.m_nsfw] = channel.NSFW(); add_threads(channel, channel_row); - m_tmp_row_map[channel.ID] = channel_row; } } diff --git a/src/components/channellist/channellist.hpp b/src/components/channellist/channellist.hpp index df7dbac1..8c644c95 100644 --- a/src/components/channellist/channellist.hpp +++ b/src/components/channellist/channellist.hpp @@ -188,11 +188,6 @@ class ChannelList : public Gtk::ScrolledWindow { Snowflake m_active_channel; - // (GetIteratorForChannelFromID is rather slow) - // only temporary since i dont want to worry about maintaining this map - std::unordered_map m_tmp_row_map; - std::unordered_map m_tmp_guild_row_map; - public: using type_signal_action_channel_item_select = sigc::signal; using type_signal_action_guild_leave = sigc::signal; From 201b114183454f3fb9114fed2caef4ac46587225 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 24 Oct 2023 21:42:19 -0400 Subject: [PATCH 04/48] refactor ChannelList -> ChannelListTree --- .../{channellist.cpp => channellisttree.cpp} | 176 +++++++++--------- .../{channellist.hpp => channellisttree.hpp} | 4 +- src/windows/mainwindow.cpp | 4 +- src/windows/mainwindow.hpp | 6 +- 4 files changed, 95 insertions(+), 95 deletions(-) rename src/components/channellist/{channellist.cpp => channellisttree.cpp} (89%) rename src/components/channellist/{channellist.hpp => channellisttree.hpp} (99%) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellisttree.cpp similarity index 89% rename from src/components/channellist/channellist.cpp rename to src/components/channellist/channellisttree.cpp index ca731c98..22f85814 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -1,12 +1,12 @@ -#include "channellist.hpp" +#include "channellisttree.hpp" #include "imgmanager.hpp" #include "components/statusindicator.hpp" #include #include #include -ChannelList::ChannelList() - : Glib::ObjectBase(typeid(ChannelList)) +ChannelListTree::ChannelListTree() + : Glib::ObjectBase(typeid(ChannelListTree)) , m_model(Gtk::TreeStore::create(m_columns)) , m_filter_model(Gtk::TreeModelFilter::create(m_model)) , m_menu_guild_copy_id("_Copy ID", true) @@ -60,12 +60,12 @@ ChannelList::ChannelList() } }; m_view.signal_row_activated().connect(cb, false); - m_view.signal_row_collapsed().connect(sigc::mem_fun(*this, &ChannelList::OnRowCollapsed), false); - m_view.signal_row_expanded().connect(sigc::mem_fun(*this, &ChannelList::OnRowExpanded), false); + m_view.signal_row_collapsed().connect(sigc::mem_fun(*this, &ChannelListTree::OnRowCollapsed), false); + m_view.signal_row_expanded().connect(sigc::mem_fun(*this, &ChannelListTree::OnRowExpanded), false); m_view.set_activate_on_single_click(true); m_view.get_selection()->set_mode(Gtk::SELECTION_SINGLE); - m_view.get_selection()->set_select_function(sigc::mem_fun(*this, &ChannelList::SelectionFunc)); - m_view.signal_button_press_event().connect(sigc::mem_fun(*this, &ChannelList::OnButtonPressEvent), false); + m_view.get_selection()->set_select_function(sigc::mem_fun(*this, &ChannelListTree::SelectionFunc)); + m_view.signal_button_press_event().connect(sigc::mem_fun(*this, &ChannelListTree::OnButtonPressEvent), false); m_view.set_hexpand(true); m_view.set_vexpand(true); @@ -272,40 +272,40 @@ ChannelList::ChannelList() m_menu_thread.show_all(); auto &discord = Abaddon::Get().GetDiscordClient(); - discord.signal_message_create().connect(sigc::mem_fun(*this, &ChannelList::OnMessageCreate)); - discord.signal_guild_create().connect(sigc::mem_fun(*this, &ChannelList::UpdateNewGuild)); - discord.signal_guild_delete().connect(sigc::mem_fun(*this, &ChannelList::UpdateRemoveGuild)); - discord.signal_channel_delete().connect(sigc::mem_fun(*this, &ChannelList::UpdateRemoveChannel)); - discord.signal_channel_update().connect(sigc::mem_fun(*this, &ChannelList::UpdateChannel)); - discord.signal_channel_create().connect(sigc::mem_fun(*this, &ChannelList::UpdateCreateChannel)); - discord.signal_thread_delete().connect(sigc::mem_fun(*this, &ChannelList::OnThreadDelete)); - discord.signal_thread_update().connect(sigc::mem_fun(*this, &ChannelList::OnThreadUpdate)); - discord.signal_thread_list_sync().connect(sigc::mem_fun(*this, &ChannelList::OnThreadListSync)); - discord.signal_added_to_thread().connect(sigc::mem_fun(*this, &ChannelList::OnThreadJoined)); - discord.signal_removed_from_thread().connect(sigc::mem_fun(*this, &ChannelList::OnThreadRemoved)); - discord.signal_guild_update().connect(sigc::mem_fun(*this, &ChannelList::UpdateGuild)); - discord.signal_message_ack().connect(sigc::mem_fun(*this, &ChannelList::OnMessageAck)); - discord.signal_channel_muted().connect(sigc::mem_fun(*this, &ChannelList::OnChannelMute)); - discord.signal_channel_unmuted().connect(sigc::mem_fun(*this, &ChannelList::OnChannelUnmute)); - discord.signal_guild_muted().connect(sigc::mem_fun(*this, &ChannelList::OnGuildMute)); - discord.signal_guild_unmuted().connect(sigc::mem_fun(*this, &ChannelList::OnGuildUnmute)); + discord.signal_message_create().connect(sigc::mem_fun(*this, &ChannelListTree::OnMessageCreate)); + discord.signal_guild_create().connect(sigc::mem_fun(*this, &ChannelListTree::UpdateNewGuild)); + discord.signal_guild_delete().connect(sigc::mem_fun(*this, &ChannelListTree::UpdateRemoveGuild)); + discord.signal_channel_delete().connect(sigc::mem_fun(*this, &ChannelListTree::UpdateRemoveChannel)); + discord.signal_channel_update().connect(sigc::mem_fun(*this, &ChannelListTree::UpdateChannel)); + discord.signal_channel_create().connect(sigc::mem_fun(*this, &ChannelListTree::UpdateCreateChannel)); + discord.signal_thread_delete().connect(sigc::mem_fun(*this, &ChannelListTree::OnThreadDelete)); + discord.signal_thread_update().connect(sigc::mem_fun(*this, &ChannelListTree::OnThreadUpdate)); + discord.signal_thread_list_sync().connect(sigc::mem_fun(*this, &ChannelListTree::OnThreadListSync)); + discord.signal_added_to_thread().connect(sigc::mem_fun(*this, &ChannelListTree::OnThreadJoined)); + discord.signal_removed_from_thread().connect(sigc::mem_fun(*this, &ChannelListTree::OnThreadRemoved)); + discord.signal_guild_update().connect(sigc::mem_fun(*this, &ChannelListTree::UpdateGuild)); + discord.signal_message_ack().connect(sigc::mem_fun(*this, &ChannelListTree::OnMessageAck)); + discord.signal_channel_muted().connect(sigc::mem_fun(*this, &ChannelListTree::OnChannelMute)); + discord.signal_channel_unmuted().connect(sigc::mem_fun(*this, &ChannelListTree::OnChannelUnmute)); + discord.signal_guild_muted().connect(sigc::mem_fun(*this, &ChannelListTree::OnGuildMute)); + discord.signal_guild_unmuted().connect(sigc::mem_fun(*this, &ChannelListTree::OnGuildUnmute)); #if WITH_VOICE - discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &ChannelList::OnVoiceUserConnect)); - discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &ChannelList::OnVoiceUserDisconnect)); - discord.signal_voice_state_set().connect(sigc::mem_fun(*this, &ChannelList::OnVoiceStateSet)); + discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &ChannelListTree::OnVoiceUserConnect)); + discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &ChannelListTree::OnVoiceUserDisconnect)); + discord.signal_voice_state_set().connect(sigc::mem_fun(*this, &ChannelListTree::OnVoiceStateSet)); #endif } -void ChannelList::UsePanedHack(Gtk::Paned &paned) { - paned.property_position().signal_changed().connect(sigc::mem_fun(*this, &ChannelList::OnPanedPositionChanged)); +void ChannelListTree::UsePanedHack(Gtk::Paned &paned) { + paned.property_position().signal_changed().connect(sigc::mem_fun(*this, &ChannelListTree::OnPanedPositionChanged)); } -void ChannelList::OnPanedPositionChanged() { +void ChannelListTree::OnPanedPositionChanged() { m_view.queue_draw(); } -void ChannelList::UpdateListing() { +void ChannelListTree::UpdateListing() { m_updating_listing = true; m_model->clear(); @@ -375,7 +375,7 @@ void ChannelList::UpdateListing() { } // TODO update for folders -void ChannelList::UpdateNewGuild(const GuildData &guild) { +void ChannelListTree::UpdateNewGuild(const GuildData &guild) { AddGuild(guild, m_model->children()); // update sort order int sortnum = 0; @@ -386,19 +386,19 @@ void ChannelList::UpdateNewGuild(const GuildData &guild) { } } -void ChannelList::UpdateRemoveGuild(Snowflake id) { +void ChannelListTree::UpdateRemoveGuild(Snowflake id) { auto iter = GetIteratorForGuildFromID(id); if (!iter) return; m_model->erase(iter); } -void ChannelList::UpdateRemoveChannel(Snowflake id) { +void ChannelListTree::UpdateRemoveChannel(Snowflake id) { auto iter = GetIteratorForRowFromID(id); if (!iter) return; m_model->erase(iter); } -void ChannelList::UpdateChannel(Snowflake id) { +void ChannelListTree::UpdateChannel(Snowflake id) { auto iter = GetIteratorForRowFromID(id); auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); if (!iter || !channel.has_value()) return; @@ -422,7 +422,7 @@ void ChannelList::UpdateChannel(Snowflake id) { MoveRow(iter, new_parent); } -void ChannelList::UpdateCreateChannel(const ChannelData &channel) { +void ChannelListTree::UpdateCreateChannel(const ChannelData &channel) { if (channel.Type == ChannelType::GUILD_CATEGORY) return (void)UpdateCreateChannelCategory(channel); if (channel.Type == ChannelType::DM || channel.Type == ChannelType::GROUP_DM) return UpdateCreateDMChannel(channel); if (channel.Type != ChannelType::GUILD_TEXT && channel.Type != ChannelType::GUILD_NEWS) return; @@ -448,7 +448,7 @@ void ChannelList::UpdateCreateChannel(const ChannelData &channel) { channel_row[m_columns.m_sort] = *channel.Position; } -void ChannelList::UpdateGuild(Snowflake id) { +void ChannelListTree::UpdateGuild(Snowflake id) { auto iter = GetIteratorForGuildFromID(id); auto &img = Abaddon::Get().GetImageManager(); const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id); @@ -472,7 +472,7 @@ void ChannelList::UpdateGuild(Snowflake id) { } } -void ChannelList::OnThreadJoined(Snowflake id) { +void ChannelListTree::OnThreadJoined(Snowflake id) { if (GetIteratorForRowFromID(id)) return; const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); if (!channel.has_value()) return; @@ -481,16 +481,16 @@ void ChannelList::OnThreadJoined(Snowflake id) { CreateThreadRow(parent->children(), *channel); } -void ChannelList::OnThreadRemoved(Snowflake id) { +void ChannelListTree::OnThreadRemoved(Snowflake id) { DeleteThreadRow(id); } -void ChannelList::OnThreadDelete(const ThreadDeleteData &data) { +void ChannelListTree::OnThreadDelete(const ThreadDeleteData &data) { DeleteThreadRow(data.ID); } // todo probably make the row stick around if its selected until the selection changes -void ChannelList::OnThreadUpdate(const ThreadUpdateData &data) { +void ChannelListTree::OnThreadUpdate(const ThreadUpdateData &data) { auto iter = GetIteratorForRowFromID(data.Thread.ID); if (iter) (*iter)[m_columns.m_name] = "- " + Glib::Markup::escape_text(*data.Thread.Name); @@ -499,7 +499,7 @@ void ChannelList::OnThreadUpdate(const ThreadUpdateData &data) { DeleteThreadRow(data.Thread.ID); } -void ChannelList::OnThreadListSync(const ThreadListSyncData &data) { +void ChannelListTree::OnThreadListSync(const ThreadListSyncData &data) { // get the threads in the guild std::vector threads; auto guild_iter = GetIteratorForGuildFromID(data.GuildID); @@ -535,7 +535,7 @@ void ChannelList::OnThreadListSync(const ThreadListSyncData &data) { } #ifdef WITH_VOICE -void ChannelList::OnVoiceUserConnect(Snowflake user_id, Snowflake channel_id) { +void ChannelListTree::OnVoiceUserConnect(Snowflake user_id, Snowflake channel_id) { auto parent_iter = GetIteratorForRowFromIDOfType(channel_id, RenderType::VoiceChannel); if (!parent_iter) parent_iter = GetIteratorForRowFromIDOfType(channel_id, RenderType::DM); if (!parent_iter) return; @@ -545,48 +545,48 @@ void ChannelList::OnVoiceUserConnect(Snowflake user_id, Snowflake channel_id) { CreateVoiceParticipantRow(*user, parent_iter->children()); } -void ChannelList::OnVoiceUserDisconnect(Snowflake user_id, Snowflake channel_id) { +void ChannelListTree::OnVoiceUserDisconnect(Snowflake user_id, Snowflake channel_id) { if (auto iter = GetIteratorForRowFromIDOfType(user_id, RenderType::VoiceParticipant)) { m_model->erase(iter); } } -void ChannelList::OnVoiceStateSet(Snowflake user_id, Snowflake channel_id, VoiceStateFlags flags) { +void ChannelListTree::OnVoiceStateSet(Snowflake user_id, Snowflake channel_id, VoiceStateFlags flags) { if (auto iter = GetIteratorForRowFromIDOfType(user_id, RenderType::VoiceParticipant)) { (*iter)[m_columns.m_voice_flags] = flags; } } #endif -void ChannelList::DeleteThreadRow(Snowflake id) { +void ChannelListTree::DeleteThreadRow(Snowflake id) { auto iter = GetIteratorForRowFromID(id); if (iter) m_model->erase(iter); } -void ChannelList::OnChannelMute(Snowflake id) { +void ChannelListTree::OnChannelMute(Snowflake id) { if (auto iter = GetIteratorForRowFromID(id)) m_model->row_changed(m_model->get_path(iter), iter); } -void ChannelList::OnChannelUnmute(Snowflake id) { +void ChannelListTree::OnChannelUnmute(Snowflake id) { if (auto iter = GetIteratorForRowFromID(id)) m_model->row_changed(m_model->get_path(iter), iter); } -void ChannelList::OnGuildMute(Snowflake id) { +void ChannelListTree::OnGuildMute(Snowflake id) { if (auto iter = GetIteratorForGuildFromID(id)) m_model->row_changed(m_model->get_path(iter), iter); } -void ChannelList::OnGuildUnmute(Snowflake id) { +void ChannelListTree::OnGuildUnmute(Snowflake id) { if (auto iter = GetIteratorForGuildFromID(id)) m_model->row_changed(m_model->get_path(iter), iter); } // create a temporary channel row for non-joined threads // and delete them when the active channel switches off of them if still not joined -void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { +void ChannelListTree::SetActiveChannel(Snowflake id, bool expand_to) { // mark channel as read when switching off if (m_active_channel.IsValid()) Abaddon::Get().GetDiscordClient().MarkChannelAsRead(m_active_channel, [](...) {}); @@ -627,16 +627,16 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { } } -void ChannelList::UseExpansionState(const ExpansionStateRoot &root) { +void ChannelListTree::UseExpansionState(const ExpansionStateRoot &root) { } -ExpansionStateRoot ChannelList::GetExpansionState() const { +ExpansionStateRoot ChannelListTree::GetExpansionState() const { ExpansionStateRoot r; return r; } -Gtk::TreeModel::iterator ChannelList::AddFolder(const UserSettingsGuildFoldersEntry &folder) { +Gtk::TreeModel::iterator ChannelListTree::AddFolder(const UserSettingsGuildFoldersEntry &folder) { if (!folder.ID.has_value()) { // just a guild if (!folder.GuildIDs.empty()) { @@ -673,7 +673,7 @@ Gtk::TreeModel::iterator ChannelList::AddFolder(const UserSettingsGuildFoldersEn return {}; } -Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk::TreeNodeChildren &root) { +Gtk::TreeModel::iterator ChannelListTree::AddGuild(const GuildData &guild, const Gtk::TreeNodeChildren &root) { auto &discord = Abaddon::Get().GetDiscordClient(); auto &img = Abaddon::Get().GetImageManager(); @@ -800,7 +800,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk return guild_row; } -Gtk::TreeModel::iterator ChannelList::UpdateCreateChannelCategory(const ChannelData &channel) { +Gtk::TreeModel::iterator ChannelListTree::UpdateCreateChannelCategory(const ChannelData &channel) { const auto iter = GetIteratorForGuildFromID(*channel.GuildID); if (!iter) return {}; @@ -814,7 +814,7 @@ Gtk::TreeModel::iterator ChannelList::UpdateCreateChannelCategory(const ChannelD return cat_row; } -Gtk::TreeModel::iterator ChannelList::CreateThreadRow(const Gtk::TreeNodeChildren &children, const ChannelData &channel) { +Gtk::TreeModel::iterator ChannelListTree::CreateThreadRow(const Gtk::TreeNodeChildren &children, const ChannelData &channel) { auto thread_iter = m_model->append(children); auto thread_row = *thread_iter; thread_row[m_columns.m_type] = RenderType::Thread; @@ -827,7 +827,7 @@ Gtk::TreeModel::iterator ChannelList::CreateThreadRow(const Gtk::TreeNodeChildre } #ifdef WITH_VOICE -Gtk::TreeModel::iterator ChannelList::CreateVoiceParticipantRow(const UserData &user, const Gtk::TreeNodeChildren &parent) { +Gtk::TreeModel::iterator ChannelListTree::CreateVoiceParticipantRow(const UserData &user, const Gtk::TreeNodeChildren &parent) { auto row = *m_model->append(parent); row[m_columns.m_type] = RenderType::VoiceParticipant; row[m_columns.m_id] = user.ID; @@ -850,7 +850,7 @@ Gtk::TreeModel::iterator ChannelList::CreateVoiceParticipantRow(const UserData & } #endif -void ChannelList::UpdateChannelCategory(const ChannelData &channel) { +void ChannelListTree::UpdateChannelCategory(const ChannelData &channel) { auto iter = GetIteratorForRowFromID(channel.ID); if (!iter) return; @@ -859,7 +859,7 @@ void ChannelList::UpdateChannelCategory(const ChannelData &channel) { } // todo this all needs refactoring for shooore -Gtk::TreeModel::iterator ChannelList::GetIteratorForTopLevelFromID(Snowflake id) { +Gtk::TreeModel::iterator ChannelListTree::GetIteratorForTopLevelFromID(Snowflake id) { for (const auto &child : m_model->children()) { if ((child[m_columns.m_type] == RenderType::Guild || child[m_columns.m_type] == RenderType::Folder) && child[m_columns.m_id] == id) { return child; @@ -874,7 +874,7 @@ Gtk::TreeModel::iterator ChannelList::GetIteratorForTopLevelFromID(Snowflake id) return {}; } -Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) { +Gtk::TreeModel::iterator ChannelListTree::GetIteratorForGuildFromID(Snowflake id) { for (const auto &child : m_model->children()) { if (child[m_columns.m_type] == RenderType::Guild && child[m_columns.m_id] == id) { return child; @@ -889,7 +889,7 @@ Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) { return {}; } -Gtk::TreeModel::iterator ChannelList::GetIteratorForRowFromID(Snowflake id) { +Gtk::TreeModel::iterator ChannelListTree::GetIteratorForRowFromID(Snowflake id) { std::queue queue; for (const auto &child : m_model->children()) for (const auto &child2 : child.children()) @@ -906,7 +906,7 @@ Gtk::TreeModel::iterator ChannelList::GetIteratorForRowFromID(Snowflake id) { return {}; } -Gtk::TreeModel::iterator ChannelList::GetIteratorForRowFromIDOfType(Snowflake id, RenderType type) { +Gtk::TreeModel::iterator ChannelListTree::GetIteratorForRowFromIDOfType(Snowflake id, RenderType type) { std::queue queue; for (const auto &child : m_model->children()) for (const auto &child2 : child.children()) @@ -923,16 +923,16 @@ Gtk::TreeModel::iterator ChannelList::GetIteratorForRowFromIDOfType(Snowflake id return {}; } -bool ChannelList::IsTextChannel(ChannelType type) { +bool ChannelListTree::IsTextChannel(ChannelType type) { return type == ChannelType::GUILD_TEXT || type == ChannelType::GUILD_NEWS; } // this should be unncessary but something is behaving strange so its just in case -void ChannelList::OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) const { +void ChannelListTree::OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) const { (*iter)[m_columns.m_expanded] = false; } -void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) { +void ChannelListTree::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) { // restore previous expansion for (auto it = iter->children().begin(); it != iter->children().end(); it++) { if ((*it)[m_columns.m_expanded]) @@ -947,7 +947,7 @@ void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk: (*iter)[m_columns.m_expanded] = true; } -bool ChannelList::SelectionFunc(const Glib::RefPtr &model, const Gtk::TreeModel::Path &path, bool is_currently_selected) { +bool ChannelListTree::SelectionFunc(const Glib::RefPtr &model, const Gtk::TreeModel::Path &path, bool is_currently_selected) { if (auto selection = m_view.get_selection()) { if (auto row = selection->get_selected()) { m_last_selected = m_filter_model->get_path(row); @@ -958,7 +958,7 @@ bool ChannelList::SelectionFunc(const Glib::RefPtr &model, const return type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread; } -void ChannelList::AddPrivateChannels() { +void ChannelListTree::AddPrivateChannels() { auto header_row = *m_model->append(); header_row[m_columns.m_type] = RenderType::DMHeader; header_row[m_columns.m_sort] = -1; @@ -999,7 +999,7 @@ void ChannelList::AddPrivateChannels() { } } -void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) { +void ChannelListTree::UpdateCreateDMChannel(const ChannelData &dm) { auto header_row = m_model->get_iter(m_dm_header); auto &img = Abaddon::Get().GetImageManager(); @@ -1014,7 +1014,7 @@ void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) { SetDMChannelIcon(iter, dm); } -void ChannelList::SetDMChannelIcon(Gtk::TreeIter iter, const ChannelData &dm) { +void ChannelListTree::SetDMChannelIcon(Gtk::TreeIter iter, const ChannelData &dm) { auto &img = Abaddon::Get().GetImageManager(); std::optional top_recipient; @@ -1071,7 +1071,7 @@ void ChannelList::SetDMChannelIcon(Gtk::TreeIter iter, const ChannelData &dm) { } } -void ChannelList::RedrawUnreadIndicatorsForChannel(const ChannelData &channel) { +void ChannelListTree::RedrawUnreadIndicatorsForChannel(const ChannelData &channel) { if (channel.GuildID.has_value()) { auto iter = GetIteratorForGuildFromID(*channel.GuildID); if (iter) m_model->row_changed(m_model->get_path(iter), iter); @@ -1082,7 +1082,7 @@ void ChannelList::RedrawUnreadIndicatorsForChannel(const ChannelData &channel) { } } -void ChannelList::OnMessageAck(const MessageAckData &data) { +void ChannelListTree::OnMessageAck(const MessageAckData &data) { // trick renderer into redrawing m_model->row_changed(Gtk::TreeModel::Path("0"), m_model->get_iter("0")); // 0 is always path for dm header auto iter = GetIteratorForRowFromID(data.ChannelID); @@ -1093,7 +1093,7 @@ void ChannelList::OnMessageAck(const MessageAckData &data) { } } -void ChannelList::OnMessageCreate(const Message &msg) { +void ChannelListTree::OnMessageCreate(const Message &msg) { auto iter = GetIteratorForRowFromID(msg.ChannelID); if (iter) m_model->row_changed(m_model->get_path(iter), iter); // redraw const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(msg.ChannelID); @@ -1105,7 +1105,7 @@ void ChannelList::OnMessageCreate(const Message &msg) { RedrawUnreadIndicatorsForChannel(*channel); } -bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) { +bool ChannelListTree::OnButtonPressEvent(GdkEventButton *ev) { if (ev->button == GDK_BUTTON_SECONDARY && ev->type == GDK_BUTTON_PRESS) { if (m_view.get_path_at_pos(static_cast(ev->x), static_cast(ev->y), m_path_for_menu)) { auto row = (*m_filter_model->get_iter(m_path_for_menu)); @@ -1152,7 +1152,7 @@ bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) { return false; } -void ChannelList::MoveRow(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::iterator &new_parent) { +void ChannelListTree::MoveRow(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::iterator &new_parent) { // duplicate the row data under the new parent and then delete the old row auto row = *m_model->append(new_parent->children()); // would be nice to be able to get all columns out at runtime so i dont need this @@ -1180,7 +1180,7 @@ void ChannelList::MoveRow(const Gtk::TreeModel::iterator &iter, const Gtk::TreeM m_model->erase(iter); } -void ChannelList::OnGuildSubmenuPopup() { +void ChannelListTree::OnGuildSubmenuPopup() { const auto iter = m_model->get_iter(m_path_for_menu); if (!iter) return; const auto id = static_cast((*iter)[m_columns.m_id]); @@ -1195,7 +1195,7 @@ void ChannelList::OnGuildSubmenuPopup() { m_menu_guild_leave.set_sensitive(!(guild.has_value() && guild->OwnerID == self_id)); } -void ChannelList::OnCategorySubmenuPopup() { +void ChannelListTree::OnCategorySubmenuPopup() { const auto iter = m_model->get_iter(m_path_for_menu); if (!iter) return; const auto id = static_cast((*iter)[m_columns.m_id]); @@ -1205,7 +1205,7 @@ void ChannelList::OnCategorySubmenuPopup() { m_menu_category_toggle_mute.set_label("Mute"); } -void ChannelList::OnChannelSubmenuPopup() { +void ChannelListTree::OnChannelSubmenuPopup() { const auto iter = m_model->get_iter(m_path_for_menu); if (!iter) return; const auto id = static_cast((*iter)[m_columns.m_id]); @@ -1221,7 +1221,7 @@ void ChannelList::OnChannelSubmenuPopup() { } #ifdef WITH_VOICE -void ChannelList::OnVoiceChannelSubmenuPopup() { +void ChannelListTree::OnVoiceChannelSubmenuPopup() { const auto iter = m_model->get_iter(m_path_for_menu); if (!iter) return; const auto id = static_cast((*iter)[m_columns.m_id]); @@ -1236,7 +1236,7 @@ void ChannelList::OnVoiceChannelSubmenuPopup() { } #endif -void ChannelList::OnDMSubmenuPopup() { +void ChannelListTree::OnDMSubmenuPopup() { auto iter = m_model->get_iter(m_path_for_menu); if (!iter) return; const auto id = static_cast((*iter)[m_columns.m_id]); @@ -1257,7 +1257,7 @@ void ChannelList::OnDMSubmenuPopup() { #endif } -void ChannelList::OnThreadSubmenuPopup() { +void ChannelListTree::OnThreadSubmenuPopup() { m_menu_thread_archive.set_visible(false); m_menu_thread_unarchive.set_visible(false); @@ -1279,35 +1279,35 @@ void ChannelList::OnThreadSubmenuPopup() { m_menu_thread_unarchive.set_visible(channel->ThreadMetadata->IsArchived); } -ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() { +ChannelListTree::type_signal_action_channel_item_select ChannelListTree::signal_action_channel_item_select() { return m_signal_action_channel_item_select; } -ChannelList::type_signal_action_guild_leave ChannelList::signal_action_guild_leave() { +ChannelListTree::type_signal_action_guild_leave ChannelListTree::signal_action_guild_leave() { return m_signal_action_guild_leave; } -ChannelList::type_signal_action_guild_settings ChannelList::signal_action_guild_settings() { +ChannelListTree::type_signal_action_guild_settings ChannelListTree::signal_action_guild_settings() { return m_signal_action_guild_settings; } #ifdef WITH_LIBHANDY -ChannelList::type_signal_action_open_new_tab ChannelList::signal_action_open_new_tab() { +ChannelListTree::type_signal_action_open_new_tab ChannelListTree::signal_action_open_new_tab() { return m_signal_action_open_new_tab; } #endif #ifdef WITH_VOICE -ChannelList::type_signal_action_join_voice_channel ChannelList::signal_action_join_voice_channel() { +ChannelListTree::type_signal_action_join_voice_channel ChannelListTree::signal_action_join_voice_channel() { return m_signal_action_join_voice_channel; } -ChannelList::type_signal_action_disconnect_voice ChannelList::signal_action_disconnect_voice() { +ChannelListTree::type_signal_action_disconnect_voice ChannelListTree::signal_action_disconnect_voice() { return m_signal_action_disconnect_voice; } #endif -ChannelList::ModelColumns::ModelColumns() { +ChannelListTree::ModelColumns::ModelColumns() { add(m_type); add(m_id); add(m_name); diff --git a/src/components/channellist/channellist.hpp b/src/components/channellist/channellisttree.hpp similarity index 99% rename from src/components/channellist/channellist.hpp rename to src/components/channellist/channellisttree.hpp index 8c644c95..0cd76e04 100644 --- a/src/components/channellist/channellist.hpp +++ b/src/components/channellist/channellisttree.hpp @@ -20,9 +20,9 @@ constexpr static int DMIconSize = 20; constexpr static int VoiceParticipantIconSize = 18; constexpr static int OrphanChannelSortOffset = -100; // forces orphan channels to the top of the list -class ChannelList : public Gtk::ScrolledWindow { +class ChannelListTree : public Gtk::ScrolledWindow { public: - ChannelList(); + ChannelListTree(); void UpdateListing(); void SetActiveChannel(Snowflake id, bool expand_to); diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index 8156659a..729dcc50 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -1,5 +1,5 @@ #include "mainwindow.hpp" -#include "components/channellist/channellist.hpp" +#include "components/channellist/channellisttree.hpp" MainWindow::MainWindow() : m_main_box(Gtk::ORIENTATION_VERTICAL) @@ -235,7 +235,7 @@ void MainWindow::OnViewSubmenuPopup() { } } -ChannelList *MainWindow::GetChannelList() { +ChannelListTree *MainWindow::GetChannelList() { return &m_channel_list; } diff --git a/src/windows/mainwindow.hpp b/src/windows/mainwindow.hpp index 37c1b87a..19222b87 100644 --- a/src/windows/mainwindow.hpp +++ b/src/windows/mainwindow.hpp @@ -1,5 +1,5 @@ #pragma once -#include "components/channellist/channellist.hpp" +#include "components/channellist/channellisttree.hpp" #include "components/chatwindow.hpp" #include "components/memberlist.hpp" #include "components/friendslist.hpp" @@ -39,7 +39,7 @@ class MainWindow : public Gtk::Window { void GoToTab(int idx); #endif - ChannelList *GetChannelList(); + ChannelListTree *GetChannelList(); ChatWindow *GetChatWindow(); MemberList *GetMemberList(); @@ -54,7 +54,7 @@ class MainWindow : public Gtk::Window { Gtk::Paned m_chan_content_paned; Gtk::Paned m_content_members_paned; - ChannelList m_channel_list; + ChannelListTree m_channel_list; ChatWindow m_chat; MemberList m_members; FriendsList m_friends; From 8b034e48e27e4889b08ed648745a74d05630a431 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 24 Oct 2023 21:55:26 -0400 Subject: [PATCH 05/48] add intermediate container for channel list --- src/components/channellist/channellist.cpp | 50 ++++++++++++++++++ src/components/channellist/channellist.hpp | 61 ++++++++++++++++++++++ src/windows/mainwindow.cpp | 3 +- src/windows/mainwindow.hpp | 6 +-- 4 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 src/components/channellist/channellist.cpp create mode 100644 src/components/channellist/channellist.hpp diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp new file mode 100644 index 00000000..38a9ecc8 --- /dev/null +++ b/src/components/channellist/channellist.cpp @@ -0,0 +1,50 @@ +#include "channellist.hpp" + +ChannelList::ChannelList() { + m_tree.show(); + add(m_tree); +} + +void ChannelList::UpdateListing() { + m_tree.UpdateListing(); +} + +void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { + m_tree.SetActiveChannel(id, expand_to); +} + +void ChannelList::UseExpansionState(const ExpansionStateRoot &state) { + m_tree.UseExpansionState(state); +} + +ExpansionStateRoot ChannelList::GetExpansionState() const { + m_tree.GetExpansionState(); +} + +void ChannelList::UsePanedHack(Gtk::Paned &paned) { + m_tree.UsePanedHack(paned); +} + +ChannelList::type_signal_action_open_new_tab ChannelList::signal_action_open_new_tab() { + return m_signal_action_open_new_tab; +} + +ChannelList::type_signal_action_join_voice_channel ChannelList::signal_action_join_voice_channel() { + return m_signal_action_join_voice_channel; +} + +ChannelList::type_signal_action_disconnect_voice ChannelList::signal_action_disconnect_voice() { + return m_signal_action_disconnect_voice; +} + +ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() { + return m_signal_action_channel_item_select; +} + +ChannelList::type_signal_action_guild_leave ChannelList::signal_action_guild_leave() { + return m_signal_action_guild_leave; +} + +ChannelList::type_signal_action_guild_settings ChannelList::signal_action_guild_settings() { + return m_signal_action_guild_settings; +} diff --git a/src/components/channellist/channellist.hpp b/src/components/channellist/channellist.hpp new file mode 100644 index 00000000..5863485b --- /dev/null +++ b/src/components/channellist/channellist.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include "channellisttree.hpp" +#include "discord/snowflake.hpp" +#include "state.hpp" + +// Contains the actual ChannelListTree and the classic listing if enabled +class ChannelList : public Gtk::Box { + // have to proxy public and signals to underlying tree... ew!!! +public: + ChannelList(); + + void UpdateListing(); + void SetActiveChannel(Snowflake id, bool expand_to); + + // channel list should be populated when this is called + void UseExpansionState(const ExpansionStateRoot &state); + ExpansionStateRoot GetExpansionState() const; + + void UsePanedHack(Gtk::Paned &paned); + +private: + ChannelListTree m_tree; + +public: + using type_signal_action_channel_item_select = sigc::signal; + using type_signal_action_guild_leave = sigc::signal; + using type_signal_action_guild_settings = sigc::signal; + +#ifdef WITH_LIBHANDY + using type_signal_action_open_new_tab = sigc::signal; + type_signal_action_open_new_tab signal_action_open_new_tab(); +#endif + +#ifdef WITH_VOICE + using type_signal_action_join_voice_channel = sigc::signal; + using type_signal_action_disconnect_voice = sigc::signal; + + type_signal_action_join_voice_channel signal_action_join_voice_channel(); + type_signal_action_disconnect_voice signal_action_disconnect_voice(); +#endif + + type_signal_action_channel_item_select signal_action_channel_item_select(); + type_signal_action_guild_leave signal_action_guild_leave(); + type_signal_action_guild_settings signal_action_guild_settings(); + +private: + type_signal_action_channel_item_select m_signal_action_channel_item_select; + type_signal_action_guild_leave m_signal_action_guild_leave; + type_signal_action_guild_settings m_signal_action_guild_settings; + +#ifdef WITH_LIBHANDY + type_signal_action_open_new_tab m_signal_action_open_new_tab; +#endif + +#ifdef WITH_VOICE + type_signal_action_join_voice_channel m_signal_action_join_voice_channel; + type_signal_action_disconnect_voice m_signal_action_disconnect_voice; +#endif +}; diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index 729dcc50..8e030edc 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -1,5 +1,4 @@ #include "mainwindow.hpp" -#include "components/channellist/channellisttree.hpp" MainWindow::MainWindow() : m_main_box(Gtk::ORIENTATION_VERTICAL) @@ -235,7 +234,7 @@ void MainWindow::OnViewSubmenuPopup() { } } -ChannelListTree *MainWindow::GetChannelList() { +ChannelList *MainWindow::GetChannelList() { return &m_channel_list; } diff --git a/src/windows/mainwindow.hpp b/src/windows/mainwindow.hpp index 19222b87..37c1b87a 100644 --- a/src/windows/mainwindow.hpp +++ b/src/windows/mainwindow.hpp @@ -1,5 +1,5 @@ #pragma once -#include "components/channellist/channellisttree.hpp" +#include "components/channellist/channellist.hpp" #include "components/chatwindow.hpp" #include "components/memberlist.hpp" #include "components/friendslist.hpp" @@ -39,7 +39,7 @@ class MainWindow : public Gtk::Window { void GoToTab(int idx); #endif - ChannelListTree *GetChannelList(); + ChannelList *GetChannelList(); ChatWindow *GetChatWindow(); MemberList *GetMemberList(); @@ -54,7 +54,7 @@ class MainWindow : public Gtk::Window { Gtk::Paned m_chan_content_paned; Gtk::Paned m_content_members_paned; - ChannelListTree m_channel_list; + ChannelList m_channel_list; ChatWindow m_chat; MemberList m_members; FriendsList m_friends; From 182128705e10adf529b39a290232f0cd27bf4837 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 24 Oct 2023 22:45:31 -0400 Subject: [PATCH 06/48] very very rudimentary classic channels list --- src/components/channellist/channellist.cpp | 50 ++++++++++++++++++- src/components/channellist/channellist.hpp | 10 +++- .../channellist/channellisttree.cpp | 14 +++++- .../channellist/channellisttree.hpp | 6 +++ .../channellist/classic/guildlist.cpp | 24 ++++++++- .../channellist/classic/guildlist.hpp | 12 ++++- .../classic/guildlistguilditem.cpp | 7 --- src/windows/mainwindow.cpp | 2 + 8 files changed, 112 insertions(+), 13 deletions(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 38a9ecc8..6cb53213 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -1,12 +1,26 @@ #include "channellist.hpp" ChannelList::ChannelList() { + ConnectSignals(); + + m_guilds.set_halign(Gtk::ALIGN_START); + + m_guilds_scroll.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + + m_guilds.signal_guild_selected().connect([this](Snowflake guild_id) { + m_tree.SetSelectedGuild(guild_id); + }); + + m_guilds.show(); m_tree.show(); - add(m_tree); + m_guilds_scroll.add(m_guilds); + pack_start(m_guilds_scroll, false, false); // only take the space it needs + pack_start(m_tree, true, true); // use all the remaining space } void ChannelList::UpdateListing() { m_tree.UpdateListing(); + m_guilds.UpdateListing(); } void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { @@ -18,13 +32,45 @@ void ChannelList::UseExpansionState(const ExpansionStateRoot &state) { } ExpansionStateRoot ChannelList::GetExpansionState() const { - m_tree.GetExpansionState(); + return m_tree.GetExpansionState(); } void ChannelList::UsePanedHack(Gtk::Paned &paned) { m_tree.UsePanedHack(paned); } +void ChannelList::SetClassic(bool value) { + m_tree.SetClassic(value); + m_guilds_scroll.set_visible(value); +} + +void ChannelList::ConnectSignals() { + // TODO: if these all just travel upwards to the singleton then get rid of them but mayeb they dont + m_tree.signal_action_open_new_tab().connect([this](Snowflake id) { + m_signal_action_open_new_tab.emit(id); + }); + + m_tree.signal_action_join_voice_channel().connect([this](Snowflake id) { + m_signal_action_join_voice_channel.emit(id); + }); + + m_tree.signal_action_disconnect_voice().connect([this]() { + m_signal_action_disconnect_voice.emit(); + }); + + m_tree.signal_action_channel_item_select().connect([this](Snowflake id) { + m_signal_action_channel_item_select.emit(id); + }); + + m_tree.signal_action_guild_leave().connect([this](Snowflake id) { + m_signal_action_guild_leave.emit(id); + }); + + m_tree.signal_action_guild_settings().connect([this](Snowflake id) { + m_signal_action_guild_settings.emit(id); + }); +} + ChannelList::type_signal_action_open_new_tab ChannelList::signal_action_open_new_tab() { return m_signal_action_open_new_tab; } diff --git a/src/components/channellist/channellist.hpp b/src/components/channellist/channellist.hpp index 5863485b..692afa7d 100644 --- a/src/components/channellist/channellist.hpp +++ b/src/components/channellist/channellist.hpp @@ -2,11 +2,12 @@ #include #include #include "channellisttree.hpp" +#include "classic/guildlist.hpp" #include "discord/snowflake.hpp" #include "state.hpp" // Contains the actual ChannelListTree and the classic listing if enabled -class ChannelList : public Gtk::Box { +class ChannelList : public Gtk::HBox { // have to proxy public and signals to underlying tree... ew!!! public: ChannelList(); @@ -20,9 +21,16 @@ class ChannelList : public Gtk::Box { void UsePanedHack(Gtk::Paned &paned); + void SetClassic(bool value); + private: + void ConnectSignals(); + ChannelListTree m_tree; + Gtk::ScrolledWindow m_guilds_scroll; + GuildList m_guilds; + public: using type_signal_action_channel_item_select = sigc::signal; using type_signal_action_guild_leave = sigc::signal; diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 22f85814..b9b5c085 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -85,8 +85,10 @@ ChannelListTree::ChannelListTree() }); m_filter_model->set_visible_func([this](const Gtk::TreeModel::const_iterator &iter) -> bool { + if (!m_classic) return true; + if ((*iter)[m_columns.m_type] == RenderType::Guild) { - return (*iter)[m_columns.m_id] == 754921263616753776ULL; + return (*iter)[m_columns.m_id] == m_classic_selected_guild; } return true; }); @@ -301,6 +303,16 @@ void ChannelListTree::UsePanedHack(Gtk::Paned &paned) { paned.property_position().signal_changed().connect(sigc::mem_fun(*this, &ChannelListTree::OnPanedPositionChanged)); } +void ChannelListTree::SetClassic(bool value) { + m_classic = value; + m_filter_model->refilter(); +} + +void ChannelListTree::SetSelectedGuild(Snowflake guild_id) { + m_classic_selected_guild = guild_id; + m_filter_model->refilter(); +} + void ChannelListTree::OnPanedPositionChanged() { m_view.queue_draw(); } diff --git a/src/components/channellist/channellisttree.hpp b/src/components/channellist/channellisttree.hpp index 0cd76e04..bb7396e6 100644 --- a/src/components/channellist/channellisttree.hpp +++ b/src/components/channellist/channellisttree.hpp @@ -33,6 +33,9 @@ class ChannelListTree : public Gtk::ScrolledWindow { void UsePanedHack(Gtk::Paned &paned); + void SetClassic(bool value); + void SetSelectedGuild(Snowflake guild_id); + protected: void OnPanedPositionChanged(); @@ -186,6 +189,9 @@ class ChannelListTree : public Gtk::ScrolledWindow { bool m_updating_listing = false; + bool m_classic = false; + Snowflake m_classic_selected_guild; + Snowflake m_active_channel; public: diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index 0d5737b4..6c1fbc31 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -6,18 +6,40 @@ GuildList::GuildList() { show_all_children(); } +void GuildList::UpdateListing() { + auto &discord = Abaddon::Get().GetDiscordClient(); + + Clear(); + + // does this function still even work ??lol + const auto ids = discord.GetUserSortedGuilds(); + for (const auto id : ids) { + AddGuild(id); + } +} + void GuildList::AddGuild(Snowflake id) { const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id); if (!guild.has_value()) return; auto *item = Gtk::make_managed(*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); + } + return true; + }); item->show(); add(*item); } void GuildList::Clear() { const auto children = get_children(); - for (auto child : children) { + for (auto *child : children) { delete child; } } + +GuildList::type_signal_guild_selected GuildList::signal_guild_selected() { + return m_signal_guild_selected; +} diff --git a/src/components/channellist/classic/guildlist.hpp b/src/components/channellist/classic/guildlist.hpp index 244313fb..856cf4d3 100644 --- a/src/components/channellist/classic/guildlist.hpp +++ b/src/components/channellist/classic/guildlist.hpp @@ -6,7 +6,17 @@ class GuildList : public Gtk::ListBox { public: GuildList(); - void AddGuild(Snowflake id); + void UpdateListing(); +private: + void AddGuild(Snowflake id); void Clear(); + +public: + using type_signal_guild_selected = sigc::signal; + + type_signal_guild_selected signal_guild_selected(); + +private: + type_signal_guild_selected m_signal_guild_selected; }; diff --git a/src/components/channellist/classic/guildlistguilditem.cpp b/src/components/channellist/classic/guildlistguilditem.cpp index 88ed2a96..5cec281d 100644 --- a/src/components/channellist/classic/guildlistguilditem.cpp +++ b/src/components/channellist/classic/guildlistguilditem.cpp @@ -6,13 +6,6 @@ GuildListGuildItem::GuildListGuildItem(const GuildData &guild) add(m_image); show_all_children(); - signal_button_press_event().connect([this](GdkEventButton *event) -> bool { - if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { - printf("Click %llu\n", (uint64_t)ID); - } - return true; - }); - set_tooltip_text(guild.Name); UpdateIcon(); diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index 8e030edc..ea67d2b3 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -33,6 +33,8 @@ MainWindow::MainWindow() }); #endif + // TEMP TEMP TEMP TEMP!!!!!!!!!!!! AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH + m_channel_list.SetClassic(true); m_channel_list.set_vexpand(true); m_channel_list.set_size_request(-1, -1); m_channel_list.show(); From 2168f011f59b4947d802dceac3ce60b51fee0b2f Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 26 Oct 2023 00:58:57 -0400 Subject: [PATCH 07/48] fix build --- src/components/channellist/channellist.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 6cb53213..de19a805 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -46,10 +46,14 @@ void ChannelList::SetClassic(bool value) { void ChannelList::ConnectSignals() { // TODO: if these all just travel upwards to the singleton then get rid of them but mayeb they dont + +#ifdef WITH_LIBHANDY m_tree.signal_action_open_new_tab().connect([this](Snowflake id) { m_signal_action_open_new_tab.emit(id); }); +#endif +#ifdef WITH_VOICE m_tree.signal_action_join_voice_channel().connect([this](Snowflake id) { m_signal_action_join_voice_channel.emit(id); }); @@ -57,6 +61,7 @@ void ChannelList::ConnectSignals() { m_tree.signal_action_disconnect_voice().connect([this]() { m_signal_action_disconnect_voice.emit(); }); +#endif m_tree.signal_action_channel_item_select().connect([this](Snowflake id) { m_signal_action_channel_item_select.emit(id); @@ -71,10 +76,13 @@ void ChannelList::ConnectSignals() { }); } +#ifdef WITH_LIBHANDY ChannelList::type_signal_action_open_new_tab ChannelList::signal_action_open_new_tab() { return m_signal_action_open_new_tab; } +#endif +#ifdef WITH_VOICE ChannelList::type_signal_action_join_voice_channel ChannelList::signal_action_join_voice_channel() { return m_signal_action_join_voice_channel; } @@ -82,6 +90,7 @@ ChannelList::type_signal_action_join_voice_channel ChannelList::signal_action_jo ChannelList::type_signal_action_disconnect_voice ChannelList::signal_action_disconnect_voice() { return m_signal_action_disconnect_voice; } +#endif ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() { return m_signal_action_channel_item_select; From 09cfa864be6e706fd2576eee60a285d5f4431b04 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 26 Oct 2023 01:20:10 -0400 Subject: [PATCH 08/48] create folders in guild list --- .../channellist/classic/guildlist.cpp | 55 +++++++++++++++++-- .../channellist/classic/guildlist.hpp | 6 ++ .../classic/guildlistfolderitem.cpp | 7 +++ .../classic/guildlistfolderitem.hpp | 4 ++ 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index 6c1fbc31..7a4b87c6 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -12,15 +12,37 @@ void GuildList::UpdateListing() { Clear(); // does this function still even work ??lol - const auto ids = discord.GetUserSortedGuilds(); - for (const auto id : ids) { - AddGuild(id); + const auto folders = discord.GetUserSettings().GuildFolders; + const auto guild_ids = discord.GetUserSortedGuilds(); + + // same logic from ChannelListTree + + std::set 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; + if (!guild.has_value()) return nullptr; auto *item = Gtk::make_managed(*guild); item->signal_button_press_event().connect([this, id](GdkEventButton *event) -> bool { @@ -29,8 +51,29 @@ void GuildList::AddGuild(Snowflake id) { } return true; }); - item->show(); - add(*item); + + 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(); + 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() { diff --git a/src/components/channellist/classic/guildlist.hpp b/src/components/channellist/classic/guildlist.hpp index 856cf4d3..dc19102d 100644 --- a/src/components/channellist/classic/guildlist.hpp +++ b/src/components/channellist/classic/guildlist.hpp @@ -1,6 +1,9 @@ #pragma once #include #include "discord/snowflake.hpp" +#include "discord/usersettings.hpp" + +class GuildListGuildItem; class GuildList : public Gtk::ListBox { public: @@ -10,8 +13,11 @@ class GuildList : public Gtk::ListBox { private: void AddGuild(Snowflake id); + void AddFolder(const UserSettingsGuildFoldersEntry &folder); void Clear(); + GuildListGuildItem *CreateGuildWidget(Snowflake id); + public: using type_signal_guild_selected = sigc::signal; diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index 36d5a5a1..aead8483 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -1,9 +1,12 @@ #include "guildlistfolderitem.hpp" +#include "guildlistguilditem.hpp" GuildListFolderItem::GuildListFolderItem() { m_revealer.add(m_box); m_revealer.set_reveal_child(true); + 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()); @@ -16,3 +19,7 @@ GuildListFolderItem::GuildListFolderItem() { add(m_revealer); show_all_children(); } + +void GuildListFolderItem::AddGuildWidget(GuildListGuildItem *widget) { + m_box.add(*widget); +} diff --git a/src/components/channellist/classic/guildlistfolderitem.hpp b/src/components/channellist/classic/guildlistfolderitem.hpp index 3506969d..4b7ba0a2 100644 --- a/src/components/channellist/classic/guildlistfolderitem.hpp +++ b/src/components/channellist/classic/guildlistfolderitem.hpp @@ -6,10 +6,14 @@ #include "guildlistguilditem.hpp" +class GuildListGuildItem; + class GuildListFolderItem : public Gtk::VBox { public: GuildListFolderItem(); + void AddGuildWidget(GuildListGuildItem *widget); + private: Gtk::EventBox m_ev; Gtk::Image m_image; From 1abe572c5bac5fcc1328286981e7e58f0f7a30f8 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 27 Oct 2023 01:49:29 -0400 Subject: [PATCH 09/48] convert menu path from filter to child immediately --- src/components/channellist/channellisttree.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index b9b5c085..2a4970d3 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -1120,7 +1120,9 @@ void ChannelListTree::OnMessageCreate(const Message &msg) { bool ChannelListTree::OnButtonPressEvent(GdkEventButton *ev) { if (ev->button == GDK_BUTTON_SECONDARY && ev->type == GDK_BUTTON_PRESS) { if (m_view.get_path_at_pos(static_cast(ev->x), static_cast(ev->y), m_path_for_menu)) { - auto row = (*m_filter_model->get_iter(m_path_for_menu)); + m_path_for_menu = m_filter_model->convert_path_to_child_path(m_path_for_menu); + if (!m_path_for_menu) return true; + auto row = (*m_model->get_iter(m_path_for_menu)); switch (static_cast(row[m_columns.m_type])) { case RenderType::Guild: OnGuildSubmenuPopup(); From 6fc3624e3bfc1ae2f30a922c3a86533e50e99def Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 28 Oct 2023 22:51:47 -0400 Subject: [PATCH 10/48] dont create folders in tree in classic, tmp remove expansion --- .../channellist/channellisttree.cpp | 35 +++++++++++++------ .../channellist/channellisttree.hpp | 2 ++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 2a4970d3..c166d42b 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -317,7 +317,30 @@ void ChannelListTree::OnPanedPositionChanged() { m_view.queue_draw(); } +void ChannelListTree::UpdateListingClassic() { + m_updating_listing = true; + + m_model->clear(); + + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto guild_ids = discord.GetUserSortedGuilds(); + for (const auto guild_id : guild_ids) { + if (const auto guild = discord.GetGuild(guild_id); guild.has_value()) { + AddGuild(*guild, m_model->children()); + } + } + + m_updating_listing = false; + + AddPrivateChannels(); +} + void ChannelListTree::UpdateListing() { + if (m_classic) { + UpdateListingClassic(); + return; + } + m_updating_listing = true; m_model->clear(); @@ -599,6 +622,8 @@ void ChannelListTree::OnGuildUnmute(Snowflake id) { // create a temporary channel row for non-joined threads // and delete them when the active channel switches off of them if still not joined void ChannelListTree::SetActiveChannel(Snowflake id, bool expand_to) { + while (Gtk::Main::events_pending()) Gtk::Main::iteration(); + // mark channel as read when switching off if (m_active_channel.IsValid()) Abaddon::Get().GetDiscordClient().MarkChannelAsRead(m_active_channel, [](...) {}); @@ -615,16 +640,6 @@ void ChannelListTree::SetActiveChannel(Snowflake id, bool expand_to) { const auto channel_iter = GetIteratorForRowFromID(id); if (channel_iter) { - if (expand_to) { - const auto filter_path = m_filter_model->convert_child_path_to_path(m_model->get_path(channel_iter)); - if (filter_path) { - m_view.expand_to_path(filter_path); - } - } - const auto filter_iter = m_filter_model->convert_child_iter_to_iter(channel_iter); - if (filter_iter) { - m_view.get_selection()->select(filter_iter); - } } else { m_view.get_selection()->unselect_all(); const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); diff --git a/src/components/channellist/channellisttree.hpp b/src/components/channellist/channellisttree.hpp index bb7396e6..a138bece 100644 --- a/src/components/channellist/channellisttree.hpp +++ b/src/components/channellist/channellisttree.hpp @@ -39,6 +39,8 @@ class ChannelListTree : public Gtk::ScrolledWindow { protected: void OnPanedPositionChanged(); + void UpdateListingClassic(); + void UpdateNewGuild(const GuildData &guild); void UpdateRemoveGuild(Snowflake id); void UpdateRemoveChannel(Snowflake id); From 8bd628c1776c20dbf41d2fb18f6204c4a398da0e Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 3 Nov 2023 01:00:29 -0400 Subject: [PATCH 11/48] show guild icon previews as grid in folder button --- .../channellist/classic/guildlist.cpp | 2 +- .../classic/guildlistfolderitem.cpp | 53 +++++++++++++++++-- .../classic/guildlistfolderitem.hpp | 3 +- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index 7a4b87c6..97741cf2 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -64,7 +64,7 @@ void GuildList::AddFolder(const UserSettingsGuildFoldersEntry &folder) { return; } - auto *folder_widget = Gtk::make_managed(); + auto *folder_widget = Gtk::make_managed(folder); for (const auto guild_id : folder.GuildIDs) { if (auto *guild_widget = CreateGuildWidget(guild_id)) { guild_widget->show(); diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index aead8483..acf1a2cc 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -1,7 +1,46 @@ #include "guildlistfolderitem.hpp" #include "guildlistguilditem.hpp" -GuildListFolderItem::GuildListFolderItem() { +// doing my best to copy discord here + +const int FolderGridButtonSize = 48; +const int FolderGridImageSize = 24; + +class GuildListFolderButton : public Gtk::Grid { +public: + GuildListFolderButton() { + set_size_request(FolderGridButtonSize, FolderGridButtonSize); + } + + void SetGuilds(const std::vector &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()) { + const auto cb = [&widget](const Glib::RefPtr &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)); + } + } + } + } + } + +private: + Gtk::Image m_images[2][2]; +}; + +GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder) { + get_style_context()->add_class("classic-guild-folder"); + m_revealer.add(m_box); m_revealer.set_reveal_child(true); @@ -14,10 +53,18 @@ GuildListFolderItem::GuildListFolderItem() { return false; }); - m_ev.add(m_image); + auto *btn = Gtk::make_managed(); + btn->SetGuilds(folder.GuildIDs); + m_ev.add(*btn); add(m_ev); add(m_revealer); - show_all_children(); + + btn->show(); + m_ev.show(); + m_revealer.show(); + m_box.show(); + m_image.show(); + show(); } void GuildListFolderItem::AddGuildWidget(GuildListGuildItem *widget) { diff --git a/src/components/channellist/classic/guildlistfolderitem.hpp b/src/components/channellist/classic/guildlistfolderitem.hpp index 4b7ba0a2..460fd072 100644 --- a/src/components/channellist/classic/guildlistfolderitem.hpp +++ b/src/components/channellist/classic/guildlistfolderitem.hpp @@ -5,12 +5,13 @@ #include #include "guildlistguilditem.hpp" +#include "discord/usersettings.hpp" class GuildListGuildItem; class GuildListFolderItem : public Gtk::VBox { public: - GuildListFolderItem(); + GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder); void AddGuildWidget(GuildListGuildItem *widget); From 3095155d63acbb0144ad123730a85e5a81eb113b Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 31 Dec 2023 01:34:45 -0500 Subject: [PATCH 12/48] add selection and expansion back to channel list on open --- src/components/channellist/channellisttree.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 0e2cda62..b66beb08 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -639,6 +639,14 @@ void ChannelListTree::SetActiveChannel(Snowflake id, bool expand_to) { const auto channel_iter = GetIteratorForRowFromID(id); if (channel_iter) { + m_view.get_selection()->unselect_all(); + const auto filter_iter = m_filter_model->convert_child_iter_to_iter(channel_iter); + if (filter_iter) { + if (expand_to) { + m_view.expand_to_path(m_filter_model->get_path(filter_iter)); + } + m_view.get_selection()->select(filter_iter); + } } else { m_view.get_selection()->unselect_all(); const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); From 6f8417525883acfb77af856f44aee26c1477ca91 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 8 Jan 2024 02:59:34 -0500 Subject: [PATCH 13/48] dont reveal folders by default --- src/components/channellist/classic/guildlistfolderitem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index acf1a2cc..b6e49f15 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -42,7 +42,7 @@ GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &fo get_style_context()->add_class("classic-guild-folder"); m_revealer.add(m_box); - m_revealer.set_reveal_child(true); + m_revealer.set_reveal_child(false); m_image.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(48); From b700aa85d82b0af391e9fef84202488a779bc304 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 8 Jan 2024 03:09:30 -0500 Subject: [PATCH 14/48] explicitly expand guild row when switching to it in classic view --- src/components/channellist/channellisttree.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index b66beb08..6741a69e 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -310,6 +310,12 @@ void ChannelListTree::SetClassic(bool value) { void ChannelListTree::SetSelectedGuild(Snowflake guild_id) { m_classic_selected_guild = guild_id; m_filter_model->refilter(); + auto guild_iter = GetIteratorForGuildFromID(guild_id); + if (guild_iter) { + if (auto filter_iter = m_filter_model->convert_child_iter_to_iter(guild_iter)) { + m_view.expand_row(m_filter_model->get_path(filter_iter), false); + } + } } void ChannelListTree::OnPanedPositionChanged() { From 9131158cbbf5800dd103d5b5fbfff96384352c77 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:53:40 -0500 Subject: [PATCH 15/48] add stack with icon and grid for classic listing folder icon --- .../classic/guildlistfolderitem.cpp | 71 +++++++++++-------- .../classic/guildlistfolderitem.hpp | 13 ++++ src/settings.cpp | 1 + src/settings.hpp | 1 + 4 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index b6e49f15..e60bd5f0 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -6,37 +6,31 @@ const int FolderGridButtonSize = 48; const int FolderGridImageSize = 24; -class GuildListFolderButton : public Gtk::Grid { -public: - GuildListFolderButton() { - set_size_request(FolderGridButtonSize, FolderGridButtonSize); - } +GuildListFolderButton::GuildListFolderButton() { + set_size_request(FolderGridButtonSize, FolderGridButtonSize); +} + +void GuildListFolderButton::SetGuilds(const std::vector &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); - void SetGuilds(const std::vector &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()) { - const auto cb = [&widget](const Glib::RefPtr &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)); - } + if (i < guild_ids.size()) { + widget.show(); + + if (const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(guild_ids[i]); guild.has_value()) { + const auto cb = [&widget](const Glib::RefPtr &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)); } } } } - -private: - Gtk::Image m_images[2][2]; -}; +} GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder) { get_style_context()->add_class("classic-guild-folder"); @@ -49,17 +43,34 @@ GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &fo 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; }); - auto *btn = Gtk::make_managed(); - btn->SetGuilds(folder.GuildIDs); - m_ev.add(*btn); + m_grid.SetGuilds(folder.GuildIDs); + m_grid.show(); + + m_icon.property_icon_name() = "folder-symbolic"; + m_icon.property_icon_size() = Gtk::ICON_SIZE_DND; + 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); - btn->show(); m_ev.show(); m_revealer.show(); m_box.show(); diff --git a/src/components/channellist/classic/guildlistfolderitem.hpp b/src/components/channellist/classic/guildlistfolderitem.hpp index 460fd072..6a9fb509 100644 --- a/src/components/channellist/classic/guildlistfolderitem.hpp +++ b/src/components/channellist/classic/guildlistfolderitem.hpp @@ -9,6 +9,15 @@ class GuildListGuildItem; +class GuildListFolderButton : public Gtk::Grid { +public: + GuildListFolderButton(); + void SetGuilds(const std::vector &guild_ids); + +private: + Gtk::Image m_images[2][2]; +}; + class GuildListFolderItem : public Gtk::VBox { public: GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder); @@ -16,6 +25,10 @@ class GuildListFolderItem : public Gtk::VBox { void AddGuildWidget(GuildListGuildItem *widget); private: + Gtk::Stack m_stack; + GuildListFolderButton m_grid; + Gtk::Image m_icon; + Gtk::EventBox m_ev; Gtk::Image m_image; Gtk::Revealer m_revealer; diff --git a/src/settings.cpp b/src/settings.cpp index 23b2b898..6b051e7d 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -96,6 +96,7 @@ void SettingsManager::DefineSettings() { AddSetting("gui", "hide_to_try", false, &Settings::HideToTray); AddSetting("gui", "show_deleted_indicator", true, &Settings::ShowDeletedIndicator); AddSetting("gui", "font_scale", -1.0, &Settings::FontScale); + AddSetting("gui", "folder_icon_only", false, &Settings::FolderIconOnly); AddSetting("http", "concurrent", 20, &Settings::CacheHTTPConcurrency); AddSetting("http", "user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"s, &Settings::UserAgent); diff --git a/src/settings.hpp b/src/settings.hpp index be9660e2..e5082703 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -28,6 +28,7 @@ class SettingsManager { bool HideToTray; bool ShowDeletedIndicator; double FontScale; + bool FolderIconOnly; // [http] int CacheHTTPConcurrency; From 1e6d16f44a07da92c35b19dadf6385c2868ab295 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:53:59 -0500 Subject: [PATCH 16/48] hide dm header unless selected in classic channel list --- src/components/channellist/channellist.cpp | 4 +++ .../channellist/channellisttree.cpp | 22 ++++++++++++++-- .../channellist/channellisttree.hpp | 2 ++ .../channellist/classic/guildlist.cpp | 26 +++++++++++++++++++ .../channellist/classic/guildlist.hpp | 3 +++ 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index de19a805..e4060a89 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -11,6 +11,10 @@ ChannelList::ChannelList() { m_tree.SetSelectedGuild(guild_id); }); + m_guilds.signal_dms_selected().connect([this]() { + m_tree.SetSelectedDMs(); + }); + m_guilds.show(); m_tree.show(); m_guilds_scroll.add(m_guilds); diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 6741a69e..cbfc8bb4 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -86,10 +86,17 @@ ChannelListTree::ChannelListTree() m_filter_model->set_visible_func([this](const Gtk::TreeModel::const_iterator &iter) -> bool { if (!m_classic) return true; - if ((*iter)[m_columns.m_type] == RenderType::Guild) { + const RenderType type = (*iter)[m_columns.m_type]; + + if (m_classic_selected_dms) { + if (iter->parent()) return true; + return type == RenderType::DMHeader; + } + + if (type == RenderType::Guild) { return (*iter)[m_columns.m_id] == m_classic_selected_guild; } - return true; + return type != RenderType::DMHeader; }); m_view.show(); @@ -309,6 +316,7 @@ void ChannelListTree::SetClassic(bool value) { void ChannelListTree::SetSelectedGuild(Snowflake guild_id) { m_classic_selected_guild = guild_id; + m_classic_selected_dms = false; m_filter_model->refilter(); auto guild_iter = GetIteratorForGuildFromID(guild_id); if (guild_iter) { @@ -318,6 +326,16 @@ void ChannelListTree::SetSelectedGuild(Snowflake guild_id) { } } +void ChannelListTree::SetSelectedDMs() { + m_classic_selected_dms = true; + m_filter_model->refilter(); + if (m_dm_header) { + if (auto filter_path = m_filter_model->convert_child_path_to_path(m_dm_header)) { + m_view.expand_row(filter_path, false); + } + } +} + void ChannelListTree::OnPanedPositionChanged() { m_view.queue_draw(); } diff --git a/src/components/channellist/channellisttree.hpp b/src/components/channellist/channellisttree.hpp index a138bece..7ad0d299 100644 --- a/src/components/channellist/channellisttree.hpp +++ b/src/components/channellist/channellisttree.hpp @@ -35,6 +35,7 @@ class ChannelListTree : public Gtk::ScrolledWindow { void SetClassic(bool value); void SetSelectedGuild(Snowflake guild_id); + void SetSelectedDMs(); protected: void OnPanedPositionChanged(); @@ -193,6 +194,7 @@ class ChannelListTree : public Gtk::ScrolledWindow { bool m_classic = false; Snowflake m_classic_selected_guild; + bool m_classic_selected_dms = false; Snowflake m_active_channel; diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index 97741cf2..ca7c96f2 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -1,6 +1,18 @@ #include "guildlist.hpp" #include "guildlistfolderitem.hpp" +class GuildListDMsButton : public Gtk::EventBox { +public: + GuildListDMsButton() { + m_img.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(48); + add(m_img); + show_all_children(); + } + +private: + Gtk::Image m_img; +}; + GuildList::GuildList() { set_selection_mode(Gtk::SELECTION_NONE); show_all_children(); @@ -11,6 +23,16 @@ void GuildList::UpdateListing() { Clear(); + auto *dms = Gtk::make_managed(); + 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(); @@ -86,3 +108,7 @@ void GuildList::Clear() { 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; +} diff --git a/src/components/channellist/classic/guildlist.hpp b/src/components/channellist/classic/guildlist.hpp index dc19102d..d76e80d9 100644 --- a/src/components/channellist/classic/guildlist.hpp +++ b/src/components/channellist/classic/guildlist.hpp @@ -20,9 +20,12 @@ class GuildList : public Gtk::ListBox { public: using type_signal_guild_selected = sigc::signal; + using type_signal_dms_selected = sigc::signal; type_signal_guild_selected signal_guild_selected(); + type_signal_dms_selected signal_dms_selected(); private: type_signal_guild_selected m_signal_guild_selected; + type_signal_dms_selected m_signal_dms_selected; }; From 781bdc5d7a72cacb9d0ce6e2893dcbba04088417 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:27:53 -0500 Subject: [PATCH 17/48] add folder colors to symbol icon --- src/components/channellist/classic/guildlistfolderitem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index e60bd5f0..f79227b6 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -60,6 +60,9 @@ GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &fo 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"); From fe95335d1245152bb23c6787b5d55ea5874fd78a Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 9 Jan 2024 00:26:40 -0500 Subject: [PATCH 18/48] set folder name tooltip --- src/components/channellist/classic/guildlistfolderitem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index f79227b6..e79a5f7c 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -35,6 +35,10 @@ void GuildListFolderButton::SetGuilds(const std::vector &guild_ids) { GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder) { get_style_context()->add_class("classic-guild-folder"); + if (folder.Name.has_value()) { + set_tooltip_text(*folder.Name); + } + m_revealer.add(m_box); m_revealer.set_reveal_child(false); From 615f2c80f1b0cfa7b72a08112ff4ef8ae23e5bf6 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 9 Jan 2024 00:57:50 -0500 Subject: [PATCH 19/48] add icon to dm button in classic channels list --- src/components/channellist/classic/guildlist.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index ca7c96f2..53ba1d78 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -4,7 +4,10 @@ class GuildListDMsButton : public Gtk::EventBox { public: GuildListDMsButton() { - m_img.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(48); + 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(); } From 09bf732a122760a1f1d6ba005d066d107c57edad Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 11 Jan 2024 21:28:55 -0500 Subject: [PATCH 20/48] switch to correct guild on channel open in classic channels --- README.md | 31 +++++++++++----------- src/components/channellist/channellist.cpp | 8 ++++++ src/settings.cpp | 1 + src/settings.hpp | 1 + 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 660323d4..d9bacb65 100644 --- a/README.md +++ b/README.md @@ -291,21 +291,22 @@ For example, memory_db would be set by adding `memory_db = true` under the line #### gui -| Setting | Type | Default | Description | -|-----------------------------|---------|---------|----------------------------------------------------------------------------------------------------------------------------| -| `member_list_discriminator` | boolean | true | show user discriminators in the member list | -| `stock_emojis` | boolean | true | allow abaddon to substitute unicode emojis with images from emojis.bin, must be false to allow GTK to render emojis itself | -| `custom_emojis` | boolean | true | download and use custom Discord emojis | -| `css` | string | | path to the main CSS file | -| `animations` | boolean | true | use animated images where available (e.g. server icons, emojis, avatars). false means static images will be used | -| `animated_guild_hover_only` | boolean | true | only animate guild icons when the guild is being hovered over | -| `owner_crown` | boolean | true | show a crown next to the owner | -| `unreads` | boolean | true | show unread indicators and mention badges | -| `save_state` | boolean | true | save the state of the gui (active channels, tabs, expanded channels) | -| `alt_menu` | boolean | false | keep the menu hidden unless revealed with alt key | -| `hide_to_tray` | boolean | false | hide abaddon to the system tray on window close | -| `show_deleted_indicator` | boolean | true | show \[deleted\] indicator next to deleted messages instead of actually deleting the message | -| `font_scale` | double | | scale font rendering. 1 is unchanged | +| Setting | Type | Default | Description | +|--------------------------------|---------|---------|----------------------------------------------------------------------------------------------------------------------------| +| `member_list_discriminator` | boolean | true | show user discriminators in the member list | +| `stock_emojis` | boolean | true | allow abaddon to substitute unicode emojis with images from emojis.bin, must be false to allow GTK to render emojis itself | +| `custom_emojis` | boolean | true | download and use custom Discord emojis | +| `css` | string | | path to the main CSS file | +| `animations` | boolean | true | use animated images where available (e.g. server icons, emojis, avatars). false means static images will be used | +| `animated_guild_hover_only` | boolean | true | only animate guild icons when the guild is being hovered over | +| `owner_crown` | boolean | true | show a crown next to the owner | +| `unreads` | boolean | true | show unread indicators and mention badges | +| `save_state` | boolean | true | save the state of the gui (active channels, tabs, expanded channels) | +| `alt_menu` | boolean | false | keep the menu hidden unless revealed with alt key | +| `hide_to_tray` | boolean | false | hide abaddon to the system tray on window close | +| `show_deleted_indicator` | boolean | true | show \[deleted\] indicator next to deleted messages instead of actually deleting the message | +| `font_scale` | double | | scale font rendering. 1 is unchanged | +| `classic_change_guild_on_open` | boolean | true | change displayed guild when selecting a channel (classic channel list) | #### style diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index e4060a89..873de412 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -28,6 +28,14 @@ void ChannelList::UpdateListing() { } void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { + if (Abaddon::Get().GetSettings().ClassicChangeGuildOnOpen) { + if (const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); channel.has_value() && channel->GuildID.has_value()) { + m_tree.SetSelectedGuild(*channel->GuildID); + } else { + m_tree.SetSelectedDMs(); + } + } + m_tree.SetActiveChannel(id, expand_to); } diff --git a/src/settings.cpp b/src/settings.cpp index 6b051e7d..36fffb0e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -97,6 +97,7 @@ void SettingsManager::DefineSettings() { AddSetting("gui", "show_deleted_indicator", true, &Settings::ShowDeletedIndicator); AddSetting("gui", "font_scale", -1.0, &Settings::FontScale); AddSetting("gui", "folder_icon_only", false, &Settings::FolderIconOnly); + AddSetting("gui", "classic_change_guild_on_open", true, &Settings::ClassicChangeGuildOnOpen); AddSetting("http", "concurrent", 20, &Settings::CacheHTTPConcurrency); AddSetting("http", "user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"s, &Settings::UserAgent); diff --git a/src/settings.hpp b/src/settings.hpp index e5082703..b8e27a49 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -29,6 +29,7 @@ class SettingsManager { bool ShowDeletedIndicator; double FontScale; bool FolderIconOnly; + bool ClassicChangeGuildOnOpen; // [http] int CacheHTTPConcurrency; From f38a893e441ca47ff5f5ca50995df567cac00f63 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 13 Jan 2024 22:29:18 -0500 Subject: [PATCH 21/48] make view channels option actually hide the panel --- src/windows/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index ea67d2b3..4579a6fe 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -394,7 +394,7 @@ void MainWindow::SetupMenu() { }); m_menu_view_channels.signal_activate().connect([this]() { - m_channel_list.set_visible(m_menu_view_channels.get_active()); + m_left_pane.set_visible(m_menu_view_channels.get_active()); }); m_menu_view_members.signal_activate().connect([this]() { From 960da872bbd583a5b7ddcfdf1a90b31cebebc8e6 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 14 Jan 2024 20:25:42 -0500 Subject: [PATCH 22/48] fix some criticals --- src/components/channellist/channellisttree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index cbfc8bb4..720fdd2a 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -84,7 +84,7 @@ ChannelListTree::ChannelListTree() }); m_filter_model->set_visible_func([this](const Gtk::TreeModel::const_iterator &iter) -> bool { - if (!m_classic) return true; + if (!m_classic || m_updating_listing) return true; const RenderType type = (*iter)[m_columns.m_type]; From 7c8e91169ea2e5b30bf354cea9e6cdf572a5e307 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 14 Jan 2024 20:25:50 -0500 Subject: [PATCH 23/48] save expansion state --- src/components/channellist/channellisttree.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 720fdd2a..b9c4d3c0 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -691,6 +691,23 @@ void ChannelListTree::UseExpansionState(const ExpansionStateRoot &root) { ExpansionStateRoot ChannelListTree::GetExpansionState() const { ExpansionStateRoot r; + auto recurse = [this](auto &self, const Gtk::TreeRow &row) -> ExpansionState { + ExpansionState r; + + r.IsExpanded = row[m_columns.m_expanded]; + for (auto child : row.children()) { + r.Children.Children[static_cast(child[m_columns.m_id])] = self(self, child); + } + + return r; + }; + + for (auto child : m_model->children()) { + const auto id = static_cast(child[m_columns.m_id]); + if (static_cast(id) == 0ULL) continue; // dont save DM header + r.Children[id] = recurse(recurse, child); + } + return r; } From af69c3d9f692fc0a975ff82540bc3c0e3b72bc5b Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 14 Jan 2024 21:36:52 -0500 Subject: [PATCH 24/48] fix more criticals --- src/components/channellist/channellisttree.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index b9c4d3c0..5a8b7831 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -343,6 +343,9 @@ void ChannelListTree::OnPanedPositionChanged() { void ChannelListTree::UpdateListingClassic() { m_updating_listing = true; + // refilter so every row is visible + // otherwise clear() causes a CRITICAL assert in a slot for the filter model + m_filter_model->refilter(); m_model->clear(); auto &discord = Abaddon::Get().GetDiscordClient(); From 6e847ea31f6f9ddbbdd4a6d283812516dc56ef1d Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:46:25 -0500 Subject: [PATCH 25/48] use expansion state --- .../channellist/channellisttree.cpp | 54 +++++++++++++++++++ .../channellist/channellisttree.hpp | 4 ++ 2 files changed, 58 insertions(+) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 5a8b7831..5b1a6322 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -689,6 +689,55 @@ void ChannelListTree::SetActiveChannel(Snowflake id, bool expand_to) { } void ChannelListTree::UseExpansionState(const ExpansionStateRoot &root) { + m_updating_listing = true; + m_filter_model->refilter(); + + auto recurse = [this](auto &self, const ExpansionStateRoot &root) -> void { + for (const auto &[id, state] : root.Children) { + Gtk::TreeModel::iterator row_iter; + if (const auto map_iter = m_tmp_row_map.find(id); map_iter != m_tmp_row_map.end()) { + row_iter = map_iter->second; + } else if (const auto map_iter = m_tmp_guild_row_map.find(id); map_iter != m_tmp_guild_row_map.end()) { + row_iter = map_iter->second; + } + + if (row_iter) { + (*row_iter)[m_columns.m_expanded] = state.IsExpanded; + auto filter_iter = m_filter_model->convert_child_iter_to_iter(row_iter); + if (filter_iter) { + if (state.IsExpanded) { + m_view.expand_row(m_filter_model->get_path(filter_iter), false); + } else { + m_view.collapse_row(m_filter_model->get_path(filter_iter)); + } + } + } + + self(self, state.Children); + } + }; + + for (const auto &[id, state] : root.Children) { + if (const auto iter = GetIteratorForTopLevelFromID(id)) { + (*iter)[m_columns.m_expanded] = state.IsExpanded; + auto filter_iter = m_filter_model->convert_child_iter_to_iter(iter); + if (filter_iter) { + if (state.IsExpanded) { + m_view.expand_row(m_filter_model->get_path(filter_iter), false); + } else { + m_view.collapse_row(m_filter_model->get_path(filter_iter)); + } + } + } + + recurse(recurse, state.Children); + } + + m_updating_listing = false; + m_filter_model->refilter(); + + m_tmp_row_map.clear(); + m_tmp_guild_row_map.clear(); } ExpansionStateRoot ChannelListTree::GetExpansionState() const { @@ -727,6 +776,7 @@ Gtk::TreeModel::iterator ChannelListTree::AddFolder(const UserSettingsGuildFolde auto folder_row = *m_model->append(); folder_row[m_columns.m_type] = RenderType::Folder; folder_row[m_columns.m_id] = *folder.ID; + m_tmp_row_map[*folder.ID] = folder_row; if (folder.Name.has_value()) { folder_row[m_columns.m_name] = Glib::Markup::escape_text(*folder.Name); } else { @@ -760,6 +810,7 @@ Gtk::TreeModel::iterator ChannelListTree::AddGuild(const GuildData &guild, const guild_row[m_columns.m_id] = guild.ID; guild_row[m_columns.m_name] = "" + Glib::Markup::escape_text(guild.Name) + ""; guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize); + m_tmp_guild_row_map[guild.ID] = guild_row; if (Abaddon::Get().GetSettings().ShowAnimations && guild.HasAnimatedIcon()) { const auto cb = [this, id = guild.ID](const Glib::RefPtr &pb) { @@ -842,6 +893,7 @@ Gtk::TreeModel::iterator ChannelListTree::AddGuild(const GuildData &guild, const channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset; channel_row[m_columns.m_nsfw] = channel.NSFW(); add_threads(channel, channel_row); + m_tmp_row_map[channel.ID] = channel_row; } for (const auto &[category_id, channels] : categories) { @@ -853,6 +905,7 @@ Gtk::TreeModel::iterator ChannelListTree::AddGuild(const GuildData &guild, const cat_row[m_columns.m_name] = Glib::Markup::escape_text(*category->Name); cat_row[m_columns.m_sort] = *category->Position; cat_row[m_columns.m_expanded] = true; + m_tmp_row_map[category_id] = cat_row; // m_view.expand_row wont work because it might not have channels for (const auto &channel : channels) { @@ -872,6 +925,7 @@ Gtk::TreeModel::iterator ChannelListTree::AddGuild(const GuildData &guild, const channel_row[m_columns.m_sort] = *channel.Position; channel_row[m_columns.m_nsfw] = channel.NSFW(); add_threads(channel, channel_row); + m_tmp_row_map[channel.ID] = channel_row; } } diff --git a/src/components/channellist/channellisttree.hpp b/src/components/channellist/channellisttree.hpp index 7ad0d299..b49187a4 100644 --- a/src/components/channellist/channellisttree.hpp +++ b/src/components/channellist/channellisttree.hpp @@ -198,6 +198,10 @@ class ChannelListTree : public Gtk::ScrolledWindow { Snowflake m_active_channel; + // hashtable for the billion lookups done in UseExpansionState + std::unordered_map m_tmp_row_map; + std::unordered_map m_tmp_guild_row_map; + public: using type_signal_action_channel_item_select = sigc::signal; using type_signal_action_guild_leave = sigc::signal; From d1f6e1daa99e70ab3a9d583a0de286c97ab13413 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 19 Jan 2024 01:29:25 -0500 Subject: [PATCH 26/48] headers --- src/components/channellist/channellist.cpp | 2 ++ src/components/channellist/channellisttree.cpp | 6 +++++- src/components/channellist/classic/guildlist.cpp | 2 ++ src/components/channellist/classic/guildlistfolderitem.cpp | 3 +++ src/components/channellist/classic/guildlistfolderitem.hpp | 2 ++ src/components/channellist/classic/guildlistguilditem.cpp | 2 ++ src/components/channellist/classic/guildlistguilditem.hpp | 1 + 7 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 873de412..1cdf6198 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -1,5 +1,7 @@ #include "channellist.hpp" +#include "abaddon.hpp" + ChannelList::ChannelList() { ConnectSignals(); diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index a509f21d..8049f580 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -1,9 +1,13 @@ #include "channellisttree.hpp" -#include "imgmanager.hpp" + #include #include #include + +#include + #include "abaddon.hpp" +#include "imgmanager.hpp" #include "util.hpp" ChannelListTree::ChannelListTree() diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index 53ba1d78..ae613bfa 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -1,4 +1,6 @@ #include "guildlist.hpp" + +#include "abaddon.hpp" #include "guildlistfolderitem.hpp" class GuildListDMsButton : public Gtk::EventBox { diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index e79a5f7c..fea735c7 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -1,5 +1,8 @@ #include "guildlistfolderitem.hpp" + +#include "abaddon.hpp" #include "guildlistguilditem.hpp" +#include "util.hpp" // doing my best to copy discord here diff --git a/src/components/channellist/classic/guildlistfolderitem.hpp b/src/components/channellist/classic/guildlistfolderitem.hpp index 6a9fb509..82a5a149 100644 --- a/src/components/channellist/classic/guildlistfolderitem.hpp +++ b/src/components/channellist/classic/guildlistfolderitem.hpp @@ -1,8 +1,10 @@ #pragma once #include #include +#include #include #include +#include #include "guildlistguilditem.hpp" #include "discord/usersettings.hpp" diff --git a/src/components/channellist/classic/guildlistguilditem.cpp b/src/components/channellist/classic/guildlistguilditem.cpp index 5cec281d..c608dd9b 100644 --- a/src/components/channellist/classic/guildlistguilditem.cpp +++ b/src/components/channellist/classic/guildlistguilditem.cpp @@ -1,5 +1,7 @@ #include "guildlistguilditem.hpp" +#include "abaddon.hpp" + GuildListGuildItem::GuildListGuildItem(const GuildData &guild) : ID(guild.ID) { m_image.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(48); diff --git a/src/components/channellist/classic/guildlistguilditem.hpp b/src/components/channellist/classic/guildlistguilditem.hpp index 3114a05b..6bc0037c 100644 --- a/src/components/channellist/classic/guildlistguilditem.hpp +++ b/src/components/channellist/classic/guildlistguilditem.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include "discord/guild.hpp" From 15457c49fea25a0347b0368d0707090ba5cf8c3f Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 20 Jan 2024 19:45:19 -0500 Subject: [PATCH 27/48] make classic channels a setting --- src/settings.cpp | 1 + src/settings.hpp | 1 + src/windows/mainwindow.cpp | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 4b45b894..654a726f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -103,6 +103,7 @@ void SettingsManager::DefineSettings() { AddSetting("gui", "classic_change_guild_on_open", true, &Settings::ClassicChangeGuildOnOpen); AddSetting("gui", "image_embed_clamp_width", 400, &Settings::ImageEmbedClampWidth); AddSetting("gui", "image_embed_clamp_height", 300, &Settings::ImageEmbedClampHeight); + AddSetting("gui", "classic_channels", false, &Settings::ClassicChannels); AddSetting("http", "concurrent", 20, &Settings::CacheHTTPConcurrency); AddSetting("http", "user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"s, &Settings::UserAgent); diff --git a/src/settings.hpp b/src/settings.hpp index 65b5b630..df723fcf 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -32,6 +32,7 @@ class SettingsManager { bool ClassicChangeGuildOnOpen; int ImageEmbedClampWidth; int ImageEmbedClampHeight; + bool ClassicChannels; // [http] int CacheHTTPConcurrency; diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index e8140518..ee28d175 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -36,8 +36,7 @@ MainWindow::MainWindow() }); #endif - // TEMP TEMP TEMP TEMP!!!!!!!!!!!! AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH - m_channel_list.SetClassic(true); + m_channel_list.SetClassic(Abaddon::Get().GetSettings().ClassicChannels); m_channel_list.set_vexpand(true); m_channel_list.set_size_request(-1, -1); m_channel_list.show(); From d7bee05ee92bf14c1344d336cfa5398815e8ccc9 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 20 Jan 2024 20:37:16 -0500 Subject: [PATCH 28/48] add settings for channel list scrollbar policies --- src/components/channellist/channellist.cpp | 4 +++- src/settings.cpp | 2 ++ src/settings.hpp | 2 ++ src/util.cpp | 8 ++++++++ src/util.hpp | 3 +++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 1cdf6198..2bb2f118 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -1,13 +1,15 @@ #include "channellist.hpp" #include "abaddon.hpp" +#include "util.hpp" ChannelList::ChannelList() { ConnectSignals(); m_guilds.set_halign(Gtk::ALIGN_START); - m_guilds_scroll.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + m_guilds_scroll.set_policy(Gtk::POLICY_NEVER, util::TranslateScrollPolicy(Abaddon::Get().GetSettings().ClassicGuildScrollPolicy)); + m_tree.set_policy(Gtk::POLICY_AUTOMATIC, util::TranslateScrollPolicy(Abaddon::Get().GetSettings().ClassicChannelScrollPolicy)); m_guilds.signal_guild_selected().connect([this](Snowflake guild_id) { m_tree.SetSelectedGuild(guild_id); diff --git a/src/settings.cpp b/src/settings.cpp index 654a726f..0b1ef49f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -113,6 +113,8 @@ void SettingsManager::DefineSettings() { AddSetting("style", "mentionbadgecolor", "rgba(184, 37, 37, 0)"s, &Settings::MentionBadgeColor); AddSetting("style", "mentionbadgetextcolor", "rgba(251, 251, 251, 0)"s, &Settings::MentionBadgeTextColor); AddSetting("style", "unreadcolor", "rgba(255, 255, 255, 0)"s, &Settings::UnreadIndicatorColor); + AddSetting("style", "classic_guild_scroll_policy", "automatic"s, &Settings::ClassicGuildScrollPolicy); + AddSetting("style", "classic_channel_scroll_policy", "automatic"s, &Settings::ClassicChannelScrollPolicy); #ifdef _WIN32 AddSetting("notifications", "enabled", false, &Settings::NotificationsEnabled); diff --git a/src/settings.hpp b/src/settings.hpp index df723fcf..d29c623f 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -44,6 +44,8 @@ class SettingsManager { std::string MentionBadgeColor; std::string MentionBadgeTextColor; std::string UnreadIndicatorColor; + std::string ClassicGuildScrollPolicy; + std::string ClassicChannelScrollPolicy; // [notifications] bool NotificationsEnabled; diff --git a/src/util.cpp b/src/util.cpp index 09bb368a..d0d6e246 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -228,3 +228,11 @@ uint64_t util::TimeToEpoch(int year, int month, int day, int hour, int minute, i secs += seconds; return secs; } + +Gtk::PolicyType util::TranslateScrollPolicy(const std::string &str) { + if (str == "never") return Gtk::POLICY_NEVER; + if (str == "automatic") return Gtk::POLICY_AUTOMATIC; + if (str == "always") return Gtk::POLICY_ALWAYS; + if (str == "external") return Gtk::POLICY_EXTERNAL; + return Gtk::POLICY_AUTOMATIC; +} diff --git a/src/util.hpp b/src/util.hpp index fc9568b4..f839eca1 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -14,6 +14,7 @@ #include #include +#include #include namespace Glib { @@ -38,6 +39,8 @@ bool IsFolder(std::string_view path); bool IsFile(std::string_view path); uint64_t TimeToEpoch(int year, int month, int day, int hour, int minute, int seconds); + +Gtk::PolicyType TranslateScrollPolicy(const std::string &str); } // namespace util void LaunchBrowser(const Glib::ustring &url); From 1d1bd7e3454b58ddf3b4d803bff8e9ed24ee4a22 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 21 Jan 2024 16:27:42 -0500 Subject: [PATCH 29/48] Revert "add settings for channel list scrollbar policies" This reverts commit d7bee05ee92bf14c1344d336cfa5398815e8ccc9. --- src/components/channellist/channellist.cpp | 4 +--- src/settings.cpp | 2 -- src/settings.hpp | 2 -- src/util.cpp | 8 -------- src/util.hpp | 3 --- 5 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 2bb2f118..1cdf6198 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -1,15 +1,13 @@ #include "channellist.hpp" #include "abaddon.hpp" -#include "util.hpp" ChannelList::ChannelList() { ConnectSignals(); m_guilds.set_halign(Gtk::ALIGN_START); - m_guilds_scroll.set_policy(Gtk::POLICY_NEVER, util::TranslateScrollPolicy(Abaddon::Get().GetSettings().ClassicGuildScrollPolicy)); - m_tree.set_policy(Gtk::POLICY_AUTOMATIC, util::TranslateScrollPolicy(Abaddon::Get().GetSettings().ClassicChannelScrollPolicy)); + m_guilds_scroll.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); m_guilds.signal_guild_selected().connect([this](Snowflake guild_id) { m_tree.SetSelectedGuild(guild_id); diff --git a/src/settings.cpp b/src/settings.cpp index 0b1ef49f..654a726f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -113,8 +113,6 @@ void SettingsManager::DefineSettings() { AddSetting("style", "mentionbadgecolor", "rgba(184, 37, 37, 0)"s, &Settings::MentionBadgeColor); AddSetting("style", "mentionbadgetextcolor", "rgba(251, 251, 251, 0)"s, &Settings::MentionBadgeTextColor); AddSetting("style", "unreadcolor", "rgba(255, 255, 255, 0)"s, &Settings::UnreadIndicatorColor); - AddSetting("style", "classic_guild_scroll_policy", "automatic"s, &Settings::ClassicGuildScrollPolicy); - AddSetting("style", "classic_channel_scroll_policy", "automatic"s, &Settings::ClassicChannelScrollPolicy); #ifdef _WIN32 AddSetting("notifications", "enabled", false, &Settings::NotificationsEnabled); diff --git a/src/settings.hpp b/src/settings.hpp index d29c623f..df723fcf 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -44,8 +44,6 @@ class SettingsManager { std::string MentionBadgeColor; std::string MentionBadgeTextColor; std::string UnreadIndicatorColor; - std::string ClassicGuildScrollPolicy; - std::string ClassicChannelScrollPolicy; // [notifications] bool NotificationsEnabled; diff --git a/src/util.cpp b/src/util.cpp index d0d6e246..09bb368a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -228,11 +228,3 @@ uint64_t util::TimeToEpoch(int year, int month, int day, int hour, int minute, i secs += seconds; return secs; } - -Gtk::PolicyType util::TranslateScrollPolicy(const std::string &str) { - if (str == "never") return Gtk::POLICY_NEVER; - if (str == "automatic") return Gtk::POLICY_AUTOMATIC; - if (str == "always") return Gtk::POLICY_ALWAYS; - if (str == "external") return Gtk::POLICY_EXTERNAL; - return Gtk::POLICY_AUTOMATIC; -} diff --git a/src/util.hpp b/src/util.hpp index f839eca1..fc9568b4 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -14,7 +14,6 @@ #include #include -#include #include namespace Glib { @@ -39,8 +38,6 @@ bool IsFolder(std::string_view path); bool IsFile(std::string_view path); uint64_t TimeToEpoch(int year, int month, int day, int hour, int minute, int seconds); - -Gtk::PolicyType TranslateScrollPolicy(const std::string &str); } // namespace util void LaunchBrowser(const Glib::ustring &url); From 9cd904638c687695fb88b209e3fbc6f0be8891ac Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 22 Jan 2024 20:32:54 -0500 Subject: [PATCH 30/48] add class to entire channel/server browser pane --- src/components/channellist/channellist.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 1cdf6198..105e0d07 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -3,6 +3,8 @@ #include "abaddon.hpp" ChannelList::ChannelList() { + get_style_context()->add_class("channel-browser-pane"); + ConnectSignals(); m_guilds.set_halign(Gtk::ALIGN_START); From 43b05527e7729bb8089d0079d122216f26a7eed3 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:01:01 -0500 Subject: [PATCH 31/48] add sort model in front of filter in channel tree --- .../channellist/channellisttree.cpp | 113 +++++++++++++----- .../channellist/channellisttree.hpp | 6 + 2 files changed, 88 insertions(+), 31 deletions(-) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 8049f580..42ff59fc 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -14,6 +14,7 @@ ChannelListTree::ChannelListTree() : Glib::ObjectBase(typeid(ChannelListTree)) , m_model(Gtk::TreeStore::create(m_columns)) , m_filter_model(Gtk::TreeModelFilter::create(m_model)) + , m_sort_model(Gtk::TreeModelSort::create(m_filter_model)) , m_menu_guild_copy_id("_Copy ID", true) , m_menu_guild_settings("View _Settings", true) , m_menu_guild_leave("_Leave", true) @@ -44,7 +45,10 @@ ChannelListTree::ChannelListTree() // Filter iters const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) { - auto row = *m_filter_model->get_iter(path); + auto view_path = ConvertViewPathToModel(path); + if (!view_path) return; + auto row = *m_model->get_iter(view_path); + if (!row) return; const auto type = row[m_columns.m_type]; // text channels should not be allowed to be collapsed // maybe they should be but it seems a little difficult to handle expansion to permit this @@ -78,14 +82,15 @@ ChannelListTree::ChannelListTree() m_view.set_show_expanders(false); m_view.set_enable_search(false); m_view.set_headers_visible(false); - m_view.set_model(m_filter_model); - m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING); + m_view.set_model(m_sort_model); + m_sort_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING); m_model->signal_row_inserted().connect([this](const Gtk::TreeModel::Path &path, const Gtk::TreeModel::iterator &iter) { if (m_updating_listing) return; if (auto parent = iter->parent(); parent && (*parent)[m_columns.m_expanded]) { - const auto filter_path = m_filter_model->convert_child_path_to_path(m_model->get_path(parent)); - m_view.expand_row(filter_path, false); + if (const auto view_path = ConvertModelPathToView(m_model->get_path(parent))) { + m_view.expand_row(view_path, false); + } } }); @@ -326,8 +331,8 @@ void ChannelListTree::SetSelectedGuild(Snowflake guild_id) { m_filter_model->refilter(); auto guild_iter = GetIteratorForGuildFromID(guild_id); if (guild_iter) { - if (auto filter_iter = m_filter_model->convert_child_iter_to_iter(guild_iter)) { - m_view.expand_row(m_filter_model->get_path(filter_iter), false); + if (auto view_iter = ConvertModelIterToView(guild_iter)) { + m_view.expand_row(GetViewPathFromViewIter(view_iter), false); } } } @@ -336,8 +341,8 @@ void ChannelListTree::SetSelectedDMs() { m_classic_selected_dms = true; m_filter_model->refilter(); if (m_dm_header) { - if (auto filter_path = m_filter_model->convert_child_path_to_path(m_dm_header)) { - m_view.expand_row(filter_path, false); + if (auto view_path = ConvertModelPathToView(m_dm_header)) { + m_view.expand_row(view_path, false); } } } @@ -673,12 +678,12 @@ void ChannelListTree::SetActiveChannel(Snowflake id, bool expand_to) { const auto channel_iter = GetIteratorForRowFromID(id); if (channel_iter) { m_view.get_selection()->unselect_all(); - const auto filter_iter = m_filter_model->convert_child_iter_to_iter(channel_iter); - if (filter_iter) { + const auto view_iter = ConvertModelIterToView(channel_iter); + if (view_iter) { if (expand_to) { - m_view.expand_to_path(m_filter_model->get_path(filter_iter)); + m_view.expand_to_path(GetViewPathFromViewIter(view_iter)); } - m_view.get_selection()->select(filter_iter); + m_view.get_selection()->select(view_iter); } } else { m_view.get_selection()->unselect_all(); @@ -687,9 +692,9 @@ void ChannelListTree::SetActiveChannel(Snowflake id, bool expand_to) { auto parent_iter = GetIteratorForRowFromID(*channel->ParentID); if (!parent_iter) return; m_temporary_thread_row = CreateThreadRow(parent_iter->children(), *channel); - const auto filter_iter = m_filter_model->convert_child_iter_to_iter(m_temporary_thread_row); - if (filter_iter) { - m_view.get_selection()->select(filter_iter); + const auto view_iter = ConvertModelIterToView(m_temporary_thread_row); + if (view_iter) { + m_view.get_selection()->select(view_iter); } } } @@ -709,12 +714,12 @@ void ChannelListTree::UseExpansionState(const ExpansionStateRoot &root) { if (row_iter) { (*row_iter)[m_columns.m_expanded] = state.IsExpanded; - auto filter_iter = m_filter_model->convert_child_iter_to_iter(row_iter); - if (filter_iter) { + auto view_iter = ConvertModelIterToView(row_iter); + if (view_iter) { if (state.IsExpanded) { - m_view.expand_row(m_filter_model->get_path(filter_iter), false); + m_view.expand_row(GetViewPathFromViewIter(view_iter), false); } else { - m_view.collapse_row(m_filter_model->get_path(filter_iter)); + m_view.collapse_row(GetViewPathFromViewIter(view_iter)); } } } @@ -725,13 +730,13 @@ void ChannelListTree::UseExpansionState(const ExpansionStateRoot &root) { for (const auto &[id, state] : root.Children) { if (const auto iter = GetIteratorForTopLevelFromID(id)) { - (*iter)[m_columns.m_expanded] = state.IsExpanded; - auto filter_iter = m_filter_model->convert_child_iter_to_iter(iter); - if (filter_iter) { + (*iter)[m_columns.m_expanded] = state.IsExpanded; + auto view_iter = ConvertModelIterToView(iter); + if (view_iter) { if (state.IsExpanded) { - m_view.expand_row(m_filter_model->get_path(filter_iter), false); + m_view.expand_row(GetViewPathFromViewIter(view_iter), false); } else { - m_view.collapse_row(m_filter_model->get_path(filter_iter)); + m_view.collapse_row(GetViewPathFromViewIter(view_iter)); } } } @@ -769,6 +774,50 @@ ExpansionStateRoot ChannelListTree::GetExpansionState() const { return r; } +Gtk::TreePath ChannelListTree::ConvertModelPathToView(const Gtk::TreePath &path) { + if (const auto filter_path = m_filter_model->convert_child_path_to_path(path)) { + if (const auto sort_path = m_sort_model->convert_child_path_to_path(filter_path)) { + return sort_path; + } + } + + return {}; +} + +Gtk::TreeIter ChannelListTree::ConvertModelIterToView(const Gtk::TreeIter &iter) { + if (const auto filter_iter = m_filter_model->convert_child_iter_to_iter(iter)) { + if (const auto sort_iter = m_sort_model->convert_child_iter_to_iter(filter_iter)) { + return sort_iter; + } + } + + return {}; +} + +Gtk::TreePath ChannelListTree::ConvertViewPathToModel(const Gtk::TreePath &path) { + if (const auto filter_path = m_sort_model->convert_path_to_child_path(path)) { + if (const auto model_path = m_filter_model->convert_path_to_child_path(filter_path)) { + return model_path; + } + } + + return {}; +} + +Gtk::TreeIter ChannelListTree::ConvertViewIterToModel(const Gtk::TreeIter &iter) { + if (const auto filter_iter = m_sort_model->convert_iter_to_child_iter(iter)) { + if (const auto model_iter = m_filter_model->convert_iter_to_child_iter(filter_iter)) { + return model_iter; + } + } + + return {}; +} + +Gtk::TreePath ChannelListTree::GetViewPathFromViewIter(const Gtk::TreeIter &iter) { + return m_sort_model->get_path(iter); +} + Gtk::TreeModel::iterator ChannelListTree::AddFolder(const UserSettingsGuildFoldersEntry &folder) { if (!folder.ID.has_value()) { // just a guild @@ -1072,9 +1121,11 @@ void ChannelListTree::OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const void ChannelListTree::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) { // restore previous expansion - for (auto it = iter->children().begin(); it != iter->children().end(); it++) { - if ((*it)[m_columns.m_expanded]) - m_view.expand_row(m_filter_model->get_path(it), false); + auto model_iter = ConvertViewIterToModel(iter); + for (auto it = model_iter->children().begin(); it != model_iter->children().end(); it++) { + if ((*it)[m_columns.m_expanded]) { + m_view.expand_row(GetViewPathFromViewIter(ConvertModelIterToView(it)), false); + } } // try and restore selection if previous collapsed @@ -1082,13 +1133,13 @@ void ChannelListTree::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const selection->select(m_last_selected); } - (*iter)[m_columns.m_expanded] = true; + (*model_iter)[m_columns.m_expanded] = true; } bool ChannelListTree::SelectionFunc(const Glib::RefPtr &model, const Gtk::TreeModel::Path &path, bool is_currently_selected) { if (auto selection = m_view.get_selection()) { if (auto row = selection->get_selected()) { - m_last_selected = m_filter_model->get_path(row); + m_last_selected = GetViewPathFromViewIter(row); } } @@ -1246,7 +1297,7 @@ void ChannelListTree::OnMessageCreate(const Message &msg) { bool ChannelListTree::OnButtonPressEvent(GdkEventButton *ev) { if (ev->button == GDK_BUTTON_SECONDARY && ev->type == GDK_BUTTON_PRESS) { if (m_view.get_path_at_pos(static_cast(ev->x), static_cast(ev->y), m_path_for_menu)) { - m_path_for_menu = m_filter_model->convert_path_to_child_path(m_path_for_menu); + m_path_for_menu = m_filter_model->convert_path_to_child_path(m_sort_model->convert_path_to_child_path(m_path_for_menu)); if (!m_path_for_menu) return true; auto row = (*m_model->get_iter(m_path_for_menu)); switch (static_cast(row[m_columns.m_type])) { diff --git a/src/components/channellist/channellisttree.hpp b/src/components/channellist/channellisttree.hpp index b49187a4..9d065ecc 100644 --- a/src/components/channellist/channellisttree.hpp +++ b/src/components/channellist/channellisttree.hpp @@ -90,7 +90,13 @@ class ChannelListTree : public Gtk::ScrolledWindow { ModelColumns m_columns; Glib::RefPtr m_model; Glib::RefPtr m_filter_model; + Glib::RefPtr m_sort_model; + Gtk::TreePath ConvertModelPathToView(const Gtk::TreePath &path); + Gtk::TreeIter ConvertModelIterToView(const Gtk::TreeIter &iter); + Gtk::TreePath ConvertViewPathToModel(const Gtk::TreePath &path); + Gtk::TreeIter ConvertViewIterToModel(const Gtk::TreeIter &iter); + Gtk::TreePath GetViewPathFromViewIter(const Gtk::TreeIter &iter); Gtk::TreeModel::iterator AddFolder(const UserSettingsGuildFoldersEntry &folder); Gtk::TreeModel::iterator AddGuild(const GuildData &guild, const Gtk::TreeNodeChildren &root); Gtk::TreeModel::iterator UpdateCreateChannelCategory(const ChannelData &channel); From dbfdd01b13af7394c0b6117e878728d0eac097c2 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:08:17 -0500 Subject: [PATCH 32/48] add missing guild.HasIcon checks --- src/components/channellist/classic/guildlistfolderitem.cpp | 2 +- src/components/channellist/classic/guildlistguilditem.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index fea735c7..2c8bc3b2 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -24,7 +24,7 @@ void GuildListFolderButton::SetGuilds(const std::vector &guild_ids) { if (i < guild_ids.size()) { widget.show(); - if (const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(guild_ids[i]); guild.has_value()) { + if (const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(guild_ids[i]); guild.has_value() && guild->HasIcon()) { const auto cb = [&widget](const Glib::RefPtr &pb) { widget.property_pixbuf() = pb->scale_simple(FolderGridImageSize, FolderGridImageSize, Gdk::INTERP_BILINEAR); }; diff --git a/src/components/channellist/classic/guildlistguilditem.cpp b/src/components/channellist/classic/guildlistguilditem.cpp index c608dd9b..2b26e731 100644 --- a/src/components/channellist/classic/guildlistguilditem.cpp +++ b/src/components/channellist/classic/guildlistguilditem.cpp @@ -15,7 +15,7 @@ GuildListGuildItem::GuildListGuildItem(const GuildData &guild) void GuildListGuildItem::UpdateIcon() { const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(ID); - if (!guild.has_value()) return; + if (!guild.has_value() || !guild->HasIcon()) return; Abaddon::Get().GetImageManager().LoadFromURL(guild->GetIconURL("png", "64"), sigc::mem_fun(*this, &GuildListGuildItem::OnIconFetched)); } From c41ff1e4d9b57092b2ce8ce46aa2cdce2abe639e Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 1 Feb 2024 21:21:56 -0500 Subject: [PATCH 33/48] dont populate classic guilds list if not enabled --- src/components/channellist/channellist.cpp | 3 ++- src/components/channellist/channellist.hpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 105e0d07..87aa6ed8 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -28,7 +28,7 @@ ChannelList::ChannelList() { void ChannelList::UpdateListing() { m_tree.UpdateListing(); - m_guilds.UpdateListing(); + if (m_is_classic) m_guilds.UpdateListing(); } void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { @@ -56,6 +56,7 @@ void ChannelList::UsePanedHack(Gtk::Paned &paned) { } void ChannelList::SetClassic(bool value) { + m_is_classic = value; m_tree.SetClassic(value); m_guilds_scroll.set_visible(value); } diff --git a/src/components/channellist/channellist.hpp b/src/components/channellist/channellist.hpp index 692afa7d..78d6372f 100644 --- a/src/components/channellist/channellist.hpp +++ b/src/components/channellist/channellist.hpp @@ -31,6 +31,8 @@ class ChannelList : public Gtk::HBox { Gtk::ScrolledWindow m_guilds_scroll; GuildList m_guilds; + bool m_is_classic = false; + public: using type_signal_action_channel_item_select = sigc::signal; using type_signal_action_guild_leave = sigc::signal; From 2be776b12f6124002fdf8a35b4bef19ce8e93bb1 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 2 Feb 2024 01:32:44 -0500 Subject: [PATCH 34/48] add sort func to sort model --- src/components/channellist/channellisttree.cpp | 14 ++++++++++++++ src/components/channellist/channellisttree.hpp | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 42ff59fc..3c82fc20 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -84,6 +84,7 @@ ChannelListTree::ChannelListTree() m_view.set_headers_visible(false); m_view.set_model(m_sort_model); m_sort_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING); + m_sort_model->set_sort_func(m_columns.m_sort, sigc::mem_fun(*this, &ChannelListTree::SortFunc)); m_model->signal_row_inserted().connect([this](const Gtk::TreeModel::Path &path, const Gtk::TreeModel::iterator &iter) { if (m_updating_listing) return; @@ -347,6 +348,19 @@ void ChannelListTree::SetSelectedDMs() { } } +int ChannelListTree::SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b) { + const RenderType a_type = (*a)[m_columns.m_type]; + const RenderType b_type = (*b)[m_columns.m_type]; + const int64_t a_sort = (*a)[m_columns.m_sort]; + const int64_t b_sort = (*b)[m_columns.m_sort]; + if (a_type == RenderType::DMHeader) return -1; + if (b_type == RenderType::DMHeader) return 1; + if (a_type == RenderType::TextChannel && b_type == RenderType::VoiceChannel) return -1; + if (b_type == RenderType::TextChannel && a_type == RenderType::VoiceChannel) return 1; + if (a_type == b_type) return static_cast(a_sort - b_sort); + return 0; +} + void ChannelListTree::OnPanedPositionChanged() { m_view.queue_draw(); } diff --git a/src/components/channellist/channellisttree.hpp b/src/components/channellist/channellisttree.hpp index 9d065ecc..136522b5 100644 --- a/src/components/channellist/channellisttree.hpp +++ b/src/components/channellist/channellisttree.hpp @@ -38,6 +38,8 @@ class ChannelListTree : public Gtk::ScrolledWindow { void SetSelectedDMs(); protected: + int SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b); + void OnPanedPositionChanged(); void UpdateListingClassic(); From b92091b97d1d43e33c3217451f72addbd0f76726 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 2 Feb 2024 02:00:32 -0500 Subject: [PATCH 35/48] hide scrollbar in classic guild list --- res/css/main.css | 11 +++++++++++ src/components/channellist/channellist.cpp | 1 + src/components/channellist/classic/guildlist.cpp | 1 + 3 files changed, 13 insertions(+) diff --git a/res/css/main.css b/res/css/main.css index ae7bf609..d29174ce 100644 --- a/res/css/main.css +++ b/res/css/main.css @@ -145,3 +145,14 @@ .message-text.failed { color: red; } + +.guild-list-scroll > scrollbar { + border: none; +} + +.guild-list-scroll > scrollbar slider { + border: none; + margin: 0px; + min-width: 0px; + min-height: 0px; +} diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 87aa6ed8..076a83e0 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -9,6 +9,7 @@ ChannelList::ChannelList() { m_guilds.set_halign(Gtk::ALIGN_START); + m_guilds_scroll.get_style_context()->add_class("guild-list-scroll"); m_guilds_scroll.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); m_guilds.signal_guild_selected().connect([this](Snowflake guild_id) { diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index ae613bfa..49ee87c7 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -19,6 +19,7 @@ class GuildListDMsButton : public Gtk::EventBox { }; GuildList::GuildList() { + get_style_context()->add_class("guild-list"); set_selection_mode(Gtk::SELECTION_NONE); show_all_children(); } From 746c24a3696f7a5b98a274679a9f45935b62c74d Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 2 Feb 2024 04:16:29 -0500 Subject: [PATCH 36/48] try fix build --- src/components/channellist/channellisttree.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 3c82fc20..59611c7b 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -355,8 +355,10 @@ int ChannelListTree::SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::Tree const int64_t b_sort = (*b)[m_columns.m_sort]; if (a_type == RenderType::DMHeader) return -1; if (b_type == RenderType::DMHeader) return 1; +#ifdef WITH_VOICE if (a_type == RenderType::TextChannel && b_type == RenderType::VoiceChannel) return -1; if (b_type == RenderType::TextChannel && a_type == RenderType::VoiceChannel) return 1; +#endif if (a_type == b_type) return static_cast(a_sort - b_sort); return 0; } From a22036a4ff6ea3161b9875fc7322417b2aa43a37 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:28:04 -0500 Subject: [PATCH 37/48] loosen sort condition --- src/components/channellist/channellisttree.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 59611c7b..92360313 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -359,8 +359,7 @@ int ChannelListTree::SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::Tree if (a_type == RenderType::TextChannel && b_type == RenderType::VoiceChannel) return -1; if (b_type == RenderType::TextChannel && a_type == RenderType::VoiceChannel) return 1; #endif - if (a_type == b_type) return static_cast(a_sort - b_sort); - return 0; + return static_cast(a_sort - b_sort); } void ChannelListTree::OnPanedPositionChanged() { From 8f6bafa78aa8b535cb4399dd38e81bc887a558cf Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:28:13 -0500 Subject: [PATCH 38/48] add icons --- ci/used-icons.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/used-icons.txt b/ci/used-icons.txt index 0ea18ae4..870c05e9 100644 --- a/ci/used-icons.txt +++ b/ci/used-icons.txt @@ -3,3 +3,5 @@ actions/call-stop-symbolic status/microphone-disabled-symbolic status/audio-volume-muted-symbolic devices/camera-web-symbolic +status/user-available-symbolic +places/folder-symbolic From bddaf99c12f5c3954504ebb8fceab192613bf9fc Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 10 Feb 2024 22:28:03 -0500 Subject: [PATCH 39/48] add guild menus to classic guild list --- src/components/channellist/channellist.cpp | 8 +++ .../channellist/classic/guildlist.cpp | 59 ++++++++++++++++++- .../channellist/classic/guildlist.hpp | 17 ++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp index 076a83e0..f4095920 100644 --- a/src/components/channellist/channellist.cpp +++ b/src/components/channellist/channellist.cpp @@ -92,6 +92,14 @@ void ChannelList::ConnectSignals() { m_tree.signal_action_guild_settings().connect([this](Snowflake id) { m_signal_action_guild_settings.emit(id); }); + + m_guilds.signal_action_guild_leave().connect([this](Snowflake id) { + m_signal_action_guild_leave.emit(id); + }); + + m_guilds.signal_action_guild_settings().connect([this](Snowflake id) { + m_signal_action_guild_settings.emit(id); + }); } #ifdef WITH_LIBHANDY diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index 49ee87c7..9c634f7c 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -18,10 +18,41 @@ class GuildListDMsButton : public Gtk::EventBox { Gtk::Image m_img; }; -GuildList::GuildList() { +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("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() { @@ -76,6 +107,10 @@ GuildListGuildItem *GuildList::CreateGuildWidget(Snowflake id) { 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(event)); } return true; }); @@ -111,6 +146,20 @@ void GuildList::Clear() { } } +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; } @@ -118,3 +167,11 @@ GuildList::type_signal_guild_selected GuildList::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 index d76e80d9..72e88e83 100644 --- a/src/components/channellist/classic/guildlist.hpp +++ b/src/components/channellist/classic/guildlist.hpp @@ -18,14 +18,31 @@ class GuildList : public Gtk::ListBox { 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; using type_signal_dms_selected = sigc::signal; + using type_signal_action_guild_leave = sigc::signal; + using type_signal_action_guild_settings = sigc::signal; 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; }; From 0f5c4524c0fb4946c25672bfc07e000011fcfd31 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 19 Feb 2024 02:22:28 -0500 Subject: [PATCH 40/48] make sure channel list view uses theme accent color fr --- res/css/main.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/css/main.css b/res/css/main.css index d29174ce..71434cf1 100644 --- a/res/css/main.css +++ b/res/css/main.css @@ -156,3 +156,7 @@ min-width: 0px; min-height: 0px; } + +.channel-list .view:selected { + background-color: @theme_selected_bg_color; +} From 473ff6f777ae3a8d1b3fa08a3ea15e2905340e03 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 19 Feb 2024 02:24:57 -0500 Subject: [PATCH 41/48] widget tweaking for styling --- src/components/channellist/classic/guildlist.cpp | 2 +- src/components/channellist/classic/guildlistfolderitem.cpp | 2 +- src/components/channellist/classic/guildlistguilditem.cpp | 6 +++++- src/components/channellist/classic/guildlistguilditem.hpp | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index 9c634f7c..d756c6fb 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -23,7 +23,7 @@ GuildList::GuildList() , 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("guild-list"); + get_style_context()->add_class("classic-guild-list"); set_selection_mode(Gtk::SELECTION_NONE); show_all_children(); diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index 2c8bc3b2..30e1adb7 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -36,7 +36,7 @@ void GuildListFolderButton::SetGuilds(const std::vector &guild_ids) { } GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder) { - get_style_context()->add_class("classic-guild-folder"); + get_style_context()->add_class("classic-guild-list-folder"); if (folder.Name.has_value()) { set_tooltip_text(*folder.Name); diff --git a/src/components/channellist/classic/guildlistguilditem.cpp b/src/components/channellist/classic/guildlistguilditem.cpp index 2b26e731..ee6b35a5 100644 --- a/src/components/channellist/classic/guildlistguilditem.cpp +++ b/src/components/channellist/classic/guildlistguilditem.cpp @@ -4,8 +4,12 @@ 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_image); + + add(m_box); + m_box.pack_start(m_image); show_all_children(); set_tooltip_text(guild.Name); diff --git a/src/components/channellist/classic/guildlistguilditem.hpp b/src/components/channellist/classic/guildlistguilditem.hpp index 6bc0037c..e5504f6d 100644 --- a/src/components/channellist/classic/guildlistguilditem.hpp +++ b/src/components/channellist/classic/guildlistguilditem.hpp @@ -14,5 +14,6 @@ class GuildListGuildItem : public Gtk::EventBox { void UpdateIcon(); void OnIconFetched(const Glib::RefPtr &pb); + Gtk::Box m_box; Gtk::Image m_image; }; From 3badc04db5770defa308dc70e5ddde07828202cf Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:27:03 -0500 Subject: [PATCH 42/48] unread indicators for classic guilds --- res/css/main.css | 19 ++++++++++++++++ .../classic/guildlistguilditem.cpp | 22 +++++++++++++++++++ .../classic/guildlistguilditem.hpp | 3 +++ 3 files changed, 44 insertions(+) diff --git a/res/css/main.css b/res/css/main.css index 71434cf1..db548ac1 100644 --- a/res/css/main.css +++ b/res/css/main.css @@ -160,3 +160,22 @@ .channel-list .view:selected { background-color: @theme_selected_bg_color; } + +.classic-guild-list > row { + padding-left: 0px; + box-shadow: none; + border: none; + outline: none; +} + +.classic-guild-list > row:hover { + background: none; +} + +.classic-guild-list-guild.has-unread { + background: radial-gradient(7px circle at left, @theme_selected_bg_color 50%, transparent 20%); +} + +.classic-guild-list-guild box, .classic-guild-list-folder stack { + padding-left: 10px; +} diff --git a/src/components/channellist/classic/guildlistguilditem.cpp b/src/components/channellist/classic/guildlistguilditem.cpp index ee6b35a5..5acad02f 100644 --- a/src/components/channellist/classic/guildlistguilditem.cpp +++ b/src/components/channellist/classic/guildlistguilditem.cpp @@ -15,6 +15,9 @@ GuildListGuildItem::GuildListGuildItem(const GuildData &guild) 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)); } void GuildListGuildItem::UpdateIcon() { @@ -26,3 +29,22 @@ void GuildListGuildItem::UpdateIcon() { void GuildListGuildItem::OnIconFetched(const Glib::RefPtr &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 index e5504f6d..6e2b2415 100644 --- a/src/components/channellist/classic/guildlistguilditem.hpp +++ b/src/components/channellist/classic/guildlistguilditem.hpp @@ -13,6 +13,9 @@ class GuildListGuildItem : public Gtk::EventBox { private: void UpdateIcon(); void OnIconFetched(const Glib::RefPtr &pb); + void OnMessageCreate(const Message &msg); + void OnMessageAck(const MessageAckData &data); + void CheckUnreadStatus(); Gtk::Box m_box; Gtk::Image m_image; From 3c54ff85622ff7210f8acee00e075b12128567a9 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 23 Feb 2024 21:57:24 -0500 Subject: [PATCH 43/48] check unread status for classic guild on launch --- src/components/channellist/classic/guildlistguilditem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/channellist/classic/guildlistguilditem.cpp b/src/components/channellist/classic/guildlistguilditem.cpp index 5acad02f..5b578be3 100644 --- a/src/components/channellist/classic/guildlistguilditem.cpp +++ b/src/components/channellist/classic/guildlistguilditem.cpp @@ -18,6 +18,8 @@ GuildListGuildItem::GuildListGuildItem(const GuildData &guild) 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() { From bab713baf557de427a1f93cc377d2fe79561731d Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 6 Mar 2024 01:19:55 -0500 Subject: [PATCH 44/48] folder unread indicators --- .../classic/guildlistfolderitem.cpp | 36 +++++++++++++++++++ .../classic/guildlistfolderitem.hpp | 6 ++++ 2 files changed, 42 insertions(+) diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index 30e1adb7..e062d42d 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -36,6 +36,8 @@ void GuildListFolderButton::SetGuilds(const std::vector &guild_ids) { } GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder) { + m_guild_ids = folder.GuildIDs; + get_style_context()->add_class("classic-guild-list-folder"); if (folder.Name.has_value()) { @@ -86,8 +88,42 @@ GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &fo 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 index 82a5a149..e5772c00 100644 --- a/src/components/channellist/classic/guildlistfolderitem.hpp +++ b/src/components/channellist/classic/guildlistfolderitem.hpp @@ -27,6 +27,12 @@ class GuildListFolderItem : public Gtk::VBox { void AddGuildWidget(GuildListGuildItem *widget); private: + void OnMessageCreate(const Message &msg); + void OnMessageAck(const MessageAckData &data); + void CheckUnreadStatus(); + + std::vector m_guild_ids; + Gtk::Stack m_stack; GuildListFolderButton m_grid; Gtk::Image m_icon; From f142df9155b9df0022c38c9420a2e6168f48fd9e Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 6 Mar 2024 01:26:34 -0500 Subject: [PATCH 45/48] mindeps build annoyance --- src/abaddon.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/abaddon.cpp b/src/abaddon.cpp index f8c4b397..045b8a72 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -54,7 +54,10 @@ Abaddon::Abaddon() : m_settings(Platform::FindConfigFile()) , m_discord(GetSettings().UseMemoryDB) // stupid but easy , m_emojis(GetResPath("/emojis.bin")) - , m_audio(GetSettings().Backends) { +#ifdef WITH_VOICE + , m_audio(GetSettings().Backends) +#endif +{ LoadFromSettings(); // todo: set user agent for non-client(?) From 969820fba2a353b74d57f6cb76e3a108115391a7 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:37:24 -0500 Subject: [PATCH 46/48] fix overflow in sorting --- src/components/channellist/channellisttree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index 92360313..c26cc249 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -359,7 +359,7 @@ int ChannelListTree::SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::Tree if (a_type == RenderType::TextChannel && b_type == RenderType::VoiceChannel) return -1; if (b_type == RenderType::TextChannel && a_type == RenderType::VoiceChannel) return 1; #endif - return static_cast(a_sort - b_sort); + return static_cast(std::clamp(a_sort - b_sort, -1LL, 1LL)); } void ChannelListTree::OnPanedPositionChanged() { From 143bb579139ad4260f5f3131c66f0ea3b6479680 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:41:04 -0500 Subject: [PATCH 47/48] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9b4a2d70..cbbe341b 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,7 @@ For example, memory_db would be set by adding `memory_db = true` under the line | `font_scale` | double | | scale font rendering. 1 is unchanged | | `image_embed_clamp_width` | int | 400 | maximum width of image embeds | | `image_embed_clamp_height` | int | 300 | maximum height of image embeds | +| `classic_channels` | boolean | false | use classic Discord-style interface for server/channel listing | | `classic_change_guild_on_open` | boolean | true | change displayed guild when selecting a channel (classic channel list) | #### style From ff76ec5f866b5dc77bb635f2d62bec861794fb63 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 8 Mar 2024 19:18:15 -0500 Subject: [PATCH 48/48] type nonsense --- src/components/channellist/channellisttree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index c26cc249..4816b423 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -359,7 +359,7 @@ int ChannelListTree::SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::Tree if (a_type == RenderType::TextChannel && b_type == RenderType::VoiceChannel) return -1; if (b_type == RenderType::TextChannel && a_type == RenderType::VoiceChannel) return 1; #endif - return static_cast(std::clamp(a_sort - b_sort, -1LL, 1LL)); + return static_cast(std::clamp(a_sort - b_sort, int64_t(-1), int64_t(1))); } void ChannelListTree::OnPanedPositionChanged() {