Compare commits
No commits in common. "101b6217bc0168b9d5a6fe6479d046f3bad400ac" and "60c0d0ac113fc25e1b6623c578c0db97951ef1aa" have entirely different histories.
101b6217bc
...
60c0d0ac11
@ -50,21 +50,6 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
<release version="0.10.0" date="2026-05-21">
|
|
||||||
<description translate="no">
|
|
||||||
<p>Version 0.10.0 release</p>
|
|
||||||
<ul>
|
|
||||||
<li>Responsive layout: sidebar collapses to an overlay panel on narrow windows</li>
|
|
||||||
<li>Narrow mode: single-panel view with Request/Response toggle buttons when sidebar is hidden</li>
|
|
||||||
<li>Adaptive request panel: Headers/Body/Scripts tabs stack vertically when the panel is narrow, with a hard minimum width</li>
|
|
||||||
<li>Import requests from .http files</li>
|
|
||||||
<li>Sidebar hamburger menu consolidating all actions (Add Project, Import, Preferences, Keyboard Shortcuts, About)</li>
|
|
||||||
<li>Method chips (GET/POST/PUT/DELETE color badges) in sidebar request list</li>
|
|
||||||
<li>Fix header bar clipping and window controls placement on narrow windows</li>
|
|
||||||
<li>Set minimum window width to prevent layout overflow</li>
|
|
||||||
</ul>
|
|
||||||
</description>
|
|
||||||
</release>
|
|
||||||
<release version="0.9.3" date="2026-05-19">
|
<release version="0.9.3" date="2026-05-19">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>Version 0.9.3 release</p>
|
<p>Version 0.9.3 release</p>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
project('roster',
|
project('roster',
|
||||||
version: '0.10.0',
|
version: '0.9.3',
|
||||||
meson_version: '>= 1.0.0',
|
meson_version: '>= 1.0.0',
|
||||||
default_options: [ 'warning_level=2', 'werror=false', ],
|
default_options: [ 'warning_level=2', 'werror=false', ],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -80,24 +80,28 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
def _build_ui(self) -> None:
|
def _build_ui(self) -> None:
|
||||||
"""Build the complete UI for this tab."""
|
"""Build the complete UI for this tab."""
|
||||||
|
|
||||||
# URL Input Section
|
# URL Input Section - outer container (store as instance var for dynamic updates)
|
||||||
self.url_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
self.url_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
||||||
self.url_container.set_margin_start(12)
|
self.url_container.set_margin_start(12)
|
||||||
self.url_container.set_margin_end(12)
|
self.url_container.set_margin_end(12)
|
||||||
self.url_container.set_margin_top(12)
|
self.url_container.set_margin_top(12)
|
||||||
self.url_container.set_margin_bottom(12)
|
self.url_container.set_margin_bottom(12)
|
||||||
|
|
||||||
|
# Environment Selector (left-aligned, outside clamp)
|
||||||
if self.project_id:
|
if self.project_id:
|
||||||
self._build_environment_selector_inline(self.url_container)
|
self._build_environment_selector_inline(self.url_container)
|
||||||
|
# Add visual separator/spacer after environment
|
||||||
self.env_separator = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
|
self.env_separator = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)
|
||||||
self.env_separator.set_margin_start(12)
|
self.env_separator.set_margin_start(12)
|
||||||
self.env_separator.set_margin_end(12)
|
self.env_separator.set_margin_end(12)
|
||||||
self.url_container.append(self.env_separator)
|
self.url_container.append(self.env_separator)
|
||||||
|
|
||||||
|
# URL bar (method, URL, send) - centered in clamp
|
||||||
url_clamp = Adw.Clamp(maximum_size=1000)
|
url_clamp = Adw.Clamp(maximum_size=1000)
|
||||||
url_clamp.set_hexpand(True)
|
url_clamp.set_hexpand(True)
|
||||||
self.url_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
|
self.url_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
|
||||||
|
|
||||||
|
# Method Dropdown
|
||||||
self.method_dropdown = Gtk.DropDown()
|
self.method_dropdown = Gtk.DropDown()
|
||||||
methods = Gtk.StringList()
|
methods = Gtk.StringList()
|
||||||
for method in ["GET", "POST", "PUT", "DELETE"]:
|
for method in ["GET", "POST", "PUT", "DELETE"]:
|
||||||
@ -107,12 +111,14 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
self.method_dropdown.set_enable_search(False)
|
self.method_dropdown.set_enable_search(False)
|
||||||
self.url_box.append(self.method_dropdown)
|
self.url_box.append(self.method_dropdown)
|
||||||
|
|
||||||
|
# URL Entry
|
||||||
self.url_entry = Gtk.Entry()
|
self.url_entry = Gtk.Entry()
|
||||||
self.url_entry.set_placeholder_text("Enter URL...")
|
self.url_entry.set_placeholder_text("Enter URL...")
|
||||||
self.url_entry.set_hexpand(True)
|
self.url_entry.set_hexpand(True)
|
||||||
self.url_entry.add_css_class("url-entry")
|
self.url_entry.add_css_class("url-entry")
|
||||||
self.url_box.append(self.url_entry)
|
self.url_box.append(self.url_entry)
|
||||||
|
|
||||||
|
# Send Button
|
||||||
self.send_button = Gtk.Button(icon_name="media-playback-start-symbolic")
|
self.send_button = Gtk.Button(icon_name="media-playback-start-symbolic")
|
||||||
self.send_button.set_tooltip_text("Send Request")
|
self.send_button.set_tooltip_text("Send Request")
|
||||||
self.send_button.add_css_class("suggested-action")
|
self.send_button.add_css_class("suggested-action")
|
||||||
@ -122,89 +128,62 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
self.url_container.append(url_clamp)
|
self.url_container.append(url_clamp)
|
||||||
self.append(self.url_container)
|
self.append(self.url_container)
|
||||||
|
|
||||||
# Build content panels
|
# Horizontal Split: Request | Response
|
||||||
self.request_panel = self._create_request_panel()
|
split_pane = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
self.response_panel = self._create_response_panel()
|
split_pane.set_vexpand(True)
|
||||||
|
split_pane.set_position(UI_PANE_REQUEST_RESPONSE_POSITION)
|
||||||
# Normal layout: horizontal split pane
|
split_pane.set_shrink_start_child(True) # request can collapse to 0
|
||||||
self.split_pane = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL)
|
split_pane.set_shrink_end_child(False) # response stays
|
||||||
self.split_pane.set_vexpand(True)
|
split_pane.set_resize_start_child(True) # request gives up space first
|
||||||
self.split_pane.set_position(UI_PANE_REQUEST_RESPONSE_POSITION)
|
split_pane.set_resize_end_child(False) # response keeps its size
|
||||||
self.split_pane.set_shrink_start_child(False)
|
|
||||||
self.split_pane.set_shrink_end_child(False)
|
|
||||||
self.split_pane.set_resize_start_child(True)
|
|
||||||
self.split_pane.set_resize_end_child(False)
|
|
||||||
self.split_pane.set_start_child(self.request_panel)
|
|
||||||
self.split_pane.set_end_child(self.response_panel)
|
|
||||||
|
|
||||||
# Narrow layout: single panel with Request/Response toggle
|
|
||||||
self._build_narrow_layout()
|
|
||||||
self._narrow_mode = False
|
|
||||||
|
|
||||||
self.append(self.split_pane)
|
|
||||||
|
|
||||||
def _create_request_panel(self) -> Gtk.Box:
|
|
||||||
"""Create the request panel with a responsive tab switcher."""
|
|
||||||
request_panel = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
||||||
request_panel.set_size_request(220, -1)
|
|
||||||
|
|
||||||
# Responsive tab switcher (custom, so orientation can change)
|
|
||||||
self.request_tab_switcher = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
|
||||||
self.request_tab_switcher.set_halign(Gtk.Align.CENTER)
|
|
||||||
self.request_tab_switcher.set_margin_top(8)
|
|
||||||
self.request_tab_switcher.set_margin_bottom(8)
|
|
||||||
self.request_tab_switcher.add_css_class("request-tab-switcher")
|
|
||||||
|
|
||||||
|
# Request Panel
|
||||||
|
request_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
self.request_stack = Gtk.Stack()
|
self.request_stack = Gtk.Stack()
|
||||||
self.request_stack.set_vexpand(True)
|
self.request_stack.set_vexpand(True)
|
||||||
self.request_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
|
self.request_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
|
||||||
self.request_stack.set_transition_duration(150)
|
self.request_stack.set_transition_duration(150)
|
||||||
|
|
||||||
# Build tab pages
|
request_switcher = Gtk.StackSwitcher()
|
||||||
|
request_switcher.set_stack(self.request_stack)
|
||||||
|
request_switcher.set_halign(Gtk.Align.CENTER)
|
||||||
|
request_switcher.set_margin_top(8)
|
||||||
|
request_switcher.set_margin_bottom(8)
|
||||||
|
|
||||||
|
request_box.append(request_switcher)
|
||||||
|
request_box.append(self.request_stack)
|
||||||
|
|
||||||
|
# Headers tab
|
||||||
self._build_headers_tab()
|
self._build_headers_tab()
|
||||||
|
|
||||||
|
# Body tab
|
||||||
self._build_body_tab()
|
self._build_body_tab()
|
||||||
|
|
||||||
|
# Scripts tab
|
||||||
self._build_scripts_tab()
|
self._build_scripts_tab()
|
||||||
|
|
||||||
# Build linked toggle buttons for each page
|
split_pane.set_start_child(request_box)
|
||||||
self._request_tab_buttons = {}
|
|
||||||
first_btn = None
|
|
||||||
for page_name, label in [("headers", "Headers"), ("body", "Body"), ("scripts", "Scripts")]:
|
|
||||||
btn = Gtk.ToggleButton(label=label)
|
|
||||||
btn.add_css_class("flat")
|
|
||||||
if first_btn is None:
|
|
||||||
btn.set_active(True)
|
|
||||||
first_btn = btn
|
|
||||||
else:
|
|
||||||
btn.set_group(first_btn)
|
|
||||||
btn.connect("toggled", self._on_request_tab_toggled, page_name)
|
|
||||||
self.request_tab_switcher.append(btn)
|
|
||||||
self._request_tab_buttons[page_name] = btn
|
|
||||||
|
|
||||||
request_panel.append(self.request_tab_switcher)
|
# Response Panel
|
||||||
request_panel.append(self.request_stack)
|
|
||||||
|
|
||||||
# Switch switcher orientation based on available width
|
|
||||||
request_panel.connect("notify::width", self._on_request_panel_width_changed)
|
|
||||||
|
|
||||||
return request_panel
|
|
||||||
|
|
||||||
def _create_response_panel(self) -> Gtk.Box:
|
|
||||||
"""Create the response panel."""
|
|
||||||
response_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
response_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
response_box.set_size_request(250, -1)
|
response_box.set_size_request(250, -1)
|
||||||
|
|
||||||
|
# Stack switcher (placed in bottom bar together with status info)
|
||||||
response_switcher = Gtk.StackSwitcher()
|
response_switcher = Gtk.StackSwitcher()
|
||||||
response_switcher.set_halign(Gtk.Align.START)
|
response_switcher.set_halign(Gtk.Align.START)
|
||||||
response_switcher.set_valign(Gtk.Align.CENTER)
|
response_switcher.set_valign(Gtk.Align.CENTER)
|
||||||
|
|
||||||
|
# Create a vertical paned for response stack and result panels
|
||||||
self.response_main_paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
|
self.response_main_paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
|
||||||
self.response_main_paned.set_vexpand(True)
|
self.response_main_paned.set_vexpand(True)
|
||||||
self.response_main_paned.set_position(UI_PANE_RESPONSE_DETAILS_POSITION)
|
self.response_main_paned.set_position(UI_PANE_RESPONSE_DETAILS_POSITION)
|
||||||
self.response_main_paned.set_shrink_start_child(False)
|
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_shrink_end_child(False)
|
||||||
self.response_main_paned.set_resize_start_child(True)
|
self.response_main_paned.set_resize_start_child(True)
|
||||||
self.response_main_paned.set_resize_end_child(True)
|
self.response_main_paned.set_resize_end_child(True)
|
||||||
|
|
||||||
|
# Response Stack
|
||||||
self.response_stack = Gtk.Stack()
|
self.response_stack = Gtk.Stack()
|
||||||
self.response_stack.set_vexpand(True)
|
self.response_stack.set_vexpand(True)
|
||||||
self.response_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
|
self.response_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
|
||||||
@ -213,6 +192,7 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
response_switcher.set_stack(self.response_stack)
|
response_switcher.set_stack(self.response_stack)
|
||||||
self.response_main_paned.set_start_child(self.response_stack)
|
self.response_main_paned.set_start_child(self.response_stack)
|
||||||
|
|
||||||
|
# Response headers
|
||||||
headers_scroll = Gtk.ScrolledWindow()
|
headers_scroll = Gtk.ScrolledWindow()
|
||||||
headers_scroll.set_vexpand(True)
|
headers_scroll.set_vexpand(True)
|
||||||
self.response_headers_textview = Gtk.TextView()
|
self.response_headers_textview = Gtk.TextView()
|
||||||
@ -225,6 +205,7 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
headers_scroll.set_child(self.response_headers_textview)
|
headers_scroll.set_child(self.response_headers_textview)
|
||||||
self.response_stack.add_titled(headers_scroll, "headers", "Headers")
|
self.response_stack.add_titled(headers_scroll, "headers", "Headers")
|
||||||
|
|
||||||
|
# Response body
|
||||||
body_scroll = Gtk.ScrolledWindow()
|
body_scroll = Gtk.ScrolledWindow()
|
||||||
body_scroll.set_vexpand(True)
|
body_scroll.set_vexpand(True)
|
||||||
self.response_body_sourceview = GtkSource.View()
|
self.response_body_sourceview = GtkSource.View()
|
||||||
@ -236,8 +217,10 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
self.response_body_sourceview.set_top_margin(12)
|
self.response_body_sourceview.set_top_margin(12)
|
||||||
self.response_body_sourceview.set_bottom_margin(12)
|
self.response_body_sourceview.set_bottom_margin(12)
|
||||||
|
|
||||||
|
# Set up theme
|
||||||
self._setup_sourceview_theme()
|
self._setup_sourceview_theme()
|
||||||
|
|
||||||
|
# Set font
|
||||||
css_provider = Gtk.CssProvider()
|
css_provider = Gtk.CssProvider()
|
||||||
css_provider.load_from_data(b"""
|
css_provider.load_from_data(b"""
|
||||||
textview {
|
textview {
|
||||||
@ -254,19 +237,24 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
body_scroll.set_child(self.response_body_sourceview)
|
body_scroll.set_child(self.response_body_sourceview)
|
||||||
self.response_stack.add_titled(body_scroll, "body", "Body")
|
self.response_stack.add_titled(body_scroll, "body", "Body")
|
||||||
|
|
||||||
|
# Create a second paned for preprocessing and script results
|
||||||
self.results_paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
|
self.results_paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
|
||||||
self.results_paned.set_vexpand(True)
|
self.results_paned.set_vexpand(True)
|
||||||
self.results_paned.set_position(UI_PANE_RESULTS_PANEL_POSITION)
|
self.results_paned.set_position(UI_PANE_RESULTS_PANEL_POSITION)
|
||||||
self.results_paned.set_visible(False)
|
self.results_paned.set_visible(False) # Initially hidden until scripts produce output
|
||||||
|
# Don't allow children to shrink below their minimum size
|
||||||
self.results_paned.set_shrink_start_child(False)
|
self.results_paned.set_shrink_start_child(False)
|
||||||
self.results_paned.set_shrink_end_child(False)
|
self.results_paned.set_shrink_end_child(False)
|
||||||
self.results_paned.set_resize_start_child(True)
|
self.results_paned.set_resize_start_child(True)
|
||||||
self.results_paned.set_resize_end_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 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
|
||||||
self.preprocessing_results_container.set_visible(False)
|
self.preprocessing_results_container.set_visible(False) # Initially hidden
|
||||||
|
# Set minimum height to ensure header is always visible (header ~40px + content min 60px)
|
||||||
self.preprocessing_results_container.set_size_request(-1, 100)
|
self.preprocessing_results_container.set_size_request(-1, 100)
|
||||||
|
|
||||||
|
# Header
|
||||||
preprocessing_header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
preprocessing_header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
||||||
preprocessing_header.set_margin_start(12)
|
preprocessing_header.set_margin_start(12)
|
||||||
preprocessing_header.set_margin_end(12)
|
preprocessing_header.set_margin_end(12)
|
||||||
@ -278,16 +266,19 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
preprocessing_label.set_halign(Gtk.Align.START)
|
preprocessing_label.set_halign(Gtk.Align.START)
|
||||||
preprocessing_header.append(preprocessing_label)
|
preprocessing_header.append(preprocessing_label)
|
||||||
|
|
||||||
|
# Spacer
|
||||||
preprocessing_spacer = Gtk.Box()
|
preprocessing_spacer = Gtk.Box()
|
||||||
preprocessing_spacer.set_hexpand(True)
|
preprocessing_spacer.set_hexpand(True)
|
||||||
preprocessing_header.append(preprocessing_spacer)
|
preprocessing_header.append(preprocessing_spacer)
|
||||||
|
|
||||||
|
# Status icon
|
||||||
self.preprocessing_status_icon = Gtk.Image()
|
self.preprocessing_status_icon = Gtk.Image()
|
||||||
self.preprocessing_status_icon.set_from_icon_name("object-select-symbolic")
|
self.preprocessing_status_icon.set_from_icon_name("object-select-symbolic")
|
||||||
preprocessing_header.append(self.preprocessing_status_icon)
|
preprocessing_header.append(self.preprocessing_status_icon)
|
||||||
|
|
||||||
self.preprocessing_results_container.append(preprocessing_header)
|
self.preprocessing_results_container.append(preprocessing_header)
|
||||||
|
|
||||||
|
# Output text view (scrollable, resizable)
|
||||||
self.preprocessing_output_scroll = Gtk.ScrolledWindow()
|
self.preprocessing_output_scroll = Gtk.ScrolledWindow()
|
||||||
self.preprocessing_output_scroll.set_vexpand(True)
|
self.preprocessing_output_scroll.set_vexpand(True)
|
||||||
self.preprocessing_output_scroll.set_min_content_height(60)
|
self.preprocessing_output_scroll.set_min_content_height(60)
|
||||||
@ -309,10 +300,13 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
|
|
||||||
self.results_paned.set_start_child(self.preprocessing_results_container)
|
self.results_paned.set_start_child(self.preprocessing_results_container)
|
||||||
|
|
||||||
|
# Script Results Panel (resizable)
|
||||||
self.script_results_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
|
self.script_results_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
|
||||||
self.script_results_container.set_visible(False)
|
self.script_results_container.set_visible(False) # Initially hidden
|
||||||
|
# Set minimum height to ensure header is always visible (header ~40px + content min 60px)
|
||||||
self.script_results_container.set_size_request(-1, 100)
|
self.script_results_container.set_size_request(-1, 100)
|
||||||
|
|
||||||
|
# Header
|
||||||
results_header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
results_header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
||||||
results_header.set_margin_start(12)
|
results_header.set_margin_start(12)
|
||||||
results_header.set_margin_end(12)
|
results_header.set_margin_end(12)
|
||||||
@ -324,16 +318,19 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
results_label.set_halign(Gtk.Align.START)
|
results_label.set_halign(Gtk.Align.START)
|
||||||
results_header.append(results_label)
|
results_header.append(results_label)
|
||||||
|
|
||||||
|
# Spacer
|
||||||
results_spacer = Gtk.Box()
|
results_spacer = Gtk.Box()
|
||||||
results_spacer.set_hexpand(True)
|
results_spacer.set_hexpand(True)
|
||||||
results_header.append(results_spacer)
|
results_header.append(results_spacer)
|
||||||
|
|
||||||
|
# Status icon
|
||||||
self.script_status_icon = Gtk.Image()
|
self.script_status_icon = Gtk.Image()
|
||||||
self.script_status_icon.set_from_icon_name("object-select-symbolic")
|
self.script_status_icon.set_from_icon_name("object-select-symbolic")
|
||||||
results_header.append(self.script_status_icon)
|
results_header.append(self.script_status_icon)
|
||||||
|
|
||||||
self.script_results_container.append(results_header)
|
self.script_results_container.append(results_header)
|
||||||
|
|
||||||
|
# Output text view (scrollable, resizable)
|
||||||
self.script_output_scroll = Gtk.ScrolledWindow()
|
self.script_output_scroll = Gtk.ScrolledWindow()
|
||||||
self.script_output_scroll.set_vexpand(True)
|
self.script_output_scroll.set_vexpand(True)
|
||||||
self.script_output_scroll.set_min_content_height(60)
|
self.script_output_scroll.set_min_content_height(60)
|
||||||
@ -354,10 +351,14 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
self.script_results_container.append(self.script_output_scroll)
|
self.script_results_container.append(self.script_output_scroll)
|
||||||
|
|
||||||
self.results_paned.set_end_child(self.script_results_container)
|
self.results_paned.set_end_child(self.script_results_container)
|
||||||
|
|
||||||
|
# Set the results paned as the end child of the main response paned
|
||||||
self.response_main_paned.set_end_child(self.results_paned)
|
self.response_main_paned.set_end_child(self.results_paned)
|
||||||
|
|
||||||
|
# Add the main paned to response_box
|
||||||
response_box.append(self.response_main_paned)
|
response_box.append(self.response_main_paned)
|
||||||
|
|
||||||
|
# Bottom bar: response tabs (left) + status info (right)
|
||||||
bottom_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
bottom_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
||||||
bottom_bar.set_margin_start(6)
|
bottom_bar.set_margin_start(6)
|
||||||
bottom_bar.set_margin_end(12)
|
bottom_bar.set_margin_end(12)
|
||||||
@ -382,94 +383,12 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
status_box.append(self.size_label)
|
status_box.append(self.size_label)
|
||||||
|
|
||||||
bottom_bar.append(status_box)
|
bottom_bar.append(status_box)
|
||||||
|
|
||||||
response_box.append(bottom_bar)
|
response_box.append(bottom_bar)
|
||||||
|
|
||||||
return response_box
|
split_pane.set_end_child(response_box)
|
||||||
|
|
||||||
def _build_narrow_layout(self) -> None:
|
self.append(split_pane)
|
||||||
"""Build the narrow single-panel layout with Request/Response toggle."""
|
|
||||||
self.narrow_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
||||||
self.narrow_box.set_vexpand(True)
|
|
||||||
|
|
||||||
toggle_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
|
|
||||||
toggle_bar.set_halign(Gtk.Align.CENTER)
|
|
||||||
toggle_bar.set_margin_top(6)
|
|
||||||
toggle_bar.set_margin_bottom(6)
|
|
||||||
toggle_bar.add_css_class("linked")
|
|
||||||
|
|
||||||
self.narrow_request_btn = Gtk.ToggleButton(label="Request")
|
|
||||||
self.narrow_request_btn.set_active(True)
|
|
||||||
|
|
||||||
self.narrow_response_btn = Gtk.ToggleButton(label="Response")
|
|
||||||
self.narrow_response_btn.set_group(self.narrow_request_btn)
|
|
||||||
|
|
||||||
self.narrow_request_btn.connect("toggled", self._on_narrow_panel_toggled, "request")
|
|
||||||
self.narrow_response_btn.connect("toggled", self._on_narrow_panel_toggled, "response")
|
|
||||||
|
|
||||||
toggle_bar.append(self.narrow_request_btn)
|
|
||||||
toggle_bar.append(self.narrow_response_btn)
|
|
||||||
self.narrow_box.append(toggle_bar)
|
|
||||||
|
|
||||||
self.narrow_stack = Gtk.Stack()
|
|
||||||
self.narrow_stack.set_vexpand(True)
|
|
||||||
self.narrow_stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
|
|
||||||
self.narrow_stack.set_transition_duration(200)
|
|
||||||
self.narrow_box.append(self.narrow_stack)
|
|
||||||
|
|
||||||
def set_narrow_mode(self, narrow: bool) -> None:
|
|
||||||
"""Switch between split-pane (normal) and single-panel (narrow) layout."""
|
|
||||||
if narrow == self._narrow_mode:
|
|
||||||
return
|
|
||||||
self._narrow_mode = narrow
|
|
||||||
|
|
||||||
if narrow:
|
|
||||||
self.split_pane.set_start_child(None)
|
|
||||||
self.split_pane.set_end_child(None)
|
|
||||||
self.remove(self.split_pane)
|
|
||||||
|
|
||||||
self.narrow_stack.add_named(self.request_panel, "request")
|
|
||||||
self.narrow_stack.add_named(self.response_panel, "response")
|
|
||||||
|
|
||||||
# Sync stack visible child to match current button state
|
|
||||||
# (button state persists across wide↔narrow transitions)
|
|
||||||
if self.narrow_response_btn.get_active():
|
|
||||||
self.narrow_stack.set_visible_child_name("response")
|
|
||||||
else:
|
|
||||||
self.narrow_stack.set_visible_child_name("request")
|
|
||||||
|
|
||||||
self.append(self.narrow_box)
|
|
||||||
else:
|
|
||||||
self.narrow_stack.remove(self.request_panel)
|
|
||||||
self.narrow_stack.remove(self.response_panel)
|
|
||||||
self.remove(self.narrow_box)
|
|
||||||
|
|
||||||
self.split_pane.set_start_child(self.request_panel)
|
|
||||||
self.split_pane.set_end_child(self.response_panel)
|
|
||||||
self.append(self.split_pane)
|
|
||||||
|
|
||||||
def _on_request_tab_toggled(self, button: Gtk.ToggleButton, page_name: str) -> None:
|
|
||||||
"""Handle request tab button toggle."""
|
|
||||||
if button.get_active():
|
|
||||||
self.request_stack.set_visible_child_name(page_name)
|
|
||||||
|
|
||||||
def _on_request_panel_width_changed(self, widget, pspec) -> None:
|
|
||||||
"""Switch request tab switcher to vertical when panel is narrow."""
|
|
||||||
width = widget.get_width()
|
|
||||||
if width > 0 and width < 260:
|
|
||||||
if self.request_tab_switcher.get_orientation() != Gtk.Orientation.VERTICAL:
|
|
||||||
self.request_tab_switcher.set_orientation(Gtk.Orientation.VERTICAL)
|
|
||||||
self.request_tab_switcher.set_halign(Gtk.Align.START)
|
|
||||||
self.request_tab_switcher.set_margin_start(8)
|
|
||||||
elif width >= 260:
|
|
||||||
if self.request_tab_switcher.get_orientation() != Gtk.Orientation.HORIZONTAL:
|
|
||||||
self.request_tab_switcher.set_orientation(Gtk.Orientation.HORIZONTAL)
|
|
||||||
self.request_tab_switcher.set_halign(Gtk.Align.CENTER)
|
|
||||||
self.request_tab_switcher.set_margin_start(0)
|
|
||||||
|
|
||||||
def _on_narrow_panel_toggled(self, button: Gtk.ToggleButton, panel_name: str) -> None:
|
|
||||||
"""Handle narrow mode Request/Response toggle."""
|
|
||||||
if button.get_active():
|
|
||||||
self.narrow_stack.set_visible_child_name(panel_name)
|
|
||||||
|
|
||||||
def _build_headers_tab(self) -> None:
|
def _build_headers_tab(self) -> None:
|
||||||
"""Build the headers tab."""
|
"""Build the headers tab."""
|
||||||
@ -1012,11 +931,6 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
# Switch to body tab
|
# Switch to body tab
|
||||||
self.response_stack.set_visible_child_name("body")
|
self.response_stack.set_visible_child_name("body")
|
||||||
|
|
||||||
# In narrow mode, automatically show the response panel
|
|
||||||
if self._narrow_mode:
|
|
||||||
self.narrow_stack.set_visible_child_name("response")
|
|
||||||
self.narrow_response_btn.set_active(True)
|
|
||||||
|
|
||||||
def display_error(self, error: str) -> None:
|
def display_error(self, error: str) -> None:
|
||||||
"""Display error in this tab's UI."""
|
"""Display error in this tab's UI."""
|
||||||
self.status_label.set_text("Error")
|
self.status_label.set_text("Error")
|
||||||
@ -1032,10 +946,6 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
source_buffer.set_text(error)
|
source_buffer.set_text(error)
|
||||||
source_buffer.set_language(None)
|
source_buffer.set_language(None)
|
||||||
|
|
||||||
if self._narrow_mode:
|
|
||||||
self.narrow_stack.set_visible_child_name("response")
|
|
||||||
self.narrow_response_btn.set_active(True)
|
|
||||||
|
|
||||||
def display_script_results(self, script_result):
|
def display_script_results(self, script_result):
|
||||||
"""Display script execution results."""
|
"""Display script execution results."""
|
||||||
from .script_executor import ScriptResult
|
from .script_executor import ScriptResult
|
||||||
|
|||||||
@ -108,9 +108,6 @@ class RosterWindow(Adw.ApplicationWindow):
|
|||||||
GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE
|
GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE
|
||||||
)
|
)
|
||||||
|
|
||||||
# Switch tab widgets to narrow layout when sidebar collapses
|
|
||||||
self.split_view.connect("notify::collapsed", self._on_split_view_collapsed_changed)
|
|
||||||
|
|
||||||
# Setup UI
|
# Setup UI
|
||||||
self._setup_tab_system()
|
self._setup_tab_system()
|
||||||
self._load_projects()
|
self._load_projects()
|
||||||
@ -122,12 +119,6 @@ class RosterWindow(Adw.ApplicationWindow):
|
|||||||
# Create first tab
|
# Create first tab
|
||||||
self._create_new_tab()
|
self._create_new_tab()
|
||||||
|
|
||||||
def _on_split_view_collapsed_changed(self, split_view, pspec) -> None:
|
|
||||||
"""Switch all tab widgets to narrow or normal layout based on sidebar state."""
|
|
||||||
is_narrow = split_view.get_collapsed()
|
|
||||||
for widget in self.page_to_widget.values():
|
|
||||||
widget.set_narrow_mode(is_narrow)
|
|
||||||
|
|
||||||
def _on_close_request(self, window) -> bool:
|
def _on_close_request(self, window) -> bool:
|
||||||
"""Handle window close request - warn if there are unsaved changes."""
|
"""Handle window close request - warn if there are unsaved changes."""
|
||||||
# Check if any tabs have unsaved changes
|
# Check if any tabs have unsaved changes
|
||||||
@ -176,15 +167,15 @@ class RosterWindow(Adw.ApplicationWindow):
|
|||||||
/* AdwToolbarView handles header bar heights automatically */
|
/* AdwToolbarView handles header bar heights automatically */
|
||||||
/* Just add minimal custom styling for other elements */
|
/* Just add minimal custom styling for other elements */
|
||||||
|
|
||||||
/* Request tab switcher (Headers/Body/Scripts) */
|
/* Stack switchers styling (Headers/Body tabs) */
|
||||||
.request-tab-switcher button {
|
stackswitcher button {
|
||||||
padding: 6px 16px;
|
padding: 6px 16px;
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.request-tab-switcher button:checked {
|
stackswitcher button:checked {
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,10 +405,6 @@ class RosterWindow(Adw.ApplicationWindow):
|
|||||||
# incorrectly marked all variables as undefined.
|
# incorrectly marked all variables as undefined.
|
||||||
widget._update_variable_indicators()
|
widget._update_variable_indicators()
|
||||||
|
|
||||||
# Apply narrow mode if sidebar is currently collapsed
|
|
||||||
if self.split_view.get_collapsed():
|
|
||||||
widget.set_narrow_mode(True)
|
|
||||||
|
|
||||||
# Connect to send button
|
# Connect to send button
|
||||||
widget.send_button.connect("clicked", lambda btn: self._on_send_clicked(widget))
|
widget.send_button.connect("clicked", lambda btn: self._on_send_clicked(widget))
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user