mirror of
https://github.com/celisej567/abaddon.git
synced 2026-09-13 20:18:50 +03:00
Merge branch 'unread' into msys
This commit is contained in:
@@ -1,21 +1,22 @@
|
||||
#include "abaddon.hpp"
|
||||
#include "channels.hpp"
|
||||
#include "imgmanager.hpp"
|
||||
#include "statusindicator.hpp"
|
||||
#include "util.hpp"
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include "abaddon.hpp"
|
||||
#include "imgmanager.hpp"
|
||||
#include "util.hpp"
|
||||
#include "statusindicator.hpp"
|
||||
|
||||
ChannelList::ChannelList()
|
||||
: Glib::ObjectBase(typeid(ChannelList))
|
||||
, Gtk::ScrolledWindow()
|
||||
, m_model(Gtk::TreeStore::create(m_columns))
|
||||
, m_menu_guild_copy_id("_Copy ID", true)
|
||||
, m_menu_guild_settings("View _Settings", true)
|
||||
, m_menu_guild_leave("_Leave", true)
|
||||
, m_menu_guild_mark_as_read("Mark as _Read", true)
|
||||
, m_menu_category_copy_id("_Copy ID", true)
|
||||
, m_menu_channel_copy_id("_Copy ID", true)
|
||||
, m_menu_channel_mark_as_read("Mark as _Read", true)
|
||||
, m_menu_dm_copy_id("_Copy ID", true)
|
||||
, m_menu_dm_close("") // changes depending on if group or not
|
||||
, m_menu_thread_copy_id("_Copy ID", true)
|
||||
@@ -24,6 +25,7 @@ ChannelList::ChannelList()
|
||||
, m_menu_thread_unarchive("_Unarchive", true) {
|
||||
get_style_context()->add_class("channel-list");
|
||||
|
||||
// todo: move to method
|
||||
const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) {
|
||||
auto row = *m_model->get_iter(path);
|
||||
const auto type = row[m_columns.m_type];
|
||||
@@ -40,7 +42,9 @@ ChannelList::ChannelList()
|
||||
}
|
||||
|
||||
if (type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread) {
|
||||
m_signal_action_channel_item_select.emit(static_cast<Snowflake>(row[m_columns.m_id]));
|
||||
const auto id = static_cast<Snowflake>(row[m_columns.m_id]);
|
||||
m_signal_action_channel_item_select.emit(id);
|
||||
Abaddon::Get().GetDiscordClient().MarkChannelAsRead(id, [](...) {});
|
||||
}
|
||||
};
|
||||
m_view.signal_row_activated().connect(cb, false);
|
||||
@@ -77,6 +81,7 @@ ChannelList::ChannelList()
|
||||
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_id(), m_columns.m_id);
|
||||
column->add_attribute(renderer->property_expanded(), m_columns.m_expanded);
|
||||
column->add_attribute(renderer->property_nsfw(), m_columns.m_nsfw);
|
||||
m_view.append_column(*column);
|
||||
@@ -90,20 +95,55 @@ ChannelList::ChannelList()
|
||||
m_menu_guild_leave.signal_activate().connect([this] {
|
||||
m_signal_action_guild_leave.emit(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
|
||||
});
|
||||
m_menu_guild.append(m_menu_guild_copy_id);
|
||||
m_menu_guild_mark_as_read.signal_activate().connect([this] {
|
||||
Abaddon::Get().GetDiscordClient().MarkGuildAsRead(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), [](...) {});
|
||||
});
|
||||
m_menu_guild_toggle_mute.signal_activate().connect([this] {
|
||||
const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]);
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
if (discord.IsGuildMuted(id))
|
||||
discord.UnmuteGuild(id, NOOP_CALLBACK);
|
||||
else
|
||||
discord.MuteGuild(id, NOOP_CALLBACK);
|
||||
});
|
||||
m_menu_guild.append(m_menu_guild_mark_as_read);
|
||||
m_menu_guild.append(m_menu_guild_settings);
|
||||
m_menu_guild.append(m_menu_guild_leave);
|
||||
m_menu_guild.append(m_menu_guild_toggle_mute);
|
||||
m_menu_guild.append(m_menu_guild_copy_id);
|
||||
m_menu_guild.show_all();
|
||||
|
||||
m_menu_category_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]));
|
||||
});
|
||||
m_menu_category_toggle_mute.signal_activate().connect([this] {
|
||||
const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]);
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
if (discord.IsChannelMuted(id))
|
||||
discord.UnmuteChannel(id, NOOP_CALLBACK);
|
||||
else
|
||||
discord.MuteChannel(id, NOOP_CALLBACK);
|
||||
});
|
||||
m_menu_category.append(m_menu_category_toggle_mute);
|
||||
m_menu_category.append(m_menu_category_copy_id);
|
||||
m_menu_category.show_all();
|
||||
|
||||
m_menu_channel_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]));
|
||||
});
|
||||
m_menu_channel_mark_as_read.signal_activate().connect([this] {
|
||||
Abaddon::Get().GetDiscordClient().MarkChannelAsRead(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), [](...) {});
|
||||
});
|
||||
m_menu_channel_toggle_mute.signal_activate().connect([this] {
|
||||
const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]);
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
if (discord.IsChannelMuted(id))
|
||||
discord.UnmuteChannel(id, NOOP_CALLBACK);
|
||||
else
|
||||
discord.MuteChannel(id, NOOP_CALLBACK);
|
||||
});
|
||||
m_menu_channel.append(m_menu_channel_mark_as_read);
|
||||
m_menu_channel.append(m_menu_channel_toggle_mute);
|
||||
m_menu_channel.append(m_menu_channel_copy_id);
|
||||
m_menu_channel.show_all();
|
||||
|
||||
@@ -121,8 +161,8 @@ ChannelList::ChannelList()
|
||||
else if (Abaddon::Get().ShowConfirm("Are you sure you want to leave this group DM?"))
|
||||
Abaddon::Get().GetDiscordClient().CloseDM(id);
|
||||
});
|
||||
m_menu_dm.append(m_menu_dm_copy_id);
|
||||
m_menu_dm.append(m_menu_dm_close);
|
||||
m_menu_dm.append(m_menu_dm_copy_id);
|
||||
m_menu_dm.show_all();
|
||||
|
||||
m_menu_thread_copy_id.signal_activate().connect([this] {
|
||||
@@ -144,6 +184,9 @@ ChannelList::ChannelList()
|
||||
m_menu_thread.append(m_menu_thread_unarchive);
|
||||
m_menu_thread.show_all();
|
||||
|
||||
m_menu_guild.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnGuildSubmenuPopup));
|
||||
m_menu_category.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnCategorySubmenuPopup));
|
||||
m_menu_channel.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnChannelSubmenuPopup));
|
||||
m_menu_thread.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnThreadSubmenuPopup));
|
||||
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
@@ -159,6 +202,19 @@ ChannelList::ChannelList()
|
||||
discord.signal_added_to_thread().connect(sigc::mem_fun(*this, &ChannelList::OnThreadJoined));
|
||||
discord.signal_removed_from_thread().connect(sigc::mem_fun(*this, &ChannelList::OnThreadRemoved));
|
||||
discord.signal_guild_update().connect(sigc::mem_fun(*this, &ChannelList::UpdateGuild));
|
||||
discord.signal_message_ack().connect(sigc::mem_fun(*this, &ChannelList::OnMessageAck));
|
||||
discord.signal_channel_muted().connect(sigc::mem_fun(*this, &ChannelList::OnChannelMute));
|
||||
discord.signal_channel_unmuted().connect(sigc::mem_fun(*this, &ChannelList::OnChannelUnmute));
|
||||
discord.signal_guild_muted().connect(sigc::mem_fun(*this, &ChannelList::OnGuildMute));
|
||||
discord.signal_guild_unmuted().connect(sigc::mem_fun(*this, &ChannelList::OnGuildUnmute));
|
||||
}
|
||||
|
||||
void ChannelList::UsePanedHack(Gtk::Paned &paned) {
|
||||
paned.property_position().signal_changed().connect(sigc::mem_fun(*this, &ChannelList::OnPanedPositionChanged));
|
||||
}
|
||||
|
||||
void ChannelList::OnPanedPositionChanged() {
|
||||
m_view.queue_draw();
|
||||
}
|
||||
|
||||
void ChannelList::UpdateListing() {
|
||||
@@ -231,7 +287,6 @@ void ChannelList::UpdateChannel(Snowflake id) {
|
||||
}
|
||||
|
||||
void ChannelList::UpdateCreateChannel(const ChannelData &channel) {
|
||||
;
|
||||
if (channel.Type == ChannelType::GUILD_CATEGORY) return (void)UpdateCreateChannelCategory(channel);
|
||||
if (channel.Type == ChannelType::DM || channel.Type == ChannelType::GROUP_DM) return UpdateCreateDMChannel(channel);
|
||||
if (channel.Type != ChannelType::GUILD_TEXT && channel.Type != ChannelType::GUILD_NEWS) return;
|
||||
@@ -347,9 +402,35 @@ void ChannelList::DeleteThreadRow(Snowflake id) {
|
||||
m_model->erase(iter);
|
||||
}
|
||||
|
||||
void ChannelList::OnChannelMute(Snowflake id) {
|
||||
if (auto iter = GetIteratorForChannelFromID(id))
|
||||
m_model->row_changed(m_model->get_path(iter), iter);
|
||||
}
|
||||
|
||||
void ChannelList::OnChannelUnmute(Snowflake id) {
|
||||
if (auto iter = GetIteratorForChannelFromID(id))
|
||||
m_model->row_changed(m_model->get_path(iter), iter);
|
||||
}
|
||||
|
||||
void ChannelList::OnGuildMute(Snowflake id) {
|
||||
if (auto iter = GetIteratorForGuildFromID(id))
|
||||
m_model->row_changed(m_model->get_path(iter), iter);
|
||||
}
|
||||
|
||||
void ChannelList::OnGuildUnmute(Snowflake id) {
|
||||
if (auto iter = GetIteratorForGuildFromID(id))
|
||||
m_model->row_changed(m_model->get_path(iter), iter);
|
||||
}
|
||||
|
||||
// create a temporary channel row for non-joined threads
|
||||
// and delete them when the active channel switches off of them if still not joined
|
||||
void ChannelList::SetActiveChannel(Snowflake id) {
|
||||
// mark channel as read when switching off
|
||||
if (m_active_channel.IsValid())
|
||||
Abaddon::Get().GetDiscordClient().MarkChannelAsRead(m_active_channel, [](...) {});
|
||||
|
||||
m_active_channel = id;
|
||||
|
||||
if (m_temporary_thread_row) {
|
||||
const auto thread_id = static_cast<Snowflake>((*m_temporary_thread_row)[m_columns.m_id]);
|
||||
const auto thread = Abaddon::Get().GetDiscordClient().GetChannel(thread_id);
|
||||
@@ -378,11 +459,11 @@ void ChannelList::UseExpansionState(const ExpansionStateRoot &root) {
|
||||
auto recurse = [this](auto &self, const ExpansionStateRoot &root) -> void {
|
||||
// and these are only channels
|
||||
for (const auto &[id, state] : root.Children) {
|
||||
if (const auto iter = GetIteratorForChannelFromID(id)) {
|
||||
if (const auto iter = m_tmp_channel_map.find(id); iter != m_tmp_channel_map.end()) {
|
||||
if (state.IsExpanded)
|
||||
m_view.expand_row(m_model->get_path(iter), false);
|
||||
m_view.expand_row(m_model->get_path(iter->second), false);
|
||||
else
|
||||
m_view.collapse_row(m_model->get_path(iter));
|
||||
m_view.collapse_row(m_model->get_path(iter->second));
|
||||
}
|
||||
|
||||
self(self, state.Children);
|
||||
@@ -400,6 +481,8 @@ void ChannelList::UseExpansionState(const ExpansionStateRoot &root) {
|
||||
|
||||
recurse(recurse, state.Children);
|
||||
}
|
||||
|
||||
m_tmp_channel_map.clear();
|
||||
}
|
||||
|
||||
ExpansionStateRoot ChannelList::GetExpansionState() const {
|
||||
@@ -480,7 +563,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
|
||||
if (it == threads.end()) return;
|
||||
|
||||
for (const auto &thread : it->second)
|
||||
CreateThreadRow(row.children(), thread);
|
||||
m_tmp_channel_map[thread.ID] = CreateThreadRow(row.children(), thread);
|
||||
};
|
||||
|
||||
for (const auto &channel : orphan_channels) {
|
||||
@@ -491,6 +574,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
|
||||
channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset;
|
||||
channel_row[m_columns.m_nsfw] = channel.NSFW();
|
||||
add_threads(channel, channel_row);
|
||||
m_tmp_channel_map[channel.ID] = channel_row;
|
||||
}
|
||||
|
||||
for (const auto &[category_id, channels] : categories) {
|
||||
@@ -502,6 +586,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
|
||||
cat_row[m_columns.m_name] = Glib::Markup::escape_text(*category->Name);
|
||||
cat_row[m_columns.m_sort] = *category->Position;
|
||||
cat_row[m_columns.m_expanded] = true;
|
||||
m_tmp_channel_map[category_id] = cat_row;
|
||||
// m_view.expand_row wont work because it might not have channels
|
||||
|
||||
for (const auto &channel : channels) {
|
||||
@@ -512,6 +597,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
|
||||
channel_row[m_columns.m_sort] = *channel.Position;
|
||||
channel_row[m_columns.m_nsfw] = channel.NSFW();
|
||||
add_threads(channel, channel_row);
|
||||
m_tmp_channel_map[channel.ID] = channel_row;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,7 +744,7 @@ void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) {
|
||||
|
||||
std::optional<UserData> top_recipient;
|
||||
const auto recipients = dm.GetDMRecipients();
|
||||
if (recipients.size() > 0)
|
||||
if (!recipients.empty())
|
||||
top_recipient = recipients[0];
|
||||
|
||||
auto iter = m_model->append(header_row->children());
|
||||
@@ -682,13 +768,30 @@ void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) {
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelList::OnMessageAck(const MessageAckData &data) {
|
||||
// trick renderer into redrawing
|
||||
m_model->row_changed(Gtk::TreeModel::Path("0"), m_model->get_iter("0")); // 0 is always path for dm header
|
||||
auto iter = GetIteratorForChannelFromID(data.ChannelID);
|
||||
if (iter) m_model->row_changed(m_model->get_path(iter), iter);
|
||||
auto channel = Abaddon::Get().GetDiscordClient().GetChannel(data.ChannelID);
|
||||
if (channel.has_value() && channel->GuildID.has_value()) {
|
||||
iter = GetIteratorForGuildFromID(*channel->GuildID);
|
||||
if (iter) m_model->row_changed(m_model->get_path(iter), iter);
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelList::OnMessageCreate(const Message &msg) {
|
||||
auto iter = GetIteratorForChannelFromID(msg.ChannelID);
|
||||
if (iter) m_model->row_changed(m_model->get_path(iter), iter); // redraw
|
||||
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(msg.ChannelID);
|
||||
if (!channel.has_value()) return;
|
||||
if (channel->Type != ChannelType::DM && channel->Type != ChannelType::GROUP_DM) return;
|
||||
auto iter = GetIteratorForChannelFromID(msg.ChannelID);
|
||||
if (iter)
|
||||
(*iter)[m_columns.m_sort] = -msg.ID;
|
||||
if (channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM) {
|
||||
if (iter)
|
||||
(*iter)[m_columns.m_sort] = -msg.ID;
|
||||
}
|
||||
if (channel->GuildID.has_value())
|
||||
if ((iter = GetIteratorForGuildFromID(*channel->GuildID)))
|
||||
m_model->row_changed(m_model->get_path(iter), iter);
|
||||
}
|
||||
|
||||
bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) {
|
||||
@@ -754,6 +857,36 @@ void ChannelList::MoveRow(const Gtk::TreeModel::iterator &iter, const Gtk::TreeM
|
||||
m_model->erase(iter);
|
||||
}
|
||||
|
||||
void ChannelList::OnGuildSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
|
||||
const auto iter = m_model->get_iter(m_path_for_menu);
|
||||
if (!iter) return;
|
||||
const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
|
||||
if (Abaddon::Get().GetDiscordClient().IsGuildMuted(id))
|
||||
m_menu_guild_toggle_mute.set_label("Unmute");
|
||||
else
|
||||
m_menu_guild_toggle_mute.set_label("Mute");
|
||||
}
|
||||
|
||||
void ChannelList::OnCategorySubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
|
||||
const auto iter = m_model->get_iter(m_path_for_menu);
|
||||
if (!iter) return;
|
||||
const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
|
||||
if (Abaddon::Get().GetDiscordClient().IsChannelMuted(id))
|
||||
m_menu_category_toggle_mute.set_label("Unmute");
|
||||
else
|
||||
m_menu_category_toggle_mute.set_label("Mute");
|
||||
}
|
||||
|
||||
void ChannelList::OnChannelSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
|
||||
const auto iter = m_model->get_iter(m_path_for_menu);
|
||||
if (!iter) return;
|
||||
const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
|
||||
if (Abaddon::Get().GetDiscordClient().IsChannelMuted(id))
|
||||
m_menu_channel_toggle_mute.set_label("Unmute");
|
||||
else
|
||||
m_menu_channel_toggle_mute.set_label("Mute");
|
||||
}
|
||||
|
||||
void ChannelList::OnThreadSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
|
||||
m_menu_thread_archive.set_visible(false);
|
||||
m_menu_thread_unarchive.set_visible(false);
|
||||
|
||||
@@ -25,7 +25,11 @@ public:
|
||||
void UseExpansionState(const ExpansionStateRoot &state);
|
||||
ExpansionStateRoot GetExpansionState() const;
|
||||
|
||||
void UsePanedHack(Gtk::Paned &paned);
|
||||
|
||||
protected:
|
||||
void OnPanedPositionChanged();
|
||||
|
||||
void UpdateNewGuild(const GuildData &guild);
|
||||
void UpdateRemoveGuild(Snowflake id);
|
||||
void UpdateRemoveChannel(Snowflake id);
|
||||
@@ -33,6 +37,10 @@ protected:
|
||||
void UpdateCreateChannel(const ChannelData &channel);
|
||||
void UpdateGuild(Snowflake id);
|
||||
void DeleteThreadRow(Snowflake id);
|
||||
void OnChannelMute(Snowflake id);
|
||||
void OnChannelUnmute(Snowflake id);
|
||||
void OnGuildMute(Snowflake id);
|
||||
void OnGuildUnmute(Snowflake id);
|
||||
|
||||
void OnThreadJoined(Snowflake id);
|
||||
void OnThreadRemoved(Snowflake id);
|
||||
@@ -89,6 +97,8 @@ protected:
|
||||
void AddPrivateChannels();
|
||||
void UpdateCreateDMChannel(const ChannelData &channel);
|
||||
|
||||
void OnMessageAck(const MessageAckData &data);
|
||||
|
||||
void OnMessageCreate(const Message &msg);
|
||||
Gtk::TreeModel::Path m_path_for_menu;
|
||||
|
||||
@@ -99,12 +109,17 @@ protected:
|
||||
Gtk::MenuItem m_menu_guild_copy_id;
|
||||
Gtk::MenuItem m_menu_guild_settings;
|
||||
Gtk::MenuItem m_menu_guild_leave;
|
||||
Gtk::MenuItem m_menu_guild_mark_as_read;
|
||||
Gtk::MenuItem m_menu_guild_toggle_mute;
|
||||
|
||||
Gtk::Menu m_menu_category;
|
||||
Gtk::MenuItem m_menu_category_copy_id;
|
||||
Gtk::MenuItem m_menu_category_toggle_mute;
|
||||
|
||||
Gtk::Menu m_menu_channel;
|
||||
Gtk::MenuItem m_menu_channel_copy_id;
|
||||
Gtk::MenuItem m_menu_channel_mark_as_read;
|
||||
Gtk::MenuItem m_menu_channel_toggle_mute;
|
||||
|
||||
Gtk::Menu m_menu_dm;
|
||||
Gtk::MenuItem m_menu_dm_copy_id;
|
||||
@@ -116,10 +131,19 @@ protected:
|
||||
Gtk::MenuItem m_menu_thread_archive;
|
||||
Gtk::MenuItem m_menu_thread_unarchive;
|
||||
|
||||
void OnGuildSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
|
||||
void OnCategorySubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
|
||||
void OnChannelSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
|
||||
void OnThreadSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
|
||||
|
||||
bool m_updating_listing = false;
|
||||
|
||||
Snowflake m_active_channel;
|
||||
|
||||
// (GetIteratorForChannelFromID is rather slow)
|
||||
// only temporary since i dont want to worry about maintaining this map
|
||||
std::unordered_map<Snowflake, Gtk::TreeModel::iterator> m_tmp_channel_map;
|
||||
|
||||
public:
|
||||
typedef sigc::signal<void, Snowflake> type_signal_action_channel_item_select;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_action_guild_leave;
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
#include "channelscellrenderer.hpp"
|
||||
#include "abaddon.hpp"
|
||||
#include "channelscellrenderer.hpp"
|
||||
#include <gtkmm.h>
|
||||
|
||||
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;
|
||||
|
||||
CellRendererChannels::CellRendererChannels()
|
||||
: Glib::ObjectBase(typeid(CellRendererChannels))
|
||||
, Gtk::CellRenderer()
|
||||
, m_property_type(*this, "render-type")
|
||||
, m_property_id(*this, "id")
|
||||
, m_property_name(*this, "name")
|
||||
, m_property_pixbuf(*this, "pixbuf")
|
||||
, m_property_pixbuf_animation(*this, "pixbuf-animation")
|
||||
@@ -26,6 +34,10 @@ Glib::PropertyProxy<RenderType> CellRendererChannels::property_type() {
|
||||
return m_property_type.get_proxy();
|
||||
}
|
||||
|
||||
Glib::PropertyProxy<uint64_t> CellRendererChannels::property_id() {
|
||||
return m_property_id.get_proxy();
|
||||
}
|
||||
|
||||
Glib::PropertyProxy<Glib::ustring> CellRendererChannels::property_name() {
|
||||
return m_property_name.get_proxy();
|
||||
}
|
||||
@@ -192,7 +204,7 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context
|
||||
|
||||
const double icon_w = pixbuf_w;
|
||||
const double icon_h = pixbuf_h;
|
||||
const double icon_x = background_area.get_x();
|
||||
const double icon_x = background_area.get_x() + 3;
|
||||
const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - icon_h / 2.0;
|
||||
|
||||
const double text_x = icon_x + icon_w + 5.0;
|
||||
@@ -233,6 +245,32 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context
|
||||
cr->rectangle(icon_x, icon_y, icon_w, icon_h);
|
||||
cr->fill();
|
||||
}
|
||||
|
||||
// unread
|
||||
|
||||
const auto id = m_property_id.get_value();
|
||||
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
int total_mentions;
|
||||
const auto has_unread = discord.GetUnreadStateForGuild(id, total_mentions);
|
||||
|
||||
if (has_unread && !discord.IsGuildMuted(id)) {
|
||||
cr->set_source_rgb(1.0, 1.0, 1.0);
|
||||
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 - 24 / 2, 3, 24);
|
||||
cr->fill();
|
||||
}
|
||||
|
||||
if (total_mentions < 1) return;
|
||||
auto *paned = static_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
|
||||
if (paned != nullptr) {
|
||||
const auto edge = std::min(paned->get_position(), background_area.get_width());
|
||||
|
||||
unread_render_mentions(cr, widget, total_mentions, edge, background_area);
|
||||
}
|
||||
}
|
||||
|
||||
// category
|
||||
@@ -289,7 +327,11 @@ void CellRendererChannels::render_vfunc_category(const Cairo::RefPtr<Cairo::Cont
|
||||
|
||||
Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
|
||||
|
||||
static Gdk::RGBA muted_color("#7f7f7f");
|
||||
if (Abaddon::Get().GetDiscordClient().IsChannelMuted(m_property_id.get_value()))
|
||||
m_renderer_text.property_foreground_rgba() = muted_color;
|
||||
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
||||
m_renderer_text.property_foreground_set() = false;
|
||||
}
|
||||
|
||||
// text channel
|
||||
@@ -321,13 +363,51 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr<Cairo::Conte
|
||||
|
||||
Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
|
||||
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto id = m_property_id.get_value();
|
||||
const bool is_muted = discord.IsChannelMuted(id);
|
||||
|
||||
// move to style in msys?
|
||||
static Gdk::RGBA sfw_unmuted("#FFFFFF");
|
||||
|
||||
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;
|
||||
}
|
||||
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
||||
// setting property_foreground_rgba() sets this to true which makes non-nsfw cells use the property too which is bad
|
||||
// so unset it
|
||||
// unset foreground to default so properties dont bleed
|
||||
m_renderer_text.property_foreground_set() = false;
|
||||
|
||||
// unread
|
||||
|
||||
const auto unread_state = discord.GetUnreadStateForChannel(id);
|
||||
if (unread_state < 0) return;
|
||||
|
||||
if (!is_muted) {
|
||||
cr->set_source_rgb(1.0, 1.0, 1.0);
|
||||
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();
|
||||
}
|
||||
|
||||
if (unread_state < 1) return;
|
||||
auto *paned = static_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
|
||||
if (paned != nullptr) {
|
||||
const auto edge = std::min(paned->get_position(), cell_area.get_width());
|
||||
|
||||
unread_render_mentions(cr, widget, unread_state, edge, cell_area);
|
||||
}
|
||||
}
|
||||
|
||||
// thread
|
||||
@@ -385,6 +465,13 @@ void CellRendererChannels::render_vfunc_dmheader(const Cairo::RefPtr<Cairo::Cont
|
||||
cell_area.get_x() + 9, cell_area.get_y(), // maybe theres a better way to align this ?
|
||||
cell_area.get_width(), cell_area.get_height());
|
||||
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
||||
|
||||
auto *paned = static_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
|
||||
if (paned != nullptr) {
|
||||
const auto edge = std::min(paned->get_position(), background_area.get_width());
|
||||
if (const auto unread = Abaddon::Get().GetDiscordClient().GetUnreadDMsCount(); unread > 0)
|
||||
unread_render_mentions(cr, widget, unread, edge, background_area);
|
||||
}
|
||||
}
|
||||
|
||||
// dm (basically the same thing as guild)
|
||||
@@ -436,19 +523,75 @@ void CellRendererChannels::render_vfunc_dm(const Cairo::RefPtr<Cairo::Context> &
|
||||
|
||||
const double icon_w = pixbuf->get_width();
|
||||
const double icon_h = pixbuf->get_height();
|
||||
const double icon_x = background_area.get_x() + 2;
|
||||
const double icon_x = background_area.get_x() + 3;
|
||||
const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - icon_h / 2.0;
|
||||
|
||||
const double text_x = icon_x + icon_w + 5.0;
|
||||
const double text_x = icon_x + icon_w + 6.0;
|
||||
const double text_y = background_area.get_y() + background_area.get_height() / 2.0 - text_natural.height / 2.0;
|
||||
const double text_w = text_natural.width;
|
||||
const double text_h = text_natural.height;
|
||||
|
||||
Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
|
||||
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto id = m_property_id.get_value();
|
||||
const bool is_muted = discord.IsChannelMuted(id);
|
||||
|
||||
if (is_muted)
|
||||
m_renderer_text.property_foreground() = "#7f7f7f";
|
||||
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);
|
||||
cr->fill();
|
||||
|
||||
// unread
|
||||
|
||||
const auto unread_state = discord.GetUnreadStateForChannel(id);
|
||||
if (unread_state < 0) return;
|
||||
|
||||
if (!is_muted) {
|
||||
cr->set_source_rgb(1.0, 1.0, 1.0);
|
||||
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::cairo_path_rounded_rect(const Cairo::RefPtr<Cairo::Context> &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<Cairo::Context> &cr, Gtk::Widget &widget, int mentions, int edge, const Gdk::Rectangle &cell_area) {
|
||||
Pango::FontDescription font;
|
||||
font.set_family("sans 14");
|
||||
//font.set_weight(Pango::WEIGHT_BOLD);
|
||||
|
||||
auto layout = widget.create_pango_layout(std::to_string(mentions));
|
||||
layout->set_font_description(font);
|
||||
layout->set_alignment(Pango::ALIGN_RIGHT);
|
||||
|
||||
int width, height;
|
||||
layout->get_pixel_size(width, height);
|
||||
{
|
||||
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;
|
||||
cairo_path_rounded_rect(cr, x - 4, y + 2, width + 8, height, 5);
|
||||
cr->set_source_rgb(184.0 / 255.0, 37.0 / 255.0, 37.0 / 255.0);
|
||||
cr->fill();
|
||||
cr->set_source_rgb(1.0, 1.0, 1.0);
|
||||
cr->move_to(x, y);
|
||||
layout->show_in_cairo_context(cr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <gdkmm/pixbufanimation.h>
|
||||
#include <glibmm/property.h>
|
||||
#include <map>
|
||||
#include "discord/snowflake.hpp"
|
||||
|
||||
enum class RenderType : uint8_t {
|
||||
Guild,
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
virtual ~CellRendererChannels();
|
||||
|
||||
Glib::PropertyProxy<RenderType> property_type();
|
||||
Glib::PropertyProxy<uint64_t> property_id();
|
||||
Glib::PropertyProxy<Glib::ustring> property_name();
|
||||
Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf>> property_icon();
|
||||
Glib::PropertyProxy<Glib::RefPtr<Gdk::PixbufAnimation>> property_icon_animation();
|
||||
@@ -103,11 +105,15 @@ protected:
|
||||
const Gdk::Rectangle &cell_area,
|
||||
Gtk::CellRendererState flags);
|
||||
|
||||
static void cairo_path_rounded_rect(const Cairo::RefPtr<Cairo::Context> &cr, double x, double y, double w, double h, double r);
|
||||
void unread_render_mentions(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, int mentions, int edge, const Gdk::Rectangle &cell_area);
|
||||
|
||||
private:
|
||||
Gtk::CellRendererText m_renderer_text;
|
||||
|
||||
Glib::Property<RenderType> m_property_type; // all
|
||||
Glib::Property<Glib::ustring> m_property_name; // all
|
||||
Glib::Property<RenderType> m_property_type; // all
|
||||
Glib::Property<Glib::ustring> m_property_name; // all
|
||||
Glib::Property<uint64_t> m_property_id;
|
||||
Glib::Property<Glib::RefPtr<Gdk::Pixbuf>> m_property_pixbuf; // guild, dm
|
||||
Glib::Property<Glib::RefPtr<Gdk::PixbufAnimation>> m_property_pixbuf_animation; // guild
|
||||
Glib::Property<bool> m_property_expanded; // category
|
||||
|
||||
Reference in New Issue
Block a user