diff --git a/components/channels.cpp b/components/channels.cpp index 172286b..38b1e1f 100644 --- a/components/channels.cpp +++ b/components/channels.cpp @@ -119,13 +119,25 @@ ChannelListRowGuild::ChannelListRowGuild(const Guild *data) { m_menu.show_all(); + const auto show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); + auto &img = Abaddon::Get().GetImageManager(); if (data->HasIcon()) { - auto buf = Abaddon::Get().GetImageManager().GetFromURLIfCached(data->GetIconURL("png", "32")); - if (buf) - m_icon = Gtk::manage(new Gtk::Image(buf->scale_simple(24, 24, Gdk::INTERP_BILINEAR))); - else { - m_icon = Gtk::manage(new Gtk::Image(Abaddon::Get().GetImageManager().GetPlaceholder(24))); - Abaddon::Get().GetImageManager().LoadFromURL(data->GetIconURL("png", "32"), sigc::mem_fun(*this, &ChannelListRowGuild::OnImageLoad)); + if (data->HasAnimatedIcon() && show_animations) { + auto buf = img.GetAnimationFromURLIfCached(data->GetIconURL("gif", "32"), 24, 24); + if (buf) + m_icon = Gtk::manage(new Gtk::Image(buf)); + else { + m_icon = Gtk::manage(new Gtk::Image(img.GetPlaceholder(24))); + img.LoadAnimationFromURL(data->GetIconURL("gif", "32"), 24, 24, sigc::mem_fun(*this, &ChannelListRowGuild::OnAnimatedImageLoad)); + } + } else { + auto buf = img.GetFromURLIfCached(data->GetIconURL("png", "32")); + if (buf) + m_icon = Gtk::manage(new Gtk::Image(buf->scale_simple(24, 24, Gdk::INTERP_BILINEAR))); + else { + m_icon = Gtk::manage(new Gtk::Image(img.GetPlaceholder(24))); + img.LoadFromURL(data->GetIconURL("png", "32"), sigc::mem_fun(*this, &ChannelListRowGuild::OnImageLoad)); + } } } else { m_icon = Gtk::manage(new Gtk::Image(Abaddon::Get().GetImageManager().GetPlaceholder(24))); @@ -148,10 +160,14 @@ ChannelListRowGuild::ChannelListRowGuild(const Guild *data) { show_all_children(); } -void ChannelListRowGuild::OnImageLoad(Glib::RefPtr buf) { +void ChannelListRowGuild::OnImageLoad(const Glib::RefPtr &buf) { m_icon->property_pixbuf() = buf->scale_simple(24, 24, Gdk::INTERP_BILINEAR); } +void ChannelListRowGuild::OnAnimatedImageLoad(const Glib::RefPtr &buf) { + m_icon->property_pixbuf_animation() = buf; +} + ChannelListRowGuild::type_signal_copy_id ChannelListRowGuild::signal_copy_id() { return m_signal_copy_id; } diff --git a/components/channels.hpp b/components/channels.hpp index d481b13..f259266 100644 --- a/components/channels.hpp +++ b/components/channels.hpp @@ -53,7 +53,8 @@ public: int GuildIndex; protected: - void OnImageLoad(Glib::RefPtr buf); + void OnImageLoad(const Glib::RefPtr &buf); + void OnAnimatedImageLoad(const Glib::RefPtr &buf); Gtk::EventBox *m_ev; Gtk::Box *m_box; diff --git a/discord/guild.cpp b/discord/guild.cpp index 13e20bf..c3c35b9 100644 --- a/discord/guild.cpp +++ b/discord/guild.cpp @@ -122,6 +122,10 @@ bool Guild::HasIcon() const { return Icon != ""; } +bool Guild::HasAnimatedIcon() const { + return HasIcon() && Icon[0] == 'a' && Icon[1] == '_'; +} + std::string Guild::GetIconURL(std::string ext, std::string size) const { return "https://cdn.discordapp.com/icons/" + std::to_string(ID) + "/" + Icon + "." + ext + "?size=" + size; } diff --git a/discord/guild.hpp b/discord/guild.hpp index d0863d5..ba46d75 100644 --- a/discord/guild.hpp +++ b/discord/guild.hpp @@ -68,6 +68,7 @@ struct Guild { void update_from_json(const nlohmann::json &j); bool HasIcon() const; + bool HasAnimatedIcon() const; std::string GetIconURL(std::string ext = "png", std::string size = "32") const; std::vector GetSortedChannels(Snowflake ignore = Snowflake::Invalid) const; }; diff --git a/imgmanager.cpp b/imgmanager.cpp index 218c9ca..c1c67fd 100644 --- a/imgmanager.cpp +++ b/imgmanager.cpp @@ -20,9 +20,19 @@ Glib::RefPtr ImageManager::ReadFileToPixbuf(std::string path) { }); loader->write(static_cast(data.data()), data.size()); loader->close(); - auto buf = loader->get_pixbuf(); + return loader->get_pixbuf(); +} - return buf; +Glib::RefPtr ImageManager::ReadFileToPixbufAnimation(std::string path, int w, int h) { + const auto &data = ReadWholeFile(path); + if (data.size() == 0) return Glib::RefPtr(nullptr); + auto loader = Gdk::PixbufLoader::create(); + loader->signal_size_prepared().connect([&loader, w, h](int, int) { + loader->set_size(w, h); + }); + loader->write(static_cast(data.data()), data.size()); + loader->close(); + return loader->get_animation(); } void ImageManager::LoadFromURL(std::string url, callback_type cb) { @@ -35,12 +45,28 @@ void ImageManager::LoadFromURL(std::string url, callback_type cb) { m_cb_queue.push([signal, buf]() { signal.emit(buf); }); m_cb_dispatcher.emit(); m_cb_mutex.unlock(); - } catch (std::exception &e) { + } catch (const std::exception &e) { fprintf(stderr, "err loading pixbuf from %s: %s\n", path.c_str(), e.what()); } }); } +void ImageManager::LoadAnimationFromURL(std::string url, int w, int h, callback_anim_type cb) { + sigc::signal)> signal; + signal.connect(cb); + m_cache.GetFileFromURL(url, [this, url, signal, w, h](std::string path) { + try { + auto buf = ReadFileToPixbufAnimation(path, w, h); + m_cb_mutex.lock(); + m_cb_queue.push([signal, buf]() { signal.emit(buf); }); + m_cb_dispatcher.emit(); + m_cb_mutex.unlock(); + } catch (const std::exception &e) { + fprintf(stderr, "err loading pixbuf animation from %s: %s\n", path.c_str(), e.what()); + } + }); +} + void ImageManager::Prefetch(std::string url) { m_cache.GetFileFromURL(url, [](const auto &) {}); } @@ -60,6 +86,14 @@ Glib::RefPtr ImageManager::GetFromURLIfCached(std::string url) { return Glib::RefPtr(nullptr); } +Glib::RefPtr ImageManager::GetAnimationFromURLIfCached(std::string url, int w, int h) { + std::string path = m_cache.GetPathIfCached(url); + if (path != "") + return ReadFileToPixbufAnimation(path, w, h); + + return Glib::RefPtr(nullptr); +} + Glib::RefPtr ImageManager::GetPlaceholder(int size) { std::string name = "/placeholder" + std::to_string(size); if (m_pixs.find(name) != m_pixs.end()) diff --git a/imgmanager.hpp b/imgmanager.hpp index 46d8ab3..f665a7b 100644 --- a/imgmanager.hpp +++ b/imgmanager.hpp @@ -10,16 +10,21 @@ class ImageManager { public: ImageManager(); + using callback_anim_type = sigc::slot)>; using callback_type = sigc::slot)>; Cache &GetCache(); void LoadFromURL(std::string url, callback_type cb); + // animations need dimensions before loading since there is no (easy) way to scale a PixbufAnimation + void LoadAnimationFromURL(std::string url, int w, int h, callback_anim_type cb); void Prefetch(std::string url); Glib::RefPtr GetFromURLIfCached(std::string url); + Glib::RefPtr GetAnimationFromURLIfCached(std::string url, int w, int h); Glib::RefPtr GetPlaceholder(int size); private: Glib::RefPtr ReadFileToPixbuf(std::string path); + Glib::RefPtr ReadFileToPixbufAnimation(std::string path, int w, int h); mutable std::mutex m_load_mutex; void RunCallbacks(); diff --git a/settings.cpp b/settings.cpp index ce7d94e..7790e36 100644 --- a/settings.cpp +++ b/settings.cpp @@ -73,3 +73,7 @@ bool SettingsManager::GetPrefetch() const { std::string SettingsManager::GetMainCSS() const { return GetSettingString("gui", "css", "./css/main.css"); } + +bool SettingsManager::GetShowAnimations() const { + return GetSettingBool("gui", "animations", true); +} diff --git a/settings.hpp b/settings.hpp index 30ce975..7bb022e 100644 --- a/settings.hpp +++ b/settings.hpp @@ -18,6 +18,7 @@ public: int GetCacheHTTPConcurrency() const; bool GetPrefetch() const; std::string GetMainCSS() const; + bool GetShowAnimations() const; bool IsValid() const;