Compare commits

...

11 Commits

3 changed files with 160 additions and 150 deletions

View File

@ -3,7 +3,13 @@
<requires lib="gtk" version="4.0"/> <requires lib="gtk" version="4.0"/>
<requires lib="Adw" version="1.0"/> <requires lib="Adw" version="1.0"/>
<menu id="import_menu"> <menu id="sidebar_menu">
<section>
<item>
<attribute name="label">Add Project</attribute>
<attribute name="action">win.add-project</attribute>
</item>
</section>
<section> <section>
<item> <item>
<attribute name="label">Import from OpenAPI / Swagger</attribute> <attribute name="label">Import from OpenAPI / Swagger</attribute>
@ -18,133 +24,148 @@
<attribute name="action">win.import-http-file</attribute> <attribute name="action">win.import-http-file</attribute>
</item> </item>
</section> </section>
<section>
<item>
<attribute name="label" translatable="yes">_Preferences</attribute>
<attribute name="action">app.preferences</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
<attribute name="action">app.shortcuts</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_About Roster</attribute>
<attribute name="action">app.about</attribute>
</item>
</section>
</menu> </menu>
<template class="RosterWindow" parent="AdwApplicationWindow"> <template class="RosterWindow" parent="AdwApplicationWindow">
<property name="default-width">1200</property> <property name="default-width">1200</property>
<property name="default-height">800</property> <property name="default-height">800</property>
<property name="width-request">470</property>
<property name="content"> <property name="content">
<object class="AdwToastOverlay" id="toast_overlay"> <object class="AdwToastOverlay" id="toast_overlay">
<property name="child"> <property name="child">
<object class="GtkPaned" id="main_pane"> <object class="AdwOverlaySplitView" id="split_view">
<property name="orientation">horizontal</property> <property name="min-sidebar-width">200</property>
<property name="position">180</property> <property name="max-sidebar-width">320</property>
<property name="shrink-start-child">False</property>
<property name="resize-start-child">True</property>
<property name="shrink-end-child">False</property>
<property name="resize-end-child">True</property>
<property name="wide-handle">False</property>
<!-- LEFT: Sidebar Panel with AdwToolbarView --> <!-- LEFT: Sidebar Panel with AdwToolbarView -->
<property name="start-child"> <property name="sidebar">
<object class="AdwToolbarView"> <object class="AdwToolbarView">
<property name="width-request">200</property>
<!-- Sidebar Header Bar --> <!-- Sidebar Header Bar -->
<child type="top"> <child type="top">
<object class="AdwHeaderBar"> <object class="AdwHeaderBar">
<property name="show-end-title-buttons">False</property> <property name="show-end-title-buttons">False</property>
<property name="show-start-title-buttons">False</property> <property name="show-start-title-buttons">False</property>
<property name="title-widget"> <property name="title-widget">
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label">Projects</property> <property name="label">Projects</property>
<style>
<class name="title"/>
</style>
</object>
</property>
<child type="start">
<object class="GtkWindowControls">
<property name="side">start</property>
</object>
</child>
<child type="end">
<object class="GtkButton" id="add_project_button">
<property name="icon-name">list-add-symbolic</property>
<property name="tooltip-text">Add Project</property>
<signal name="clicked" handler="on_add_project_clicked"/>
<style>
<class name="flat"/>
</style>
</object>
</child>
<child type="end">
<object class="GtkMenuButton" id="import_menu_button">
<property name="icon-name">papyrus-vertical-symbolic</property>
<property name="tooltip-text">Import</property>
<property name="menu-model">import_menu</property>
<style>
<class name="flat"/>
</style>
</object>
</child>
</object>
</child>
<!-- Sidebar Content -->
<property name="content">
<object class="GtkBox">
<property name="orientation">vertical</property>
<!-- Projects List -->
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">True</property>
<child>
<object class="GtkListBox" id="projects_listbox">
<style> <style>
<class name="navigation-sidebar"/> <class name="title"/>
</style>
</object>
</property>
<child type="start">
<object class="GtkWindowControls">
<property name="side">start</property>
</object>
</child>
<child type="end">
<object class="GtkMenuButton" id="sidebar_hamburger_button">
<property name="primary">True</property>
<property name="icon-name">open-menu-symbolic</property>
<property name="tooltip-text">Menu</property>
<property name="menu-model">sidebar_menu</property>
<style>
<class name="flat"/>
</style> </style>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
<!-- Sidebar Content -->
<property name="content">
<object class="GtkBox">
<property name="orientation">vertical</property>
<!-- Projects List -->
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">True</property>
<child>
<object class="GtkListBox" id="projects_listbox">
<style>
<class name="navigation-sidebar"/>
</style>
</object>
</child>
</object>
</child>
</object>
</property>
</object> </object>
</property> </property>
</object>
</property>
<!-- RIGHT: Main Content Panel with AdwToolbarView --> <!-- RIGHT: Main Content Panel with AdwToolbarView -->
<property name="end-child"> <property name="content">
<object class="AdwToolbarView"> <object class="AdwToolbarView">
<!-- Main Header Bar --> <!-- Main Header Bar -->
<child type="top"> <child type="top">
<object class="AdwHeaderBar"> <object class="AdwHeaderBar">
<property name="show-title">False</property> <property name="show-title">False</property>
<property name="show-start-title-buttons">False</property> <property name="show-start-title-buttons">False</property>
<property name="show-end-title-buttons">False</property> <property name="show-end-title-buttons">False</property>
<!-- Left side buttons --> <!-- Sidebar toggle (only visible when collapsed) -->
<child type="start"> <child type="start">
<object class="GtkButton" id="save_request_button"> <object class="GtkToggleButton" id="sidebar_toggle_button">
<property name="icon-name">document-save-symbolic</property> <property name="icon-name">sidebar-show-symbolic</property>
<property name="tooltip-text">Save Current Request (Ctrl+S)</property> <property name="tooltip-text">Show Sidebar</property>
<signal name="clicked" handler="on_save_request_clicked"/> <style>
<style> <class name="flat"/>
<class name="flat"/> </style>
</style> <binding name="visible">
</object> <lookup name="collapsed">split_view</lookup>
</child> </binding>
</object>
</child>
<child type="start"> <!-- Left side buttons -->
<object class="GtkButton" id="export_request_button"> <child type="start">
<property name="icon-name">export-symbolic</property> <object class="GtkButton" id="save_request_button">
<property name="tooltip-text">Export as cURL</property> <property name="icon-name">document-save-symbolic</property>
<signal name="clicked" handler="on_export_request_clicked"/> <property name="tooltip-text">Save Current Request (Ctrl+S)</property>
<style> <signal name="clicked" handler="on_save_request_clicked"/>
<class name="flat"/> <style>
</style> <class name="flat"/>
</object> </style>
</child> </object>
</child>
<child type="start">
<object class="GtkButton" id="export_request_button">
<property name="icon-name">export-symbolic</property>
<property name="tooltip-text">Export as cURL</property>
<signal name="clicked" handler="on_export_request_clicked"/>
<style>
<class name="flat"/>
</style>
</object>
</child>
<!-- Right side buttons --> <!-- Window controls: separate end child so it's always rightmost -->
<child type="end"> <child type="end">
<object class="GtkBox"> <object class="GtkWindowControls">
<property name="spacing">6</property> <property name="side">end</property>
</object>
</child>
<!-- New Request Button --> <!-- Right side buttons -->
<child> <child type="end">
<object class="GtkButton" id="new_request_button"> <object class="GtkButton" id="new_request_button">
<property name="icon-name">list-add-symbolic</property> <property name="icon-name">list-add-symbolic</property>
<property name="tooltip-text">New Request (Ctrl+T)</property> <property name="tooltip-text">New Request (Ctrl+T)</property>
@ -153,27 +174,8 @@
</style> </style>
</object> </object>
</child> </child>
<!-- Main Menu -->
<child>
<object class="GtkMenuButton">
<property name="primary">True</property>
<property name="icon-name">open-menu-symbolic</property>
<property name="tooltip-text" translatable="yes">Main Menu</property>
<property name="menu-model">primary_menu</property>
</object>
</child>
<!-- Window Controls -->
<child>
<object class="GtkWindowControls">
<property name="side">end</property>
</object>
</child>
</object> </object>
</child> </child>
</object>
</child>
<!-- Tab Bar as separate top bar --> <!-- Tab Bar as separate top bar -->
<child type="top"> <child type="top">
@ -256,22 +258,14 @@
</property> </property>
</object> </object>
</property> </property>
<!-- Collapse sidebar when window is narrow -->
<child>
<object class="AdwBreakpoint">
<condition>max-width: 700sp</condition>
<setter object="split_view" property="collapsed">True</setter>
</object>
</child>
</template> </template>
<menu id="primary_menu">
<section>
<item>
<attribute name="label" translatable="yes">_Preferences</attribute>
<attribute name="action">app.preferences</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
<attribute name="action">app.shortcuts</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_About Roster</attribute>
<attribute name="action">app.about</attribute>
</item>
</section>
</menu>
</interface> </interface>

View File

@ -115,6 +115,7 @@ class RequestTabWidget(Gtk.Box):
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_box.append(self.url_entry) self.url_box.append(self.url_entry)
# Send Button # Send Button
@ -131,10 +132,10 @@ class RequestTabWidget(Gtk.Box):
split_pane = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL) split_pane = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL)
split_pane.set_vexpand(True) split_pane.set_vexpand(True)
split_pane.set_position(UI_PANE_REQUEST_RESPONSE_POSITION) split_pane.set_position(UI_PANE_REQUEST_RESPONSE_POSITION)
split_pane.set_shrink_start_child(False) split_pane.set_shrink_start_child(True) # request can collapse to 0
split_pane.set_shrink_end_child(False) split_pane.set_shrink_end_child(False) # response stays
split_pane.set_resize_start_child(True) split_pane.set_resize_start_child(True) # request gives up space first
split_pane.set_resize_end_child(True) split_pane.set_resize_end_child(False) # response keeps its size
# Request Panel # Request Panel
request_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) request_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
@ -165,6 +166,7 @@ class RequestTabWidget(Gtk.Box):
# Response Panel # 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)
# Stack switcher (placed in bottom bar together with status info) # Stack switcher (placed in bottom bar together with status info)
response_switcher = Gtk.StackSwitcher() response_switcher = Gtk.StackSwitcher()

View File

@ -19,7 +19,7 @@
import gi import gi
gi.require_version('GtkSource', '5') gi.require_version('GtkSource', '5')
from gi.repository import Adw, Gtk, GLib, Gio, GtkSource from gi.repository import Adw, Gtk, GLib, Gio, GtkSource, GObject
from typing import Dict, Optional from typing import Dict, Optional
import logging import logging
from .models import HttpRequest, HttpResponse, HistoryEntry, RequestTab from .models import HttpRequest, HttpResponse, HistoryEntry, RequestTab
@ -63,14 +63,12 @@ class RosterWindow(Adw.ApplicationWindow):
tab_view = Gtk.Template.Child() tab_view = Gtk.Template.Child()
tab_bar = Gtk.Template.Child() tab_bar = Gtk.Template.Child()
# Panes # Split view
main_pane = Gtk.Template.Child() split_view = Gtk.Template.Child()
sidebar_toggle_button = Gtk.Template.Child()
# Sidebar widgets # Sidebar widgets
projects_listbox = Gtk.Template.Child() projects_listbox = Gtk.Template.Child()
add_project_button = Gtk.Template.Child()
import_menu_button = Gtk.Template.Child()
# History (hidden but kept for compatibility) # History (hidden but kept for compatibility)
history_listbox = Gtk.Template.Child() history_listbox = Gtk.Template.Child()
@ -102,6 +100,14 @@ class RosterWindow(Adw.ApplicationWindow):
# Setup custom CSS # Setup custom CSS
self._setup_custom_css() self._setup_custom_css()
# Bind sidebar toggle button to split view (bidirectional)
self.split_view.bind_property(
'show-sidebar',
self.sidebar_toggle_button,
'active',
GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE
)
# Setup UI # Setup UI
self._setup_tab_system() self._setup_tab_system()
self._load_projects() self._load_projects()
@ -204,6 +210,11 @@ class RosterWindow(Adw.ApplicationWindow):
font-weight: 600; font-weight: 600;
} }
/* URL entry can shrink to near-zero so the Send button stays visible */
entry.url-entry {
min-width: 0;
}
/* Method chips in sidebar request list */ /* Method chips in sidebar request list */
.method-chip { .method-chip {
border-radius: 4px; border-radius: 4px;
@ -508,6 +519,10 @@ class RosterWindow(Adw.ApplicationWindow):
self.add_action(action) self.add_action(action)
self.get_application().set_accels_for_action("win.send-request", ["<Control>Return"]) self.get_application().set_accels_for_action("win.send-request", ["<Control>Return"])
action = Gio.SimpleAction.new("add-project", None)
action.connect("activate", lambda a, p: self.on_add_project_clicked(None))
self.add_action(action)
action = Gio.SimpleAction.new("import-openapi", None) action = Gio.SimpleAction.new("import-openapi", None)
action.connect("activate", lambda a, p: self.on_import_openapi_clicked(None)) action.connect("activate", lambda a, p: self.on_import_openapi_clicked(None))
self.add_action(action) self.add_action(action)
@ -949,7 +964,6 @@ class RosterWindow(Adw.ApplicationWindow):
item.connect('manage-environments-requested', self._on_manage_environments, project) item.connect('manage-environments-requested', self._on_manage_environments, project)
self.projects_listbox.append(item) self.projects_listbox.append(item)
@Gtk.Template.Callback()
def on_add_project_clicked(self, button): def on_add_project_clicked(self, button):
"""Show dialog to create project.""" """Show dialog to create project."""
dialog = Adw.AlertDialog() dialog = Adw.AlertDialog()