From 33e9df9139c7de743223af99b717f2816b2354bc Mon Sep 17 00:00:00 2001 From: Pavel Baksy Date: Sat, 3 Jan 2026 09:17:20 +0100 Subject: [PATCH] Make result and history panels resizable with minimum size --- src/main-window.ui | 7 +- src/request_tab_widget.py | 257 +++++++++++++++++--------------------- 2 files changed, 117 insertions(+), 147 deletions(-) diff --git a/src/main-window.ui b/src/main-window.ui index b89f71c..3061b4b 100644 --- a/src/main-window.ui +++ b/src/main-window.ui @@ -146,7 +146,8 @@ vertical 600 False - True + + False True True @@ -161,6 +162,8 @@ vertical + + 50 @@ -188,7 +191,7 @@ True - 150 + 50 diff --git a/src/request_tab_widget.py b/src/request_tab_widget.py index 24643fc..07e3c48 100644 --- a/src/request_tab_widget.py +++ b/src/request_tab_widget.py @@ -149,19 +149,32 @@ class RequestTabWidget(Gtk.Box): # Response Panel response_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + + # Stack switcher at the top + response_switcher = Gtk.StackSwitcher() + response_switcher.set_halign(Gtk.Align.CENTER) + response_switcher.set_margin_top(8) + response_switcher.set_margin_bottom(8) + response_box.append(response_switcher) + + # Create a vertical paned for response stack and result panels + self.response_main_paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL) + self.response_main_paned.set_vexpand(True) + self.response_main_paned.set_position(400) + self.response_main_paned.set_shrink_start_child(False) + # Don't allow results panels to shrink below their minimum size + self.response_main_paned.set_shrink_end_child(False) + self.response_main_paned.set_resize_start_child(True) + self.response_main_paned.set_resize_end_child(True) + + # Response Stack self.response_stack = Gtk.Stack() self.response_stack.set_vexpand(True) self.response_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self.response_stack.set_transition_duration(150) - response_switcher = Gtk.StackSwitcher() response_switcher.set_stack(self.response_stack) - response_switcher.set_halign(Gtk.Align.CENTER) - response_switcher.set_margin_top(8) - response_switcher.set_margin_bottom(8) - - response_box.append(response_switcher) - response_box.append(self.response_stack) + self.response_main_paned.set_start_child(self.response_stack) # Response headers headers_scroll = Gtk.ScrolledWindow() @@ -208,14 +221,21 @@ class RequestTabWidget(Gtk.Box): body_scroll.set_child(self.response_body_sourceview) self.response_stack.add_titled(body_scroll, "body", "Body") - # Preprocessing Results Panel (collapsible) + # Create a second paned for preprocessing and script results + self.results_paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL) + self.results_paned.set_vexpand(True) + self.results_paned.set_position(120) + # Don't allow children to shrink below their minimum size + self.results_paned.set_shrink_start_child(False) + self.results_paned.set_shrink_end_child(False) + self.results_paned.set_resize_start_child(True) + self.results_paned.set_resize_end_child(True) + + # Preprocessing Results Panel (resizable) self.preprocessing_results_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) self.preprocessing_results_container.set_visible(False) # Initially hidden - - # Separator - preprocessing_separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) - preprocessing_separator.set_margin_top(6) - self.preprocessing_results_container.append(preprocessing_separator) + # Set minimum height to ensure header is always visible (header ~40px + content min 60px) + self.preprocessing_results_container.set_size_request(-1, 100) # Header preprocessing_header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) @@ -241,10 +261,10 @@ class RequestTabWidget(Gtk.Box): self.preprocessing_results_container.append(preprocessing_header) - # Output text view (scrollable) + # Output text view (scrollable, resizable) self.preprocessing_output_scroll = Gtk.ScrolledWindow() + self.preprocessing_output_scroll.set_vexpand(True) self.preprocessing_output_scroll.set_min_content_height(60) - self.preprocessing_output_scroll.set_max_content_height(60) self.preprocessing_output_scroll.set_margin_start(12) self.preprocessing_output_scroll.set_margin_end(12) self.preprocessing_output_scroll.set_margin_bottom(6) @@ -261,46 +281,13 @@ class RequestTabWidget(Gtk.Box): self.preprocessing_output_scroll.set_child(self.preprocessing_output_textview) self.preprocessing_results_container.append(self.preprocessing_output_scroll) - # Expander control (separator-based, shown when output > 3 lines) - self.preprocessing_expander = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) - self.preprocessing_expander.set_margin_start(12) - self.preprocessing_expander.set_margin_end(12) - self.preprocessing_expander.set_margin_bottom(6) - self.preprocessing_expander.set_visible(False) # Hidden by default + self.results_paned.set_start_child(self.preprocessing_results_container) - preprocessing_sep1 = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) - preprocessing_sep1.set_hexpand(True) - self.preprocessing_expander.append(preprocessing_sep1) - - self.preprocessing_expander_label = Gtk.Label(label="Show full output") - self.preprocessing_expander_label.add_css_class("dim-label") - self.preprocessing_expander_label.add_css_class("caption") - self.preprocessing_expander.append(self.preprocessing_expander_label) - - preprocessing_sep2 = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) - preprocessing_sep2.set_hexpand(True) - self.preprocessing_expander.append(preprocessing_sep2) - - # Add click gesture for expander - preprocessing_expander_gesture = Gtk.GestureClick() - preprocessing_expander_gesture.connect("released", self._on_preprocessing_expander_clicked) - self.preprocessing_expander.add_controller(preprocessing_expander_gesture) - - self.preprocessing_results_container.append(self.preprocessing_expander) - - # Track expansion state - self.preprocessing_output_expanded = False - - response_box.append(self.preprocessing_results_container) - - # Script Results Panel (collapsible) + # Script Results Panel (resizable) self.script_results_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) self.script_results_container.set_visible(False) # Initially hidden - - # Separator - results_separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) - results_separator.set_margin_top(6) - self.script_results_container.append(results_separator) + # Set minimum height to ensure header is always visible (header ~40px + content min 60px) + self.script_results_container.set_size_request(-1, 100) # Header results_header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) @@ -326,10 +313,10 @@ class RequestTabWidget(Gtk.Box): self.script_results_container.append(results_header) - # Output text view (scrollable) + # Output text view (scrollable, resizable) self.script_output_scroll = Gtk.ScrolledWindow() + self.script_output_scroll.set_vexpand(True) self.script_output_scroll.set_min_content_height(60) - self.script_output_scroll.set_max_content_height(60) self.script_output_scroll.set_margin_start(12) self.script_output_scroll.set_margin_end(12) self.script_output_scroll.set_margin_bottom(6) @@ -346,37 +333,13 @@ class RequestTabWidget(Gtk.Box): self.script_output_scroll.set_child(self.script_output_textview) self.script_results_container.append(self.script_output_scroll) - # Expander control (separator-based, shown when output > 3 lines) - self.script_expander = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) - self.script_expander.set_margin_start(12) - self.script_expander.set_margin_end(12) - self.script_expander.set_margin_bottom(6) - self.script_expander.set_visible(False) # Hidden by default + self.results_paned.set_end_child(self.script_results_container) - expander_sep1 = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) - expander_sep1.set_hexpand(True) - self.script_expander.append(expander_sep1) + # Set the results paned as the end child of the main response paned + self.response_main_paned.set_end_child(self.results_paned) - self.script_expander_label = Gtk.Label(label="Show full output") - self.script_expander_label.add_css_class("dim-label") - self.script_expander_label.add_css_class("caption") - self.script_expander.append(self.script_expander_label) - - expander_sep2 = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) - expander_sep2.set_hexpand(True) - self.script_expander.append(expander_sep2) - - # Add click gesture for expander - expander_gesture = Gtk.GestureClick() - expander_gesture.connect("released", self._on_script_expander_clicked) - self.script_expander.add_controller(expander_gesture) - - self.script_results_container.append(self.script_expander) - - # Track expansion state - self.script_output_expanded = False - - response_box.append(self.script_results_container) + # Add the main paned to response_box + response_box.append(self.response_main_paned) # Status Bar status_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12) @@ -957,6 +920,9 @@ class RequestTabWidget(Gtk.Box): # Show container self.script_results_container.set_visible(True) + # Adjust paned positions to make the panel visible (use idle_add to wait for proper sizing) + GLib.idle_add(self._adjust_paned_for_script_results) + # Set status icon if script_result.success: self.script_status_icon.set_from_icon_name("emblem-ok-symbolic") @@ -987,44 +953,43 @@ class RequestTabWidget(Gtk.Box): buffer = self.script_output_textview.get_buffer() buffer.set_text(output_text) - # Determine if expander is needed - num_lines = output_text.count('\n') + 1 - needs_expander = num_lines > 3 or len(output_text) > 150 - - if needs_expander: - self.script_expander.set_visible(True) - self.script_expander.set_cursor_from_name("pointer") - else: - self.script_expander.set_visible(False) - - # Reset expansion state - self.script_output_expanded = False - self.script_output_scroll.set_min_content_height(60) - self.script_output_scroll.set_max_content_height(60) - self.script_expander_label.set_text("Show full output") - def _clear_script_results(self): """Clear and hide script results panel.""" self.script_results_container.set_visible(False) buffer = self.script_output_textview.get_buffer() buffer.set_text("") - self.script_expander.set_visible(False) - self.script_output_expanded = False - def _on_script_expander_clicked(self, gesture, n_press, x, y): - """Toggle script output expansion.""" - self.script_output_expanded = not self.script_output_expanded - - if self.script_output_expanded: - # Expand: set max first to avoid assertion errors - self.script_output_scroll.set_max_content_height(150) - self.script_output_scroll.set_min_content_height(150) - self.script_expander_label.set_text("Collapse output") + # Update results_paned minimum size based on remaining visible panels + if self.preprocessing_results_container.get_visible(): + self.results_paned.set_size_request(-1, 100) # Only preprocessing visible else: - # Collapse: set min first to avoid assertion errors - self.script_output_scroll.set_min_content_height(60) - self.script_output_scroll.set_max_content_height(60) - self.script_expander_label.set_text("Show full output") + self.results_paned.set_size_request(-1, -1) # No panels visible, no minimum + + def _adjust_paned_for_script_results(self): + """Adjust paned positions to show script results panel.""" + # Ensure results_paned has proper minimum size based on visible panels + if self.preprocessing_results_container.get_visible(): + # Both panels visible: minimum 200px (100px each) + self.results_paned.set_size_request(-1, 200) + else: + # Only script results visible: minimum 100px + self.results_paned.set_size_request(-1, 100) + + # Reserve space in the main paned for results + main_height = self.response_main_paned.get_height() + if main_height > 200: + # Set position to show ~150px of results at the bottom + self.response_main_paned.set_position(main_height - 150) + + # Adjust results paned to show script output (bottom panel) + results_height = self.results_paned.get_height() + if results_height > 150: + # If preprocessing is visible, share space + if self.preprocessing_results_container.get_visible(): + self.results_paned.set_position(120) # Give preprocessing minimal space + # If only script results, the paned will show it automatically + + return False # Don't repeat def display_preprocessing_results(self, preprocessing_result): """Display preprocessing execution results.""" @@ -1036,6 +1001,9 @@ class RequestTabWidget(Gtk.Box): # Show container self.preprocessing_results_container.set_visible(True) + # Adjust paned positions to make the panel visible (use idle_add to wait for proper sizing) + GLib.idle_add(self._adjust_paned_for_preprocessing_results) + # Set status icon if preprocessing_result.success: self.preprocessing_status_icon.set_from_icon_name("emblem-ok-symbolic") @@ -1073,44 +1041,43 @@ class RequestTabWidget(Gtk.Box): buffer = self.preprocessing_output_textview.get_buffer() buffer.set_text(output_text) - # Determine if expander is needed - num_lines = output_text.count('\n') + 1 - needs_expander = num_lines > 3 or len(output_text) > 150 - - if needs_expander: - self.preprocessing_expander.set_visible(True) - self.preprocessing_expander.set_cursor_from_name("pointer") - else: - self.preprocessing_expander.set_visible(False) - - # Reset expansion state - self.preprocessing_output_expanded = False - self.preprocessing_output_scroll.set_min_content_height(60) - self.preprocessing_output_scroll.set_max_content_height(60) - self.preprocessing_expander_label.set_text("Show full output") - def _clear_preprocessing_results(self): """Clear and hide preprocessing results panel.""" self.preprocessing_results_container.set_visible(False) buffer = self.preprocessing_output_textview.get_buffer() buffer.set_text("") - self.preprocessing_expander.set_visible(False) - self.preprocessing_output_expanded = False - def _on_preprocessing_expander_clicked(self, gesture, n_press, x, y): - """Toggle preprocessing output expansion.""" - self.preprocessing_output_expanded = not self.preprocessing_output_expanded - - if self.preprocessing_output_expanded: - # Expand: set max first to avoid assertion errors - self.preprocessing_output_scroll.set_max_content_height(150) - self.preprocessing_output_scroll.set_min_content_height(150) - self.preprocessing_expander_label.set_text("Collapse output") + # Update results_paned minimum size based on remaining visible panels + if self.script_results_container.get_visible(): + self.results_paned.set_size_request(-1, 100) # Only script results visible else: - # Collapse: set min first to avoid assertion errors - self.preprocessing_output_scroll.set_min_content_height(60) - self.preprocessing_output_scroll.set_max_content_height(60) - self.preprocessing_expander_label.set_text("Show full output") + self.results_paned.set_size_request(-1, -1) # No panels visible, no minimum + + def _adjust_paned_for_preprocessing_results(self): + """Adjust paned positions to show preprocessing results panel.""" + # Ensure results_paned has proper minimum size based on visible panels + if self.script_results_container.get_visible(): + # Both panels visible: minimum 200px (100px each) + self.results_paned.set_size_request(-1, 200) + else: + # Only preprocessing visible: minimum 100px + self.results_paned.set_size_request(-1, 100) + + # Reserve space in the main paned for results + main_height = self.response_main_paned.get_height() + if main_height > 200: + # Set position to show ~150px of results at the bottom + self.response_main_paned.set_position(main_height - 150) + + # Adjust results paned to show preprocessing output (top panel) + results_height = self.results_paned.get_height() + if results_height > 150: + # If script results is also visible, share space equally + if self.script_results_container.get_visible(): + self.results_paned.set_position(results_height // 2) # Split space + # If only preprocessing, the paned will show it automatically + + return False # Don't repeat def _extract_content_type(self, headers_text): """Extract content-type from response headers."""