diff --git a/src/vocalinux/ui/settings_dialog.py b/src/vocalinux/ui/settings_dialog.py index 998247c..093e0c2 100644 --- a/src/vocalinux/ui/settings_dialog.py +++ b/src/vocalinux/ui/settings_dialog.py @@ -5844,12 +5844,13 @@ class SettingsDialog(Gtk.Dialog): self._report_shortcut_apply_result(display_name, applied) def _build_sidebar_footer(self, sidebar_box: Gtk.Box): - """Build the sidebar footer: dictation status, test action, and Close. + """Build the sidebar footer: test output, status, test action, and Close. Always visible regardless of the selected page: recognition state, live microphone level, a dictation test button, and the dialog's - in-window Close button. Test output is revealed inline, so any - instant-applied change can be verified immediately. + in-window Close button. Test output leads the footer so the revealed + box grows into the empty sidebar space above the controls instead of + pushing them down (#677). """ separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) separator.set_margin_start(8) @@ -5859,6 +5860,29 @@ class SettingsDialog(Gtk.Dialog): footer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8) footer.get_style_context().add_class("sidebar-footer") + # Test transcription output, revealed while testing + self.test_output_revealer = Gtk.Revealer() + self.test_output_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN) + + scrolled_window = Gtk.ScrolledWindow() + scrolled_window.set_min_content_height(60) + scrolled_window.set_max_content_height(100) + scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + # GTK3 default IN shadow draws a left etched stripe on GNOME/Wayland (#847). + scrolled_window.set_shadow_type(Gtk.ShadowType.NONE) + scrolled_window.get_style_context().add_class("test-area") + + self.test_textview = Gtk.TextView() + self.test_textview.set_editable(False) + self.test_textview.set_cursor_visible(False) + self.test_textview.set_wrap_mode(Gtk.WrapMode.WORD) + self.test_textview.get_style_context().add_class("test-textview") + self.test_buffer = self.test_textview.get_buffer() + scrolled_window.add(self.test_textview) + self.test_output_revealer.add(scrolled_window) + + footer.pack_start(self.test_output_revealer, False, False, 0) + status_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) self.recognition_indicator = Gtk.Image.new_from_icon_name( @@ -5896,29 +5920,6 @@ class SettingsDialog(Gtk.Dialog): self.audio_test_status = self.progress_info_label footer.pack_start(self.progress_info_label, False, False, 0) - # Test transcription output, revealed while testing - self.test_output_revealer = Gtk.Revealer() - self.test_output_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN) - - scrolled_window = Gtk.ScrolledWindow() - scrolled_window.set_min_content_height(60) - scrolled_window.set_max_content_height(100) - scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) - # GTK3 default IN shadow draws a left etched stripe on GNOME/Wayland (#847). - scrolled_window.set_shadow_type(Gtk.ShadowType.NONE) - scrolled_window.get_style_context().add_class("test-area") - - self.test_textview = Gtk.TextView() - self.test_textview.set_editable(False) - self.test_textview.set_cursor_visible(False) - self.test_textview.set_wrap_mode(Gtk.WrapMode.WORD) - self.test_textview.get_style_context().add_class("test-textview") - self.test_buffer = self.test_textview.get_buffer() - scrolled_window.add(self.test_textview) - self.test_output_revealer.add(scrolled_window) - - footer.pack_start(self.test_output_revealer, False, False, 0) - # Separate Close from the dictation-test controls so it reads as # dialog chrome, not as part of Test Dictation (#651). close_separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) diff --git a/tests/test_settings_dialog.py b/tests/test_settings_dialog.py index b1e4a62..6fef6dc 100644 --- a/tests/test_settings_dialog.py +++ b/tests/test_settings_dialog.py @@ -1226,6 +1226,16 @@ class TestSettingsNavigation(unittest.TestCase): self.assertIn("Gtk.Separator", before_close) self.assertIn("footer.pack_start(close_separator", before_close) + def test_dictation_output_box_leads_footer(self) -> None: + """The transcription box packs before the status row and Test button so + it reveals into the empty sidebar space above the controls (#677).""" + footer_body = self.source_code.split("def _build_sidebar_footer")[1].split("\n def ")[0] + box_pack = footer_body.index("footer.pack_start(self.test_output_revealer") + status_pack = footer_body.index("footer.pack_start(status_row") + button_pack = footer_body.index("footer.pack_start(self.test_button") + self.assertLess(box_pack, status_pack) + self.assertLess(status_pack, button_pack) + def test_test_dictation_scrolled_window_has_no_shadow(self) -> None: """GTK3 ScrolledWindow default shadow is a left stripe on GNOME/Wayland (#847).""" footer_body = self.source_code.split("def _build_sidebar_footer")[1].split("\n def ")[0]