animated avatars on hover

This commit is contained in:
ouwou
2020-12-22 02:24:09 -05:00
parent 8a2f13795d
commit 81d3ab2f86
4 changed files with 80 additions and 21 deletions

View File

@@ -4,6 +4,7 @@
#include <unordered_map> #include <unordered_map>
constexpr const int EmojiSize = 24; // settings eventually constexpr const int EmojiSize = 24; // settings eventually
constexpr const int AvatarSize = 32;
ChatMessageItemContainer::ChatMessageItemContainer() { ChatMessageItemContainer::ChatMessageItemContainer() {
m_main = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); m_main = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
@@ -44,14 +45,14 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(Snowflake id) {
if (data->Content.size() > 0 || data->Type != MessageType::DEFAULT) { if (data->Content.size() > 0 || data->Type != MessageType::DEFAULT) {
container->m_text_component = container->CreateTextComponent(&*data); container->m_text_component = container->CreateTextComponent(&*data);
container->AttachGuildMenuHandler(container->m_text_component); container->AttachEventHandlers(container->m_text_component);
container->m_main->add(*container->m_text_component); container->m_main->add(*container->m_text_component);
} }
// there should only ever be 1 embed (i think?) // there should only ever be 1 embed (i think?)
if (data->Embeds.size() == 1) { if (data->Embeds.size() == 1) {
container->m_embed_component = container->CreateEmbedComponent(&*data); container->m_embed_component = container->CreateEmbedComponent(&*data);
container->AttachGuildMenuHandler(container->m_embed_component); container->AttachEventHandlers(container->m_embed_component);
container->m_main->add(*container->m_embed_component); container->m_main->add(*container->m_embed_component);
} }
@@ -103,7 +104,7 @@ void ChatMessageItemContainer::UpdateContent() {
if (m_embed_imgurl.size() > 0) { if (m_embed_imgurl.size() > 0) {
m_signal_image_load.emit(m_embed_imgurl); m_signal_image_load.emit(m_embed_imgurl);
} }
AttachGuildMenuHandler(m_embed_component); AttachEventHandlers(m_embed_component);
m_main->add(*m_embed_component); m_main->add(*m_embed_component);
} }
} }
@@ -375,7 +376,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateImageComponent(const AttachmentData
widget->set_halign(Gtk::ALIGN_START); widget->set_halign(Gtk::ALIGN_START);
widget->set_size_request(w, h); widget->set_size_request(w, h);
AttachGuildMenuHandler(ev); AttachEventHandlers(ev);
AddClickHandler(ev, data.URL); AddClickHandler(ev, data.URL);
HandleImage(data, widget, data.ProxyURL); HandleImage(data, widget, data.ProxyURL);
@@ -390,7 +391,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateAttachmentComponent(const Attachmen
ev->get_style_context()->add_class("message-attachment-box"); ev->get_style_context()->add_class("message-attachment-box");
ev->add(*btn); ev->add(*btn);
AttachGuildMenuHandler(ev); AttachEventHandlers(ev);
AddClickHandler(ev, data.URL); AddClickHandler(ev, data.URL);
return ev; return ev;
@@ -410,7 +411,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateStickerComponent(const Sticker &dat
// clang-format on // clang-format on
} }
AttachGuildMenuHandler(box); AttachEventHandlers(box);
return box; return box;
} }
@@ -859,21 +860,40 @@ ChatMessageItemContainer::type_signal_action_reaction_remove ChatMessageItemCont
return m_signal_action_reaction_remove; return m_signal_action_reaction_remove;
} }
ChatMessageItemContainer::type_signal_enter ChatMessageItemContainer::signal_enter() {
return m_signal_enter;
}
ChatMessageItemContainer::type_signal_leave ChatMessageItemContainer::signal_leave() {
return m_signal_leave;
}
ChatMessageItemContainer::type_signal_image_load ChatMessageItemContainer::signal_image_load() { ChatMessageItemContainer::type_signal_image_load ChatMessageItemContainer::signal_image_load() {
return m_signal_image_load; return m_signal_image_load;
} }
void ChatMessageItemContainer::AttachGuildMenuHandler(Gtk::Widget *widget) { void ChatMessageItemContainer::AttachEventHandlers(Gtk::Widget *widget) {
// clang-format off const auto on_button_press_event = [this](GdkEventButton *event) -> bool {
widget->signal_button_press_event().connect([this](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) { if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) {
ShowMenu(reinterpret_cast<GdkEvent*>(event)); ShowMenu(reinterpret_cast<GdkEvent *>(event));
return true; return true;
} }
return false; return false;
}, false); };
// clang-format on widget->signal_button_press_event().connect(on_button_press_event, false);
widget->add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
const auto on_notify_crossing = [this](GdkEventCrossing *event) -> bool {
if (event->type == GDK_ENTER_NOTIFY)
m_signal_enter.emit();
else if (event->type == GDK_LEAVE_NOTIFY)
m_signal_leave.emit();
return false;
};
widget->signal_enter_notify_event().connect(on_notify_crossing);
widget->signal_leave_notify_event().connect(on_notify_crossing);
} }
ChatMessageHeader::ChatMessageHeader(const Message *data) { ChatMessageHeader::ChatMessageHeader(const Message *data) {
@@ -890,16 +910,22 @@ ChatMessageHeader::ChatMessageHeader(const Message *data) {
const auto author = Abaddon::Get().GetDiscordClient().GetUser(UserID); const auto author = Abaddon::Get().GetDiscordClient().GetUser(UserID);
auto &img = Abaddon::Get().GetImageManager(); auto &img = Abaddon::Get().GetImageManager();
Glib::RefPtr<Gdk::Pixbuf> buf;
if (author.has_value())
buf = img.GetFromURLIfCached(author->GetAvatarURL());
if (buf) auto static_buf = img.GetFromURLIfCached(author->GetAvatarURL());
m_avatar = Gtk::manage(new Gtk::Image(buf)); auto anim_buf = img.GetAnimationFromURLIfCached(author->GetAvatarURL("gif"), AvatarSize, AvatarSize);
else {
m_avatar = Gtk::manage(new Gtk::Image(img.GetPlaceholder(32))); if (anim_buf)
if (author.has_value()) m_anim_avatar = anim_buf;
if (static_buf) {
m_static_avatar = static_buf;
m_avatar = Gtk::manage(new Gtk::Image(static_buf));
} else {
m_avatar = Gtk::manage(new Gtk::Image(img.GetPlaceholder(AvatarSize)));
if (author.has_value()) {
img.LoadFromURL(author->GetAvatarURL(), sigc::mem_fun(*this, &ChatMessageHeader::OnAvatarLoad)); img.LoadFromURL(author->GetAvatarURL(), sigc::mem_fun(*this, &ChatMessageHeader::OnAvatarLoad));
img.LoadAnimationFromURL(author->GetAvatarURL("gif"), AvatarSize, AvatarSize, sigc::mem_fun(*this, &ChatMessageHeader::OnAnimatedAvatarLoad));
}
} }
get_style_context()->add_class("message-container"); get_style_context()->add_class("message-container");
@@ -990,9 +1016,14 @@ void ChatMessageHeader::UpdateNameColor() {
} }
void ChatMessageHeader::OnAvatarLoad(const Glib::RefPtr<Gdk::Pixbuf> &pixbuf) { void ChatMessageHeader::OnAvatarLoad(const Glib::RefPtr<Gdk::Pixbuf> &pixbuf) {
m_static_avatar = pixbuf;
m_avatar->property_pixbuf() = pixbuf; m_avatar->property_pixbuf() = pixbuf;
} }
void ChatMessageHeader::OnAnimatedAvatarLoad(const Glib::RefPtr<Gdk::PixbufAnimation> &pixbuf) {
m_anim_avatar = pixbuf;
}
void ChatMessageHeader::AttachUserMenuHandler(Gtk::Widget &widget) { void ChatMessageHeader::AttachUserMenuHandler(Gtk::Widget &widget) {
widget.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { widget.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool {
if (ev->type == GDK_BUTTON_PRESS && ev->button == GDK_BUTTON_SECONDARY) { if (ev->type == GDK_BUTTON_PRESS && ev->button == GDK_BUTTON_SECONDARY) {
@@ -1022,6 +1053,19 @@ ChatMessageHeader::type_signal_action_open_user_menu ChatMessageHeader::signal_a
} }
void ChatMessageHeader::AddContent(Gtk::Widget *widget, bool prepend) { void ChatMessageHeader::AddContent(Gtk::Widget *widget, bool prepend) {
if (Abaddon::Get().GetSettings().GetShowAnimations()) {
auto *container = dynamic_cast<ChatMessageItemContainer *>(widget);
if (container != nullptr) {
container->signal_enter().connect([this] {
if (m_anim_avatar)
m_avatar->property_pixbuf_animation() = m_anim_avatar;
});
container->signal_leave().connect([this] {
if (m_anim_avatar) // if there is no anim avatar then it would have never switched off
m_avatar->property_pixbuf() = m_static_avatar;
});
}
}
m_content_box->add(*widget); m_content_box->add(*widget);
if (prepend) if (prepend)
m_content_box->reorder_child(*widget, 1); m_content_box->reorder_child(*widget, 1);

View File

@@ -56,7 +56,7 @@ protected:
std::unordered_map<std::string, std::pair<Gtk::Image *, AttachmentData>> m_img_loadmap; std::unordered_map<std::string, std::pair<Gtk::Image *, AttachmentData>> m_img_loadmap;
void AttachGuildMenuHandler(Gtk::Widget *widget); void AttachEventHandlers(Gtk::Widget *widget);
void ShowMenu(GdkEvent *event); void ShowMenu(GdkEvent *event);
Gtk::Menu m_menu; Gtk::Menu m_menu;
@@ -88,12 +88,16 @@ public:
typedef sigc::signal<void, Snowflake> type_signal_channel_click; typedef sigc::signal<void, Snowflake> type_signal_channel_click;
typedef sigc::signal<void, Glib::ustring> type_signal_action_reaction_add; typedef sigc::signal<void, Glib::ustring> type_signal_action_reaction_add;
typedef sigc::signal<void, Glib::ustring> type_signal_action_reaction_remove; typedef sigc::signal<void, Glib::ustring> type_signal_action_reaction_remove;
typedef sigc::signal<void> type_signal_enter;
typedef sigc::signal<void> type_signal_leave;
type_signal_action_delete signal_action_delete(); type_signal_action_delete signal_action_delete();
type_signal_action_edit signal_action_edit(); type_signal_action_edit signal_action_edit();
type_signal_channel_click signal_action_channel_click(); type_signal_channel_click signal_action_channel_click();
type_signal_action_reaction_add signal_action_reaction_add(); type_signal_action_reaction_add signal_action_reaction_add();
type_signal_action_reaction_remove signal_action_reaction_remove(); type_signal_action_reaction_remove signal_action_reaction_remove();
type_signal_enter signal_enter();
type_signal_leave signal_leave();
type_signal_image_load signal_image_load(); type_signal_image_load signal_image_load();
@@ -103,6 +107,8 @@ private:
type_signal_channel_click m_signal_action_channel_click; type_signal_channel_click m_signal_action_channel_click;
type_signal_action_reaction_add m_signal_action_reaction_add; type_signal_action_reaction_add m_signal_action_reaction_add;
type_signal_action_reaction_remove m_signal_action_reaction_remove; type_signal_action_reaction_remove m_signal_action_reaction_remove;
type_signal_enter m_signal_enter;
type_signal_leave m_signal_leave;
type_signal_image_load m_signal_image_load; type_signal_image_load m_signal_image_load;
}; };
@@ -118,6 +124,7 @@ public:
protected: protected:
void OnAvatarLoad(const Glib::RefPtr<Gdk::Pixbuf> &pixbuf); void OnAvatarLoad(const Glib::RefPtr<Gdk::Pixbuf> &pixbuf);
void OnAnimatedAvatarLoad(const Glib::RefPtr<Gdk::PixbufAnimation> &pixbuf);
void AttachUserMenuHandler(Gtk::Widget &widget); void AttachUserMenuHandler(Gtk::Widget &widget);
@@ -133,6 +140,9 @@ protected:
Gtk::Image *m_avatar; Gtk::Image *m_avatar;
Gtk::EventBox *m_avatar_ev; Gtk::EventBox *m_avatar_ev;
Glib::RefPtr<Gdk::Pixbuf> m_static_avatar;
Glib::RefPtr<Gdk::PixbufAnimation> m_anim_avatar;
typedef sigc::signal<void> type_signal_action_insert_mention; typedef sigc::signal<void> type_signal_action_insert_mention;
typedef sigc::signal<void, const GdkEvent *> type_signal_action_open_user_menu; typedef sigc::signal<void, const GdkEvent *> type_signal_action_open_user_menu;

View File

@@ -5,6 +5,10 @@ bool User::HasAvatar() const {
return Avatar.size() > 0; return Avatar.size() > 0;
} }
bool User::HasAnimatedAvatar() const {
return Avatar.size() > 0 && Avatar[0] == 'a' && Avatar[1] == '_';
}
std::string User::GetAvatarURL(std::string ext, std::string size) const { std::string User::GetAvatarURL(std::string ext, std::string size) const {
return "https://cdn.discordapp.com/avatars/" + std::to_string(ID) + "/" + Avatar + "." + ext + "?size=" + size; return "https://cdn.discordapp.com/avatars/" + std::to_string(ID) + "/" + Avatar + "." + ext + "?size=" + size;
} }

View File

@@ -29,6 +29,7 @@ struct User {
static void update_from_json(const nlohmann::json &j, User &m); static void update_from_json(const nlohmann::json &j, User &m);
bool HasAvatar() const; bool HasAvatar() const;
bool HasAnimatedAvatar() const;
std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const; std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const;
Snowflake GetHoistedRole(Snowflake guild_id, bool with_color = false) const; Snowflake GetHoistedRole(Snowflake guild_id, bool with_color = false) const;
std::string GetMention() const; std::string GetMention() const;