start channel list refactor for server list

This commit is contained in:
ouwou 2023-10-16 21:31:28 -04:00
parent 1895a8f133
commit 3e0cd83dd6
12 changed files with 152 additions and 67 deletions

View File

@ -1,4 +1,4 @@
#include "channelscellrenderer.hpp"
#include "cellrendererchannels.hpp"
constexpr static int MentionsRightPad = 7;
#ifndef M_PI

View File

@ -1,6 +1,6 @@
#include "channels.hpp"
#include "channellist.hpp"
#include "imgmanager.hpp"
#include "statusindicator.hpp"
#include "components/statusindicator.hpp"
#include <algorithm>
#include <map>
#include <unordered_map>
@ -8,6 +8,7 @@
ChannelList::ChannelList()
: Glib::ObjectBase(typeid(ChannelList))
, m_model(Gtk::TreeStore::create(m_columns))
, m_filter_model(Gtk::TreeModelFilter::create(m_model))
, m_menu_guild_copy_id("_Copy ID", true)
, m_menu_guild_settings("View _Settings", true)
, m_menu_guild_leave("_Leave", true)
@ -36,9 +37,9 @@ ChannelList::ChannelList()
, m_menu_thread_mark_as_read("Mark as _Read", true) {
get_style_context()->add_class("channel-list");
// todo: move to method
// Filter iters
const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) {
auto row = *m_model->get_iter(path);
auto row = *m_filter_model->get_iter(path);
const auto type = row[m_columns.m_type];
// text channels should not be allowed to be collapsed
// maybe they should be but it seems a little difficult to handle expansion to permit this
@ -72,13 +73,22 @@ ChannelList::ChannelList()
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_view.set_model(m_filter_model);
m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING);
m_model->signal_row_inserted().connect([this](const Gtk::TreeModel::Path &path, const Gtk::TreeModel::iterator &iter) {
if (m_updating_listing) return;
if (auto parent = iter->parent(); parent && (*parent)[m_columns.m_expanded])
m_view.expand_row(m_model->get_path(parent), false);
if (auto parent = iter->parent(); parent && (*parent)[m_columns.m_expanded]) {
const auto filter_path = m_filter_model->convert_child_path_to_path(m_model->get_path(parent));
m_view.expand_row(filter_path, false);
}
});
m_filter_model->set_visible_func([this](const Gtk::TreeModel::const_iterator &iter) -> bool {
if ((*iter)[m_columns.m_type] == RenderType::Guild) {
return (*iter)[m_columns.m_id] == 754921263616753776ULL;
}
return true;
});
m_view.show();
@ -594,9 +604,9 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) {
const auto channel_iter = GetIteratorForRowFromID(id);
if (channel_iter) {
if (expand_to) {
m_view.expand_to_path(m_model->get_path(channel_iter));
m_view.expand_to_path(m_filter_model->convert_child_path_to_path(m_model->get_path(channel_iter)));
}
m_view.get_selection()->select(channel_iter);
m_view.get_selection()->select(m_filter_model->convert_child_iter_to_iter(channel_iter));
} else {
m_view.get_selection()->unselect_all();
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
@ -604,64 +614,17 @@ void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) {
auto parent_iter = GetIteratorForRowFromID(*channel->ParentID);
if (!parent_iter) return;
m_temporary_thread_row = CreateThreadRow(parent_iter->children(), *channel);
m_view.get_selection()->select(m_temporary_thread_row);
m_view.get_selection()->select(m_filter_model->convert_child_iter_to_iter(m_temporary_thread_row));
}
}
void ChannelList::UseExpansionState(const ExpansionStateRoot &root) {
auto recurse = [this](auto &self, const ExpansionStateRoot &root) -> void {
for (const auto &[id, state] : root.Children) {
Gtk::TreeModel::iterator row_iter;
if (const auto map_iter = m_tmp_row_map.find(id); map_iter != m_tmp_row_map.end()) {
row_iter = map_iter->second;
} else if (const auto map_iter = m_tmp_guild_row_map.find(id); map_iter != m_tmp_guild_row_map.end()) {
row_iter = map_iter->second;
}
if (row_iter) {
if (state.IsExpanded)
m_view.expand_row(m_model->get_path(row_iter), false);
else
m_view.collapse_row(m_model->get_path(row_iter));
}
self(self, state.Children);
}
};
for (const auto &[id, state] : root.Children) {
if (const auto iter = GetIteratorForTopLevelFromID(id)) {
if (state.IsExpanded)
m_view.expand_row(m_model->get_path(iter), false);
else
m_view.collapse_row(m_model->get_path(iter));
}
recurse(recurse, state.Children);
}
m_tmp_row_map.clear();
}
ExpansionStateRoot ChannelList::GetExpansionState() const {
ExpansionStateRoot r;
auto recurse = [this](auto &self, const Gtk::TreeRow &row) -> ExpansionState {
ExpansionState r;
r.IsExpanded = row[m_columns.m_expanded];
for (const auto &child : row.children())
r.Children.Children[static_cast<Snowflake>(child[m_columns.m_id])] = self(self, child);
return r;
};
for (const auto &child : m_model->children()) {
const auto id = static_cast<Snowflake>(child[m_columns.m_id]);
if (static_cast<uint64_t>(id) == 0ULL) continue; // dont save DM header
r.Children[id] = recurse(recurse, child);
}
return r;
}
@ -969,7 +932,7 @@ void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk:
// restore previous expansion
for (auto it = iter->children().begin(); it != iter->children().end(); it++) {
if ((*it)[m_columns.m_expanded])
m_view.expand_row(m_model->get_path(it), false);
m_view.expand_row(m_filter_model->get_path(it), false);
}
// try and restore selection if previous collapsed
@ -981,11 +944,13 @@ void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk:
}
bool ChannelList::SelectionFunc(const Glib::RefPtr<Gtk::TreeModel> &model, const Gtk::TreeModel::Path &path, bool is_currently_selected) {
if (auto selection = m_view.get_selection())
if (auto row = selection->get_selected())
m_last_selected = m_model->get_path(row);
if (auto selection = m_view.get_selection()) {
if (auto row = selection->get_selected()) {
m_last_selected = m_filter_model->get_path(row);
}
}
auto type = (*m_model->get_iter(path))[m_columns.m_type];
auto type = (*model->get_iter(path))[m_columns.m_type];
return type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread;
}
@ -1139,7 +1104,7 @@ void ChannelList::OnMessageCreate(const Message &msg) {
bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) {
if (ev->button == GDK_BUTTON_SECONDARY && ev->type == GDK_BUTTON_PRESS) {
if (m_view.get_path_at_pos(static_cast<int>(ev->x), static_cast<int>(ev->y), m_path_for_menu)) {
auto row = (*m_model->get_iter(m_path_for_menu));
auto row = (*m_filter_model->get_iter(m_path_for_menu));
switch (static_cast<RenderType>(row[m_columns.m_type])) {
case RenderType::Guild:
OnGuildSubmenuPopup();

View File

@ -8,11 +8,12 @@
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/treemodel.h>
#include <gtkmm/treestore.h>
#include <gtkmm/treemodelfilter.h>
#include <gtkmm/treeview.h>
#include <sigc++/sigc++.h>
#include "discord/discord.hpp"
#include "state.hpp"
#include "channelscellrenderer.hpp"
#include "cellrendererchannels.hpp"
constexpr static int GuildIconSize = 24;
constexpr static int DMIconSize = 20;
@ -82,6 +83,7 @@ protected:
ModelColumns m_columns;
Glib::RefPtr<Gtk::TreeStore> m_model;
Glib::RefPtr<Gtk::TreeModelFilter> m_filter_model;
Gtk::TreeModel::iterator AddFolder(const UserSettingsGuildFoldersEntry &folder);
Gtk::TreeModel::iterator AddGuild(const GuildData &guild, const Gtk::TreeNodeChildren &root);
@ -116,7 +118,7 @@ protected:
void UpdateCreateDMChannel(const ChannelData &channel);
void SetDMChannelIcon(Gtk::TreeIter iter, const ChannelData &dm);
void RedrawUnreadIndicatorsForChannel(const ChannelData& channel);
void RedrawUnreadIndicatorsForChannel(const ChannelData &channel);
void OnMessageAck(const MessageAckData &data);
void OnMessageCreate(const Message &msg);

View File

@ -0,0 +1,23 @@
#include "guildlist.hpp"
#include "guildlistfolderitem.hpp"
GuildList::GuildList() {
set_selection_mode(Gtk::SELECTION_NONE);
show_all_children();
}
void GuildList::AddGuild(Snowflake id) {
const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id);
if (!guild.has_value()) return;
auto *item = Gtk::make_managed<GuildListGuildItem>(*guild);
item->show();
add(*item);
}
void GuildList::Clear() {
const auto children = get_children();
for (auto child : children) {
delete child;
}
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <gtkmm/listbox.h>
#include "discord/snowflake.hpp"
class GuildList : public Gtk::ListBox {
public:
GuildList();
void AddGuild(Snowflake id);
void Clear();
};

View File

@ -0,0 +1,18 @@
#include "guildlistfolderitem.hpp"
GuildListFolderItem::GuildListFolderItem() {
m_revealer.add(m_box);
m_revealer.set_reveal_child(true);
m_ev.signal_button_press_event().connect([this](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) {
m_revealer.set_reveal_child(!m_revealer.get_reveal_child());
}
return false;
});
m_ev.add(m_image);
add(m_ev);
add(m_revealer);
show_all_children();
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <gtkmm/box.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/image.h>
#include <gtkmm/revealer.h>
#include "guildlistguilditem.hpp"
class GuildListFolderItem : public Gtk::VBox {
public:
GuildListFolderItem();
private:
Gtk::EventBox m_ev;
Gtk::Image m_image;
Gtk::Revealer m_revealer;
Gtk::VBox m_box;
};

View File

@ -0,0 +1,29 @@
#include "guildlistguilditem.hpp"
GuildListGuildItem::GuildListGuildItem(const GuildData &guild)
: ID(guild.ID) {
m_image.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(48);
add(m_image);
show_all_children();
signal_button_press_event().connect([this](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) {
printf("Click %llu\n", (uint64_t)ID);
}
return true;
});
set_tooltip_text(guild.Name);
UpdateIcon();
}
void GuildListGuildItem::UpdateIcon() {
const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(ID);
if (!guild.has_value()) return;
Abaddon::Get().GetImageManager().LoadFromURL(guild->GetIconURL("png", "64"), sigc::mem_fun(*this, &GuildListGuildItem::OnIconFetched));
}
void GuildListGuildItem::OnIconFetched(const Glib::RefPtr<Gdk::Pixbuf> &pb) {
m_image.property_pixbuf() = pb->scale_simple(48, 48, Gdk::INTERP_BILINEAR);
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <gtkmm/box.h>
#include <gtkmm/image.h>
#include "discord/guild.hpp"
class GuildListGuildItem : public Gtk::EventBox {
public:
GuildListGuildItem(const GuildData &guild);
Snowflake ID;
private:
void UpdateIcon();
void OnIconFetched(const Glib::RefPtr<Gdk::Pixbuf> &pb);
Gtk::Image m_image;
};

View File

@ -1,4 +1,5 @@
#include "mainwindow.hpp"
#include "components/channellist/channellist.hpp"
MainWindow::MainWindow()
: m_main_box(Gtk::ORIENTATION_VERTICAL)

View File

@ -1,5 +1,5 @@
#pragma once
#include "components/channels.hpp"
#include "components/channellist/channellist.hpp"
#include "components/chatwindow.hpp"
#include "components/memberlist.hpp"
#include "components/friendslist.hpp"