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));