From 66e2311bf01fb355afccd732af3671f1956e0edd Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sat, 3 Jul 2021 19:06:49 -0400
Subject: initial working better channel list
---
components/channels.cpp | 967 +++++++++++++++---------------------------------
components/channels.hpp | 202 ++++------
2 files changed, 383 insertions(+), 786 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 970251f..64f1c15 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -7,767 +7,408 @@
#include "../util.hpp"
#include "statusindicator.hpp"
-void ChannelListRow::Collapse() {}
-
-void ChannelListRow::Expand() {}
-
-void ChannelListRow::MakeReadOnly(Gtk::TextView *tv) {
- tv->set_can_focus(false);
- tv->set_editable(false);
- tv->signal_realize().connect([tv]() {
- auto window = tv->get_window(Gtk::TEXT_WINDOW_TEXT);
- auto display = window->get_display();
- auto cursor = Gdk::Cursor::create(display, "default"); // textview uses "text" which looks out of place
- window->set_cursor(cursor);
- });
- // stupid hack to prevent selection
- auto buf = tv->get_buffer();
- buf->property_has_selection().signal_changed().connect([tv, buf]() {
- Gtk::TextBuffer::iterator a, b;
- buf->get_bounds(a, b);
- buf->select_range(a, a);
- });
-}
-
-ChannelListRowDMHeader::ChannelListRowDMHeader() {
- m_ev = Gtk::manage(new Gtk::EventBox);
- m_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
- m_lbl = Gtk::manage(new Gtk::Label);
-
- get_style_context()->add_class("channel-row");
- m_lbl->get_style_context()->add_class("channel-row-label");
-
- m_lbl->set_use_markup(true);
- m_lbl->set_markup("Direct Messages");
- m_box->set_halign(Gtk::ALIGN_START);
- m_box->pack_start(*m_lbl);
-
- m_ev->add(*m_box);
- add(*m_ev);
- show_all_children();
-}
+ChannelList::ChannelList()
+ : m_model(Gtk::TreeStore::create(m_columns))
+ , m_main(Gtk::manage(new Gtk::ScrolledWindow)) {
+ const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) {
+ auto row = *m_model->get_iter(path);
+ if (row[m_columns.m_expanded]) {
+ m_view.collapse_row(path);
+ row[m_columns.m_expanded] = false;
+ } else {
+ m_view.expand_row(path, false);
+ row[m_columns.m_expanded] = true;
+ }
-ChannelListRowDMChannel::ChannelListRowDMChannel(const ChannelData *data) {
- ID = data->ID;
- m_ev = Gtk::manage(new Gtk::EventBox);
- m_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
- m_lbl = Gtk::manage(new Gtk::TextView);
- MakeReadOnly(m_lbl);
+ if (row[m_columns.m_type] == RenderType::TextChannel) {
+ m_signal_action_channel_item_select.emit(static_cast(row[m_columns.m_id]));
+ }
+ };
+ m_view.signal_row_activated().connect(cb);
+ m_view.set_activate_on_single_click(true);
- AddWidgetMenuHandler(m_ev, m_menu);
- AddWidgetMenuHandler(m_lbl, m_menu);
+ m_view.set_hexpand(true);
+ m_view.set_vexpand(true);
- m_menu_copy_id = Gtk::manage(new Gtk::MenuItem("_Copy ID", true));
- m_menu_copy_id->signal_activate().connect([this] {
- Gtk::Clipboard::get()->set_text(std::to_string(ID));
- });
+ m_view.set_headers_visible(false);
+ m_view.set_model(m_model);
+ m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING);
- if (data->Type == ChannelType::GROUP_DM)
- m_menu_close = Gtk::manage(new Gtk::MenuItem("_Leave DM", true));
- else
- m_menu_close = Gtk::manage(new Gtk::MenuItem("_Close DM", true));
- m_menu_close->signal_activate().connect([this] {
- Abaddon::Get().GetDiscordClient().CloseDM(ID);
- });
+ m_view.show();
- m_menu.append(*m_menu_copy_id);
- m_menu.append(*m_menu_close);
- m_menu.show_all();
+ m_main->add(m_view);
+ m_main->show_all();
- get_style_context()->add_class("channel-row");
- m_lbl->get_style_context()->add_class("channel-row-label");
+ auto *column = Gtk::manage(new Gtk::TreeView::Column("display"));
+ auto *renderer = Gtk::manage(new CellRendererChannels);
+ column->pack_start(*renderer);
+ column->add_attribute(renderer->property_type(), m_columns.m_type);
+ column->add_attribute(renderer->property_icon(), m_columns.m_icon);
+ column->add_attribute(renderer->property_name(), m_columns.m_name);
+ column->add_attribute(renderer->property_expanded(), m_columns.m_expanded);
+ m_view.append_column(*column);
+}
- std::optional top_recipient; // potentially nullopt in group dm
- const auto recipients = data->GetDMRecipients();
- if (recipients.size() > 0)
- top_recipient = recipients[0];
+Gtk::Widget *ChannelList::GetRoot() const {
+ return m_main;
+}
- if (data->Type == ChannelType::DM) {
- m_status = Gtk::manage(new StatusIndicator(top_recipient->ID));
- m_status->set_margin_start(5);
+void ChannelList::UpdateListing() {
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ auto &img = Abaddon::Get().GetImageManager();
- m_icon = Gtk::manage(new Gtk::Image(Abaddon::Get().GetImageManager().GetPlaceholder(24)));
- auto cb = [this](const Glib::RefPtr &pb) {
- m_icon->property_pixbuf() = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR);
- };
- Abaddon::Get().GetImageManager().LoadFromURL(top_recipient->GetAvatarURL("png", "16"), sigc::track_obj(cb, *this));
- }
+ const auto guild_ids = discord.GetUserSortedGuilds();
+ int sortnum = 0;
+ for (const auto &guild_id : guild_ids) {
+ const auto guild = discord.GetGuild(guild_id);
+ if (!guild.has_value()) continue;
+
+ auto guild_row = *m_model->append();
+ guild_row[m_columns.m_type] = RenderType::Guild;
+ 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(24);
+ guild_row[m_columns.m_sort] = ++sortnum;
+
+ if (guild->HasIcon()) {
+ const auto cb = [this, guild_row](const Glib::RefPtr &pb) {
+ guild_row[m_columns.m_icon] = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR);
+ };
+ img.LoadFromURL(guild->GetIconURL("png", "32"), sigc::track_obj(cb, *this));
+ }
- auto buf = m_lbl->get_buffer();
- if (data->Type == ChannelType::DM)
- buf->set_text(top_recipient->Username);
- else if (data->Type == ChannelType::GROUP_DM)
- buf->set_text(std::to_string(recipients.size()) + " users");
-
- static bool show_emojis = Abaddon::Get().GetSettings().GetShowStockEmojis();
- if (show_emojis)
- Abaddon::Get().GetEmojis().ReplaceEmojis(buf, ChannelEmojiSize);
-
- m_box->set_halign(Gtk::ALIGN_START);
- if (m_icon != nullptr)
- m_box->pack_start(*m_icon);
- if (m_status != nullptr)
- m_box->pack_start(*m_status);
- m_box->pack_start(*m_lbl);
- m_ev->add(*m_box);
- add(*m_ev);
- show_all_children();
-}
-
-ChannelListRowGuild::ChannelListRowGuild(const GuildData *data) {
- ID = data->ID;
- m_ev = Gtk::manage(new Gtk::EventBox);
- m_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
- m_lbl = Gtk::manage(new Gtk::TextView);
- MakeReadOnly(m_lbl);
-
- AddWidgetMenuHandler(m_ev, m_menu);
- AddWidgetMenuHandler(m_lbl, m_menu);
-
- m_menu_copyid = Gtk::manage(new Gtk::MenuItem("_Copy ID", true));
- m_menu_copyid->signal_activate().connect([this]() {
- m_signal_copy_id.emit();
- });
- m_menu.append(*m_menu_copyid);
+ if (!guild->Channels.has_value()) continue;
- m_menu_leave = Gtk::manage(new Gtk::MenuItem("_Leave Guild", true));
- m_menu_leave->signal_activate().connect([this]() {
- m_signal_leave.emit();
- });
- m_menu.append(*m_menu_leave);
+ // separate out the channels
+ std::vector orphan_channels;
+ std::map> categories;
- m_menu_settings = Gtk::manage(new Gtk::MenuItem("Guild _Settings", true));
- m_menu_settings->signal_activate().connect([this]() {
- m_signal_settings.emit();
- });
- m_menu.append(*m_menu_settings);
+ for (const auto &channel_ : *guild->Channels) {
+ const auto channel = discord.GetChannel(channel_.ID);
+ if (!channel.has_value()) continue;
+ if (channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::GUILD_NEWS) {
+ if (channel->ParentID.has_value())
+ categories[*channel->ParentID].push_back(*channel);
+ else
+ orphan_channels.push_back(*channel);
+ } else if (channel->Type == ChannelType::GUILD_CATEGORY) {
+ categories[channel->ID];
+ }
+ }
- m_menu.show_all();
+ for (const auto &channel : orphan_channels) {
+ auto channel_row = *m_model->append(guild_row.children());
+ channel_row[m_columns.m_type] = RenderType::TextChannel;
+ channel_row[m_columns.m_id] = channel.ID;
+ channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
+ channel_row[m_columns.m_sort] = *channel.Position - 100; // subtract 100 to make sure they stay behind categories
+ }
- const auto show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
- auto &img = Abaddon::Get().GetImageManager();
- if (data->HasIcon()) {
- if (data->HasAnimatedIcon() && show_animations) {
- m_icon = Gtk::manage(new Gtk::Image(img.GetPlaceholder(24)));
- auto cb = [this](const Glib::RefPtr &pb) {
- m_icon->property_pixbuf_animation() = pb;
- };
- img.LoadAnimationFromURL(data->GetIconURL("gif", "32"), 24, 24, sigc::track_obj(cb, *this));
- } else {
- m_icon = Gtk::manage(new Gtk::Image(img.GetPlaceholder(24)));
- auto cb = [this](const Glib::RefPtr &pb) {
- m_icon->property_pixbuf() = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR);
- };
- img.LoadFromURL(data->GetIconURL("png", "32"), sigc::track_obj(cb, *this));
+ for (const auto &[category_id, channels] : categories) {
+ const auto category = discord.GetChannel(category_id);
+ if (!category.has_value()) continue;
+ auto cat_row = *m_model->append(guild_row.children());
+ cat_row[m_columns.m_type] = RenderType::Category;
+ cat_row[m_columns.m_id] = category_id;
+ cat_row[m_columns.m_name] = Glib::Markup::escape_text(*category->Name);
+ cat_row[m_columns.m_sort] = *category->Position;
+
+ for (const auto &channel : channels) {
+ auto channel_row = *m_model->append(cat_row.children());
+ channel_row[m_columns.m_type] = RenderType::TextChannel;
+ channel_row[m_columns.m_id] = channel.ID;
+ channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
+ channel_row[m_columns.m_sort] = *channel.Position;
+ }
}
- } else {
- m_icon = Gtk::manage(new Gtk::Image(Abaddon::Get().GetImageManager().GetPlaceholder(24)));
}
-
- get_style_context()->add_class("channel-row");
- get_style_context()->add_class("channel-row-guild");
- m_lbl->get_style_context()->add_class("channel-row-label");
-
- auto buf = m_lbl->get_buffer();
- Gtk::TextBuffer::iterator start, end;
- buf->get_bounds(start, end);
- buf->insert_markup(start, "" + Glib::Markup::escape_text(data->Name) + "");
- static bool show_emojis = Abaddon::Get().GetSettings().GetShowStockEmojis();
- if (show_emojis)
- Abaddon::Get().GetEmojis().ReplaceEmojis(buf, ChannelEmojiSize);
- m_box->set_halign(Gtk::ALIGN_START);
- m_box->pack_start(*m_icon);
- m_box->pack_start(*m_lbl);
- m_ev->add(*m_box);
- add(*m_ev);
- show_all_children();
}
-ChannelListRowGuild::type_signal_copy_id ChannelListRowGuild::signal_copy_id() {
- return m_signal_copy_id;
+void ChannelList::UpdateNewGuild(Snowflake id) {
}
-ChannelListRowGuild::type_signal_leave ChannelListRowGuild::signal_leave() {
- return m_signal_leave;
+void ChannelList::UpdateRemoveGuild(Snowflake id) {
}
-ChannelListRowGuild::type_signal_settings ChannelListRowGuild::signal_settings() {
- return m_signal_settings;
+void ChannelList::UpdateRemoveChannel(Snowflake id) {
}
-ChannelListRowCategory::ChannelListRowCategory(const ChannelData *data) {
- ID = data->ID;
- m_ev = Gtk::manage(new Gtk::EventBox);
- m_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
- m_lbl = Gtk::manage(new Gtk::TextView);
- MakeReadOnly(m_lbl);
- m_arrow = Gtk::manage(new Gtk::Arrow(Gtk::ARROW_DOWN, Gtk::SHADOW_NONE));
-
- m_menu_copyid = Gtk::manage(new Gtk::MenuItem("_Copy ID", true));
- m_menu_copyid->signal_activate().connect([this]() {
- m_signal_copy_id.emit();
- });
- m_menu.append(*m_menu_copyid);
-
- m_menu.show_all();
-
- AddWidgetMenuHandler(m_ev, m_menu);
- AddWidgetMenuHandler(m_lbl, m_menu);
-
- get_style_context()->add_class("channel-row");
- get_style_context()->add_class("channel-row-category");
- m_lbl->get_style_context()->add_class("channel-row-label");
-
- auto buf = m_lbl->get_buffer();
- buf->set_text(*data->Name);
- static bool show_emojis = Abaddon::Get().GetSettings().GetShowStockEmojis();
- if (show_emojis)
- Abaddon::Get().GetEmojis().ReplaceEmojis(buf, ChannelEmojiSize);
- m_box->set_halign(Gtk::ALIGN_START);
- m_box->pack_start(*m_arrow);
- m_box->pack_start(*m_lbl);
- m_ev->add(*m_box);
- add(*m_ev);
- show_all_children();
+void ChannelList::UpdateChannel(Snowflake id) {
}
-void ChannelListRowCategory::Collapse() {
- m_arrow->set(Gtk::ARROW_RIGHT, Gtk::SHADOW_NONE);
+void ChannelList::UpdateCreateDMChannel(Snowflake id) {
}
-void ChannelListRowCategory::Expand() {
- m_arrow->set(IsUserCollapsed ? Gtk::ARROW_RIGHT : Gtk::ARROW_DOWN, Gtk::SHADOW_NONE);
+void ChannelList::UpdateCreateChannel(Snowflake id) {
}
-ChannelListRowCategory::type_signal_copy_id ChannelListRowCategory::signal_copy_id() {
- return m_signal_copy_id;
+void ChannelList::UpdateGuild(Snowflake id) {
}
-bool ChannelListRowChannel::m_menu_init = false;
-Gtk::Menu *ChannelListRowChannel::m_menu;
-ChannelListRowChannel::ChannelListRowChannel(const ChannelData *data) {
- if (!m_menu_init) {
- m_menu = Gtk::manage(new Gtk::Menu);
- auto *menu_copy_id = Gtk::manage(new Gtk::MenuItem("_Copy ID", true));
- menu_copy_id->signal_activate().connect([this] {
- Gtk::Clipboard::get()->set_text(std::to_string(ID));
- });
- m_menu->append(*menu_copy_id);
- m_menu->show_all();
- m_menu_init = true;
- }
-
- ID = data->ID;
- auto *ev = Gtk::manage(new Gtk::EventBox);
- auto *lbl = Gtk::manage(new Gtk::TextView);
- MakeReadOnly(lbl);
+void ChannelList::SetActiveChannel(Snowflake id) {
+}
- AddWidgetMenuHandler(ev, *m_menu);
- AddWidgetMenuHandler(lbl, *m_menu);
+ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
+ return m_signal_action_channel_item_select;
+}
- get_style_context()->add_class("channel-row");
- get_style_context()->add_class("channel-row-channel");
- lbl->get_style_context()->add_class("channel-row-label");
+ChannelList::type_signal_action_guild_leave ChannelList::signal_action_guild_leave() {
+ return m_signal_action_guild_leave;
+}
- auto buf = lbl->get_buffer();
- if (data->IsNSFW.has_value() && *data->IsNSFW) {
- get_style_context()->add_class("nsfw");
- lbl->get_style_context()->add_class("nsfw");
- }
- buf->set_text("#" + *data->Name);
- static bool show_emojis = Abaddon::Get().GetSettings().GetShowStockEmojis();
- if (show_emojis)
- Abaddon::Get().GetEmojis().ReplaceEmojis(buf, ChannelEmojiSize);
- ev->add(*lbl);
- add(*ev);
- show_all_children();
+ChannelList::type_signal_action_guild_settings ChannelList::signal_action_guild_settings() {
+ return m_signal_action_guild_settings;
}
-ChannelList::ChannelList() {
- m_main = Gtk::manage(new Gtk::ScrolledWindow);
- m_list = Gtk::manage(new Gtk::ListBox);
+ChannelList::ModelColumns::ModelColumns() {
+ add(m_type);
+ add(m_id);
+ add(m_name);
+ add(m_icon);
+ add(m_sort);
+ add(m_expanded);
+}
+
+CellRendererChannels::CellRendererChannels()
+ : Glib::ObjectBase(typeid(CellRendererChannels))
+ , Gtk::CellRenderer()
+ , m_property_type(*this, "render-type")
+ , m_property_name(*this, "name")
+ , m_property_pixbuf(*this, "pixbuf")
+ , m_property_expanded(*this, "expanded") {
+ property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
+ property_xpad() = 2;
+ property_ypad() = 2;
+ m_property_name.get_proxy().signal_changed().connect([this] {
+ m_renderer_text.property_markup() = m_property_name;
+ });
+}
- m_list->get_style_context()->add_class("channel-list");
+CellRendererChannels::~CellRendererChannels() {
+}
- m_list->set_activate_on_single_click(true);
- m_list->signal_row_activated().connect(sigc::mem_fun(*this, &ChannelList::on_row_activated));
+Glib::PropertyProxy CellRendererChannels::property_type() {
+ return m_property_type.get_proxy();
+}
- m_main->add(*m_list);
- m_main->show_all();
+Glib::PropertyProxy CellRendererChannels::property_name() {
+ return m_property_name.get_proxy();
+}
- // maybe will regret doing it this way
- auto &discord = Abaddon::Get().GetDiscordClient();
- auto cb = [this, &discord](const Message &message) {
- const auto channel = discord.GetChannel(message.ChannelID);
- if (!channel.has_value()) return;
- if (channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM)
- CheckBumpDM(message.ChannelID);
- };
- discord.signal_message_create().connect(sigc::track_obj(cb, *this));
+Glib::PropertyProxy> CellRendererChannels::property_icon() {
+ return m_property_pixbuf.get_proxy();
}
-Gtk::Widget *ChannelList::GetRoot() const {
- return m_main;
+Glib::PropertyProxy CellRendererChannels::property_expanded() {
+ return m_property_expanded.get_proxy();
}
-void ChannelList::UpdateNewGuild(Snowflake id) {
- auto sort = Abaddon::Get().GetDiscordClient().GetUserSortedGuilds();
- if (sort.size() == 1) {
- UpdateListing();
- return;
+void CellRendererChannels::get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
+ switch (m_property_type.get_value()) {
+ case RenderType::Guild:
+ return get_preferred_width_vfunc_guild(widget, minimum_width, natural_width);
+ case RenderType::Category:
+ return get_preferred_width_vfunc_category(widget, minimum_width, natural_width);
+ case RenderType::TextChannel:
+ return get_preferred_width_vfunc_channel(widget, minimum_width, natural_width);
}
+}
- const auto insert_at = [this, id](int listpos) {
- InsertGuildAt(id, listpos);
- };
-
- auto it = std::find(sort.begin(), sort.end(), id);
- if (it == sort.end()) return;
- // if the new guild pos is at the end use -1
- if (it + 1 == sort.end()) {
- insert_at(-1);
- return;
- }
- // find the position of the guild below it into the listbox
- auto below_id = *(it + 1);
- auto below_it = m_id_to_row.find(below_id);
- if (below_it == m_id_to_row.end()) {
- UpdateListing();
- return;
+void CellRendererChannels::get_preferred_width_for_height_vfunc(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
+ switch (m_property_type.get_value()) {
+ case RenderType::Guild:
+ return get_preferred_width_for_height_vfunc_guild(widget, height, minimum_width, natural_width);
+ case RenderType::Category:
+ return get_preferred_width_for_height_vfunc_category(widget, height, minimum_width, natural_width);
+ case RenderType::TextChannel:
+ return get_preferred_width_for_height_vfunc_channel(widget, height, minimum_width, natural_width);
}
- auto below_pos = below_it->second->get_index();
- // stick it just above
- insert_at(below_pos - 1);
}
-void ChannelList::UpdateRemoveGuild(Snowflake id) {
- auto it = m_guild_id_to_row.find(id);
- if (it == m_guild_id_to_row.end()) return;
- auto row = dynamic_cast(it->second);
- if (row == nullptr) return;
- DeleteRow(row);
+void CellRendererChannels::get_preferred_height_vfunc(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
+ switch (m_property_type.get_value()) {
+ case RenderType::Guild:
+ return get_preferred_height_vfunc_guild(widget, minimum_height, natural_height);
+ case RenderType::Category:
+ return get_preferred_height_vfunc_category(widget, minimum_height, natural_height);
+ case RenderType::TextChannel:
+ return get_preferred_height_vfunc_channel(widget, minimum_height, natural_height);
+ }
}
-void ChannelList::UpdateRemoveChannel(Snowflake id) {
- auto it = m_id_to_row.find(id);
- if (it == m_id_to_row.end()) return;
- auto row = dynamic_cast(it->second);
- if (row == nullptr) return;
- DeleteRow(row);
-}
-
-// this is total shit
-void ChannelList::UpdateChannelCategory(Snowflake id) {
- const auto data = Abaddon::Get().GetDiscordClient().GetChannel(id);
- const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(*data->GuildID);
- auto git = m_guild_id_to_row.find(*data->GuildID);
- if (git == m_guild_id_to_row.end()) return;
- auto *guild_row = git->second;
- if (!data.has_value() || !guild.has_value()) return;
- auto it = m_id_to_row.find(id);
- if (it == m_id_to_row.end()) return;
- auto row = dynamic_cast(it->second);
- if (row == nullptr) return;
- const bool old_collapsed = row->IsUserCollapsed;
- const bool visible = row->is_visible();
- std::map child_rows;
- for (auto child : row->Children) {
- child_rows[child->get_index()] = child->ID;
- }
- guild_row->Children.erase(std::remove(guild_row->Children.begin(), guild_row->Children.end(), row), guild_row->Children.end());
- DeleteRow(row);
-
- int pos = guild_row->get_index();
- const auto sorted = guild->GetSortedChannels(id);
- const auto sorted_it = std::find(sorted.begin(), sorted.end(), id);
- if (sorted_it == sorted.end()) return;
- if (std::next(sorted_it) == sorted.end()) {
- const auto x = m_id_to_row.find(*std::prev(sorted_it));
- if (x != m_id_to_row.end())
- pos = x->second->get_index() + 1;
- } else {
- const auto x = m_id_to_row.find(*std::next(sorted_it));
- if (x != m_id_to_row.end())
- pos = x->second->get_index();
+void CellRendererChannels::get_preferred_height_for_width_vfunc(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
+ switch (m_property_type.get_value()) {
+ case RenderType::Guild:
+ return get_preferred_height_for_width_vfunc_guild(widget, width, minimum_height, natural_height);
+ case RenderType::Category:
+ return get_preferred_height_for_width_vfunc_category(widget, width, minimum_height, natural_height);
+ case RenderType::TextChannel:
+ return get_preferred_height_for_width_vfunc_channel(widget, width, minimum_height, natural_height);
}
+}
- auto *new_row = Gtk::manage(new ChannelListRowCategory(&*data));
- new_row->IsUserCollapsed = old_collapsed;
- if (visible)
- new_row->show();
- m_id_to_row[id] = new_row;
- new_row->signal_copy_id().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnMenuCopyID), new_row->ID));
- new_row->Parent = guild_row;
- guild_row->Children.push_back(new_row);
- m_list->insert(*new_row, pos);
- int i = 1;
- for (const auto &[idx, child_id] : child_rows) {
- const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(child_id);
- if (channel.has_value()) {
- auto *new_child = Gtk::manage(new ChannelListRowChannel(&*channel));
- new_row->Children.push_back(new_child);
- new_child->Parent = new_row;
- m_id_to_row[child_id] = new_child;
- if (visible && !new_row->IsUserCollapsed)
- new_child->show();
- m_list->insert(*new_child, pos + i++);
- }
+void CellRendererChannels::render_vfunc(const Cairo::RefPtr &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
+ switch (m_property_type.get_value()) {
+ case RenderType::Guild:
+ return render_vfunc_guild(cr, widget, background_area, cell_area, flags);
+ case RenderType::Category:
+ return render_vfunc_category(cr, widget, background_area, cell_area, flags);
+ case RenderType::TextChannel:
+ return render_vfunc_channel(cr, widget, background_area, cell_area, flags);
}
}
-// so is this
-void ChannelList::UpdateChannel(Snowflake id) {
- const auto data = Abaddon::Get().GetDiscordClient().GetChannel(id);
- const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(*data->GuildID);
- const auto *guild_row = m_guild_id_to_row.at(*data->GuildID);
- if (data->Type == ChannelType::GUILD_CATEGORY) {
- UpdateChannelCategory(id);
- return;
- }
+// guild functions
- auto it = m_id_to_row.find(id);
- if (it == m_id_to_row.end()) return; // stuff like voice doesnt have a row yet
- auto row = dynamic_cast(it->second);
- const bool old_collapsed = row->IsUserCollapsed;
- const bool old_visible = row->is_visible();
- DeleteRow(row);
-
- int pos = guild_row->get_index() + 1; // fallback
- const auto sorted = guild->GetSortedChannels();
- const auto sorted_it = std::find(sorted.begin(), sorted.end(), id);
- if (sorted_it + 1 == sorted.end()) {
- const auto x = m_id_to_row.find(*std::prev(sorted_it));
- if (x != m_id_to_row.end())
- pos = x->second->get_index() + 1;
- } else {
- const auto x = m_id_to_row.find(*std::next(sorted_it));
- if (x != m_id_to_row.end())
- pos = x->second->get_index();
- }
+void CellRendererChannels::get_preferred_width_vfunc_guild(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
+ int pixbuf_width = 0;
+ if (auto pixbuf = m_property_pixbuf.get_value())
+ pixbuf_width = pixbuf->get_width();
- auto *new_row = Gtk::manage(new ChannelListRowChannel(&*data));
- new_row->IsUserCollapsed = old_collapsed;
- m_id_to_row[id] = new_row;
- if (data->ParentID.has_value()) {
- new_row->Parent = m_id_to_row.at(*data->ParentID);
- } else {
- new_row->Parent = m_guild_id_to_row.at(*data->GuildID);
- }
- new_row->Parent->Children.push_back(new_row);
- if (new_row->Parent->is_visible() && !new_row->Parent->IsUserCollapsed)
- new_row->show();
- m_list->insert(*new_row, pos);
+ int text_min, text_nat;
+ m_renderer_text.get_preferred_width(widget, text_min, text_nat);
+
+ int xpad, ypad;
+ get_padding(xpad, ypad);
+ minimum_width = std::max(text_min, pixbuf_width) + xpad * 2;
+ natural_width = std::max(text_nat, pixbuf_width) + xpad * 2;
}
-void ChannelList::UpdateCreateDMChannel(Snowflake id) {
- const auto chan = Abaddon::Get().GetDiscordClient().GetChannel(id);
- auto *dm_row = Gtk::manage(new ChannelListRowDMChannel(&*chan));
- dm_row->IsUserCollapsed = false;
- m_list->insert(*dm_row, m_dm_header_row->get_index() + 1);
- m_dm_header_row->Children.push_back(dm_row);
- m_id_to_row[id] = dm_row;
- if (!m_dm_header_row->IsUserCollapsed)
- dm_row->show();
+void CellRendererChannels::get_preferred_width_for_height_vfunc_guild(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
+ get_preferred_width_vfunc_guild(widget, minimum_width, natural_width);
}
-void ChannelList::UpdateCreateChannel(Snowflake id) {
- const auto &discord = Abaddon::Get().GetDiscordClient();
- const auto data = discord.GetChannel(id);
- if (data->Type == ChannelType::DM || data->Type == ChannelType::GROUP_DM) {
- UpdateCreateDMChannel(id);
- return;
- }
- const auto guild = discord.GetGuild(*data->GuildID);
- auto *guild_row = m_guild_id_to_row.at(*data->GuildID);
-
- int pos = guild_row->get_index() + 1;
- const auto sorted = guild->GetSortedChannels();
- const auto sorted_it = std::find(sorted.begin(), sorted.end(), id);
- if (sorted_it + 1 == sorted.end()) {
- const auto x = m_id_to_row.find(*std::prev(sorted_it));
- if (x != m_id_to_row.end())
- pos = x->second->get_index() + 1;
- } else {
- const auto x = m_id_to_row.find(*std::next(sorted_it));
- if (x != m_id_to_row.end())
- pos = x->second->get_index();
- }
+void CellRendererChannels::get_preferred_height_vfunc_guild(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
+ int pixbuf_height = 0;
+ if (auto pixbuf = m_property_pixbuf.get_value())
+ pixbuf_height = pixbuf->get_height();
- ChannelListRow *row;
- if (data->Type == ChannelType::GUILD_TEXT || data->Type == ChannelType::GUILD_NEWS) {
- auto *tmp = Gtk::manage(new ChannelListRowChannel(&*data));
- row = tmp;
- } else if (data->Type == ChannelType::GUILD_CATEGORY) {
- auto *tmp = Gtk::manage(new ChannelListRowCategory(&*data));
- tmp->signal_copy_id().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnMenuCopyID), tmp->ID));
- row = tmp;
- } else
- return;
- row->IsUserCollapsed = false;
- if (!guild_row->IsUserCollapsed)
- row->show();
- row->Parent = guild_row;
- guild_row->Children.push_back(row);
- m_id_to_row[id] = row;
- m_list->insert(*row, pos);
-}
+ int text_min, text_nat;
+ m_renderer_text.get_preferred_height(widget, text_min, text_nat);
-void ChannelList::UpdateGuild(Snowflake id) {
- // the only thing changed is the row containing the guild item so just recreate it
- const auto data = Abaddon::Get().GetDiscordClient().GetGuild(id);
- if (!data.has_value()) return;
- auto it = m_guild_id_to_row.find(id);
- if (it == m_guild_id_to_row.end()) return;
- auto *row = dynamic_cast(it->second);
- const auto children = row->Children;
- const auto index = row->get_index();
- const bool old_collapsed = row->IsUserCollapsed;
- const bool old_gindex = row->GuildIndex;
- delete row;
- auto *new_row = Gtk::manage(new ChannelListRowGuild(&*data));
- new_row->IsUserCollapsed = old_collapsed;
- new_row->GuildIndex = old_gindex;
- m_guild_id_to_row[new_row->ID] = new_row;
- new_row->signal_leave().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnGuildMenuLeave), new_row->ID));
- new_row->signal_copy_id().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnMenuCopyID), new_row->ID));
- new_row->signal_settings().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnGuildMenuSettings), new_row->ID));
- new_row->Children = children;
- for (auto child : children)
- child->Parent = new_row;
- new_row->show_all();
- m_list->insert(*new_row, index);
+ int xpad, ypad;
+ get_padding(xpad, ypad);
+ minimum_height = std::max(text_min, pixbuf_height) + ypad * 2;
+ natural_height = std::max(text_nat, pixbuf_height) + ypad * 2;
}
-void ChannelList::SetActiveChannel(Snowflake id) {
- auto it = m_id_to_row.find(id);
- if (it == m_id_to_row.end()) return;
- m_list->select_row(*it->second);
+void CellRendererChannels::get_preferred_height_for_width_vfunc_guild(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
+ get_preferred_height_vfunc_guild(widget, minimum_height, natural_height);
}
-void ChannelList::CollapseRow(ChannelListRow *row) {
- row->Collapse();
- for (auto child : row->Children) {
- child->hide();
- CollapseRow(child);
- }
-}
+void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
+ Gtk::Requisition text_minimum, text_natural;
+ m_renderer_text.get_preferred_size(widget, text_minimum, text_natural);
-void ChannelList::ExpandRow(ChannelListRow *row) {
- row->Expand();
- row->show();
- if (!row->IsUserCollapsed)
- for (auto child : row->Children)
- ExpandRow(child);
-}
-
-void ChannelList::DeleteRow(ChannelListRow *row) {
- for (auto child : row->Children)
- DeleteRow(child);
- if (row->Parent != nullptr)
- row->Parent->Children.erase(std::remove(row->Parent->Children.begin(), row->Parent->Children.end(), row), row->Parent->Children.end());
- else
- printf("row has no parent!\n");
- if (dynamic_cast(row) != nullptr)
- m_guild_id_to_row.erase(row->ID);
- else
- m_id_to_row.erase(row->ID);
- delete row;
-}
-
-void ChannelList::on_row_activated(Gtk::ListBoxRow *tmprow) {
- auto row = dynamic_cast(tmprow);
- if (row == nullptr) return;
- bool new_collapsed = !row->IsUserCollapsed;
- row->IsUserCollapsed = new_collapsed;
-
- // kinda ugly
- if (dynamic_cast(row) != nullptr || dynamic_cast(row) != nullptr)
- m_signal_action_channel_item_select.emit(row->ID);
-
- if (new_collapsed)
- CollapseRow(row);
- else
- ExpandRow(row);
-}
-
-void ChannelList::InsertGuildAt(Snowflake id, int pos) {
- const auto insert_and_adjust = [&](Gtk::Widget &widget) {
- m_list->insert(widget, pos);
- if (pos != -1) pos++;
- };
+ Gtk::Requisition minimum, natural;
+ get_preferred_size(widget, minimum, natural);
- const auto &discord = Abaddon::Get().GetDiscordClient();
- const auto guild_data = discord.GetGuild(id);
- if (!guild_data.has_value()) return;
+ auto pixbuf = m_property_pixbuf.get_value();
- std::map orphan_channels;
- std::unordered_map> cat_to_channels;
- if (guild_data->Channels.has_value())
- for (const auto &dc : *guild_data->Channels) {
- const auto channel = discord.GetChannel(dc.ID);
- if (!channel.has_value()) continue;
- if (channel->Type != ChannelType::GUILD_TEXT && channel->Type != ChannelType::GUILD_NEWS) continue;
+ const int icon_x = background_area.get_x();
+ const int icon_y = background_area.get_y();
+ const int icon_w = pixbuf->get_width();
+ const int icon_h = pixbuf->get_height();
- if (channel->ParentID.has_value())
- cat_to_channels[*channel->ParentID].push_back(*channel);
- else
- orphan_channels[*channel->Position] = *channel;
- }
+ const int text_x = icon_x + icon_w + 5;
+ const int text_y = background_area.get_y() + background_area.get_height() / 2 - text_natural.height / 2;
+ const int text_w = text_natural.width;
+ const int text_h = text_natural.height;
- auto *guild_row = Gtk::manage(new ChannelListRowGuild(&*guild_data));
- guild_row->show_all();
- guild_row->IsUserCollapsed = true;
- guild_row->GuildIndex = m_guild_count++;
- insert_and_adjust(*guild_row);
- m_guild_id_to_row[guild_row->ID] = guild_row;
- guild_row->signal_leave().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnGuildMenuLeave), guild_row->ID));
- guild_row->signal_copy_id().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnMenuCopyID), guild_row->ID));
- guild_row->signal_settings().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnGuildMenuSettings), guild_row->ID));
-
- // add channels with no parent category
- for (const auto &[pos, channel] : orphan_channels) {
- auto *chan_row = Gtk::manage(new ChannelListRowChannel(&channel));
- chan_row->IsUserCollapsed = false;
- insert_and_adjust(*chan_row);
- guild_row->Children.push_back(chan_row);
- chan_row->Parent = guild_row;
- m_id_to_row[chan_row->ID] = chan_row;
- }
+ Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
- // categories
- std::map> sorted_categories;
- if (guild_data->Channels.has_value())
- for (const auto &dc : *guild_data->Channels) {
- const auto channel = discord.GetChannel(dc.ID);
- if (!channel.has_value()) continue;
- if (channel->Type == ChannelType::GUILD_CATEGORY)
- sorted_categories[*channel->Position].push_back(*channel);
- }
+ m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
- for (auto &[pos, catvec] : sorted_categories) {
- std::sort(catvec.begin(), catvec.end(), [](const ChannelData &a, const ChannelData &b) { return a.ID < b.ID; });
- for (const auto cat : catvec) {
- auto *cat_row = Gtk::manage(new ChannelListRowCategory(&cat));
- cat_row->IsUserCollapsed = false;
- cat_row->signal_copy_id().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnMenuCopyID), cat_row->ID));
- insert_and_adjust(*cat_row);
- guild_row->Children.push_back(cat_row);
- cat_row->Parent = guild_row;
- m_id_to_row[cat_row->ID] = cat_row;
-
- // child channels
- if (cat_to_channels.find(cat.ID) == cat_to_channels.end()) continue;
- std::map sorted_channels;
-
- for (const auto channel : cat_to_channels.at(cat.ID))
- sorted_channels[*channel.Position] = channel;
-
- for (const auto &[pos, channel] : sorted_channels) {
- auto *chan_row = Gtk::manage(new ChannelListRowChannel(&channel));
- chan_row->IsUserCollapsed = false;
- insert_and_adjust(*chan_row);
- cat_row->Children.push_back(chan_row);
- chan_row->Parent = cat_row;
- m_id_to_row[chan_row->ID] = chan_row;
- }
- }
- }
+ Gdk::Cairo::set_source_pixbuf(cr, m_property_pixbuf.get_value(), icon_x, icon_y);
+ cr->rectangle(icon_x, icon_y, icon_w, icon_h);
+ cr->fill();
}
-void ChannelList::AddPrivateChannels() {
- const auto &discord = Abaddon::Get().GetDiscordClient();
- auto dms_ = discord.GetPrivateChannels();
- std::vector dms;
- for (const auto &x : dms_) {
- const auto chan = discord.GetChannel(x);
- dms.push_back(*chan);
- }
- std::sort(dms.begin(), dms.end(), [&](const ChannelData &a, const ChannelData &b) -> bool {
- return a.LastMessageID > b.LastMessageID;
- });
+// category
- m_dm_header_row = Gtk::manage(new ChannelListRowDMHeader);
- m_dm_header_row->show_all();
- m_dm_header_row->IsUserCollapsed = true;
- m_list->add(*m_dm_header_row);
-
- for (const auto &dm : dms) {
- auto *dm_row = Gtk::manage(new ChannelListRowDMChannel(&dm));
- dm_row->Parent = m_dm_header_row;
- m_id_to_row[dm.ID] = dm_row;
- dm_row->IsUserCollapsed = false;
- m_list->add(*dm_row);
- m_dm_header_row->Children.push_back(dm_row);
- }
+void CellRendererChannels::get_preferred_width_vfunc_category(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
+ m_renderer_text.get_preferred_width(widget, minimum_width, natural_width);
}
-void ChannelList::UpdateListing() {
- std::unordered_set guilds = Abaddon::Get().GetDiscordClient().GetGuilds();
+void CellRendererChannels::get_preferred_width_for_height_vfunc_category(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
+ m_renderer_text.get_preferred_width_for_height(widget, height, minimum_width, natural_width);
+}
- auto children = m_list->get_children();
- auto it = children.begin();
+void CellRendererChannels::get_preferred_height_vfunc_category(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
+ m_renderer_text.get_preferred_height(widget, minimum_height, natural_height);
+}
+
+void CellRendererChannels::get_preferred_height_for_width_vfunc_category(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
+ m_renderer_text.get_preferred_height_for_width(widget, width, minimum_height, natural_height);
+}
+
+void CellRendererChannels::render_vfunc_category(const Cairo::RefPtr &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
+ int available_xpad = background_area.get_width();
+ int available_ypad = background_area.get_height();
- while (it != children.end()) {
- delete *it;
- it++;
+ int x1, y1, x2, y2, x3, y3;
+ if (property_expanded()) {
+ x1 = background_area.get_x() + 7;
+ y1 = background_area.get_y() + 5;
+ x2 = background_area.get_x() + 12;
+ y2 = background_area.get_y() + background_area.get_height() - 5;
+ x3 = background_area.get_x() + 17;
+ y3 = background_area.get_y() + 5;
+ } else {
+ x1 = background_area.get_x() + 7;
+ y1 = background_area.get_y() + 4;
+ x2 = background_area.get_x() + 15;
+ y2 = (2 * background_area.get_y() + background_area.get_height()) / 2;
+ x3 = background_area.get_x() + 7;
+ y3 = background_area.get_y() + background_area.get_height() - 4;
}
+ cr->move_to(x1, y1);
+ cr->line_to(x2, y2);
+ cr->line_to(x3, y3);
+ cr->set_source_rgb(34.0 / 255.0, 112.0 / 255.0, 1.0);
+ cr->stroke();
- m_guild_id_to_row.clear();
- m_id_to_row.clear();
+ Gtk::Requisition text_minimum, text_natural;
+ m_renderer_text.get_preferred_size(widget, text_minimum, text_natural);
- m_guild_count = 0;
+ const int text_x = background_area.get_x() + 22;
+ const int text_y = background_area.get_y() + background_area.get_height() / 2 - text_natural.height / 2;
+ const int text_w = text_natural.width;
+ const int text_h = text_natural.height;
- AddPrivateChannels();
+ Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
- auto sorted_guilds = Abaddon::Get().GetDiscordClient().GetUserSortedGuilds();
- for (auto gid : sorted_guilds) {
- InsertGuildAt(gid, -1);
- }
+ m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
}
-void ChannelList::OnMenuCopyID(Snowflake id) {
- Gtk::Clipboard::get()->set_text(std::to_string(id));
-}
+// text channel
-void ChannelList::OnGuildMenuLeave(Snowflake id) {
- m_signal_action_guild_leave.emit(id);
+void CellRendererChannels::get_preferred_width_vfunc_channel(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
+ m_renderer_text.get_preferred_width(widget, minimum_width, natural_width);
}
-void ChannelList::OnGuildMenuSettings(Snowflake id) {
- m_signal_action_guild_settings.emit(id);
+void CellRendererChannels::get_preferred_width_for_height_vfunc_channel(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
+ m_renderer_text.get_preferred_width_for_height(widget, height, minimum_width, natural_width);
}
-void ChannelList::CheckBumpDM(Snowflake channel_id) {
- auto it = m_id_to_row.find(channel_id);
- if (it == m_id_to_row.end()) return;
- auto *row = it->second;
- const auto index = row->get_index();
- if (index == 1) return; // 1 is top of dm list
- const bool selected = row->is_selected();
- row->Parent->Children.erase(std::remove(row->Parent->Children.begin(), row->Parent->Children.end(), row), row->Parent->Children.end());
- delete row;
- const auto chan = Abaddon::Get().GetDiscordClient().GetChannel(channel_id);
- auto *dm_row = Gtk::manage(new ChannelListRowDMChannel(&*chan));
- dm_row->Parent = m_dm_header_row;
- m_dm_header_row->Children.push_back(dm_row);
- m_id_to_row[channel_id] = dm_row;
- dm_row->IsUserCollapsed = false;
- m_list->insert(*dm_row, 1);
- m_dm_header_row->Children.push_back(dm_row);
- if (selected)
- m_list->select_row(*dm_row);
- if (m_dm_header_row->is_visible() && !m_dm_header_row->IsUserCollapsed)
- dm_row->show();
+void CellRendererChannels::get_preferred_height_vfunc_channel(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
+ m_renderer_text.get_preferred_height(widget, minimum_height, natural_height);
}
-ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
- return m_signal_action_channel_item_select;
+void CellRendererChannels::get_preferred_height_for_width_vfunc_channel(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
+ m_renderer_text.get_preferred_height_for_width(widget, width, minimum_height, natural_height);
}
-ChannelList::type_signal_action_guild_leave ChannelList::signal_action_guild_leave() {
- return m_signal_action_guild_leave;
-}
+void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
+ Gtk::Requisition minimum_size, natural_size;
+ m_renderer_text.get_preferred_size(widget, minimum_size, natural_size);
-ChannelList::type_signal_action_guild_settings ChannelList::signal_action_guild_settings() {
- return m_signal_action_guild_settings;
+ const int text_x = background_area.get_x() + 5;
+ const int text_y = background_area.get_y() + background_area.get_height() / 2 - natural_size.height / 2;
+ const int text_w = natural_size.width;
+ const int text_h = natural_size.height;
+
+ Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
+
+ m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
}
diff --git a/components/channels.hpp b/components/channels.hpp
index 08b69fa..4ab8a23 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -10,110 +10,74 @@
static const constexpr int ChannelEmojiSize = 16;
-class ChannelListRow : public Gtk::ListBoxRow {
-public:
- bool IsUserCollapsed;
- Snowflake ID;
- std::vector Children;
- ChannelListRow *Parent = nullptr;
-
- virtual void Collapse();
- virtual void Expand();
-
- static void MakeReadOnly(Gtk::TextView *tv);
-};
-
-class ChannelListRowDMHeader : public ChannelListRow {
-public:
- ChannelListRowDMHeader();
-
-protected:
- Gtk::EventBox *m_ev;
- Gtk::Box *m_box;
- Gtk::Label *m_lbl;
-};
-
-class StatusIndicator;
-class ChannelListRowDMChannel : public ChannelListRow {
-public:
- ChannelListRowDMChannel(const ChannelData *data);
-
-protected:
- Gtk::EventBox *m_ev;
- Gtk::Box *m_box;
- StatusIndicator *m_status = nullptr;
- Gtk::TextView *m_lbl;
- Gtk::Image *m_icon = nullptr;
-
- Gtk::Menu m_menu;
- Gtk::MenuItem *m_menu_close; // leave if group
- Gtk::MenuItem *m_menu_copy_id;
-};
-
-class ChannelListRowGuild : public ChannelListRow {
-public:
- ChannelListRowGuild(const GuildData *data);
-
- int GuildIndex;
-
-protected:
- Gtk::EventBox *m_ev;
- Gtk::Box *m_box;
- Gtk::TextView *m_lbl;
- Gtk::Image *m_icon;
-
- Gtk::Menu m_menu;
- Gtk::MenuItem *m_menu_copyid;
- Gtk::MenuItem *m_menu_leave;
- Gtk::MenuItem *m_menu_settings;
-
-private:
- typedef sigc::signal type_signal_copy_id;
- typedef sigc::signal type_signal_leave;
- typedef sigc::signal type_signal_settings;
-
- type_signal_copy_id m_signal_copy_id;
- type_signal_leave m_signal_leave;
- type_signal_settings m_signal_settings;
-
-public:
- type_signal_copy_id signal_copy_id();
- type_signal_leave signal_leave();
- type_signal_settings signal_settings();
+enum class RenderType {
+ Guild,
+ Category,
+ TextChannel,
};
-class ChannelListRowCategory : public ChannelListRow {
+class CellRendererChannels : public Gtk::CellRenderer {
public:
- ChannelListRowCategory(const ChannelData *data);
+ CellRendererChannels();
+ virtual ~CellRendererChannels();
- virtual void Collapse();
- virtual void Expand();
+ Glib::PropertyProxy property_type();
+ Glib::PropertyProxy property_name();
+ Glib::PropertyProxy> property_icon();
+ Glib::PropertyProxy property_expanded();
protected:
- Gtk::EventBox *m_ev;
- Gtk::Box *m_box;
- Gtk::TextView *m_lbl;
- Gtk::Arrow *m_arrow;
-
- Gtk::Menu m_menu;
- Gtk::MenuItem *m_menu_copyid;
+ void get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const override;
+ void get_preferred_width_for_height_vfunc(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const override;
+ void get_preferred_height_vfunc(Gtk::Widget &widget, int &minimum_height, int &natural_height) const override;
+ void get_preferred_height_for_width_vfunc(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const override;
+ void render_vfunc(const Cairo::RefPtr &cr,
+ Gtk::Widget &widget,
+ const Gdk::Rectangle &background_area,
+ const Gdk::Rectangle &cell_area,
+ Gtk::CellRendererState flags) override;
+
+ // guild functions
+ void get_preferred_width_vfunc_guild(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
+ void get_preferred_width_for_height_vfunc_guild(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
+ void get_preferred_height_vfunc_guild(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
+ void get_preferred_height_for_width_vfunc_guild(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
+ void render_vfunc_guild(const Cairo::RefPtr &cr,
+ Gtk::Widget &widget,
+ const Gdk::Rectangle &background_area,
+ const Gdk::Rectangle &cell_area,
+ Gtk::CellRendererState flags);
+
+ // category
+ void get_preferred_width_vfunc_category(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
+ void get_preferred_width_for_height_vfunc_category(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
+ void get_preferred_height_vfunc_category(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
+ void get_preferred_height_for_width_vfunc_category(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
+ void render_vfunc_category(const Cairo::RefPtr &cr,
+ Gtk::Widget &widget,
+ const Gdk::Rectangle &background_area,
+ const Gdk::Rectangle &cell_area,
+ Gtk::CellRendererState flags);
+
+ // text channel
+ void get_preferred_width_vfunc_channel(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
+ void get_preferred_width_for_height_vfunc_channel(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
+ void get_preferred_height_vfunc_channel(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
+ void get_preferred_height_for_width_vfunc_channel(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
+ void render_vfunc_channel(const Cairo::RefPtr &cr,
+ Gtk::Widget &widget,
+ const Gdk::Rectangle &background_area,
+ const Gdk::Rectangle &cell_area,
+ Gtk::CellRendererState flags);
private:
- typedef sigc::signal type_signal_copy_id;
+ Gtk::CellRendererText m_renderer_text;
- type_signal_copy_id m_signal_copy_id;
+ Glib::Property m_property_type;
-public:
- type_signal_copy_id signal_copy_id();
-};
-
-class ChannelListRowChannel : public ChannelListRow {
-public:
- ChannelListRowChannel(const ChannelData *data);
-
-private:
- static Gtk::Menu *m_menu;
- static bool m_menu_init;
+ Glib::Property m_property_name; // guild
+ Glib::Property> m_property_pixbuf; // guild
+ Glib::Property m_property_expanded; // category
};
class ChannelList {
@@ -132,36 +96,28 @@ public:
void SetActiveChannel(Snowflake id);
protected:
- Gtk::ListBox *m_list;
Gtk::ScrolledWindow *m_main;
-
- ChannelListRowDMHeader *m_dm_header_row = nullptr;
-
- void CollapseRow(ChannelListRow *row);
- void ExpandRow(ChannelListRow *row);
- void DeleteRow(ChannelListRow *row);
-
- void UpdateChannelCategory(Snowflake id);
-
- void on_row_activated(Gtk::ListBoxRow *row);
-
- int m_guild_count;
- void OnMenuCopyID(Snowflake id);
- void OnGuildMenuLeave(Snowflake id);
- void OnGuildMenuSettings(Snowflake id);
-
- Gtk::Menu m_channel_menu;
- Gtk::MenuItem *m_channel_menu_copyid;
-
- // i would use one map but in really old guilds there can be a channel w/ same id as the guild so this hacky shit has to do
- std::unordered_map m_guild_id_to_row;
- std::unordered_map m_id_to_row;
-
- void InsertGuildAt(Snowflake id, int pos);
-
- void AddPrivateChannels();
-
- void CheckBumpDM(Snowflake channel_id);
+ Gtk::TreeView m_view;
+
+ class ModelColumns : public Gtk::TreeModel::ColumnRecord {
+ public:
+ ModelColumns();
+
+ Gtk::TreeModelColumn m_type;
+ Gtk::TreeModelColumn m_id;
+ Gtk::TreeModelColumn m_name;
+ Gtk::TreeModelColumn> m_icon;
+ Gtk::TreeModelColumn m_sort;
+ // Gtk::CellRenderer's property_is_expanded only works how i want it to if it has children
+ // because otherwise it doesnt count as an "expander" (property_is_expander)
+ // so this solution will have to do which i hate but the alternative is adding invisible children
+ // to all categories without children and having a filter model but that sounds worse
+ // of course its a lot better than the absolute travesty i had before
+ Gtk::TreeModelColumn m_expanded;
+ };
+
+ ModelColumns m_columns;
+ Glib::RefPtr m_model;
public:
typedef sigc::signal type_signal_action_channel_item_select;
--
cgit v1.2.3
From 67c944f219003e8632a887d85a81d92b307ecdce Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sat, 3 Jul 2021 19:47:13 -0400
Subject: channel list: handle guild create/delete
---
components/channels.cpp | 152 ++++++++++++++++++++++++++++++------------------
components/channels.hpp | 3 +
2 files changed, 97 insertions(+), 58 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 64f1c15..56534ba 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -54,6 +54,8 @@ Gtk::Widget *ChannelList::GetRoot() const {
}
void ChannelList::UpdateListing() {
+ m_model->clear();
+
auto &discord = Abaddon::Get().GetDiscordClient();
auto &img = Abaddon::Get().GetImageManager();
@@ -63,71 +65,32 @@ void ChannelList::UpdateListing() {
const auto guild = discord.GetGuild(guild_id);
if (!guild.has_value()) continue;
- auto guild_row = *m_model->append();
- guild_row[m_columns.m_type] = RenderType::Guild;
- 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(24);
- guild_row[m_columns.m_sort] = ++sortnum;
-
- if (guild->HasIcon()) {
- const auto cb = [this, guild_row](const Glib::RefPtr &pb) {
- guild_row[m_columns.m_icon] = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR);
- };
- img.LoadFromURL(guild->GetIconURL("png", "32"), sigc::track_obj(cb, *this));
- }
-
- if (!guild->Channels.has_value()) continue;
-
- // separate out the channels
- std::vector orphan_channels;
- std::map> categories;
-
- for (const auto &channel_ : *guild->Channels) {
- const auto channel = discord.GetChannel(channel_.ID);
- if (!channel.has_value()) continue;
- if (channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::GUILD_NEWS) {
- if (channel->ParentID.has_value())
- categories[*channel->ParentID].push_back(*channel);
- else
- orphan_channels.push_back(*channel);
- } else if (channel->Type == ChannelType::GUILD_CATEGORY) {
- categories[channel->ID];
- }
- }
-
- for (const auto &channel : orphan_channels) {
- auto channel_row = *m_model->append(guild_row.children());
- channel_row[m_columns.m_type] = RenderType::TextChannel;
- channel_row[m_columns.m_id] = channel.ID;
- channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
- channel_row[m_columns.m_sort] = *channel.Position - 100; // subtract 100 to make sure they stay behind categories
- }
-
- for (const auto &[category_id, channels] : categories) {
- const auto category = discord.GetChannel(category_id);
- if (!category.has_value()) continue;
- auto cat_row = *m_model->append(guild_row.children());
- cat_row[m_columns.m_type] = RenderType::Category;
- cat_row[m_columns.m_id] = category_id;
- cat_row[m_columns.m_name] = Glib::Markup::escape_text(*category->Name);
- cat_row[m_columns.m_sort] = *category->Position;
-
- for (const auto &channel : channels) {
- auto channel_row = *m_model->append(cat_row.children());
- channel_row[m_columns.m_type] = RenderType::TextChannel;
- channel_row[m_columns.m_id] = channel.ID;
- channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
- channel_row[m_columns.m_sort] = *channel.Position;
- }
- }
+ auto iter = AddGuild(*guild);
+ (*iter)[m_columns.m_sort] = sortnum++;
}
}
void ChannelList::UpdateNewGuild(Snowflake id) {
+ const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id);
+ auto &img = Abaddon::Get().GetImageManager();
+
+ if (!guild.has_value()) return;
+
+ auto iter = AddGuild(*guild);
+
+ // update sort order
+ int sortnum = 0;
+ for (const auto guild_id : Abaddon::Get().GetDiscordClient().GetUserSortedGuilds()) {
+ auto iter = GetIteratorForGuildFromID(guild_id);
+ if (iter)
+ (*iter)[m_columns.m_sort] = ++sortnum;
+ }
}
void ChannelList::UpdateRemoveGuild(Snowflake id) {
+ auto iter = GetIteratorForGuildFromID(id);
+ if (!iter) return;
+ m_model->erase(iter);
}
void ChannelList::UpdateRemoveChannel(Snowflake id) {
@@ -148,6 +111,79 @@ void ChannelList::UpdateGuild(Snowflake id) {
void ChannelList::SetActiveChannel(Snowflake id) {
}
+Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ auto &img = Abaddon::Get().GetImageManager();
+
+ auto guild_row = *m_model->append();
+ guild_row[m_columns.m_type] = RenderType::Guild;
+ 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(24);
+
+ if (guild.HasIcon()) {
+ const auto cb = [this, guild_row](const Glib::RefPtr &pb) {
+ guild_row[m_columns.m_icon] = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR);
+ };
+ img.LoadFromURL(guild.GetIconURL("png", "32"), sigc::track_obj(cb, *this));
+ }
+
+ if (!guild.Channels.has_value()) return guild_row;
+
+ // separate out the channels
+ std::vector orphan_channels;
+ std::map> categories;
+
+ for (const auto &channel_ : *guild.Channels) {
+ const auto channel = discord.GetChannel(channel_.ID);
+ if (!channel.has_value()) continue;
+ if (channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::GUILD_NEWS) {
+ if (channel->ParentID.has_value())
+ categories[*channel->ParentID].push_back(*channel);
+ else
+ orphan_channels.push_back(*channel);
+ } else if (channel->Type == ChannelType::GUILD_CATEGORY) {
+ categories[channel->ID];
+ }
+ }
+
+ for (const auto &channel : orphan_channels) {
+ auto channel_row = *m_model->append(guild_row.children());
+ channel_row[m_columns.m_type] = RenderType::TextChannel;
+ channel_row[m_columns.m_id] = channel.ID;
+ channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
+ channel_row[m_columns.m_sort] = *channel.Position - 100; // subtract 100 to make sure they stay behind categories
+ }
+
+ for (const auto &[category_id, channels] : categories) {
+ const auto category = discord.GetChannel(category_id);
+ if (!category.has_value()) continue;
+ auto cat_row = *m_model->append(guild_row.children());
+ cat_row[m_columns.m_type] = RenderType::Category;
+ cat_row[m_columns.m_id] = category_id;
+ cat_row[m_columns.m_name] = Glib::Markup::escape_text(*category->Name);
+ cat_row[m_columns.m_sort] = *category->Position;
+
+ for (const auto &channel : channels) {
+ auto channel_row = *m_model->append(cat_row.children());
+ channel_row[m_columns.m_type] = RenderType::TextChannel;
+ channel_row[m_columns.m_id] = channel.ID;
+ channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
+ channel_row[m_columns.m_sort] = *channel.Position;
+ }
+ }
+
+ return guild_row;
+}
+
+Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) {
+ for (const auto child : m_model->children()) {
+ if (child[m_columns.m_id] == id)
+ return child;
+ }
+ return {};
+}
+
ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
return m_signal_action_channel_item_select;
}
diff --git a/components/channels.hpp b/components/channels.hpp
index 4ab8a23..4907124 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -119,6 +119,9 @@ protected:
ModelColumns m_columns;
Glib::RefPtr m_model;
+ Gtk::TreeModel::iterator AddGuild(const GuildData &guild);
+ Gtk::TreeModel::iterator GetIteratorForGuildFromID(Snowflake id);
+
public:
typedef sigc::signal type_signal_action_channel_item_select;
typedef sigc::signal type_signal_action_guild_leave;
--
cgit v1.2.3
From f1504eca15b25431cfc58d2f5805495e342aecf4 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sat, 3 Jul 2021 21:11:51 -0400
Subject: handle channel remove
---
components/channels.cpp | 20 ++++++++++++++++++++
components/channels.hpp | 1 +
2 files changed, 21 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index 56534ba..cc23bb5 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -94,6 +94,9 @@ void ChannelList::UpdateRemoveGuild(Snowflake id) {
}
void ChannelList::UpdateRemoveChannel(Snowflake id) {
+ auto iter = GetIteratorForChannelFromID(id);
+ if (!iter) return;
+ m_model->erase(iter);
}
void ChannelList::UpdateChannel(Snowflake id) {
@@ -184,6 +187,23 @@ Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) {
return {};
}
+Gtk::TreeModel::iterator ChannelList::GetIteratorForChannelFromID(Snowflake id) {
+ std::queue queue;
+ for (const auto child : m_model->children())
+ for (const auto child2 : child.children())
+ queue.push(child2);
+
+ while (!queue.empty()) {
+ auto item = queue.front();
+ if ((*item)[m_columns.m_id] == id) return item;
+ for (const auto child : item->children())
+ queue.push(child);
+ queue.pop();
+ }
+
+ return {};
+}
+
ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
return m_signal_action_channel_item_select;
}
diff --git a/components/channels.hpp b/components/channels.hpp
index 4907124..bbe0369 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -121,6 +121,7 @@ protected:
Gtk::TreeModel::iterator AddGuild(const GuildData &guild);
Gtk::TreeModel::iterator GetIteratorForGuildFromID(Snowflake id);
+ Gtk::TreeModel::iterator GetIteratorForChannelFromID(Snowflake id);
public:
typedef sigc::signal type_signal_action_channel_item_select;
--
cgit v1.2.3
From 716627f47d06907759bab22cc063d0b891e1e082 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sat, 3 Jul 2021 22:09:53 -0400
Subject: handle update channel
---
components/channels.cpp | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index cc23bb5..1c36ab9 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -100,6 +100,15 @@ void ChannelList::UpdateRemoveChannel(Snowflake id) {
}
void ChannelList::UpdateChannel(Snowflake id) {
+ auto iter = GetIteratorForChannelFromID(id);
+ auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
+ if (!iter || !channel.has_value()) return;
+ (*iter)[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
+ const bool is_orphan = static_cast((*iter)[m_columns.m_sort]) < 0;
+ if (is_orphan)
+ (*iter)[m_columns.m_sort] = *channel->Position - 100;
+ else
+ (*iter)[m_columns.m_sort] = *channel->Position;
}
void ChannelList::UpdateCreateDMChannel(Snowflake id) {
--
cgit v1.2.3
From d0d5c655fc8c09915dc52164c18ff066d6355f0c Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sun, 4 Jul 2021 01:39:56 -0400
Subject: handle channel create
---
components/channels.cpp | 36 ++++++++++++++++++++++++++++++++++++
components/channels.hpp | 2 ++
2 files changed, 38 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index 1c36ab9..0668318 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -115,6 +115,29 @@ void ChannelList::UpdateCreateDMChannel(Snowflake id) {
}
void ChannelList::UpdateCreateChannel(Snowflake id) {
+ const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
+ if (!channel.has_value()) return;
+ if (channel->Type == ChannelType::GUILD_CATEGORY) return (void)UpdateCreateChannelCategory(*channel);
+ if (channel->Type != ChannelType::GUILD_TEXT && channel->Type != ChannelType::GUILD_NEWS) return;
+
+ Gtk::TreeRow channel_row;
+ bool orphan;
+ if (channel->ParentID.has_value()) {
+ orphan = false;
+ auto iter = GetIteratorForChannelFromID(*channel->ParentID);
+ channel_row = *m_model->append(iter->children());
+ } else {
+ orphan = true;
+ auto iter = GetIteratorForGuildFromID(*channel->GuildID);
+ channel_row = *m_model->append(iter->children());
+ }
+ channel_row[m_columns.m_type] = RenderType::TextChannel;
+ channel_row[m_columns.m_id] = channel->ID;
+ channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
+ if (orphan)
+ channel_row[m_columns.m_sort] = *channel->Position - 100;
+ else
+ channel_row[m_columns.m_sort] = *channel->Position;
}
void ChannelList::UpdateGuild(Snowflake id) {
@@ -188,6 +211,19 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
return guild_row;
}
+Gtk::TreeModel::iterator ChannelList::UpdateCreateChannelCategory(const ChannelData &channel) {
+ const auto iter = GetIteratorForGuildFromID(*channel.GuildID);
+ if (!iter) return {};
+
+ auto cat_row = *m_model->append(iter->children());
+ cat_row[m_columns.m_type] = RenderType::Category;
+ cat_row[m_columns.m_id] = channel.ID;
+ cat_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
+ cat_row[m_columns.m_sort] = *channel.Position;
+
+ return cat_row;
+}
+
Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) {
for (const auto child : m_model->children()) {
if (child[m_columns.m_id] == id)
diff --git a/components/channels.hpp b/components/channels.hpp
index bbe0369..529cb85 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -120,6 +120,8 @@ protected:
Glib::RefPtr m_model;
Gtk::TreeModel::iterator AddGuild(const GuildData &guild);
+ Gtk::TreeModel::iterator UpdateCreateChannelCategory(const ChannelData &channel);
+
Gtk::TreeModel::iterator GetIteratorForGuildFromID(Snowflake id);
Gtk::TreeModel::iterator GetIteratorForChannelFromID(Snowflake id);
--
cgit v1.2.3
From 4102db1eb9027e6d41f00893a5f4a5b3215ac07a Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sun, 4 Jul 2021 02:11:38 -0400
Subject: better channel update handling
---
components/channels.cpp | 38 ++++++++++++++++++++++++++++++++++----
components/channels.hpp | 4 ++++
2 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 0668318..80c321c 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -103,12 +103,30 @@ void ChannelList::UpdateChannel(Snowflake id) {
auto iter = GetIteratorForChannelFromID(id);
auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
if (!iter || !channel.has_value()) return;
- (*iter)[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
- const bool is_orphan = static_cast((*iter)[m_columns.m_sort]) < 0;
+ if (channel->Type == ChannelType::GUILD_CATEGORY) return UpdateChannelCategory(*channel);
+ if (!IsTextChannel(channel->Type)) return;
+
+ // delete and recreate
+ m_model->erase(iter);
+
+ Gtk::TreeStore::iterator parent;
+ bool is_orphan;
+ if (channel->ParentID.has_value()) {
+ is_orphan = false;
+ parent = GetIteratorForChannelFromID(*channel->ParentID);
+ } else {
+ is_orphan = true;
+ parent = GetIteratorForGuildFromID(*channel->GuildID);
+ }
+ if (!parent) return;
+ auto channel_row = *m_model->append(parent->children());
+ channel_row[m_columns.m_type] = RenderType::TextChannel;
+ channel_row[m_columns.m_id] = channel->ID;
+ channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
if (is_orphan)
- (*iter)[m_columns.m_sort] = *channel->Position - 100;
+ channel_row[m_columns.m_sort] = *channel->Position - 100;
else
- (*iter)[m_columns.m_sort] = *channel->Position;
+ channel_row[m_columns.m_sort] = *channel->Position;
}
void ChannelList::UpdateCreateDMChannel(Snowflake id) {
@@ -224,6 +242,14 @@ Gtk::TreeModel::iterator ChannelList::UpdateCreateChannelCategory(const ChannelD
return cat_row;
}
+void ChannelList::UpdateChannelCategory(const ChannelData &channel) {
+ auto iter = GetIteratorForChannelFromID(channel.ID);
+ if (!iter) return;
+
+ (*iter)[m_columns.m_sort] = *channel.Position;
+ (*iter)[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
+}
+
Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) {
for (const auto child : m_model->children()) {
if (child[m_columns.m_id] == id)
@@ -249,6 +275,10 @@ Gtk::TreeModel::iterator ChannelList::GetIteratorForChannelFromID(Snowflake id)
return {};
}
+bool ChannelList::IsTextChannel(ChannelType type) {
+ return type == ChannelType::GUILD_TEXT || type == ChannelType::GUILD_NEWS;
+}
+
ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
return m_signal_action_channel_item_select;
}
diff --git a/components/channels.hpp b/components/channels.hpp
index 529cb85..e40c2a0 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -122,9 +122,13 @@ protected:
Gtk::TreeModel::iterator AddGuild(const GuildData &guild);
Gtk::TreeModel::iterator UpdateCreateChannelCategory(const ChannelData &channel);
+ void UpdateChannelCategory(const ChannelData &channel);
+
Gtk::TreeModel::iterator GetIteratorForGuildFromID(Snowflake id);
Gtk::TreeModel::iterator GetIteratorForChannelFromID(Snowflake id);
+ bool IsTextChannel(ChannelType type);
+
public:
typedef sigc::signal type_signal_action_channel_item_select;
typedef sigc::signal type_signal_action_guild_leave;
--
cgit v1.2.3
From c154a63967264c5a22e5b4e4d4e7dfd83e265b9c Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sun, 4 Jul 2021 02:21:32 -0400
Subject: update guild
---
components/channels.cpp | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index 80c321c..5746259 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -159,6 +159,22 @@ void ChannelList::UpdateCreateChannel(Snowflake id) {
}
void ChannelList::UpdateGuild(Snowflake id) {
+ auto iter = GetIteratorForGuildFromID(id);
+ auto &img = Abaddon::Get().GetImageManager();
+ const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id);
+ if (!iter || !guild.has_value()) return;
+
+ (*iter)[m_columns.m_name] = "" + Glib::Markup::escape_text(guild->Name) + "";
+ (*iter)[m_columns.m_icon] = img.GetPlaceholder(24);
+ if (guild->HasIcon()) {
+ const auto cb = [this, id](const Glib::RefPtr &pb) {
+ // iter might be invalid
+ auto iter = GetIteratorForGuildFromID(id);
+ if (iter)
+ (*iter)[m_columns.m_icon] = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR);
+ };
+ img.LoadFromURL(guild->GetIconURL("png", "32"), sigc::track_obj(cb, *this));
+ }
}
void ChannelList::SetActiveChannel(Snowflake id) {
--
cgit v1.2.3
From 87d5faf30b6d73829f9b0d364519db5af3439afb Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sun, 4 Jul 2021 02:36:12 -0400
Subject: some refactorage
---
components/channels.cpp | 18 ++++++++++--------
components/channels.hpp | 4 +++-
2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 5746259..3ace286 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -124,7 +124,7 @@ void ChannelList::UpdateChannel(Snowflake id) {
channel_row[m_columns.m_id] = channel->ID;
channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
if (is_orphan)
- channel_row[m_columns.m_sort] = *channel->Position - 100;
+ channel_row[m_columns.m_sort] = *channel->Position + OrphanChannelSortOffset;
else
channel_row[m_columns.m_sort] = *channel->Position;
}
@@ -153,7 +153,7 @@ void ChannelList::UpdateCreateChannel(Snowflake id) {
channel_row[m_columns.m_id] = channel->ID;
channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
if (orphan)
- channel_row[m_columns.m_sort] = *channel->Position - 100;
+ channel_row[m_columns.m_sort] = *channel->Position + OrphanChannelSortOffset;
else
channel_row[m_columns.m_sort] = *channel->Position;
}
@@ -165,13 +165,13 @@ void ChannelList::UpdateGuild(Snowflake id) {
if (!iter || !guild.has_value()) return;
(*iter)[m_columns.m_name] = "" + Glib::Markup::escape_text(guild->Name) + "";
- (*iter)[m_columns.m_icon] = img.GetPlaceholder(24);
+ (*iter)[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize);
if (guild->HasIcon()) {
const auto cb = [this, id](const Glib::RefPtr &pb) {
// iter might be invalid
auto iter = GetIteratorForGuildFromID(id);
if (iter)
- (*iter)[m_columns.m_icon] = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR);
+ (*iter)[m_columns.m_icon] = pb->scale_simple(GuildIconSize, GuildIconSize, Gdk::INTERP_BILINEAR);
};
img.LoadFromURL(guild->GetIconURL("png", "32"), sigc::track_obj(cb, *this));
}
@@ -188,11 +188,13 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
guild_row[m_columns.m_type] = RenderType::Guild;
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(24);
+ guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize);
if (guild.HasIcon()) {
- const auto cb = [this, guild_row](const Glib::RefPtr &pb) {
- guild_row[m_columns.m_icon] = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR);
+ const auto cb = [this, id = guild.ID](const Glib::RefPtr &pb) {
+ auto iter = GetIteratorForGuildFromID(id);
+ if (iter)
+ (*iter)[m_columns.m_icon] = pb->scale_simple(GuildIconSize, GuildIconSize, Gdk::INTERP_BILINEAR);
};
img.LoadFromURL(guild.GetIconURL("png", "32"), sigc::track_obj(cb, *this));
}
@@ -221,7 +223,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
channel_row[m_columns.m_type] = RenderType::TextChannel;
channel_row[m_columns.m_id] = channel.ID;
channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
- channel_row[m_columns.m_sort] = *channel.Position - 100; // subtract 100 to make sure they stay behind categories
+ channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset;
}
for (const auto &[category_id, channels] : categories) {
diff --git a/components/channels.hpp b/components/channels.hpp
index e40c2a0..3a33925 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -8,7 +8,8 @@
#include
#include "../discord/discord.hpp"
-static const constexpr int ChannelEmojiSize = 16;
+constexpr static int GuildIconSize = 24;
+constexpr static int OrphanChannelSortOffset = -100; // forces orphan channels to the top of the list
enum class RenderType {
Guild,
@@ -124,6 +125,7 @@ protected:
void UpdateChannelCategory(const ChannelData &channel);
+ // separation necessary because a channel and guild can share the same id
Gtk::TreeModel::iterator GetIteratorForGuildFromID(Snowflake id);
Gtk::TreeModel::iterator GetIteratorForChannelFromID(Snowflake id);
--
cgit v1.2.3
From 09872cf4261d680fc6e38db6c4dcf0b24451f6b4 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Mon, 5 Jul 2021 00:10:05 -0400
Subject: expand categories by default and preserve expansion
---
components/channels.cpp | 12 ++++++++++++
components/channels.hpp | 2 ++
2 files changed, 14 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index 3ace286..bbfaf8a 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -25,6 +25,7 @@ ChannelList::ChannelList()
}
};
m_view.signal_row_activated().connect(cb);
+ m_view.signal_row_expanded().connect(sigc::mem_fun(*this, &ChannelList::OnRowExpanded));
m_view.set_activate_on_single_click(true);
m_view.set_hexpand(true);
@@ -234,6 +235,8 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
cat_row[m_columns.m_id] = category_id;
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_view.expand_row wont work because it might not have channels
for (const auto &channel : channels) {
auto channel_row = *m_model->append(cat_row.children());
@@ -256,6 +259,7 @@ Gtk::TreeModel::iterator ChannelList::UpdateCreateChannelCategory(const ChannelD
cat_row[m_columns.m_id] = channel.ID;
cat_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
cat_row[m_columns.m_sort] = *channel.Position;
+ cat_row[m_columns.m_expanded] = true;
return cat_row;
}
@@ -297,6 +301,14 @@ bool ChannelList::IsTextChannel(ChannelType type) {
return type == ChannelType::GUILD_TEXT || type == ChannelType::GUILD_NEWS;
}
+void ChannelList::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_model->get_path(it), false);
+ }
+}
+
ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
return m_signal_action_channel_item_select;
}
diff --git a/components/channels.hpp b/components/channels.hpp
index 3a33925..f98cafb 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -131,6 +131,8 @@ protected:
bool IsTextChannel(ChannelType type);
+ void OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
+
public:
typedef sigc::signal type_signal_action_channel_item_select;
typedef sigc::signal type_signal_action_guild_leave;
--
cgit v1.2.3
From ab2c7bed88a0a6519e17d4515e7402b9e5f9dd55 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Mon, 5 Jul 2021 17:04:59 -0400
Subject: tweak the arrow
---
components/channels.cpp | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index bbfaf8a..361b668 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -507,21 +507,23 @@ void CellRendererChannels::render_vfunc_category(const Cairo::RefPtrmove_to(x1, y1);
cr->line_to(x2, y2);
--
cgit v1.2.3
From 3565ec885ef631fbfe86c5b2da287ecce0ddf23b Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Mon, 5 Jul 2021 17:09:19 -0400
Subject: try to fix some weird behavior
---
components/channels.cpp | 10 ++++++++--
components/channels.hpp | 1 +
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 361b668..bde5639 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -24,8 +24,9 @@ ChannelList::ChannelList()
m_signal_action_channel_item_select.emit(static_cast(row[m_columns.m_id]));
}
};
- m_view.signal_row_activated().connect(cb);
- m_view.signal_row_expanded().connect(sigc::mem_fun(*this, &ChannelList::OnRowExpanded));
+ 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.set_activate_on_single_click(true);
m_view.set_hexpand(true);
@@ -301,6 +302,11 @@ bool ChannelList::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) {
+ (*iter)[m_columns.m_expanded] = false;
+}
+
void ChannelList::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++) {
diff --git a/components/channels.hpp b/components/channels.hpp
index f98cafb..4854c98 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -131,6 +131,7 @@ protected:
bool IsTextChannel(ChannelType type);
+ void OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
void OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
public:
--
cgit v1.2.3
From 9ec52e3473f4e90ba940324a5d058afefc0bd8f1 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Mon, 5 Jul 2021 17:35:53 -0400
Subject: make ChannelList directly subclass a container
---
components/channels.cpp | 12 ++++--------
components/channels.hpp | 4 +---
windows/mainwindow.cpp | 13 ++++++-------
3 files changed, 11 insertions(+), 18 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index bde5639..520c8fd 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -8,8 +8,9 @@
#include "statusindicator.hpp"
ChannelList::ChannelList()
- : m_model(Gtk::TreeStore::create(m_columns))
- , m_main(Gtk::manage(new Gtk::ScrolledWindow)) {
+ : Glib::ObjectBase(typeid(ChannelList))
+ , Gtk::ScrolledWindow()
+ , m_model(Gtk::TreeStore::create(m_columns)) {
const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) {
auto row = *m_model->get_iter(path);
if (row[m_columns.m_expanded]) {
@@ -38,8 +39,7 @@ ChannelList::ChannelList()
m_view.show();
- m_main->add(m_view);
- m_main->show_all();
+ add(m_view);
auto *column = Gtk::manage(new Gtk::TreeView::Column("display"));
auto *renderer = Gtk::manage(new CellRendererChannels);
@@ -51,10 +51,6 @@ ChannelList::ChannelList()
m_view.append_column(*column);
}
-Gtk::Widget *ChannelList::GetRoot() const {
- return m_main;
-}
-
void ChannelList::UpdateListing() {
m_model->clear();
diff --git a/components/channels.hpp b/components/channels.hpp
index 4854c98..0bc0b04 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -81,10 +81,9 @@ private:
Glib::Property m_property_expanded; // category
};
-class ChannelList {
+class ChannelList : public Gtk::ScrolledWindow {
public:
ChannelList();
- Gtk::Widget *GetRoot() const;
void UpdateListing();
void UpdateNewGuild(Snowflake id);
void UpdateRemoveGuild(Snowflake id);
@@ -97,7 +96,6 @@ public:
void SetActiveChannel(Snowflake id);
protected:
- Gtk::ScrolledWindow *m_main;
Gtk::TreeView m_view;
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
diff --git a/windows/mainwindow.cpp b/windows/mainwindow.cpp
index 35846b7..77bd6d3 100644
--- a/windows/mainwindow.cpp
+++ b/windows/mainwindow.cpp
@@ -100,7 +100,6 @@ MainWindow::MainWindow()
m_main_box.add(m_content_box);
m_main_box.show();
- auto *channel_list = m_channel_list.GetRoot();
auto *member_list = m_members.GetRoot();
auto *chat = m_chat.GetRoot();
@@ -108,9 +107,9 @@ MainWindow::MainWindow()
chat->set_hexpand(true);
chat->show();
- channel_list->set_vexpand(true);
- channel_list->set_size_request(-1, -1);
- channel_list->show();
+ m_channel_list.set_vexpand(true);
+ m_channel_list.set_size_request(-1, -1);
+ m_channel_list.show();
member_list->set_vexpand(true);
member_list->show();
@@ -126,10 +125,10 @@ MainWindow::MainWindow()
m_content_stack.set_visible_child("chat");
m_content_stack.show();
- m_chan_content_paned.pack1(*channel_list);
+ m_chan_content_paned.pack1(m_channel_list);
m_chan_content_paned.pack2(m_content_members_paned);
- m_chan_content_paned.child_property_shrink(*channel_list) = false;
- m_chan_content_paned.child_property_resize(*channel_list) = false;
+ m_chan_content_paned.child_property_shrink(m_channel_list) = false;
+ m_chan_content_paned.child_property_resize(m_channel_list) = false;
m_chan_content_paned.set_position(200);
m_chan_content_paned.show();
m_content_box.add(m_chan_content_paned);
--
cgit v1.2.3
From ecf8fb6a5f0cdd674c79fec78698aa66099f5fc7 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Tue, 6 Jul 2021 02:38:27 -0400
Subject: fix up channel row selection to work how i want it to
---
components/channels.cpp | 16 ++++++++++++++++
components/channels.hpp | 3 +++
css/application-low-priority.css | 3 +++
3 files changed, 22 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index 520c8fd..a085d62 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -29,6 +29,8 @@ ChannelList::ChannelList()
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.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.set_hexpand(true);
m_view.set_vexpand(true);
@@ -309,6 +311,20 @@ void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk:
if ((*it)[m_columns.m_expanded])
m_view.expand_row(m_model->get_path(it), false);
}
+
+ // try and restore selection if previous collapsed
+ if (auto selection = m_view.get_selection(); selection && !selection->get_selected()) {
+ selection->select(m_last_selected);
+ }
+}
+
+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);
+
+ auto iter = m_model->get_iter(path);
+ return (*iter)[m_columns.m_type] == RenderType::TextChannel;
}
ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
diff --git a/components/channels.hpp b/components/channels.hpp
index 0bc0b04..c0f0d33 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -131,6 +131,9 @@ protected:
void OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
void OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
+ bool SelectionFunc(const Glib::RefPtr &model, const Gtk::TreeModel::Path &path, bool is_currently_selected);
+
+ Gtk::TreeModel::Path m_last_selected;
public:
typedef sigc::signal type_signal_action_channel_item_select;
diff --git a/css/application-low-priority.css b/css/application-low-priority.css
index 449127b..cf108f4 100644
--- a/css/application-low-priority.css
+++ b/css/application-low-priority.css
@@ -38,6 +38,9 @@ has to be separate to allow main.css to override certain things
.app-window treeview {
color: @text_color;
+}
+
+.app-window treeview:not(:selected) {
background: @secondary_color;
}
--
cgit v1.2.3
From 8c3752ef9f9522cff6496c3a53cefb1f48fc5c92 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Tue, 13 Jul 2021 20:09:01 -0400
Subject: add private channels to list
---
components/channels.cpp | 220 +++++++++++++++++++++++++++++++++++++++++++++---
components/channels.hpp | 34 +++++++-
2 files changed, 242 insertions(+), 12 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index a085d62..1f730a9 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -21,7 +21,7 @@ ChannelList::ChannelList()
row[m_columns.m_expanded] = true;
}
- if (row[m_columns.m_type] == RenderType::TextChannel) {
+ if (row[m_columns.m_type] == RenderType::TextChannel || row[m_columns.m_type] == RenderType::DM) {
m_signal_action_channel_item_select.emit(static_cast(row[m_columns.m_id]));
}
};
@@ -51,6 +51,8 @@ ChannelList::ChannelList()
column->add_attribute(renderer->property_name(), m_columns.m_name);
column->add_attribute(renderer->property_expanded(), m_columns.m_expanded);
m_view.append_column(*column);
+
+ Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &ChannelList::OnMessageCreate));
}
void ChannelList::UpdateListing() {
@@ -68,6 +70,8 @@ void ChannelList::UpdateListing() {
auto iter = AddGuild(*guild);
(*iter)[m_columns.m_sort] = sortnum++;
}
+
+ AddPrivateChannels();
}
void ChannelList::UpdateNewGuild(Snowflake id) {
@@ -136,6 +140,7 @@ void ChannelList::UpdateCreateChannel(Snowflake id) {
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
if (!channel.has_value()) return;
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;
Gtk::TreeRow channel_row;
@@ -323,8 +328,89 @@ bool ChannelList::SelectionFunc(const Glib::RefPtr &model, const
if (auto row = selection->get_selected())
m_last_selected = m_model->get_path(row);
- auto iter = m_model->get_iter(path);
- return (*iter)[m_columns.m_type] == RenderType::TextChannel;
+ auto type = (*m_model->get_iter(path))[m_columns.m_type];
+ return type == RenderType::TextChannel || type == RenderType::DM;
+}
+
+void ChannelList::AddPrivateChannels() {
+ auto header_row = *m_model->append();
+ header_row[m_columns.m_type] = RenderType::DMHeader;
+ header_row[m_columns.m_sort] = -1;
+ header_row[m_columns.m_name] = "Direct Messages";
+ m_dm_header = m_model->get_path(header_row);
+
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ auto &img = Abaddon::Get().GetImageManager();
+
+ const auto dm_ids = discord.GetPrivateChannels();
+ for (const auto dm_id : dm_ids) {
+ const auto dm = discord.GetChannel(dm_id);
+ if (!dm.has_value()) continue;
+
+ std::optional top_recipient;
+ const auto recipients = dm->GetDMRecipients();
+ if (recipients.size() > 0)
+ top_recipient = recipients[0];
+
+ auto iter = m_model->append(header_row->children());
+ auto row = *iter;
+ row[m_columns.m_type] = RenderType::DM;
+ row[m_columns.m_id] = dm_id;
+ row[m_columns.m_sort] = -(dm->LastMessageID.has_value() ? *dm->LastMessageID : dm_id);
+ row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize);
+
+ if (dm->Type == ChannelType::DM && top_recipient.has_value())
+ row[m_columns.m_name] = Glib::Markup::escape_text(top_recipient->Username);
+ else if (dm->Type == ChannelType::GROUP_DM)
+ row[m_columns.m_name] = std::to_string(recipients.size()) + " members";
+
+ if (top_recipient.has_value()) {
+ const auto cb = [this, iter](const Glib::RefPtr &pb) {
+ if (iter)
+ (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR);
+ };
+ img.LoadFromURL(top_recipient->GetAvatarURL("png", "32"), sigc::track_obj(cb, *this));
+ }
+ }
+}
+
+void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) {
+ auto header_row = m_model->get_iter(m_dm_header);
+ auto &img = Abaddon::Get().GetImageManager();
+
+ std::optional top_recipient;
+ const auto recipients = dm.GetDMRecipients();
+ if (recipients.size() > 0)
+ top_recipient = recipients[0];
+
+ auto iter = m_model->append(header_row->children());
+ auto row = *iter;
+ row[m_columns.m_type] = RenderType::DM;
+ row[m_columns.m_id] = dm.ID;
+ row[m_columns.m_sort] = -(dm.LastMessageID.has_value() ? *dm.LastMessageID : dm.ID);
+ row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize);
+
+ if (dm.Type == ChannelType::DM && top_recipient.has_value())
+ row[m_columns.m_name] = Glib::Markup::escape_text(top_recipient->Username);
+ else if (dm.Type == ChannelType::GROUP_DM)
+ row[m_columns.m_name] = std::to_string(recipients.size()) + " members";
+
+ if (top_recipient.has_value()) {
+ const auto cb = [this, iter](const Glib::RefPtr &pb) {
+ if (iter)
+ (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR);
+ };
+ img.LoadFromURL(top_recipient->GetAvatarURL("png", "32"), sigc::track_obj(cb, *this));
+ }
+}
+
+void ChannelList::OnMessageCreate(const Message &msg) {
+ const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(msg.ChannelID);
+ if (!channel.has_value()) return;
+ if (channel->Type != ChannelType::DM && channel->Type != ChannelType::GROUP_DM) return;
+ auto iter = GetIteratorForChannelFromID(msg.ChannelID);
+ if (iter)
+ (*iter)[m_columns.m_sort] = -msg.ID;
}
ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
@@ -390,6 +476,10 @@ void CellRendererChannels::get_preferred_width_vfunc(Gtk::Widget &widget, int &m
return get_preferred_width_vfunc_category(widget, minimum_width, natural_width);
case RenderType::TextChannel:
return get_preferred_width_vfunc_channel(widget, minimum_width, natural_width);
+ case RenderType::DMHeader:
+ return get_preferred_width_vfunc_dmheader(widget, minimum_width, natural_width);
+ case RenderType::DM:
+ return get_preferred_width_vfunc_dm(widget, minimum_width, natural_width);
}
}
@@ -401,6 +491,10 @@ void CellRendererChannels::get_preferred_width_for_height_vfunc(Gtk::Widget &wid
return get_preferred_width_for_height_vfunc_category(widget, height, minimum_width, natural_width);
case RenderType::TextChannel:
return get_preferred_width_for_height_vfunc_channel(widget, height, minimum_width, natural_width);
+ case RenderType::DMHeader:
+ return get_preferred_width_for_height_vfunc_dmheader(widget, height, minimum_width, natural_width);
+ case RenderType::DM:
+ return get_preferred_width_for_height_vfunc_dm(widget, height, minimum_width, natural_width);
}
}
@@ -412,6 +506,10 @@ void CellRendererChannels::get_preferred_height_vfunc(Gtk::Widget &widget, int &
return get_preferred_height_vfunc_category(widget, minimum_height, natural_height);
case RenderType::TextChannel:
return get_preferred_height_vfunc_channel(widget, minimum_height, natural_height);
+ case RenderType::DMHeader:
+ return get_preferred_height_vfunc_dmheader(widget, minimum_height, natural_height);
+ case RenderType::DM:
+ return get_preferred_height_vfunc_dm(widget, minimum_height, natural_height);
}
}
@@ -423,6 +521,10 @@ void CellRendererChannels::get_preferred_height_for_width_vfunc(Gtk::Widget &wid
return get_preferred_height_for_width_vfunc_category(widget, width, minimum_height, natural_height);
case RenderType::TextChannel:
return get_preferred_height_for_width_vfunc_channel(widget, width, minimum_height, natural_height);
+ case RenderType::DMHeader:
+ return get_preferred_height_for_width_vfunc_dmheader(widget, width, minimum_height, natural_height);
+ case RenderType::DM:
+ return get_preferred_height_for_width_vfunc_dm(widget, width, minimum_height, natural_height);
}
}
@@ -434,6 +536,10 @@ void CellRendererChannels::render_vfunc(const Cairo::RefPtr &cr,
return render_vfunc_category(cr, widget, background_area, cell_area, flags);
case RenderType::TextChannel:
return render_vfunc_channel(cr, widget, background_area, cell_area, flags);
+ case RenderType::DMHeader:
+ return render_vfunc_dmheader(cr, widget, background_area, cell_area, flags);
+ case RenderType::DM:
+ return render_vfunc_dm(cr, widget, background_area, cell_area, flags);
}
}
@@ -484,15 +590,15 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtrget_width();
- const int icon_h = pixbuf->get_height();
+ const double icon_w = pixbuf->get_width();
+ const double icon_h = pixbuf->get_height();
+ const double icon_x = background_area.get_x();
+ const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - icon_h / 2.0;
- const int text_x = icon_x + icon_w + 5;
- const int text_y = background_area.get_y() + background_area.get_height() / 2 - text_natural.height / 2;
- const int text_w = text_natural.width;
- const int text_h = text_natural.height;
+ const double text_x = icon_x + icon_w + 5.0;
+ const double text_y = background_area.get_y() + background_area.get_height() / 2.0 - text_natural.height / 2.0;
+ const double text_w = text_natural.width;
+ const double text_h = text_natural.height;
Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
@@ -593,3 +699,95 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
+ // gdk::rectangle more like gdk::stupid
+ Gdk::Rectangle text_cell_area(
+ cell_area.get_x() + 9, cell_area.get_y(), // maybe theres a better way to align this ?
+ cell_area.get_width(), cell_area.get_height());
+ m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
+}
+
+// dm (basically the same thing as guild)
+
+void CellRendererChannels::get_preferred_width_vfunc_dm(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
+ int pixbuf_width = 0;
+ if (auto pixbuf = m_property_pixbuf.get_value())
+ pixbuf_width = pixbuf->get_width();
+
+ int text_min, text_nat;
+ m_renderer_text.get_preferred_width(widget, text_min, text_nat);
+
+ int xpad, ypad;
+ get_padding(xpad, ypad);
+ minimum_width = std::max(text_min, pixbuf_width) + xpad * 2;
+ natural_width = std::max(text_nat, pixbuf_width) + xpad * 2;
+}
+
+void CellRendererChannels::get_preferred_width_for_height_vfunc_dm(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
+ get_preferred_width_vfunc_guild(widget, minimum_width, natural_width);
+}
+
+void CellRendererChannels::get_preferred_height_vfunc_dm(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
+ int pixbuf_height = 0;
+ if (auto pixbuf = m_property_pixbuf.get_value())
+ pixbuf_height = pixbuf->get_height();
+
+ int text_min, text_nat;
+ m_renderer_text.get_preferred_height(widget, text_min, text_nat);
+
+ int xpad, ypad;
+ get_padding(xpad, ypad);
+ minimum_height = std::max(text_min, pixbuf_height) + ypad * 2;
+ natural_height = std::max(text_nat, pixbuf_height) + ypad * 2;
+}
+
+void CellRendererChannels::get_preferred_height_for_width_vfunc_dm(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
+ get_preferred_height_vfunc_guild(widget, minimum_height, natural_height);
+}
+
+void CellRendererChannels::render_vfunc_dm(const Cairo::RefPtr &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
+ Gtk::Requisition text_minimum, text_natural;
+ m_renderer_text.get_preferred_size(widget, text_minimum, text_natural);
+
+ Gtk::Requisition minimum, natural;
+ get_preferred_size(widget, minimum, natural);
+
+ auto pixbuf = m_property_pixbuf.get_value();
+
+ const double icon_w = pixbuf->get_width();
+ const double icon_h = pixbuf->get_height();
+ const double icon_x = background_area.get_x() + 2;
+ const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - icon_h / 2.0;
+
+ const double text_x = icon_x + icon_w + 5.0;
+ const double text_y = background_area.get_y() + background_area.get_height() / 2.0 - text_natural.height / 2.0;
+ const double text_w = text_natural.width;
+ const double text_h = text_natural.height;
+
+ Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
+
+ m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
+
+ Gdk::Cairo::set_source_pixbuf(cr, m_property_pixbuf.get_value(), icon_x, icon_y);
+ cr->rectangle(icon_x, icon_y, icon_w, icon_h);
+ cr->fill();
+}
diff --git a/components/channels.hpp b/components/channels.hpp
index c0f0d33..d38bf30 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -9,12 +9,16 @@
#include "../discord/discord.hpp"
constexpr static int GuildIconSize = 24;
+constexpr static int DMIconSize = 20;
constexpr static int OrphanChannelSortOffset = -100; // forces orphan channels to the top of the list
enum class RenderType {
Guild,
Category,
TextChannel,
+
+ DMHeader,
+ DM,
};
class CellRendererChannels : public Gtk::CellRenderer {
@@ -71,6 +75,28 @@ protected:
const Gdk::Rectangle &cell_area,
Gtk::CellRendererState flags);
+ // dm header
+ void get_preferred_width_vfunc_dmheader(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
+ void get_preferred_width_for_height_vfunc_dmheader(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
+ void get_preferred_height_vfunc_dmheader(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
+ void get_preferred_height_for_width_vfunc_dmheader(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
+ void render_vfunc_dmheader(const Cairo::RefPtr &cr,
+ Gtk::Widget &widget,
+ const Gdk::Rectangle &background_area,
+ const Gdk::Rectangle &cell_area,
+ Gtk::CellRendererState flags);
+
+ // dm
+ void get_preferred_width_vfunc_dm(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
+ void get_preferred_width_for_height_vfunc_dm(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
+ void get_preferred_height_vfunc_dm(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
+ void get_preferred_height_for_width_vfunc_dm(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
+ void render_vfunc_dm(const Cairo::RefPtr &cr,
+ Gtk::Widget &widget,
+ const Gdk::Rectangle &background_area,
+ const Gdk::Rectangle &cell_area,
+ Gtk::CellRendererState flags);
+
private:
Gtk::CellRendererText m_renderer_text;
@@ -106,7 +132,7 @@ protected:
Gtk::TreeModelColumn m_id;
Gtk::TreeModelColumn m_name;
Gtk::TreeModelColumn> m_icon;
- Gtk::TreeModelColumn m_sort;
+ Gtk::TreeModelColumn m_sort;
// Gtk::CellRenderer's property_is_expanded only works how i want it to if it has children
// because otherwise it doesnt count as an "expander" (property_is_expander)
// so this solution will have to do which i hate but the alternative is adding invisible children
@@ -134,6 +160,12 @@ protected:
bool SelectionFunc(const Glib::RefPtr &model, const Gtk::TreeModel::Path &path, bool is_currently_selected);
Gtk::TreeModel::Path m_last_selected;
+ Gtk::TreeModel::Path m_dm_header;
+
+ void AddPrivateChannels();
+ void UpdateCreateDMChannel(const ChannelData &channel);
+
+ void OnMessageCreate(const Message &msg);
public:
typedef sigc::signal type_signal_action_channel_item_select;
--
cgit v1.2.3
From a30f7346f7d234566ab3f18c979e4a5d7bf0c83a Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Wed, 14 Jul 2021 03:14:30 -0400
Subject: restore expansion cuz gtk behaves annoyingly
---
components/channels.cpp | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index 1f730a9..38793f3 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -39,6 +39,12 @@ ChannelList::ChannelList()
m_view.set_model(m_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);
+ });
+
m_view.show();
add(m_view);
@@ -56,6 +62,8 @@ ChannelList::ChannelList()
}
void ChannelList::UpdateListing() {
+ m_updating_listing = true;
+
m_model->clear();
auto &discord = Abaddon::Get().GetDiscordClient();
@@ -71,6 +79,8 @@ void ChannelList::UpdateListing() {
(*iter)[m_columns.m_sort] = sortnum++;
}
+ m_updating_listing = false;
+
AddPrivateChannels();
}
--
cgit v1.2.3
From e74e6f2342eecffe3a690c8977e372ea957bdef5 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Fri, 16 Jul 2021 23:46:03 -0400
Subject: fix build
---
components/channels.hpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/components/channels.hpp b/components/channels.hpp
index d38bf30..b5f2ad9 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -167,6 +167,8 @@ protected:
void OnMessageCreate(const Message &msg);
+ bool m_updating_listing = false;
+
public:
typedef sigc::signal type_signal_action_channel_item_select;
typedef sigc::signal type_signal_action_guild_leave;
--
cgit v1.2.3
From 8a4283edd126366472af588ebf89580eca9c95c6 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Sun, 18 Jul 2021 03:33:16 -0400
Subject: add menu items
---
components/channels.cpp | 92 +++++++++++++++++++++++++++++++++++++++++++++++--
components/channels.hpp | 17 +++++++++
2 files changed, 106 insertions(+), 3 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 38793f3..e67d014 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -8,9 +8,18 @@
#include "statusindicator.hpp"
ChannelList::ChannelList()
- : Glib::ObjectBase(typeid(ChannelList))
+ : Glib::ObjectBase("channellist")
, Gtk::ScrolledWindow()
- , m_model(Gtk::TreeStore::create(m_columns)) {
+ , m_model(Gtk::TreeStore::create(m_columns))
+ , m_menu_guild_copy_id("_Copy ID", true)
+ , m_menu_guild_settings("View _Settings", true)
+ , m_menu_guild_leave("_Leave", true)
+ , m_menu_category_copy_id("_Copy ID", true)
+ , m_menu_channel_copy_id("_Copy ID", true)
+ , m_menu_dm_close("") // changes depending on if group or not
+ , m_menu_dm_copy_id("_Copy ID", true) {
+ get_style_context()->add_class("channel-list");
+
const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) {
auto row = *m_model->get_iter(path);
if (row[m_columns.m_expanded]) {
@@ -31,6 +40,7 @@ ChannelList::ChannelList()
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.set_hexpand(true);
m_view.set_vexpand(true);
@@ -58,6 +68,50 @@ ChannelList::ChannelList()
column->add_attribute(renderer->property_expanded(), m_columns.m_expanded);
m_view.append_column(*column);
+ m_menu_guild_copy_id.signal_activate().connect([this] {
+ Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
+ });
+ m_menu_guild_settings.signal_activate().connect([this] {
+ m_signal_action_guild_settings.emit(static_cast((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
+ });
+ m_menu_guild_leave.signal_activate().connect([this] {
+ m_signal_action_guild_leave.emit(static_cast((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
+ });
+ m_menu_guild.append(m_menu_guild_copy_id);
+ m_menu_guild.append(m_menu_guild_settings);
+ m_menu_guild.append(m_menu_guild_leave);
+ m_menu_guild.show_all();
+
+ m_menu_category_copy_id.signal_activate().connect([this] {
+ Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
+ });
+ m_menu_category.append(m_menu_category_copy_id);
+ m_menu_category.show_all();
+
+ m_menu_channel_copy_id.signal_activate().connect([this] {
+ Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
+ });
+ m_menu_channel.append(m_menu_channel_copy_id);
+ m_menu_channel.show_all();
+
+ m_menu_dm_copy_id.signal_activate().connect([this] {
+ Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
+ });
+ m_menu_dm_close.signal_activate().connect([this] {
+ const auto id = static_cast((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]);
+ auto &discord = Abaddon::Get().GetDiscordClient();
+ const auto channel = discord.GetChannel(id);
+ if (!channel.has_value()) return;
+
+ if (channel->Type == ChannelType::DM)
+ discord.CloseDM(id);
+ else if (Abaddon::Get().ShowConfirm("Are you sure you want to leave this group DM?"))
+ Abaddon::Get().GetDiscordClient().CloseDM(id);
+ });
+ m_menu_dm.append(m_menu_dm_copy_id);
+ m_menu_dm.append(m_menu_dm_close);
+ m_menu_dm.show_all();
+
Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &ChannelList::OnMessageCreate));
}
@@ -423,6 +477,38 @@ void ChannelList::OnMessageCreate(const Message &msg) {
(*iter)[m_columns.m_sort] = -msg.ID;
}
+bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) {
+ if (ev->button == GDK_BUTTON_SECONDARY && ev->type == GDK_BUTTON_PRESS) {
+ if (m_view.get_path_at_pos(ev->x, ev->y, m_path_for_menu)) {
+ auto row = (*m_model->get_iter(m_path_for_menu));
+ switch (static_cast(row[m_columns.m_type])) {
+ case RenderType::Guild:
+ m_menu_guild.popup_at_pointer(reinterpret_cast(ev));
+ break;
+ case RenderType::Category:
+ m_menu_category.popup_at_pointer(reinterpret_cast(ev));
+ break;
+ case RenderType::TextChannel:
+ m_menu_channel.popup_at_pointer(reinterpret_cast(ev));
+ break;
+ case RenderType::DM: {
+ const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(static_cast(row[m_columns.m_id]));
+ if (channel.has_value()) {
+ m_menu_dm_close.set_label(channel->Type == ChannelType::DM ? "Close" : "Leave");
+ m_menu_dm_close.show();
+ } else
+ m_menu_dm_close.hide();
+ m_menu_dm.popup_at_pointer(reinterpret_cast(ev));
+ } break;
+ default:
+ break;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
return m_signal_action_channel_item_select;
}
@@ -662,7 +748,7 @@ void CellRendererChannels::render_vfunc_category(const Cairo::RefPtrmove_to(x1, y1);
cr->line_to(x2, y2);
cr->line_to(x3, y3);
- cr->set_source_rgb(34.0 / 255.0, 112.0 / 255.0, 1.0);
+ cr->set_source_rgb(1.0, 0.3255, 0.4392);
cr->stroke();
Gtk::Requisition text_minimum, text_natural;
diff --git a/components/channels.hpp b/components/channels.hpp
index b5f2ad9..d3df2d5 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -158,6 +158,7 @@ protected:
void OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
void OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
bool SelectionFunc(const Glib::RefPtr &model, const Gtk::TreeModel::Path &path, bool is_currently_selected);
+ bool OnButtonPressEvent(GdkEventButton *ev);
Gtk::TreeModel::Path m_last_selected;
Gtk::TreeModel::Path m_dm_header;
@@ -166,6 +167,22 @@ protected:
void UpdateCreateDMChannel(const ChannelData &channel);
void OnMessageCreate(const Message &msg);
+ Gtk::TreeModel::Path m_path_for_menu;
+
+ 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::Menu m_menu_category;
+ Gtk::MenuItem m_menu_category_copy_id;
+
+ Gtk::Menu m_menu_channel;
+ Gtk::MenuItem m_menu_channel_copy_id;
+
+ Gtk::Menu m_menu_dm;
+ Gtk::MenuItem m_menu_dm_copy_id;
+ Gtk::MenuItem m_menu_dm_close;
bool m_updating_listing = false;
--
cgit v1.2.3
From 0250229e8184ea18b8799feb1cc3622d847adec3 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Mon, 19 Jul 2021 01:42:55 -0400
Subject: less than ideal solution for category expander color
---
components/channels.cpp | 3 ++-
settings.cpp | 4 ++++
settings.hpp | 11 ++++++++++-
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index e67d014..0208975 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -748,7 +748,8 @@ void CellRendererChannels::render_vfunc_category(const Cairo::RefPtrmove_to(x1, y1);
cr->line_to(x2, y2);
cr->line_to(x3, y3);
- cr->set_source_rgb(1.0, 0.3255, 0.4392);
+ static const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().GetChannelsExpanderColor());
+ cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue());
cr->stroke();
Gtk::Requisition text_minimum, text_natural;
diff --git a/settings.cpp b/settings.cpp
index 6083d96..2974dca 100644
--- a/settings.cpp
+++ b/settings.cpp
@@ -66,6 +66,10 @@ std::string SettingsManager::GetLinkColor() const {
return GetSettingString("misc", "linkcolor", "rgba(40, 200, 180, 255)");
}
+std::string SettingsManager::GetChannelsExpanderColor() const {
+ return GetSettingString("misc", "expandercolor", "rgba(255, 83, 112, 255)");
+}
+
int SettingsManager::GetCacheHTTPConcurrency() const {
return GetSettingInt("http", "concurrent", 20);
}
diff --git a/settings.hpp b/settings.hpp
index d77b418..7559dc0 100644
--- a/settings.hpp
+++ b/settings.hpp
@@ -15,7 +15,6 @@ public:
bool GetShowMemberListDiscriminators() const;
bool GetShowStockEmojis() const;
bool GetShowCustomEmojis() const;
- std::string GetLinkColor() const;
int GetCacheHTTPConcurrency() const;
bool GetPrefetch() const;
std::string GetMainCSS() const;
@@ -24,6 +23,16 @@ public:
std::string GetGatewayURL() const;
std::string GetAPIBaseURL() const;
+ // i would like to use Gtk::StyleProperty for this, but it will not work on windows
+ // #1 it's missing from the project files for the version used by vcpkg
+ // #2 it's still broken and doesn't function even when added to the solution
+ // #3 it's a massive pain in the ass to try and bump the version to a functioning version
+ // because they switch build systems to nmake/meson (took months to get merged in vcpkg)
+ // #4 c++ build systems sucks
+ // perhaps i'll bump to gtk4 when it's more stable
+ std::string GetLinkColor() const;
+ std::string GetChannelsExpanderColor() const;
+
bool IsValid() const;
template
--
cgit v1.2.3
From d20a822fdbd52c316e3b33a8dbf8c72e564a7986 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Mon, 19 Jul 2021 03:07:27 -0400
Subject: tweak text channel rendering
---
components/channels.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 0208975..37e7625 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -220,7 +220,7 @@ void ChannelList::UpdateCreateChannel(Snowflake id) {
}
channel_row[m_columns.m_type] = RenderType::TextChannel;
channel_row[m_columns.m_id] = channel->ID;
- channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
+ channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel->Name);
if (orphan)
channel_row[m_columns.m_sort] = *channel->Position + OrphanChannelSortOffset;
else
@@ -291,7 +291,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
auto channel_row = *m_model->append(guild_row.children());
channel_row[m_columns.m_type] = RenderType::TextChannel;
channel_row[m_columns.m_id] = channel.ID;
- channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
+ channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name);
channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset;
}
@@ -310,7 +310,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
auto channel_row = *m_model->append(cat_row.children());
channel_row[m_columns.m_type] = RenderType::TextChannel;
channel_row[m_columns.m_id] = channel.ID;
- channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name);
+ channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name);
channel_row[m_columns.m_sort] = *channel.Position;
}
}
@@ -787,7 +787,7 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr
Date: Tue, 20 Jul 2021 17:55:03 -0400
Subject: color nsfw channels
---
components/channels.cpp | 22 ++++++++++++++++++----
components/channels.hpp | 13 +++++++------
discord/channel.cpp | 4 ++++
discord/channel.hpp | 1 +
settings.cpp | 8 ++++++--
settings.hpp | 4 +++-
6 files changed, 39 insertions(+), 13 deletions(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 37e7625..547232c 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -66,6 +66,7 @@ ChannelList::ChannelList()
column->add_attribute(renderer->property_icon(), m_columns.m_icon);
column->add_attribute(renderer->property_name(), m_columns.m_name);
column->add_attribute(renderer->property_expanded(), m_columns.m_expanded);
+ column->add_attribute(renderer->property_nsfw(), m_columns.m_nsfw);
m_view.append_column(*column);
m_menu_guild_copy_id.signal_activate().connect([this] {
@@ -191,15 +192,13 @@ void ChannelList::UpdateChannel(Snowflake id) {
channel_row[m_columns.m_type] = RenderType::TextChannel;
channel_row[m_columns.m_id] = channel->ID;
channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
+ channel_row[m_columns.m_nsfw] = channel->NSFW();
if (is_orphan)
channel_row[m_columns.m_sort] = *channel->Position + OrphanChannelSortOffset;
else
channel_row[m_columns.m_sort] = *channel->Position;
}
-void ChannelList::UpdateCreateDMChannel(Snowflake id) {
-}
-
void ChannelList::UpdateCreateChannel(Snowflake id) {
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
if (!channel.has_value()) return;
@@ -221,6 +220,7 @@ void ChannelList::UpdateCreateChannel(Snowflake id) {
channel_row[m_columns.m_type] = RenderType::TextChannel;
channel_row[m_columns.m_id] = channel->ID;
channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel->Name);
+ channel_row[m_columns.m_nsfw] = channel->NSFW();
if (orphan)
channel_row[m_columns.m_sort] = *channel->Position + OrphanChannelSortOffset;
else
@@ -293,6 +293,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
channel_row[m_columns.m_id] = channel.ID;
channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name);
channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset;
+ channel_row[m_columns.m_nsfw] = channel.NSFW();
}
for (const auto &[category_id, channels] : categories) {
@@ -312,6 +313,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
channel_row[m_columns.m_id] = channel.ID;
channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name);
channel_row[m_columns.m_sort] = *channel.Position;
+ channel_row[m_columns.m_nsfw] = channel.NSFW();
}
}
@@ -527,6 +529,7 @@ ChannelList::ModelColumns::ModelColumns() {
add(m_name);
add(m_icon);
add(m_sort);
+ add(m_nsfw);
add(m_expanded);
}
@@ -536,7 +539,8 @@ CellRendererChannels::CellRendererChannels()
, m_property_type(*this, "render-type")
, m_property_name(*this, "name")
, m_property_pixbuf(*this, "pixbuf")
- , m_property_expanded(*this, "expanded") {
+ , m_property_expanded(*this, "expanded")
+ , m_property_nsfw(*this, "nsfw") {
property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
property_xpad() = 2;
property_ypad() = 2;
@@ -564,6 +568,10 @@ Glib::PropertyProxy CellRendererChannels::property_expanded() {
return m_property_expanded.get_proxy();
}
+Glib::PropertyProxy CellRendererChannels::property_nsfw() {
+ return m_property_nsfw.get_proxy();
+}
+
void CellRendererChannels::get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
switch (m_property_type.get_value()) {
case RenderType::Guild:
@@ -794,7 +802,13 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr property_name();
Glib::PropertyProxy> property_icon();
Glib::PropertyProxy property_expanded();
+ Glib::PropertyProxy property_nsfw();
protected:
void get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const override;
@@ -100,11 +101,11 @@ protected:
private:
Gtk::CellRendererText m_renderer_text;
- Glib::Property m_property_type;
-
- Glib::Property m_property_name; // guild
- Glib::Property> m_property_pixbuf; // guild
+ Glib::Property m_property_type; // all
+ Glib::Property m_property_name; // all
+ Glib::Property> m_property_pixbuf; // guild, dm
Glib::Property m_property_expanded; // category
+ Glib::Property m_property_nsfw; // channel
};
class ChannelList : public Gtk::ScrolledWindow {
@@ -115,7 +116,6 @@ public:
void UpdateRemoveGuild(Snowflake id);
void UpdateRemoveChannel(Snowflake id);
void UpdateChannel(Snowflake id);
- void UpdateCreateDMChannel(Snowflake id);
void UpdateCreateChannel(Snowflake id);
void UpdateGuild(Snowflake id);
@@ -133,6 +133,7 @@ protected:
Gtk::TreeModelColumn m_name;
Gtk::TreeModelColumn> m_icon;
Gtk::TreeModelColumn m_sort;
+ Gtk::TreeModelColumn m_nsfw;
// Gtk::CellRenderer's property_is_expanded only works how i want it to if it has children
// because otherwise it doesnt count as an "expander" (property_is_expander)
// so this solution will have to do which i hate but the alternative is adding invisible children
diff --git a/discord/channel.cpp b/discord/channel.cpp
index 68e73e9..9d90eb5 100644
--- a/discord/channel.cpp
+++ b/discord/channel.cpp
@@ -43,6 +43,10 @@ void ChannelData::update_from_json(const nlohmann::json &j) {
JS_RD("last_pin_timestamp", LastPinTimestamp);
}
+bool ChannelData::NSFW() const {
+ return IsNSFW.has_value() && *IsNSFW;
+}
+
std::optional ChannelData::GetOverwrite(Snowflake id) const {
return Abaddon::Get().GetDiscordClient().GetPermissionOverwrite(ID, id);
}
diff --git a/discord/channel.hpp b/discord/channel.hpp
index b9bfd6d..68597e6 100644
--- a/discord/channel.hpp
+++ b/discord/channel.hpp
@@ -61,6 +61,7 @@ struct ChannelData {
friend void from_json(const nlohmann::json &j, ChannelData &m);
void update_from_json(const nlohmann::json &j);
+ bool NSFW() const;
std::optional GetOverwrite(Snowflake id) const;
std::vector GetDMRecipients() const;
};
diff --git a/settings.cpp b/settings.cpp
index 2974dca..dadd485 100644
--- a/settings.cpp
+++ b/settings.cpp
@@ -63,11 +63,15 @@ bool SettingsManager::GetShowCustomEmojis() const {
}
std::string SettingsManager::GetLinkColor() const {
- return GetSettingString("misc", "linkcolor", "rgba(40, 200, 180, 255)");
+ return GetSettingString("style", "linkcolor", "rgba(40, 200, 180, 255)");
}
std::string SettingsManager::GetChannelsExpanderColor() const {
- return GetSettingString("misc", "expandercolor", "rgba(255, 83, 112, 255)");
+ return GetSettingString("style", "expandercolor", "rgba(255, 83, 112, 255)");
+}
+
+std::string SettingsManager::GetNSFWChannelColor() const {
+ return GetSettingString("style", "nsfwchannelcolor", "#ed6666");
}
int SettingsManager::GetCacheHTTPConcurrency() const {
diff --git a/settings.hpp b/settings.hpp
index 7559dc0..d328805 100644
--- a/settings.hpp
+++ b/settings.hpp
@@ -29,9 +29,11 @@ public:
// #3 it's a massive pain in the ass to try and bump the version to a functioning version
// because they switch build systems to nmake/meson (took months to get merged in vcpkg)
// #4 c++ build systems sucks
- // perhaps i'll bump to gtk4 when it's more stable
+ // three options are: use gtk4 with updated vcpkg, try and port it myself, or use msys2 instead of vcpkg
+ // im leaning towards msys
std::string GetLinkColor() const;
std::string GetChannelsExpanderColor() const;
+ std::string GetNSFWChannelColor() const;
bool IsValid() const;
--
cgit v1.2.3
From 4988db95bc7c94865ba4fd55218b982efb216e81 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Wed, 21 Jul 2021 02:30:46 -0400
Subject: select new channel when active channel changes also automatically fix
expander indicator when indirectly expanded
---
components/channels.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index 547232c..f6c5d32 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -247,6 +247,11 @@ void ChannelList::UpdateGuild(Snowflake id) {
}
void ChannelList::SetActiveChannel(Snowflake id) {
+ const auto channel_iter = GetIteratorForChannelFromID(id);
+ if (channel_iter) {
+ m_view.expand_to_path(m_model->get_path(channel_iter));
+ m_view.get_selection()->select(channel_iter);
+ }
}
Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
@@ -387,6 +392,8 @@ void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk:
if (auto selection = m_view.get_selection(); selection && !selection->get_selected()) {
selection->select(m_last_selected);
}
+
+ (*iter)[m_columns.m_expanded] = true;
}
bool ChannelList::SelectionFunc(const Glib::RefPtr &model, const Gtk::TreeModel::Path &path, bool is_currently_selected) {
--
cgit v1.2.3
From fdee6c22cfd98133a26aa0695c57d901633ff937 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Wed, 21 Jul 2021 03:23:45 -0400
Subject: channel list: hide expanders and search
---
components/channels.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/components/channels.cpp b/components/channels.cpp
index f6c5d32..de98463 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -45,6 +45,8 @@ ChannelList::ChannelList()
m_view.set_hexpand(true);
m_view.set_vexpand(true);
+ 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_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING);
--
cgit v1.2.3
From 5bf48fa6c0650d67dca2a45d05ec1570ebe236ea Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Fri, 23 Jul 2021 00:35:33 -0400
Subject: add animated guild icons to channel list
---
README.md | 5 ++-
components/channels.cpp | 89 ++++++++++++++++++++++++++++++++++++++++---------
components/channels.hpp | 18 +++++++---
settings.cpp | 4 +++
settings.hpp | 1 +
5 files changed, 96 insertions(+), 21 deletions(-)
diff --git a/README.md b/README.md
index 1d7154a..3d1be1c 100644
--- a/README.md
+++ b/README.md
@@ -190,9 +190,12 @@ For example, memory_db would be set by adding `memory_db = true` under the line
* custom_emojis (true or false, default true) - download and use custom Discord emojis
* css (string) - path to the main CSS file
* animations (true or false, default true) - use animated images where available (e.g. server icons, emojis, avatars). false means static images will be used
+* animated_guild_hover_only (true or false, default true) - only animate guild icons when the guild is being hovered over
* owner_crown (true or false, default true) - show a crown next to the owner
* gateway (string) - override url for Discord gateway. must be json format and use zlib stream compression
* api_base (string) - override base url for Discord API
-#### misc
+#### style
* linkcolor (string) - color to use for links in messages
+* expandercolor (string) - color to use for the expander in the channel list
+* nsfwchannelcolor (string) - color to use for NSFW channels in the channel list
diff --git a/components/channels.cpp b/components/channels.cpp
index de98463..5db3b7d 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -8,7 +8,7 @@
#include "statusindicator.hpp"
ChannelList::ChannelList()
- : Glib::ObjectBase("channellist")
+ : Glib::ObjectBase(typeid(ChannelList))
, Gtk::ScrolledWindow()
, m_model(Gtk::TreeStore::create(m_columns))
, m_menu_guild_copy_id("_Copy ID", true)
@@ -66,6 +66,7 @@ ChannelList::ChannelList()
column->pack_start(*renderer);
column->add_attribute(renderer->property_type(), m_columns.m_type);
column->add_attribute(renderer->property_icon(), m_columns.m_icon);
+ column->add_attribute(renderer->property_icon_animation(), m_columns.m_icon_anim);
column->add_attribute(renderer->property_name(), m_columns.m_name);
column->add_attribute(renderer->property_expanded(), m_columns.m_expanded);
column->add_attribute(renderer->property_nsfw(), m_columns.m_nsfw);
@@ -235,14 +236,21 @@ void ChannelList::UpdateGuild(Snowflake id) {
const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id);
if (!iter || !guild.has_value()) return;
+ static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
+
(*iter)[m_columns.m_name] = "" + Glib::Markup::escape_text(guild->Name) + "";
(*iter)[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize);
- if (guild->HasIcon()) {
+ if (show_animations && guild->HasAnimatedIcon()) {
+ const auto cb = [this, id](const Glib::RefPtr &pb) {
+ auto iter = GetIteratorForGuildFromID(id);
+ if (iter) (*iter)[m_columns.m_icon_anim] = pb;
+ };
+ img.LoadAnimationFromURL(guild->GetIconURL("gif", "32"), GuildIconSize, GuildIconSize, sigc::track_obj(cb, *this));
+ } else if (guild->HasIcon()) {
const auto cb = [this, id](const Glib::RefPtr &pb) {
// iter might be invalid
auto iter = GetIteratorForGuildFromID(id);
- if (iter)
- (*iter)[m_columns.m_icon] = pb->scale_simple(GuildIconSize, GuildIconSize, Gdk::INTERP_BILINEAR);
+ if (iter) (*iter)[m_columns.m_icon] = pb->scale_simple(GuildIconSize, GuildIconSize, Gdk::INTERP_BILINEAR);
};
img.LoadFromURL(guild->GetIconURL("png", "32"), sigc::track_obj(cb, *this));
}
@@ -266,11 +274,18 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
guild_row[m_columns.m_name] = "" + Glib::Markup::escape_text(guild.Name) + "";
guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize);
- if (guild.HasIcon()) {
+ static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
+
+ if (show_animations && guild.HasAnimatedIcon()) {
+ const auto cb = [this, id = guild.ID](const Glib::RefPtr &pb) {
+ auto iter = GetIteratorForGuildFromID(id);
+ if (iter) (*iter)[m_columns.m_icon_anim] = pb;
+ };
+ img.LoadAnimationFromURL(guild.GetIconURL("gif", "32"), GuildIconSize, GuildIconSize, sigc::track_obj(cb, *this));
+ } else if (guild.HasIcon()) {
const auto cb = [this, id = guild.ID](const Glib::RefPtr &pb) {
auto iter = GetIteratorForGuildFromID(id);
- if (iter)
- (*iter)[m_columns.m_icon] = pb->scale_simple(GuildIconSize, GuildIconSize, Gdk::INTERP_BILINEAR);
+ if (iter) (*iter)[m_columns.m_icon] = pb->scale_simple(GuildIconSize, GuildIconSize, Gdk::INTERP_BILINEAR);
};
img.LoadFromURL(guild.GetIconURL("png", "32"), sigc::track_obj(cb, *this));
}
@@ -537,6 +552,7 @@ ChannelList::ModelColumns::ModelColumns() {
add(m_id);
add(m_name);
add(m_icon);
+ add(m_icon_anim);
add(m_sort);
add(m_nsfw);
add(m_expanded);
@@ -548,6 +564,7 @@ CellRendererChannels::CellRendererChannels()
, m_property_type(*this, "render-type")
, m_property_name(*this, "name")
, m_property_pixbuf(*this, "pixbuf")
+ , m_property_pixbuf_animation(*this, "pixbuf-animation")
, m_property_expanded(*this, "expanded")
, m_property_nsfw(*this, "nsfw") {
property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
@@ -573,6 +590,10 @@ Glib::PropertyProxy> CellRendererChannels::property_ic
return m_property_pixbuf.get_proxy();
}
+Glib::PropertyProxy> CellRendererChannels::property_icon_animation() {
+ return m_property_pixbuf_animation.get_proxy();
+}
+
Glib::PropertyProxy CellRendererChannels::property_expanded() {
return m_property_expanded.get_proxy();
}
@@ -660,7 +681,10 @@ void CellRendererChannels::render_vfunc(const Cairo::RefPtr &cr,
void CellRendererChannels::get_preferred_width_vfunc_guild(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
int pixbuf_width = 0;
- if (auto pixbuf = m_property_pixbuf.get_value())
+
+ if (auto pixbuf = m_property_pixbuf_animation.get_value())
+ pixbuf_width = pixbuf->get_width();
+ else if (auto pixbuf = m_property_pixbuf.get_value())
pixbuf_width = pixbuf->get_width();
int text_min, text_nat;
@@ -678,7 +702,9 @@ void CellRendererChannels::get_preferred_width_for_height_vfunc_guild(Gtk::Widge
void CellRendererChannels::get_preferred_height_vfunc_guild(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
int pixbuf_height = 0;
- if (auto pixbuf = m_property_pixbuf.get_value())
+ if (auto pixbuf = m_property_pixbuf_animation.get_value())
+ pixbuf_height = pixbuf->get_height();
+ else if (auto pixbuf = m_property_pixbuf.get_value())
pixbuf_height = pixbuf->get_height();
int text_min, text_nat;
@@ -701,10 +727,17 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtrget_width();
+ pixbuf_h = pixbuf->get_height();
+ } else if (auto pixbuf = m_property_pixbuf.get_value()) {
+ pixbuf_w = pixbuf->get_width();
+ pixbuf_h = pixbuf->get_height();
+ }
- const double icon_w = pixbuf->get_width();
- const double icon_h = pixbuf->get_height();
+ const double icon_w = pixbuf_w;
+ const double icon_h = pixbuf_h;
const double icon_x = background_area.get_x();
const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - icon_h / 2.0;
@@ -717,9 +750,35 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtrrectangle(icon_x, icon_y, icon_w, icon_h);
- cr->fill();
+ const static bool hover_only = Abaddon::Get().GetSettings().GetAnimatedGuildHoverOnly();
+ const bool is_hovered = flags & Gtk::CELL_RENDERER_PRELIT;
+ auto anim = m_property_pixbuf_animation.get_value();
+
+ // kinda gross
+ if (anim) {
+ auto map_iter = m_pixbuf_anim_iters.find(anim);
+ if (map_iter == m_pixbuf_anim_iters.end())
+ m_pixbuf_anim_iters[anim] = anim->get_iter(nullptr);
+ auto pb_iter = m_pixbuf_anim_iters.at(anim);
+
+ const auto cb = [this, &widget, anim, icon_x, icon_y, icon_w, icon_h] {
+ if (m_pixbuf_anim_iters.at(anim)->advance())
+ widget.queue_draw_area(icon_x, icon_y, icon_w, icon_h);
+ };
+
+ if ((hover_only && is_hovered) || !hover_only)
+ Glib::signal_timeout().connect_once(sigc::track_obj(cb, widget), pb_iter->get_delay_time());
+ if (hover_only && !is_hovered)
+ m_pixbuf_anim_iters[anim] = anim->get_iter(nullptr);
+
+ Gdk::Cairo::set_source_pixbuf(cr, pb_iter->get_pixbuf(), icon_x, icon_y);
+ cr->rectangle(icon_x, icon_y, icon_w, icon_h);
+ cr->fill();
+ } else if (auto pixbuf = m_property_pixbuf.get_value()) {
+ Gdk::Cairo::set_source_pixbuf(cr, pixbuf, icon_x, icon_y);
+ cr->rectangle(icon_x, icon_y, icon_w, icon_h);
+ cr->fill();
+ }
}
// category
diff --git a/components/channels.hpp b/components/channels.hpp
index 2246b3b..3e9d9e0 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -29,6 +29,7 @@ public:
Glib::PropertyProxy property_type();
Glib::PropertyProxy property_name();
Glib::PropertyProxy> property_icon();
+ Glib::PropertyProxy> property_icon_animation();
Glib::PropertyProxy property_expanded();
Glib::PropertyProxy property_nsfw();
@@ -101,11 +102,17 @@ protected:
private:
Gtk::CellRendererText m_renderer_text;
- Glib::Property m_property_type; // all
- Glib::Property m_property_name; // all
- Glib::Property> m_property_pixbuf; // guild, dm
- Glib::Property m_property_expanded; // category
- Glib::Property m_property_nsfw; // channel
+ Glib::Property m_property_type; // all
+ Glib::Property m_property_name; // all
+ Glib::Property> m_property_pixbuf; // guild, dm
+ Glib::Property> m_property_pixbuf_animation; // guild
+ Glib::Property m_property_expanded; // category
+ Glib::Property m_property_nsfw; // channel
+
+ // same pitfalls as in https://github.com/uowuo/abaddon/blob/60404783bd4ce9be26233fe66fc3a74475d9eaa3/components/cellrendererpixbufanimation.hpp#L32-L39
+ // this will manifest though since guild icons can change
+ // an animation or two wont be the end of the world though
+ std::map, Glib::RefPtr> m_pixbuf_anim_iters;
};
class ChannelList : public Gtk::ScrolledWindow {
@@ -132,6 +139,7 @@ protected:
Gtk::TreeModelColumn m_id;
Gtk::TreeModelColumn m_name;
Gtk::TreeModelColumn> m_icon;
+ Gtk::TreeModelColumn> m_icon_anim;
Gtk::TreeModelColumn m_sort;
Gtk::TreeModelColumn m_nsfw;
// Gtk::CellRenderer's property_is_expanded only works how i want it to if it has children
diff --git a/settings.cpp b/settings.cpp
index dadd485..298b301 100644
--- a/settings.cpp
+++ b/settings.cpp
@@ -101,3 +101,7 @@ std::string SettingsManager::GetGatewayURL() const {
std::string SettingsManager::GetAPIBaseURL() const {
return GetSettingString("discord", "api_base", "https://discord.com/api/v9");
}
+
+bool SettingsManager::GetAnimatedGuildHoverOnly() const {
+ return GetSettingBool("gui", "animated_guild_hover_only", true);
+}
diff --git a/settings.hpp b/settings.hpp
index d328805..2a9fb9c 100644
--- a/settings.hpp
+++ b/settings.hpp
@@ -22,6 +22,7 @@ public:
bool GetShowOwnerCrown() const;
std::string GetGatewayURL() const;
std::string GetAPIBaseURL() const;
+ bool GetAnimatedGuildHoverOnly() const;
// i would like to use Gtk::StyleProperty for this, but it will not work on windows
// #1 it's missing from the project files for the version used by vcpkg
--
cgit v1.2.3
From 30391836d0bf25732ca5b1bbd46f6e2c84608b5e Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Mon, 26 Jul 2021 00:03:36 -0400
Subject: add missing channel sign
---
components/channels.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/components/channels.cpp b/components/channels.cpp
index 5db3b7d..c59418a 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -194,7 +194,7 @@ void ChannelList::UpdateChannel(Snowflake id) {
auto channel_row = *m_model->append(parent->children());
channel_row[m_columns.m_type] = RenderType::TextChannel;
channel_row[m_columns.m_id] = channel->ID;
- channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel->Name);
+ channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel->Name);
channel_row[m_columns.m_nsfw] = channel->NSFW();
if (is_orphan)
channel_row[m_columns.m_sort] = *channel->Position + OrphanChannelSortOffset;
--
cgit v1.2.3
From df243a40b5febcf7b9834e66f0ade770c3b483a0 Mon Sep 17 00:00:00 2001
From: ouwou <26526779+ouwou@users.noreply.github.com>
Date: Mon, 26 Jul 2021 00:56:14 -0400
Subject: let channel list manage itself instead of window
---
abaddon.cpp | 30 ------------------------------
abaddon.hpp | 6 ------
components/channels.cpp | 16 ++++++++++------
components/channels.hpp | 9 +++++----
windows/mainwindow.cpp | 24 ------------------------
windows/mainwindow.hpp | 6 ------
6 files changed, 15 insertions(+), 76 deletions(-)
diff --git a/abaddon.cpp b/abaddon.cpp
index 99a2520..047ca09 100644
--- a/abaddon.cpp
+++ b/abaddon.cpp
@@ -35,12 +35,6 @@ Abaddon::Abaddon()
m_discord.signal_message_delete().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnMessageDelete));
m_discord.signal_message_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnMessageUpdate));
m_discord.signal_guild_member_list_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnGuildMemberListUpdate));
- m_discord.signal_guild_create().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnGuildCreate));
- m_discord.signal_guild_delete().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnGuildDelete));
- m_discord.signal_channel_delete().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnChannelDelete));
- m_discord.signal_channel_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnChannelUpdate));
- m_discord.signal_channel_create().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnChannelCreate));
- m_discord.signal_guild_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnGuildUpdate));
m_discord.signal_reaction_add().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReactionAdd));
m_discord.signal_reaction_remove().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReactionRemove));
m_discord.signal_guild_join_request_create().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnGuildJoinRequestCreate));
@@ -196,30 +190,6 @@ void Abaddon::DiscordOnGuildMemberListUpdate(Snowflake guild_id) {
m_main_window->UpdateMembers();
}
-void Abaddon::DiscordOnGuildCreate(const GuildData &guild) {
- m_main_window->UpdateChannelsNewGuild(guild.ID);
-}
-
-void Abaddon::DiscordOnGuildDelete(Snowflake guild_id) {
- m_main_window->UpdateChannelsRemoveGuild(guild_id);
-}
-
-void Abaddon::DiscordOnChannelDelete(Snowflake channel_id) {
- m_main_window->UpdateChannelsRemoveChannel(channel_id);
-}
-
-void Abaddon::DiscordOnChannelUpdate(Snowflake channel_id) {
- m_main_window->UpdateChannelsUpdateChannel(channel_id);
-}
-
-void Abaddon::DiscordOnChannelCreate(Snowflake channel_id) {
- m_main_window->UpdateChannelsCreateChannel(channel_id);
-}
-
-void Abaddon::DiscordOnGuildUpdate(Snowflake guild_id) {
- m_main_window->UpdateChannelsUpdateGuild(guild_id);
-}
-
void Abaddon::DiscordOnReactionAdd(Snowflake message_id, const Glib::ustring ¶m) {
m_main_window->UpdateChatReactionAdd(message_id, param);
}
diff --git a/abaddon.hpp b/abaddon.hpp
index aebb9e1..721329d 100644
--- a/abaddon.hpp
+++ b/abaddon.hpp
@@ -65,12 +65,6 @@ public:
void DiscordOnMessageDelete(Snowflake id, Snowflake channel_id);
void DiscordOnMessageUpdate(Snowflake id, Snowflake channel_id);
void DiscordOnGuildMemberListUpdate(Snowflake guild_id);
- void DiscordOnGuildCreate(const GuildData &guild);
- void DiscordOnGuildDelete(Snowflake guild_id);
- void DiscordOnChannelDelete(Snowflake channel_id);
- void DiscordOnChannelUpdate(Snowflake channel_id);
- void DiscordOnChannelCreate(Snowflake channel_id);
- void DiscordOnGuildUpdate(Snowflake guild_id);
void DiscordOnReactionAdd(Snowflake message_id, const Glib::ustring ¶m);
void DiscordOnReactionRemove(Snowflake message_id, const Glib::ustring ¶m);
void DiscordOnGuildJoinRequestCreate(const GuildJoinRequestCreateData &data);
diff --git a/components/channels.cpp b/components/channels.cpp
index c59418a..3e7862b 100644
--- a/components/channels.cpp
+++ b/components/channels.cpp
@@ -116,7 +116,14 @@ ChannelList::ChannelList()
m_menu_dm.append(m_menu_dm_close);
m_menu_dm.show_all();
- Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &ChannelList::OnMessageCreate));
+ 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_guild_update().connect(sigc::mem_fun(*this, &ChannelList::UpdateGuild));
}
void ChannelList::UpdateListing() {
@@ -142,13 +149,10 @@ void ChannelList::UpdateListing() {
AddPrivateChannels();
}
-void ChannelList::UpdateNewGuild(Snowflake id) {
- const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id);
+void ChannelList::UpdateNewGuild(const GuildData &guild) {
auto &img = Abaddon::Get().GetImageManager();
- if (!guild.has_value()) return;
-
- auto iter = AddGuild(*guild);
+ auto iter = AddGuild(guild);
// update sort order
int sortnum = 0;
diff --git a/components/channels.hpp b/components/channels.hpp
index 3e9d9e0..69f1f77 100644
--- a/components/channels.hpp
+++ b/components/channels.hpp
@@ -118,17 +118,18 @@ private:
class ChannelList : public Gtk::ScrolledWindow {
public:
ChannelList();
+
void UpdateListing();
- void UpdateNewGuild(Snowflake id);
+ void SetActiveChannel(Snowflake id);
+
+protected:
+ void UpdateNewGuild(const GuildData &guild);
void UpdateRemoveGuild(Snowflake id);
void UpdateRemoveChannel(Snowflake id);
void UpdateChannel(Snowflake id);
void UpdateCreateChannel(Snowflake id);
void UpdateGuild(Snowflake id);
- void SetActiveChannel(Snowflake id);
-
-protected:
Gtk::TreeView m_view;
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
diff --git a/windows/mainwindow.cpp b/windows/mainwindow.cpp
index dc9c51d..0d77e96 100644
--- a/windows/mainwindow.cpp
+++ b/windows/mainwindow.cpp
@@ -165,30 +165,6 @@ void MainWindow::UpdateChannelListing() {
m_channel_list.UpdateListing();
}
-void MainWindow::UpdateChannelsNewGuild(Snowflake id) {
- m_channel_list.UpdateNewGuild(id);
-}
-
-void MainWindow::UpdateChannelsRemoveGuild(Snowflake id) {
- m_channel_list.UpdateRemoveGuild(id);
-}
-
-void MainWindow::UpdateChannelsRemoveChannel(Snowflake id) {
- m_channel_list.UpdateRemoveChannel(id);
-}
-
-void MainWindow::UpdateChannelsUpdateChannel(Snowflake id) {
- m_channel_list.UpdateChannel(id);
-}
-
-void MainWindow::UpdateChannelsCreateChannel(Snowflake id) {
- m_channel_list.UpdateCreateChannel(id);
-}
-
-void MainWindow::UpdateChannelsUpdateGuild(Snowflake id) {
- m_channel_list.UpdateGuild(id);
-}
-
void MainWindow::UpdateChatWindowContents() {
auto &discord = Abaddon::Get().GetDiscordClient();
auto msgs = discord.GetMessagesForChannel(m_chat.GetActiveChannel(), 50);
diff --git a/windows/mainwindow.hpp b/windows/mainwindow.hpp
index fa3d331..d037796 100644
--- a/windows/mainwindow.hpp
+++ b/windows/mainwindow.hpp
@@ -12,12 +12,6 @@ public:
void UpdateComponents();
void UpdateMembers();
void UpdateChannelListing();
- void UpdateChannelsNewGuild(Snowflake id);
- void UpdateChannelsRemoveGuild(Snowflake id);
- void UpdateChannelsRemoveChannel(Snowflake id);
- void UpdateChannelsUpdateChannel(Snowflake id);
- void UpdateChannelsCreateChannel(Snowflake id);
- void UpdateChannelsUpdateGuild(Snowflake id);
void UpdateChatWindowContents();
void UpdateChatActiveChannel(Snowflake id);
Snowflake GetChatActiveChannel() const;
--
cgit v1.2.3