Re-organised ribbons. Fixed bug in rendering.
All checks were successful
Python CI / test (push) Successful in 1m31s
Lint / lint (push) Successful in 1m7s
Tests / test (3.11) (push) Successful in 1m43s
Tests / test (3.12) (push) Successful in 1m44s
Tests / test (3.13) (push) Successful in 1m38s
Tests / test (3.14) (push) Successful in 1m17s
All checks were successful
Python CI / test (push) Successful in 1m31s
Lint / lint (push) Successful in 1m7s
Tests / test (3.11) (push) Successful in 1m43s
Tests / test (3.12) (push) Successful in 1m44s
Tests / test (3.13) (push) Successful in 1m38s
Tests / test (3.14) (push) Successful in 1m17s
This commit is contained in:
parent
2a087f0c9d
commit
80d7d291f3
@ -62,6 +62,7 @@ try:
|
|||||||
# Clear operations
|
# Clear operations
|
||||||
glClear,
|
glClear,
|
||||||
glClearColor,
|
glClearColor,
|
||||||
|
glFlush,
|
||||||
GL_COLOR_BUFFER_BIT,
|
GL_COLOR_BUFFER_BIT,
|
||||||
GL_DEPTH_BUFFER_BIT,
|
GL_DEPTH_BUFFER_BIT,
|
||||||
# Viewport
|
# Viewport
|
||||||
@ -93,7 +94,7 @@ except ImportError:
|
|||||||
glTexParameteri = glDeleteTextures = glTexCoord2f = _gl_stub
|
glTexParameteri = glDeleteTextures = glTexCoord2f = _gl_stub
|
||||||
glPushMatrix = glPopMatrix = glScalef = glTranslatef = _gl_stub
|
glPushMatrix = glPopMatrix = glScalef = glTranslatef = _gl_stub
|
||||||
glLoadIdentity = glRotatef = _gl_stub
|
glLoadIdentity = glRotatef = _gl_stub
|
||||||
glClear = glClearColor = _gl_stub
|
glClear = glClearColor = glFlush = _gl_stub
|
||||||
glViewport = glMatrixMode = glOrtho = _gl_stub
|
glViewport = glMatrixMode = glOrtho = _gl_stub
|
||||||
glGetString = _gl_stub
|
glGetString = _gl_stub
|
||||||
|
|
||||||
|
|||||||
@ -53,11 +53,20 @@ class GLWidget(
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
|
# Store reference to main window for accessing project
|
||||||
|
self._main_window = parent
|
||||||
|
|
||||||
# Initialize async loading system
|
# Initialize async loading system
|
||||||
self._init_async_loading()
|
self._init_async_loading()
|
||||||
|
|
||||||
# Initialize OpenGL
|
# Set up OpenGL surface format with explicit double buffering
|
||||||
self.setFormat(self.format())
|
from PyQt6.QtGui import QSurfaceFormat
|
||||||
|
fmt = QSurfaceFormat()
|
||||||
|
fmt.setSwapBehavior(QSurfaceFormat.SwapBehavior.DoubleBuffer)
|
||||||
|
fmt.setSwapInterval(1) # Enable vsync
|
||||||
|
self.setFormat(fmt)
|
||||||
|
|
||||||
|
# Force full redraws to ensure viewport updates
|
||||||
self.setUpdateBehavior(QOpenGLWidget.UpdateBehavior.NoPartialUpdate)
|
self.setUpdateBehavior(QOpenGLWidget.UpdateBehavior.NoPartialUpdate)
|
||||||
|
|
||||||
# Enable mouse tracking and drag-drop
|
# Enable mouse tracking and drag-drop
|
||||||
@ -68,6 +77,20 @@ class GLWidget(
|
|||||||
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
self.setFocus()
|
self.setFocus()
|
||||||
|
|
||||||
|
def window(self):
|
||||||
|
"""Override window() to return stored main_window reference.
|
||||||
|
|
||||||
|
This fixes the Qt widget hierarchy issue where window() returns None
|
||||||
|
because the GL widget is nested in container widgets.
|
||||||
|
"""
|
||||||
|
return self._main_window if hasattr(self, '_main_window') else super().window()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Override update to force immediate repaint"""
|
||||||
|
super().update()
|
||||||
|
# Force immediate processing of paint events
|
||||||
|
self.repaint()
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""Handle widget close event."""
|
"""Handle widget close event."""
|
||||||
# Cleanup async loading
|
# Cleanup async loading
|
||||||
|
|||||||
@ -74,7 +74,7 @@ class EditOperationsMixin:
|
|||||||
@ribbon_action(
|
@ribbon_action(
|
||||||
label="Rotate Left",
|
label="Rotate Left",
|
||||||
tooltip="Rotate selected element 90° counter-clockwise",
|
tooltip="Rotate selected element 90° counter-clockwise",
|
||||||
tab="Home",
|
tab="Arrange",
|
||||||
group="Transform",
|
group="Transform",
|
||||||
requires_selection=True,
|
requires_selection=True,
|
||||||
)
|
)
|
||||||
@ -97,7 +97,7 @@ class EditOperationsMixin:
|
|||||||
@ribbon_action(
|
@ribbon_action(
|
||||||
label="Rotate Right",
|
label="Rotate Right",
|
||||||
tooltip="Rotate selected element 90° clockwise",
|
tooltip="Rotate selected element 90° clockwise",
|
||||||
tab="Home",
|
tab="Arrange",
|
||||||
group="Transform",
|
group="Transform",
|
||||||
requires_selection=True,
|
requires_selection=True,
|
||||||
)
|
)
|
||||||
@ -120,7 +120,7 @@ class EditOperationsMixin:
|
|||||||
@ribbon_action(
|
@ribbon_action(
|
||||||
label="Reset Rotation",
|
label="Reset Rotation",
|
||||||
tooltip="Reset selected element rotation to 0°",
|
tooltip="Reset selected element rotation to 0°",
|
||||||
tab="Home",
|
tab="Arrange",
|
||||||
group="Transform",
|
group="Transform",
|
||||||
requires_selection=True,
|
requires_selection=True,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -586,7 +586,7 @@ class FileOperationsMixin:
|
|||||||
x, y = element.position
|
x, y = element.position
|
||||||
element.position = (x + x_offset, y + y_offset)
|
element.position = (x + x_offset, y + y_offset)
|
||||||
|
|
||||||
@ribbon_action(label="Export PDF", tooltip="Export project to PDF", tab="Export", group="Export")
|
@ribbon_action(label="Export PDF", tooltip="Export project to PDF", tab="Home", group="File")
|
||||||
def export_pdf(self):
|
def export_pdf(self):
|
||||||
"""Export project to PDF using async backend (non-blocking)"""
|
"""Export project to PDF using async backend (non-blocking)"""
|
||||||
# Check if we have pages to export
|
# Check if we have pages to export
|
||||||
|
|||||||
@ -19,8 +19,8 @@ class MergeOperationsMixin:
|
|||||||
@ribbon_action(
|
@ribbon_action(
|
||||||
label="Merge Projects",
|
label="Merge Projects",
|
||||||
tooltip="Merge another project file with the current project",
|
tooltip="Merge another project file with the current project",
|
||||||
tab="File",
|
tab="Home",
|
||||||
group="Import/Export",
|
group="File",
|
||||||
)
|
)
|
||||||
def merge_projects(self):
|
def merge_projects(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -54,7 +54,13 @@ class ViewOperationsMixin:
|
|||||||
self.update_view()
|
self.update_view()
|
||||||
self.show_status(f"Zoom: {int(self.gl_widget.zoom_level * 100)}%", 2000)
|
self.show_status(f"Zoom: {int(self.gl_widget.zoom_level * 100)}%", 2000)
|
||||||
|
|
||||||
@ribbon_action(label="Toggle Grid Snap", tooltip="Toggle snapping to grid", tab="View", group="Snapping")
|
@ribbon_action(
|
||||||
|
label="Grid Snap",
|
||||||
|
tooltip="Enable/disable snapping to grid (Ctrl+G)",
|
||||||
|
tab="Insert",
|
||||||
|
group="Snapping",
|
||||||
|
shortcut="Ctrl+G",
|
||||||
|
)
|
||||||
def toggle_grid_snap(self):
|
def toggle_grid_snap(self):
|
||||||
"""Toggle grid snapping"""
|
"""Toggle grid snapping"""
|
||||||
if not self.project:
|
if not self.project:
|
||||||
@ -67,7 +73,13 @@ class ViewOperationsMixin:
|
|||||||
self.show_status(f"Grid snapping {status}", 2000)
|
self.show_status(f"Grid snapping {status}", 2000)
|
||||||
print(f"Grid snapping {status}")
|
print(f"Grid snapping {status}")
|
||||||
|
|
||||||
@ribbon_action(label="Toggle Edge Snap", tooltip="Toggle snapping to page edges", tab="View", group="Snapping")
|
@ribbon_action(
|
||||||
|
label="Edge Snap",
|
||||||
|
tooltip="Enable/disable snapping to page edges (Ctrl+E)",
|
||||||
|
tab="Insert",
|
||||||
|
group="Snapping",
|
||||||
|
shortcut="Ctrl+E",
|
||||||
|
)
|
||||||
def toggle_edge_snap(self):
|
def toggle_edge_snap(self):
|
||||||
"""Toggle edge snapping"""
|
"""Toggle edge snapping"""
|
||||||
if not self.project:
|
if not self.project:
|
||||||
@ -80,7 +92,7 @@ class ViewOperationsMixin:
|
|||||||
self.show_status(f"Edge snapping {status}", 2000)
|
self.show_status(f"Edge snapping {status}", 2000)
|
||||||
print(f"Edge snapping {status}")
|
print(f"Edge snapping {status}")
|
||||||
|
|
||||||
@ribbon_action(label="Toggle Guide Snap", tooltip="Toggle snapping to guides", tab="View", group="Snapping")
|
@ribbon_action(label="Guide Snap", tooltip="Enable/disable snapping to guides", tab="Insert", group="Snapping")
|
||||||
def toggle_guide_snap(self):
|
def toggle_guide_snap(self):
|
||||||
"""Toggle guide snapping"""
|
"""Toggle guide snapping"""
|
||||||
if not self.project:
|
if not self.project:
|
||||||
@ -93,7 +105,7 @@ class ViewOperationsMixin:
|
|||||||
self.show_status(f"Guide snapping {status}", 2000)
|
self.show_status(f"Guide snapping {status}", 2000)
|
||||||
print(f"Guide snapping {status}")
|
print(f"Guide snapping {status}")
|
||||||
|
|
||||||
@ribbon_action(label="Show Grid", tooltip="Toggle visibility of grid lines", tab="View", group="Snapping")
|
@ribbon_action(label="Show Grid", tooltip="Toggle visibility of grid lines", tab="Insert", group="Snapping")
|
||||||
def toggle_show_grid(self):
|
def toggle_show_grid(self):
|
||||||
"""Toggle grid visibility"""
|
"""Toggle grid visibility"""
|
||||||
if not self.project:
|
if not self.project:
|
||||||
@ -106,7 +118,7 @@ class ViewOperationsMixin:
|
|||||||
self.show_status(f"Grid {status}", 2000)
|
self.show_status(f"Grid {status}", 2000)
|
||||||
print(f"Grid {status}")
|
print(f"Grid {status}")
|
||||||
|
|
||||||
@ribbon_action(label="Show Guides", tooltip="Toggle visibility of guide lines", tab="View", group="Snapping")
|
@ribbon_action(label="Show Guides", tooltip="Toggle visibility of guide lines", tab="Insert", group="Snapping")
|
||||||
def toggle_snap_lines(self):
|
def toggle_snap_lines(self):
|
||||||
"""Toggle guide lines visibility"""
|
"""Toggle guide lines visibility"""
|
||||||
if not self.project:
|
if not self.project:
|
||||||
@ -164,7 +176,7 @@ class ViewOperationsMixin:
|
|||||||
print(f"Cleared {guide_count} guides")
|
print(f"Cleared {guide_count} guides")
|
||||||
|
|
||||||
@ribbon_action(
|
@ribbon_action(
|
||||||
label="Set Grid Size...", tooltip="Configure grid spacing for snapping", tab="View", group="Snapping"
|
label="Grid Settings...", tooltip="Configure grid size and snap threshold", tab="Insert", group="Snapping"
|
||||||
)
|
)
|
||||||
def set_grid_size(self):
|
def set_grid_size(self):
|
||||||
"""Open dialog to set grid size"""
|
"""Open dialog to set grid size"""
|
||||||
@ -234,50 +246,3 @@ class ViewOperationsMixin:
|
|||||||
self.update_view()
|
self.update_view()
|
||||||
self.show_status(f"Grid size: {new_grid_size:.1f}mm, Threshold: {new_threshold:.1f}mm", 2000)
|
self.show_status(f"Grid size: {new_grid_size:.1f}mm, Threshold: {new_threshold:.1f}mm", 2000)
|
||||||
print(f"Updated grid settings - Size: {new_grid_size:.1f}mm, Threshold: {new_threshold:.1f}mm")
|
print(f"Updated grid settings - Size: {new_grid_size:.1f}mm, Threshold: {new_threshold:.1f}mm")
|
||||||
|
|
||||||
# ===== Layout Tab Snapping Controls =====
|
|
||||||
# These provide easy access to snapping features during layout work
|
|
||||||
|
|
||||||
@ribbon_action(
|
|
||||||
label="Grid Snap",
|
|
||||||
tooltip="Enable/disable snapping to grid (Ctrl+G)",
|
|
||||||
tab="Layout",
|
|
||||||
group="Snapping",
|
|
||||||
shortcut="Ctrl+G",
|
|
||||||
)
|
|
||||||
def layout_toggle_grid_snap(self):
|
|
||||||
"""Toggle grid snapping (Layout tab)"""
|
|
||||||
self.toggle_grid_snap()
|
|
||||||
|
|
||||||
@ribbon_action(
|
|
||||||
label="Edge Snap",
|
|
||||||
tooltip="Enable/disable snapping to page edges (Ctrl+E)",
|
|
||||||
tab="Layout",
|
|
||||||
group="Snapping",
|
|
||||||
shortcut="Ctrl+E",
|
|
||||||
)
|
|
||||||
def layout_toggle_edge_snap(self):
|
|
||||||
"""Toggle edge snapping (Layout tab)"""
|
|
||||||
self.toggle_edge_snap()
|
|
||||||
|
|
||||||
@ribbon_action(label="Guide Snap", tooltip="Enable/disable snapping to guides", tab="Layout", group="Snapping")
|
|
||||||
def layout_toggle_guide_snap(self):
|
|
||||||
"""Toggle guide snapping (Layout tab)"""
|
|
||||||
self.toggle_guide_snap()
|
|
||||||
|
|
||||||
@ribbon_action(label="Show Grid", tooltip="Toggle visibility of grid lines", tab="Layout", group="Snapping")
|
|
||||||
def layout_toggle_show_grid(self):
|
|
||||||
"""Toggle grid visibility (Layout tab)"""
|
|
||||||
self.toggle_show_grid()
|
|
||||||
|
|
||||||
@ribbon_action(label="Show Guides", tooltip="Toggle visibility of guide lines", tab="Layout", group="Snapping")
|
|
||||||
def layout_toggle_snap_lines(self):
|
|
||||||
"""Toggle guide lines visibility (Layout tab)"""
|
|
||||||
self.toggle_snap_lines()
|
|
||||||
|
|
||||||
@ribbon_action(
|
|
||||||
label="Grid Settings...", tooltip="Configure grid size and snap threshold", tab="Layout", group="Snapping"
|
|
||||||
)
|
|
||||||
def layout_set_grid_size(self):
|
|
||||||
"""Open grid settings dialog (Layout tab)"""
|
|
||||||
self.set_grid_size()
|
|
||||||
|
|||||||
@ -70,15 +70,23 @@ class PageNavigationMixin:
|
|||||||
Returns:
|
Returns:
|
||||||
List of tuples (page_type, page_or_ghost_data, y_offset)
|
List of tuples (page_type, page_or_ghost_data, y_offset)
|
||||||
"""
|
"""
|
||||||
main_window = self.window()
|
# Use stored reference to main window
|
||||||
if not hasattr(main_window, "project"):
|
main_window = getattr(self, '_main_window', None)
|
||||||
|
if main_window is None:
|
||||||
|
main_window = self.window()
|
||||||
|
|
||||||
|
try:
|
||||||
|
project = main_window.project
|
||||||
|
if not project:
|
||||||
|
return []
|
||||||
|
except (AttributeError, TypeError):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
dpi = main_window.project.working_dpi
|
dpi = project.working_dpi
|
||||||
|
|
||||||
# Use project's page_spacing_mm setting (default is 10mm = 1cm)
|
# Use project's page_spacing_mm setting (default is 10mm = 1cm)
|
||||||
# Convert to pixels at working DPI
|
# Convert to pixels at working DPI
|
||||||
spacing_mm = main_window.project.page_spacing_mm
|
spacing_mm = project.page_spacing_mm
|
||||||
spacing_px = spacing_mm * dpi / 25.4
|
spacing_px = spacing_mm * dpi / 25.4
|
||||||
|
|
||||||
# Start with a small top margin (5mm)
|
# Start with a small top margin (5mm)
|
||||||
@ -89,7 +97,7 @@ class PageNavigationMixin:
|
|||||||
current_y = top_margin_px # Initial top offset in pixels (not screen pixels)
|
current_y = top_margin_px # Initial top offset in pixels (not screen pixels)
|
||||||
|
|
||||||
# First, render cover if it exists
|
# First, render cover if it exists
|
||||||
for page in main_window.project.pages:
|
for page in project.pages:
|
||||||
if page.is_cover:
|
if page.is_cover:
|
||||||
result.append(("page", page, current_y))
|
result.append(("page", page, current_y))
|
||||||
|
|
||||||
@ -102,7 +110,7 @@ class PageNavigationMixin:
|
|||||||
break # Only one cover allowed
|
break # Only one cover allowed
|
||||||
|
|
||||||
# Get page layout with ghosts from project (this excludes cover)
|
# Get page layout with ghosts from project (this excludes cover)
|
||||||
layout_with_ghosts = main_window.project.calculate_page_layout_with_ghosts()
|
layout_with_ghosts = project.calculate_page_layout_with_ghosts()
|
||||||
|
|
||||||
for page_type, page_obj, logical_pos in layout_with_ghosts:
|
for page_type, page_obj, logical_pos in layout_with_ghosts:
|
||||||
if page_type == "page":
|
if page_type == "page":
|
||||||
@ -119,7 +127,7 @@ class PageNavigationMixin:
|
|||||||
|
|
||||||
elif page_type == "ghost":
|
elif page_type == "ghost":
|
||||||
# Ghost page - use default page size
|
# Ghost page - use default page size
|
||||||
page_size_mm = main_window.project.page_size_mm
|
page_size_mm = project.page_size_mm
|
||||||
from pyPhotoAlbum.models import GhostPageData
|
from pyPhotoAlbum.models import GhostPageData
|
||||||
|
|
||||||
# Create ghost page data with correct size
|
# Create ghost page data with correct size
|
||||||
|
|||||||
@ -25,8 +25,23 @@ class RenderingMixin:
|
|||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||||
glLoadIdentity()
|
glLoadIdentity()
|
||||||
|
|
||||||
main_window = self.window()
|
# Use stored reference to main window
|
||||||
if not hasattr(main_window, "project") or not main_window.project or not main_window.project.pages:
|
main_window = getattr(self, '_main_window', None)
|
||||||
|
if main_window is None:
|
||||||
|
# Fallback to window() if _main_window not set
|
||||||
|
main_window = self.window()
|
||||||
|
|
||||||
|
if main_window is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
project = main_window.project
|
||||||
|
if not project:
|
||||||
|
return
|
||||||
|
if not project.pages:
|
||||||
|
return
|
||||||
|
except AttributeError:
|
||||||
|
# Project not yet initialized
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set initial zoom and center the page if not done yet
|
# Set initial zoom and center the page if not done yet
|
||||||
@ -36,11 +51,10 @@ class RenderingMixin:
|
|||||||
self.initial_zoom_set = True
|
self.initial_zoom_set = True
|
||||||
|
|
||||||
# Update scrollbars now that we have content bounds
|
# Update scrollbars now that we have content bounds
|
||||||
main_window = self.window()
|
if hasattr(self, '_main_window') and hasattr(self._main_window, "update_scrollbars"):
|
||||||
if hasattr(main_window, "update_scrollbars"):
|
self._main_window.update_scrollbars()
|
||||||
main_window.update_scrollbars()
|
|
||||||
|
|
||||||
dpi = main_window.project.working_dpi
|
dpi = project.working_dpi
|
||||||
|
|
||||||
# Calculate page positions with ghosts
|
# Calculate page positions with ghosts
|
||||||
page_positions = self._get_page_positions()
|
page_positions = self._get_page_positions()
|
||||||
@ -52,6 +66,7 @@ class RenderingMixin:
|
|||||||
PAGE_MARGIN = 50
|
PAGE_MARGIN = 50
|
||||||
|
|
||||||
# Render all pages
|
# Render all pages
|
||||||
|
pages_rendered = 0
|
||||||
for page_info in page_positions:
|
for page_info in page_positions:
|
||||||
page_type, page_or_ghost, y_offset = page_info
|
page_type, page_or_ghost, y_offset = page_info
|
||||||
|
|
||||||
@ -76,8 +91,9 @@ class RenderingMixin:
|
|||||||
renderer.begin_render()
|
renderer.begin_render()
|
||||||
# Pass widget reference for async loading
|
# Pass widget reference for async loading
|
||||||
page.layout._parent_widget = self
|
page.layout._parent_widget = self
|
||||||
page.layout.render(dpi=dpi, project=main_window.project)
|
page.layout.render(dpi=dpi, project=project)
|
||||||
renderer.end_render()
|
renderer.end_render()
|
||||||
|
pages_rendered += 1
|
||||||
|
|
||||||
elif page_type == "ghost":
|
elif page_type == "ghost":
|
||||||
ghost = page_or_ghost
|
ghost = page_or_ghost
|
||||||
@ -109,7 +125,8 @@ class RenderingMixin:
|
|||||||
for element in self.selected_elements:
|
for element in self.selected_elements:
|
||||||
self._draw_selection_handles(element)
|
self._draw_selection_handles(element)
|
||||||
|
|
||||||
# Render text overlays
|
# Render text overlays using QPainter
|
||||||
|
# Qt will handle OpenGL/QPainter coordination automatically
|
||||||
self._render_text_overlays()
|
self._render_text_overlays()
|
||||||
|
|
||||||
def _draw_selection_handles(self, element):
|
def _draw_selection_handles(self, element):
|
||||||
|
|||||||
@ -74,7 +74,7 @@ def build_ribbon_config(window_class: Type) -> Dict[str, Any]:
|
|||||||
ribbon_config = {}
|
ribbon_config = {}
|
||||||
|
|
||||||
# Define tab order (tabs will appear in this order)
|
# Define tab order (tabs will appear in this order)
|
||||||
tab_order = ["Home", "Insert", "Layout", "Arrange", "View", "Export"]
|
tab_order = ["Home", "Insert", "Layout", "Arrange", "View"]
|
||||||
|
|
||||||
# Add tabs in the defined order, then add any remaining tabs
|
# Add tabs in the defined order, then add any remaining tabs
|
||||||
all_tabs = list(tabs.keys())
|
all_tabs = list(tabs.keys())
|
||||||
@ -90,11 +90,10 @@ def build_ribbon_config(window_class: Type) -> Dict[str, Any]:
|
|||||||
# Define group order per tab (if needed)
|
# Define group order per tab (if needed)
|
||||||
group_orders = {
|
group_orders = {
|
||||||
"Home": ["File", "Edit"],
|
"Home": ["File", "Edit"],
|
||||||
"Insert": ["Media"],
|
"Insert": ["Media", "Snapping"],
|
||||||
"Layout": ["Navigation", "Page", "Templates"],
|
"Layout": ["Page", "Templates"],
|
||||||
"Arrange": ["Align", "Size", "Distribute"],
|
"Arrange": ["Align", "Distribute", "Size", "Order", "Transform"],
|
||||||
"View": ["Zoom"],
|
"View": ["Zoom", "Guides"],
|
||||||
"Export": ["Export"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get the group order for this tab, or use alphabetical
|
# Get the group order for this tab, or use alphabetical
|
||||||
|
|||||||
@ -373,9 +373,9 @@ class TestLayoutTabDelegation:
|
|||||||
|
|
||||||
window.project.snap_to_grid = False
|
window.project.snap_to_grid = False
|
||||||
|
|
||||||
window.layout_toggle_grid_snap()
|
window.toggle_grid_snap()
|
||||||
|
|
||||||
# Should delegate to toggle_grid_snap
|
# Should toggle snap_to_grid
|
||||||
assert window.project.snap_to_grid is True
|
assert window.project.snap_to_grid is True
|
||||||
assert window._update_view_called
|
assert window._update_view_called
|
||||||
|
|
||||||
@ -385,7 +385,7 @@ class TestLayoutTabDelegation:
|
|||||||
|
|
||||||
window.project.snap_to_edges = False
|
window.project.snap_to_edges = False
|
||||||
|
|
||||||
window.layout_toggle_edge_snap()
|
window.toggle_edge_snap()
|
||||||
|
|
||||||
assert window.project.snap_to_edges is True
|
assert window.project.snap_to_edges is True
|
||||||
assert window._update_view_called
|
assert window._update_view_called
|
||||||
@ -396,7 +396,7 @@ class TestLayoutTabDelegation:
|
|||||||
|
|
||||||
window.project.snap_to_guides = False
|
window.project.snap_to_guides = False
|
||||||
|
|
||||||
window.layout_toggle_guide_snap()
|
window.toggle_guide_snap()
|
||||||
|
|
||||||
assert window.project.snap_to_guides is True
|
assert window.project.snap_to_guides is True
|
||||||
assert window._update_view_called
|
assert window._update_view_called
|
||||||
@ -407,13 +407,13 @@ class TestLayoutTabDelegation:
|
|||||||
|
|
||||||
window.project.show_snap_lines = False
|
window.project.show_snap_lines = False
|
||||||
|
|
||||||
window.layout_toggle_snap_lines()
|
window.toggle_snap_lines()
|
||||||
|
|
||||||
assert window.project.show_snap_lines is True
|
assert window.project.show_snap_lines is True
|
||||||
assert window._update_view_called
|
assert window._update_view_called
|
||||||
|
|
||||||
def test_layout_set_grid_size_delegates(self, qtbot):
|
def test_layout_set_grid_size_delegates(self, qtbot):
|
||||||
"""Test layout tab grid size delegates to main method"""
|
"""Test grid size dialog works"""
|
||||||
window = TestViewWindow()
|
window = TestViewWindow()
|
||||||
qtbot.addWidget(window)
|
qtbot.addWidget(window)
|
||||||
|
|
||||||
@ -422,12 +422,12 @@ class TestLayoutTabDelegation:
|
|||||||
page.layout = layout
|
page.layout = layout
|
||||||
window._current_page = page
|
window._current_page = page
|
||||||
|
|
||||||
# Mock dialog to verify delegation
|
# Mock dialog to verify it runs
|
||||||
mock_dialog = Mock(spec=QDialog)
|
mock_dialog = Mock(spec=QDialog)
|
||||||
mock_dialog.exec.return_value = QDialog.DialogCode.Rejected
|
mock_dialog.exec.return_value = QDialog.DialogCode.Rejected
|
||||||
|
|
||||||
with patch("PyQt6.QtWidgets.QDialog", return_value=mock_dialog):
|
with patch("PyQt6.QtWidgets.QDialog", return_value=mock_dialog):
|
||||||
window.layout_set_grid_size()
|
window.set_grid_size()
|
||||||
|
|
||||||
# Verify method was called (dialog creation attempted)
|
# Verify dialog was shown
|
||||||
mock_dialog.exec.assert_called_once()
|
mock_dialog.exec.assert_called_once()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user