diff --git a/res/css/main.css b/res/css/main.css index ace3b0b..4b76bb4 100644 --- a/res/css/main.css +++ b/res/css/main.css @@ -360,3 +360,21 @@ background-color: #dd3300; margin-left: 1px; } + +.voice-info { + background-color: #0B0B0B; + padding: 5px; + border: 1px solid #202020; +} + +.voice-info-disconnect-image { + color: #DDDDDD; +} + +.voice-info-status { + font-weight: bold; +} + +.voice-info-location { + +} diff --git a/src/components/voiceinfobox.cpp b/src/components/voiceinfobox.cpp new file mode 100644 index 0000000..f4dc6ed --- /dev/null +++ b/src/components/voiceinfobox.cpp @@ -0,0 +1,89 @@ +#include "voiceinfobox.hpp" +#include "abaddon.hpp" +#include "util.hpp" + +VoiceInfoBox::VoiceInfoBox() + : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) + , m_left(Gtk::ORIENTATION_VERTICAL) { + m_disconnect_ev.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { + if (ev->type == GDK_BUTTON_PRESS && ev->button == GDK_BUTTON_PRIMARY) { + spdlog::get("discord")->debug("Request disconnect from info box"); + Abaddon::Get().GetDiscordClient().DisconnectFromVoice(); + return true; + } + + return false; + }); + + AddPointerCursor(m_disconnect_ev); + + get_style_context()->add_class("voice-info"); + m_status.get_style_context()->add_class("voice-info-status"); + m_location.get_style_context()->add_class("voice-info-location"); + m_disconnect_img.get_style_context()->add_class("voice-info-disconnect-image"); + + m_status.set_label("You shouldn't see me"); + m_location.set_label("You shouldn't see me"); + + Abaddon::Get().GetDiscordClient().signal_voice_requested_connect().connect([this](Snowflake channel_id) { + show(); + + if (const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(channel_id); channel.has_value() && channel->Name.has_value()) { + if (channel->GuildID.has_value()) { + if (const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(*channel->GuildID); guild.has_value()) { + m_location.set_label(*channel->Name + " / " + guild->Name); + return; + } + } + + m_location.set_label(*channel->Name); + return; + } + + m_location.set_label("Unknown"); + }); + + Abaddon::Get().GetDiscordClient().signal_voice_requested_disconnect().connect([this]() { + hide(); + }); + + Abaddon::Get().GetDiscordClient().signal_voice_client_state_update().connect([this](DiscordVoiceClient::State state) { + Glib::ustring label; + switch (state) { + case DiscordVoiceClient::State::ConnectingToWebsocket: + label = "Connecting"; + break; + case DiscordVoiceClient::State::EstablishingConnection: + label = "Establishing connection"; + break; + case DiscordVoiceClient::State::Connected: + label = "Connected"; + break; + case DiscordVoiceClient::State::DisconnectedByServer: + case DiscordVoiceClient::State::DisconnectedByClient: + label = "Disconnected"; + break; + default: + label = "Unknown"; + break; + } + m_status.set_label(label); + }); + + m_status.set_ellipsize(Pango::ELLIPSIZE_END); + m_location.set_ellipsize(Pango::ELLIPSIZE_END); + + m_disconnect_ev.add(m_disconnect_img); + m_disconnect_img.property_icon_name() = "call-stop-symbolic"; + m_disconnect_img.property_icon_size() = 5; + m_disconnect_img.set_hexpand(true); + m_disconnect_img.set_halign(Gtk::ALIGN_END); + + m_left.add(m_status); + m_left.add(m_location); + + add(m_left); + add(m_disconnect_ev); + + show_all_children(); +} diff --git a/src/components/voiceinfobox.hpp b/src/components/voiceinfobox.hpp new file mode 100644 index 0000000..e0bc4e8 --- /dev/null +++ b/src/components/voiceinfobox.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include +#include + +class VoiceInfoBox : public Gtk::Box { +public: + VoiceInfoBox(); + +private: + Gtk::Box m_left; + Gtk::Label m_status; + Gtk::Label m_location; + + Gtk::EventBox m_disconnect_ev; + Gtk::Image m_disconnect_img; +}; diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index c068243..44f64a9 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -31,6 +31,9 @@ DiscordClient::DiscordClient(bool mem_store) m_voice.signal_speaking().connect([this](const VoiceSpeakingData &data) { m_signal_voice_speaking.emit(data); }); + m_voice.signal_state_update().connect([this](DiscordVoiceClient::State state) { + m_signal_voice_client_state_update.emit(state); + }); #endif LoadEventMap(); @@ -1186,12 +1189,14 @@ void DiscordClient::ConnectToVoice(Snowflake channel_id) { m.ChannelID = channel_id; m.PreferredRegion = "newark"; m_websocket.Send(m); + m_signal_voice_requested_connect.emit(channel_id); } void DiscordClient::DisconnectFromVoice() { m_voice.Stop(); VoiceStateUpdateMessage m; m_websocket.Send(m); + m_signal_voice_requested_disconnect.emit(); } bool DiscordClient::IsVoiceConnected() const noexcept { @@ -2995,4 +3000,16 @@ DiscordClient::type_signal_voice_user_disconnect DiscordClient::signal_voice_use DiscordClient::type_signal_voice_user_connect DiscordClient::signal_voice_user_connect() { return m_signal_voice_user_connect; } + +DiscordClient::type_signal_voice_requested_connect DiscordClient::signal_voice_requested_connect() { + return m_signal_voice_requested_connect; +} + +DiscordClient::type_signal_voice_requested_disconnect DiscordClient::signal_voice_requested_disconnect() { + return m_signal_voice_requested_disconnect; +} + +DiscordClient::type_signal_voice_client_state_update DiscordClient::signal_voice_client_state_update() { + return m_signal_voice_client_state_update; +} #endif diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 1e500e9..a7e3f79 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -440,6 +440,9 @@ public: using type_signal_voice_speaking = sigc::signal; using type_signal_voice_user_disconnect = sigc::signal; using type_signal_voice_user_connect = sigc::signal; + using type_signal_voice_requested_connect = sigc::signal; + using type_signal_voice_requested_disconnect = sigc::signal; + using type_signal_voice_client_state_update = sigc::signal; #endif type_signal_gateway_ready signal_gateway_ready(); @@ -502,6 +505,9 @@ public: type_signal_voice_speaking signal_voice_speaking(); type_signal_voice_user_disconnect signal_voice_user_disconnect(); type_signal_voice_user_connect signal_voice_user_connect(); + type_signal_voice_requested_connect signal_voice_requested_connect(); + type_signal_voice_requested_disconnect signal_voice_requested_disconnect(); + type_signal_voice_client_state_update signal_voice_client_state_update(); #endif protected: @@ -565,5 +571,8 @@ protected: type_signal_voice_speaking m_signal_voice_speaking; type_signal_voice_user_disconnect m_signal_voice_user_disconnect; type_signal_voice_user_connect m_signal_voice_user_connect; + type_signal_voice_requested_connect m_signal_voice_requested_connect; + type_signal_voice_requested_disconnect m_signal_voice_requested_disconnect; + type_signal_voice_client_state_update m_signal_voice_client_state_update; #endif }; diff --git a/src/discord/voiceclient.cpp b/src/discord/voiceclient.cpp index 379a65c..ed83e3c 100644 --- a/src/discord/voiceclient.cpp +++ b/src/discord/voiceclient.cpp @@ -423,6 +423,7 @@ void DiscordVoiceClient::KeepaliveThread() { void DiscordVoiceClient::SetState(State state) { m_log->debug("Changing state to {}", GetStateName(state)); m_state = state; + m_signal_state_update.emit(state); } void DiscordVoiceClient::OnUDPData(std::vector data) { @@ -464,6 +465,10 @@ DiscordVoiceClient::type_signal_speaking DiscordVoiceClient::signal_speaking() { return m_signal_speaking; } +DiscordVoiceClient::type_signal_state_update DiscordVoiceClient::signal_state_update() { + return m_signal_state_update; +} + void from_json(const nlohmann::json &j, VoiceGatewayMessage &m) { JS_D("op", m.Opcode); m.Data = j.at("d"); diff --git a/src/discord/voiceclient.hpp b/src/discord/voiceclient.hpp index 7e4dee3..916d070 100644 --- a/src/discord/voiceclient.hpp +++ b/src/discord/voiceclient.hpp @@ -201,7 +201,6 @@ public: [[nodiscard]] bool IsConnected() const noexcept; [[nodiscard]] bool IsConnecting() const noexcept; -private: enum class State { ConnectingToWebsocket, EstablishingConnection, @@ -209,6 +208,8 @@ private: DisconnectedByClient, DisconnectedByServer, }; + +private: static const char *GetStateName(State state); void OnGatewayMessage(const std::string &msg); @@ -272,13 +273,16 @@ private: using type_signal_connected = sigc::signal; using type_signal_disconnected = sigc::signal; using type_signal_speaking = sigc::signal; + using type_signal_state_update = sigc::signal; type_signal_connected m_signal_connected; type_signal_disconnected m_signal_disconnected; type_signal_speaking m_signal_speaking; + type_signal_state_update m_signal_state_update; public: type_signal_connected signal_connected(); type_signal_disconnected signal_disconnected(); type_signal_speaking signal_speaking(); + type_signal_state_update signal_state_update(); }; #endif diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index 20da46b..c80dfda 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -6,6 +6,7 @@ MainWindow::MainWindow() , m_content_box(Gtk::ORIENTATION_HORIZONTAL) , m_chan_content_paned(Gtk::ORIENTATION_HORIZONTAL) , m_content_members_paned(Gtk::ORIENTATION_HORIZONTAL) + , m_left_pane(Gtk::ORIENTATION_VERTICAL) , m_accels(Gtk::AccelGroup::create()) { set_default_size(1200, 800); get_style_context()->add_class("app-window"); @@ -51,12 +52,18 @@ MainWindow::MainWindow() m_content_stack.set_visible_child("chat"); m_content_stack.show(); - m_chan_content_paned.pack1(m_channel_list); + m_voice_info.show(); + + m_left_pane.add(m_channel_list); + m_left_pane.add(m_voice_info); + m_left_pane.show(); + + m_chan_content_paned.pack1(m_left_pane); m_chan_content_paned.pack2(m_content_members_paned); m_chan_content_paned.child_property_shrink(m_content_members_paned) = true; m_chan_content_paned.child_property_resize(m_content_members_paned) = true; - m_chan_content_paned.child_property_shrink(m_channel_list) = true; - m_chan_content_paned.child_property_resize(m_channel_list) = true; + m_chan_content_paned.child_property_shrink(m_left_pane) = true; + m_chan_content_paned.child_property_resize(m_left_pane) = true; m_chan_content_paned.set_position(200); m_chan_content_paned.show(); m_content_box.add(m_chan_content_paned); diff --git a/src/windows/mainwindow.hpp b/src/windows/mainwindow.hpp index 6e95b72..d4013dc 100644 --- a/src/windows/mainwindow.hpp +++ b/src/windows/mainwindow.hpp @@ -3,6 +3,7 @@ #include "components/chatwindow.hpp" #include "components/memberlist.hpp" #include "components/friendslist.hpp" +#include "components/voiceinfobox.hpp" #include class MainWindow : public Gtk::Window { @@ -53,6 +54,9 @@ private: ChatWindow m_chat; MemberList m_members; FriendsList m_friends; + VoiceInfoBox m_voice_info; + + Gtk::Box m_left_pane; Gtk::Stack m_content_stack;