Guild settings 1 (#19)

* start guild settings window, tweak style
This commit is contained in:
ouwou 2021-01-15 06:37:35 +00:00 committed by GitHub
parent f51ab48009
commit 462f801af2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 442 additions and 4 deletions

View File

@ -43,6 +43,8 @@ jobs:
del /f /s /q "${{ runner.workspace }}\build\CMakeCache.txt"
xcopy /E /I "${{ github.workspace }}\css" "${{ runner.workspace }}\build\css"
xcopy /E /I "${{ github.workspace }}\res" "${{ runner.workspace }}\build\res"
mkdir "${{ runner.workspace }}\build\share"
xcopy /E /I "${{ github.workspace }}\ci\gtk-for-windows\gtk-nsis-pack\share\glib-2.0" "${{ runner.workspace }}\build\share\glib-2.0"
copy "${{ github.workspace }}\ci\vcpkg\installed\x64-windows\tools\glib\gspawn-win64-helper.exe" "${{ runner.workspace }}\build\gspawn-win64-helper.exe"
copy "${{ github.workspace }}\ci\vcpkg\installed\x64-windows\tools\glib\gspawn-win64-helper-console.exe" "${{ runner.workspace }}\build\gspawn-win64-helper-console.exe"

3
.gitmodules vendored
View File

@ -10,3 +10,6 @@
[submodule "ci/vcpkg"]
path = ci/vcpkg
url = https://github.com/microsoft/vcpkg
[submodule "ci/gtk-for-windows"]
path = ci/gtk-for-windows
url = https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer

View File

@ -43,6 +43,8 @@ file(GLOB ABADDON_SOURCES
"components/*.cpp"
"windows/*.hpp"
"windows/*.cpp"
"windows/guildsettings/*.hpp"
"windows/guildsettings/*.cpp"
"dialogs/*.hpp"
"dialogs/*.cpp"
)

View File

@ -9,6 +9,7 @@
#include "dialogs/confirm.hpp"
#include "dialogs/setstatus.hpp"
#include "abaddon.hpp"
#include "windows/guildsettingswindow.hpp"
#ifdef _WIN32
#pragma comment(lib, "crypt32.lib")
@ -105,6 +106,7 @@ int Abaddon::StartGTK() {
m_main_window->GetChannelList()->signal_action_channel_item_select().connect(sigc::mem_fun(*this, &Abaddon::ActionChannelOpened));
m_main_window->GetChannelList()->signal_action_guild_leave().connect(sigc::mem_fun(*this, &Abaddon::ActionLeaveGuild));
m_main_window->GetChannelList()->signal_action_guild_settings().connect(sigc::mem_fun(*this, &Abaddon::ActionGuildSettings));
m_main_window->GetChatWindow()->signal_action_message_delete().connect(sigc::mem_fun(*this, &Abaddon::ActionChatDeleteMessage));
m_main_window->GetChatWindow()->signal_action_message_edit().connect(sigc::mem_fun(*this, &Abaddon::ActionChatEditMessage));
@ -231,6 +233,10 @@ const SettingsManager &Abaddon::GetSettings() const {
return m_settings;
}
Glib::RefPtr<Gtk::CssProvider> Abaddon::GetStyleProvider() {
return m_css_provider;
}
void Abaddon::ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_id) {
m_shown_user_menu_id = id;
m_shown_user_menu_guild_id = guild_id;
@ -455,6 +461,11 @@ void Abaddon::ActionReactionRemove(Snowflake id, const Glib::ustring &param) {
m_discord.RemoveReaction(id, param);
}
void Abaddon::ActionGuildSettings(Snowflake id) {
auto *window = new GuildSettingsWindow(id);
window->show();
}
void Abaddon::ActionReloadSettings() {
m_settings.Reload();
}

View File

@ -45,6 +45,7 @@ public:
void ActionSetStatus();
void ActionReactionAdd(Snowflake id, const Glib::ustring &param);
void ActionReactionRemove(Snowflake id, const Glib::ustring &param);
void ActionGuildSettings(Snowflake id);
void ActionReloadSettings();
void ActionReloadCSS();
@ -73,6 +74,8 @@ public:
const SettingsManager &GetSettings() const;
Glib::RefPtr<Gtk::CssProvider> GetStyleProvider();
protected:
Snowflake m_shown_user_menu_id;
Snowflake m_shown_user_menu_guild_id;

1
ci/gtk-for-windows Submodule

@ -0,0 +1 @@
Subproject commit 27bc126a0d9f1a3ac46c6cd7ce2e6b9eea813127

View File

@ -117,6 +117,12 @@ ChannelListRowGuild::ChannelListRowGuild(const GuildData *data) {
});
m_menu.append(*m_menu_leave);
m_menu_settings = Gtk::manage(new Gtk::MenuItem("Guild _Settings", true));
m_menu_settings->signal_activate().connect([this]() {
m_signal_settings.emit();
});
m_menu.append(*m_menu_settings);
m_menu.show_all();
const auto show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
@ -176,6 +182,10 @@ ChannelListRowGuild::type_signal_leave ChannelListRowGuild::signal_leave() {
return m_signal_leave;
}
ChannelListRowGuild::type_signal_settings ChannelListRowGuild::signal_settings() {
return m_signal_settings;
}
ChannelListRowCategory::ChannelListRowCategory(const ChannelData *data) {
ID = data->ID;
m_ev = Gtk::manage(new Gtk::EventBox);
@ -519,6 +529,7 @@ void ChannelList::UpdateGuild(Snowflake id) {
m_guild_id_to_row[new_row->ID] = new_row;
new_row->signal_leave().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnGuildMenuLeave), new_row->ID));
new_row->signal_copy_id().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnMenuCopyID), new_row->ID));
new_row->signal_settings().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnGuildMenuSettings), new_row->ID));
new_row->Children = children;
for (auto child : children)
child->Parent = new_row;
@ -615,6 +626,7 @@ void ChannelList::InsertGuildAt(Snowflake id, int pos) {
m_guild_id_to_row[guild_row->ID] = guild_row;
guild_row->signal_leave().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnGuildMenuLeave), guild_row->ID));
guild_row->signal_copy_id().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnMenuCopyID), guild_row->ID));
guild_row->signal_settings().connect(sigc::bind(sigc::mem_fun(*this, &ChannelList::OnGuildMenuSettings), guild_row->ID));
// add channels with no parent category
for (const auto &[pos, channel] : orphan_channels) {
@ -726,6 +738,10 @@ void ChannelList::OnGuildMenuLeave(Snowflake id) {
m_signal_action_guild_leave.emit(id);
}
void ChannelList::OnGuildMenuSettings(Snowflake id) {
m_signal_action_guild_settings.emit(id);
}
void ChannelList::CheckBumpDM(Snowflake channel_id) {
auto it = m_dm_id_to_row.find(channel_id);
if (it == m_dm_id_to_row.end()) return;
@ -756,3 +772,7 @@ ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_c
ChannelList::type_signal_action_guild_leave ChannelList::signal_action_guild_leave() {
return m_signal_action_guild_leave;
}
ChannelList::type_signal_action_guild_settings ChannelList::signal_action_guild_settings() {
return m_signal_action_guild_settings;
}

View File

@ -64,17 +64,21 @@ protected:
Gtk::Menu m_menu;
Gtk::MenuItem *m_menu_copyid;
Gtk::MenuItem *m_menu_leave;
Gtk::MenuItem *m_menu_settings;
private:
typedef sigc::signal<void> type_signal_copy_id;
typedef sigc::signal<void> type_signal_leave;
typedef sigc::signal<void> type_signal_settings;
type_signal_copy_id m_signal_copy_id;
type_signal_leave m_signal_leave;
type_signal_settings m_signal_settings;
public:
type_signal_copy_id signal_copy_id();
type_signal_leave signal_leave();
type_signal_settings signal_settings();
};
class ChannelListRowCategory : public ChannelListRow {
@ -156,6 +160,7 @@ protected:
int m_guild_count;
void OnMenuCopyID(Snowflake id);
void OnGuildMenuLeave(Snowflake id);
void OnGuildMenuSettings(Snowflake id);
Gtk::Menu m_channel_menu;
Gtk::MenuItem *m_channel_menu_copyid;
@ -179,11 +184,14 @@ protected:
public:
typedef sigc::signal<void, Snowflake> type_signal_action_channel_item_select;
typedef sigc::signal<void, Snowflake> type_signal_action_guild_leave;
typedef sigc::signal<void, Snowflake> type_signal_action_guild_settings;
type_signal_action_channel_item_select signal_action_channel_item_select();
type_signal_action_guild_leave signal_action_guild_leave();
type_signal_action_guild_settings signal_action_guild_settings();
protected:
type_signal_action_channel_item_select m_signal_action_channel_item_select;
type_signal_action_guild_leave m_signal_action_guild_leave;
type_signal_action_guild_settings m_signal_action_guild_settings;
};

View File

@ -162,16 +162,40 @@
color: @text_color;
}
paned separator {
.app-window label:not(:disabled) {
color: @text_color;
}
.app-window entry {
background: @secondary_color;
color: @text_color;
border: 1px solid #1c2e40;
}
.app-window button {
background: @secondary_color;
color: @text_color;
border: 1px solid #1c2e40;
}
.app-window.background {
background: @background_color;
}
.app-window listbox {
background: @background_color;
}
.app-window paned separator {
background: #37474f;
}
scrollbar {
.app-window scrollbar {
background: @background_color;
border-left: 1px solid transparent;
}
menubar, menu {
.app-window menubar, menu {
background: @background_color;
color: #cccccc;
}

View File

@ -7,6 +7,7 @@ ConfirmDialog::ConfirmDialog(Gtk::Window &parent)
, m_ok("OK")
, m_cancel("Cancel") {
set_default_size(300, 50);
get_style_context()->add_class("app-window");
m_label.set_text("Are you sure?");

View File

@ -7,6 +7,7 @@ EditMessageDialog::EditMessageDialog(Gtk::Window &parent)
, m_ok("OK")
, m_cancel("Cancel") {
set_default_size(300, 50);
get_style_context()->add_class("app-window");
m_ok.signal_clicked().connect([&]() {
m_content = m_text.get_buffer()->get_text();

View File

@ -10,6 +10,7 @@ JoinGuildDialog::JoinGuildDialog(Gtk::Window &parent)
, m_cancel("Cancel")
, m_info("Enter code") {
set_default_size(300, 50);
get_style_context()->add_class("app-window");
Glib::signal_idle().connect(sigc::mem_fun(*this, &JoinGuildDialog::on_idle_slot));

View File

@ -8,6 +8,7 @@ SetStatusDialog::SetStatusDialog(Gtk::Window &parent)
, m_ok("OK")
, m_cancel("Cancel") {
set_default_size(300, 50);
get_style_context()->add_class("app-window");
m_text.set_placeholder_text("Status text");

View File

@ -7,6 +7,7 @@ TokenDialog::TokenDialog(Gtk::Window &parent)
, m_ok("OK")
, m_cancel("Cancel") {
set_default_size(300, 50);
get_style_context()->add_class("app-window");
m_ok.signal_clicked().connect([&]() {
m_token = m_entry.get_text();

View File

@ -437,6 +437,36 @@ void DiscordClient::RemoveReaction(Snowflake id, Glib::ustring param) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id) + "/reactions/" + param + "/@me", [](auto) {});
}
void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name) {
SetGuildName(id, name, [](auto) {});
}
void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name, sigc::slot<void(bool success)> callback) {
ModifyGuildObject obj;
obj.Name = name;
sigc::signal<void, bool> signal;
signal.connect(callback);
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const cpr::Response &r) {
const auto success = r.status_code == 200;
signal.emit(success);
});
}
void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data) {
SetGuildIcon(id, data, [](auto) {});
}
void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data, sigc::slot<void(bool success)> callback) {
ModifyGuildObject obj;
obj.IconData = data;
sigc::signal<void, bool> signal;
signal.connect(callback);
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const cpr::Response &r) {
const auto success = r.status_code == 200;
signal.emit(success);
});
}
void DiscordClient::UpdateToken(std::string token) {
if (!IsStarted()) {
m_token = token;

View File

@ -110,6 +110,10 @@ public:
std::optional<Snowflake> FindDM(Snowflake user_id); // wont find group dms
void AddReaction(Snowflake id, Glib::ustring param);
void RemoveReaction(Snowflake id, Glib::ustring param);
void SetGuildName(Snowflake id, const Glib::ustring &name);
void SetGuildName(Snowflake id, const Glib::ustring &name, sigc::slot<void(bool success)> callback);
void SetGuildIcon(Snowflake id, const std::string &data);
void SetGuildIcon(Snowflake id, const std::string &data, sigc::slot<void(bool success)> callback);
void UpdateToken(std::string token);
void SetUserAgent(std::string agent);

View File

@ -118,6 +118,13 @@ void GuildData::update_from_json(const nlohmann::json &j) {
JS_RD("approximate_presence_count", ApproximatePresenceCount);
}
bool GuildData::HasFeature(const std::string &search_feature) {
for (const auto &feature : Features)
if (search_feature == feature)
return true;
return false;
}
bool GuildData::HasIcon() const {
return Icon != "";
}

View File

@ -67,6 +67,7 @@ struct GuildData {
friend void from_json(const nlohmann::json &j, GuildData &m);
void update_from_json(const nlohmann::json &j);
bool HasFeature(const std::string &feature);
bool HasIcon() const;
bool HasAnimatedIcon() const;
std::string GetIconURL(std::string ext = "png", std::string size = "32") const;

View File

@ -247,3 +247,8 @@ void from_json(const nlohmann::json &j, TypingStartObject &m) {
JS_D("timestamp", m.Timestamp);
JS_O("member", m.Member);
}
void to_json(nlohmann::json &j, const ModifyGuildObject &m) {
JS_IF("name", m.Name);
JS_IF("icon", m.IconData);
}

View File

@ -345,3 +345,11 @@ struct TypingStartObject {
friend void from_json(const nlohmann::json &j, TypingStartObject &m);
};
// implement rest as needed
struct ModifyGuildObject {
std::optional<std::string> Name;
std::optional<std::string> IconData;
friend void to_json(nlohmann::json &j, const ModifyGuildObject &m);
};

View File

@ -100,8 +100,17 @@ std::string IntToCSSColor(int color) {
}
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu) {
widget->signal_button_press_event().connect([&menu](GdkEventButton *ev) -> bool {
AddWidgetMenuHandler(widget, menu, []() {});
}
// so widgets can modify the menu before it is displayed
// maybe theres a better way to do this idk
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu, sigc::slot<void()> pre_callback) {
sigc::signal<void()> signal;
signal.connect(pre_callback);
widget->signal_button_press_event().connect([&menu, signal](GdkEventButton *ev) -> bool {
if (ev->type == GDK_BUTTON_PRESS && ev->button == GDK_BUTTON_SECONDARY) {
signal.emit();
menu.popup_at_pointer(reinterpret_cast<const GdkEvent *>(ev));
return true;
}

View File

@ -39,6 +39,7 @@ void LaunchBrowser(Glib::ustring url);
void GetImageDimensions(int inw, int inh, int &outw, int &outh, int clampw = 400, int clamph = 300);
std::string IntToCSSColor(int color);
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu);
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu, sigc::slot<void()> pre_callback);
std::vector<std::string> StringSplit(const std::string &str, const char *delim);
std::string GetExtension(std::string url);
bool IsURLViewableImage(const std::string &url);

View File

@ -0,0 +1,206 @@
#include "infopane.hpp"
#include "../../abaddon.hpp"
#include <filesystem>
GuildSettingsInfoPane::GuildSettingsInfoPane(Snowflake id)
: GuildID(id)
, m_guild_name_label("Guild name")
, m_guild_icon_label("Guild icon") {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto guild = *discord.GetGuild(id);
const auto self_id = discord.GetUserData().ID;
const auto can_modify = discord.HasGuildPermission(self_id, id, Permission::MANAGE_GUILD);
set_name("guild-info-pane");
set_margin_top(10);
set_margin_bottom(10);
set_margin_left(10);
set_margin_top(10);
m_guild_name.set_sensitive(can_modify);
m_guild_name.set_text(guild.Name);
m_guild_name.signal_focus_out_event().connect([this](GdkEventFocus *e) -> bool {
UpdateGuildName();
return false;
});
m_guild_name.signal_key_press_event().connect([this](GdkEventKey *e) -> bool {
if (e->keyval == GDK_KEY_Return)
UpdateGuildName();
return false;
// clang-format off
}, false);
// clang-format on
m_guild_name.set_tooltip_text("Press enter or lose focus to submit");
m_guild_name.show();
m_guild_name_label.show();
auto load_icon_cb = [this](const Glib::RefPtr<Gdk::Pixbuf> &pixbuf) {
m_guild_icon.property_pixbuf() = pixbuf->scale_simple(64, 64, Gdk::INTERP_BILINEAR);
};
auto guild_update_cb = [this, load_icon_cb](Snowflake id) {
if (id != GuildID) return;
const auto guild = *Abaddon::Get().GetDiscordClient().GetGuild(id);
if (guild.HasIcon())
Abaddon::Get().GetImageManager().LoadFromURL(guild.GetIconURL("png", "64"), sigc::track_obj(load_icon_cb, *this));
};
discord.signal_guild_update().connect(sigc::track_obj(guild_update_cb, *this));
m_guild_icon.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(32);
if (guild.HasIcon()) {
Abaddon::Get().GetImageManager().LoadFromURL(guild.GetIconURL("png", "64"), sigc::track_obj(load_icon_cb, *this));
}
m_guild_icon.set_margin_bottom(10);
if (can_modify) {
m_guild_icon_ev.signal_realize().connect([this]() {
auto window = m_guild_icon_ev.get_window();
auto display = window->get_display();
auto cursor = Gdk::Cursor::create(display, "pointer");
window->set_cursor(cursor);
});
m_guild_icon_ev.signal_button_press_event().connect([this](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS)
if (event->button == GDK_BUTTON_PRIMARY)
UpdateGuildIconPicker();
else if (event->button == GDK_BUTTON_SECONDARY)
UpdateGuildIconClipboard();
return false;
});
}
m_guild_icon_ev.set_tooltip_text("Click to choose a file, right click to paste");
m_guild_icon.show();
m_guild_icon_ev.show();
//m_guild_icon_label.show();
m_guild_icon_ev.add(m_guild_icon);
attach(m_guild_icon_ev, 0, 0, 1, 1);
attach(m_guild_name_label, 0, 1, 1, 1);
attach_next_to(m_guild_name, m_guild_name_label, Gtk::POS_RIGHT, 1, 1);
//attach(m_guild_icon_label, 0, 1, 1, 1);
//attach_next_to(m_guild_icon, m_guild_icon_label, Gtk::POS_RIGHT, 1, 1);
}
void GuildSettingsInfoPane::UpdateGuildName() {
auto &discord = Abaddon::Get().GetDiscordClient();
if (discord.GetGuild(GuildID)->Name == m_guild_name.get_text()) return;
auto cb = [this](bool success) {
if (!success) {
m_guild_name.set_text(Abaddon::Get().GetDiscordClient().GetGuild(GuildID)->Name);
Gtk::MessageDialog dlg("Failed to set guild name", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.run();
}
};
discord.SetGuildName(GuildID, m_guild_name.get_text(), sigc::track_obj(cb, *this));
}
void GuildSettingsInfoPane::UpdateGuildIconFromData(const std::vector<uint8_t> &data, const std::string &mime) {
auto encoded = "data:" + mime + ";base64," + Glib::Base64::encode(std::string(data.begin(), data.end()));
auto &discord = Abaddon::Get().GetDiscordClient();
auto cb = [this](bool success) {
if (!success) {
Gtk::MessageDialog dlg("Failed to set guild icon", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.run();
}
};
discord.SetGuildIcon(GuildID, encoded, sigc::track_obj(cb, *this));
}
void GuildSettingsInfoPane::UpdateGuildIconFromPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf) {
int w = pixbuf->get_width();
int h = pixbuf->get_height();
if (w > 1024 || h > 1024) {
GetImageDimensions(w, h, w, h, 1024, 1024);
pixbuf = pixbuf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
}
gchar *buffer;
gsize buffer_size;
pixbuf->save_to_buffer(buffer, buffer_size, "png");
std::vector<uint8_t> data(buffer_size);
std::memcpy(data.data(), buffer, buffer_size);
UpdateGuildIconFromData(data, "image/png");
}
void GuildSettingsInfoPane::UpdateGuildIconPicker() {
// this picker fucking sucks
Gtk::FileChooserDialog dlg("Choose new guild icon", Gtk::FILE_CHOOSER_ACTION_OPEN);
dlg.get_style_context()->remove_provider(Abaddon::Get().GetStyleProvider());
dlg.set_modal(true);
dlg.signal_response().connect([this, &dlg](int response) {
if (response == Gtk::RESPONSE_OK) {
auto data = ReadWholeFile(dlg.get_filename());
if (GetExtension(dlg.get_filename()) == ".gif")
UpdateGuildIconFromData(data, "image/gif");
else
try {
auto loader = Gdk::PixbufLoader::create();
loader->signal_size_prepared().connect([&loader](int inw, int inh) {
int w, h;
GetImageDimensions(inw, inh, w, h, 1024, 1024);
loader->set_size(w, h);
});
loader->write(data.data(), data.size());
loader->close();
UpdateGuildIconFromPixbuf(loader->get_pixbuf());
} catch (const std::exception &) {};
}
});
dlg.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
auto filter_images = Gtk::FileFilter::create();
if (Abaddon::Get().GetDiscordClient().GetGuild(GuildID)->HasFeature("ANIMATED_ICON")) {
filter_images->set_name("Supported images (*.jpg, *.jpeg, *.png, *.gif)");
filter_images->add_pattern("*.gif");
} else {
filter_images->set_name("Supported images (*.jpg, *.jpeg, *.png)");
}
filter_images->add_pattern("*.jpg");
filter_images->add_pattern("*.jpeg");
filter_images->add_pattern("*.png");
dlg.add_filter(filter_images);
auto filter_all = Gtk::FileFilter::create();
filter_all->set_name("All files (*.*)");
filter_all->add_pattern("*.*");
dlg.add_filter(filter_all);
dlg.run();
}
void GuildSettingsInfoPane::UpdateGuildIconClipboard() {
std::vector<uint8_t> icon_data;
auto cb = Gtk::Clipboard::get();
// query for file path then for actual image
if (cb->wait_is_text_available()) {
auto path = cb->wait_for_text();
if (!std::filesystem::exists(path.c_str())) return;
auto data = ReadWholeFile(path);
try {
auto loader = Gdk::PixbufLoader::create();
loader->signal_size_prepared().connect([&loader](int inw, int inh) {
int w, h;
GetImageDimensions(inw, inh, w, h, 1024, 1024);
loader->set_size(w, h);
});
loader->write(data.data(), data.size());
loader->close();
auto pb = loader->get_pixbuf();
UpdateGuildIconFromPixbuf(pb);
return;
} catch (const std::exception &) {};
}
if (cb->wait_is_image_available()) {
auto pb = cb->wait_for_image();
UpdateGuildIconFromPixbuf(pb);
return;
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <gtkmm.h>
#include "../../discord/snowflake.hpp"
class GuildSettingsInfoPane : public Gtk::Grid {
public:
GuildSettingsInfoPane(Snowflake id);
private:
void UpdateGuildName();
void UpdateGuildIconFromData(const std::vector<uint8_t> &data, const std::string &mime);
void UpdateGuildIconFromPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf);
void UpdateGuildIconPicker();
void UpdateGuildIconClipboard();
Gtk::Label m_guild_icon_label;
Gtk::EventBox m_guild_icon_ev; // necessary to make custom cursor behave properly
Gtk::Image m_guild_icon;
Gtk::Label m_guild_name_label;
Gtk::Entry m_guild_name;
Snowflake GuildID;
};

View File

@ -0,0 +1,45 @@
#include "guildsettingswindow.hpp"
#include "../abaddon.hpp"
GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
: m_main(Gtk::ORIENTATION_VERTICAL)
, GuildID(id)
, m_pane_info(id) {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto guild = *discord.GetGuild(id);
auto guild_update_cb = [this](Snowflake id) {
if (id != GuildID) return;
const auto guild = *Abaddon::Get().GetDiscordClient().GetGuild(id);
set_title(guild.Name);
if (guild.HasIcon())
Abaddon::Get().GetImageManager().LoadFromURL(guild.GetIconURL(), sigc::mem_fun(*this, &GuildSettingsWindow::set_icon));
};
discord.signal_guild_update().connect(sigc::track_obj(guild_update_cb, *this));
set_name("guild-settings");
set_default_size(800, 600);
set_title(guild.Name);
set_position(Gtk::WIN_POS_CENTER);
get_style_context()->add_class("app-window");
if (guild.HasIcon()) {
Abaddon::Get().GetImageManager().LoadFromURL(guild.GetIconURL(), sigc::mem_fun(*this, &GuildSettingsWindow::set_icon));
}
m_switcher.set_stack(m_stack);
m_switcher.set_halign(Gtk::ALIGN_CENTER);
m_switcher.set_hexpand(true);
m_switcher.set_margin_top(10);
m_switcher.show();
m_pane_info.show();
m_stack.add(m_pane_info, "info", "Info");
m_stack.show();
m_main.add(m_switcher);
m_main.add(m_stack);
m_main.show();
add(m_main);
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <gtkmm.h>
#include "../discord/snowflake.hpp"
#include "guildsettings/infopane.hpp"
class GuildSettingsWindow : public Gtk::Window {
public:
GuildSettingsWindow(Snowflake id);
private:
Gtk::Box m_main;
Gtk::Stack m_stack;
Gtk::StackSwitcher m_switcher;
GuildSettingsInfoPane m_pane_info;
Snowflake GuildID;
};

View File

@ -7,6 +7,7 @@ MainWindow::MainWindow()
, m_chan_chat_paned(Gtk::ORIENTATION_HORIZONTAL)
, m_chat_members_paned(Gtk::ORIENTATION_HORIZONTAL) {
set_default_size(1200, 800);
get_style_context()->add_class("app-window");
m_menu_discord.set_label("Discord");
m_menu_discord.set_submenu(m_menu_discord_sub);