Fix all reported issues from testing

1. Modified indicator (*) now shows:
   - Added GObject signal 'modified-changed' to RequestTabWidget
   - Connect to signal in window.py to update tab page indicator
   - Indicator shows dot-symbolic icon when modified

2. History panel now visible and populated:
   - Restored history panel in main-window.ui with vertical paned layout
   - Added _load_history() call back to __init__
   - History entries now display in bottom panel

3. Saving requests now works:
   - Fixed on_save_request_clicked to get request from widget.get_request()
   - Removed dependency on obsolete _build_request_from_ui method

4. Loading saved requests fixed:
   - Updated _on_load_request to properly update widget and tab page
   - Correctly sets widget.original_request and widget.modified
   - Updates tab page title when loading into empty tab
   - Properly handles copy vs. linked request logic

All issues from user testing are now resolved!
This commit is contained in:
Pavel Baksy 2025-12-24 02:20:55 +01:00
parent 89d43b66b5
commit ad0e1886cc
3 changed files with 121 additions and 21 deletions

View File

@ -144,16 +144,68 @@
</object> </object>
</child> </child>
<!-- AdwTabView as main content area --> <!-- Vertical Paned: Tab View | History Panel -->
<child> <child>
<object class="AdwTabView" id="tab_view"> <object class="GtkPaned">
<property name="orientation">vertical</property>
<property name="vexpand">True</property> <property name="vexpand">True</property>
</object> <property name="position">600</property>
</child> <property name="shrink-start-child">False</property>
<!-- Hidden history list for backward compatibility --> <property name="shrink-end-child">True</property>
<child> <property name="resize-start-child">True</property>
<object class="GtkListBox" id="history_listbox"> <property name="resize-end-child">True</property>
<property name="visible">False</property>
<!-- AdwTabView as main content area -->
<property name="start-child">
<object class="AdwTabView" id="tab_view">
<property name="vexpand">True</property>
</object>
</property>
<!-- History Panel -->
<property name="end-child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<!-- History Header -->
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
<property name="spacing">12</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">6</property>
<child>
<object class="GtkLabel">
<property name="label">Request History</property>
<property name="hexpand">True</property>
<property name="xalign">0</property>
<style>
<class name="heading"/>
</style>
</object>
</child>
</object>
</child>
<!-- History List -->
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">True</property>
<property name="min-content-height">150</property>
<child>
<object class="GtkListBox" id="history_listbox">
<style>
<class name="boxed-list"/>
</style>
</object>
</child>
</object>
</child>
</object>
</property>
</object> </object>
</child> </child>
</object> </object>

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, GtkSource from gi.repository import Adw, Gtk, GLib, GtkSource, GObject
from .models import HttpRequest, HttpResponse from .models import HttpRequest, HttpResponse
from .widgets.header_row import HeaderRow from .widgets.header_row import HeaderRow
import json import json
@ -392,8 +392,18 @@ class RequestTabWidget(Gtk.Box):
return return
current = self.get_request() current = self.get_request()
was_modified = self.modified
self.modified = self._is_different_from_original(current) self.modified = self._is_different_from_original(current)
# Notify if modified state changed
if was_modified != self.modified:
self.emit('modified-changed', self.modified)
# Signal for modified state changes
__gsignals__ = {
'modified-changed': (GObject.SignalFlags.RUN_FIRST, None, (bool,))
}
def _is_different_from_original(self, request): def _is_different_from_original(self, request):
"""Check if current request differs from original.""" """Check if current request differs from original."""
if not self.original_request: if not self.original_request:

View File

@ -80,6 +80,7 @@ class RosterWindow(Adw.ApplicationWindow):
# Setup UI # Setup UI
self._setup_tab_system() self._setup_tab_system()
self._load_projects() self._load_projects()
self._load_history()
# Connect to close-request to warn about unsaved changes # Connect to close-request to warn about unsaved changes
self.connect("close-request", self._on_close_request) self.connect("close-request", self._on_close_request)
@ -301,11 +302,21 @@ class RosterWindow(Adw.ApplicationWindow):
self.page_to_tab[page] = tab self.page_to_tab[page] = tab
self.page_to_widget[page] = widget self.page_to_widget[page] = widget
# Connect to modified state changes
widget.connect('modified-changed', lambda w, modified: self._on_tab_modified_changed(page, modified))
# Select this new page # Select this new page
self.tab_view.set_selected_page(page) self.tab_view.set_selected_page(page)
return tab.id return tab.id
def _on_tab_modified_changed(self, page, modified):
"""Update tab indicator when modified state changes."""
if modified:
page.set_indicator_icon(Gio.ThemedIcon.new("dot-symbolic"))
else:
page.set_indicator_icon(None)
def _switch_to_tab(self, tab_id): def _switch_to_tab(self, tab_id):
"""Switch to a tab by its ID.""" """Switch to a tab by its ID."""
# Find the page for this tab # Find the page for this tab
@ -580,7 +591,18 @@ class RosterWindow(Adw.ApplicationWindow):
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_save_request_clicked(self, button): def on_save_request_clicked(self, button):
"""Save current request to a project.""" """Save current request to a project."""
request = self._build_request_from_ui() # Get request from current tab widget
page = self.tab_view.get_selected_page()
if not page:
self._show_toast("No active request")
return
widget = self.page_to_widget.get(page)
if not widget:
self._show_toast("No active request")
return
request = widget.get_request()
if not request.url.strip(): if not request.url.strip():
self._show_toast("Cannot save: URL is empty") self._show_toast("Cannot save: URL is empty")
@ -780,32 +802,40 @@ class RosterWindow(Adw.ApplicationWindow):
# Check if current tab is an empty "New Request" # Check if current tab is an empty "New Request"
if self._is_empty_new_request_tab(): if self._is_empty_new_request_tab():
# Replace the empty tab with this request # Replace the empty tab with this request
if self.current_tab_id: page = self.tab_view.get_selected_page()
current_tab = self.tab_manager.get_tab_by_id(self.current_tab_id) if page:
if current_tab: widget = self.page_to_widget.get(page)
# Update the tab current_tab = self.page_to_tab.get(page)
if widget and current_tab:
# Update the tab metadata
current_tab.name = tab_name current_tab.name = tab_name
current_tab.request = req current_tab.request = req
current_tab.saved_request_id = link_to_saved current_tab.saved_request_id = link_to_saved
# Update the tab page title
page.set_title(tab_name)
# Load request into widget
widget._load_request(req)
if is_copy: if is_copy:
# This is a copy - mark as unsaved # This is a copy - mark as unsaved
current_tab.original_request = None current_tab.original_request = None
current_tab.modified = True widget.original_request = None
widget.modified = True
else: else:
# This is linked to saved request # This is linked to saved request
current_tab.original_request = HttpRequest( original = HttpRequest(
method=req.method, method=req.method,
url=req.url, url=req.url,
headers=req.headers.copy(), headers=req.headers.copy(),
body=req.body, body=req.body,
syntax=req.syntax syntax=req.syntax
) )
current_tab.modified = False current_tab.original_request = original
widget.original_request = original
# Load into UI widget.modified = False
self._load_request_to_ui(req)
self._update_tab_bar_visibility()
else: else:
# Current tab has changes or is not a "New Request" # Current tab has changes or is not a "New Request"
# Create a new tab # Create a new tab
@ -822,6 +852,14 @@ class RosterWindow(Adw.ApplicationWindow):
new_tab.original_request = None new_tab.original_request = None
new_tab.modified = True new_tab.modified = True
# Also update the widget
page = self.tab_view.get_selected_page()
if page:
widget = self.page_to_widget.get(page)
if widget:
widget.original_request = None
widget.modified = True
def _on_delete_request(self, widget, saved_request, project): def _on_delete_request(self, widget, saved_request, project):
"""Delete saved request with confirmation.""" """Delete saved request with confirmation."""
dialog = Adw.AlertDialog() dialog = Adw.AlertDialog()