add private channels to list

This commit is contained in:
ouwou 2021-07-13 20:09:01 -04:00
parent f60e2cd6bd
commit 8c3752ef9f
2 changed files with 242 additions and 12 deletions

View File

@ -21,7 +21,7 @@ ChannelList::ChannelList()
row[m_columns.m_expanded] = true;
}
if (row[m_columns.m_type] == RenderType::TextChannel) {
if (row[m_columns.m_type] == RenderType::TextChannel || row[m_columns.m_type] == RenderType::DM) {
m_signal_action_channel_item_select.emit(static_cast<Snowflake>(row[m_columns.m_id]));
}
};
@ -51,6 +51,8 @@ ChannelList::ChannelList()
column->add_attribute(renderer->property_name(), m_columns.m_name);
column->add_attribute(renderer->property_expanded(), m_columns.m_expanded);
m_view.append_column(*column);
Abaddon::Get().GetDiscordClient().signal_message_create().connect(sigc::mem_fun(*this, &ChannelList::OnMessageCreate));
}
void ChannelList::UpdateListing() {
@ -68,6 +70,8 @@ void ChannelList::UpdateListing() {
auto iter = AddGuild(*guild);
(*iter)[m_columns.m_sort] = sortnum++;
}
AddPrivateChannels();
}
void ChannelList::UpdateNewGuild(Snowflake id) {
@ -136,6 +140,7 @@ void ChannelList::UpdateCreateChannel(Snowflake id) {
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
if (!channel.has_value()) return;
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;
Gtk::TreeRow channel_row;
@ -323,8 +328,89 @@ bool ChannelList::SelectionFunc(const Glib::RefPtr<Gtk::TreeModel> &model, const
if (auto row = selection->get_selected())
m_last_selected = m_model->get_path(row);
auto iter = m_model->get_iter(path);
return (*iter)[m_columns.m_type] == RenderType::TextChannel;
auto type = (*m_model->get_iter(path))[m_columns.m_type];
return type == RenderType::TextChannel || type == RenderType::DM;
}
void ChannelList::AddPrivateChannels() {
auto header_row = *m_model->append();
header_row[m_columns.m_type] = RenderType::DMHeader;
header_row[m_columns.m_sort] = -1;
header_row[m_columns.m_name] = "<b>Direct Messages</b>";
m_dm_header = m_model->get_path(header_row);
auto &discord = Abaddon::Get().GetDiscordClient();
auto &img = Abaddon::Get().GetImageManager();
const auto dm_ids = discord.GetPrivateChannels();
for (const auto dm_id : dm_ids) {
const auto dm = discord.GetChannel(dm_id);
if (!dm.has_value()) continue;
std::optional<UserData> top_recipient;
const auto recipients = dm->GetDMRecipients();
if (recipients.size() > 0)
top_recipient = recipients[0];
auto iter = m_model->append(header_row->children());
auto row = *iter;
row[m_columns.m_type] = RenderType::DM;
row[m_columns.m_id] = dm_id;
row[m_columns.m_sort] = -(dm->LastMessageID.has_value() ? *dm->LastMessageID : dm_id);
row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize);
if (dm->Type == ChannelType::DM && top_recipient.has_value())
row[m_columns.m_name] = Glib::Markup::escape_text(top_recipient->Username);
else if (dm->Type == ChannelType::GROUP_DM)
row[m_columns.m_name] = std::to_string(recipients.size()) + " members";
if (top_recipient.has_value()) {
const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) {
if (iter)
(*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR);
};
img.LoadFromURL(top_recipient->GetAvatarURL("png", "32"), sigc::track_obj(cb, *this));
}
}
}
void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) {
auto header_row = m_model->get_iter(m_dm_header);
auto &img = Abaddon::Get().GetImageManager();
std::optional<UserData> top_recipient;
const auto recipients = dm.GetDMRecipients();
if (recipients.size() > 0)
top_recipient = recipients[0];
auto iter = m_model->append(header_row->children());
auto row = *iter;
row[m_columns.m_type] = RenderType::DM;
row[m_columns.m_id] = dm.ID;
row[m_columns.m_sort] = -(dm.LastMessageID.has_value() ? *dm.LastMessageID : dm.ID);
row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize);
if (dm.Type == ChannelType::DM && top_recipient.has_value())
row[m_columns.m_name] = Glib::Markup::escape_text(top_recipient->Username);
else if (dm.Type == ChannelType::GROUP_DM)
row[m_columns.m_name] = std::to_string(recipients.size()) + " members";
if (top_recipient.has_value()) {
const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) {
if (iter)
(*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR);
};
img.LoadFromURL(top_recipient->GetAvatarURL("png", "32"), sigc::track_obj(cb, *this));
}
}
void ChannelList::OnMessageCreate(const Message &msg) {
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;
}
ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() {
@ -390,6 +476,10 @@ void CellRendererChannels::get_preferred_width_vfunc(Gtk::Widget &widget, int &m
return get_preferred_width_vfunc_category(widget, minimum_width, natural_width);
case RenderType::TextChannel:
return get_preferred_width_vfunc_channel(widget, minimum_width, natural_width);
case RenderType::DMHeader:
return get_preferred_width_vfunc_dmheader(widget, minimum_width, natural_width);
case RenderType::DM:
return get_preferred_width_vfunc_dm(widget, minimum_width, natural_width);
}
}
@ -401,6 +491,10 @@ void CellRendererChannels::get_preferred_width_for_height_vfunc(Gtk::Widget &wid
return get_preferred_width_for_height_vfunc_category(widget, height, minimum_width, natural_width);
case RenderType::TextChannel:
return get_preferred_width_for_height_vfunc_channel(widget, height, minimum_width, natural_width);
case RenderType::DMHeader:
return get_preferred_width_for_height_vfunc_dmheader(widget, height, minimum_width, natural_width);
case RenderType::DM:
return get_preferred_width_for_height_vfunc_dm(widget, height, minimum_width, natural_width);
}
}
@ -412,6 +506,10 @@ void CellRendererChannels::get_preferred_height_vfunc(Gtk::Widget &widget, int &
return get_preferred_height_vfunc_category(widget, minimum_height, natural_height);
case RenderType::TextChannel:
return get_preferred_height_vfunc_channel(widget, minimum_height, natural_height);
case RenderType::DMHeader:
return get_preferred_height_vfunc_dmheader(widget, minimum_height, natural_height);
case RenderType::DM:
return get_preferred_height_vfunc_dm(widget, minimum_height, natural_height);
}
}
@ -423,6 +521,10 @@ void CellRendererChannels::get_preferred_height_for_width_vfunc(Gtk::Widget &wid
return get_preferred_height_for_width_vfunc_category(widget, width, minimum_height, natural_height);
case RenderType::TextChannel:
return get_preferred_height_for_width_vfunc_channel(widget, width, minimum_height, natural_height);
case RenderType::DMHeader:
return get_preferred_height_for_width_vfunc_dmheader(widget, width, minimum_height, natural_height);
case RenderType::DM:
return get_preferred_height_for_width_vfunc_dm(widget, width, minimum_height, natural_height);
}
}
@ -434,6 +536,10 @@ void CellRendererChannels::render_vfunc(const Cairo::RefPtr<Cairo::Context> &cr,
return render_vfunc_category(cr, widget, background_area, cell_area, flags);
case RenderType::TextChannel:
return render_vfunc_channel(cr, widget, background_area, cell_area, flags);
case RenderType::DMHeader:
return render_vfunc_dmheader(cr, widget, background_area, cell_area, flags);
case RenderType::DM:
return render_vfunc_dm(cr, widget, background_area, cell_area, flags);
}
}
@ -484,15 +590,15 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context
auto pixbuf = m_property_pixbuf.get_value();
const int icon_x = background_area.get_x();
const int icon_y = background_area.get_y();
const int icon_w = pixbuf->get_width();
const int icon_h = pixbuf->get_height();
const double icon_w = pixbuf->get_width();
const double icon_h = pixbuf->get_height();
const double icon_x = background_area.get_x();
const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - icon_h / 2.0;
const int text_x = icon_x + icon_w + 5;
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;
const double text_x = icon_x + icon_w + 5.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);
@ -593,3 +699,95 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr<Cairo::Conte
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
}
// dm header
void CellRendererChannels::get_preferred_width_vfunc_dmheader(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_dmheader(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_dmheader(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_dmheader(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_dmheader(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
// gdk::rectangle more like gdk::stupid
Gdk::Rectangle text_cell_area(
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);
}
// dm (basically the same thing as guild)
void CellRendererChannels::get_preferred_width_vfunc_dm(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
int pixbuf_width = 0;
if (auto pixbuf = m_property_pixbuf.get_value())
pixbuf_width = pixbuf->get_width();
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 = std::max(text_min, pixbuf_width) + xpad * 2;
natural_width = std::max(text_nat, pixbuf_width) + xpad * 2;
}
void CellRendererChannels::get_preferred_width_for_height_vfunc_dm(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const {
get_preferred_width_vfunc_guild(widget, minimum_width, natural_width);
}
void CellRendererChannels::get_preferred_height_vfunc_dm(Gtk::Widget &widget, int &minimum_height, int &natural_height) const {
int pixbuf_height = 0;
if (auto pixbuf = m_property_pixbuf.get_value())
pixbuf_height = pixbuf->get_height();
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 = std::max(text_min, pixbuf_height) + ypad * 2;
natural_height = std::max(text_nat, pixbuf_height) + ypad * 2;
}
void CellRendererChannels::get_preferred_height_for_width_vfunc_dm(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const {
get_preferred_height_vfunc_guild(widget, minimum_height, natural_height);
}
void CellRendererChannels::render_vfunc_dm(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_minimum, text_natural;
m_renderer_text.get_preferred_size(widget, text_minimum, text_natural);
Gtk::Requisition minimum, natural;
get_preferred_size(widget, minimum, natural);
auto pixbuf = m_property_pixbuf.get_value();
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_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_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);
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
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();
}

View File

@ -9,12 +9,16 @@
#include "../discord/discord.hpp"
constexpr static int GuildIconSize = 24;
constexpr static int DMIconSize = 20;
constexpr static int OrphanChannelSortOffset = -100; // forces orphan channels to the top of the list
enum class RenderType {
Guild,
Category,
TextChannel,
DMHeader,
DM,
};
class CellRendererChannels : public Gtk::CellRenderer {
@ -71,6 +75,28 @@ protected:
const Gdk::Rectangle &cell_area,
Gtk::CellRendererState flags);
// dm header
void get_preferred_width_vfunc_dmheader(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
void get_preferred_width_for_height_vfunc_dmheader(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
void get_preferred_height_vfunc_dmheader(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
void get_preferred_height_for_width_vfunc_dmheader(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
void render_vfunc_dmheader(const Cairo::RefPtr<Cairo::Context> &cr,
Gtk::Widget &widget,
const Gdk::Rectangle &background_area,
const Gdk::Rectangle &cell_area,
Gtk::CellRendererState flags);
// dm
void get_preferred_width_vfunc_dm(Gtk::Widget &widget, int &minimum_width, int &natural_width) const;
void get_preferred_width_for_height_vfunc_dm(Gtk::Widget &widget, int height, int &minimum_width, int &natural_width) const;
void get_preferred_height_vfunc_dm(Gtk::Widget &widget, int &minimum_height, int &natural_height) const;
void get_preferred_height_for_width_vfunc_dm(Gtk::Widget &widget, int width, int &minimum_height, int &natural_height) const;
void render_vfunc_dm(const Cairo::RefPtr<Cairo::Context> &cr,
Gtk::Widget &widget,
const Gdk::Rectangle &background_area,
const Gdk::Rectangle &cell_area,
Gtk::CellRendererState flags);
private:
Gtk::CellRendererText m_renderer_text;
@ -106,7 +132,7 @@ protected:
Gtk::TreeModelColumn<uint64_t> m_id;
Gtk::TreeModelColumn<Glib::ustring> m_name;
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_icon;
Gtk::TreeModelColumn<int> m_sort;
Gtk::TreeModelColumn<int64_t> m_sort;
// 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
@ -134,6 +160,12 @@ protected:
bool SelectionFunc(const Glib::RefPtr<Gtk::TreeModel> &model, const Gtk::TreeModel::Path &path, bool is_currently_selected);
Gtk::TreeModel::Path m_last_selected;
Gtk::TreeModel::Path m_dm_header;
void AddPrivateChannels();
void UpdateCreateDMChannel(const ChannelData &channel);
void OnMessageCreate(const Message &msg);
public:
typedef sigc::signal<void, Snowflake> type_signal_action_channel_item_select;