WIP: Refactoring window.py for per-tab widgets
This commit is contained in:
parent
6f32a61799
commit
a89aa5c206
249
src/window.py
249
src/window.py
@ -26,12 +26,11 @@ from .history_manager import HistoryManager
|
||||
from .project_manager import ProjectManager
|
||||
from .tab_manager import TabManager
|
||||
from .icon_picker_dialog import IconPickerDialog
|
||||
from .widgets.header_row import HeaderRow
|
||||
from .request_tab_widget import RequestTabWidget
|
||||
from .widgets.history_item import HistoryItem
|
||||
from .widgets.project_item import ProjectItem
|
||||
from datetime import datetime
|
||||
import json
|
||||
import xml.dom.minidom
|
||||
import uuid
|
||||
|
||||
|
||||
@ -40,31 +39,19 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
__gtype_name__ = 'RosterWindow'
|
||||
|
||||
# Top bar widgets
|
||||
method_dropdown = Gtk.Template.Child()
|
||||
url_entry = Gtk.Template.Child()
|
||||
send_button = Gtk.Template.Child()
|
||||
new_request_button = Gtk.Template.Child()
|
||||
tab_view = Gtk.Template.Child()
|
||||
tab_bar = Gtk.Template.Child()
|
||||
|
||||
# Panes
|
||||
main_pane = Gtk.Template.Child()
|
||||
split_pane = Gtk.Template.Child()
|
||||
|
||||
# Sidebar widgets
|
||||
projects_listbox = Gtk.Template.Child()
|
||||
add_project_button = Gtk.Template.Child()
|
||||
save_request_button = Gtk.Template.Child()
|
||||
|
||||
# Containers for tabs
|
||||
request_tabs_container = Gtk.Template.Child()
|
||||
response_tabs_container = Gtk.Template.Child()
|
||||
|
||||
# Status bar
|
||||
status_label = Gtk.Template.Child()
|
||||
time_label = Gtk.Template.Child()
|
||||
|
||||
# History
|
||||
# History (hidden but kept for compatibility)
|
||||
history_listbox = Gtk.Template.Child()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -75,7 +62,8 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
self.project_manager = ProjectManager()
|
||||
self.tab_manager = TabManager()
|
||||
|
||||
# Map AdwTabPage to RequestTab - stores our tab data
|
||||
# Map AdwTabPage to tab data
|
||||
self.page_to_widget = {} # AdwTabPage -> RequestTabWidget
|
||||
self.page_to_tab = {} # AdwTabPage -> RequestTab
|
||||
self.current_tab_id = None
|
||||
|
||||
@ -90,48 +78,24 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
self._setup_custom_css()
|
||||
|
||||
# Setup UI
|
||||
self._setup_method_dropdown()
|
||||
self._setup_request_tabs()
|
||||
self._setup_response_tabs()
|
||||
self._setup_tab_system()
|
||||
self._load_history()
|
||||
self._load_projects()
|
||||
|
||||
# Add initial header row
|
||||
self._add_header_row()
|
||||
|
||||
# Set split pane position to center after window is shown
|
||||
self.connect("map", self._on_window_mapped)
|
||||
|
||||
# Connect to close-request to warn about unsaved changes
|
||||
self.connect("close-request", self._on_close_request)
|
||||
|
||||
# Create first tab
|
||||
self._create_new_tab()
|
||||
|
||||
def _on_window_mapped(self, widget):
|
||||
"""Set split pane position to center when window is mapped."""
|
||||
# Use idle_add to ensure the widget is fully allocated
|
||||
GLib.idle_add(self._center_split_pane)
|
||||
|
||||
def _center_split_pane(self):
|
||||
"""Center the split pane divider."""
|
||||
# Get the allocated width of the paned widget
|
||||
allocation = self.split_pane.get_allocation()
|
||||
if allocation.width > 0:
|
||||
self.split_pane.set_position(allocation.width // 2)
|
||||
return False # Don't repeat
|
||||
|
||||
def _on_close_request(self, window):
|
||||
"""Handle window close request - warn if there are unsaved changes."""
|
||||
# Save current tab state first
|
||||
if self.current_tab_id:
|
||||
current_tab = self.tab_manager.get_tab_by_id(self.current_tab_id)
|
||||
if current_tab:
|
||||
current_tab.request = self._build_request_from_ui()
|
||||
|
||||
# Check if any tabs have unsaved changes
|
||||
modified_tabs = [tab for tab in self.tab_manager.tabs if tab.is_modified()]
|
||||
modified_tabs = []
|
||||
for page, widget in self.page_to_widget.items():
|
||||
if widget.modified:
|
||||
tab = self.page_to_tab.get(page)
|
||||
if tab:
|
||||
modified_tabs.append(tab)
|
||||
|
||||
if modified_tabs:
|
||||
# Show warning dialog
|
||||
@ -194,82 +158,11 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||
)
|
||||
|
||||
def _setup_sourceview_theme(self):
|
||||
"""Set up GtkSourceView theme based on system color scheme."""
|
||||
# Get the style manager to detect dark mode
|
||||
style_manager = Adw.StyleManager.get_default()
|
||||
is_dark = style_manager.get_dark()
|
||||
|
||||
# Get the appropriate color scheme (using classic for more subtle colors)
|
||||
# You can change these to customize the color theme:
|
||||
# - 'classic' / 'classic-dark' (subtle, muted colors)
|
||||
# - 'Adwaita' / 'Adwaita-dark' (GNOME default)
|
||||
# - 'tango' (colorful)
|
||||
# - 'solarized-light' / 'solarized-dark' (popular alternative)
|
||||
# - 'kate' / 'kate-dark'
|
||||
style_scheme_manager = GtkSource.StyleSchemeManager.get_default()
|
||||
scheme_name = 'Adwaita-dark' if is_dark else 'Adwaita'
|
||||
scheme = style_scheme_manager.get_scheme(scheme_name)
|
||||
|
||||
if scheme:
|
||||
self.response_body_sourceview.get_buffer().set_style_scheme(scheme)
|
||||
|
||||
# Listen for theme changes
|
||||
style_manager.connect('notify::dark', self._on_theme_changed)
|
||||
|
||||
def _on_theme_changed(self, style_manager, param):
|
||||
"""Handle system theme changes."""
|
||||
is_dark = style_manager.get_dark()
|
||||
style_scheme_manager = GtkSource.StyleSchemeManager.get_default()
|
||||
scheme_name = 'Adwaita-dark' if is_dark else 'Adwaita'
|
||||
scheme = style_scheme_manager.get_scheme(scheme_name)
|
||||
|
||||
if scheme:
|
||||
self.response_body_sourceview.get_buffer().set_style_scheme(scheme)
|
||||
# Also update request body theme
|
||||
self.body_sourceview.get_buffer().set_style_scheme(scheme)
|
||||
|
||||
def _setup_request_body_theme(self):
|
||||
"""Set up GtkSourceView theme for request body."""
|
||||
style_manager = Adw.StyleManager.get_default()
|
||||
is_dark = style_manager.get_dark()
|
||||
|
||||
style_scheme_manager = GtkSource.StyleSchemeManager.get_default()
|
||||
scheme_name = 'Adwaita-dark' if is_dark else 'Adwaita'
|
||||
scheme = style_scheme_manager.get_scheme(scheme_name)
|
||||
|
||||
if scheme:
|
||||
self.body_sourceview.get_buffer().set_style_scheme(scheme)
|
||||
|
||||
def _on_request_body_language_changed(self, dropdown, param):
|
||||
"""Handle request body language selection change."""
|
||||
selected = dropdown.get_selected()
|
||||
language_manager = GtkSource.LanguageManager.get_default()
|
||||
|
||||
buffer = self.body_sourceview.get_buffer()
|
||||
|
||||
if selected == 0: # RAW
|
||||
buffer.set_language(None)
|
||||
elif selected == 1: # JSON
|
||||
language = language_manager.get_language('json')
|
||||
buffer.set_language(language)
|
||||
elif selected == 2: # XML
|
||||
language = language_manager.get_language('xml')
|
||||
buffer.set_language(language)
|
||||
|
||||
def _setup_tab_system(self):
|
||||
"""Set up the tab system."""
|
||||
# Connect new request button
|
||||
self.new_request_button.connect("clicked", self._on_new_request_clicked)
|
||||
|
||||
# Connect change tracking to detect modifications
|
||||
self.url_entry.connect("changed", self._on_request_changed)
|
||||
self.method_dropdown.connect("notify::selected", self._on_request_changed)
|
||||
self.body_language_dropdown.connect("notify::selected", self._on_request_changed)
|
||||
# Track body changes
|
||||
body_buffer = self.body_sourceview.get_buffer()
|
||||
body_buffer.connect("changed", self._on_request_changed)
|
||||
|
||||
def _on_tab_selected(self, tab_view, param):
|
||||
"""Handle tab selection change."""
|
||||
page = tab_view.get_selected_page()
|
||||
@ -280,31 +173,18 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
tab = self.page_to_tab.get(page)
|
||||
if tab:
|
||||
self.current_tab_id = tab.id
|
||||
self._load_request_to_ui(tab.request)
|
||||
if tab.response:
|
||||
self._display_response(tab.response)
|
||||
else:
|
||||
# Clear response display
|
||||
self.status_label.set_text("Ready")
|
||||
self.time_label.set_text("")
|
||||
buffer = self.response_headers_textview.get_buffer()
|
||||
buffer.set_text("")
|
||||
buffer = self.response_body_sourceview.get_buffer()
|
||||
buffer.set_text("")
|
||||
|
||||
def _on_tab_close_page(self, tab_view, page):
|
||||
"""Handle tab close request."""
|
||||
# Get the RequestTab for this page
|
||||
# Get the RequestTab and widget for this page
|
||||
tab = self.page_to_tab.get(page)
|
||||
if not tab:
|
||||
widget = self.page_to_widget.get(page)
|
||||
|
||||
if not tab or not widget:
|
||||
return True # Allow close
|
||||
|
||||
# Save current tab state if this is the active tab
|
||||
if self.current_tab_id == tab.id:
|
||||
tab.request = self._build_request_from_ui()
|
||||
|
||||
# Check if tab has unsaved changes
|
||||
if tab.is_modified():
|
||||
if widget.modified:
|
||||
# Show warning dialog
|
||||
dialog = Adw.AlertDialog()
|
||||
dialog.set_heading("Close Unsaved Request?")
|
||||
@ -320,6 +200,7 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
# Remove from our tracking
|
||||
self.tab_manager.close_tab(tab.id)
|
||||
del self.page_to_tab[page]
|
||||
del self.page_to_widget[page]
|
||||
# Close the page
|
||||
tab_view.close_page_finish(page, True)
|
||||
else:
|
||||
@ -333,6 +214,7 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
# No unsaved changes, allow close
|
||||
self.tab_manager.close_tab(tab.id)
|
||||
del self.page_to_tab[page]
|
||||
del self.page_to_widget[page]
|
||||
|
||||
# If no tabs left, create a new one
|
||||
if self.tab_view.get_n_pages() == 1: # This page is still counted
|
||||
@ -340,33 +222,29 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
|
||||
return False # Allow close
|
||||
|
||||
def _on_request_changed(self, widget, *args):
|
||||
"""Track changes to mark tab as modified."""
|
||||
if self.current_tab_id:
|
||||
current_tab = self.tab_manager.get_tab_by_id(self.current_tab_id)
|
||||
if current_tab:
|
||||
# Update the current request in the tab
|
||||
current_tab.request = self._build_request_from_ui()
|
||||
# Check if modified
|
||||
current_tab.modified = current_tab.is_modified()
|
||||
# Update tab indicator
|
||||
self._update_tab_indicator(current_tab)
|
||||
|
||||
def _is_empty_new_request_tab(self):
|
||||
"""Check if current tab is an empty 'New Request' tab."""
|
||||
if not self.current_tab_id:
|
||||
return False
|
||||
|
||||
current_tab = self.tab_manager.get_tab_by_id(self.current_tab_id)
|
||||
if not current_tab:
|
||||
# Find the current page
|
||||
page = self.tab_view.get_selected_page()
|
||||
if not page:
|
||||
return False
|
||||
|
||||
tab = self.page_to_tab.get(page)
|
||||
widget = self.page_to_widget.get(page)
|
||||
|
||||
if not tab or not widget:
|
||||
return False
|
||||
|
||||
# Check if it's a "New Request" (not from saved request)
|
||||
if current_tab.saved_request_id:
|
||||
if tab.saved_request_id:
|
||||
return False
|
||||
|
||||
# Check if it's empty (no URL, no body, no headers)
|
||||
request = self._build_request_from_ui()
|
||||
# Check if it's empty
|
||||
request = widget.get_request()
|
||||
is_empty = (
|
||||
not request.url.strip() and
|
||||
not request.body.strip() and
|
||||
@ -398,52 +276,36 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
|
||||
return f"{base_name} (copy {counter})"
|
||||
|
||||
def _create_new_tab(self, name="New Request", request=None, saved_request_id=None):
|
||||
"""Create a new tab using AdwTabView."""
|
||||
def _create_new_tab(self, name="New Request", request=None, saved_request_id=None, response=None):
|
||||
"""Create a new tab with RequestTabWidget."""
|
||||
# Create tab in tab manager
|
||||
if not request:
|
||||
request = HttpRequest(method="GET", url="", headers={}, body="", syntax="RAW")
|
||||
|
||||
tab = self.tab_manager.create_tab(name, request, saved_request_id)
|
||||
tab = self.tab_manager.create_tab(name, request, saved_request_id, response)
|
||||
self.current_tab_id = tab.id
|
||||
|
||||
# Create a placeholder widget for the tab page
|
||||
placeholder = Gtk.Box()
|
||||
# Create RequestTabWidget for this tab
|
||||
widget = RequestTabWidget(tab.id, request, response)
|
||||
widget.original_request = tab.original_request
|
||||
|
||||
# Connect to send button
|
||||
widget.send_button.connect("clicked", lambda btn: self._on_send_clicked(widget))
|
||||
|
||||
# Create AdwTabPage
|
||||
page = self.tab_view.append(placeholder)
|
||||
page = self.tab_view.append(widget)
|
||||
page.set_title(name)
|
||||
page.set_indicator_activatable(False)
|
||||
|
||||
# Store mapping
|
||||
# Store mappings
|
||||
self.page_to_tab[page] = tab
|
||||
|
||||
# Update indicator if modified
|
||||
if tab.is_modified():
|
||||
page.set_indicator_icon(Gio.ThemedIcon.new("dot-symbolic"))
|
||||
self.page_to_widget[page] = widget
|
||||
|
||||
# Select this new page
|
||||
self.tab_view.set_selected_page(page)
|
||||
|
||||
# Load request into UI (will be triggered by selection change signal)
|
||||
|
||||
return tab.id
|
||||
|
||||
def _update_tab_indicator(self, tab):
|
||||
"""Update the indicator for a tab based on its modified state."""
|
||||
# Find the page for this tab
|
||||
page = None
|
||||
for p, t in self.page_to_tab.items():
|
||||
if t.id == tab.id:
|
||||
page = p
|
||||
break
|
||||
|
||||
if page:
|
||||
if tab.is_modified():
|
||||
page.set_indicator_icon(Gio.ThemedIcon.new("dot-symbolic"))
|
||||
else:
|
||||
page.set_indicator_icon(None)
|
||||
|
||||
def _switch_to_tab(self, tab_id):
|
||||
"""Switch to a tab by its ID."""
|
||||
# Find the page for this tab
|
||||
@ -458,35 +320,6 @@ class RosterWindow(Adw.ApplicationWindow):
|
||||
if page:
|
||||
self.tab_view.close_page(page)
|
||||
|
||||
def _load_request_to_ui(self, request):
|
||||
"""Load a request into the UI."""
|
||||
# Set method
|
||||
methods = ["GET", "POST", "PUT", "DELETE"]
|
||||
if request.method in methods:
|
||||
self.method_dropdown.set_selected(methods.index(request.method))
|
||||
|
||||
# Set URL
|
||||
self.url_entry.set_text(request.url)
|
||||
|
||||
# Clear and set headers
|
||||
while child := self.headers_listbox.get_first_child():
|
||||
self.headers_listbox.remove(child)
|
||||
|
||||
for key, value in request.headers.items():
|
||||
self._add_header_row(key, value)
|
||||
|
||||
if not request.headers:
|
||||
self._add_header_row()
|
||||
|
||||
# Set body
|
||||
buffer = self.body_sourceview.get_buffer()
|
||||
buffer.set_text(request.body)
|
||||
|
||||
# Set syntax
|
||||
syntax_options = ["RAW", "JSON", "XML"]
|
||||
if request.syntax in syntax_options:
|
||||
self.body_language_dropdown.set_selected(syntax_options.index(request.syntax))
|
||||
|
||||
def _on_new_request_clicked(self, button):
|
||||
"""Handle New Request button click."""
|
||||
self._create_new_tab()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user