diff --git a/src/AssemblySharedInfoGenerator/AssemblySharedInfo.cs b/src/AssemblySharedInfoGenerator/AssemblySharedInfo.cs index d70bfe8dc92..86f8e9192a4 100644 --- a/src/AssemblySharedInfoGenerator/AssemblySharedInfo.cs +++ b/src/AssemblySharedInfoGenerator/AssemblySharedInfo.cs @@ -8,7 +8,7 @@ // associated with an assembly. [assembly: AssemblyCompany("Autodesk, Inc")] [assembly: AssemblyProduct("Dynamo")] -[assembly: AssemblyCopyright("Copyright © Autodesk, Inc 2024")] +[assembly: AssemblyCopyright("Copyright © Autodesk, Inc 2024")] [assembly: AssemblyTrademark("")] //In order to begin building localizable applications, set diff --git a/src/DynamoCoreWpf/Commands/WorkspaceCommands.cs b/src/DynamoCoreWpf/Commands/WorkspaceCommands.cs index 3780bbc167f..cb402539e11 100644 --- a/src/DynamoCoreWpf/Commands/WorkspaceCommands.cs +++ b/src/DynamoCoreWpf/Commands/WorkspaceCommands.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Dynamo.Graph.Nodes; using Dynamo.Graph.Workspaces; using Dynamo.Selection; @@ -30,6 +30,7 @@ public partial class WorkspaceViewModel private DelegateCommand hideAllPopupCommand; private DelegateCommand showAllWiresCommand; private DelegateCommand hideAllWiresCommand; + private DelegateCommand unpinAllPreviewBubblesCommand; #endregion @@ -275,6 +276,20 @@ public DelegateCommand HideAllWiresCommand return hideAllWiresCommand; } } + + /// + /// View Command to hide all currently pinned preview bubbles within the workspace + /// + [JsonIgnore] + public DelegateCommand UnpinAllPreviewBubblesCommand + { + get + { + if (unpinAllPreviewBubblesCommand == null) + unpinAllPreviewBubblesCommand = new DelegateCommand(UnpinAllPreviewBubbles, CanUnpinAllPreviewBubbles); + return unpinAllPreviewBubblesCommand; + } + } #endregion #region Properties for Command Data Binding diff --git a/src/DynamoCoreWpf/Properties/Resources.Designer.cs b/src/DynamoCoreWpf/Properties/Resources.Designer.cs index 7cfe1382e34..ccf77f9158c 100644 --- a/src/DynamoCoreWpf/Properties/Resources.Designer.cs +++ b/src/DynamoCoreWpf/Properties/Resources.Designer.cs @@ -1693,6 +1693,15 @@ public static string DynamoViewEditMenuUndo { } } + /// + /// Looks up a localized string similar to _Unpin All Preview Bubbles. + /// + public static string DynamoViewEditMenuUnpinAllPreviewBubbles { + get { + return ResourceManager.GetString("DynamoViewEditMenuUnpinAllPreviewBubbles", resourceCulture); + } + } + /// /// Looks up a localized string similar to E_xtensions. /// diff --git a/src/DynamoCoreWpf/Properties/Resources.en-US.resx b/src/DynamoCoreWpf/Properties/Resources.en-US.resx index b01df364ba5..28f58ba9e91 100644 --- a/src/DynamoCoreWpf/Properties/Resources.en-US.resx +++ b/src/DynamoCoreWpf/Properties/Resources.en-US.resx @@ -581,6 +581,10 @@ Don't worry, you'll have the option to save your work. _Select All Edit menu | Select all nodes + + + _Unpin All Preview Bubbles + Edit menu | Unpin preview bubbles _Select Neighbors diff --git a/src/DynamoCoreWpf/Properties/Resources.resx b/src/DynamoCoreWpf/Properties/Resources.resx index 4f861fad7ef..dcb104b3f26 100644 --- a/src/DynamoCoreWpf/Properties/Resources.resx +++ b/src/DynamoCoreWpf/Properties/Resources.resx @@ -329,6 +329,10 @@ _Select All Edit menu | Select all nodes + + + _Unpin All Preview Bubbles + Edit menu | Unpin preview bubbles _Undo diff --git a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt index a077646bf6c..15bd18573ac 100644 --- a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt +++ b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt @@ -2026,6 +2026,9 @@ Dynamo.ViewModels.DynamoViewModel.ScaleFactorLog.set -> void Dynamo.ViewModels.DynamoViewModel.SelectAll(object parameter) -> void Dynamo.ViewModels.DynamoViewModel.SelectAllCommand.get -> Dynamo.UI.Commands.DelegateCommand Dynamo.ViewModels.DynamoViewModel.SelectAllCommand.set -> void +Dynamo.ViewModels.DynamoViewModel.UnpinAllPreviewBubbles(object parameter) -> void +Dynamo.ViewModels.DynamoViewModel.UnpinAllPreviewBubblesCommand.get -> Dynamo.UI.Commands.DelegateCommand +Dynamo.ViewModels.DynamoViewModel.UnpinAllPreviewBubblesCommand.set -> void Dynamo.ViewModels.DynamoViewModel.SelectNeighbors(object parameters) -> void Dynamo.ViewModels.DynamoViewModel.SelectNeighborsCommand.get -> Dynamo.UI.Commands.DelegateCommand Dynamo.ViewModels.DynamoViewModel.SelectNeighborsCommand.set -> void @@ -3040,6 +3043,7 @@ Dynamo.ViewModels.WorkspaceViewModel.SetZoomCommand.get -> Dynamo.UI.Commands.De Dynamo.ViewModels.WorkspaceViewModel.ShowAllWiresCommand.get -> Dynamo.UI.Commands.DelegateCommand Dynamo.ViewModels.WorkspaceViewModel.ShowHideAllGeometryPreviewCommand.get -> Dynamo.UI.Commands.DelegateCommand Dynamo.ViewModels.WorkspaceViewModel.ShowInCanvasSearchCommand.get -> Dynamo.UI.Commands.DelegateCommand +Dynamo.ViewModels.WorkspaceViewModel.UnpinAllPreviewBubblesTriggered -> Dynamo.ViewModels.ViewEventHandler Dynamo.ViewModels.WorkspaceViewModel.Watch3DViewModels.get -> System.Collections.ObjectModel.ObservableCollection Dynamo.ViewModels.WorkspaceViewModel.Watch3DViewModels.set -> void Dynamo.ViewModels.WorkspaceViewModel.WorkspaceElements.get -> System.Windows.Data.CompositeCollection @@ -3053,6 +3057,7 @@ Dynamo.ViewModels.WorkspaceViewModel.Zoom.get -> double Dynamo.ViewModels.WorkspaceViewModel.Zoom.set -> void Dynamo.ViewModels.WorkspaceViewModel.ZoomChanged -> Dynamo.ViewModels.WorkspaceViewModel.ZoomEventHandler Dynamo.ViewModels.WorkspaceViewModel.ZoomEventHandler +Dynamo.ViewModels.WorkspaceViewModel.UnpinAllPreviewBubblesCommand.get -> Dynamo.UI.Commands.DelegateCommand Dynamo.Views.GeometryScalingPopup Dynamo.Views.GeometryScalingPopup.GeometryScalingPopup(Dynamo.ViewModels.DynamoViewModel dynViewModel) -> void Dynamo.Views.GeometryScalingPopup.InitializeComponent() -> void @@ -3077,6 +3082,7 @@ Dynamo.Views.WorkspaceView.Dispose() -> void Dynamo.Views.WorkspaceView.HideAllPopUp(object sender) -> void Dynamo.Views.WorkspaceView.InitializeComponent() -> void Dynamo.Views.WorkspaceView.ViewModel.get -> Dynamo.ViewModels.WorkspaceViewModel +Dynamo.Views.WorkspaceView.vm_UnpinAllPreviewBubblesTriggered(object sender, System.EventArgs e) -> void Dynamo.Views.WorkspaceView.WorkspacePropertyEditClick(object sender, System.Windows.RoutedEventArgs routedEventArgs) -> void Dynamo.Views.WorkspaceView.WorkspaceView() -> void Dynamo.Wpf.AssemblyNodeViewCustomizations @@ -4523,6 +4529,7 @@ static Dynamo.Wpf.Properties.Resources.DynamoViewFileMenuOpenTemplate.get -> str static Dynamo.Wpf.Properties.Resources.DynamoViewFileMenuRecentFiles.get -> string static Dynamo.Wpf.Properties.Resources.DynamoViewFileMenuSave.get -> string static Dynamo.Wpf.Properties.Resources.DynamoViewFileMenuSaveAs.get -> string +static Dynamo.Wpf.Properties.Resources.DynamoViewEditMenuUnpinAllPreviewBubbles.get -> string static Dynamo.Wpf.Properties.Resources.DynamoViewHelpDictionary.get -> string static Dynamo.Wpf.Properties.Resources.DynamoViewHelpMenu.get -> string static Dynamo.Wpf.Properties.Resources.DynamoViewHelpMenuDisplayStartPage.get -> string diff --git a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs index b5b4bc132ac..a18de1acf74 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs @@ -2974,6 +2974,16 @@ internal bool CanSelectAll(object parameter) return this.CurrentSpaceViewModel.CanSelectAll(null); } + public void UnpinAllPreviewBubbles(object parameter) + { + this.CurrentSpaceViewModel.UnpinAllPreviewBubbles(null); + } + + internal bool CanUnpinAllPreviewBubbles(object parameter) + { + return this.CurrentSpaceViewModel.CanUnpinAllPreviewBubbles(null); + } + public void MakeNewHomeWorkspace(object parameter) { if (ClearHomeWorkspaceInternal()) diff --git a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModelDelegateCommands.cs b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModelDelegateCommands.cs index 56faceb04df..9405cf7910f 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModelDelegateCommands.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModelDelegateCommands.cs @@ -93,6 +93,7 @@ private void InitializeDelegateCommands() NodeFromSelectionCommand = new DelegateCommand(CreateNodeFromSelection, CanCreateNodeFromSelection); OpenDocumentationLinkCommand = new DelegateCommand(OpenDocumentationLink); ShowNodeDocumentationCommand = new DelegateCommand(ShowNodeDocumentation, CanShowNodeDocumentation); + UnpinAllPreviewBubblesCommand = new DelegateCommand(UnpinAllPreviewBubbles, CanUnpinAllPreviewBubbles); } public DelegateCommand OpenFromJsonIfSavedCommand { get; set; } public DelegateCommand OpenFromJsonCommand { get; set; } @@ -175,6 +176,6 @@ private void InitializeDelegateCommands() public DelegateCommand NodeFromSelectionCommand { get; set; } public DelegateCommand OpenDocumentationLinkCommand { get; set; } public DelegateCommand ShowNodeDocumentationCommand { get; set; } - + public DelegateCommand UnpinAllPreviewBubblesCommand { get; set; } } } diff --git a/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs index c48f4adc246..213f58e9c3d 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs @@ -1722,6 +1722,23 @@ private void ShowHideAllWires(List nodeModels, bool isHidden) } } + public event ViewEventHandler UnpinAllPreviewBubblesTriggered; + /// + /// Triggers unpinning of all preview bubbles in the workspace + /// + /// + internal void UnpinAllPreviewBubbles(object o) + { + RaisePropertyChanged("UnpinAllPreviewBubbles"); + + UnpinAllPreviewBubblesTriggered?.Invoke(this, EventArgs.Empty); + } + + internal bool CanUnpinAllPreviewBubbles(object o) + { + return true; + } + /// /// Collapse a set of nodes and notes currently selected in workspace /// diff --git a/src/DynamoCoreWpf/Views/Core/DynamoView.xaml b/src/DynamoCoreWpf/Views/Core/DynamoView.xaml index 8e7220b40f1..5e8bb76558e 100644 --- a/src/DynamoCoreWpf/Views/Core/DynamoView.xaml +++ b/src/DynamoCoreWpf/Views/Core/DynamoView.xaml @@ -86,7 +86,9 @@ - + @@ -448,6 +450,10 @@ Command="{Binding SelectAllCommand}" Name="selectAll" InputGestureText="Ctrl + A" /> + /// PreviewControl /// Event arguments diff --git a/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs b/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs index 2af85880d8f..5bff79c5833 100644 --- a/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs +++ b/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs @@ -142,6 +142,7 @@ private void RemoveViewModelsubscriptions(WorkspaceViewModel ViewModel) ViewModel.RequestAddViewToOuterCanvas -= vm_RequestAddViewToOuterCanvas; ViewModel.WorkspacePropertyEditRequested -= VmOnWorkspacePropertyEditRequested; ViewModel.RequestSelectionBoxUpdate -= VmOnRequestSelectionBoxUpdate; + ViewModel.UnpinAllPreviewBubblesTriggered -= vm_UnpinAllPreviewBubblesTriggered; ViewModel.Model.RequestNodeCentered -= vm_RequestNodeCentered; ViewModel.Model.CurrentOffsetChanged -= vm_CurrentOffsetChanged; @@ -170,6 +171,7 @@ private void AttachViewModelsubscriptions(WorkspaceViewModel ViewModel) ViewModel.RequestAddViewToOuterCanvas += vm_RequestAddViewToOuterCanvas; ViewModel.WorkspacePropertyEditRequested += VmOnWorkspacePropertyEditRequested; ViewModel.RequestSelectionBoxUpdate += VmOnRequestSelectionBoxUpdate; + ViewModel.UnpinAllPreviewBubblesTriggered += vm_UnpinAllPreviewBubblesTriggered; ViewModel.Model.RequestNodeCentered += vm_RequestNodeCentered; ViewModel.Model.CurrentOffsetChanged += vm_CurrentOffsetChanged; @@ -783,6 +785,22 @@ private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) } } + /// + /// Handles the event triggered when all preview bubbles in the workspace need to be unpinned + /// + /// + /// + public void vm_UnpinAllPreviewBubblesTriggered(object sender, EventArgs e) + { + var nodesWithPinnedPreview = this.ChildrenOfType() + .Where(view => view.HasPreviewControl && view.PreviewControl.StaysOpen); + + foreach (var node in nodesWithPinnedPreview) + { + node.PreviewControl.UnpinPreviewBubble(); + } + } + private void OnCanvasMouseDown(object sender, MouseButtonEventArgs e) { ContextMenuPopup.IsOpen = false; diff --git a/src/DynamoCoreWpf/Views/Preview/PreviewControl.xaml.cs b/src/DynamoCoreWpf/Views/Preview/PreviewControl.xaml.cs index 983001f69cb..f6fa2b2214d 100644 --- a/src/DynamoCoreWpf/Views/Preview/PreviewControl.xaml.cs +++ b/src/DynamoCoreWpf/Views/Preview/PreviewControl.xaml.cs @@ -180,6 +180,16 @@ internal void HidePreviewBubble() } } + /// + /// Unpins and hides the preview bubbles + /// + internal void UnpinPreviewBubble() + { + StaysOpen = false; + nodeViewModel.PreviewPinned = false; + this.HidePreviewBubble(); + } + /// /// It is possible for a run to complete while the preview display is /// in transition. In these situations, we can store the MirrorData and diff --git a/test/DynamoCoreWpfTests/PreviewBubbleTests.cs b/test/DynamoCoreWpfTests/PreviewBubbleTests.cs index b3d5900ec6f..3150e97956f 100644 --- a/test/DynamoCoreWpfTests/PreviewBubbleTests.cs +++ b/test/DynamoCoreWpfTests/PreviewBubbleTests.cs @@ -81,7 +81,7 @@ public void PreviewBubbleVisible_MouseMoveOutOfNode() } [Test] - public void PreviewBubbleHiiden_OnFrozenNode() + public void PreviewBubbleHidden_OnFrozenNode() { Open(@"core\DetailedPreviewMargin_Test.dyn"); var nodeView = NodeViewWithGuid("7828a9dd-88e6-49f4-9ed3-72e355f89bcc"); @@ -525,6 +525,48 @@ public void PreviewBubble_ToggleShowPreviewBubbles() Assert.IsTrue(nodeView.PreviewControl.IsHidden, "Preview bubble is not hidden"); } + [Test] + public void PreviewBubble_UnpinAllPreviewBubble() + { + Open(@"core\DetailedPreviewMargin_Test.dyn"); + + // List of GUIDs and corresponding NodeView instances + var guids = new List + { + "1382aaf9-9432-4cf0-86ae-c586d311767e", + "81c94fd0-35a0-4680-8535-00aff41192d3", + "7828a9dd-88e6-49f4-9ed3-72e355f89bcc" + }; + var nodeViews = guids.Select(guid => NodeViewWithGuid(guid)).ToList(); + + // Pin each preview bubble + foreach (var nodeView in nodeViews) + { + var previewBubble = nodeView.PreviewControl; + previewBubble.RaiseEvent(new RoutedEventArgs(FrameworkElement.LoadedEvent)); + previewBubble.bubbleTools.RaiseEvent(new RoutedEventArgs(FrameworkElement.LoadedEvent)); + + // Simulate mouse enter events to trigger bubble visibility and pinning + RaiseMouseEnterOnNode(nodeView); + RaiseMouseEnterOnNode(previewBubble); + RaiseMouseEnterOnNode(previewBubble.bubbleTools); + RaiseLeftMouseClick(previewBubble.pinIconBorder); + + // Assert the bubble is expanded and pinned + Assert.IsTrue(previewBubble.IsExpanded, "Expanded preview bubble should be shown"); + Assert.IsTrue(previewBubble.StaysOpen, "Expanded preview bubble should be pinned"); + } + + // Execute command to unpin all preview bubbles + ViewModel.UnpinAllPreviewBubblesCommand.Execute(null); + + // Assert all preview bubbles are unpinned + foreach (var nodeView in nodeViews) + { + Assert.IsTrue(!nodeView.PreviewControl.StaysOpen, "Expanded preview bubble should be unpinned"); + } + } + [Test] public void PreviewBubble_ShowExpandedPreviewOnPinIconHover() { @@ -915,6 +957,16 @@ private void RaiseMouseLeaveNode(IInputElement nv) DispatcherUtil.DoEvents(); } + private void RaiseLeftMouseClick(IInputElement nv) + { + View.Dispatcher.Invoke(() => + { + nv.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = Mouse.MouseDownEvent }); + }); + + DispatcherUtil.DoEvents(); + } + protected NodeModel GetNodeModel(string guid) { return Model.CurrentWorkspace.Nodes.First(n => n.GUID == Guid.Parse(guid));