forked from OpenGamers/abaddon
Compare commits
5 Commits
folders
...
member-lis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
190118bb58 | ||
|
|
6926b12a50 | ||
|
|
fb010b5ac5 | ||
|
|
b0370ee489 | ||
|
|
00fe6642a9 |
@@ -89,7 +89,6 @@ ChannelList::ChannelList()
|
||||
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);
|
||||
column->add_attribute(renderer->property_color(), m_columns.m_color);
|
||||
m_view.append_column(*column);
|
||||
|
||||
m_menu_guild_copy_id.signal_activate().connect([this] {
|
||||
@@ -263,51 +262,14 @@ void ChannelList::UpdateListing() {
|
||||
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
|
||||
/*
|
||||
guild_folders looks something like this
|
||||
"guild_folders": [
|
||||
{
|
||||
"color": null,
|
||||
"guild_ids": [
|
||||
"8009060___________"
|
||||
],
|
||||
"id": null,
|
||||
"name": null
|
||||
},
|
||||
{
|
||||
"color": null,
|
||||
"guild_ids": [
|
||||
"99615594__________",
|
||||
"86132141__________",
|
||||
"35450138__________",
|
||||
"83714048__________"
|
||||
],
|
||||
"id": 2853066769,
|
||||
"name": null
|
||||
}
|
||||
]
|
||||
const auto guild_ids = discord.GetUserSortedGuilds();
|
||||
int sortnum = 0;
|
||||
for (const auto &guild_id : guild_ids) {
|
||||
const auto guild = discord.GetGuild(guild_id);
|
||||
if (!guild.has_value()) continue;
|
||||
|
||||
so if id != null then its a folder (they can have single entries)
|
||||
*/
|
||||
|
||||
int sort_value = 0;
|
||||
|
||||
const auto folders = discord.GetUserSettings().GuildFolders;
|
||||
if (folders.empty()) {
|
||||
// fallback if no organization has occurred (guild_folders will be empty)
|
||||
const auto guild_ids = discord.GetUserSortedGuilds();
|
||||
for (const auto &guild_id : guild_ids) {
|
||||
const auto guild = discord.GetGuild(guild_id);
|
||||
if (!guild.has_value()) continue;
|
||||
|
||||
auto iter = AddGuild(*guild, m_model->children());
|
||||
(*iter)[m_columns.m_sort] = sort_value++;
|
||||
}
|
||||
} else {
|
||||
for (const auto &group : folders) {
|
||||
auto iter = AddFolder(group);
|
||||
(*iter)[m_columns.m_sort] = sort_value++;
|
||||
}
|
||||
auto iter = AddGuild(*guild);
|
||||
(*iter)[m_columns.m_sort] = sortnum++;
|
||||
}
|
||||
|
||||
m_updating_listing = false;
|
||||
@@ -315,9 +277,8 @@ void ChannelList::UpdateListing() {
|
||||
AddPrivateChannels();
|
||||
}
|
||||
|
||||
// TODO update for folders
|
||||
void ChannelList::UpdateNewGuild(const GuildData &guild) {
|
||||
AddGuild(guild, m_model->children());
|
||||
AddGuild(guild);
|
||||
// update sort order
|
||||
int sortnum = 0;
|
||||
for (const auto guild_id : Abaddon::Get().GetDiscordClient().GetUserSortedGuilds()) {
|
||||
@@ -444,8 +405,6 @@ void ChannelList::OnThreadListSync(const ThreadListSyncData &data) {
|
||||
// get the threads in the guild
|
||||
std::vector<Snowflake> threads;
|
||||
auto guild_iter = GetIteratorForGuildFromID(data.GuildID);
|
||||
if (!guild_iter) return;
|
||||
|
||||
std::queue<Gtk::TreeModel::iterator> queue;
|
||||
queue.push(guild_iter);
|
||||
|
||||
@@ -587,48 +546,11 @@ ExpansionStateRoot ChannelList::GetExpansionState() const {
|
||||
return r;
|
||||
}
|
||||
|
||||
Gtk::TreeModel::iterator ChannelList::AddFolder(const UserSettingsGuildFoldersEntry &folder) {
|
||||
if (!folder.ID.has_value()) {
|
||||
// just a guild
|
||||
if (!folder.GuildIDs.empty()) {
|
||||
const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(folder.GuildIDs[0]);
|
||||
if (guild.has_value()) {
|
||||
return AddGuild(*guild, m_model->children());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto folder_row = *m_model->append();
|
||||
folder_row[m_columns.m_type] = RenderType::Folder;
|
||||
folder_row[m_columns.m_id] = *folder.ID;
|
||||
if (folder.Name.has_value()) {
|
||||
folder_row[m_columns.m_name] = Glib::Markup::escape_text(*folder.Name);
|
||||
} else {
|
||||
folder_row[m_columns.m_name] = "Folder";
|
||||
}
|
||||
if (folder.Color.has_value()) {
|
||||
folder_row[m_columns.m_color] = IntToRGBA(*folder.Color);
|
||||
}
|
||||
|
||||
int sort_value = 0;
|
||||
for (const auto &guild_id : folder.GuildIDs) {
|
||||
const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(guild_id);
|
||||
if (guild.has_value()) {
|
||||
auto guild_row = AddGuild(*guild, folder_row->children());
|
||||
(*guild_row)[m_columns.m_sort] = sort_value++;
|
||||
}
|
||||
}
|
||||
|
||||
return folder_row;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk::TreeNodeChildren &root) {
|
||||
Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
auto &img = Abaddon::Get().GetImageManager();
|
||||
|
||||
auto guild_row = *m_model->append(root);
|
||||
auto guild_row = *m_model->append();
|
||||
guild_row[m_columns.m_type] = RenderType::Guild;
|
||||
guild_row[m_columns.m_id] = guild.ID;
|
||||
guild_row[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild.Name) + "</b>";
|
||||
@@ -757,15 +679,8 @@ void ChannelList::UpdateChannelCategory(const ChannelData &channel) {
|
||||
|
||||
Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) {
|
||||
for (const auto &child : m_model->children()) {
|
||||
if (child[m_columns.m_type] == RenderType::Guild && child[m_columns.m_id] == id) {
|
||||
if (child[m_columns.m_id] == id)
|
||||
return child;
|
||||
} else if (child[m_columns.m_type] == RenderType::Folder) {
|
||||
for (const auto &folder_child : child->children()) {
|
||||
if (folder_child[m_columns.m_id] == id) {
|
||||
return folder_child;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -979,7 +894,6 @@ void ChannelList::MoveRow(const Gtk::TreeModel::iterator &iter, const Gtk::TreeM
|
||||
M(m_sort);
|
||||
M(m_nsfw);
|
||||
M(m_expanded);
|
||||
M(m_color);
|
||||
#undef M
|
||||
|
||||
// recursively move children
|
||||
@@ -1092,5 +1006,4 @@ ChannelList::ModelColumns::ModelColumns() {
|
||||
add(m_sort);
|
||||
add(m_nsfw);
|
||||
add(m_expanded);
|
||||
add(m_color);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ protected:
|
||||
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::PixbufAnimation>> m_icon_anim;
|
||||
Gtk::TreeModelColumn<int64_t> m_sort;
|
||||
Gtk::TreeModelColumn<bool> m_nsfw;
|
||||
Gtk::TreeModelColumn<std::optional<Gdk::RGBA>> m_color; // for folders right now
|
||||
// Gtk::CellRenderer's property_is_expanded only works how i want it to if it has children
|
||||
// because otherwise it doesnt count as an "expander" (property_is_expander)
|
||||
// so this solution will have to do which i hate but the alternative is adding invisible children
|
||||
@@ -73,8 +72,7 @@ protected:
|
||||
ModelColumns m_columns;
|
||||
Glib::RefPtr<Gtk::TreeStore> m_model;
|
||||
|
||||
Gtk::TreeModel::iterator AddFolder(const UserSettingsGuildFoldersEntry &folder);
|
||||
Gtk::TreeModel::iterator AddGuild(const GuildData &guild, const Gtk::TreeNodeChildren &root);
|
||||
Gtk::TreeModel::iterator AddGuild(const GuildData &guild);
|
||||
Gtk::TreeModel::iterator UpdateCreateChannelCategory(const ChannelData &channel);
|
||||
Gtk::TreeModel::iterator CreateThreadRow(const Gtk::TreeNodeChildren &children, const ChannelData &channel);
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ CellRendererChannels::CellRendererChannels()
|
||||
, m_property_pixbuf(*this, "pixbuf")
|
||||
, m_property_pixbuf_animation(*this, "pixbuf-animation")
|
||||
, m_property_expanded(*this, "expanded")
|
||||
, m_property_nsfw(*this, "nsfw")
|
||||
, m_property_color(*this, "color") {
|
||||
, m_property_nsfw(*this, "nsfw") {
|
||||
property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
|
||||
property_xpad() = 2;
|
||||
property_ypad() = 2;
|
||||
@@ -56,14 +55,8 @@ Glib::PropertyProxy<bool> CellRendererChannels::property_nsfw() {
|
||||
return m_property_nsfw.get_proxy();
|
||||
}
|
||||
|
||||
Glib::PropertyProxy<std::optional<Gdk::RGBA>> CellRendererChannels::property_color() {
|
||||
return m_property_color.get_proxy();
|
||||
}
|
||||
|
||||
void CellRendererChannels::get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
|
||||
switch (m_property_type.get_value()) {
|
||||
case RenderType::Folder:
|
||||
return get_preferred_width_vfunc_folder(widget, minimum_width, natural_width);
|
||||
case RenderType::Guild:
|
||||
return get_preferred_width_vfunc_guild(widget, minimum_width, natural_width);
|
||||
case RenderType::Category:
|
||||
@@ -81,8 +74,6 @@ void CellRendererChannels::get_preferred_width_vfunc(Gtk::Widget &widget, int &m
|
||||
|
||||
void CellRendererChannels::get_preferred_width_for_height_vfunc(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
|
||||
switch (m_property_type.get_value()) {
|
||||
case RenderType::Folder:
|
||||
return get_preferred_width_for_height_vfunc_folder(widget, height, minimum_width, natural_width);
|
||||
case RenderType::Guild:
|
||||
return get_preferred_width_for_height_vfunc_guild(widget, height, minimum_width, natural_width);
|
||||
case RenderType::Category:
|
||||
@@ -100,8 +91,6 @@ void CellRendererChannels::get_preferred_width_for_height_vfunc(Gtk::Widget &wid
|
||||
|
||||
void CellRendererChannels::get_preferred_height_vfunc(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
|
||||
switch (m_property_type.get_value()) {
|
||||
case RenderType::Folder:
|
||||
return get_preferred_height_vfunc_folder(widget, minimum_height, natural_height);
|
||||
case RenderType::Guild:
|
||||
return get_preferred_height_vfunc_guild(widget, minimum_height, natural_height);
|
||||
case RenderType::Category:
|
||||
@@ -119,8 +108,6 @@ void CellRendererChannels::get_preferred_height_vfunc(Gtk::Widget &widget, int &
|
||||
|
||||
void CellRendererChannels::get_preferred_height_for_width_vfunc(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
|
||||
switch (m_property_type.get_value()) {
|
||||
case RenderType::Folder:
|
||||
return get_preferred_height_for_width_vfunc_folder(widget, width, minimum_height, natural_height);
|
||||
case RenderType::Guild:
|
||||
return get_preferred_height_for_width_vfunc_guild(widget, width, minimum_height, natural_height);
|
||||
case RenderType::Category:
|
||||
@@ -138,8 +125,6 @@ void CellRendererChannels::get_preferred_height_for_width_vfunc(Gtk::Widget &wid
|
||||
|
||||
void CellRendererChannels::render_vfunc(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
|
||||
switch (m_property_type.get_value()) {
|
||||
case RenderType::Folder:
|
||||
return render_vfunc_folder(cr, widget, background_area, cell_area, flags);
|
||||
case RenderType::Guild:
|
||||
return render_vfunc_guild(cr, widget, background_area, cell_area, flags);
|
||||
case RenderType::Category:
|
||||
@@ -155,69 +140,6 @@ void CellRendererChannels::render_vfunc(const Cairo::RefPtr<Cairo::Context> &cr,
|
||||
}
|
||||
}
|
||||
|
||||
// folder functions
|
||||
|
||||
void CellRendererChannels::get_preferred_width_vfunc_folder(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
|
||||
m_renderer_text.get_preferred_width(widget, minimum_width, natural_width);
|
||||
}
|
||||
|
||||
void CellRendererChannels::get_preferred_width_for_height_vfunc_folder(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
|
||||
m_renderer_text.get_preferred_width_for_height(widget, height, minimum_width, natural_width);
|
||||
}
|
||||
|
||||
void CellRendererChannels::get_preferred_height_vfunc_folder(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
|
||||
m_renderer_text.get_preferred_height(widget, minimum_height, natural_height);
|
||||
}
|
||||
|
||||
void CellRendererChannels::get_preferred_height_for_width_vfunc_folder(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
|
||||
m_renderer_text.get_preferred_height_for_width(widget, width, minimum_height, natural_height);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
Gtk::Requisition text_minimum, text_natural;
|
||||
m_renderer_text.get_preferred_size(widget, text_minimum, text_natural);
|
||||
|
||||
const int text_x = background_area.get_x() + 22;
|
||||
const int text_y = background_area.get_y() + background_area.get_height() / 2 - text_natural.height / 2;
|
||||
const int text_w = text_natural.width;
|
||||
const int text_h = text_natural.height;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// guild functions
|
||||
|
||||
void CellRendererChannels::get_preferred_width_vfunc_guild(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "discord/snowflake.hpp"
|
||||
|
||||
enum class RenderType : uint8_t {
|
||||
Folder,
|
||||
Guild,
|
||||
Category,
|
||||
TextChannel,
|
||||
@@ -28,7 +27,6 @@ public:
|
||||
Glib::PropertyProxy<Glib::RefPtr<Gdk::PixbufAnimation>> property_icon_animation();
|
||||
Glib::PropertyProxy<bool> property_expanded();
|
||||
Glib::PropertyProxy<bool> property_nsfw();
|
||||
Glib::PropertyProxy<std::optional<Gdk::RGBA>> property_color();
|
||||
|
||||
protected:
|
||||
void get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const override;
|
||||
@@ -41,17 +39,6 @@ protected:
|
||||
const Gdk::Rectangle &cell_area,
|
||||
Gtk::CellRendererState flags) override;
|
||||
|
||||
// guild functions
|
||||
void get_preferred_width_vfunc_folder(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
|
||||
void get_preferred_width_for_height_vfunc_folder(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
|
||||
void get_preferred_height_vfunc_folder(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
|
||||
void get_preferred_height_for_width_vfunc_folder(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
|
||||
void 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);
|
||||
|
||||
// guild functions
|
||||
void get_preferred_width_vfunc_guild(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
|
||||
void get_preferred_width_for_height_vfunc_guild(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
|
||||
@@ -131,7 +118,6 @@ private:
|
||||
Glib::Property<Glib::RefPtr<Gdk::PixbufAnimation>> m_property_pixbuf_animation; // guild
|
||||
Glib::Property<bool> m_property_expanded; // category
|
||||
Glib::Property<bool> m_property_nsfw; // channel
|
||||
Glib::Property<std::optional<Gdk::RGBA>> m_property_color; // folder
|
||||
|
||||
// same pitfalls as in https://github.com/uowuo/abaddon/blob/60404783bd4ce9be26233fe66fc3a74475d9eaa3/components/cellrendererpixbufanimation.hpp#L32-L39
|
||||
// this will manifest though since guild icons can change
|
||||
|
||||
@@ -1,85 +1,32 @@
|
||||
#include "memberlist.hpp"
|
||||
#include "abaddon.hpp"
|
||||
#include "util.hpp"
|
||||
#include "lazyimage.hpp"
|
||||
#include "statusindicator.hpp"
|
||||
|
||||
constexpr static const int MaxMemberListRows = 200;
|
||||
MemberList::MemberList()
|
||||
: m_model(Gtk::TreeStore::create(m_columns)) {
|
||||
add(m_view);
|
||||
show_all_children();
|
||||
|
||||
MemberListUserRow::MemberListUserRow(const std::optional<GuildData> &guild, const UserData &data) {
|
||||
ID = data.ID;
|
||||
m_ev = Gtk::manage(new Gtk::EventBox);
|
||||
m_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
|
||||
m_label = Gtk::manage(new Gtk::Label);
|
||||
m_avatar = Gtk::manage(new LazyImage(16, 16));
|
||||
m_status_indicator = Gtk::manage(new StatusIndicator(ID));
|
||||
m_view.set_activate_on_single_click(true);
|
||||
m_view.set_hexpand(true);
|
||||
m_view.set_vexpand(true);
|
||||
|
||||
if (Abaddon::Get().GetSettings().ShowOwnerCrown && guild.has_value() && guild->OwnerID == data.ID) {
|
||||
try {
|
||||
const static auto crown_path = Abaddon::GetResPath("/crown.png");
|
||||
auto pixbuf = Gdk::Pixbuf::create_from_file(crown_path, 12, 12);
|
||||
m_crown = Gtk::manage(new Gtk::Image(pixbuf));
|
||||
m_crown->set_valign(Gtk::ALIGN_CENTER);
|
||||
m_crown->set_margin_end(8);
|
||||
} catch (...) {}
|
||||
}
|
||||
m_view.set_show_expanders(false);
|
||||
m_view.set_enable_search(false);
|
||||
m_view.set_headers_visible(false);
|
||||
m_view.set_model(m_model);
|
||||
|
||||
m_status_indicator->set_margin_start(3);
|
||||
m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_DESCENDING);
|
||||
|
||||
if (guild.has_value())
|
||||
m_avatar->SetURL(data.GetAvatarURL(guild->ID, "png"));
|
||||
else
|
||||
m_avatar->SetURL(data.GetAvatarURL("png"));
|
||||
auto *column = Gtk::make_managed<Gtk::TreeView::Column>("display");
|
||||
auto *renderer = Gtk::make_managed<CellRendererMemberList>();
|
||||
column->pack_start(*renderer);
|
||||
column->add_attribute(renderer->property_type(), m_columns.m_type);
|
||||
column->add_attribute(renderer->property_id(), m_columns.m_id);
|
||||
column->add_attribute(renderer->property_markup(), m_columns.m_markup);
|
||||
column->add_attribute(renderer->property_icon(), m_columns.m_icon);
|
||||
m_view.append_column(*column);
|
||||
|
||||
get_style_context()->add_class("members-row");
|
||||
get_style_context()->add_class("members-row-member");
|
||||
m_label->get_style_context()->add_class("members-row-label");
|
||||
m_avatar->get_style_context()->add_class("members-row-avatar");
|
||||
|
||||
m_label->set_single_line_mode(true);
|
||||
m_label->set_ellipsize(Pango::ELLIPSIZE_END);
|
||||
|
||||
std::string display = data.Username;
|
||||
if (Abaddon::Get().GetSettings().ShowMemberListDiscriminators)
|
||||
display += "#" + data.Discriminator;
|
||||
if (guild.has_value()) {
|
||||
if (const auto col_id = data.GetHoistedRole(guild->ID, true); col_id.IsValid()) {
|
||||
auto color = Abaddon::Get().GetDiscordClient().GetRole(col_id)->Color;
|
||||
m_label->set_use_markup(true);
|
||||
m_label->set_markup("<span color='#" + IntToCSSColor(color) + "'>" + Glib::Markup::escape_text(display) + "</span>");
|
||||
} else {
|
||||
m_label->set_text(display);
|
||||
}
|
||||
} else {
|
||||
m_label->set_text(display);
|
||||
}
|
||||
|
||||
m_label->set_halign(Gtk::ALIGN_START);
|
||||
m_box->add(*m_avatar);
|
||||
m_box->add(*m_status_indicator);
|
||||
m_box->add(*m_label);
|
||||
if (m_crown != nullptr)
|
||||
m_box->add(*m_crown);
|
||||
m_ev->add(*m_box);
|
||||
add(*m_ev);
|
||||
show_all();
|
||||
}
|
||||
|
||||
MemberList::MemberList() {
|
||||
m_main = Gtk::manage(new Gtk::ScrolledWindow);
|
||||
m_listbox = Gtk::manage(new Gtk::ListBox);
|
||||
|
||||
m_listbox->get_style_context()->add_class("members");
|
||||
|
||||
m_listbox->set_selection_mode(Gtk::SELECTION_NONE);
|
||||
|
||||
m_main->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
|
||||
m_main->add(*m_listbox);
|
||||
m_main->show_all();
|
||||
}
|
||||
|
||||
Gtk::Widget *MemberList::GetRoot() const {
|
||||
return m_main;
|
||||
m_view.expand_all();
|
||||
}
|
||||
|
||||
void MemberList::Clear() {
|
||||
@@ -97,131 +44,110 @@ void MemberList::SetActiveChannel(Snowflake id) {
|
||||
}
|
||||
|
||||
void MemberList::UpdateMemberList() {
|
||||
m_id_to_row.clear();
|
||||
|
||||
auto children = m_listbox->get_children();
|
||||
auto it = children.begin();
|
||||
while (it != children.end()) {
|
||||
delete *it;
|
||||
it++;
|
||||
}
|
||||
|
||||
if (!Abaddon::Get().GetDiscordClient().IsStarted()) return;
|
||||
if (!m_chan_id.IsValid()) return;
|
||||
|
||||
m_model->clear();
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto chan = discord.GetChannel(m_chan_id);
|
||||
if (!chan.has_value()) return;
|
||||
if (chan->Type == ChannelType::DM || chan->Type == ChannelType::GROUP_DM) {
|
||||
int num_rows = 0;
|
||||
for (const auto &user : chan->GetDMRecipients()) {
|
||||
if (num_rows++ > MaxMemberListRows) break;
|
||||
auto *row = Gtk::manage(new MemberListUserRow(std::nullopt, user));
|
||||
m_id_to_row[user.ID] = row;
|
||||
AttachUserMenuHandler(row, user.ID);
|
||||
m_listbox->add(*row);
|
||||
if (!discord.IsStarted()) return;
|
||||
|
||||
const auto channel = discord.GetChannel(m_chan_id);
|
||||
if (!channel.has_value()) return;
|
||||
|
||||
// dm
|
||||
if (channel->IsDM()) {
|
||||
// todo eliminate for dm
|
||||
auto everyone_row = *m_model->append();
|
||||
everyone_row[m_columns.m_type] = MemberListRenderType::Role;
|
||||
everyone_row[m_columns.m_id] = Snowflake::Invalid;
|
||||
everyone_row[m_columns.m_markup] = "<b>Users</b>";
|
||||
|
||||
for (const auto &user : channel->GetDMRecipients()) {
|
||||
auto row = *m_model->append(everyone_row.children());
|
||||
row[m_columns.m_type] = MemberListRenderType::Member;
|
||||
row[m_columns.m_id] = user.ID;
|
||||
row[m_columns.m_markup] = Glib::Markup::escape_text(user.Username + "#" + user.Discriminator);
|
||||
row[m_columns.m_icon] = Abaddon::Get().GetImageManager().GetPlaceholder(16);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<Snowflake> ids;
|
||||
if (chan->IsThread()) {
|
||||
const auto x = discord.GetUsersInThread(m_chan_id);
|
||||
ids = { x.begin(), x.end() };
|
||||
} else
|
||||
ids = discord.GetUsersInGuild(m_guild_id);
|
||||
std::vector<UserData> users;
|
||||
if (channel->IsThread()) {
|
||||
// auto x = discord.GetUsersInThread(m_chan_id);
|
||||
// ids = { x.begin(), x.end() };
|
||||
} else {
|
||||
users = discord.GetUserDataInGuildBulk(m_guild_id);
|
||||
}
|
||||
|
||||
// process all the shit first so its in proper order
|
||||
std::map<int, RoleData> pos_to_role;
|
||||
std::map<int, std::vector<UserData>> pos_to_users;
|
||||
std::unordered_map<Snowflake, int> user_to_color;
|
||||
// std::unordered_map<Snowflake, int> user_to_color;
|
||||
std::vector<Snowflake> roleless_users;
|
||||
|
||||
for (const auto &id : ids) {
|
||||
auto user = discord.GetUser(id);
|
||||
if (!user.has_value() || user->IsDeleted())
|
||||
for (const auto &user : users) {
|
||||
if (user.IsDeleted())
|
||||
continue;
|
||||
|
||||
auto pos_role_id = discord.GetMemberHoistedRole(m_guild_id, id); // role for positioning
|
||||
auto col_role_id = discord.GetMemberHoistedRole(m_guild_id, id, true); // role for color
|
||||
auto pos_role_id = discord.GetMemberHoistedRole(m_guild_id, user.ID); // role for positioning
|
||||
// auto col_role_id = discord.GetMemberHoistedRole(m_guild_id, id, true); // role for color
|
||||
auto pos_role = discord.GetRole(pos_role_id);
|
||||
auto col_role = discord.GetRole(col_role_id);
|
||||
// auto col_role = discord.GetRole(col_role_id);
|
||||
|
||||
if (!pos_role.has_value()) {
|
||||
roleless_users.push_back(id);
|
||||
roleless_users.push_back(user.ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
pos_to_role[pos_role->Position] = *pos_role;
|
||||
pos_to_users[pos_role->Position].push_back(std::move(*user));
|
||||
if (col_role.has_value())
|
||||
user_to_color[id] = col_role->Color;
|
||||
pos_to_users[pos_role->Position].push_back(user);
|
||||
// if (col_role.has_value())
|
||||
// user_to_color[id] = col_role->Color;
|
||||
}
|
||||
|
||||
int num_rows = 0;
|
||||
const auto guild = discord.GetGuild(m_guild_id);
|
||||
if (!guild.has_value()) return;
|
||||
auto add_user = [this, &num_rows, guild](const UserData &data) -> bool {
|
||||
if (num_rows++ > MaxMemberListRows) return false;
|
||||
auto *row = Gtk::manage(new MemberListUserRow(*guild, data));
|
||||
m_id_to_row[data.ID] = row;
|
||||
AttachUserMenuHandler(row, data.ID);
|
||||
m_listbox->add(*row);
|
||||
auto add_user = [this](const UserData &user, const Gtk::TreeNodeChildren &node) -> bool {
|
||||
auto row = *m_model->append(node);
|
||||
row[m_columns.m_type] = MemberListRenderType::Member;
|
||||
row[m_columns.m_id] = user.ID;
|
||||
row[m_columns.m_markup] = user.GetEscapedName() + "#" + user.Discriminator;
|
||||
row[m_columns.m_sort] = static_cast<int>(user.ID);
|
||||
row[m_columns.m_icon] = Abaddon::Get().GetImageManager().GetPlaceholder(16);
|
||||
// come on
|
||||
Gtk::TreeRowReference ref(m_model, m_model->get_path(row));
|
||||
Abaddon::Get().GetImageManager().LoadFromURL(user.GetAvatarURL(), [this, ref = std::move(ref)](const Glib::RefPtr<Gdk::Pixbuf> &pb) {
|
||||
if (ref.is_valid()) {
|
||||
auto row = *m_model->get_iter(ref.get_path());
|
||||
row[m_columns.m_icon] = pb->scale_simple(16, 16, Gdk::INTERP_BILINEAR);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
auto add_role = [this](const std::string &name) {
|
||||
auto *role_row = Gtk::manage(new Gtk::ListBoxRow);
|
||||
auto *role_lbl = Gtk::manage(new Gtk::Label);
|
||||
|
||||
role_row->get_style_context()->add_class("members-row");
|
||||
role_row->get_style_context()->add_class("members-row-role");
|
||||
role_lbl->get_style_context()->add_class("members-row-label");
|
||||
|
||||
role_lbl->set_single_line_mode(true);
|
||||
role_lbl->set_ellipsize(Pango::ELLIPSIZE_END);
|
||||
role_lbl->set_use_markup(true);
|
||||
role_lbl->set_markup("<b>" + Glib::Markup::escape_text(name) + "</b>");
|
||||
role_lbl->set_halign(Gtk::ALIGN_START);
|
||||
role_row->add(*role_lbl);
|
||||
role_row->show_all();
|
||||
m_listbox->add(*role_row);
|
||||
auto add_role = [this](const RoleData &role) -> Gtk::TreeRow {
|
||||
auto row = *m_model->append();
|
||||
row[m_columns.m_type] = MemberListRenderType::Role;
|
||||
row[m_columns.m_id] = role.ID;
|
||||
row[m_columns.m_markup] = "<b>" + role.GetEscapedName() + "</b>";
|
||||
row[m_columns.m_sort] = role.Position;
|
||||
return row;
|
||||
};
|
||||
|
||||
for (auto it = pos_to_role.crbegin(); it != pos_to_role.crend(); it++) {
|
||||
auto pos = it->first;
|
||||
const auto &role = it->second;
|
||||
|
||||
add_role(role.Name);
|
||||
|
||||
if (pos_to_users.find(pos) == pos_to_users.end()) continue;
|
||||
|
||||
auto &users = pos_to_users.at(pos);
|
||||
AlphabeticalSort(users.begin(), users.end(), [](const auto &e) { return e.Username; });
|
||||
|
||||
for (const auto &data : users)
|
||||
if (!add_user(data)) return;
|
||||
}
|
||||
|
||||
if (chan->Type == ChannelType::DM || chan->Type == ChannelType::GROUP_DM)
|
||||
add_role("Users");
|
||||
else
|
||||
add_role("@everyone");
|
||||
for (const auto &id : roleless_users) {
|
||||
const auto user = discord.GetUser(id);
|
||||
if (user.has_value())
|
||||
if (!add_user(*user)) return;
|
||||
}
|
||||
}
|
||||
|
||||
void MemberList::AttachUserMenuHandler(Gtk::ListBoxRow *row, Snowflake id) {
|
||||
row->signal_button_press_event().connect([this, id](GdkEventButton *e) -> bool {
|
||||
if (e->type == GDK_BUTTON_PRESS && e->button == GDK_BUTTON_SECONDARY) {
|
||||
Abaddon::Get().ShowUserMenu(reinterpret_cast<const GdkEvent *>(e), id, m_guild_id);
|
||||
return true;
|
||||
for (auto &[pos, role] : pos_to_role) {
|
||||
auto role_children = add_role(role).children();
|
||||
if (auto it = pos_to_users.find(pos); it != pos_to_users.end()) {
|
||||
for (const auto &user : it->second) {
|
||||
if (!add_user(user, role_children)) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
m_view.expand_all();
|
||||
}
|
||||
|
||||
MemberList::ModelColumns::ModelColumns() {
|
||||
add(m_type);
|
||||
add(m_id);
|
||||
add(m_markup);
|
||||
add(m_icon);
|
||||
add(m_sort);
|
||||
}
|
||||
|
||||
@@ -1,44 +1,37 @@
|
||||
#pragma once
|
||||
#include <gtkmm.h>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include "discord/discord.hpp"
|
||||
#include <variant>
|
||||
#include <gtkmm/scrolledwindow.h>
|
||||
#include <gtkmm/treemodel.h>
|
||||
#include <gtkmm/treestore.h>
|
||||
#include <gtkmm/treeview.h>
|
||||
#include "discord/snowflake.hpp"
|
||||
#include "memberlistcellrenderer.hpp"
|
||||
|
||||
class LazyImage;
|
||||
class StatusIndicator;
|
||||
class MemberListUserRow : public Gtk::ListBoxRow {
|
||||
public:
|
||||
MemberListUserRow(const std::optional<GuildData> &guild, const UserData &data);
|
||||
|
||||
Snowflake ID;
|
||||
|
||||
private:
|
||||
Gtk::EventBox *m_ev;
|
||||
Gtk::Box *m_box;
|
||||
LazyImage *m_avatar;
|
||||
StatusIndicator *m_status_indicator;
|
||||
Gtk::Label *m_label;
|
||||
Gtk::Image *m_crown = nullptr;
|
||||
};
|
||||
|
||||
class MemberList {
|
||||
class MemberList : public Gtk::ScrolledWindow {
|
||||
public:
|
||||
MemberList();
|
||||
Gtk::Widget *GetRoot() const;
|
||||
|
||||
void UpdateMemberList();
|
||||
void Clear();
|
||||
void SetActiveChannel(Snowflake id);
|
||||
|
||||
private:
|
||||
void AttachUserMenuHandler(Gtk::ListBoxRow *row, Snowflake id);
|
||||
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
|
||||
public:
|
||||
ModelColumns();
|
||||
|
||||
Gtk::ScrolledWindow *m_main;
|
||||
Gtk::ListBox *m_listbox;
|
||||
Gtk::TreeModelColumn<MemberListRenderType> m_type;
|
||||
Gtk::TreeModelColumn<uint64_t> m_id;
|
||||
Gtk::TreeModelColumn<Glib::ustring> m_markup;
|
||||
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_icon;
|
||||
|
||||
Gtk::TreeModelColumn<int> m_sort;
|
||||
};
|
||||
|
||||
ModelColumns m_columns;
|
||||
Glib::RefPtr<Gtk::TreeStore> m_model;
|
||||
Gtk::TreeView m_view;
|
||||
|
||||
Snowflake m_guild_id;
|
||||
Snowflake m_chan_id;
|
||||
|
||||
std::unordered_map<Snowflake, Gtk::ListBoxRow *> m_id_to_row;
|
||||
};
|
||||
|
||||
177
src/components/memberlistcellrenderer.cpp
Normal file
177
src/components/memberlistcellrenderer.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "memberlistcellrenderer.hpp"
|
||||
#include <gdkmm/general.h>
|
||||
|
||||
CellRendererMemberList::CellRendererMemberList()
|
||||
: Glib::ObjectBase(typeid(CellRendererMemberList))
|
||||
, m_property_type(*this, "render-type")
|
||||
, m_property_id(*this, "id")
|
||||
, m_property_markup(*this, "markup")
|
||||
, m_property_icon(*this, "pixbuf") {
|
||||
m_renderer_text.property_ellipsize() = Pango::ELLIPSIZE_END;
|
||||
m_property_markup.get_proxy().signal_changed().connect([this]() {
|
||||
m_renderer_text.property_markup() = m_property_markup;
|
||||
});
|
||||
}
|
||||
|
||||
Glib::PropertyProxy<MemberListRenderType> CellRendererMemberList::property_type() {
|
||||
return m_property_type.get_proxy();
|
||||
}
|
||||
|
||||
Glib::PropertyProxy<uint64_t> CellRendererMemberList::property_id() {
|
||||
return m_property_id.get_proxy();
|
||||
}
|
||||
|
||||
Glib::PropertyProxy<Glib::ustring> CellRendererMemberList::property_markup() {
|
||||
return m_property_markup.get_proxy();
|
||||
}
|
||||
|
||||
Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf>> CellRendererMemberList::property_icon() {
|
||||
return m_property_icon.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::Member:
|
||||
return get_preferred_width_vfunc_member(widget, minimum_width, natural_width);
|
||||
case MemberListRenderType::Role:
|
||||
return get_preferred_width_vfunc_role(widget, minimum_width, natural_width);
|
||||
}
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_width_for_height_vfunc(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
|
||||
switch (m_property_type.get_value()) {
|
||||
case MemberListRenderType::Member:
|
||||
return get_preferred_width_for_height_vfunc_member(widget, height, minimum_width, natural_width);
|
||||
case MemberListRenderType::Role:
|
||||
return get_preferred_width_for_height_vfunc_role(widget, height, minimum_width, natural_width);
|
||||
}
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_height_vfunc(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
|
||||
switch (m_property_type.get_value()) {
|
||||
case MemberListRenderType::Member:
|
||||
return get_preferred_height_vfunc_member(widget, minimum_height, natural_height);
|
||||
case MemberListRenderType::Role:
|
||||
return get_preferred_height_vfunc_role(widget, minimum_height, natural_height);
|
||||
}
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_height_for_width_vfunc(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
|
||||
switch (m_property_type.get_value()) {
|
||||
case MemberListRenderType::Member:
|
||||
return get_preferred_height_for_width_vfunc_member(widget, width, minimum_height, natural_height);
|
||||
case MemberListRenderType::Role:
|
||||
return get_preferred_height_for_width_vfunc_role(widget, width, minimum_height, natural_height);
|
||||
}
|
||||
}
|
||||
|
||||
void CellRendererMemberList::render_vfunc(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
|
||||
switch (m_property_type.get_value()) {
|
||||
case MemberListRenderType::Member:
|
||||
return render_vfunc_member(cr, widget, background_area, cell_area, flags);
|
||||
case MemberListRenderType::Role:
|
||||
return render_vfunc_role(cr, widget, background_area, cell_area, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_width_vfunc_role(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
|
||||
int text_min, text_nat;
|
||||
m_renderer_text.get_preferred_width(widget, text_min, text_nat);
|
||||
int xpad, ypad;
|
||||
get_padding(xpad, ypad);
|
||||
minimum_width = text_min + xpad * 2;
|
||||
natural_width = text_nat + xpad * 2;
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_width_for_height_vfunc_role(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
|
||||
get_preferred_width_vfunc_role(widget, minimum_width, natural_width);
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_height_vfunc_role(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
|
||||
int text_min, text_nat;
|
||||
m_renderer_text.get_preferred_height(widget, text_min, text_nat);
|
||||
int xpad, ypad;
|
||||
get_padding(xpad, ypad);
|
||||
minimum_height = text_min + ypad * 2;
|
||||
natural_height = text_nat + ypad * 2;
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_height_for_width_vfunc_role(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
|
||||
get_preferred_height_vfunc_role(widget, minimum_height, natural_height);
|
||||
}
|
||||
|
||||
void CellRendererMemberList::render_vfunc_role(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
|
||||
Gtk::Requisition text_min, text_nat, min, nat;
|
||||
m_renderer_text.get_preferred_size(widget, text_min, text_nat);
|
||||
get_preferred_size(widget, min, nat);
|
||||
|
||||
int x = background_area.get_x() + 5;
|
||||
int y = background_area.get_y() + background_area.get_height() / 2.0 - text_nat.height / 2.0;
|
||||
int w = text_nat.width;
|
||||
int h = text_nat.height;
|
||||
|
||||
Gdk::Rectangle text_cell_area(x, y, w, h);
|
||||
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_width_vfunc_member(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
|
||||
int text_min, text_nat;
|
||||
m_renderer_text.get_preferred_width(widget, text_min, text_nat);
|
||||
int xpad, ypad;
|
||||
get_padding(xpad, ypad);
|
||||
minimum_width = text_min + xpad * 2;
|
||||
natural_width = text_nat + xpad * 2;
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_width_for_height_vfunc_member(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
|
||||
get_preferred_width_vfunc_role(widget, minimum_width, natural_width);
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_height_vfunc_member(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
|
||||
int text_min, text_nat;
|
||||
m_renderer_text.get_preferred_height(widget, text_min, text_nat);
|
||||
int xpad, ypad;
|
||||
get_padding(xpad, ypad);
|
||||
minimum_height = text_min + ypad * 2;
|
||||
natural_height = text_nat + ypad * 2;
|
||||
}
|
||||
|
||||
void CellRendererMemberList::get_preferred_height_for_width_vfunc_member(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
|
||||
get_preferred_height_vfunc_role(widget, minimum_height, natural_height);
|
||||
}
|
||||
|
||||
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) {
|
||||
Gtk::Requisition text_min, text_nat, min, nat;
|
||||
m_renderer_text.get_preferred_size(widget, text_min, text_nat);
|
||||
get_preferred_size(widget, min, nat);
|
||||
|
||||
int pixbuf_w = 0, pixbuf_h = 0;
|
||||
if (auto pixbuf = m_property_icon.get_value()) {
|
||||
pixbuf_w = pixbuf->get_width();
|
||||
pixbuf_h = pixbuf->get_height();
|
||||
}
|
||||
|
||||
const double icon_w = pixbuf_w;
|
||||
const double icon_h = pixbuf_h;
|
||||
const double icon_x = background_area.get_x() + 3.0;
|
||||
const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - icon_h / 2.0;
|
||||
|
||||
const double x = icon_x + icon_w + 5;
|
||||
const double y = background_area.get_y() + background_area.get_height() / 2.0 - text_nat.height / 2.0;
|
||||
const double w = text_nat.width;
|
||||
const double h = text_nat.height;
|
||||
|
||||
if (auto pixbuf = m_property_icon.get_value()) {
|
||||
Gdk::Cairo::set_source_pixbuf(cr, pixbuf, icon_x, icon_y);
|
||||
cr->rectangle(icon_x, icon_y, icon_w, icon_h);
|
||||
cr->fill();
|
||||
}
|
||||
|
||||
Gdk::Rectangle text_cell_area(
|
||||
static_cast<int>(x),
|
||||
static_cast<int>(y),
|
||||
static_cast<int>(w),
|
||||
static_cast<int>(h));
|
||||
|
||||
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
||||
}
|
||||
60
src/components/memberlistcellrenderer.hpp
Normal file
60
src/components/memberlistcellrenderer.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include <gdkmm/pixbuf.h>
|
||||
#include <glibmm/property.h>
|
||||
#include <gtkmm/cellrenderertext.h>
|
||||
|
||||
enum class MemberListRenderType {
|
||||
Role,
|
||||
Member,
|
||||
};
|
||||
|
||||
class CellRendererMemberList : public Gtk::CellRenderer {
|
||||
public:
|
||||
CellRendererMemberList();
|
||||
~CellRendererMemberList() = default;
|
||||
|
||||
Glib::PropertyProxy<MemberListRenderType> property_type();
|
||||
Glib::PropertyProxy<uint64_t> property_id();
|
||||
Glib::PropertyProxy<Glib::ustring> property_markup();
|
||||
Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf>> property_icon();
|
||||
|
||||
private:
|
||||
void get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const override;
|
||||
void get_preferred_width_for_height_vfunc(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const override;
|
||||
void get_preferred_height_vfunc(Gtk::Widget &widget, int &minimum_height, int &natural_height) const override;
|
||||
void get_preferred_height_for_width_vfunc(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const override;
|
||||
void render_vfunc(const Cairo::RefPtr<Cairo::Context> &cr,
|
||||
Gtk::Widget &widget,
|
||||
const Gdk::Rectangle &background_area,
|
||||
const Gdk::Rectangle &cell_area,
|
||||
Gtk::CellRendererState flags) override;
|
||||
|
||||
// role
|
||||
void get_preferred_width_vfunc_role(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
|
||||
void get_preferred_width_for_height_vfunc_role(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
|
||||
void get_preferred_height_vfunc_role(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
|
||||
void get_preferred_height_for_width_vfunc_role(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
|
||||
void render_vfunc_role(const Cairo::RefPtr<Cairo::Context> &cr,
|
||||
Gtk::Widget &widget,
|
||||
const Gdk::Rectangle &background_area,
|
||||
const Gdk::Rectangle &cell_area,
|
||||
Gtk::CellRendererState flags);
|
||||
|
||||
// member
|
||||
void get_preferred_width_vfunc_member(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
|
||||
void get_preferred_width_for_height_vfunc_member(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
|
||||
void get_preferred_height_vfunc_member(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
|
||||
void get_preferred_height_for_width_vfunc_member(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
|
||||
void 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);
|
||||
|
||||
Gtk::CellRendererText m_renderer_text;
|
||||
|
||||
Glib::Property<MemberListRenderType> m_property_type;
|
||||
Glib::Property<uint64_t> m_property_id;
|
||||
Glib::Property<Glib::ustring> m_property_markup;
|
||||
Glib::Property<Glib::RefPtr<Gdk::Pixbuf>> m_property_icon;
|
||||
};
|
||||
@@ -262,6 +262,12 @@ std::set<Snowflake> DiscordClient::GetUsersInGuild(Snowflake id) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<UserData> DiscordClient::GetUserDataInGuildBulk(Snowflake id) {
|
||||
const auto ids = GetUsersInGuild(id);
|
||||
std::vector<Snowflake> test;
|
||||
return m_store.GetUserDataBulk(ids.begin(), ids.end());
|
||||
}
|
||||
|
||||
std::set<Snowflake> DiscordClient::GetChannelsInGuild(Snowflake id) const {
|
||||
auto it = m_guild_to_channels.find(id);
|
||||
if (it != m_guild_to_channels.end())
|
||||
@@ -1204,10 +1210,6 @@ void DiscordClient::SetUserAgent(const std::string &agent) {
|
||||
m_websocket.SetUserAgent(agent);
|
||||
}
|
||||
|
||||
void DiscordClient::SetDumpReady(bool dump) {
|
||||
m_dump_ready = dump;
|
||||
}
|
||||
|
||||
bool DiscordClient::IsChannelMuted(Snowflake id) const noexcept {
|
||||
return m_muted_channels.find(id) != m_muted_channels.end();
|
||||
}
|
||||
@@ -1570,17 +1572,6 @@ void DiscordClient::ProcessNewGuild(GuildData &guild) {
|
||||
|
||||
void DiscordClient::HandleGatewayReady(const GatewayMessage &msg) {
|
||||
m_ready_received = true;
|
||||
|
||||
if (m_dump_ready) {
|
||||
const auto name = "./payload_ready-" + Glib::DateTime::create_now_utc().format("%Y-%m-%d_%H-%M-%S") + ".json";
|
||||
auto *fp = std::fopen(name.c_str(), "wb");
|
||||
if (fp != nullptr) {
|
||||
const auto contents = msg.Data.dump(4);
|
||||
std::fwrite(contents.data(), contents.size(), 1, fp);
|
||||
std::fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
ReadyEventData data = msg.Data;
|
||||
for (auto &g : data.Guilds)
|
||||
ProcessNewGuild(g);
|
||||
@@ -2292,10 +2283,6 @@ std::set<Snowflake> DiscordClient::GetPrivateChannels() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
const UserSettings &DiscordClient::GetUserSettings() const {
|
||||
return m_user_settings;
|
||||
}
|
||||
|
||||
EPremiumType DiscordClient::GetSelfPremiumType() const {
|
||||
const auto &data = GetUserData();
|
||||
if (data.PremiumType.has_value())
|
||||
|
||||
@@ -60,7 +60,6 @@ public:
|
||||
std::vector<Message> GetMessagesForChannel(Snowflake id, size_t limit = 50) const;
|
||||
std::vector<Message> GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit = 50) const;
|
||||
std::set<Snowflake> GetPrivateChannels() const;
|
||||
const UserSettings &GetUserSettings() const;
|
||||
|
||||
EPremiumType GetSelfPremiumType() const;
|
||||
|
||||
@@ -77,6 +76,7 @@ public:
|
||||
Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const;
|
||||
std::optional<RoleData> GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const;
|
||||
std::set<Snowflake> GetUsersInGuild(Snowflake id) const;
|
||||
std::vector<UserData> GetUserDataInGuildBulk(Snowflake id);
|
||||
std::set<Snowflake> GetChannelsInGuild(Snowflake id) const;
|
||||
std::vector<Snowflake> GetUsersInThread(Snowflake id) const;
|
||||
std::vector<ChannelData> GetActiveThreads(Snowflake channel_id) const;
|
||||
@@ -212,8 +212,6 @@ public:
|
||||
void UpdateToken(const std::string &token);
|
||||
void SetUserAgent(const std::string &agent);
|
||||
|
||||
void SetDumpReady(bool dump);
|
||||
|
||||
bool IsChannelMuted(Snowflake id) const noexcept;
|
||||
bool IsGuildMuted(Snowflake id) const noexcept;
|
||||
int GetUnreadStateForChannel(Snowflake id) const noexcept;
|
||||
@@ -232,8 +230,6 @@ private:
|
||||
std::vector<uint8_t> m_decompress_buf;
|
||||
z_stream m_zstream;
|
||||
|
||||
bool m_dump_ready = false;
|
||||
|
||||
static std::string GetAPIURL();
|
||||
static std::string GetGatewayURL();
|
||||
|
||||
|
||||
@@ -1033,6 +1033,22 @@ RoleData Store::GetRoleBound(std::unique_ptr<Statement> &s) {
|
||||
return r;
|
||||
}
|
||||
|
||||
UserData Store::GetUserBound(Statement *s) const {
|
||||
UserData r;
|
||||
|
||||
s->Get(0, r.ID);
|
||||
s->Get(1, r.Username);
|
||||
s->Get(2, r.Discriminator);
|
||||
s->Get(3, r.Avatar);
|
||||
s->Get(4, r.IsBot);
|
||||
s->Get(5, r.IsSystem);
|
||||
s->Get(6, r.IsMFAEnabled);
|
||||
s->Get(7, r.PremiumType);
|
||||
s->Get(8, r.PublicFlags);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
std::optional<UserData> Store::GetUser(Snowflake id) const {
|
||||
auto &s = m_stmt_get_user;
|
||||
s->Bind(1, id);
|
||||
@@ -1043,17 +1059,7 @@ std::optional<UserData> Store::GetUser(Snowflake id) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
UserData r;
|
||||
|
||||
r.ID = id;
|
||||
s->Get(1, r.Username);
|
||||
s->Get(2, r.Discriminator);
|
||||
s->Get(3, r.Avatar);
|
||||
s->Get(4, r.IsBot);
|
||||
s->Get(5, r.IsSystem);
|
||||
s->Get(6, r.IsMFAEnabled);
|
||||
s->Get(7, r.PremiumType);
|
||||
s->Get(8, r.PublicFlags);
|
||||
auto r = GetUserBound(s.get());
|
||||
|
||||
s->Reset();
|
||||
|
||||
|
||||
@@ -39,6 +39,36 @@ public:
|
||||
std::optional<BanData> GetBan(Snowflake guild_id, Snowflake user_id) const;
|
||||
std::vector<BanData> GetBans(Snowflake guild_id) const;
|
||||
|
||||
template<typename Iter>
|
||||
std::vector<UserData> GetUserDataBulk(Iter start, Iter end) {
|
||||
std::string query = "SELECT * FROM users WHERE id IN (";
|
||||
for (Iter it = start; it != end; it++) {
|
||||
query += "?,";
|
||||
}
|
||||
query.pop_back();
|
||||
query += ")";
|
||||
|
||||
Statement stmt(m_db, query.c_str());
|
||||
if (!stmt.OK()) {
|
||||
printf("failed to prepare GetUserDataBulk: %s\n", m_db.ErrStr());
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (Iter it = start; it != end; it++) {
|
||||
i++;
|
||||
if (stmt.Bind(i, *it) != SQLITE_OK) {
|
||||
printf("failed to bind GetUserDataBulk: %s\n", m_db.ErrStr());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<UserData> r;
|
||||
while (stmt.FetchOne()) {
|
||||
r.push_back(GetUserBound(&stmt));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
std::vector<Message> GetLastMessages(Snowflake id, size_t num) const;
|
||||
std::vector<Message> GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit) const;
|
||||
std::vector<Message> GetPinnedMessages(Snowflake channel_id) const;
|
||||
@@ -240,6 +270,7 @@ private:
|
||||
|
||||
Message GetMessageBound(std::unique_ptr<Statement> &stmt) const;
|
||||
static RoleData GetRoleBound(std::unique_ptr<Statement> &stmt);
|
||||
UserData GetUserBound(Statement *stmt) const;
|
||||
|
||||
void SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction);
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#pragma once
|
||||
#include "json.hpp"
|
||||
#include "snowflake.hpp"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
struct UserSettingsGuildFoldersEntry {
|
||||
std::optional<int> Color;
|
||||
int Color = -1; // null
|
||||
std::vector<Snowflake> GuildIDs;
|
||||
std::optional<Snowflake> ID; // (this can be a snowflake as a string or an int that isnt a snowflake lol)
|
||||
std::optional<std::string> Name;
|
||||
Snowflake ID; // null (this can be a snowflake as a string or an int that isnt a snowflake lol)
|
||||
std::string Name; // null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserSettingsGuildFoldersEntry &m);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "mainwindow.hpp"
|
||||
#include "abaddon.hpp"
|
||||
|
||||
#include "components/memberlist.hpp" // TMP!!!!
|
||||
|
||||
MainWindow::MainWindow()
|
||||
: m_main_box(Gtk::ORIENTATION_VERTICAL)
|
||||
, m_content_box(Gtk::ORIENTATION_HORIZONTAL)
|
||||
@@ -20,7 +22,6 @@ MainWindow::MainWindow()
|
||||
m_main_box.add(m_content_box);
|
||||
m_main_box.show();
|
||||
|
||||
auto *member_list = m_members.GetRoot();
|
||||
auto *chat = m_chat.GetRoot();
|
||||
|
||||
chat->set_vexpand(true);
|
||||
@@ -37,8 +38,8 @@ MainWindow::MainWindow()
|
||||
m_channel_list.set_size_request(-1, -1);
|
||||
m_channel_list.show();
|
||||
|
||||
member_list->set_vexpand(true);
|
||||
member_list->show();
|
||||
m_members.set_vexpand(true);
|
||||
m_members.show();
|
||||
|
||||
m_friends.set_vexpand(true);
|
||||
m_friends.set_hexpand(true);
|
||||
@@ -63,11 +64,11 @@ MainWindow::MainWindow()
|
||||
m_channel_list.UsePanedHack(m_chan_content_paned);
|
||||
|
||||
m_content_members_paned.pack1(m_content_stack);
|
||||
m_content_members_paned.pack2(*member_list);
|
||||
m_content_members_paned.pack2(m_members);
|
||||
m_content_members_paned.child_property_shrink(m_content_stack) = true;
|
||||
m_content_members_paned.child_property_resize(m_content_stack) = true;
|
||||
m_content_members_paned.child_property_shrink(*member_list) = true;
|
||||
m_content_members_paned.child_property_resize(*member_list) = true;
|
||||
m_content_members_paned.child_property_shrink(m_members) = true;
|
||||
m_content_members_paned.child_property_resize(m_members) = true;
|
||||
int w, h;
|
||||
get_default_size(w, h); // :s
|
||||
m_content_members_paned.set_position(w - m_chan_content_paned.get_position() - 150);
|
||||
@@ -255,10 +256,8 @@ void MainWindow::SetupMenu() {
|
||||
m_menu_file.set_submenu(m_menu_file_sub);
|
||||
m_menu_file_reload_css.set_label("Reload CSS");
|
||||
m_menu_file_clear_cache.set_label("Clear file cache");
|
||||
m_menu_file_dump_ready.set_label("Dump ready message");
|
||||
m_menu_file_sub.append(m_menu_file_reload_css);
|
||||
m_menu_file_sub.append(m_menu_file_clear_cache);
|
||||
m_menu_file_sub.append(m_menu_file_dump_ready);
|
||||
|
||||
m_menu_view.set_label("View");
|
||||
m_menu_view.set_submenu(m_menu_view_sub);
|
||||
@@ -337,10 +336,6 @@ void MainWindow::SetupMenu() {
|
||||
Abaddon::Get().GetImageManager().ClearCache();
|
||||
});
|
||||
|
||||
m_menu_file_dump_ready.signal_toggled().connect([this]() {
|
||||
Abaddon::Get().GetDiscordClient().SetDumpReady(m_menu_file_dump_ready.get_active());
|
||||
});
|
||||
|
||||
m_menu_discord_add_recipient.signal_activate().connect([this] {
|
||||
m_signal_action_add_recipient.emit(GetChatActiveChannel());
|
||||
});
|
||||
@@ -373,7 +368,7 @@ void MainWindow::SetupMenu() {
|
||||
});
|
||||
|
||||
m_menu_view_members.signal_activate().connect([this]() {
|
||||
m_members.GetRoot()->set_visible(m_menu_view_members.get_active());
|
||||
m_members.set_visible(m_menu_view_members.get_active());
|
||||
});
|
||||
|
||||
#ifdef WITH_LIBHANDY
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include "components/channels.hpp"
|
||||
#include "components/chatwindow.hpp"
|
||||
#include "components/memberlist.hpp"
|
||||
#include "components/friendslist.hpp"
|
||||
#include "components/memberlist.hpp"
|
||||
#include <gtkmm.h>
|
||||
|
||||
class MainWindow : public Gtk::Window {
|
||||
@@ -72,7 +72,6 @@ private:
|
||||
Gtk::Menu m_menu_file_sub;
|
||||
Gtk::MenuItem m_menu_file_reload_css;
|
||||
Gtk::MenuItem m_menu_file_clear_cache;
|
||||
Gtk::CheckMenuItem m_menu_file_dump_ready;
|
||||
|
||||
Gtk::MenuItem m_menu_view;
|
||||
Gtk::Menu m_menu_view_sub;
|
||||
|
||||
Reference in New Issue
Block a user