fix(ui): show the selected microphone's full name in the device tooltip (#1029) (#1037)

The Input Device dropdown has a fixed width and cuts labels off at about
18 characters. Raw ALSA names such as "sof-hda-dsp: - (hw:0,6)" therefore
all look like "sof-hda-dsp: - (h…", and the tooltip always showed the same
generic text, so there was no way to tell which entry was selected.

The tooltip now shows the full label of the selected device. It updates
when the list is filled and every time the selection changes, and goes
back to the generic hint for System Default.
This commit is contained in:
Jatin K Malik
2026-11-02 12:31:02 -08:00
committed by GitHub
parent d11af1c232
commit f3913fb6bf
2 changed files with 61 additions and 1 deletions
+15 -1
View File
@@ -2140,6 +2140,18 @@ def _combo_chrome(combo: Gtk.Widget, tooltip: Optional[str] = None) -> None:
_prevent_scroll_on_hover(combo)
_AUDIO_DEVICE_TOOLTIP = "Select the microphone to use for voice recognition"
def _sync_audio_device_tooltip(combo: Gtk.ComboBoxText) -> None:
"""Show the selected microphone's full name, which the fixed-width combo truncates."""
label = combo.get_active_text()
if not label or combo.get_active_id() in (None, "-1"):
combo.set_tooltip_text(_AUDIO_DEVICE_TOOLTIP)
else:
combo.set_tooltip_text(label)
def _new_spin(
lower: float, upper: float, step: float, digits: int, tooltip: Optional[str] = None
) -> Gtk.SpinButton:
@@ -3137,7 +3149,7 @@ class SettingsDialog(AboutSectionMixin):
self.audio_device_combo = Gtk.ComboBoxText()
_combo_chrome(
self.audio_device_combo,
tooltip="Select the microphone to use for voice recognition",
tooltip=_AUDIO_DEVICE_TOOLTIP,
)
refresh_btn = Gtk.Button.new_from_icon_name("view-refresh-symbolic", Gtk.IconSize.BUTTON)
@@ -9925,6 +9937,7 @@ For now, the engine has been reverted to VOSK."""
if self.speech_engine is not None:
self.speech_engine.set_audio_device(None, None)
_sync_audio_device_tooltip(self.audio_device_combo)
logger.info(f"Found {len(devices)} audio input devices")
def _on_refresh_audio_devices(self, widget):
@@ -9934,6 +9947,7 @@ For now, the engine has been reverted to VOSK."""
def _on_audio_device_changed(self, widget):
"""Handle changes in the selected audio device."""
_sync_audio_device_tooltip(self.audio_device_combo)
if self._initializing:
return
+46
View File
@@ -633,6 +633,52 @@ class TestSettingsPersistsServerName:
dialog.audio_device_combo.set_active_id.assert_called_with(str(PULSE_INDEX_BASE - 1))
class TestAudioDeviceTooltip:
"""The fixed-width dropdown truncates long names; the tooltip shows them whole (#1029)."""
_SOF = "sof-hda-dsp: - (hw:0,6)"
def _combo(self, device_id: Optional[str], label: Optional[str]) -> MagicMock:
combo = MagicMock()
combo.get_active_id.return_value = device_id
combo.get_active_text.return_value = label
return combo
def test_choosing_a_device_shows_its_full_name_in_the_tooltip(self) -> None:
settings_dialog = _load_settings_dialog()
dialog = MagicMock(_initializing=False, _pulse_source_names={})
dialog.audio_device_combo = self._combo("1", self._SOF)
settings_dialog.SettingsDialog._on_audio_device_changed(dialog, None)
dialog.audio_device_combo.set_tooltip_text.assert_called_with(self._SOF)
def test_system_default_keeps_the_generic_tooltip(self) -> None:
settings_dialog = _load_settings_dialog()
dialog = MagicMock(_initializing=False, _pulse_source_names={})
dialog.audio_device_combo = self._combo("-1", "System Default")
settings_dialog.SettingsDialog._on_audio_device_changed(dialog, None)
dialog.audio_device_combo.set_tooltip_text.assert_called_with(
"Select the microphone to use for voice recognition"
)
def test_populating_shows_the_saved_device_in_the_tooltip(self) -> None:
"""The saved device is selected before the change handler is wired up."""
settings_dialog = _load_settings_dialog()
dialog = MagicMock()
dialog.config_manager.get_optional_int.return_value = 1
dialog.config_manager.get.return_value = self._SOF
dialog.audio_device_combo = self._combo("1", self._SOF)
devices = [(0, "sof-hda-dsp: - (hw:0,0)", False), (1, self._SOF, False)]
with (
patch.object(settings_dialog, "get_pulse_sources", return_value=[]),
patch(
"vocalinux.speech_recognition.recognition_manager.get_audio_input_devices",
return_value=devices,
),
):
settings_dialog.SettingsDialog._populate_audio_devices(dialog)
dialog.audio_device_combo.set_tooltip_text.assert_called_with(self._SOF)
def _load_settings_dialog() -> ModuleType:
"""Import settings_dialog with real GTK base classes so its methods are callable.