diff --git a/Pinta.Core/Managers/WorkspaceManager.cs b/Pinta.Core/Managers/WorkspaceManager.cs index 34700fe2ff..1bb3975b7f 100644 --- a/Pinta.Core/Managers/WorkspaceManager.cs +++ b/Pinta.Core/Managers/WorkspaceManager.cs @@ -383,12 +383,13 @@ public void SetActiveDocument ( } internal void SetActiveDocumentInternal ( - ToolManager tools, - Document document) + ToolManager tools, + Document document, + bool suppressCommit = false) { // Work around a case where we closed a document but haven't updated // the active_document_index yet and it points to the closed document - if (HasOpenDocuments && active_document_index != -1 && open_documents.Count > active_document_index) + if (!suppressCommit && HasOpenDocuments && active_document_index != -1 && open_documents.Count > active_document_index) tools.Commit (); int index = open_documents.IndexOf (document); @@ -397,6 +398,13 @@ internal void SetActiveDocumentInternal ( OnActiveDocumentChanged (EventArgs.Empty); } + public void SetActiveDocumentForClose (Document document) + { + // Sets the active document in preparation for closing it. This avoids committing + // tool state when switching, so canceling the close will not finalize editable shapes, for example. + SetActiveDocumentInternal (PintaCore.Tools, document, suppressCommit: true); + } + private void OnActiveDocumentChanged (EventArgs _) { ActiveDocumentChanged?.Invoke (this, EventArgs.Empty); diff --git a/Pinta.Docking/DockNotebook.cs b/Pinta.Docking/DockNotebook.cs index 3ac16d80f9..eb2b634557 100644 --- a/Pinta.Docking/DockNotebook.cs +++ b/Pinta.Docking/DockNotebook.cs @@ -45,6 +45,7 @@ public sealed class DockNotebook : Gtk.Box private readonly Adw.TabView tab_view; private readonly Adw.TabBar tab_bar; private readonly HashSet items = []; + public bool IsClosingTab { get; private set; } public DockNotebook () { @@ -95,12 +96,17 @@ private bool TabView_OnClosePage (Adw.TabView _, Adw.TabView.ClosePageSignalArgs TabClosedEventArgs close_args = new (item); - TabClosed?.Invoke (this, close_args); + IsClosingTab = true; + try { + TabClosed?.Invoke (this, close_args); - tab_view.ClosePageFinish (page, !close_args.Cancel); + tab_view.ClosePageFinish (page, !close_args.Cancel); - if (!close_args.Cancel) - items.Remove (item); + if (!close_args.Cancel) + items.Remove (item); + } finally { + IsClosingTab = false; + } // Prevent the default close handler from running. return Gdk.Constants.EVENT_STOP; diff --git a/Pinta.Gui.Widgets/Widgets/Canvas/PintaCanvas.cs b/Pinta.Gui.Widgets/Widgets/Canvas/PintaCanvas.cs index 90c4d07dd0..b2a125a05d 100644 --- a/Pinta.Gui.Widgets/Widgets/Canvas/PintaCanvas.cs +++ b/Pinta.Gui.Widgets/Widgets/Canvas/PintaCanvas.cs @@ -256,6 +256,11 @@ private void DrawHandles (Gtk.Snapshot snapshot, Graphene.Rect canvasViewBounds) if (tool is null) return; + // Only draw tool handles on the canvas for the active document. + // This prevents handles from one document appearing on another tab's canvas. + if (!ReferenceEquals (document, PintaCore.Workspace.ActiveDocumentOrDefault)) + return; + snapshot.PushClip (canvasViewBounds); foreach (IToolHandle control in tool.Handles.Where (c => c.Active)) { diff --git a/Pinta.Tools/Editable/EditEngines/BaseEditEngine.cs b/Pinta.Tools/Editable/EditEngines/BaseEditEngine.cs index efc41111b5..453b37e439 100644 --- a/Pinta.Tools/Editable/EditEngines/BaseEditEngine.cs +++ b/Pinta.Tools/Editable/EditEngines/BaseEditEngine.cs @@ -177,9 +177,40 @@ public ShapeEngine? ActiveShapeEngine { //Helps to keep track of the first modification on a shape after the mouse is clicked, to prevent unnecessary history items. protected bool clicked_without_modifying = false; - //Stores the editable shape data. + // Stores editable shape data per document; the static SEngines reference is + // rebound on active-document changes to point at the current document's list. + private static readonly Dictionary> shapes_by_document = new (); + + // Stores the editable shape data for the currently active document. public static Collection SEngines = []; + static BaseEditEngine () + { + // Initialize mapping for the current active document if available. + if (PintaCore.Workspace.ActiveDocumentOrDefault is Document doc) + SEngines = GetShapesForDocument (doc); + + PintaCore.Workspace.ActiveDocumentChanged += (_, __) => { + if (PintaCore.Workspace.ActiveDocumentOrDefault is Document active) + SEngines = GetShapesForDocument (active); + }; + + PintaCore.Workspace.DocumentClosed += (_, e) => { + // Clean up mapping for closed documents. + if (e.Document is Document closed) + shapes_by_document.Remove (closed); + }; + } + + private static Collection GetShapesForDocument (Document doc) + { + if (!shapes_by_document.TryGetValue (doc, out var shapes)) { + shapes = []; + shapes_by_document[doc] = shapes; + } + return shapes; + } + #region ToolbarEventHandlers protected virtual void BrushMinusButtonClickedEvent (object? o, EventArgs args) @@ -1455,7 +1486,8 @@ protected void CalculateModifiedCurrentPoint () /// protected void ResetShapes () { - SEngines = []; + // Clear the current document's editable shape data. + SEngines.Clear (); //The fields are modified instead of the properties here because a redraw call is undesired (for speed/efficiency). SelectedPointIndex = -1; diff --git a/Pinta/Actions/File/CloseDocumentAction.cs b/Pinta/Actions/File/CloseDocumentAction.cs index 309f78bd26..c06dac6582 100644 --- a/Pinta/Actions/File/CloseDocumentAction.cs +++ b/Pinta/Actions/File/CloseDocumentAction.cs @@ -59,9 +59,6 @@ void IActionHandler.Uninitialize () private void Activated (object sender, EventArgs e) { - // Commit any pending changes - tools.Commit (); - // If it's not dirty, just close it if (!workspace.ActiveDocument.IsDirty) { workspace.CloseActiveDocument (actions); diff --git a/Pinta/MainWindow.cs b/Pinta/MainWindow.cs index ab101acf3a..5bc0a15d9d 100644 --- a/Pinta/MainWindow.cs +++ b/Pinta/MainWindow.cs @@ -150,7 +150,7 @@ private void DockNotebook_TabClosed (object? sender, TabClosedEventArgs e) if (PintaCore.Workspace.OpenDocuments.IndexOf (view.Document) < 0) return; - PintaCore.Actions.Window.SetActiveDocument (view.Document); + PintaCore.Workspace.SetActiveDocumentForClose (view.Document); PintaCore.Actions.File.Close.Activate (); if (PintaCore.Workspace.OpenDocuments.IndexOf (view.Document) < 0) @@ -162,6 +162,9 @@ private void DockNotebook_TabClosed (object? sender, TabClosedEventArgs e) private void DockNotebook_ActiveTabChanged (object? sender, EventArgs e) { + if (canvas_pad.Notebook.IsClosingTab) + return; + var item = canvas_pad.Notebook.ActiveItem; if (item == null)