Repository navigation
Draft: Improve canvas navigation and editing, with nested topology containers - #3859
Draft
redlintles wants to merge 63 commits into
Draft
redlintles wants to merge 63 commits into
redlintles wants to merge 63 commits into
Conversation
Add a toolbar action and canvas shortcuts for grouping and ungrouping devices. Reuse native Qt multi-selection movement to preserve node coordinates, link updates and controller synchronization. Prune stale memberships and prevent dragging groups with locked members. Cover grouping, drag events and action wiring with tests.
Explain the Python/PyQt6 stack, scene and controller update paths, session-only grouping tradeoffs, known scope limits and commands for running the experimental GUI and its tests.
Add real parent-child scene hierarchies for devices, drawings and nested groups, with local coordinates and named container editing. Implement recursive movement, deletion, duplication, ungrouping and sibling alignment while preserving the existing absolute-coordinate API. Persist versioned membership metadata through native drawings and remap regenerated IDs on portable import. Handle asynchronous node copies and stale group acknowledgments. Add Qt regression coverage and a reproducible server integration check for export, import and recursive operations.
…tion Replace the session-only guide with named nested-container usage, coordinate and import identity rules, compatibility limits, and reproducible unit and real-server checks.
…ides Share name and dimension properties between creation and editing, rename group titles on double click, and leave editing on outside clicks. Persist fixed dimensions in group metadata v2 with v1 migration; clip children and resize all four corners without scaling or moving their scene contents. Draw transient parent-only center guides and snap direct-child drags. Show nested groups in Topology Summary with rename, duplicate and recursive delete actions while retaining device port rows and filters. Restore toolbar status help, momentary button state and normal/hover icons. Verify Qt interaction, clipping, hierarchy and server export/import behavior.
Describe corner resizing, transient alignment guides, group properties, metadata v2 migration and the validated unit and server integration workflows.
…eation Increase initial content dimensions by 30 percent in total, keep dimension fields white under dark themes, and highlight group destinations with transient center guides when dragging new device templates. Attach only the node returned by the creation request and preserve its local drop position across parent movement. Queue drawing updates until POST acknowledgment to prevent updates to unregistered IDs, retain edits made while creation is pending, and clean up pending deletions. Add delayed-response regression tests and extend the real-server check with template insertion and deliberately deferred drawing creation.
Explain the SVG transport, coordinate conversion, import ID remapping, client-side recursive operations, the reproducible creation-order race and test coverage limits. Provide a natural technical report, an English PR draft and a proposed server model, migration plan, operation lifecycle and source entry points.
Retain QNetworkReply wrappers while requests are in flight, then release them when finished is processed, including unsuccessful responses. Requests with timeout=None and showProgress=False previously had only collectible Python signal cycles, so successful server-side node duplication could lose completion callbacks and never rebuild group ownership or internal links. Add a regression using actual Qt reply wrappers and signals: force garbage collection with three pending background requests, complete them out of order, and verify every callback runs and all retained references are released.
…tion Prefix copied root group names with Copy of, adding a numeric suffix when needed while preserving nested names and the original hierarchy. Exercise the actual Topology Summary Duplicate action as well as the canvas entry point, asserting copied ownership, local coordinates, internal links, persisted restoration and summary parent rows. Replace synchronous node duplication in the disposable-server check with the real Node completion hook and Qt HTTP transport. Force garbage collection before processing replies and verify copied devices belong to the copied tree, internal links use copied endpoints, all replies are released, and portable export/import retains the nested groups. Validated against GNS3 server 2.2.61; full suite: 226 passed, 1 skipped.
Record the server-201/client-callback mismatch, isolated reproduction, QNetworkReply wrapper collection mechanism, and why the former synchronous integration adapter missed the failure. Explain copied root naming, unchanged nested names, internal-link scope and restoration checks. Update the usage guide, server integration notes and PR draft to distinguish this client lifecycle bug from the proposed native server group API. Include the 226-pass regression result and real-server duplication/export/import checks, with the remaining lightweight-model and emulator coverage limits.
…ghts Use a solid four-unit border only while a new device template hovers over a group, restoring the normal dashed frame afterward. Track selected sibling devices during an editable-group drag and evaluate their symbol centers after Qt processes mouse release. Promote moved devices outside their direct parent to the nearest containing ancestor or the canvas, preserving scene coordinates, stacking and links. Keep partial overflow clipped, ignore clicks without movement, clear alignment guides, and include the previous parent in the release save so empty group metadata and Topology Summary ownership remain consistent. Add real mouse-event coverage for all four edges, nested promotion, hierarchy restoration, summary rows, unchanged clicks and partial overflow, plus a rendered-border check. Full suite: 235 passed, 1 skipped; focused group/view checks after save consolidation: 44 passed.
Describe the symbol-center release threshold, partial clipping, promotion to containing ancestors, preservation of coordinates and links, and persistence of both sides of the hierarchy change. Explain drag-state cleanup and post-release processing, record the regression results, and update the proposed PR description.
Track existing devices dragged from the canvas or editable groups, calculate visible unlocked destinations after mouse movement, and display red source frames with solid blue destination highlights and center guides. Reparent only when the mouse is released, preserving scene coordinates, links and persisted ownership. Render exiting symbols and labels in the view foreground only outside ancestor clipping paths. Keep the actual hierarchy intact during preview and leave other overflowing children clipped. Clear transient borders, guides and previews on release, focus loss and editing exit; guard snapping when no parent guide exists. Add rendered-pixel and real-mouse regressions for unclipped exit previews, red borders, hidden sibling clipping, return to the original group, existing-device insertion and group-to-group transfer, including summary ownership and restoration. Full suite: 239 passed, 1 skipped; focused group/view suite: 48 passed.
Update the usage guide and PR draft with red exit feedback, unclipped symbol and label previews, blue destination frames, direct group transfers and release-time ownership changes. Explain foreground-only rendering, ancestor clipping, transient cleanup, persistence and the 239-pass validation result.
Route the grouping action through a one-shot request: existing selections open the dialog immediately, while an empty selection waits for a normal left-button selection gesture and opens the dialog after Qt completes release. Consume the request before opening the modal, allow Escape to cancel, reset pending requests with the project, and exclude pan, drawing and link tools from completion. Use solid group frames in every selection/editing/drag state and a solid outer toolbar icon, retaining thicker blue and red feedback and dashed alignment guides. Keep the toolbar button momentary while selection is pending. Exercise the real toolbar and rubber-band mouse events with two devices, a note and an excluded device. Cover dialog confirmation/cancellation, preselected immediate grouping, empty selection, Escape, single-use requests and rendered continuous frames across selection/editing states. Full suite: 247 passed, 1 skipped.
Explain immediate grouping versus waiting for a completed selection, exact selected members, cancellation, empty selections and existing hierarchy validation. Update the usage guide and PR draft for solid frames in every state, preserve the distinction from dashed center guides, and record the 247-pass validation result.
Pass the wheel event viewport position into scaleView, preserve its scene coordinate while scaling with automatic anchoring temporarily disabled, and compensate the resulting displacement. Restore the previous Qt anchor afterward and retain the existing non-wheel zoom path and 10%-1000% limits. Extend only the view navigation rectangle around the requested viewport so scene edges and small scaled projects do not force center alignment. Reset navigation padding when scene dimensions are assigned, without changing project scene dimensions or item positions. Consume Ctrl-wheel events and avoid project updates for zero-delta or rejected zoom steps. Add real Qt wheel-event coverage without prior mouse movement across off-center positions, small/large scenes and 20%, 100%, 500% initial scales. Verify zoom reversal, cursor stability, project zoom persistence, bounds, ordinary wheel scrolling and padding reset. Full suite: 262 passed, 1 skipped.
Explain Ctrl-wheel behavior, explicit event-position anchoring, compensation for Qt scene alignment and scroll limits, and separation between navigation padding and persisted topology dimensions. Record regression coverage and the 262-pass validation result.
…ject echoes Bring the scene point chosen by Ctrl-wheel zoom-in to the viewport center and retain that focus across stationary-cursor steps instead of repeatedly selecting a new region. Zoom-out widens the current centered overview; pointer movement or ordinary scrolling allows a new focus. Request a complete viewport repaint after zoom. Make setSceneSize idempotent for unchanged dimensions. Project update acknowledgments and notifications previously reapplied the same size and cleared navigation padding, causing scroll geometry changes and undoing the zoom positioning. Clear focus and padding only on actual scene resizing or project reset. Revise cursor tests for centered-region behavior and add regressions for unchanged server-echo dimensions, stable viewport geometry, and navigation between opposite regions without scrollbars. Full suite: 264 passed, 1 skipped; final focused zoom checks: 18 passed.
Replace the former fixed-cursor description with the requested region-to-center workflow, explain stationary focus and zoom-out overview, trace the HTTP/notification size-reset bug, and document viewport repainting and regression coverage. Distinguish offscreen geometry validation from desktop visual confirmation.
… focus Remove region-to-center movement and stationary focus state. Each Ctrl-wheel event preserves its own scene point at the event pixel through scale compensation, equally for zoom-in and zoom-out. Retain navigation padding, idempotent project-size echoes and full viewport repainting from the previous fixes. Halve the wheel exponent for approximately 9% steps per conventional notch, with proportional smaller deltas and reversible zoom-out. Update real Qt regressions to require cursor stability, moving-cursor focus on every event, smaller proportional steps and equivalent reversal. Full suite: 265 passed, 1 skipped.
Replace the centered-region workflow with per-event cursor anchoring, explain removal of retained focus, document the smaller proportional wheel factor, and preserve the project-echo regression explanation and updated validation results.
… style Render a transient normalized preview, create exact scene-space dimensions on release, and submit only after the existing style dialog is accepted. Cancel or Escape discards the draft. Increase style dimensions to one million units and preserve untouched fractional dimensions. Add nine gesture and large-size regressions and authoring documentation.
Expose General and Style tabs with accessible icon-only rectangle, triangle and ellipse choices plus alpha colors, border style and width. Keep rectangular interaction/resize bounds; decorative shapes do not clip children. Validate optional style metadata, preserve defaults for legacy files and copy appearance during duplication. Rename user-facing grouping actions to containers while retaining compatible internal IDs. Add appearance, round-trip and corner regressions; suite: 278 passed, 1 skipped.
Open the text editor for new note drafts, submitting only accepted nonempty content. Add inline family, size, bold, italic and underline controls synchronized with the existing font dialog; prioritize project families and common installed fonts while retaining local fallbacks. Preserve current font/color defaults and SVG typography. Add creation/cancellation and typography round-trip tests and documentation.
…corners Add a creation-time dimensions dialog with aspect-ratio locking and cancellation before server creation. Preserve image center on import and persist resized dimensions, viewBox and ratio preference in the embedded SVG. Add visible corner handles and edge/corner resize gestures, respecting locks, original scene transforms, opposite-corner placement, nested containers and optional free sizing. Retain SVG renderer ownership across replacements. Add import, cancellation, ratio and nested-corner persistence tests. Full suite: 288 passed, 1 skipped; document workflow and limitations.
Label transparent fills explicitly and choose contrasting color-button text. Preserve QWidget.style by exposing serialized appearance through styleData. Cancel shape previews on focus loss and finish user-facing container terminology while retaining legacy identifiers. Update the main container guide and consolidated authoring documentation. Add a regression for readable color controls, the QWidget style API and focus-loss cleanup. Final suite: 289 passed, 1 skipped; dialogs rendered and visually inspected offscreen.
Scope white backgrounds and black text to image/shape numeric editors. Permit zero shape border width in the runtime and Designer definitions and map zero to NoPen for shapes and containers instead of Qt cosmetic one-pixel strokes. Add contrast and border regressions and document the behavior.
Add context actions in the canvas and summary only for editing containers. Add Apply for existing-container properties, preview changes locally without closing the dialog, restore the original name/geometry/style on Cancel, and save on OK. Keep summary rows in sync and cover action visibility plus preview confirmation/rollback.
Apply a white background and black text only to the border width spin box and its line editor. Extend numeric-field contrast coverage and document the fix and full-suite validation.
…ble click Distinguish stationary context requests from right-button rectangle drags, preserving existing multi-selection and exposing Edit first during container editing. Resolve double clicks to the deepest visible container and consume them before ancestor event filters can intercept, without changing persisted layer values. Document the behavior and cover editing menus, device context clicks and nested containers with Qt regression tests. Full suite: 311 passed, 1 skipped.
Separate ancestor event passthrough from the editing flag so only the active container displays editing and exposes its attributes. Preserve interaction with nested devices and restore ancestor filters when leaving or switching containers. Document and test three-level nesting, target resolution and editing transitions. Full suite: 312 passed, 1 skipped.
Author
|
Today, I fixed a few bugs and split the improvements into separate branches to make future reviews and merges easier, if needed. This PR will still point to the original branch containing all the improvements together, but I can open smaller, more focused PRs for each feature if the maintainers prefer. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft: Improve canvas navigation and editing, with nested topology containers
Why I worked on this
This started with frustrations I had while organizing larger GNS3 topologies. My concern is with the graphical editing workflow, rather than the simulation itself: moving around a large canvas, keeping related elements together, drawing backgrounds, and sizing imported images took more effort than I expected.
I am a developer and a GNS3 user, but I am not familiar with this project's Python/Qt stack. I used OpenAI Codex as an experiment to investigate the code and implement these changes, iterating on the results through actual use, screenshots, and recordings of problems I encountered. Codex generated the implementation, tests, and much of the technical documentation. I supplied the requirements, tested the interactions, and asked for revisions as issues appeared. I want to make that process explicit: this is not a claim that I independently audited every Qt or networking detail.
I am now using this fork in place of the installed official GUI because, for my own topology editing workflow, I find it more comfortable to use. That is personal experience, not evidence that the implementation is ready to merge unchanged. I would appreciate maintainers and other users trying it, particularly on larger projects and different desktop environments.
This is a draft for feedback on both the interaction and the implementation. It grew into several related features, and I am happy to split it into smaller PRs or change the approach to match the project's direction.
Changes
Named, nested containers
Containers organize devices, notes, shapes, images, and other containers into a hierarchy. They are actual parent/child graphics items, rather than a saved multi-selection.
Double-clicking a nested container targets the deepest visible container at that position and consumes the event before an ancestor can intercept it. Only the active container displays
[editing]; its ancestors merely allow events to reach it.GroupItemimplements geometry, appearance, and serialization.CanvasGroupscoordinates membership, editing, alignment, restoration, and recursive operations.GraphicsViewhandles interaction, andTopologySummaryViewreflects the same hierarchy. Existing internalgroupnames remain for compatibility; the user-facing feature is called Container.There are some deliberate limits. Rectangular containers clip overflowing children; triangle and ellipse containers keep overflow visible so elements remain accessible. Changing shape moves overflowing children inside and can enlarge the frame if needed, but it is not an overlap-free layout algorithm. Corner handles remain at the bounding rectangle's corners even for nonrectangular shapes.
Container persistence: the SVG workaround
This is the main architectural compromise I would like reviewed. The implementation targets the existing GNS3 2.2.61 API. That API persists nodes, links, and drawings, but does not provide a native container resource or parent relationship.
The hierarchy itself uses Qt parenting (
setParentItem). SVG is used as a transport workaround, not because Qt cannot represent containers. Each container is stored through the drawing API with versioned JSON inside an SVG<metadata>element. It records identity, parent-local child positions, dimensions, appearance, and child references. Devices remain ordinary devices; they are not flattened into an image.Normal node/drawing requests still use scene coordinates, while container metadata stores local coordinates. The GUI converts between them when receiving server updates and saving changes. This makes the interaction testable with an unmodified server, but it leaves the GUI responsible for maintaining both representations.
Consequences and limitations:
The intended follow-up would be a native server layout resource with a parent ID per element, hierarchy validation, import/export ID remapping, coordinated recursive operations, and consistent notifications. The Qt interaction could remain while the SVG persistence adapter is replaced. No such server API is implemented here.
Cursor-centered zoom
Ctrl + wheel now zooms around the current cursor position. The scene point underneath it stays at the same viewport location, rather than jumping toward the center of the window. Moving the cursor changes the reference for the next wheel event.
The view records the scene coordinate under the event position, temporarily disables automatic Qt anchoring, applies the scale, and compensates the displacement. A view-only navigation margin allows this near scene boundaries without changing saved topology dimensions. Wheel steps are smaller, using
2 ** (delta_y / 960); existing zoom limits remain.An additional fix avoids resetting navigation when server acknowledgments reapply unchanged scene dimensions. That reset was undoing the cursor compensation and contributing to scrollbar/border flicker. Quantized Qt scrollbars still impose a small positioning tolerance; this is not an animated zoom implementation.
Hand tool and selection
Two exclusive toolbar actions provide Hand and Selection, including status-bar help. Hand is the default: left-dragging empty canvas pans the view, with open/closed hand cursors. Left-dragging an element still moves that element.
Right-dragging draws a selection rectangle and opens the selected items' context menu on release. A stationary right-click opens the traditional item menu instead. Ctrl preserves the previous selection when adding a rectangle. The container toolbar's pending creation gesture retains left-button rectangle selection.
The selection rectangle is a viewport
QRubberBand, mapped into scene coordinates for item selection. Mouse context events are consumed separately to avoid opening a second menu. Panning changes view scrollbars, not topology coordinates.This changes an established mouse interaction, so I would especially like feedback on whether it should be optional or use different bindings upstream.
Shapes drawn by dragging
Rectangle and ellipse tools now let the user drag out the desired area, with a temporary preview. Releasing opens the style dialog before the drawing is created on the server. Cancel discards the draft; Escape cancels the gesture.
The preview uses scene coordinates and normalized bounds, so all drag directions work under zoom. Dimension controls now allow up to 1,000,000 units instead of the previous 1,000-unit limit. This increases the editing limit; it does not guarantee practical rendering performance at every size.
Border style, color, thickness, no-border, and fill controls reuse the existing editor. Zero thickness explicitly selects
NoPen, avoiding Qt's zero-width cosmetic stroke behavior. Relevant numeric fields have scoped white backgrounds and black text for readability.Notes and imported images
Creating a note opens typography controls before committing it: family, size, color, bold, italic, and underline, with the existing defaults. The font selector prioritizes families used in the project and common installed fonts. Fonts are not embedded or installed; another system may substitute a missing family.
Image import now offers width, height, and aspect-ratio controls before creation. Selected images can be resized from edges and corners. During dragging, a temporary transform reuses the decoded renderer; dimensions are written back into the SVG once on release. This avoids repeatedly parsing SVG and decoding embedded image data on every mouse event. The gesture accounts for item transforms and parent coordinates while preserving the opposite corner.
Two server compatibility workarounds are involved:
gelement so the server's simple-image optimization does not extract the bitmap and discard custom drawing dimensions/metadata. This depends on the existing 2.2 server behavior and deserves compatibility review.Supporting asynchronous fixes
Drawing updates now wait for creation acknowledgment before sending PUT requests. Edits made while POST is pending are retained; deleting a pending creation schedules cleanup after acknowledgment. This addresses a reproducible ordering race that can produce a drawing 404, although the screenshot originally reported during development was not enough to establish that specific session's cause.
The HTTP client also retains in-flight Qt reply wrappers until completion. Without that reference, some requests could lose Python callbacks to garbage collection, leaving duplicated devices without their reconstructed hierarchy or internal links.
These changes affect shared request handling beyond containers and warrant review on their own.
Validation and remaining review
The latest full automated run completed with 312 passed and 1 skipped, with an existing
tarfiledeprecation warning:Tests include Qt mouse/wheel events, nested editing, selection and context menus, cursor anchoring, geometry, resizing, cancellation, serialization, and asynchronous callbacks. Earlier disposable-server checks against GNS3 2.2.61 exercised Ethernet-switch nodes, links, duplication, export/import, delayed drawing creation, and large-image resource uploads. Those checks do not validate every emulator, every API branch, or concurrent clients, and they should not be read as a fresh end-to-end run of every change in this branch.
I would appreciate testing on real projects, particularly nested containers, duplication and portable export/import, rotated or large images, high-DPI desktops, and different server setups. I would also like guidance on the target branch, whether the mouse bindings fit GNS3, and whether to separate the independent canvas improvements from the container/server-model discussion.
The implementation notes are under
docs/, particularlycanvas-authoring.md,cursor-zoom.md, andgroup-server-integration.md. Some notes describe intermediate revisions; the test count above is the latest full-suite result.I was unsure which channel would be best for getting feedback, so I am starting with this draft rather than contacting individual developers privately. The goal is to make complex topologies easier to organize while keeping GNS3's existing simulation workflow intact.
Some more visual examples
It's important to tell that there will be some bugs in these implementations since these are only experimental changes. i'm finding new ones everywhere while studying for my CCNA exam. and i'll be fixing them as possible.