From 8672f876134582a72fbe23c4c34370e17558e877 Mon Sep 17 00:00:00 2001 From: trigg Date: Sat, 9 Jul 2022 21:14:30 +0100 Subject: [PATCH] - Added option to show voice connection status - Added option to show channel title - Added option to not hide voice overlay on disconnect (debug) - Added font picker for voice title - Added new translations to base pot - Update discord connector to chase forcibly moved user --- discover_overlay/discord_connector.py | 8 +- discover_overlay/locales/base.pot | 47 ++++++- discover_overlay/voice_overlay.py | 168 +++++++++++++++++++++++--- discover_overlay/voice_settings.py | 108 +++++++++++++++-- 4 files changed, 298 insertions(+), 33 deletions(-) diff --git a/discover_overlay/discord_connector.py b/discover_overlay/discord_connector.py index 6585833..8f2a44a 100644 --- a/discover_overlay/discord_connector.py +++ b/discover_overlay/discord_connector.py @@ -57,7 +57,6 @@ class DiscordConnector: self.current_text = "0" self.list_altered = False self.text_altered = False - self.last_connection = "" self.text = [] self.authed = False @@ -288,6 +287,8 @@ class DiscordConnector: self.set_in_room(j["data"]["user"]["id"], False) if j["data"]["user"]["id"] == self.user["id"]: self.in_room = [] + self.find_user() + # User might have been forcibly moved room elif j["evt"] == "SPEAKING_START": self.list_altered = True # It's only possible to get alerts for the room you're in @@ -306,8 +307,7 @@ class DiscordConnector: elif j["evt"] == "VOICE_CHANNEL_SELECT": self.set_channel(j["data"]["channel_id"]) elif j["evt"] == "VOICE_CONNECTION_STATUS": - # VOICE_CONNECTED > CONNECTING > AWAITING_ENDPOINT > DISCONNECTED - self.last_connection = j["data"]["state"] + self.discover.voice_overlay.set_connection_status(j["data"]) elif j["evt"] == "MESSAGE_CREATE": if self.current_text == j["data"]["channel_id"]: self.add_text(j["data"]["message"]) @@ -374,6 +374,7 @@ class DiscordConnector: elif j["cmd"] == "GET_SELECTED_VOICE_CHANNEL": if 'data' in j and j['data'] and 'id' in j['data']: self.set_channel(j['data']['id']) + self.discover.voice_overlay.set_channel_title(j["data"]["name"]) self.list_altered = True self.in_room = [] for u in j['data']['voice_states']: @@ -701,7 +702,6 @@ class DiscordConnector: for userid in self.in_room: newlist.append(self.userlist[userid]) self.discover.voice_overlay.set_user_list(newlist, self.list_altered) - self.discover.voice_overlay.set_connection(self.last_connection) self.list_altered = False # Update text list if self.discover.text_overlay.popup_style: diff --git a/discover_overlay/locales/base.pot b/discover_overlay/locales/base.pot index 618332b..9798e80 100644 --- a/discover_overlay/locales/base.pot +++ b/discover_overlay/locales/base.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-05-29 23:27+0000\n" +"POT-Creation-Date: 2022-07-09 21:08+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -89,6 +89,9 @@ msgstr "" msgid "Settings" msgstr "" +msgid "Toggle Hidden" +msgstr "" + msgid "Close" msgstr "" @@ -221,6 +224,39 @@ msgstr "" msgid "Line limit" msgstr "" +msgid "DISCONNECTED" +msgstr "" + +msgid "NO_ROUTE" +msgstr "" + +msgid "VOICE_DISCONNECTED" +msgstr "" + +msgid "ICE_CHECKING" +msgstr "" + +msgid "AWAITING_ENDPOINT" +msgstr "" + +msgid "AUTHENTICATING" +msgstr "" + +msgid "CONNECTING" +msgstr "" + +msgid "CONNECTED" +msgstr "" + +msgid "VOICE_CONNECTING" +msgstr "" + +msgid "VOICE_CONNECTED" +msgstr "" + +msgid "Title Font" +msgstr "" + msgid "Label" msgstr "" @@ -293,5 +329,14 @@ msgstr "" msgid "Shrink" msgstr "" +msgid "Show Title" +msgstr "" + +msgid "Show Connection Status" +msgstr "" + +msgid "Show while disconnected" +msgstr "" + msgid "Dummy count" msgstr "" diff --git a/discover_overlay/voice_overlay.py b/discover_overlay/voice_overlay.py index b5ca6c8..316887b 100644 --- a/discover_overlay/voice_overlay.py +++ b/discover_overlay/voice_overlay.py @@ -11,10 +11,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Overlay window for voice""" +import gettext import logging import math import cairo import sys +import pkg_resources from .overlay import OverlayWindow from .image_getter import get_surface, draw_img_to_rect # pylint: disable=wrong-import-order @@ -27,6 +29,10 @@ import random log = logging.getLogger(__name__) +t = gettext.translation( + 'default', pkg_resources.resource_filename('discover_overlay', 'locales'), fallback=True) +_ = t.gettext + class VoiceOverlayWindow(OverlayWindow): """Overlay window for voice""" @@ -58,6 +64,7 @@ class VoiceOverlayWindow(OverlayWindow): self.avatar_size = 48 self.text_pad = 6 self.text_font = None + self.title_font = None self.text_size = 13 self.text_baseline_adj = 0 self.icon_spacing = 8 @@ -70,6 +77,10 @@ class VoiceOverlayWindow(OverlayWindow): self.overflow = None self.use_dummy = False self.dummy_count = 10 + self.show_title = True + self.show_connection = True + self.show_disconnected = True + self.channel_title="" self.round_avatar = True self.icon_only = True @@ -82,7 +93,7 @@ class VoiceOverlayWindow(OverlayWindow): self.hili_col = [0.0, 0.0, 0.0, 0.9] self.border_col = [0.0, 0.0, 0.0, 0.0] self.userlist = [] - self.connected = False + self.connection_status = "DISCONNECTED" self.horizontal = False self.guild_ids = tuple() self.force_location() @@ -95,6 +106,22 @@ class VoiceOverlayWindow(OverlayWindow): def set_blank(self): self.userlist=[] self.needsredraw=True + + def set_title_font(self, font): + self.title_font = font + self.needsredraw = True + + def set_show_connection(self, show_connection): + self.show_connection = show_connection + self.needsredraw = True + + def set_show_title(self, show_title): + self.show_title = show_title + self.needsredraw = True + + def set_show_disconnected(self, show_disconnected): + self.show_disconnected = show_disconnected + self.needsredraw = True def set_show_dummy(self, show_dummy): """ @@ -274,6 +301,12 @@ class VoiceOverlayWindow(OverlayWindow): """ self.col(self.mute_col, alpha) + def set_channel_title(self, channel_title): + """ + Set title above voice list + """ + self.channel_title = channel_title + def set_user_list(self, userlist, alt): """ Set the users in list to draw @@ -288,13 +321,12 @@ class VoiceOverlayWindow(OverlayWindow): if alt: self.needsredraw = True - def set_connection(self, connection): + def set_connection_status(self, connection): """ Set if discord has a clean connection to server """ - is_connected = connection == "VOICE_CONNECTED" - if self.connected != is_connected: - self.connected = is_connected + if self.connection_status != connection['state']: + self.connection_status = connection['state'] self.needsredraw = True def sort_list(self, in_list): @@ -336,7 +368,7 @@ class VoiceOverlayWindow(OverlayWindow): context.clip() context.set_operator(cairo.OPERATOR_OVER) - if not self.connected and not self.use_dummy: + if not self.show_disconnected and self.connection_status == "DISCONNECTED" and not self.use_dummy: return connection = self.discover.connection @@ -412,7 +444,16 @@ class VoiceOverlayWindow(OverlayWindow): current_y+=offset_y else: # Calculate height needed to show overlay - needed_height = (len(users_to_draw) * self.avatar_size) + \ + doTitle = False + doConnection = False + if self.show_connection: + users_to_draw.insert(0, None) + doConnection = True + if self.show_title: + users_to_draw.insert(0, None) + doTitle = True + + needed_height = ((len(users_to_draw)+0) * self.avatar_size) + \ (len(users_to_draw) + 1) * self.icon_spacing if needed_height > height: @@ -453,9 +494,24 @@ class VoiceOverlayWindow(OverlayWindow): current_y = height - needed_height - self.vert_edge_padding largest_text_width = 0 for user in col: - text_width = self.draw_avatar(context, user, current_x, current_y, avatar_size) - largest_text_width = max(text_width, largest_text_width) - current_y += avatar_size + self.icon_spacing + if not user: + if doTitle: + # Draw header + text_width = self.draw_title(context, current_x, current_y, avatar_size) + largest_text_width = max(text_width, largest_text_width) + current_y += avatar_size + self.icon_spacing + doTitle = False + elif doConnection: + # Draw header + text_width = self.draw_connection(context, current_x, current_y, avatar_size) + largest_text_width = max(text_width, largest_text_width) + current_y += avatar_size + self.icon_spacing + doConnection = False + + else: + text_width = self.draw_avatar(context, user, current_x, current_y, avatar_size) + largest_text_width = max(text_width, largest_text_width) + current_y += avatar_size + self.icon_spacing if largest_text_width > 0: largest_text_width += self.text_pad else: @@ -482,6 +538,52 @@ class VoiceOverlayWindow(OverlayWindow): if identifier in self.avatars: del self.avatars[identifier] + def draw_title(self,context, pos_x, pos_y, avatar_size): + """ + Draw title at given Y position. Includes both text and image based on settings + """ + title = self.channel_title + if self.use_dummy: + title="Dummy Title" + tw = self.draw_text( + context, title, + pos_x, + pos_y, + self.text_col, + self.norm_col, + avatar_size, + self.title_font + ) + return tw + + def unused_fn_needed_translations(self): + _("DISCONNECTED") + _("NO_ROUTE") + _("VOICE_DISCONNECTED") + _("ICE_CHECKING") + _("AWAITING_ENDPOINT") + _("AUTHENTICATING") + _("CONNECTING") + _("CONNECTED") + _("VOICE_CONNECTING") + _("VOICE_CONNECTED") + + def draw_connection(self,context, pos_x, pos_y, avatar_size): + """ + Draw title at given Y position. Includes both text and image based on settings + """ + tw = self.draw_text( + context, _(self.connection_status), + pos_x, + pos_y, + self.text_col, + self.norm_col, + avatar_size, + self.text_font + ) + self.draw_connection_icon(context, pos_x, pos_y, avatar_size) + return tw + def draw_avatar(self, context, user, pos_x, pos_y, avatar_size): """ Draw avatar at given Y position. Includes both text and image based on settings @@ -527,7 +629,8 @@ class VoiceOverlayWindow(OverlayWindow): pos_y, fg_col, bg_col, - avatar_size + avatar_size, + self.text_font ) self.draw_avatar_pix(context, pix,pos_x,pos_y,colour, avatar_size) if deaf: @@ -536,7 +639,7 @@ class VoiceOverlayWindow(OverlayWindow): self.draw_mute(context, pos_x, pos_y, avatar_size) return tw - def draw_text(self, context, string, pos_x, pos_y, tx_col, bg_col, avatar_size): + def draw_text(self, context, string, pos_x, pos_y, tx_col, bg_col, avatar_size, font): """ Draw username & background at given position """ @@ -546,9 +649,8 @@ class VoiceOverlayWindow(OverlayWindow): layout.set_width(Pango.SCALE * self.width) layout.set_spacing(Pango.SCALE * 3) - font = None - if self.text_font: - font = Pango.FontDescription(self.text_font) + if font: + font = Pango.FontDescription(font) layout.set_font_description(font) (_ink_rect, logical_rect) = layout.get_pixel_extents() text_height = logical_rect.height @@ -741,3 +843,39 @@ class VoiceOverlayWindow(OverlayWindow): context.fill() context.restore() + + def draw_connection_icon(self, context, pos_x, pos_y, avatar_size): + context.save() + context.translate(pos_x, pos_y) + context.scale(avatar_size, avatar_size) + + bars = 0 + s = self.connection_status + if s == "DISCONNECTED" or s == "NO_ROUTE" or s == "VOICE_DISCONNECTED": + bars = 0 + self.col([1.0,0.0,0.0,1.0]) + elif s == "ICE_CHECKING" or s == "AWAITING_ENDPOINT" or s == "AUTHENTICATING": + bars = 1 + self.col([1.0,0.0,0.0,1.0]) + elif s == "CONNECTING" or s == "CONNECTED" or s == "VOICE_CONNECTING": + bars = 2 + self.col([1.0,1.0,0.0,1.0]) + elif s == "VOICE_CONNECTED": + bars = 3 + self.col([0.0,1.0,0.0,1.0]) + context.set_line_width(0.1) + + if bars >= 1: + context.move_to(0.3, 0.8) + context.line_to(0.3, 0.7) + context.stroke() + if bars >= 2: + context.move_to(0.5, 0.8) + context.line_to(0.5, 0.5) + context.stroke() + if bars == 3: + context.move_to(0.7, 0.8) + context.line_to(0.7, 0.2) + context.stroke() + context.restore() + diff --git a/discover_overlay/voice_settings.py b/discover_overlay/voice_settings.py index 4c7d325..07cd9d1 100644 --- a/discover_overlay/voice_settings.py +++ b/discover_overlay/voice_settings.py @@ -65,6 +65,7 @@ class VoiceSettingsWindow(SettingsWindow): self.text_padding = None self.text_baseline_adj = None self.font = None + self.title_font = None self.square_avatar = None self.only_speaking = None self.highlight_self = None @@ -77,11 +78,14 @@ class VoiceSettingsWindow(SettingsWindow): self.horizontal = None self.guild_ids = None self.overflow = None - self.init_config() self.guild_filter_string = "" self.warned = False self.show_dummy = False self.dummy_count = 10 + self.show_connection = None + self.show_title = None + self.show_disconnected = None + self.init_config() self.create_gui() @@ -129,6 +133,7 @@ class VoiceSettingsWindow(SettingsWindow): self.text_baseline_adj = config.getint( "main", "text_baseline_adj", fallback=0) self.font = config.get("main", "font", fallback=None) + self.title_font = config.get("main", "title_font", fallback=None) self.square_avatar = config.getboolean( "main", "square_avatar", fallback=True) self.only_speaking = config.getboolean( @@ -154,6 +159,9 @@ class VoiceSettingsWindow(SettingsWindow): self.guild_ids = parse_guild_ids( config.get("main", "guild_ids", fallback="")) self.overflow = config.getint("main", "overflow", fallback = 0) + self.show_connection = config.getboolean("main", "show_connection", fallback=False) + self.show_title = config.getboolean("main", "show_title", fallback=False) + self.show_disconnected = config.getboolean("main", "show_disconnected", fallback=False) # Pass all of our config over to the overlay self.overlay.set_align_x(self.align_x) @@ -183,12 +191,17 @@ class VoiceSettingsWindow(SettingsWindow): self.overlay.set_guild_ids(self.guild_ids) self.overlay.set_overflow(self.overflow) self.overlay.set_enabled(True) + self.overlay.set_show_connection(self.show_connection) + self.overlay.set_show_title(self.show_title) + self.overlay.set_show_disconnected(self.show_disconnected) self.overlay.set_floating( self.floating, self.floating_x, self.floating_y, self.floating_w, self.floating_h) if self.font: self.overlay.set_font(self.font) + if self.title_font: + self.overlay.set_title_font(self.title_font) def save_config(self): """ @@ -216,6 +229,8 @@ class VoiceSettingsWindow(SettingsWindow): (self.text_baseline_adj)) if self.font: config.set("main", "font", self.font) + if self.title_font: + config.set("main", "title_font", self.title_font) config.set("main", "square_avatar", "%d" % (int(self.square_avatar))) config.set("main", "only_speaking", "%d" % (int(self.only_speaking))) config.set("main", "highlight_self", "%d" % (int(self.highlight_self))) @@ -235,6 +250,9 @@ class VoiceSettingsWindow(SettingsWindow): config.set("main", "guild_ids", "%s" % guild_ids_to_string(self.guild_ids)) config.set("main", "overflow", "%s" % (int(self.overflow))) + config.set("main", "show_connection", "%d" % (int(self.show_connection))) + config.set("main", "show_title", "%d" % (int(self.show_title))) + config.set("main", "show_disconnected", "%d" % (int(self.show_disconnected))) with open(self.config_file, 'w') as file: config.write(file) @@ -280,6 +298,15 @@ class VoiceSettingsWindow(SettingsWindow): alignment_box.attach(font_label, 0, 0, 1, 1) alignment_box.attach(font, 1, 0, 1, 1) + # Title Font chooser + title_font_label = Gtk.Label.new(_("Title Font")) + title_font = Gtk.FontButton() + if self.title_font: + title_font.set_font(self.title_font) + title_font.connect("font-set", self.change_title_font) + alignment_box.attach(title_font_label, 0, 1, 1, 1) + alignment_box.attach(title_font, 1, 1, 1, 1) + # Colours bg_col = Gtk.ColorButton.new_with_rgba( Gdk.RGBA(self.bg_col[0], self.bg_col[1], self.bg_col[2], self.bg_col[3])) @@ -487,8 +514,8 @@ class VoiceSettingsWindow(SettingsWindow): icon_spacing = Gtk.SpinButton.new(icon_spacing_adjustment, 0, 0) icon_spacing.connect("value-changed", self.change_icon_spacing) - alignment_box.attach(icon_spacing_label, 0, 1, 1, 1) - alignment_box.attach(icon_spacing, 1, 1, 1, 1) + alignment_box.attach(icon_spacing_label, 0, 2, 1, 1) + alignment_box.attach(icon_spacing, 1, 2, 1, 1) # Text padding text_padding_label = Gtk.Label.new(_("Text Padding")) @@ -498,8 +525,8 @@ class VoiceSettingsWindow(SettingsWindow): text_padding = Gtk.SpinButton.new(text_padding_adjustment, 0, 0) text_padding.connect("value-changed", self.change_text_padding) - alignment_box.attach(text_padding_label, 0, 2, 1, 1) - alignment_box.attach(text_padding, 1, 2, 1, 1) + alignment_box.attach(text_padding_label, 0, 3, 1, 1) + alignment_box.attach(text_padding, 1, 3, 1, 1) # Text Baseline Adjustment text_baseline_label = Gtk.Label.new(_("Text Vertical Offset")) @@ -509,8 +536,8 @@ class VoiceSettingsWindow(SettingsWindow): text_baseline = Gtk.SpinButton.new(text_baseline_adjustment, 0, 0) text_baseline.connect("value-changed", self.change_text_baseline) - alignment_box.attach(text_baseline_label, 0, 3, 1, 1) - alignment_box.attach(text_baseline, 1, 3, 1, 1) + alignment_box.attach(text_baseline_label, 0, 4, 1, 1) + alignment_box.attach(text_baseline, 1, 4, 1, 1) # Edge padding vert_edge_padding_label = Gtk.Label.new(_("Vertical Edge Padding")) @@ -522,8 +549,8 @@ class VoiceSettingsWindow(SettingsWindow): vert_edge_padding.connect( "value-changed", self.change_vert_edge_padding) - alignment_box.attach(vert_edge_padding_label, 0, 4, 1, 1) - alignment_box.attach(vert_edge_padding, 1, 4, 1, 1) + alignment_box.attach(vert_edge_padding_label, 0, 5, 1, 1) + alignment_box.attach(vert_edge_padding, 1, 5, 1, 1) horz_edge_padding_label = Gtk.Label.new(_("Horizontal Edge Padding")) horz_edge_padding_adjustment = Gtk.Adjustment.new( @@ -533,8 +560,8 @@ class VoiceSettingsWindow(SettingsWindow): horz_edge_padding.connect( "value-changed", self.change_horz_edge_padding) - alignment_box.attach(horz_edge_padding_label, 0, 5, 1, 1) - alignment_box.attach(horz_edge_padding, 1, 5, 1, 1) + alignment_box.attach(horz_edge_padding_label, 0, 6, 1, 1) + alignment_box.attach(horz_edge_padding, 1, 6, 1, 1) # Display icon horizontally horizontal_label = Gtk.Label.new(_("Display Horizontally")) @@ -543,8 +570,8 @@ class VoiceSettingsWindow(SettingsWindow): horizontal.set_active(self.horizontal) horizontal.connect("toggled", self.change_horizontal) - alignment_box.attach(horizontal_label, 0, 6, 1, 1) - alignment_box.attach(horizontal, 1, 6, 1, 1) + alignment_box.attach(horizontal_label, 0, 7, 1, 1) + alignment_box.attach(horizontal, 1, 7, 1, 1) # Overflow overflow_label = Gtk.Label.new(_("Overflow style")) @@ -563,6 +590,36 @@ class VoiceSettingsWindow(SettingsWindow): avatar_box.attach(overflow_label, 0, 6, 1, 1) avatar_box.attach(overflow, 1, 6, 1, 1) + # Show Title + show_title_label = Gtk.Label.new(_("Show Title")) + show_title_label.set_xalign(0) + show_title = Gtk.CheckButton.new() + show_title.set_active(self.show_title) + show_title.connect("toggled", self.change_show_title) + + avatar_box.attach(show_title_label, 0, 7, 1,1) + avatar_box.attach(show_title, 1, 7, 1,1) + + # Show Connection + show_connection_label = Gtk.Label.new(_("Show Connection Status")) + show_connection_label.set_xalign(0) + show_connection = Gtk.CheckButton.new() + show_connection.set_active(self.show_connection) + show_connection.connect("toggled", self.change_show_connection) + + avatar_box.attach(show_connection_label, 0, 8, 1,1) + avatar_box.attach(show_connection, 1, 8, 1,1) + + # Show Disconnected + show_disconnected_label = Gtk.Label.new(_("Show while disconnected")) + show_disconnected_label.set_xalign(0) + show_disconnected = Gtk.CheckButton.new() + show_disconnected.set_active(self.show_disconnected) + show_disconnected.connect("toggled", self.change_show_disconnected) + + avatar_box.attach(show_disconnected_label, 0, 9, 1,1) + avatar_box.attach(show_disconnected, 1, 9, 1,1) + # use dummy dummy_label = Gtk.Label.new(_("Show test content")) dummy_label.set_xalign(0) @@ -600,6 +657,16 @@ class VoiceSettingsWindow(SettingsWindow): self.font = font self.save_config() + def change_title_font(self, button): + """ + Font settings changed + """ + title_font = button.get_font() + self.overlay.set_title_font(title_font) + + self.title_font = title_font + self.save_config() + def change_bg(self, button): """ Background colour changed @@ -802,6 +869,21 @@ class VoiceSettingsWindow(SettingsWindow): self.overflow = button.get_active() self.save_config() + def change_show_title(self, button): + self.overlay.set_show_title(button.get_active()) + self.show_title = button.get_active() + self.save_config() + + def change_show_connection(self, button): + self.overlay.set_show_connection(button.get_active()) + self.show_connection = button.get_active() + self.save_config() + + def change_show_disconnected(self, button): + self.overlay.set_show_disconnected(button.get_active()) + self.show_disconnected = button.get_active() + self.save_config() + def on_guild_selection_changed(self, tree, number, selection): model, treeiter = tree.get_selection().get_selected() if treeiter is not None: