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