diff --git a/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs index 703b2594680..4793222f40c 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs @@ -583,6 +583,14 @@ internal GeometryScalingViewModel GeoScalingViewModel } } + /// + /// Ensures that a preview control is initialized only when it is + /// absolutely needed. It lives here so that it can be shared by + /// all node views for reduced overhead and better management. + /// + internal Wpf.Utilities.ActionDebouncer DelayNodePreviewControl + = new Wpf.Utilities.ActionDebouncer(null); + #endregion public WorkspaceViewModel(WorkspaceModel model, DynamoViewModel dynamoViewModel) @@ -746,6 +754,9 @@ public override void Dispose() InCanvasSearchViewModel?.Dispose(); NodeAutoCompleteSearchViewModel.LuceneUtility?.DisposeAll(); NodeAutoCompleteSearchViewModel?.Dispose(); + + DelayNodePreviewControl?.Dispose(); + DelayNodePreviewControl = null; } internal void ZoomInInternal() diff --git a/src/DynamoCoreWpf/Views/Core/NodeView.xaml.cs b/src/DynamoCoreWpf/Views/Core/NodeView.xaml.cs index 5a2d7c14d46..171f53342f5 100644 --- a/src/DynamoCoreWpf/Views/Core/NodeView.xaml.cs +++ b/src/DynamoCoreWpf/Views/Core/NodeView.xaml.cs @@ -49,7 +49,6 @@ public partial class NodeView : IViewModelView /// Old ZIndex of node. It's set, when mouse leaves node. /// private int oldZIndex; - private bool nodeWasClicked; public NodeView TopControl @@ -296,6 +295,9 @@ public Grid PresentationGrid #region constructors static NodeView() { + //Set bitmap scaling mode to low quality for default node icon. + RenderOptions.SetBitmapScalingMode(_defaultNodeIcon, BitmapScalingMode.LowQuality); + //Freeze the static resource to reduce memory overhead _frozenImageSource.Freeze(); _transientImageSource.Freeze(); @@ -641,6 +643,8 @@ public NodeView() Source = _frozenImageSource }; + RenderOptions.SetBitmapScalingMode(FrozenImage, BitmapScalingMode.LowQuality); + FrozenImage.SetBinding(Grid.VisibilityProperty, new Binding("IsFrozen") { Converter = _boolToVisibilityCollapsedConverter, @@ -659,6 +663,8 @@ public NodeView() Source = _transientImageSource }; + RenderOptions.SetBitmapScalingMode(TransientImage, BitmapScalingMode.LowQuality); + TransientImage.SetBinding(Grid.VisibilityProperty, new Binding("IsTransient") { Converter = _boolToVisibilityCollapsedConverter, @@ -677,6 +683,8 @@ public NodeView() Source = _hiddenEyeImageSource }; + RenderOptions.SetBitmapScalingMode(HiddenEyeImage, BitmapScalingMode.LowQuality); + HiddenEyeImage.SetBinding(Grid.VisibilityProperty, new Binding("IsVisible") { Converter = _inverseBooleanToVisibilityCollapsedConverter, @@ -1031,6 +1039,7 @@ public NodeView() UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, TargetNullValue = null }); + RenderOptions.SetBitmapScalingMode(zoomStateImgOne, BitmapScalingMode.LowQuality); zoomGlyphRowZero.Children.Add(zoomStateImgOne); @@ -1062,6 +1071,7 @@ public NodeView() Converter = _emptyToVisibilityCollapsedConverter, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); + RenderOptions.SetBitmapScalingMode(zoomStateImgTwo, BitmapScalingMode.LowQuality); // Image in column 1 var zoomStateImgThree = new Image @@ -1085,6 +1095,7 @@ public NodeView() Converter = _emptyToVisibilityCollapsedConverter, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); + RenderOptions.SetBitmapScalingMode(zoomStateImgThree, BitmapScalingMode.LowQuality); zoomGlyphRowOne.Children.Add(zoomStateImgTwo); zoomGlyphRowOne.Children.Add(zoomStateImgThree); @@ -1536,6 +1547,21 @@ private static Style GetCodeBlockPortItemControlStyle() #endregion + private void DelayPreviewControlAction() + { + if (!IsMouseOver) return; + + TryShowPreviewBubbles(); + } + + private void InitialTryShowPreviewBubble() + { + // Always set old ZIndex to the last value, even if mouse is not over the node. + oldZIndex = NodeViewModel.StaticZIndex; + + viewModel.WorkspaceViewModel.DelayNodePreviewControl.Debounce(300, DelayPreviewControlAction); + } + private void OnNodeViewUnloaded(object sender, RoutedEventArgs e) { ViewModel.NodeLogic.DispatchedToUI -= NodeLogic_DispatchedToUI; @@ -1763,7 +1789,14 @@ private void NodeModel_ConnectorAdded(Graph.Connectors.ConnectorModel obj) // try to show the preview bubble without new mouse enter event. if (IsMouseOver) { - Dispatcher.BeginInvoke(new Action(TryShowPreviewBubbles), DispatcherPriority.Loaded); + if (DynCmd.IsTestMode) + { + Dispatcher.BeginInvoke(new Action(TryShowPreviewBubbles), DispatcherPriority.Loaded); + } + else + { + InitialTryShowPreviewBubble(); + } } } @@ -2065,16 +2098,20 @@ private void OnNodeViewMouseEnter(object sender, MouseEventArgs e) // if the node is located under "Hide preview bubbles" menu item and the item is clicked, // ViewModel.DynamoViewModel.ShowPreviewBubbles will be updated AFTER node mouse enter event occurs // so, wait while ShowPreviewBubbles binding updates value - Dispatcher.BeginInvoke(new Action(TryShowPreviewBubbles), DispatcherPriority.Loaded); + if (DynCmd.IsTestMode) + { + Dispatcher.BeginInvoke(new Action(TryShowPreviewBubbles), DispatcherPriority.Loaded); + } + else + { + InitialTryShowPreviewBubble(); + } } private void TryShowPreviewBubbles() { nodeWasClicked = false; - // Always set old ZIndex to the last value, even if mouse is not over the node. - oldZIndex = NodeViewModel.StaticZIndex; - // There is no need run further. if (IsPreviewDisabled()) return; @@ -2099,23 +2136,30 @@ private bool IsPreviewDisabled() // Or the user is selecting nodes // Or preview is disabled for this node // Or preview shouldn't be shown for some nodes (e.g. number sliders, watch nodes etc.) - // Or node is frozen. - // Or node is transient state. + // Or node is frozen + // Or we are panning the view. return !ViewModel.DynamoViewModel.ShowPreviewBubbles || ViewModel.WorkspaceViewModel.IsConnecting || ViewModel.WorkspaceViewModel.IsSelecting || !previewEnabled || - !ViewModel.IsPreviewInsetVisible || ViewModel.IsFrozen || viewModel.IsTransient; + !ViewModel.IsPreviewInsetVisible || ViewModel.IsFrozen || + ViewModel.WorkspaceViewModel.IsPanning; } private void OnNodeViewMouseLeave(object sender, MouseEventArgs e) { ViewModel.ZIndex = oldZIndex; + viewModel.WorkspaceViewModel.DelayNodePreviewControl.Cancel(); - //Watch nodes doesn't have Preview so we should avoid to use any method/property in PreviewControl class due that Preview is created automatically + // The preview hasn't been instantiated yet, we should stop here + if (previewControl == null) return; + + // Watch nodes doesn't have Preview so we should avoid to use any method/property in PreviewControl class due that Preview is created automatically if (ViewModel.NodeModel != null && ViewModel.NodeModel is CoreNodeModels.Watch) return; // If mouse in over node/preview control or preview control is pined, we can not hide preview control. - if (IsMouseOver || PreviewControl.IsMouseOver || PreviewControl.StaysOpen || IsMouseInsidePreview(e) || + // check the field and not the property because that will trigger the instantiation + if (IsMouseOver || previewControl?.IsMouseOver == true || + previewControl?.StaysOpen == true || IsMouseInsidePreview(e) || (Mouse.Captured is DragCanvas && IsMouseInsideNodeOrPreview(e.GetPosition(this)))) return; // If it's expanded, then first condense it. diff --git a/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs b/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs index 8288bc2d22a..ce312622cf8 100644 --- a/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs +++ b/src/DynamoCoreWpf/Views/Core/WorkspaceView.xaml.cs @@ -901,6 +901,11 @@ private void OnCanvasMouseDown(object sender, MouseButtonEventArgs e) GeoScalingPopup.IsOpen = false; if(PortContextMenu.IsOpen) DestroyPortContextMenu(); + + if (!ViewModel.IsPanning && e.MiddleButton == MouseButtonState.Pressed) + { + ViewModel.RequestTogglePanMode(); + } } /// @@ -937,6 +942,11 @@ private void OnMouseRelease(object sender, MouseButtonEventArgs e) // (not node, note, not buttons from viewControlPanel such as zoom, pan and so on) ContextMenuPopup.IsOpen = true; } + + if (ViewModel.IsPanning && e.MiddleButton == MouseButtonState.Released) + { + ViewModel.RequestTogglePanMode(); + } } private void OnMouseMove(object sender, MouseEventArgs e)