From e25be30fef91bbf3133db56d9858154aa1d8fa99 Mon Sep 17 00:00:00 2001 From: phyce Date: Fri, 22 May 2026 10:37:49 +0000 Subject: [PATCH] Rework mute mode: explicit panel toggle, mode-gated right-click MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, right-click "Listen" on an actor silently called setListenMode(true), flipping the global mute system from blocklist to allowlist mode. Users repeatedly hit this by accident and ended up with "everyone but this person" muted, with no obvious way back. Now: - A new "Mute Settings" section in the plugin panel exposes the mode toggle. This is the only place the mode flips, and the change persists immediately via muteManager.saveConfig(). - The right-click sub-menu shows only mode-appropriate actions: - Blocklist mode: Mute / Unmute - Allowlist mode: Listen / Unlisten - Listen-click no longer flips mode behind the user's back. - The old "Stop Listen Mode" sub-entry is removed — the panel owns the mode now. Switching modes preserves both lists (no auto clearListens), so users don't lose their settings when toggling. Files touched: - MainSettingsPanel: inject MuteManager, add buildMuteModeSegment(). - MenuEventHandler: gate the primary menu entry by mode, remove the silent setListenMode(true) and "Stop Listen Mode" branch. --- .../phyce/naturalspeech/MenuEventHandler.java | 69 +++++++------- .../ui/panels/MainSettingsPanel.java | 89 ++++++++++++++++++- 2 files changed, 119 insertions(+), 39 deletions(-) diff --git a/src/main/java/dev/phyce/naturalspeech/MenuEventHandler.java b/src/main/java/dev/phyce/naturalspeech/MenuEventHandler.java index d35f2fb..ce1e8a2 100644 --- a/src/main/java/dev/phyce/naturalspeech/MenuEventHandler.java +++ b/src/main/java/dev/phyce/naturalspeech/MenuEventHandler.java @@ -187,34 +187,7 @@ else if (actor instanceof Player) { Menu subMenu = parent.createSubMenu(); - if (isListened) { - subMenu.createMenuEntry(0) - .setOption("Unlisten") - .setType(MenuAction.RUNELITE) - .onClick(e -> { - if (npc != null) { - muteManager.unlistenNpc(npc); - } - else { - muteManager.unlistenUsername(standardActorName); - } - }); - } - else { - subMenu.createMenuEntry(0) - .setOption("Listen") - .setType(MenuAction.RUNELITE) - .onClick(e -> { - if (npc != null) { - muteManager.listenNpc(npc); - } - else { - muteManager.listenUsername(standardActorName); - } - muteManager.setListenMode(true); - }); - } - + // Configure is shown in both modes. { final String value = voiceID != null ? voiceID.toVoiceIDString() : ""; subMenu.createMenuEntry(1) @@ -229,18 +202,39 @@ else if (actor instanceof Player) { }); } + // The primary action is decided by the active mute mode. The mode + // itself is set from the plugin panel, never from the right-click. if (muteManager.isListenMode()) { - subMenu.createMenuEntry(1) - .setOption("Stop Listen Mode") - .setType(MenuAction.RUNELITE) - .onClick(e -> { - muteManager.setListenMode(false); - muteManager.clearListens(); - }); + if (isListened) { + subMenu.createMenuEntry(0) + .setOption("Unlisten") + .setType(MenuAction.RUNELITE) + .onClick(e -> { + if (npc != null) { + muteManager.unlistenNpc(npc); + } + else { + muteManager.unlistenUsername(standardActorName); + } + }); + } + else { + subMenu.createMenuEntry(0) + .setOption("Listen") + .setType(MenuAction.RUNELITE) + .onClick(e -> { + if (npc != null) { + muteManager.listenNpc(npc); + } + else { + muteManager.listenUsername(standardActorName); + } + }); + } } else { if (isUnmuted) { - subMenu.createMenuEntry(1) + subMenu.createMenuEntry(0) .setOption("Mute") .setType(MenuAction.RUNELITE) .onClick(e -> { @@ -251,10 +245,9 @@ else if (actor instanceof Player) { muteManager.muteUsername(standardActorName); } }); - } else { - subMenu.createMenuEntry(1) + subMenu.createMenuEntry(0) .setOption("Unmute") .setType(MenuAction.RUNELITE) .onClick(e -> { diff --git a/src/main/java/dev/phyce/naturalspeech/ui/panels/MainSettingsPanel.java b/src/main/java/dev/phyce/naturalspeech/ui/panels/MainSettingsPanel.java index 0567f76..3dc413e 100644 --- a/src/main/java/dev/phyce/naturalspeech/ui/panels/MainSettingsPanel.java +++ b/src/main/java/dev/phyce/naturalspeech/ui/panels/MainSettingsPanel.java @@ -2,6 +2,7 @@ import com.google.inject.Inject; import dev.phyce.naturalspeech.tts.ModelRepository; +import dev.phyce.naturalspeech.tts.MuteManager; import dev.phyce.naturalspeech.configs.NaturalSpeechConfig; import dev.phyce.naturalspeech.configs.NaturalSpeechRuntimeConfig; import dev.phyce.naturalspeech.downloader.Downloader; @@ -73,6 +74,7 @@ public class MainSettingsPanel extends PluginPanel { private final ModelRepository modelRepository; private final TextToSpeech textToSpeech; private final NaturalSpeechRuntimeConfig runtimeConfig; + private final MuteManager muteManager; private final List modelRepositoryListeners; @Inject @@ -82,13 +84,15 @@ public MainSettingsPanel( ConfigManager configManager, Downloader downloader, ClientThread clientThread, TextToSpeech textToSpeech, - NaturalSpeechRuntimeConfig runtimeConfig + NaturalSpeechRuntimeConfig runtimeConfig, + MuteManager muteManager ) { super(false); this.textToSpeech = textToSpeech; this.modelRepository = modelRepository; this.clientThread = clientThread; this.runtimeConfig = runtimeConfig; + this.muteManager = muteManager; this.modelRepositoryListeners = new ArrayList<>(); this.setLayout(new BorderLayout()); @@ -115,6 +119,7 @@ public MainSettingsPanel( buildHeaderSegment(); buildPiperStatusSection(); + buildMuteModeSegment(); buildVoiceRepositorySegment(); // buildVoiceHistorySegment(); @@ -199,6 +204,88 @@ public void mouseClicked(MouseEvent e) { } + public void buildMuteModeSegment() { + final JPanel section = new JPanel(); + section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS)); + section.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); + + final JPanel sectionHeader = new JPanel(); + sectionHeader.setLayout(new BorderLayout()); + sectionHeader.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); + sectionHeader.setBorder(new CompoundBorder( + new MatteBorder(0, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR), + new EmptyBorder(0, 0, 3, 1))); + section.add(sectionHeader); + + final JButton sectionToggle = new JButton(SECTION_RETRACT_ICON); + sectionToggle.setPreferredSize(new Dimension(18, 0)); + sectionToggle.setBorder(new EmptyBorder(0, 0, 0, 5)); + sectionToggle.setToolTipText("Retract"); + SwingUtil.removeButtonDecorations(sectionToggle); + sectionHeader.add(sectionToggle, BorderLayout.WEST); + + final String name = "Mute Settings"; + final String description = + "Choose between blocklist (mute the listed players/NPCs) and allowlist (only listen to the listed players/NPCs)."; + final JLabel sectionName = new JLabel(name); + sectionName.setForeground(ColorScheme.BRAND_ORANGE); + sectionName.setFont(FontManager.getRunescapeBoldFont()); + sectionName.setToolTipText("" + name + ":
" + description + ""); + sectionHeader.add(sectionName, BorderLayout.CENTER); + + final JPanel sectionContent = new JPanel(); + sectionContent.setLayout(new DynamicGridLayout(0, 1, 0, 5)); + sectionContent.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); + section.setBorder(new CompoundBorder( + new MatteBorder(0, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR), + new EmptyBorder(BORDER_OFFSET, 0, BORDER_OFFSET, 0) + )); + section.add(sectionContent, BorderLayout.SOUTH); + + mainContentPanel.add(section); + + final MouseAdapter adapter = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + toggleSection(sectionToggle, sectionContent); + } + }; + sectionToggle.addActionListener(actionEvent -> toggleSection(sectionToggle, sectionContent)); + sectionName.addMouseListener(adapter); + sectionHeader.addMouseListener(adapter); + + final JLabel modeLabel = new JLabel(); + modeLabel.setBorder(new EmptyBorder(5, 5, 0, 5)); + + final JButton modeToggle = new JButton(); + modeToggle.setFocusable(false); + + final Runnable refresh = () -> { + if (muteManager.isListenMode()) { + modeLabel.setText( + "Allowlist mode — only listed players/NPCs are spoken.
" + + "Use right-click \"Listen\" / \"Unlisten\" to manage the list."); + modeToggle.setText("Switch to Blocklist (Mute) mode"); + } + else { + modeLabel.setText( + "Blocklist mode — everyone is spoken except listed players/NPCs.
" + + "Use right-click \"Mute\" / \"Unmute\" to manage the list."); + modeToggle.setText("Switch to Allowlist (Listen) mode"); + } + }; + refresh.run(); + + modeToggle.addActionListener(e -> { + muteManager.setListenMode(!muteManager.isListenMode()); + muteManager.saveConfig(); + refresh.run(); + }); + + sectionContent.add(modeLabel); + sectionContent.add(modeToggle); + } + public void buildVoiceRepositorySegment() { final JPanel section = new JPanel(); section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS));