Make result and history panels resizable with minimum size

This commit is contained in:
vesp 2026-01-03 09:17:20 +01:00
parent 38742054ba
commit b38c1e77c7
2 changed files with 117 additions and 147 deletions

View File

@ -146,7 +146,8 @@
<property name="orientation">vertical</property>
<property name="position">600</property>
<property name="shrink-start-child">False</property>
<property name="shrink-end-child">True</property>
<!-- Don't allow history panel to shrink below its minimum size -->
<property name="shrink-end-child">False</property>
<property name="resize-start-child">True</property>
<property name="resize-end-child">True</property>
@ -161,6 +162,8 @@
<property name="end-child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<!-- Set minimum height to ensure header is always visible -->
<property name="height-request">50</property>
<!-- History Header -->
<child>
@ -188,7 +191,7 @@
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">True</property>
<property name="min-content-height">150</property>
<property name="min-content-height">50</property>
<child>
<object class="GtkListBox" id="history_listbox">

View File

@ -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."""