diff --git a/src/components/channellist/cellrendererchannels.cpp b/src/components/channellist/cellrendererchannels.cpp index b049252..bb2b9ba 100644 --- a/src/components/channellist/cellrendererchannels.cpp +++ b/src/components/channellist/cellrendererchannels.cpp @@ -1,13 +1,15 @@ #include "cellrendererchannels.hpp" + #include + +#include "misc/cairo.hpp" + #include "abaddon.hpp" constexpr static int MentionsRightPad = 7; #ifndef M_PI constexpr static double M_PI = 3.14159265358979; #endif -constexpr static double M_PI_H = M_PI / 2.0; -constexpr static double M_PI_3_2 = M_PI * 3.0 / 2.0; void AddUnreadIndicator(const Cairo::RefPtr &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area) { static const auto color_setting = Gdk::RGBA(Abaddon::Get().GetSettings().UnreadIndicatorColor); @@ -832,17 +834,6 @@ void CellRendererChannels::render_vfunc_dm(const Cairo::RefPtr & } } -void CellRendererChannels::cairo_path_rounded_rect(const Cairo::RefPtr &cr, double x, double y, double w, double h, double r) { - const double degrees = M_PI / 180.0; - - cr->begin_new_sub_path(); - cr->arc(x + w - r, y + r, r, -M_PI_H, 0); - cr->arc(x + w - r, y + h - r, r, 0, M_PI_H); - cr->arc(x + r, y + h - r, r, M_PI_H, M_PI); - cr->arc(x + r, y + r, r, M_PI, M_PI_3_2); - cr->close_path(); -} - void CellRendererChannels::unread_render_mentions(const Cairo::RefPtr &cr, Gtk::Widget &widget, int mentions, int edge, const Gdk::Rectangle &cell_area) { Pango::FontDescription font; font.set_family("sans 14"); @@ -863,7 +854,7 @@ void CellRendererChannels::unread_render_mentions(const Cairo::RefPtrset_source_rgb(bg.get_red(), bg.get_green(), bg.get_blue()); cr->fill(); cr->set_source_rgb(text.get_red(), text.get_green(), text.get_blue()); diff --git a/src/components/channellist/cellrendererchannels.hpp b/src/components/channellist/cellrendererchannels.hpp index e142b2a..a313dc7 100644 --- a/src/components/channellist/cellrendererchannels.hpp +++ b/src/components/channellist/cellrendererchannels.hpp @@ -153,7 +153,6 @@ protected: const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags); - static void cairo_path_rounded_rect(const Cairo::RefPtr &cr, double x, double y, double w, double h, double r); static void unread_render_mentions(const Cairo::RefPtr &cr, Gtk::Widget &widget, int mentions, int edge, const Gdk::Rectangle &cell_area); private: diff --git a/src/components/channellist/classic/guildlist.cpp b/src/components/channellist/classic/guildlist.cpp index 6a6e3d4..17cd79a 100644 --- a/src/components/channellist/classic/guildlist.cpp +++ b/src/components/channellist/classic/guildlist.cpp @@ -3,6 +3,7 @@ #include "abaddon.hpp" #include "util.hpp" #include "guildlistfolderitem.hpp" +#include "mentionoverlay.hpp" class GuildListDMsButton : public Gtk::EventBox { public: @@ -93,10 +94,19 @@ void GuildList::UpdateListing() { } } +static Gtk::Widget *AddMentionOverlay(Gtk::Widget *widget, Snowflake guild_id) { + auto *overlay = Gtk::make_managed(); + overlay->add(*widget); + auto *mention_overlay = Gtk::make_managed(guild_id); + overlay->add_overlay(*mention_overlay); + overlay->show_all(); + return overlay; +} + void GuildList::AddGuild(Snowflake id) { if (auto item = CreateGuildWidget(id)) { item->show(); - add(*item); + add(*AddMentionOverlay(item, id)); } } @@ -132,7 +142,7 @@ void GuildList::AddFolder(const UserSettingsGuildFoldersEntry &folder) { for (const auto guild_id : folder.GuildIDs) { if (auto *guild_widget = CreateGuildWidget(guild_id)) { guild_widget->show(); - folder_widget->AddGuildWidget(guild_widget); + folder_widget->AddGuildWidget(AddMentionOverlay(guild_widget, guild_id)); } } diff --git a/src/components/channellist/classic/guildlistfolderitem.cpp b/src/components/channellist/classic/guildlistfolderitem.cpp index e062d42..337d4b3 100644 --- a/src/components/channellist/classic/guildlistfolderitem.cpp +++ b/src/components/channellist/classic/guildlistfolderitem.cpp @@ -95,7 +95,7 @@ GuildListFolderItem::GuildListFolderItem(const UserSettingsGuildFoldersEntry &fo CheckUnreadStatus(); } -void GuildListFolderItem::AddGuildWidget(GuildListGuildItem *widget) { +void GuildListFolderItem::AddGuildWidget(Gtk::Widget *widget) { m_box.add(*widget); } diff --git a/src/components/channellist/classic/guildlistfolderitem.hpp b/src/components/channellist/classic/guildlistfolderitem.hpp index e5772c0..06d05f1 100644 --- a/src/components/channellist/classic/guildlistfolderitem.hpp +++ b/src/components/channellist/classic/guildlistfolderitem.hpp @@ -24,7 +24,7 @@ class GuildListFolderItem : public Gtk::VBox { public: GuildListFolderItem(const UserSettingsGuildFoldersEntry &folder); - void AddGuildWidget(GuildListGuildItem *widget); + void AddGuildWidget(Gtk::Widget *widget); private: void OnMessageCreate(const Message &msg); diff --git a/src/components/channellist/classic/mentionoverlay.cpp b/src/components/channellist/classic/mentionoverlay.cpp new file mode 100644 index 0000000..c734ced --- /dev/null +++ b/src/components/channellist/classic/mentionoverlay.cpp @@ -0,0 +1,62 @@ +#include "mentionoverlay.hpp" + +#include "misc/cairo.hpp" + +#include "abaddon.hpp" + +MentionOverlay::MentionOverlay(Snowflake guild_id) + : m_guild_id(guild_id) { + m_font.set_family("sans 14"); + m_layout = create_pango_layout("12"); + m_layout->set_font_description(m_font); + m_layout->set_alignment(Pango::ALIGN_RIGHT); + + get_style_context()->add_class("classic-mention-overlay"); // fuck you + + set_hexpand(false); + set_vexpand(false); + + signal_draw().connect(sigc::mem_fun(*this, &MentionOverlay::OnDraw)); + + Abaddon::Get().GetDiscordClient().signal_message_ack().connect([this](const MessageAckData &data) { + // fetching and checking guild id is probably more expensive than just forcing a redraw anyways + queue_draw(); + }); + + Abaddon::Get().GetDiscordClient().signal_message_create().connect([this](const Message &msg) { + if (msg.GuildID.has_value() && *msg.GuildID != m_guild_id) return; + if (!msg.DoesMentionEveryone && msg.Mentions.empty() && msg.MentionRoles.empty()) return; + queue_draw(); + }); +} + +bool MentionOverlay::OnDraw(const Cairo::RefPtr &cr) { + int mentions; + Abaddon::Get().GetDiscordClient().GetUnreadStateForGuild(m_guild_id, mentions); + if (mentions == 0) return true; + m_layout->set_text(std::to_string(mentions)); + + const int width = get_allocated_width(); + const int height = get_allocated_height(); + + int lw, lh; + m_layout->get_pixel_size(lw, lh); + { + static const auto badge_setting = Gdk::RGBA(Abaddon::Get().GetSettings().MentionBadgeColor); + static const auto text_setting = Gdk::RGBA(Abaddon::Get().GetSettings().MentionBadgeTextColor); + + auto bg = badge_setting.get_alpha_u() > 0 ? badge_setting : get_style_context()->get_background_color(Gtk::STATE_FLAG_SELECTED); + auto text = text_setting.get_alpha_u() > 0 ? text_setting : get_style_context()->get_color(Gtk::STATE_FLAG_SELECTED); + + const auto x = width - lw - 5; + const auto y = height - lh - 1; + CairoUtil::PathRoundedRect(cr, x - 4, y + 2, lw + 8, lh, 5); + cr->set_source_rgb(bg.get_red(), bg.get_green(), bg.get_blue()); + cr->fill(); + cr->set_source_rgb(text.get_red(), text.get_green(), text.get_blue()); + cr->move_to(x, y); + m_layout->show_in_cairo_context(cr); + } + + return true; +} diff --git a/src/components/channellist/classic/mentionoverlay.hpp b/src/components/channellist/classic/mentionoverlay.hpp new file mode 100644 index 0000000..24eb7e2 --- /dev/null +++ b/src/components/channellist/classic/mentionoverlay.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#include "discord/snowflake.hpp" + +class MentionOverlay : public Gtk::DrawingArea { +public: + MentionOverlay(Snowflake guild_id); + +private: + bool OnDraw(const Cairo::RefPtr &cr); + + Snowflake m_guild_id; + + Pango::FontDescription m_font; + Glib::RefPtr m_layout; +}; diff --git a/src/misc/cairo.cpp b/src/misc/cairo.cpp new file mode 100644 index 0000000..1272f75 --- /dev/null +++ b/src/misc/cairo.cpp @@ -0,0 +1,17 @@ +#include "cairo.hpp" + +#include + +constexpr static double M_PI_H = M_PI / 2.0; +constexpr static double M_PI_3_2 = M_PI * 3.0 / 2.0; + +void CairoUtil::PathRoundedRect(const Cairo::RefPtr &cr, double x, double y, double w, double h, double r) { + const double degrees = M_PI / 180.0; + + cr->begin_new_sub_path(); + cr->arc(x + w - r, y + r, r, -M_PI_H, 0); + cr->arc(x + w - r, y + h - r, r, 0, M_PI_H); + cr->arc(x + r, y + h - r, r, M_PI_H, M_PI); + cr->arc(x + r, y + r, r, M_PI, M_PI_3_2); + cr->close_path(); +} \ No newline at end of file diff --git a/src/misc/cairo.hpp b/src/misc/cairo.hpp new file mode 100644 index 0000000..b66fa34 --- /dev/null +++ b/src/misc/cairo.hpp @@ -0,0 +1,5 @@ +#pragma once + +namespace CairoUtil { +void PathRoundedRect(const Cairo::RefPtr &cr, double x, double y, double w, double h, double r); +} // namespace CairoUtil