diff options
author | ouwou <26526779+ouwou@users.noreply.github.com> | 2023-12-27 01:57:22 -0500 |
---|---|---|
committer | ouwou <26526779+ouwou@users.noreply.github.com> | 2023-12-27 01:57:22 -0500 |
commit | 1bb749687b92fa7fbccb77ee18654210a647a1f3 (patch) | |
tree | 68b79b6ca1ef7b3c17c4ada5655255c20f2fa8d3 /src/components | |
parent | 8bd628c1776c20dbf41d2fb18f6204c4a398da0e (diff) | |
parent | 155d95e29cc50a66cbe7711b172b195be637bc48 (diff) | |
download | abaddon-portaudio-1bb749687b92fa7fbccb77ee18654210a647a1f3.tar.gz abaddon-portaudio-1bb749687b92fa7fbccb77ee18654210a647a1f3.zip |
Merge branch 'master' into classic-channels
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/cellrenderermemberlist.cpp | 34 | ||||
-rw-r--r-- | src/components/cellrenderermemberlist.hpp | 3 | ||||
-rw-r--r-- | src/components/channellist/cellrendererchannels.cpp | 229 | ||||
-rw-r--r-- | src/components/channellist/channellisttree.cpp | 1 | ||||
-rw-r--r-- | src/components/chatinput.cpp | 82 | ||||
-rw-r--r-- | src/components/chatinput.hpp | 20 | ||||
-rw-r--r-- | src/components/chatmessage.cpp | 33 | ||||
-rw-r--r-- | src/components/chatwindow.cpp | 10 | ||||
-rw-r--r-- | src/components/memberlist.cpp | 19 | ||||
-rw-r--r-- | src/components/memberlist.hpp | 3 | ||||
-rw-r--r-- | src/components/progressbar.cpp | 19 | ||||
-rw-r--r-- | src/components/progressbar.hpp | 11 | ||||
-rw-r--r-- | src/components/statusindicator.cpp | 122 | ||||
-rw-r--r-- | src/components/statusindicator.hpp | 29 |
14 files changed, 226 insertions, 389 deletions
diff --git a/src/components/cellrenderermemberlist.cpp b/src/components/cellrenderermemberlist.cpp index 66b223e..ee1ea3e 100644 --- a/src/components/cellrenderermemberlist.cpp +++ b/src/components/cellrenderermemberlist.cpp @@ -6,7 +6,8 @@ CellRendererMemberList::CellRendererMemberList() , m_property_id(*this, "id") , m_property_name(*this, "name") , m_property_pixbuf(*this, "pixbuf") - , m_property_color(*this, "color") { + , m_property_color(*this, "color") + , m_property_status(*this, "status") { property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; property_xpad() = 2; property_ypad() = 2; @@ -35,6 +36,10 @@ Glib::PropertyProxy<Gdk::RGBA> CellRendererMemberList::property_color() { return m_property_color.get_proxy(); } +Glib::PropertyProxy<PresenceStatus> CellRendererMemberList::property_status() { + return m_property_status.get_proxy(); +} + void CellRendererMemberList::get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const { switch (m_property_type.get_value()) { case MemberListRenderType::Role: @@ -117,8 +122,9 @@ void CellRendererMemberList::get_preferred_height_for_width_vfunc_member(Gtk::Wi } void CellRendererMemberList::render_vfunc_member(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) { + // Text Gdk::Rectangle text_cell_area = cell_area; - text_cell_area.set_x(22); + text_cell_area.set_x(31); const auto color = m_property_color.get_value(); if (color.get_alpha_u() > 0) { m_renderer_text.property_foreground_rgba().set_value(color); @@ -126,6 +132,30 @@ void CellRendererMemberList::render_vfunc_member(const Cairo::RefPtr<Cairo::Cont m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); m_renderer_text.property_foreground_set().set_value(false); + // Status indicator + // TODO: reintroduce custom status colors... somehow + cr->begin_new_path(); + switch (m_property_status.get_value()) { + case PresenceStatus::Online: + cr->set_source_rgb(33.0 / 255.0, 157.0 / 255.0, 86.0 / 255.0); + break; + case PresenceStatus::Idle: + cr->set_source_rgb(230.0 / 255.0, 170.0 / 255.0, 48.0 / 255.0); + break; + case PresenceStatus::DND: + cr->set_source_rgb(233.0 / 255.0, 61.0 / 255.0, 65.0 / 255.0); + break; + case PresenceStatus::Offline: + cr->set_source_rgb(122.0 / 255.0, 126.0 / 255.0, 135.0 / 255.0); + break; + } + + cr->arc(background_area.get_x() + 6.0 + 16.0 + 6.0, background_area.get_y() + background_area.get_height() / 2.0, 2.0, 0.0, 2 * (4 * std::atan(1))); + cr->close_path(); + cr->fill_preserve(); + cr->stroke(); + + // Icon const double icon_x = background_area.get_x() + 6.0; const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - 8.0; Gdk::Cairo::set_source_pixbuf(cr, m_property_pixbuf.get_value(), icon_x, icon_y); diff --git a/src/components/cellrenderermemberlist.hpp b/src/components/cellrenderermemberlist.hpp index 7a49ccf..79d32e3 100644 --- a/src/components/cellrenderermemberlist.hpp +++ b/src/components/cellrenderermemberlist.hpp @@ -1,5 +1,6 @@ #pragma once #include <gtkmm/cellrenderer.h> +#include "discord/activity.hpp" enum class MemberListRenderType : uint8_t { Role, @@ -16,6 +17,7 @@ public: Glib::PropertyProxy<Glib::ustring> property_name(); Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf>> property_pixbuf(); Glib::PropertyProxy<Gdk::RGBA> property_color(); + Glib::PropertyProxy<PresenceStatus> property_status(); protected: void get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const override; @@ -56,6 +58,7 @@ private: Glib::Property<Glib::ustring> m_property_name; Glib::Property<Glib::RefPtr<Gdk::Pixbuf>> m_property_pixbuf; Glib::Property<Gdk::RGBA> m_property_color; + Glib::Property<PresenceStatus> m_property_status; using type_signal_render = sigc::signal<void(uint64_t)>; type_signal_render m_signal_render; diff --git a/src/components/channellist/cellrendererchannels.cpp b/src/components/channellist/cellrendererchannels.cpp index e0079cd..3db136b 100644 --- a/src/components/channellist/cellrendererchannels.cpp +++ b/src/components/channellist/cellrendererchannels.cpp @@ -7,6 +7,49 @@ constexpr static double M_PI = 3.14159265358979; 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<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area) { + static const auto color_setting = Gdk::RGBA(Abaddon::Get().GetSettings().UnreadIndicatorColor); + + const auto color = color_setting.get_alpha_u() > 0 ? color_setting : widget.get_style_context()->get_background_color(Gtk::STATE_FLAG_SELECTED); + + cr->set_source_rgb(color.get_red(), color.get_green(), color.get_blue()); + const auto x = background_area.get_x(); + const auto y = background_area.get_y(); + const auto w = background_area.get_width(); + const auto h = background_area.get_height(); + cr->rectangle(x, y, 3, h); + cr->fill(); +} + +void RenderExpander(int x_offset, const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, bool is_expanded) { + constexpr static int len = 5; + int x1, y1, x2, y2, x3, y3; + if (is_expanded) { + x1 = background_area.get_x() + x_offset; + y1 = background_area.get_y() + background_area.get_height() / 2 - len; + x2 = background_area.get_x() + x_offset + len; + y2 = background_area.get_y() + background_area.get_height() / 2 + len; + x3 = background_area.get_x() + x_offset + len * 2; + y3 = background_area.get_y() + background_area.get_height() / 2 - len; + } else { + x1 = background_area.get_x() + x_offset; + y1 = background_area.get_y() + background_area.get_height() / 2 - len; + x2 = background_area.get_x() + x_offset + len * 2; + y2 = background_area.get_y() + background_area.get_height() / 2; + x3 = background_area.get_x() + x_offset; + y3 = background_area.get_y() + background_area.get_height() / 2 + len; + } + cr->move_to(x1, y1); + cr->line_to(x2, y2); + cr->line_to(x3, y3); + auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelsExpanderColor); + if (expander_color.get_alpha_u() == 0) { + expander_color = widget.get_style_context()->get_background_color(Gtk::STATE_FLAG_SELECTED); + } + cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue()); + cr->stroke(); +} + CellRendererChannels::CellRendererChannels() : Glib::ObjectBase(typeid(CellRendererChannels)) , Gtk::CellRenderer() @@ -207,30 +250,7 @@ void CellRendererChannels::get_preferred_height_for_width_vfunc_folder(Gtk::Widg } void CellRendererChannels::render_vfunc_folder(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) { - constexpr static int len = 5; - int x1, y1, x2, y2, x3, y3; - if (property_expanded()) { - x1 = background_area.get_x() + 7; - y1 = background_area.get_y() + background_area.get_height() / 2 - len; - x2 = background_area.get_x() + 7 + len; - y2 = background_area.get_y() + background_area.get_height() / 2 + len; - x3 = background_area.get_x() + 7 + len * 2; - y3 = background_area.get_y() + background_area.get_height() / 2 - len; - } else { - x1 = background_area.get_x() + 7; - y1 = background_area.get_y() + background_area.get_height() / 2 - len; - x2 = background_area.get_x() + 7 + len * 2; - y2 = background_area.get_y() + background_area.get_height() / 2; - x3 = background_area.get_x() + 7; - y3 = background_area.get_y() + background_area.get_height() / 2 + len; - } - cr->move_to(x1, y1); - cr->line_to(x2, y2); - cr->line_to(x3, y3); - const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelsExpanderColor); - cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue()); - cr->stroke(); - + RenderExpander(7, cr, widget, background_area, property_expanded()); Gtk::Requisition text_minimum, text_natural; m_renderer_text.get_preferred_size(widget, text_minimum, text_natural); @@ -241,11 +261,8 @@ void CellRendererChannels::render_vfunc_folder(const Cairo::RefPtr<Cairo::Contex Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h); - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelColor); if (m_property_color.get_value().has_value()) { m_renderer_text.property_foreground_rgba() = *m_property_color.get_value(); - } else { - m_renderer_text.property_foreground_rgba() = color; } m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); m_renderer_text.property_foreground_set() = false; @@ -325,8 +342,6 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context static_cast<int>(text_w), static_cast<int>(text_h)); - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelColor); - m_renderer_text.property_foreground_rgba() = color; m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); const bool hover_only = Abaddon::Get().GetSettings().AnimatedGuildHoverOnly; @@ -373,14 +388,9 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context const auto has_unread = discord.GetUnreadStateForGuild(id, total_mentions); if (has_unread && !discord.IsGuildMuted(id)) { - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().UnreadIndicatorColor); - cr->set_source_rgb(color.get_red(), color.get_green(), color.get_blue()); - const auto x = background_area.get_x(); - const auto y = background_area.get_y(); - const auto w = background_area.get_width(); - const auto h = background_area.get_height(); - cr->rectangle(x, y + h / 2.0 - 24.0 / 2.0, 3.0, 24.0); - cr->fill(); + auto area = background_area; + area.set_y(area.get_y() + area.get_height() / 2.0 - 24.0 / 2.0); + AddUnreadIndicator(cr, widget, area); } if (total_mentions < 1) return; @@ -410,42 +420,8 @@ void CellRendererChannels::get_preferred_height_for_width_vfunc_category(Gtk::Wi m_renderer_text.get_preferred_height_for_width(widget, width, minimum_height, natural_height); } -void AddUnreadIndicator(const Cairo::RefPtr<Cairo::Context> &cr, const Gdk::Rectangle &background_area) { - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().UnreadIndicatorColor); - cr->set_source_rgb(color.get_red(), color.get_green(), color.get_blue()); - const auto x = background_area.get_x(); - const auto y = background_area.get_y(); - const auto w = background_area.get_width(); - const auto h = background_area.get_height(); - cr->rectangle(x, y, 3, h); - cr->fill(); -} - void CellRendererChannels::render_vfunc_category(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) { - // todo: figure out how Gtk::Arrow is rendered because i like it better :^) - constexpr static int len = 5; - int x1, y1, x2, y2, x3, y3; - if (property_expanded()) { - x1 = background_area.get_x() + 7; - y1 = background_area.get_y() + background_area.get_height() / 2 - len; - x2 = background_area.get_x() + 7 + len; - y2 = background_area.get_y() + background_area.get_height() / 2 + len; - x3 = background_area.get_x() + 7 + len * 2; - y3 = background_area.get_y() + background_area.get_height() / 2 - len; - } else { - x1 = background_area.get_x() + 7; - y1 = background_area.get_y() + background_area.get_height() / 2 - len; - x2 = background_area.get_x() + 7 + len * 2; - y2 = background_area.get_y() + background_area.get_height() / 2; - x3 = background_area.get_x() + 7; - y3 = background_area.get_y() + background_area.get_height() / 2 + len; - } - cr->move_to(x1, y1); - cr->line_to(x2, y2); - cr->line_to(x3, y3); - const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelsExpanderColor); - cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue()); - cr->stroke(); + RenderExpander(7, cr, widget, background_area, property_expanded()); Gtk::Requisition text_minimum, text_natural; m_renderer_text.get_preferred_size(widget, text_minimum, text_natural); @@ -457,23 +433,14 @@ void CellRendererChannels::render_vfunc_category(const Cairo::RefPtr<Cairo::Cont Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h); - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelColor); auto &discord = Abaddon::Get().GetDiscordClient(); const auto id = m_property_id.get_value(); - if (discord.IsChannelMuted(m_property_id.get_value())) { - auto muted = color; - muted.set_red(muted.get_red() * 0.5); - muted.set_green(muted.get_green() * 0.5); - muted.set_blue(muted.get_blue() * 0.5); - m_renderer_text.property_foreground_rgba() = muted; - } else { + if (!discord.IsChannelMuted(m_property_id.get_value())) { if (discord.GetUnreadChannelsCountForCategory(id) > 0) { - AddUnreadIndicator(cr, background_area); + AddUnreadIndicator(cr, widget, background_area); } - m_renderer_text.property_foreground_rgba() = color; } m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); - m_renderer_text.property_foreground_set() = false; } // text channel @@ -509,23 +476,14 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr<Cairo::Conte const auto id = m_property_id.get_value(); const bool is_muted = discord.IsChannelMuted(id); - static const auto sfw_unmuted = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelColor); - - m_renderer_text.property_sensitive() = false; static const auto nsfw_color = Gdk::RGBA(Abaddon::Get().GetSettings().NSFWChannelColor); - if (m_property_nsfw.get_value()) - m_renderer_text.property_foreground_rgba() = nsfw_color; - else - m_renderer_text.property_foreground_rgba() = sfw_unmuted; - if (is_muted) { - auto col = m_renderer_text.property_foreground_rgba().get_value(); - col.set_red(col.get_red() * 0.5); - col.set_green(col.get_green() * 0.5); - col.set_blue(col.get_blue() * 0.5); - m_renderer_text.property_foreground_rgba() = col; - } + + auto color = widget.get_style_context()->get_color(Gtk::STATE_FLAG_NORMAL); + if (property_nsfw()) color = nsfw_color; + if (is_muted) color.set_alpha(0.6); + + m_renderer_text.property_foreground_rgba() = color; m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); - // unset foreground to default so properties dont bleed m_renderer_text.property_foreground_set() = false; // unread @@ -535,7 +493,7 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr<Cairo::Conte if (unread_state < 0) return; if (!is_muted) { - AddUnreadIndicator(cr, background_area); + AddUnreadIndicator(cr, widget, background_area); } if (unread_state < 1) return; @@ -580,18 +538,7 @@ void CellRendererChannels::render_vfunc_thread(const Cairo::RefPtr<Cairo::Contex const auto id = m_property_id.get_value(); const bool is_muted = discord.IsChannelMuted(id); - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelColor); - if (Abaddon::Get().GetDiscordClient().IsChannelMuted(m_property_id.get_value())) { - auto muted = color; - muted.set_red(muted.get_red() * 0.5); - muted.set_green(muted.get_green() * 0.5); - muted.set_blue(muted.get_blue() * 0.5); - m_renderer_text.property_foreground_rgba() = muted; - } else { - m_renderer_text.property_foreground_rgba() = color; - } m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); - m_renderer_text.property_foreground_set() = false; // unread if (!Abaddon::Get().GetSettings().Unreads) return; @@ -600,14 +547,7 @@ void CellRendererChannels::render_vfunc_thread(const Cairo::RefPtr<Cairo::Contex if (unread_state < 0) return; if (!is_muted) { - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().UnreadIndicatorColor); - cr->set_source_rgb(color.get_red(), color.get_green(), color.get_blue()); - const auto x = background_area.get_x(); - const auto y = background_area.get_y(); - const auto w = background_area.get_width(); - const auto h = background_area.get_height(); - cr->rectangle(x, y, 3, h); - cr->fill(); + AddUnreadIndicator(cr, widget, background_area); } if (unread_state < 1) return; @@ -667,31 +607,7 @@ void CellRendererChannels::render_vfunc_voice_channel(const Cairo::RefPtr<Cairo: cell_area.get_y() + cell_area.get_height() / 2.0 - height / 2.0); layout->show_in_cairo_context(cr); - // expander - constexpr static int len = 5; - constexpr static int offset = 24; - int x1, y1, x2, y2, x3, y3; - if (property_expanded()) { - x1 = background_area.get_x() + offset; - y1 = background_area.get_y() + background_area.get_height() / 2 - len; - x2 = background_area.get_x() + offset + len; - y2 = background_area.get_y() + background_area.get_height() / 2 + len; - x3 = background_area.get_x() + offset + len * 2; - y3 = background_area.get_y() + background_area.get_height() / 2 - len; - } else { - x1 = background_area.get_x() + offset; - y1 = background_area.get_y() + background_area.get_height() / 2 - len; - x2 = background_area.get_x() + offset + len * 2; - y2 = background_area.get_y() + background_area.get_height() / 2; - x3 = background_area.get_x() + offset; - y3 = background_area.get_y() + background_area.get_height() / 2 + len; - } - cr->move_to(x1, y1); - cr->line_to(x2, y2); - cr->line_to(x3, y3); - const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelsExpanderColor); - cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue()); - cr->stroke(); + RenderExpander(24, cr, widget, background_area, property_expanded()); } // voice participant @@ -897,18 +813,7 @@ void CellRendererChannels::render_vfunc_dm(const Cairo::RefPtr<Cairo::Context> & const auto id = m_property_id.get_value(); const bool is_muted = discord.IsChannelMuted(id); - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelColor); - if (Abaddon::Get().GetDiscordClient().IsChannelMuted(m_property_id.get_value())) { - auto muted = color; - muted.set_red(muted.get_red() * 0.5); - muted.set_green(muted.get_green() * 0.5); - muted.set_blue(muted.get_blue() * 0.5); - m_renderer_text.property_foreground_rgba() = muted; - } else { - m_renderer_text.property_foreground_rgba() = color; - } m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); - m_renderer_text.property_foreground_set() = false; 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); @@ -921,14 +826,7 @@ void CellRendererChannels::render_vfunc_dm(const Cairo::RefPtr<Cairo::Context> & if (unread_state < 0) return; if (!is_muted) { - static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().UnreadIndicatorColor); - cr->set_source_rgb(color.get_red(), color.get_green(), color.get_blue()); - const auto x = background_area.get_x(); - const auto y = background_area.get_y(); - const auto w = background_area.get_width(); - const auto h = background_area.get_height(); - cr->rectangle(x, y, 3, h); - cr->fill(); + AddUnreadIndicator(cr, widget, background_area); } } @@ -955,8 +853,11 @@ void CellRendererChannels::unread_render_mentions(const Cairo::RefPtr<Cairo::Con int width, height; layout->get_pixel_size(width, height); { - static const auto bg = Gdk::RGBA(Abaddon::Get().GetSettings().MentionBadgeColor); - static const auto text = Gdk::RGBA(Abaddon::Get().GetSettings().MentionBadgeTextColor); + 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 : widget.get_style_context()->get_background_color(Gtk::STATE_FLAG_SELECTED); + auto text = text_setting.get_alpha_u() > 0 ? text_setting : widget.get_style_context()->get_color(Gtk::STATE_FLAG_SELECTED); const auto x = cell_area.get_x() + edge - width - MentionsRightPad; const auto y = cell_area.get_y() + cell_area.get_height() / 2.0 - height / 2.0 - 1; diff --git a/src/components/channellist/channellisttree.cpp b/src/components/channellist/channellisttree.cpp index c166d42..0e2cda6 100644 --- a/src/components/channellist/channellisttree.cpp +++ b/src/components/channellist/channellisttree.cpp @@ -1,6 +1,5 @@ #include "channellisttree.hpp" #include "imgmanager.hpp" -#include "components/statusindicator.hpp" #include <algorithm> #include <map> #include <unordered_map> diff --git a/src/components/chatinput.cpp b/src/components/chatinput.cpp index c802413..1db03ed 100644 --- a/src/components/chatinput.cpp +++ b/src/components/chatinput.cpp @@ -20,7 +20,7 @@ ChatInputText::ChatInputText() { m_textview.signal_key_press_event().connect(cb, false); m_textview.set_hexpand(false); m_textview.set_halign(Gtk::ALIGN_FILL); - m_textview.set_valign(Gtk::ALIGN_CENTER); + m_textview.set_valign(Gtk::ALIGN_FILL); m_textview.set_wrap_mode(Gtk::WRAP_WORD_CHAR); m_textview.show(); add(m_textview); @@ -113,30 +113,25 @@ ChatInputTextContainer::ChatInputTextContainer() { }; m_input.signal_key_press_proxy().connect(cb); + m_upload_button.set_image(m_upload_img); + m_upload_button.set_halign(Gtk::ALIGN_CENTER); + m_upload_button.set_valign(Gtk::ALIGN_CENTER); + m_upload_button.get_style_context()->add_class(GTK_STYLE_CLASS_FLAT); + m_upload_img.property_icon_name() = "document-send-symbolic"; m_upload_img.property_icon_size() = Gtk::ICON_SIZE_LARGE_TOOLBAR; m_upload_img.get_style_context()->add_class("message-input-browse-icon"); - AddPointerCursor(m_upload_ev); - - m_upload_ev.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { - if (ev->button == GDK_BUTTON_PRIMARY) { - ShowFileChooser(); - // return focus - m_input.grab_focus(); - return true; - } - return false; + m_upload_button.signal_clicked().connect([this]() { + ShowFileChooser(); + m_input.grab_focus(); }); - m_upload_ev.add(m_upload_img); - add_overlay(m_upload_ev); - add(m_input); + m_upload_box.pack_start(m_upload_button); + pack_start(m_upload_box, false, false); + pack_start(m_input); show_all_children(); - - // stop the overlay from using (start) padding - signal_get_child_position().connect(sigc::mem_fun(*this, &ChatInputTextContainer::GetChildPosition), false); } void ChatInputTextContainer::ShowFileChooser() { @@ -160,39 +155,11 @@ ChatInputText &ChatInputTextContainer::Get() { } void ChatInputTextContainer::ShowChooserIcon() { - m_upload_ev.show(); + m_upload_button.show(); } void ChatInputTextContainer::HideChooserIcon() { - m_upload_ev.hide(); -} - -bool ChatInputTextContainer::GetChildPosition(Gtk::Widget *child, Gdk::Rectangle &pos) { - Gtk::Allocation main_alloc; - { - auto *grandchild = m_input.get_child(); - int x, y; - if (grandchild->translate_coordinates(m_input, 0, 0, x, y)) { - main_alloc.set_x(x); - main_alloc.set_y(y); - } else { - main_alloc.set_x(0); - main_alloc.set_y(0); - } - main_alloc.set_width(grandchild->get_allocated_width()); - main_alloc.set_height(grandchild->get_allocated_height()); - } - - Gtk::Requisition min, req; - child->get_preferred_size(min, req); - - // let css move it around - pos.set_x(0); - pos.set_y(0); - pos.set_width(std::max(min.width, std::min(main_alloc.get_width(), req.width))); - pos.set_height(std::max(min.height, std::min(main_alloc.get_height(), req.height))); - - return true; + m_upload_button.hide(); } ChatInputTextContainer::type_signal_add_attachment ChatInputTextContainer::signal_add_attachment() { @@ -295,7 +262,7 @@ std::vector<ChatSubmitParams::Attachment> ChatInputAttachmentContainer::GetAttac for (auto *x : m_attachments) { if (!x->GetFile()->query_exists()) puts("bad!"); - ret.push_back({ x->GetFile(), x->GetType(), x->GetFilename() }); + ret.push_back({ x->GetFile(), x->GetType(), x->GetFilename(), x->GetDescription() }); } return ret; } @@ -343,6 +310,7 @@ ChatInputAttachmentItem::ChatInputAttachmentItem(const Glib::RefPtr<Gio::File> & , m_img(Gtk::make_managed<Gtk::Image>()) , m_type(is_extant ? ChatSubmitParams::ExtantFile : ChatSubmitParams::PastedImage) , m_filename("unknown.png") + , m_is_image(true) , m_label("unknown.png") , m_box(Gtk::ORIENTATION_VERTICAL) { get_style_context()->add_class("attachment-item"); @@ -389,10 +357,18 @@ std::string ChatInputAttachmentItem::GetFilename() const { return m_filename; } +std::optional<std::string> ChatInputAttachmentItem::GetDescription() const { + return m_description.empty() ? std::nullopt : std::optional<std::string>(m_description); +} + bool ChatInputAttachmentItem::IsTemp() const noexcept { return m_type == ChatSubmitParams::PastedImage; } +bool ChatInputAttachmentItem::IsImage() const noexcept { + return m_is_image; +} + void ChatInputAttachmentItem::RemoveIfTemp() { if (IsTemp()) m_file->remove(); @@ -420,12 +396,22 @@ void ChatInputAttachmentItem::SetupMenu() { } }); + m_menu_set_alt_text.set_label("Change Alt-Text"); + m_menu_set_alt_text.signal_activate().connect([this]() { + const auto description = Abaddon::Get().ShowTextPrompt("Enter description (alt-text) for attachment", "Enter alt-text", m_description); + if (description.has_value()) { + m_description = *description; + } + }); + m_menu.add(m_menu_set_filename); + m_menu.add(m_menu_set_alt_text); m_menu.add(m_menu_remove); m_menu.show_all(); signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { if (ev->button == GDK_BUTTON_SECONDARY) { + m_menu_set_alt_text.set_visible(IsImage()); m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev)); return true; } diff --git a/src/components/chatinput.hpp b/src/components/chatinput.hpp index a3c9742..231d67c 100644 --- a/src/components/chatinput.hpp +++ b/src/components/chatinput.hpp @@ -7,10 +7,13 @@ public: ChatInputAttachmentItem(const Glib::RefPtr<Gio::File> &file); ChatInputAttachmentItem(const Glib::RefPtr<Gio::File> &file, const Glib::RefPtr<Gdk::Pixbuf> &pb, bool is_extant = false); - [[nodiscard]] Glib::RefPtr<Gio::File> GetFile() const; - [[nodiscard]] ChatSubmitParams::AttachmentType GetType() const; - [[nodiscard]] std::string GetFilename() const; - [[nodiscard]] bool IsTemp() const noexcept; + Glib::RefPtr<Gio::File> GetFile() const; + ChatSubmitParams::AttachmentType GetType() const; + std::string GetFilename() const; + std::optional<std::string> GetDescription() const; + bool IsTemp() const noexcept; + bool IsImage() const noexcept; + void RemoveIfTemp(); private: @@ -21,6 +24,7 @@ private: Gtk::Menu m_menu; Gtk::MenuItem m_menu_remove; Gtk::MenuItem m_menu_set_filename; + Gtk::MenuItem m_menu_set_alt_text; Gtk::Box m_box; Gtk::Label m_label; @@ -29,6 +33,8 @@ private: Glib::RefPtr<Gio::File> m_file; ChatSubmitParams::AttachmentType m_type; std::string m_filename; + std::string m_description; + bool m_is_image = false; private: using type_signal_item_removed = sigc::signal<void>; @@ -98,7 +104,7 @@ private: }; // file upload, text -class ChatInputTextContainer : public Gtk::Overlay { +class ChatInputTextContainer : public Gtk::Box { public: ChatInputTextContainer(); @@ -110,9 +116,9 @@ public: private: void ShowFileChooser(); - bool GetChildPosition(Gtk::Widget *child, Gdk::Rectangle &pos); - Gtk::EventBox m_upload_ev; + Gtk::Box m_upload_box; + Gtk::Button m_upload_button; Gtk::Image m_upload_img; ChatInputText m_input; diff --git a/src/components/chatmessage.cpp b/src/components/chatmessage.cpp index 44396a2..a503294 100644 --- a/src/components/chatmessage.cpp +++ b/src/components/chatmessage.cpp @@ -46,6 +46,9 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(const Message &d for (const auto &a : data.Attachments) { if (IsURLViewableImage(a.ProxyURL) && a.Width.has_value() && a.Height.has_value()) { auto *widget = container->CreateImageComponent(a.ProxyURL, a.URL, *a.Width, *a.Height); + if (a.Description.has_value()) { + widget->set_tooltip_text(*a.Description); + } container->m_main.add(*widget); } else { auto *widget = container->CreateAttachmentComponent(a); @@ -361,7 +364,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const EmbedData &emb } return false; }); - static auto color = Abaddon::Get().GetSettings().LinkColor; + const auto color = title_label->get_style_context()->get_color(Gtk::STATE_FLAG_LINK); title_label->override_color(Gdk::RGBA(color)); title_label->set_markup("<b>" + Glib::Markup::escape_text(*embed.Title) + "</b>"); } @@ -620,7 +623,11 @@ Gtk::Widget *ChatMessageItemContainer::CreateReactionsComponent(const Message &d } else { // custom ev->set_tooltip_text(reaction.Emoji.Name); - auto img = Gtk::manage(new LazyImage(reaction.Emoji.GetURL(), 16, 16)); + auto *img = Gtk::make_managed<LazyImage>(reaction.Emoji.GetURL(), 16, 16); + if (reaction.Emoji.IsEmojiAnimated() && Abaddon::Get().GetSettings().ShowAnimations) { + img->SetURL(reaction.Emoji.GetURL("gif")); + img->SetAnimated(true); + } img->set_can_focus(false); box->add(*img); } @@ -657,7 +664,12 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data) if (role.has_value()) { const auto author = discord.GetUser(author_id); if (author.has_value()) { - return "<b><span color=\"#" + IntToCSSColor(role->Color) + "\">" + author->GetDisplayNameEscaped(guild_id) + "</span></b>"; + const auto is_mention = !data.Interaction.has_value() && data.DoesMention(author_id); + if (is_mention) { + return "<b><span color=\"#" + IntToCSSColor(role->Color) + "\">@" + author->GetDisplayNameEscaped(guild_id) + "</span></b>"; + } else { + return "<b><span color=\"#" + IntToCSSColor(role->Color) + "\">" + author->GetDisplayNameEscaped(guild_id) + "</span></b>"; + } } } } @@ -713,16 +725,12 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data) HandleChannelMentions(buf); text = Glib::Markup::escape_text(buf->get_text()); } - // getting markup out of a textbuffer seems like something that to me should be really simple - // but actually is horribly annoying. replies won't have mention colors because you can't do this - // also no emojis because idk how to make a textview act like a label - // which of course would not be an issue if i could figure out how to get fonts to work on this god-forsaken framework - // oh well - // but ill manually get colors for the user who is being replied to - if (referenced.GuildID.has_value()) + + if (referenced.GuildID.has_value()) { lbl->set_markup(get_author_markup(referenced.Author.ID, *referenced.GuildID) + ": " + text); - else + } else { lbl->set_markup(get_author_markup(referenced.Author.ID) + ": " + text); + } } } else { lbl->set_markup("<i>reply unavailable</i>"); @@ -848,7 +856,8 @@ void ChatMessageItemContainer::HandleLinks(Gtk::TextView &tv) { std::string link = match.fetch(0); auto tag = buf->create_tag(); m_link_tagmap[tag] = link; - tag->property_foreground_rgba() = Gdk::RGBA(Abaddon::Get().GetSettings().LinkColor); + const auto color = tv.get_style_context()->get_color(Gtk::STATE_FLAG_LINK); + tag->property_foreground_rgba() = color; tag->set_property("underline", 1); // stupid workaround for vcpkg bug (i think) const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart); diff --git a/src/components/chatwindow.cpp b/src/components/chatwindow.cpp index aeed4ed..9a2493d 100644 --- a/src/components/chatwindow.cpp +++ b/src/components/chatwindow.cpp @@ -5,7 +5,7 @@ #include "chatlist.hpp" #include "constants.hpp" #ifdef WITH_LIBHANDY - #include "channeltabswitcherhandy.hpp" +#include "channeltabswitcherhandy.hpp" #endif ChatWindow::ChatWindow() { @@ -109,7 +109,13 @@ ChatWindow::ChatWindow() { m_main->add(*m_meta); m_main->add(m_progress); - m_progress.show(); + m_progress.signal_start().connect([this]() { + m_progress.show(); + }); + + m_progress.signal_stop().connect([this]() { + m_progress.hide(); + }); m_main->show(); } diff --git a/src/components/memberlist.cpp b/src/components/memberlist.cpp index b082daa..7046b52 100644 --- a/src/components/memberlist.cpp +++ b/src/components/memberlist.cpp @@ -28,6 +28,7 @@ MemberList::MemberList() column->add_attribute(renderer->property_name(), m_columns.m_name); column->add_attribute(renderer->property_pixbuf(), m_columns.m_pixbuf); column->add_attribute(renderer->property_color(), m_columns.m_color); + column->add_attribute(renderer->property_status(), m_columns.m_status); m_view.append_column(*column); m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING); @@ -44,6 +45,8 @@ MemberList::MemberList() m_menu_role_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])); }); + + Abaddon::Get().GetDiscordClient().signal_presence_update().connect(sigc::mem_fun(*this, &MemberList::OnPresenceUpdate)); } Gtk::Widget *MemberList::GetRoot() { @@ -73,6 +76,7 @@ void MemberList::UpdateMemberList() { row[m_columns.m_color] = color_transparent; row[m_columns.m_av_requested] = false; row[m_columns.m_pixbuf] = Abaddon::Get().GetImageManager().GetPlaceholder(16); + row[m_columns.m_status] = Abaddon::Get().GetDiscordClient().GetUserStatus(user.ID); m_pending_avatars[user.ID] = row_iter; } } @@ -126,6 +130,7 @@ void MemberList::UpdateMemberList() { row[m_columns.m_id] = user.ID; row[m_columns.m_name] = user.GetDisplayNameEscaped(); row[m_columns.m_pixbuf] = Abaddon::Get().GetImageManager().GetPlaceholder(16); + row[m_columns.m_status] = Abaddon::Get().GetDiscordClient().GetUserStatus(user.ID); row[m_columns.m_av_requested] = false; if (const auto iter = user_to_color.find(user.ID); iter != user_to_color.end()) { row[m_columns.m_color] = IntToRGBA(iter->second); @@ -241,12 +246,24 @@ int MemberList::SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel return 0; } +void MemberList::OnPresenceUpdate(const UserData &user, PresenceStatus status) { + for (auto &role : m_model->children()) { + for (auto &member : role.children()) { + if ((*member)[m_columns.m_id] == user.ID) { + (*member)[m_columns.m_status] = status; + return; + } + } + } +} + MemberList::ModelColumns::ModelColumns() { add(m_type); add(m_id); add(m_name); add(m_pixbuf); - add(m_av_requested); add(m_color); + add(m_status); add(m_sort); + add(m_av_requested); } diff --git a/src/components/memberlist.hpp b/src/components/memberlist.hpp index cb6c191..1c4aaf4 100644 --- a/src/components/memberlist.hpp +++ b/src/components/memberlist.hpp @@ -26,6 +26,8 @@ private: int SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b); + void OnPresenceUpdate(const UserData &user, PresenceStatus status); + class ModelColumns : public Gtk::TreeModel::ColumnRecord { public: ModelColumns(); @@ -35,6 +37,7 @@ private: Gtk::TreeModelColumn<Glib::ustring> m_name; Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_pixbuf; Gtk::TreeModelColumn<Gdk::RGBA> m_color; + Gtk::TreeModelColumn<PresenceStatus> m_status; Gtk::TreeModelColumn<int> m_sort; Gtk::TreeModelColumn<bool> m_av_requested; diff --git a/src/components/progressbar.cpp b/src/components/progressbar.cpp index 75f86bb..65abfae 100644 --- a/src/components/progressbar.cpp +++ b/src/components/progressbar.cpp @@ -5,12 +5,19 @@ MessageUploadProgressBar::MessageUploadProgressBar() { auto &discord = Abaddon::Get().GetDiscordClient(); discord.signal_message_progress().connect([this](const std::string &nonce, float percent) { if (nonce == m_last_nonce) { + if (!m_active) { + m_active = true; + m_signal_start.emit(); + } set_fraction(percent); } }); discord.signal_message_send_fail().connect([this](const std::string &nonce, float) { - if (nonce == m_last_nonce) + if (nonce == m_last_nonce) { set_fraction(0.0); + m_active = false; + m_signal_stop.emit(); + } }); discord.signal_message_create().connect([this](const Message &msg) { if (msg.IsPending) { @@ -18,6 +25,16 @@ MessageUploadProgressBar::MessageUploadProgressBar() { } else if (msg.Nonce.has_value() && (*msg.Nonce == m_last_nonce)) { m_last_nonce = ""; set_fraction(0.0); + m_active = false; + m_signal_stop.emit(); } }); } + +MessageUploadProgressBar::type_signal_start MessageUploadProgressBar::signal_start() { + return m_signal_start; +} + +MessageUploadProgressBar::type_signal_stop MessageUploadProgressBar::signal_stop() { + return m_signal_stop; +} diff --git a/src/components/progressbar.hpp b/src/components/progressbar.hpp index 483ee47..8efb87a 100644 --- a/src/components/progressbar.hpp +++ b/src/components/progressbar.hpp @@ -6,5 +6,16 @@ public: MessageUploadProgressBar(); private: + bool m_active = false; std::string m_last_nonce; + + using type_signal_start = sigc::signal<void()>; + using type_signal_stop = sigc::signal<void()>; + + type_signal_start m_signal_start; + type_signal_stop m_signal_stop; + +public: + type_signal_start signal_start(); + type_signal_stop signal_stop(); }; diff --git a/src/components/statusindicator.cpp b/src/components/statusindicator.cpp deleted file mode 100644 index 47e8b44..0000000 --- a/src/components/statusindicator.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "statusindicator.hpp" - -static const constexpr int Diameter = 8; - -StatusIndicator::StatusIndicator(Snowflake user_id) - : Glib::ObjectBase("statusindicator") - , Gtk::Widget() - , m_id(user_id) - , m_status(static_cast<PresenceStatus>(-1)) { - set_has_window(true); - set_name("status-indicator"); - - get_style_context()->add_class("status-indicator"); - - Abaddon::Get().GetDiscordClient().signal_guild_member_list_update().connect(sigc::hide(sigc::mem_fun(*this, &StatusIndicator::CheckStatus))); - auto cb = [this](const UserData &user, PresenceStatus status) { - if (user.ID == m_id) CheckStatus(); - }; - Abaddon::Get().GetDiscordClient().signal_presence_update().connect(sigc::track_obj(cb, *this)); - - CheckStatus(); -} - -void StatusIndicator::CheckStatus() { - const auto status = Abaddon::Get().GetDiscordClient().GetUserStatus(m_id); - const auto last_status = m_status; - get_style_context()->remove_class("online"); - get_style_context()->remove_class("dnd"); - get_style_context()->remove_class("idle"); - get_style_context()->remove_class("offline"); - get_style_context()->add_class(GetPresenceString(status)); - m_status = status; - - if (last_status != m_status) - queue_draw(); -} - -Gtk::SizeRequestMode StatusIndicator::get_request_mode_vfunc() const { - return Gtk::Widget::get_request_mode_vfunc(); -} - -void StatusIndicator::get_preferred_width_vfunc(int &minimum_width, int &natural_width) const { - minimum_width = 0; - natural_width = Diameter; -} - -void StatusIndicator::get_preferred_height_for_width_vfunc(int width, int &minimum_height, int &natural_height) const { - minimum_height = 0; - natural_height = Diameter; -} - -void StatusIndicator::get_preferred_height_vfunc(int &minimum_height, int &natural_height) const { - minimum_height = 0; - natural_height = Diameter; -} - -void StatusIndicator::get_preferred_width_for_height_vfunc(int height, int &minimum_width, int &natural_width) const { - minimum_width = 0; - natural_width = Diameter; -} - -void StatusIndicator::on_size_allocate(Gtk::Allocation &allocation) { - set_allocation(allocation); - - if (m_window) - m_window->move_resize(allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height()); -} - -void StatusIndicator::on_map() { - Gtk::Widget::on_map(); -} - -void StatusIndicator::on_unmap() { - Gtk::Widget::on_unmap(); -} - -void StatusIndicator::on_realize() { - set_realized(true); - - if (!m_window) { - GdkWindowAttr attributes; - std::memset(&attributes, 0, sizeof(attributes)); - - auto allocation = get_allocation(); - - attributes.x = allocation.get_x(); - attributes.y = allocation.get_y(); - attributes.width = allocation.get_width(); - attributes.height = allocation.get_height(); - - attributes.event_mask = get_events() | Gdk::EXPOSURE_MASK; - attributes.window_type = GDK_WINDOW_CHILD; - attributes.wclass = GDK_INPUT_OUTPUT; - - m_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y); - set_window(m_window); - - m_window->set_user_data(gobj()); - } -} - -void StatusIndicator::on_unrealize() { - m_window.reset(); - - Gtk::Widget::on_unrealize(); -} - -bool StatusIndicator::on_draw(const Cairo::RefPtr<Cairo::Context> &cr) { - const auto allocation = get_allocation(); - const auto width = allocation.get_width(); - const auto height = allocation.get_height(); - - const auto color = get_style_context()->get_color(Gtk::STATE_FLAG_NORMAL); - - cr->set_source_rgb(color.get_red(), color.get_green(), color.get_blue()); - cr->arc(width / 2.0, height / 2.0, width / 3.0, 0.0, 2 * (4 * std::atan(1))); - cr->close_path(); - cr->fill_preserve(); - cr->stroke(); - - return true; -} diff --git a/src/components/statusindicator.hpp b/src/components/statusindicator.hpp deleted file mode 100644 index edd64ea..0000000 --- a/src/components/statusindicator.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "discord/snowflake.hpp" -#include "discord/activity.hpp" - -class StatusIndicator : public Gtk::Widget { -public: - StatusIndicator(Snowflake user_id); - ~StatusIndicator() override = default; - -protected: - Gtk::SizeRequestMode get_request_mode_vfunc() const override; - void get_preferred_width_vfunc(int &minimum_width, int &natural_width) const override; - void get_preferred_width_for_height_vfunc(int height, int &minimum_width, int &natural_width) const override; - void get_preferred_height_vfunc(int &minimum_height, int &natural_height) const override; - void get_preferred_height_for_width_vfunc(int width, int &minimum_height, int &natural_height) const override; - void on_size_allocate(Gtk::Allocation &allocation) override; - void on_map() override; - void on_unmap() override; - void on_realize() override; - void on_unrealize() override; - bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) override; - - Glib::RefPtr<Gdk::Window> m_window; - - void CheckStatus(); - - Snowflake m_id; - PresenceStatus m_status; -}; |