reimplement status indicator in member list
This commit is contained in:
@@ -199,11 +199,6 @@ spam filter's wrath:
|
|||||||
| `.embed-field-value` | The value of an embed field |
|
| `.embed-field-value` | The value of an embed field |
|
||||||
| `.embed-footer` | The footer of an embed |
|
| `.embed-footer` | The footer of an embed |
|
||||||
| `.member-list` | Container of the member list |
|
| `.member-list` | Container of the member list |
|
||||||
| `.status-indicator` | The status indicator |
|
|
||||||
| `.online` | Applied to status indicators when the associated user is online |
|
|
||||||
| `.idle` | Applied to status indicators when the associated user is away |
|
|
||||||
| `.dnd` | Applied to status indicators when the associated user is on do not disturb |
|
|
||||||
| `.offline` | Applied to status indicators when the associated user is offline |
|
|
||||||
| `.typing-indicator` | The typing indicator (also used for replies) |
|
| `.typing-indicator` | The typing indicator (also used for replies) |
|
||||||
|
|
||||||
Used in reorderable list implementation:
|
Used in reorderable list implementation:
|
||||||
|
@@ -6,7 +6,8 @@ CellRendererMemberList::CellRendererMemberList()
|
|||||||
, m_property_id(*this, "id")
|
, m_property_id(*this, "id")
|
||||||
, m_property_name(*this, "name")
|
, m_property_name(*this, "name")
|
||||||
, m_property_pixbuf(*this, "pixbuf")
|
, m_property_pixbuf(*this, "pixbuf")
|
||||||
, m_property_color(*this, "color") {
|
, m_property_color(*this, "color")
|
||||||
|
, m_property_status(*this, "status") {
|
||||||
property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
|
property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
|
||||||
property_xpad() = 2;
|
property_xpad() = 2;
|
||||||
property_ypad() = 2;
|
property_ypad() = 2;
|
||||||
@@ -35,6 +36,10 @@ Glib::PropertyProxy<Gdk::RGBA> CellRendererMemberList::property_color() {
|
|||||||
return m_property_color.get_proxy();
|
return m_property_color.get_proxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Glib::PropertyProxy<PresenceStatus> CellRendererMemberList::property_status() {
|
||||||
|
return m_property_status.get_proxy();
|
||||||
|
}
|
||||||
|
|
||||||
void CellRendererMemberList::get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
|
void CellRendererMemberList::get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const {
|
||||||
switch (m_property_type.get_value()) {
|
switch (m_property_type.get_value()) {
|
||||||
case MemberListRenderType::Role:
|
case MemberListRenderType::Role:
|
||||||
@@ -117,8 +122,9 @@ void CellRendererMemberList::get_preferred_height_for_width_vfunc_member(Gtk::Wi
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CellRendererMemberList::render_vfunc_member(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
|
void CellRendererMemberList::render_vfunc_member(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, const Gdk::Rectangle &background_area, const Gdk::Rectangle &cell_area, Gtk::CellRendererState flags) {
|
||||||
|
// Text
|
||||||
Gdk::Rectangle text_cell_area = cell_area;
|
Gdk::Rectangle text_cell_area = cell_area;
|
||||||
text_cell_area.set_x(22);
|
text_cell_area.set_x(31);
|
||||||
const auto color = m_property_color.get_value();
|
const auto color = m_property_color.get_value();
|
||||||
if (color.get_alpha_u() > 0) {
|
if (color.get_alpha_u() > 0) {
|
||||||
m_renderer_text.property_foreground_rgba().set_value(color);
|
m_renderer_text.property_foreground_rgba().set_value(color);
|
||||||
@@ -126,6 +132,30 @@ void CellRendererMemberList::render_vfunc_member(const Cairo::RefPtr<Cairo::Cont
|
|||||||
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
||||||
m_renderer_text.property_foreground_set().set_value(false);
|
m_renderer_text.property_foreground_set().set_value(false);
|
||||||
|
|
||||||
|
// Status indicator
|
||||||
|
// TODO: reintroduce custom status colors... somehow
|
||||||
|
cr->begin_new_path();
|
||||||
|
switch (m_property_status.get_value()) {
|
||||||
|
case PresenceStatus::Online:
|
||||||
|
cr->set_source_rgb(33.0 / 255.0, 157.0 / 255.0, 86.0 / 255.0);
|
||||||
|
break;
|
||||||
|
case PresenceStatus::Idle:
|
||||||
|
cr->set_source_rgb(230.0 / 255.0, 170.0 / 255.0, 48.0 / 255.0);
|
||||||
|
break;
|
||||||
|
case PresenceStatus::DND:
|
||||||
|
cr->set_source_rgb(233.0 / 255.0, 61.0 / 255.0, 65.0 / 255.0);
|
||||||
|
break;
|
||||||
|
case PresenceStatus::Offline:
|
||||||
|
cr->set_source_rgb(122.0 / 255.0, 126.0 / 255.0, 135.0 / 255.0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cr->arc(background_area.get_x() + 6.0 + 16.0 + 6.0, background_area.get_y() + background_area.get_height() / 2.0, 2.0, 0.0, 2 * (4 * std::atan(1)));
|
||||||
|
cr->close_path();
|
||||||
|
cr->fill_preserve();
|
||||||
|
cr->stroke();
|
||||||
|
|
||||||
|
// Icon
|
||||||
const double icon_x = background_area.get_x() + 6.0;
|
const double icon_x = background_area.get_x() + 6.0;
|
||||||
const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - 8.0;
|
const double icon_y = background_area.get_y() + background_area.get_height() / 2.0 - 8.0;
|
||||||
Gdk::Cairo::set_source_pixbuf(cr, m_property_pixbuf.get_value(), icon_x, icon_y);
|
Gdk::Cairo::set_source_pixbuf(cr, m_property_pixbuf.get_value(), icon_x, icon_y);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <gtkmm/cellrenderer.h>
|
#include <gtkmm/cellrenderer.h>
|
||||||
|
#include "discord/activity.hpp"
|
||||||
|
|
||||||
enum class MemberListRenderType : uint8_t {
|
enum class MemberListRenderType : uint8_t {
|
||||||
Role,
|
Role,
|
||||||
@@ -16,6 +17,7 @@ public:
|
|||||||
Glib::PropertyProxy<Glib::ustring> property_name();
|
Glib::PropertyProxy<Glib::ustring> property_name();
|
||||||
Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf>> property_pixbuf();
|
Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf>> property_pixbuf();
|
||||||
Glib::PropertyProxy<Gdk::RGBA> property_color();
|
Glib::PropertyProxy<Gdk::RGBA> property_color();
|
||||||
|
Glib::PropertyProxy<PresenceStatus> property_status();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const override;
|
void get_preferred_width_vfunc(Gtk::Widget &widget, int &minimum_width, int &natural_width) const override;
|
||||||
@@ -56,6 +58,7 @@ private:
|
|||||||
Glib::Property<Glib::ustring> m_property_name;
|
Glib::Property<Glib::ustring> m_property_name;
|
||||||
Glib::Property<Glib::RefPtr<Gdk::Pixbuf>> m_property_pixbuf;
|
Glib::Property<Glib::RefPtr<Gdk::Pixbuf>> m_property_pixbuf;
|
||||||
Glib::Property<Gdk::RGBA> m_property_color;
|
Glib::Property<Gdk::RGBA> m_property_color;
|
||||||
|
Glib::Property<PresenceStatus> m_property_status;
|
||||||
|
|
||||||
using type_signal_render = sigc::signal<void(uint64_t)>;
|
using type_signal_render = sigc::signal<void(uint64_t)>;
|
||||||
type_signal_render m_signal_render;
|
type_signal_render m_signal_render;
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
#include "channels.hpp"
|
#include "channels.hpp"
|
||||||
#include "imgmanager.hpp"
|
#include "imgmanager.hpp"
|
||||||
#include "statusindicator.hpp"
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@@ -28,6 +28,7 @@ MemberList::MemberList()
|
|||||||
column->add_attribute(renderer->property_name(), m_columns.m_name);
|
column->add_attribute(renderer->property_name(), m_columns.m_name);
|
||||||
column->add_attribute(renderer->property_pixbuf(), m_columns.m_pixbuf);
|
column->add_attribute(renderer->property_pixbuf(), m_columns.m_pixbuf);
|
||||||
column->add_attribute(renderer->property_color(), m_columns.m_color);
|
column->add_attribute(renderer->property_color(), m_columns.m_color);
|
||||||
|
column->add_attribute(renderer->property_status(), m_columns.m_status);
|
||||||
m_view.append_column(*column);
|
m_view.append_column(*column);
|
||||||
|
|
||||||
m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING);
|
m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING);
|
||||||
@@ -44,6 +45,8 @@ MemberList::MemberList()
|
|||||||
m_menu_role_copy_id.signal_activate().connect([this]() {
|
m_menu_role_copy_id.signal_activate().connect([this]() {
|
||||||
Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
|
Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Abaddon::Get().GetDiscordClient().signal_presence_update().connect(sigc::mem_fun(*this, &MemberList::OnPresenceUpdate));
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk::Widget *MemberList::GetRoot() {
|
Gtk::Widget *MemberList::GetRoot() {
|
||||||
@@ -73,6 +76,7 @@ void MemberList::UpdateMemberList() {
|
|||||||
row[m_columns.m_color] = color_transparent;
|
row[m_columns.m_color] = color_transparent;
|
||||||
row[m_columns.m_av_requested] = false;
|
row[m_columns.m_av_requested] = false;
|
||||||
row[m_columns.m_pixbuf] = Abaddon::Get().GetImageManager().GetPlaceholder(16);
|
row[m_columns.m_pixbuf] = Abaddon::Get().GetImageManager().GetPlaceholder(16);
|
||||||
|
row[m_columns.m_status] = Abaddon::Get().GetDiscordClient().GetUserStatus(user.ID);
|
||||||
m_pending_avatars[user.ID] = row_iter;
|
m_pending_avatars[user.ID] = row_iter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,6 +130,7 @@ void MemberList::UpdateMemberList() {
|
|||||||
row[m_columns.m_id] = user.ID;
|
row[m_columns.m_id] = user.ID;
|
||||||
row[m_columns.m_name] = user.GetDisplayNameEscaped();
|
row[m_columns.m_name] = user.GetDisplayNameEscaped();
|
||||||
row[m_columns.m_pixbuf] = Abaddon::Get().GetImageManager().GetPlaceholder(16);
|
row[m_columns.m_pixbuf] = Abaddon::Get().GetImageManager().GetPlaceholder(16);
|
||||||
|
row[m_columns.m_status] = Abaddon::Get().GetDiscordClient().GetUserStatus(user.ID);
|
||||||
row[m_columns.m_av_requested] = false;
|
row[m_columns.m_av_requested] = false;
|
||||||
if (const auto iter = user_to_color.find(user.ID); iter != user_to_color.end()) {
|
if (const auto iter = user_to_color.find(user.ID); iter != user_to_color.end()) {
|
||||||
row[m_columns.m_color] = IntToRGBA(iter->second);
|
row[m_columns.m_color] = IntToRGBA(iter->second);
|
||||||
@@ -241,12 +246,24 @@ int MemberList::SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemberList::OnPresenceUpdate(const UserData &user, PresenceStatus status) {
|
||||||
|
for (auto &role : m_model->children()) {
|
||||||
|
for (auto &member : role.children()) {
|
||||||
|
if ((*member)[m_columns.m_id] == user.ID) {
|
||||||
|
(*member)[m_columns.m_status] = status;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MemberList::ModelColumns::ModelColumns() {
|
MemberList::ModelColumns::ModelColumns() {
|
||||||
add(m_type);
|
add(m_type);
|
||||||
add(m_id);
|
add(m_id);
|
||||||
add(m_name);
|
add(m_name);
|
||||||
add(m_pixbuf);
|
add(m_pixbuf);
|
||||||
add(m_av_requested);
|
|
||||||
add(m_color);
|
add(m_color);
|
||||||
|
add(m_status);
|
||||||
add(m_sort);
|
add(m_sort);
|
||||||
|
add(m_av_requested);
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,8 @@ private:
|
|||||||
|
|
||||||
int SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b);
|
int SortFunc(const Gtk::TreeModel::iterator &a, const Gtk::TreeModel::iterator &b);
|
||||||
|
|
||||||
|
void OnPresenceUpdate(const UserData &user, PresenceStatus status);
|
||||||
|
|
||||||
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
|
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
|
||||||
public:
|
public:
|
||||||
ModelColumns();
|
ModelColumns();
|
||||||
@@ -35,6 +37,7 @@ private:
|
|||||||
Gtk::TreeModelColumn<Glib::ustring> m_name;
|
Gtk::TreeModelColumn<Glib::ustring> m_name;
|
||||||
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_pixbuf;
|
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_pixbuf;
|
||||||
Gtk::TreeModelColumn<Gdk::RGBA> m_color;
|
Gtk::TreeModelColumn<Gdk::RGBA> m_color;
|
||||||
|
Gtk::TreeModelColumn<PresenceStatus> m_status;
|
||||||
Gtk::TreeModelColumn<int> m_sort;
|
Gtk::TreeModelColumn<int> m_sort;
|
||||||
|
|
||||||
Gtk::TreeModelColumn<bool> m_av_requested;
|
Gtk::TreeModelColumn<bool> m_av_requested;
|
||||||
|
@@ -1,122 +0,0 @@
|
|||||||
#include "statusindicator.hpp"
|
|
||||||
|
|
||||||
static const constexpr int Diameter = 8;
|
|
||||||
|
|
||||||
StatusIndicator::StatusIndicator(Snowflake user_id)
|
|
||||||
: Glib::ObjectBase("statusindicator")
|
|
||||||
, Gtk::Widget()
|
|
||||||
, m_id(user_id)
|
|
||||||
, m_status(static_cast<PresenceStatus>(-1)) {
|
|
||||||
set_has_window(true);
|
|
||||||
set_name("status-indicator");
|
|
||||||
|
|
||||||
get_style_context()->add_class("status-indicator");
|
|
||||||
|
|
||||||
Abaddon::Get().GetDiscordClient().signal_guild_member_list_update().connect(sigc::hide(sigc::mem_fun(*this, &StatusIndicator::CheckStatus)));
|
|
||||||
auto cb = [this](const UserData &user, PresenceStatus status) {
|
|
||||||
if (user.ID == m_id) CheckStatus();
|
|
||||||
};
|
|
||||||
Abaddon::Get().GetDiscordClient().signal_presence_update().connect(sigc::track_obj(cb, *this));
|
|
||||||
|
|
||||||
CheckStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::CheckStatus() {
|
|
||||||
const auto status = Abaddon::Get().GetDiscordClient().GetUserStatus(m_id);
|
|
||||||
const auto last_status = m_status;
|
|
||||||
get_style_context()->remove_class("online");
|
|
||||||
get_style_context()->remove_class("dnd");
|
|
||||||
get_style_context()->remove_class("idle");
|
|
||||||
get_style_context()->remove_class("offline");
|
|
||||||
get_style_context()->add_class(GetPresenceString(status));
|
|
||||||
m_status = status;
|
|
||||||
|
|
||||||
if (last_status != m_status)
|
|
||||||
queue_draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk::SizeRequestMode StatusIndicator::get_request_mode_vfunc() const {
|
|
||||||
return Gtk::Widget::get_request_mode_vfunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::get_preferred_width_vfunc(int &minimum_width, int &natural_width) const {
|
|
||||||
minimum_width = 0;
|
|
||||||
natural_width = Diameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::get_preferred_height_for_width_vfunc(int width, int &minimum_height, int &natural_height) const {
|
|
||||||
minimum_height = 0;
|
|
||||||
natural_height = Diameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::get_preferred_height_vfunc(int &minimum_height, int &natural_height) const {
|
|
||||||
minimum_height = 0;
|
|
||||||
natural_height = Diameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::get_preferred_width_for_height_vfunc(int height, int &minimum_width, int &natural_width) const {
|
|
||||||
minimum_width = 0;
|
|
||||||
natural_width = Diameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::on_size_allocate(Gtk::Allocation &allocation) {
|
|
||||||
set_allocation(allocation);
|
|
||||||
|
|
||||||
if (m_window)
|
|
||||||
m_window->move_resize(allocation.get_x(), allocation.get_y(), allocation.get_width(), allocation.get_height());
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::on_map() {
|
|
||||||
Gtk::Widget::on_map();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::on_unmap() {
|
|
||||||
Gtk::Widget::on_unmap();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::on_realize() {
|
|
||||||
set_realized(true);
|
|
||||||
|
|
||||||
if (!m_window) {
|
|
||||||
GdkWindowAttr attributes;
|
|
||||||
std::memset(&attributes, 0, sizeof(attributes));
|
|
||||||
|
|
||||||
auto allocation = get_allocation();
|
|
||||||
|
|
||||||
attributes.x = allocation.get_x();
|
|
||||||
attributes.y = allocation.get_y();
|
|
||||||
attributes.width = allocation.get_width();
|
|
||||||
attributes.height = allocation.get_height();
|
|
||||||
|
|
||||||
attributes.event_mask = get_events() | Gdk::EXPOSURE_MASK;
|
|
||||||
attributes.window_type = GDK_WINDOW_CHILD;
|
|
||||||
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
||||||
|
|
||||||
m_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
|
|
||||||
set_window(m_window);
|
|
||||||
|
|
||||||
m_window->set_user_data(gobj());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusIndicator::on_unrealize() {
|
|
||||||
m_window.reset();
|
|
||||||
|
|
||||||
Gtk::Widget::on_unrealize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool StatusIndicator::on_draw(const Cairo::RefPtr<Cairo::Context> &cr) {
|
|
||||||
const auto allocation = get_allocation();
|
|
||||||
const auto width = allocation.get_width();
|
|
||||||
const auto height = allocation.get_height();
|
|
||||||
|
|
||||||
const auto color = get_style_context()->get_color(Gtk::STATE_FLAG_NORMAL);
|
|
||||||
|
|
||||||
cr->set_source_rgb(color.get_red(), color.get_green(), color.get_blue());
|
|
||||||
cr->arc(width / 2.0, height / 2.0, width / 3.0, 0.0, 2 * (4 * std::atan(1)));
|
|
||||||
cr->close_path();
|
|
||||||
cr->fill_preserve();
|
|
||||||
cr->stroke();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "discord/snowflake.hpp"
|
|
||||||
#include "discord/activity.hpp"
|
|
||||||
|
|
||||||
class StatusIndicator : public Gtk::Widget {
|
|
||||||
public:
|
|
||||||
StatusIndicator(Snowflake user_id);
|
|
||||||
~StatusIndicator() override = default;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Gtk::SizeRequestMode get_request_mode_vfunc() const override;
|
|
||||||
void get_preferred_width_vfunc(int &minimum_width, int &natural_width) const override;
|
|
||||||
void get_preferred_width_for_height_vfunc(int height, int &minimum_width, int &natural_width) const override;
|
|
||||||
void get_preferred_height_vfunc(int &minimum_height, int &natural_height) const override;
|
|
||||||
void get_preferred_height_for_width_vfunc(int width, int &minimum_height, int &natural_height) const override;
|
|
||||||
void on_size_allocate(Gtk::Allocation &allocation) override;
|
|
||||||
void on_map() override;
|
|
||||||
void on_unmap() override;
|
|
||||||
void on_realize() override;
|
|
||||||
void on_unrealize() override;
|
|
||||||
bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) override;
|
|
||||||
|
|
||||||
Glib::RefPtr<Gdk::Window> m_window;
|
|
||||||
|
|
||||||
void CheckStatus();
|
|
||||||
|
|
||||||
Snowflake m_id;
|
|
||||||
PresenceStatus m_status;
|
|
||||||
};
|
|
Reference in New Issue
Block a user