bus mono now a ButtonMenu.

this allows users to select between  `mono off`, `mono on` and `stereo reverse`. This properly reflects the Voicemeter GUI.
This commit is contained in:
onyx-and-iris 2026-03-11 01:11:17 +00:00
parent 1ea1c59f06
commit cc6e187998
3 changed files with 39 additions and 7 deletions

View File

@ -438,13 +438,20 @@ class Builder:
def make_tab3_button_row(self, i) -> psg.Frame: def make_tab3_button_row(self, i) -> psg.Frame:
"""tab3 row represents bus composite toggle""" """tab3 row represents bus composite toggle"""
def add_strip_outputs(layout): def add_bus_buttons(layout):
params = ['MONO', 'EQ', 'MUTE'] busmono = util.get_bus_mono()
params = ['EQ', 'MUTE']
if self.kind.name == 'basic': if self.kind.name == 'basic':
params.remove('EQ') params.remove('EQ')
busmodes = [util._bus_mode_map[mode] for mode in util.get_bus_modes(self.vm)] busmodes = [util._bus_mode_map[mode] for mode in util.get_bus_modes(self.vm)]
layout.append( layout.append(
[ [
psg.ButtonMenu(
'Mono',
size=(6, 2),
menu_def=['', busmono],
key=f'BUS {i}||MONO',
),
*[ *[
psg.Button( psg.Button(
param.capitalize(), param.capitalize(),
@ -454,7 +461,7 @@ class Builder:
for param in params for param in params
], ],
psg.ButtonMenu( psg.ButtonMenu(
'BUSMODE', 'Bus Mode',
size=(12, 2), size=(12, 2),
menu_def=['', busmodes], menu_def=['', busmodes],
key=f'BUS {i}||MODE', key=f'BUS {i}||MODE',
@ -463,7 +470,7 @@ class Builder:
) )
outputs = [] outputs = []
[step(outputs) for step in (add_strip_outputs,)] [step(outputs) for step in (add_bus_buttons,)]
return psg.Frame( return psg.Frame(
self.window.cache['labels'][f'BUS {i}||LABEL'], self.window.cache['labels'][f'BUS {i}||LABEL'],
outputs, outputs,

View File

@ -155,6 +155,10 @@ def get_bus_modes(vm) -> list:
] ]
def get_bus_mono() -> list:
return ['off', 'on', 'stereo reverse']
def check_bounds(val, bounds: tuple) -> int | float: def check_bounds(val, bounds: tuple) -> int | float:
lower, upper = bounds lower, upper = bounds
if val > upper: if val > upper:

View File

@ -58,6 +58,7 @@ class NVDAVMWindow(psg.Window):
self[f'STRIP {i}||SLIDER LIMIT'].Widget.config(**slider_opts) self[f'STRIP {i}||SLIDER LIMIT'].Widget.config(**slider_opts)
for i in range(self.kind.num_bus): for i in range(self.kind.num_bus):
self[f'BUS {i}||SLIDER GAIN'].Widget.config(**slider_opts) self[f'BUS {i}||SLIDER GAIN'].Widget.config(**slider_opts)
self[f'BUS {i}||MONO'].Widget.config(**buttonmenu_opts)
self[f'BUS {i}||MODE'].Widget.config(**buttonmenu_opts) self[f'BUS {i}||MODE'].Widget.config(**buttonmenu_opts)
self.register_events() self.register_events()
@ -251,13 +252,16 @@ class NVDAVMWindow(psg.Window):
self[f'STRIP {i}||SLIDER {param}'].bind('<Control-Shift-KeyPress-R>', '||KEY CTRL SHIFT R') self[f'STRIP {i}||SLIDER {param}'].bind('<Control-Shift-KeyPress-R>', '||KEY CTRL SHIFT R')
# Bus Params # Bus Params
params = ['MONO', 'EQ', 'MUTE'] params = ['EQ', 'MUTE']
if self.kind.name == 'basic': if self.kind.name == 'basic':
params.remove('EQ') params.remove('EQ')
for i in range(self.kind.num_bus): for i in range(self.kind.num_bus):
for param in params: for param in params:
self[f'BUS {i}||{param}'].bind('<FocusIn>', '||FOCUS IN') self[f'BUS {i}||{param}'].bind('<FocusIn>', '||FOCUS IN')
self[f'BUS {i}||{param}'].bind('<Return>', '||KEY ENTER') self[f'BUS {i}||{param}'].bind('<Return>', '||KEY ENTER')
self[f'BUS {i}||MONO'].bind('<FocusIn>', '||FOCUS IN')
self[f'BUS {i}||MONO'].bind('<space>', '||KEY SPACE', propagate=False)
self[f'BUS {i}||MONO'].bind('<Return>', '||KEY ENTER')
self[f'BUS {i}||MODE'].bind('<FocusIn>', '||FOCUS IN') self[f'BUS {i}||MODE'].bind('<FocusIn>', '||FOCUS IN')
self[f'BUS {i}||MODE'].bind('<space>', '||KEY SPACE', propagate=False) self[f'BUS {i}||MODE'].bind('<space>', '||KEY SPACE', propagate=False)
self[f'BUS {i}||MODE'].bind('<Return>', '||KEY ENTER', propagate=False) self[f'BUS {i}||MODE'].bind('<Return>', '||KEY ENTER', propagate=False)
@ -306,7 +310,7 @@ class NVDAVMWindow(psg.Window):
mode = None mode = None
continue continue
match parsed_cmd := self.parser.match.parseString(event): match parsed_cmd := self.parser.match.parse_string(event):
# Slider mode # Slider mode
case [['ALT', 'LEFT' | 'RIGHT' | 'UP' | 'DOWN' as direction], ['PRESS' | 'RELEASE' as e]]: case [['ALT', 'LEFT' | 'RIGHT' | 'UP' | 'DOWN' as direction], ['PRESS' | 'RELEASE' as e]]:
if mode: if mode:
@ -972,7 +976,7 @@ class NVDAVMWindow(psg.Window):
self.nvda.speak, self.nvda.speak,
'on' if val else 'off', 'on' if val else 'off',
) )
case 'MONO' | 'MUTE': case 'MUTE':
val = not val val = not val
setattr(self.vm.bus[int(index)], param.lower(), val) setattr(self.vm.bus[int(index)], param.lower(), val)
self.cache['bus'][event] = val self.cache['bus'][event] = val
@ -981,6 +985,15 @@ class NVDAVMWindow(psg.Window):
self.nvda.speak, self.nvda.speak,
'on' if val else 'off', 'on' if val else 'off',
) )
case 'MONO':
chosen = values[event]
self.vm.bus[int(index)].mono = util.get_bus_mono().index(chosen)
self.cache['bus'][event] = chosen
self.TKroot.after(
200,
self.nvda.speak,
f'mono {chosen}',
)
case 'MODE': case 'MODE':
chosen = util._bus_mode_map_reversed[values[event]] chosen = util._bus_mode_map_reversed[values[event]]
setattr(self.vm.bus[int(index)].mode, chosen, True) setattr(self.vm.bus[int(index)].mode, chosen, True)
@ -996,11 +1009,19 @@ class NVDAVMWindow(psg.Window):
val = self.cache['bus'][f'BUS {index}||{param}'] val = self.cache['bus'][f'BUS {index}||{param}']
if param == 'MODE': if param == 'MODE':
self.nvda.speak(f'{label} bus {param} {util._bus_mode_map[val]}') self.nvda.speak(f'{label} bus {param} {util._bus_mode_map[val]}')
elif param == 'MONO':
busmode = util.get_bus_mono()[val]
if busmode in ('on', 'off'):
self.nvda.speak(f'{label} {param} {busmode}')
else:
self.nvda.speak(f'{label} {busmode}')
else: else:
self.nvda.speak(f'{label} {param} {"on" if val else "off"}') self.nvda.speak(f'{label} {param} {"on" if val else "off"}')
case [['BUS', index], [param], ['KEY', 'SPACE' | 'ENTER']]: case [['BUS', index], [param], ['KEY', 'SPACE' | 'ENTER']]:
if param == 'MODE': if param == 'MODE':
util.open_context_menu_for_buttonmenu(self, f'BUS {index}||MODE') util.open_context_menu_for_buttonmenu(self, f'BUS {index}||MODE')
elif param == 'MONO':
util.open_context_menu_for_buttonmenu(self, f'BUS {index}||MONO')
else: else:
self.find_element_with_focus().click() self.find_element_with_focus().click()