Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main/src/addins/MonoDevelop.Debugger/Gui/MonoDevelop.Debugger.ExpressionEvaluatorDialog.cs1
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.PreviewVisualizers/PreviewWindowManager.cs8
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj1
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/ObjectValueTreeViewControllerTests.cs353
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchAdornmentManager.cs190
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchProvider.cs54
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchView.cs195
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs68
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/IDebugInfoProvider.cs2
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/MacDebuggerTooltipWindow.cs158
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj18
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs39
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggerOptionsPanelWidget.cs2
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs4
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs41
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/AddNewExpressionObjectValueNode.cs35
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs6
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs196
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs24
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/LoadingObjectValueNode.cs38
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectCellViewBase.cs204
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectNameView.cs288
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectPinView.cs155
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectTypeView.cs70
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectValueView.cs381
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueNode.cs50
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeView.cs1030
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeViewDataSource.cs343
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeViewDelegate.cs155
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeView.cs12
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs213
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs14
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs5
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs96
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatch.cs47
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatchLocation.cs65
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs15
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs18
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs14
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/PadFontChanger.cs2
41 files changed, 4274 insertions, 338 deletions
diff --git a/main/src/addins/MonoDevelop.Debugger/Gui/MonoDevelop.Debugger.ExpressionEvaluatorDialog.cs b/main/src/addins/MonoDevelop.Debugger/Gui/MonoDevelop.Debugger.ExpressionEvaluatorDialog.cs
index 38404d7c74..1943ffd98c 100644
--- a/main/src/addins/MonoDevelop.Debugger/Gui/MonoDevelop.Debugger.ExpressionEvaluatorDialog.cs
+++ b/main/src/addins/MonoDevelop.Debugger/Gui/MonoDevelop.Debugger.ExpressionEvaluatorDialog.cs
@@ -72,7 +72,6 @@ namespace MonoDevelop.Debugger
this.valueTree.AllowPinning = false;
this.valueTree.RootPinAlwaysVisible = false;
this.valueTree.AllowExpanding = false;
- this.valueTree.PinnedWatchLine = 0;
this.valueTree.CompactView = false;
this.GtkScrolledWindow.Add (this.valueTree);
this.vbox2.Add (this.GtkScrolledWindow);
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.PreviewVisualizers/PreviewWindowManager.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.PreviewVisualizers/PreviewWindowManager.cs
index 3bdfa4f776..f694826222 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.PreviewVisualizers/PreviewWindowManager.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.PreviewVisualizers/PreviewWindowManager.cs
@@ -47,10 +47,16 @@ namespace MonoDevelop.Debugger
wnd = new PreviewVisualizerWindow (val, widget);
IdeApp.CommandService.RegisterTopWindow (wnd);
wnd.ShowPopup (widget, previewButtonArea, PopupPosition.Left);
+ wnd.FocusOutEvent += HandleFocusOutEvent;
wnd.Destroyed += HandleDestroyed;
OnWindowShown (EventArgs.Empty);
}
+ private static void HandleFocusOutEvent (object o, Gtk.FocusOutEventArgs args)
+ {
+ DestroyWindow ();
+ }
+
static void HandleDestroyed (object sender, EventArgs e)
{
wnd = null;
@@ -85,6 +91,8 @@ namespace MonoDevelop.Debugger
public static void DestroyWindow ()
{
if (wnd != null) {
+ wnd.FocusOutEvent -= HandleFocusOutEvent;
+ wnd.Destroyed -= HandleDestroyed;
wnd.Destroy ();
wnd = null;
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj
index 6007317a64..44807cad5e 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj
@@ -23,6 +23,7 @@
<Compile Include="TextFile.cs" />
<Compile Include="VsCodeStackFrameTests.cs" />
<Compile Include="BreakpointsAndSteppingTests.cs" />
+ <Compile Include="ObjectValueTreeViewControllerTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MonoDevelop.Debugger.csproj">
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/ObjectValueTreeViewControllerTests.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/ObjectValueTreeViewControllerTests.cs
new file mode 100644
index 0000000000..6c1e702e39
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/ObjectValueTreeViewControllerTests.cs
@@ -0,0 +1,353 @@
+//
+// ObjectValueTreeViewControllerTests.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using NUnit.Framework;
+
+using Mono.Debugging.Client;
+
+namespace MonoDevelop.Debugger.Tests
+{
+ class DummyDebuggerService : IDebuggerService
+ {
+ public bool IsConnected => true;
+
+ public bool IsPaused => true;
+
+ public bool HasInlineVisualizer (ObjectValueNode node)
+ {
+ return false;
+ }
+
+ public bool HasValueVisualizers (ObjectValueNode node)
+ {
+ return false;
+ }
+
+ public void NotifyVariableChanged ()
+ {
+ }
+
+ public bool ShowValueVisualizer (ObjectValueNode node)
+ {
+ return false;
+ }
+ }
+
+ class DummyStackFrame : IStackFrame
+ {
+ public EvaluationOptions CloneSessionEvaluationOpions ()
+ {
+ return new EvaluationOptions ();
+ }
+
+ public ObjectValueNode EvaluateExpression (string expression)
+ {
+ return new FakeObjectValueNode (expression);
+ }
+
+ public ObjectValueNode [] EvaluateExpressions (IList<string> expressions)
+ {
+ var values = new ObjectValueNode [expressions.Count];
+
+ for (int i = 0; i < expressions.Count; i++)
+ values [i] = new FakeObjectValueNode (expressions [i]);
+
+ return values;
+ }
+ }
+
+ class DummyObjectValueTreeViewController : ObjectValueTreeViewController
+ {
+ protected override IDebuggerService OnGetDebuggerService ()
+ {
+ return new DummyDebuggerService ();
+ }
+
+ public void SetViewControl (IObjectValueTreeView control)
+ {
+ ConfigureView (control);
+ }
+ }
+
+ class ObjectValueNodeReplacedEventArgs : EventArgs
+ {
+ public ObjectValueNodeReplacedEventArgs (ObjectValueNode node, ObjectValueNode[] replacementNodes)
+ {
+ Node = node;
+ ReplacementNodes = replacementNodes;
+ }
+
+ public ObjectValueNode Node {
+ get; private set;
+ }
+
+ public ObjectValueNode[] ReplacementNodes {
+ get; private set;
+ }
+ }
+
+ class DummyObjectValueTreeView : IObjectValueTreeView
+ {
+ public bool AllowEditing { get; set; }
+ public bool AllowExpanding { get; set; }
+ public PinnedWatch PinnedWatch { get; set; }
+
+ public int PinnedWatchOffset { get; set; }
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodeExpand;
+
+ public object EmitNodeExpand (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeExpand (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodeCollapse;
+
+ public object EmitNodeCollapse (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeCollapse (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodeLoadMoreChildren;
+
+ public object EmitNodeLoadMoreChildren (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeLoadMoreChildren (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodeRefresh;
+
+ public object EmitNodeRefresh (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeRefresh (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodeGetCanEdit;
+
+ public object EmitNodeGetCanEdit (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeGetCanEdit (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueEditEventArgs> NodeEditValue;
+
+ public object EmitNodeEditValue (ObjectValueNode node, string newValue)
+ {
+ var args = new ObjectValueEditEventArgs (node, newValue);
+ NodeEditValue (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodeRemoved;
+
+ public object EmitNodeRemoved (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeRemoved (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodePinned;
+
+ public object EmitNodePinned (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodePinned (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<EventArgs> NodeUnpinned;
+
+ public object EmitNodeUnpinned (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeUnpinned (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> NodeShowVisualiser;
+
+ public object EmitNodeShowVisualizer (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeShowVisualiser (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueExpressionEventArgs> ExpressionAdded;
+
+ public object EmitExpressionAdded (ObjectValueNode node, string expression)
+ {
+ var args = new ObjectValueExpressionEventArgs (node, expression);
+ ExpressionAdded (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler<ObjectValueExpressionEventArgs> ExpressionEdited;
+
+ public object EmitExpressionEdited (ObjectValueNode node, string expression)
+ {
+ var args = new ObjectValueExpressionEventArgs (node, expression);
+ ExpressionEdited (this, args);
+ return args.Response;
+ }
+
+ public event EventHandler StartEditing;
+ public event EventHandler EndEditing;
+
+ public event EventHandler<ObjectValueNodeEventArgs> ViewAppendedNode;
+
+ public void Appended (ObjectValueNode node)
+ {
+ ViewAppendedNode.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ public void Appended (IList<ObjectValueNode> nodes)
+ {
+ foreach (var node in nodes)
+ ViewAppendedNode?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ public event EventHandler ViewCleared;
+
+ public void Cleared ()
+ {
+ ViewCleared?.Invoke (this, EventArgs.Empty);
+ }
+
+ public event EventHandler<ObjectValueNodeReplacedEventArgs> ViewReplacedNode;
+
+ public void LoadEvaluatedNode (ObjectValueNode node, ObjectValueNode[] replacementNodes)
+ {
+ ViewReplacedNode?.Invoke (this, new ObjectValueNodeReplacedEventArgs (node, replacementNodes));
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> ViewLoadedChildren;
+
+ public void LoadNodeChildren (ObjectValueNode node, int startIndex, int count)
+ {
+ ViewLoadedChildren?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ public event EventHandler<ObjectValueNodeEventArgs> ViewExpandedNode;
+
+ public void OnNodeExpanded (ObjectValueNode node)
+ {
+ ViewExpandedNode?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+ }
+
+ [TestFixture]
+ public class ObjectValueTreeViewControllerTests
+ {
+ [Test]
+ public async Task TestBasicFunctionalityAsync ()
+ {
+ var controller = new DummyObjectValueTreeViewController ();
+ var view = new DummyObjectValueTreeView ();
+ ObjectValueNode[] replacements = null;
+ int appended = 0;
+ int replaced = 0;
+ int expanded = 0;
+ int cleared = 0;
+ int loaded = 0;
+
+ view.ViewAppendedNode += (o, e) => {
+ appended++;
+ };
+
+ view.ViewReplacedNode += (o, e) => {
+ replaced++;
+ replacements = e.ReplacementNodes;
+ };
+
+ view.ViewLoadedChildren += (o, e) => {
+ loaded++;
+ };
+
+ view.ViewExpandedNode += (o, e) => {
+ expanded++;
+ };
+
+ view.ViewCleared += (o, e) => {
+ cleared++;
+ };
+
+ controller.SetViewControl (view);
+ controller.Frame = new DummyStackFrame ();
+
+ var xx = new List<ObjectValueNode> ();
+
+ xx.Add (new FakeObjectValueNode ("f1"));
+ xx.Add (new FakeIsImplicitNotSupportedObjectValueNode ());
+
+ xx.Add (new FakeEvaluatingGroupObjectValueNode (1));
+ xx.Add (new FakeEvaluatingGroupObjectValueNode (0));
+ xx.Add (new FakeEvaluatingGroupObjectValueNode (5));
+
+ xx.Add (new FakeEvaluatingObjectValueNode ());
+ xx.Add (new FakeEnumerableObjectValueNode (10));
+ xx.Add (new FakeEnumerableObjectValueNode (20));
+ xx.Add (new FakeEnumerableObjectValueNode (23));
+
+ controller.AddValues (xx);
+
+ Assert.AreEqual (xx.Count, appended, "Number of appended object value nodes do not match.");
+
+ // the fake evaluating nodes are using a 5000 timer, so 5100 should be enough...
+ await Task.Delay (5100);
+
+ Assert.AreEqual (4, replaced, "Number of replaced nodes does not match.");
+
+ // expand the "f1" node
+ view.EmitNodeExpand (xx[0]);
+
+ // expanding a fake node uses a 1000 timer, so 1100 should be enough
+ await Task.Delay (1100);
+
+ Assert.AreEqual (1, expanded, "Expected the f1 node to be expanded.");
+
+ controller.ClearAll ();
+
+ Assert.AreEqual (1, cleared, "Expected ClearAll to clear the values.");
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchAdornmentManager.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchAdornmentManager.cs
new file mode 100644
index 0000000000..51539ac2cc
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchAdornmentManager.cs
@@ -0,0 +1,190 @@
+//
+// PinnedWatchAdornmentManager.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using AppKit;
+using CoreGraphics;
+
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Adornments;
+
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Debugger.VSTextView.PinnedWatches
+{
+ sealed class PinnedWatchAdornmentManager : IDisposable
+ {
+ readonly Dictionary<PinnedWatch, NSView> adornments = new Dictionary<PinnedWatch, NSView> ();
+ readonly ICocoaViewFactory cocoaViewFactory;
+ readonly IXPlatAdornmentLayer layer;
+ readonly ICocoaTextView textView;
+ readonly string path;
+ bool debugging;
+
+ public PinnedWatchAdornmentManager (ICocoaViewFactory cocoaViewFactory, ICocoaTextView textView)
+ {
+ path = textView.TextBuffer.GetFilePathOrNull ();
+
+ if (path == null)
+ return;
+
+ DebuggingService.PinnedWatches.WatchAdded += OnWatchAdded;
+ DebuggingService.PinnedWatches.WatchChanged += OnWatchChanged;
+ DebuggingService.PinnedWatches.WatchRemoved += OnWatchRemoved;
+ DebuggingService.DebugSessionStarted += OnDebugSessionStarted;
+ DebuggingService.StoppedEvent += OnDebuggingSessionStopped;
+
+ this.layer = textView.GetXPlatAdornmentLayer ("PinnedWatch");
+ this.cocoaViewFactory = cocoaViewFactory;
+ this.textView = textView;
+
+ //this.textView.LayoutChanged += OnTextViewLayoutChanged;
+
+ if (DebuggingService.IsDebugging) {
+ RenderAllAdornments ();
+ debugging = true;
+ }
+ }
+
+ void OnWatchAdded (object sender, PinnedWatchEventArgs e)
+ {
+ if (!debugging || e.Watch.File != path)
+ return;
+
+ RenderAdornment (e.Watch);
+ }
+
+ void OnWatchChanged (object sender, PinnedWatchEventArgs e)
+ {
+ if (!debugging || e.Watch.File != path)
+ return;
+
+ if (!adornments.TryGetValue (e.Watch, out var adornment))
+ return;
+
+ var view = (PinnedWatchView) ((ICocoaMaterialView) adornment).ContentView;
+
+ view.SetObjectValue (e.Watch.Value);
+ }
+
+ void OnWatchRemoved (object sender, PinnedWatchEventArgs e)
+ {
+ if (!debugging || e.Watch.File != path)
+ return;
+
+ layer.RemoveAdornmentsByTag (e.Watch);
+ adornments.Remove (e.Watch);
+ }
+
+ void RenderAdornment (PinnedWatch watch)
+ {
+ var newSpan = textView.TextSnapshot.SpanFromMDColumnAndLine (watch.Line, watch.Column, watch.EndLine, watch.EndColumn);
+ var trackingSpan = textView.TextSnapshot.CreateTrackingSpan (newSpan, SpanTrackingMode.EdgeInclusive);
+ var span = trackingSpan.GetSpan (textView.TextSnapshot);
+
+ if (textView.TextViewLines == null)
+ return;
+
+ if (!textView.TextViewLines.FormattedSpan.Contains (span.End))
+ return;
+
+ var pinnedWatchView = new PinnedWatchView (watch, DebuggingService.CurrentFrame);
+ var materialView = cocoaViewFactory.CreateMaterialView ();
+ materialView.Material = NSVisualEffectMaterial.WindowBackground;
+ materialView.ContentView = pinnedWatchView;
+ materialView.CornerRadius = 3;
+
+ var view = (NSView) materialView;
+ view.WantsLayer = true;
+
+ try {
+ var charBound = textView.TextViewLines.GetCharacterBounds (span.End);
+ var origin = new CGPoint (
+ Math.Round (charBound.Left),
+ Math.Round (charBound.TextTop + charBound.TextHeight / 2 - view.Frame.Height / 2));
+ view.SetFrameOrigin (origin);
+ } catch (Exception ex) {
+ view.SetFrameOrigin (default);
+ LoggingService.LogInternalError ("https://vsmac.dev/923058", ex);
+ }
+
+ layer.AddAdornment (XPlatAdornmentPositioningBehavior.TextRelative, span, watch, view, null);
+ adornments[watch] = view;
+ }
+
+ void RenderAllAdornments ()
+ {
+ foreach (var watch in DebuggingService.PinnedWatches.GetWatchesForFile (path))
+ RenderAdornment (watch);
+ }
+
+ void OnDebugSessionStarted (object sender, EventArgs e)
+ {
+ if (debugging || !DebuggingService.IsDebugging)
+ return;
+
+ RenderAllAdornments ();
+ debugging = true;
+ }
+
+ void OnDebuggingSessionStopped (object sender, EventArgs e)
+ {
+ if (DebuggingService.IsDebugging)
+ return;
+
+ layer.RemoveAllAdornments ();
+ adornments.Clear ();
+ debugging = false;
+ }
+
+ //void OnTextViewLayoutChanged (object sender, TextViewLayoutChangedEventArgs e)
+ //{
+ // if (!DebuggingService.IsDebugging)
+ // return;
+
+ // layer.RemoveAllAdornments ();
+ // adornments.Clear ();
+
+ // RenderAllAdornments ();
+ //}
+
+ public void Dispose ()
+ {
+ if (path == null)
+ return;
+
+ DebuggingService.PinnedWatches.WatchAdded -= OnWatchAdded;
+ DebuggingService.PinnedWatches.WatchChanged -= OnWatchChanged;
+ DebuggingService.PinnedWatches.WatchRemoved -= OnWatchRemoved;
+ DebuggingService.DebugSessionStarted -= OnDebugSessionStarted;
+ DebuggingService.StoppedEvent -= OnDebuggingSessionStopped;
+ //textView.LayoutChanged -= OnTextViewLayoutChanged;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchProvider.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchProvider.cs
new file mode 100644
index 0000000000..a73bcee1fe
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchProvider.cs
@@ -0,0 +1,54 @@
+//
+// PinnedWatchProvider.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text.Adornments;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Utilities;
+
+namespace MonoDevelop.Debugger.VSTextView.PinnedWatches
+{
+ [Export (typeof (ICocoaTextViewCreationListener))]
+ [ContentType ("text")]
+ [TextViewRole (PredefinedTextViewRoles.Debuggable)]
+ sealed class PinnedWatchProvider : ICocoaTextViewCreationListener
+ {
+ [Import]
+ internal ICocoaViewFactory cocoaViewFactory;
+
+ public void TextViewCreated (ICocoaTextView textView)
+ {
+ var manager = new PinnedWatchAdornmentManager (cocoaViewFactory, textView);
+ textView.Closed += (s, e) => manager.Dispose ();
+ }
+
+ [Export]
+ [Name ("PinnedWatch")]
+ [Order (After = PredefinedAdornmentLayers.Caret)]
+ internal AdornmentLayerDefinition visibleWhitespaceLayer;
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchView.cs
new file mode 100644
index 0000000000..eee1df7af6
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/PinnedWatches/PinnedWatchView.cs
@@ -0,0 +1,195 @@
+//
+// MacPinnedWatchView.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using AppKit;
+
+using Mono.Debugging.Client;
+
+namespace MonoDevelop.Debugger.VSTextView.PinnedWatches
+{
+ sealed class PinnedWatchView : NSScrollView
+ {
+ readonly ObjectValueTreeViewController controller;
+ readonly NSLayoutConstraint heightConstraint;
+ readonly NSLayoutConstraint widthConstraint;
+ readonly MacObjectValueTreeView treeView;
+ NSLayoutConstraint superHeightConstraint;
+ NSLayoutConstraint superWidthConstraint;
+ ObjectValue objectValue;
+ bool disposed;
+
+ public PinnedWatchView (PinnedWatch watch, StackFrame frame)
+ {
+ HasVerticalScroller = true;
+ AutohidesScrollers = true;
+
+ controller = new ObjectValueTreeViewController ();
+ controller.SetStackFrame (frame);
+ controller.AllowEditing = true;
+
+ treeView = controller.GetMacControl (headersVisible: false, compactView: true, allowPinning: true);
+
+ controller.PinnedWatch = watch;
+
+ if (watch.Value != null)
+ controller.AddValue (watch.Value);
+
+ var rect = treeView.Frame;
+
+ if (rect.Height < 1)
+ treeView.Frame = new CoreGraphics.CGRect (rect.X, rect.Y, rect.Width, 19);
+
+ DocumentView = treeView;
+ Frame = treeView.Frame;
+
+ heightConstraint = HeightAnchor.ConstraintEqualToConstant (treeView.Frame.Height);
+ heightConstraint.Active = true;
+
+ widthConstraint = WidthAnchor.ConstraintEqualToConstant (treeView.Frame.Width);
+ widthConstraint.Active = true;
+
+ DebuggingService.ResumedEvent += OnDebuggerResumed;
+ DebuggingService.PausedEvent += OnDebuggerPaused;
+ treeView.Resized += OnTreeViewResized;
+ }
+
+ public void SetObjectValue (ObjectValue value)
+ {
+ if (value == objectValue)
+ return;
+
+ controller.ClearAll ();
+
+ if (value != null)
+ controller.AddValue (value);
+
+ objectValue = value;
+ }
+
+ public override void ViewDidMoveToSuperview ()
+ {
+ base.ViewDidMoveToSuperview ();
+
+ if (Superview != null) {
+ superHeightConstraint = Superview.HeightAnchor.ConstraintEqualToConstant (Frame.Height);
+ superWidthConstraint = Superview.WidthAnchor.ConstraintEqualToConstant (Frame.Width);
+ superHeightConstraint.Active = true;
+ superWidthConstraint.Active = true;
+ } else {
+ superHeightConstraint?.Dispose ();
+ superWidthConstraint?.Dispose ();
+ superHeightConstraint = null;
+ superWidthConstraint = null;
+ }
+ }
+
+ void OnTreeViewResized (object sender, EventArgs e)
+ {
+ //const string CocoaTextViewScrollView = "CocoaTextViewScrollView";
+ const string CocoaTextViewControl = "CocoaTextViewControl";
+ //const string CocoaEditorGridView = "CocoaEditorGridView";
+
+ var materialView = Superview;
+
+ // Find our parent CocoaTextViewControl
+ var textView = materialView;
+ while (textView != null && textView.GetType ().Name != CocoaTextViewControl)
+ textView = textView.Superview;
+
+ if (textView == null)
+ return;
+
+ var origin = textView.ConvertPointFromView (Frame.Location, this);
+ var maxHeight = NMath.Max (textView.Frame.Bottom - origin.Y, treeView.RowHeight * 2);
+ var height = treeView.FittingSize.Height;
+ var width = treeView.Frame.Width;
+
+ height = NMath.Min (height, maxHeight);
+
+ heightConstraint.Constant = height;
+ widthConstraint.Constant = width;
+
+ superHeightConstraint.Constant = height;
+ superWidthConstraint.Constant = width;
+
+#if REPARENT_SO_SCROLLING_WORKS
+ // Find our parent CocoaEditorGridView
+ var gridView = textView.Superview;
+ while (gridView != null && gridView.GetType ().Name != CocoaEditorGridView)
+ gridView = gridView.Superview;
+
+ if (gridView == null)
+ return;
+
+ // Find the CocoaTextViewScrollView
+ NSView textViewScrollView = null;
+ foreach (var child in gridView.Subviews) {
+ if (child.GetType ().Name == CocoaTextViewScrollView) {
+ textViewScrollView = child;
+ break;
+ }
+ }
+
+ materialView.RemoveFromSuperview ();
+
+ gridView.AddSubview (materialView, NSWindowOrderingMode.Above, textViewScrollView);
+#endif
+ }
+
+ void OnDebuggerResumed (object sender, EventArgs e)
+ {
+ controller.ChangeCheckpoint ();
+ controller.AllowExpanding = false;
+ controller.AllowEditing = false;
+ }
+
+ void OnDebuggerPaused (object sender, EventArgs e)
+ {
+ controller.AllowExpanding = true;
+ controller.AllowEditing = true;
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing && !disposed) {
+ DebuggingService.ResumedEvent -= OnDebuggerResumed;
+ DebuggingService.PausedEvent -= OnDebuggerPaused;
+ treeView.Resized -= OnTreeViewResized;
+ superHeightConstraint?.Dispose ();
+ superWidthConstraint?.Dispose ();
+ superHeightConstraint = null;
+ superWidthConstraint = null;
+ heightConstraint.Dispose ();
+ widthConstraint.Dispose ();
+ disposed = true;
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs
index 023af1e8cf..7ef683ad20 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/DebuggerQuickInfoSource.cs
@@ -1,11 +1,14 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+
+using Gtk;
+
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
-using MonoDevelop.Core;
using Microsoft.VisualStudio.Text.Editor;
-using Gtk;
+
+using MonoDevelop.Core;
using MonoDevelop.Ide.Gui.Documents;
namespace MonoDevelop.Debugger.VSTextView.QuickInfo
@@ -14,9 +17,11 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
{
readonly DebuggerQuickInfoSourceProvider provider;
readonly ITextBuffer textBuffer;
- DebugValueWindow window;
- ITextView lastView;
DocumentView lastDocumentView;
+#if MAC
+ MacDebuggerTooltipWindow window;
+#endif
+ ITextView lastView;
public DebuggerQuickInfoSource (DebuggerQuickInfoSourceProvider provider, ITextBuffer textBuffer)
{
@@ -24,7 +29,6 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
this.textBuffer = textBuffer;
DebuggingService.CurrentFrameChanged += CurrentFrameChanged;
DebuggingService.StoppedEvent += TargetProcessExited;
-
}
void CurrentFrameChanged (object sender, EventArgs e)
@@ -79,10 +83,11 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
{
if (DebuggingService.CurrentFrame == null)
return null;
+
if (window != null)
await Runtime.RunInMainThread (DestroyWindow);
- var view = session.TextView;
+ var view = session.TextView;
var textViewLines = view.TextViewLines;
var snapshot = textViewLines.FormattedSpan.Snapshot;
var triggerPoint = session.GetTriggerPoint (textBuffer);
@@ -110,6 +115,7 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
return null;
}
}
+
return null;
}
@@ -130,7 +136,7 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
if (val == null || val.IsUnknown || val.IsNotSupported)
return;
- if (!view.Properties.TryGetProperty (typeof (Gtk.Widget), out Gtk.Widget gtkParent))
+ if (!view.Properties.TryGetProperty (typeof (Widget), out Widget gtkParent))
return;
provider.textDocumentFactoryService.TryGetTextDocument (view.TextDataModel.DocumentBuffer, out var textDocument);
@@ -140,22 +146,38 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
// and do our own thing, notice VS does same thing
await session.DismissAsync ();
await provider.joinableTaskContext.Factory.SwitchToMainThreadAsync ();
- this.lastView = view;
+ lastView = view;
+
val.Name = debugInfo.Text;
- window = new DebugValueWindow ((Gtk.Window)gtkParent.Toplevel, textDocument?.FilePath, textBuffer.CurrentSnapshot.GetLineNumberFromPosition (debugInfo.Span.GetStartPoint (textBuffer.CurrentSnapshot)), DebuggingService.CurrentFrame, val, null);
- Ide.IdeApp.CommandService.RegisterTopWindow (window);
- var bounds = view.TextViewLines.GetCharacterBounds (point);
+
+#if MAC
+ var location = new PinnedWatchLocation (textDocument?.FilePath);
+ var snapshot = view.TextDataModel.DocumentBuffer.CurrentSnapshot;
+ int line, column;
+
+ var start = debugInfo.Span.GetStartPoint (snapshot);
+ snapshot.GetLineAndColumn (start, out line, out column);
+ location.Column = column;
+ location.Line = line;
+
+ var end = debugInfo.Span.GetEndPoint (snapshot);
+ snapshot.GetLineAndColumn (end, out line, out column);
+ location.EndColumn = column;
+ location.EndLine = line;
+
+ window = new MacDebuggerTooltipWindow (location, DebuggingService.CurrentFrame, val, watch: null);
+
view.LayoutChanged += LayoutChanged;
#if CLOSE_ON_FOCUS_LOST
view.LostAggregateFocus += View_LostAggregateFocus;
#endif
RegisterForHiddenAsync (view).Ignore ();
- window.LeaveNotifyEvent += LeaveNotifyEvent;
-#if MAC
- var cocoaView = ((ICocoaTextView)view);
- var cgPoint = cocoaView.VisualElement.ConvertPointToView (new CoreGraphics.CGPoint (bounds.Left - view.ViewportLeft, bounds.Top - view.ViewportTop), cocoaView.VisualElement.Superview);
- cgPoint.Y = cocoaView.VisualElement.Superview.Frame.Height - cgPoint.Y;
- window.ShowPopup (gtkParent, new Gdk.Rectangle ((int)cgPoint.X, (int)cgPoint.Y, (int)bounds.Width, (int)bounds.Height), Components.PopupPosition.TopLeft);
+
+ var cocoaView = (ICocoaTextView) view;
+ var bounds = view.TextViewLines.GetCharacterBounds (point);
+ var rect = new CoreGraphics.CGRect (bounds.Left - view.ViewportLeft, bounds.Top - view.ViewportTop, bounds.Width, bounds.Height);
+
+ window.Show (rect, cocoaView.VisualElement, AppKit.NSRectEdge.MaxXEdge);
#else
throw new NotImplementedException ();
#endif
@@ -173,6 +195,7 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
{
DestroyWindow ();
}
+
#if CLOSE_ON_FOCUS_LOST
private void View_LostAggregateFocus (object sender, EventArgs e)
{
@@ -186,6 +209,7 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
#endif
}
#endif
+
private void LayoutChanged (object sender, TextViewLayoutChangedEventArgs e)
{
if (e.OldViewState.ViewportLeft != e.NewViewState.ViewportLeft ||
@@ -195,19 +219,11 @@ namespace MonoDevelop.Debugger.VSTextView.QuickInfo
DestroyWindow ();
}
- private void LeaveNotifyEvent (object o, LeaveNotifyEventArgs args)
- {
- if(args.Event.Detail != Gdk.NotifyType.Nonlinear)
- return;
- DestroyWindow ();
- }
-
void DestroyWindow ()
{
Runtime.AssertMainThread ();
if (window != null) {
- window.Destroy ();
- window.LeaveNotifyEvent -= LeaveNotifyEvent;
+ window.Close ();
window = null;
}
if (lastView != null) {
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/IDebugInfoProvider.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/IDebugInfoProvider.cs
index 94dc291a48..1effd8017b 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/IDebugInfoProvider.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/IDebugInfoProvider.cs
@@ -1,7 +1,7 @@
using System;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Text;
-using System.Threading;
namespace MonoDevelop.Debugger.VSTextView.QuickInfo
{
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/MacDebuggerTooltipWindow.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/MacDebuggerTooltipWindow.cs
new file mode 100644
index 0000000000..dc60ce593e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/QuickInfo/MacDebuggerTooltipWindow.cs
@@ -0,0 +1,158 @@
+//
+// MacDebuggerTooltipWindow.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using AppKit;
+
+using Mono.Debugging.Client;
+
+namespace MonoDevelop.Debugger
+{
+ sealed class MacDebuggerTooltipWindow : NSPopover
+ {
+ readonly ObjectValueTreeViewController controller;
+ readonly NSLayoutConstraint heightConstraint;
+ readonly NSLayoutConstraint widthConstraint;
+ readonly MacObjectValueTreeView treeView;
+ readonly NSScrollView scrollView;
+ bool disposed;
+
+ public MacDebuggerTooltipWindow (PinnedWatchLocation location, StackFrame frame, ObjectValue value, PinnedWatch watch)
+ {
+ Animates = false;
+ Behavior = NSPopoverBehavior.Semitransient;
+
+ controller = new ObjectValueTreeViewController ();
+ controller.SetStackFrame (frame);
+ controller.AllowEditing = true;
+ controller.PinnedWatch = watch;
+ controller.PinnedWatchLocation = location;
+
+ treeView = controller.GetMacControl (headersVisible: false, allowPinning: true, compactView: true, rootPinVisible: true);
+ treeView.NodePinned += OnPinStatusChanged;
+ treeView.StartEditing += OnStartEditing;
+ treeView.EndEditing += OnEndEditing;
+ controller.AddValue (value);
+
+ scrollView = new NSScrollView {
+ HasVerticalScroller = true,
+ AutohidesScrollers = true,
+ DocumentView = treeView,
+ Frame = treeView.Frame
+ };
+
+ ContentViewController = new NSViewController {
+ View = scrollView
+ };
+
+ widthConstraint = scrollView.WidthAnchor.ConstraintEqualToAnchor (treeView.WidthAnchor);
+ widthConstraint.Active = true;
+
+ heightConstraint = scrollView.HeightAnchor.ConstraintEqualToConstant (treeView.Frame.Height);
+ heightConstraint.Active = true;
+
+ treeView.Resized += OnTreeViewResized;
+ }
+
+ public DebuggerSession GetDebuggerSession ()
+ {
+ return controller.GetStackFrame ()?.DebuggerSession;
+ }
+
+ static nfloat GetMaxHeight (NSWindow window)
+ {
+ var visibleFrame = window.Screen.VisibleFrame;
+
+ // Note: You would think that we could make use of the full VisualFrame height,
+ // but macOS will not actually make our tooltip window that large no matter
+ // what.
+ //
+ // The downside of *trying* to use the full VisualFrame height is that the
+ // scrollView will think that it is that tall when it in fact is not, thereby
+ // making it impossible to scroll all the way to the top (and/or, potentially,
+ // the bottom).
+ //
+ // On my machine, the VisualFrame height is 972 (Frame height is 1050 with a
+ // menubar 23 pixels tall and a dock that is 55 pixels tall).
+ //
+ // macOS does not seem to allow the tooltip window to get larger than 943 pixels
+ // which is 29 pixels shorter than the VisualFrame height. Let's just round that
+ // up to 30 pixels.
+
+ return visibleFrame.Height - 30;
+ }
+
+ void OnTreeViewResized (object sender, EventArgs e)
+ {
+ var maxHeight = GetMaxHeight (treeView.Window);
+ var height = treeView.FittingSize.Height;
+
+ height = NMath.Min (height, maxHeight);
+
+ heightConstraint.Constant = height;
+ }
+
+ void OnPinStatusChanged (object sender, EventArgs args)
+ {
+ Close ();
+ }
+
+ void OnStartEditing (object sender, EventArgs args)
+ {
+ //Modal = true;
+ //PresentViewControllerAsModalWindow (this);
+ }
+
+ void OnEndEditing (object sender, EventArgs args)
+ {
+ //Modal = false;
+ }
+
+ void PreviewWindowManager_WindowClosed (object sender, EventArgs e)
+ {
+ // When Preview window is closed we want to put focus(IsActive=true) back on DebugValueWindow
+ // otherwise CommandManager will think IDE doesn't have any window Active/Focused and think
+ // user switched to another app and DebugValueWindow will closed itself on "FocusOut" event
+ //Present ();
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing && !disposed) {
+ //PreviewWindowManager.WindowClosed -= PreviewWindowManager_WindowClosed;
+ treeView.Resized -= OnTreeViewResized;
+ treeView.NodePinned -= OnPinStatusChanged;
+ treeView.StartEditing -= OnStartEditing;
+ treeView.EndEditing -= OnEndEditing;
+ heightConstraint.Dispose ();
+ widthConstraint.Dispose ();
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
index 924762a652..d38a58406b 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
@@ -189,7 +189,23 @@
<Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueStackFrame.cs" />
<Compile Include="MonoDevelop.Debugger\ObjectValue\ObjectValueNodeEventArgs.cs" />
<Compile Include="MonoDevelop.Debugger\ObjectValue\Gtk\GtkObjectValueTreeView.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacObjectValueTreeView.cs" />
<Compile Include="MonoDevelop.Debugger\ObjectValue\IObjectValueTreeView.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\LoadingObjectValueNode.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\AddNewExpressionObjectValueNode.cs" />
+ <Compile Include="MonoDevelop.Debugger.VSTextView\QuickInfo\MacDebuggerTooltipWindow.cs" />
+ <Compile Include="MonoDevelop.Debugger.VSTextView\PinnedWatches\PinnedWatchProvider.cs" />
+ <Compile Include="MonoDevelop.Debugger.VSTextView\PinnedWatches\PinnedWatchAdornmentManager.cs" />
+ <Compile Include="MonoDevelop.Debugger.VSTextView\PinnedWatches\PinnedWatchView.cs" />
+ <Compile Include="MonoDevelop.Debugger\PinnedWatchLocation.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacObjectValueNode.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacObjectValueTreeViewDataSource.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacObjectValueTreeViewDelegate.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacDebuggerObjectCellViewBase.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacDebuggerObjectNameView.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacDebuggerObjectValueView.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacDebuggerObjectTypeView.cs" />
+ <Compile Include="MonoDevelop.Debugger\ObjectValue\Mac\MacDebuggerObjectPinView.cs" />
</ItemGroup>
<ItemGroup Condition="$(OS) != 'Windows_NT'">
<Compile Include="MonoDevelop.Debugger.VSTextView\ExceptionCaught\ExceptionCaughtProvider.cs" />
@@ -380,6 +396,8 @@
<Folder Include="MonoDevelop.Debugger.VSTextView\ExceptionCaught\" />
<Folder Include="MonoDevelop.Debugger\ObjectValue\" />
<Folder Include="MonoDevelop.Debugger\ObjectValue\Gtk\" />
+ <Folder Include="MonoDevelop.Debugger\ObjectValue\Mac\" />
+ <Folder Include="MonoDevelop.Debugger.VSTextView\PinnedWatches\" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="MonoDevelop.SourceEditor" />
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs
index d4ad47e7f5..934e39d2c2 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugValueWindow.cs
@@ -25,6 +25,8 @@
//
//
+// Note: This is only used by the old (Gtk) TextEditor.
+
using System;
using Gdk;
@@ -38,9 +40,10 @@ using MonoDevelop.Components;
namespace MonoDevelop.Debugger
{
+ [Obsolete ("This API is only used by the old Gtk TextEditor")]
class DebugValueWindow : PopoverWindow
{
- readonly bool useNewTreeView = PropertyService.Get ("MonoDevelop.Debugger.UseNewTreeView", false);
+ readonly bool useNewTreeView = PropertyService.Get ("MonoDevelop.Debugger.UseNewTreeView", true);
readonly ObjectValueTreeViewController controller;
readonly ObjectValueTreeView objValueTreeView;
readonly TreeView treeView;
@@ -78,7 +81,7 @@ namespace MonoDevelop.Debugger
currentBgColor = bgColor;
}
- public DebugValueWindow (Gtk.Window transientFor, string pinnedWatchFileName, int pinnedWatchLine, StackFrame frame, ObjectValue value, PinnedWatch watch) : base (Gtk.WindowType.Toplevel)
+ public DebugValueWindow (Gtk.Window transientFor, PinnedWatchLocation location, StackFrame frame, ObjectValue value, PinnedWatch watch) : base (Gtk.WindowType.Toplevel)
{
TypeHint = WindowTypeHint.PopupMenu;
AllowShrink = false;
@@ -89,9 +92,10 @@ namespace MonoDevelop.Debugger
// Avoid getting the focus when the window is shown. We'll get it when the mouse enters the window
AcceptFocus = false;
- sw = new ScrolledWindow ();
- sw.HscrollbarPolicy = PolicyType.Never;
- sw.VscrollbarPolicy = PolicyType.Never;
+ sw = new ScrolledWindow {
+ HscrollbarPolicy = PolicyType.Never,
+ VscrollbarPolicy = PolicyType.Never
+ };
UpdateTreeStyle (Theme.BackgroundColor);
@@ -99,12 +103,10 @@ namespace MonoDevelop.Debugger
controller = new ObjectValueTreeViewController ();
controller.SetStackFrame (frame);
controller.AllowEditing = true;
-
- treeView = (TreeView) controller.GetControl (headersVisible: false, allowPinning: true, compactView: true, rootPinVisible: true);
-
controller.PinnedWatch = watch;
- controller.PinnedWatchLine = pinnedWatchLine;
- controller.PinnedWatchFile = pinnedWatchFileName;
+ controller.PinnedWatchLocation = location;
+
+ treeView = controller.GetGtkControl (headersVisible: false, allowPinning: true, compactView: true, rootPinVisible: true);
if (treeView is IObjectValueTreeView ovtv) {
ovtv.StartEditing += OnStartEditing;
@@ -121,8 +123,7 @@ namespace MonoDevelop.Debugger
objValueTreeView.AllowPinning = true;
objValueTreeView.CompactView = true;
objValueTreeView.PinnedWatch = watch;
- objValueTreeView.PinnedWatchLine = pinnedWatchLine;
- objValueTreeView.PinnedWatchFile = pinnedWatchFileName;
+ objValueTreeView.PinnedWatchLocation = location;
objValueTreeView.Frame = frame;
objValueTreeView.AddValue (value);
@@ -204,8 +205,8 @@ namespace MonoDevelop.Debugger
GetPosition (out x, out y);
h = (int)sw.Vadjustment.Upper;
w = (int)sw.Hadjustment.Upper;
- int dy = y + h - this.Screen.Height;
- int dx = x + w - this.Screen.Width;
+ int dy = y + h - Screen.Height;
+ int dx = x + w - Screen.Width;
if (dy > 0 && sw.VscrollbarPolicy == PolicyType.Never) {
sw.VscrollbarPolicy = PolicyType.Always;
@@ -226,17 +227,17 @@ namespace MonoDevelop.Debugger
QueueDraw ();
}
- protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+ protected override void OnSizeAllocated (Rectangle allocation)
{
- if (MonoDevelop.Core.Platform.IsMac || MonoDevelop.Core.Platform.IsWindows) {
+ if (Platform.IsMac || Platform.IsWindows) {
// fails on linux see: Bug 8481 - Debug value tooltips very often appear at the top-left corner of the screen instead of near the element to inspect
const int edgeGap = 2;
int oldY, x, y;
- this.GetPosition (out x, out y);
+ GetPosition (out x, out y);
oldY = y;
- Xwt.Rectangle geometry = IdeServices.DesktopService.GetUsableMonitorGeometry (Screen.Number, Screen.GetMonitorAtPoint (x, y));
+ var geometry = IdeServices.DesktopService.GetUsableMonitorGeometry (Screen.Number, Screen.GetMonitorAtPoint (x, y));
int top = (int)geometry.Top;
if (allocation.Height <= geometry.Height && y + allocation.Height >= geometry.Y + geometry.Height - edgeGap)
y = top + ((int)geometry.Height - allocation.Height - edgeGap);
@@ -257,7 +258,7 @@ namespace MonoDevelop.Debugger
// When Preview window is closed we want to put focus(IsActive=true) back on DebugValueWindow
// otherwise CommandManager will think IDE doesn't have any window Active/Focused and think
// user switched to another app and DebugValueWindow will closed itself on "FocusOut" event
- this.Present ();
+ Present ();
}
}
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggerOptionsPanelWidget.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggerOptionsPanelWidget.cs
index 466c799857..04da2882de 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggerOptionsPanelWidget.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggerOptionsPanelWidget.cs
@@ -183,7 +183,7 @@ namespace MonoDevelop.Debugger
checkAllowToString.Sensitive = checkAllowEval.Active;
spinTimeout.Value = options.EvaluationOptions.EvaluationTimeout;
enableLogging.Active = PropertyService.Get ("MonoDevelop.Debugger.DebuggingService.DebuggerLogging", false);
- useNewTreeView.Active = PropertyService.Get ("MonoDevelop.Debugger.UseNewTreeView", false);
+ useNewTreeView.Active = PropertyService.Get ("MonoDevelop.Debugger.UseNewTreeView", true);
}
public void Store ()
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
index bc64aa7348..220fbc011c 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
@@ -48,7 +48,7 @@ namespace MonoDevelop.Debugger
static readonly Xwt.Drawing.Image WarningIconPixbufInner = Xwt.Drawing.Image.FromResource ("exception-outline-16.png");
readonly Dictionary<ExceptionInfo, ExceptionInfo> reverseInnerExceptions = new Dictionary<ExceptionInfo, ExceptionInfo> ();
- readonly bool useNewTreeView = PropertyService.Get ("MonoDevelop.Debugger.UseNewTreeView", false);
+ readonly bool useNewTreeView = PropertyService.Get ("MonoDevelop.Debugger.UseNewTreeView", true);
readonly ExceptionCaughtMessage message;
readonly ExceptionInfo exception;
@@ -186,7 +186,7 @@ widget ""*.exception_help_link_label"" style ""exception-help-link-label""
controller.SetStackFrame (DebuggingService.CurrentFrame);
controller.AllowExpanding = true;
- exceptionValueTreeView = (TreeView) controller.GetControl (allowPopupMenu: false);
+ exceptionValueTreeView = controller.GetGtkControl (allowPopupMenu: false);
} else {
var objValueTreeView = new ObjectValueTreeView ();
objValueTreeView.Frame = DebuggingService.CurrentFrame;
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs
index 881a590921..f979b5c29c 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/LocalsPad.cs
@@ -25,8 +25,6 @@
//
//
-using System.Linq;
-
using Mono.Debugging.Client;
namespace MonoDevelop.Debugger
@@ -43,6 +41,27 @@ namespace MonoDevelop.Debugger
}
}
+#if ADD_FAKE_NODES
+ void AddFakeNodes ()
+ {
+ var xx = new System.Collections.Generic.List<ObjectValueNode> ();
+
+ xx.Add (new FakeObjectValueNode ("f1"));
+ xx.Add (new FakeIsImplicitNotSupportedObjectValueNode ());
+
+ xx.Add (new FakeEvaluatingGroupObjectValueNode (1));
+ xx.Add (new FakeEvaluatingGroupObjectValueNode (0));
+ xx.Add (new FakeEvaluatingGroupObjectValueNode (5));
+
+ xx.Add (new FakeEvaluatingObjectValueNode ());
+ xx.Add (new FakeEnumerableObjectValueNode (10));
+ xx.Add (new FakeEnumerableObjectValueNode (20));
+ xx.Add (new FakeEnumerableObjectValueNode (23));
+
+ controller.AddValues (xx);
+ }
+#endif
+
void ReloadValues ()
{
var frame = DebuggingService.CurrentFrame;
@@ -61,21 +80,9 @@ namespace MonoDevelop.Debugger
controller.ClearValues ();
controller.AddValues (locals);
- //var xx = new System.Collections.Generic.List<ObjectValueNode> ();
-
- //xx.Add (new FakeObjectValueNode ("f1"));
- //xx.Add (new FakeIsImplicitNotSupportedObjectValueNode ());
-
- //xx.Add (new FakeEvaluatingGroupObjectValueNode (1));
- //xx.Add (new FakeEvaluatingGroupObjectValueNode (0));
- //xx.Add (new FakeEvaluatingGroupObjectValueNode (5));
-
- //xx.Add (new FakeEvaluatingObjectValueNode ());
- //xx.Add (new FakeEnumerableObjectValueNode (10));
- //xx.Add (new FakeEnumerableObjectValueNode (20));
- //xx.Add (new FakeEnumerableObjectValueNode (23));
-
- //controller.AddValues (xx);
+#if ADD_FAKE_NODES
+ AddFakeNodes ();
+#endif
} else {
tree.ClearValues ();
tree.AddValues (locals);
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/AddNewExpressionObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/AddNewExpressionObjectValueNode.cs
new file mode 100644
index 0000000000..981553c2f3
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/AddNewExpressionObjectValueNode.cs
@@ -0,0 +1,35 @@
+//
+// AddNewExpressionObjectValueNode.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace MonoDevelop.Debugger
+{
+ sealed class AddNewExpressionObjectValueNode : ObjectValueNode
+ {
+ public AddNewExpressionObjectValueNode () : base (string.Empty)
+ {
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs
index d03cbea270..34f1106645 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/DebuggerObjectValueNode.cs
@@ -62,7 +62,7 @@ namespace MonoDevelop.Debugger
var name = node.Name;
while (node != null && node.Parent is DebuggerObjectValueNode) {
expression = node.DebuggerObject.ChildSelector + expression;
- node = (DebuggerObjectValueNode)node.Parent;
+ node = (DebuggerObjectValueNode) node.Parent;
name = node.Name;
}
@@ -107,7 +107,7 @@ namespace MonoDevelop.Debugger
#region IEvaluatingGroupObjectValueNode
bool IEvaluatingGroupObjectValueNode.IsEvaluatingGroup => DebuggerObject.IsEvaluatingGroup;
- ObjectValueNode [] IEvaluatingGroupObjectValueNode.GetEvaluationGroupReplacementNodes ()
+ ObjectValueNode[] IEvaluatingGroupObjectValueNode.GetEvaluationGroupReplacementNodes ()
{
var replacementNodes = new ObjectValueNode[DebuggerObject.ArrayCount];
@@ -157,7 +157,7 @@ namespace MonoDevelop.Debugger
}, cancellationToken);
}
- static Task<ObjectValue []> GetChildrenAsync (ObjectValue value, int index, int count, CancellationToken cancellationToken)
+ static Task<ObjectValue[]> GetChildrenAsync (ObjectValue value, int index, int count, CancellationToken cancellationToken)
{
return Task.Run(() => {
try {
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs
index 49338b09f1..20b9cd981e 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Gtk/GtkObjectValueTreeView.cs
@@ -102,15 +102,14 @@ namespace MonoDevelop.Debugger
bool editing;
bool allowEditing;
- bool allowWatchExpressions;
bool wasHandled;
CodeCompletionContext ctx;
Gdk.Key key;
char keyChar;
Gdk.ModifierType modifierState;
uint keyValue;
- PreviewButtonIcons iconBeforeSelected;
- PreviewButtonIcons currentIcon;
+ PreviewButtonIcon iconBeforeSelected;
+ PreviewButtonIcon currentIcon;
TreeIter currentHoverIter = TreeIter.Zero;
Adjustment oldHadjustment;
Adjustment oldVadjustment;
@@ -173,7 +172,6 @@ namespace MonoDevelop.Debugger
ObjectValueTreeViewController controller,
bool allowEditing,
bool headersVisible,
- bool allowWatchExpressions,
bool compactView,
bool allowPinning,
bool allowPopupMenu,
@@ -186,10 +184,10 @@ namespace MonoDevelop.Debugger
// ensure this is set when we set up the view, don't try and refresh just yet
this.allowEditing = allowEditing;
- this.allowWatchExpressions = allowWatchExpressions;
this.debuggerService = debuggerService;
this.controller = controller;
+ this.root = controller.Root;
store = new TreeStore (typeof (string), typeof (string), typeof (string), typeof (bool), typeof (bool), typeof (string), typeof (string), typeof (string), typeof (bool), typeof (string), typeof (Xwt.Drawing.Image), typeof (bool), typeof (string), typeof (Xwt.Drawing.Image), typeof (bool), typeof (string), typeof (ObjectValueNode));
Model = store;
@@ -318,6 +316,7 @@ namespace MonoDevelop.Debugger
focus_line_width = (int)StyleGetProperty ("focus-line-width") * 2; //we just use *2 version in GetMaxWidth
AdjustColumnSizes ();
+ Refresh (false);
}
/// <summary>
@@ -342,13 +341,7 @@ namespace MonoDevelop.Debugger
/// Gets a value indicating whether the user should be able to add watch expressions to the tree
/// </summary>
public bool AllowWatchExpressions {
- get => allowWatchExpressions;
- set {
- if (allowWatchExpressions != value) {
- allowWatchExpressions = value;
- Refresh (false);
- }
- }
+ get { return controller.AllowWatchExpressions; }
}
/// <summary>
@@ -491,8 +484,7 @@ namespace MonoDevelop.Debugger
{
base.OnShown ();
AdjustColumnSizes ();
- if (compactView)
- RecalculateWidth ();
+ CompactColumns ();
}
protected override void OnRealized ()
@@ -522,12 +514,28 @@ namespace MonoDevelop.Debugger
}
/// <summary>
- /// Reloads the tree from the root node
+ /// Notifies the treeview that the tree has been cleared
+ /// </summary>
+ void IObjectValueTreeView.Cleared ()
+ {
+ Refresh (false);
+ }
+
+ /// <summary>
+ /// Notifies the treeview that the specified node has been added to the root node's children
+ /// </summary>
+ /// <param name="node">The node that was appended.</param>
+ void IObjectValueTreeView.Appended (ObjectValueNode node)
+ {
+ Refresh (false);
+ }
+
+ /// <summary>
+ /// Notifies the treeview that the specified nodes have been added to the root node's children
/// </summary>
- public void Reload (ObjectValueNode root)
+ /// <param name="nodes">The nodes that were appended.</param>
+ void IObjectValueTreeView.Appended (IList<ObjectValueNode> nodes)
{
- // TODO: how to tell whether to reset scroll position or not?
- this.root = root;
Refresh (false);
}
@@ -544,7 +552,7 @@ namespace MonoDevelop.Debugger
/// the set of replacement nodes. Handles the case where, for example, the "locals" is replaced
/// with the set of local values
/// </summary>
- public void LoadEvaluatedNode (ObjectValueNode node, ObjectValueNode [] replacementNodes)
+ public void LoadEvaluatedNode (ObjectValueNode node, ObjectValueNode[] replacementNodes)
{
OnEvaluationCompleted (node, replacementNodes);
}
@@ -561,15 +569,13 @@ namespace MonoDevelop.Debugger
// them in so that the tree does not collapse the row when the last child is removed
MergeChildrenIntoTree (node, iter, index, count);
- // if we did not load all the children, add a More node
+ // if we did not load all the children, add a Show More node
if (!node.ChildrenLoaded) {
AppendNodeToTreeModel (iter, null, new ShowMoreValuesObjectValueNode (node));
}
}
- if (compactView) {
- RecalculateWidth ();
- }
+ CompactColumns ();
}
// TODO: if we don't want the scrolling, we can probably get rid of this
@@ -589,8 +595,7 @@ namespace MonoDevelop.Debugger
ExpandRow (path, false);
}
- if (compactView)
- RecalculateWidth ();
+ CompactColumns ();
// TODO: all this scrolling kind of seems awkward
//if (path != null)
@@ -649,8 +654,8 @@ namespace MonoDevelop.Debugger
if (replacementNodes.Length == 0) {
// we can remove the node altogether, eg there are no local variables to show
Remove (ref iter);
- } else if (replacementNodes.Length > 0) {
- node = replacementNodes [0];
+ } else {
+ node = replacementNodes[0];
SetValues (parent, iter, node.Name, node);
for (int n = 1; n < replacementNodes.Length; n++) {
@@ -660,9 +665,7 @@ namespace MonoDevelop.Debugger
}
}
- if (compactView) {
- RecalculateWidth ();
- }
+ CompactColumns ();
}
bool Remove (ref TreeIter iter)
@@ -697,12 +700,6 @@ namespace MonoDevelop.Debugger
restoringState = false;
}
- //public void Update ()
- //{
- // //cachedValues.Clear ();
- // Refresh (true);
- //}
-
void Refresh (bool resetScrollPosition)
{
// Note: this is a hack that ideally we could get rid of...
@@ -849,7 +846,7 @@ namespace MonoDevelop.Debugger
if (val.CanRefresh)
valueButton = GettextCatalog.GetString ("Show Value");
} else if (val.IsEvaluating) {
- strval = GettextCatalog.GetString ("Evaluating...");
+ strval = GettextCatalog.GetString ("Evaluating\u2026");
evaluateStatusIcon = "md-spinner-16";
@@ -956,8 +953,7 @@ namespace MonoDevelop.Debugger
base.OnRowExpanded (iter, path);
- if (compactView)
- RecalculateWidth ();
+ CompactColumns ();
HideValueButton (iter);
@@ -970,8 +966,7 @@ namespace MonoDevelop.Debugger
base.OnRowCollapsed (iter, path);
- if (compactView)
- RecalculateWidth ();
+ CompactColumns ();
NodeCollapse?.Invoke (this, new ObjectValueNodeEventArgs (node));
@@ -1140,16 +1135,6 @@ namespace MonoDevelop.Debugger
}
}
- enum PreviewButtonIcons
- {
- None,
- Hidden,
- RowHover,
- Hover,
- Active,
- Selected,
- }
-
bool ValidObjectForPreviewIcon (TreeIter it)
{
var obj = GetDebuggerObjectValueAtIter (it);
@@ -1193,13 +1178,13 @@ namespace MonoDevelop.Debugger
var iconXOffset = cellArea.X + w + cr.Xpad * 3;
if (iconXOffset < evnt.X &&
iconXOffset + 16 > evnt.X) {
- SetPreviewButtonIcon (PreviewButtonIcons.Hover, it);
+ SetPreviewButtonIcon (PreviewButtonIcon.Hover, it);
} else {
- SetPreviewButtonIcon (PreviewButtonIcons.RowHover, it);
+ SetPreviewButtonIcon (PreviewButtonIcon.RowHover, it);
}
}
} else {
- SetPreviewButtonIcon (PreviewButtonIcons.RowHover, it);
+ SetPreviewButtonIcon (PreviewButtonIcon.RowHover, it);
}
if (allowPinning) {
@@ -1214,7 +1199,7 @@ namespace MonoDevelop.Debugger
}
}
} else {
- SetPreviewButtonIcon (PreviewButtonIcons.Hidden);
+ SetPreviewButtonIcon (PreviewButtonIcon.Hidden);
}
return base.OnMotionNotifyEvent (evnt);
}
@@ -1231,7 +1216,7 @@ namespace MonoDevelop.Debugger
{
if (!editing)
CleanPinIcon ();
- SetPreviewButtonIcon (PreviewButtonIcons.Hidden);
+ SetPreviewButtonIcon (PreviewButtonIcon.Hidden);
return base.OnLeaveNotifyEvent (evnt);
}
@@ -1283,8 +1268,8 @@ namespace MonoDevelop.Debugger
case Gdk.Key.Delete:
case Gdk.Key.KP_Delete:
case Gdk.Key.BackSpace:
- string expression;
- ObjectValue val;
+ //string expression;
+ //ObjectValue val;
TreeIter iter;
if (!AllowEditing || !AllowWatchExpressions)
@@ -1332,14 +1317,14 @@ namespace MonoDevelop.Debugger
{
allowStoreColumnSizes = true;
+ bool closePreviewWindow = true;
+ bool clickProcessed = false;
TreeViewColumn col;
CellRenderer cr;
TreePath path;
- bool closePreviewWindow = true;
- bool clickProcessed = false;
TreeIter it;
- if (this.debuggerService.CanQueryDebugger && evnt.Button == 1 && GetCellAtPos ((int)evnt.X, (int)evnt.Y, out path, out col, out cr) && store.GetIter (out it, path)) {
+ if (debuggerService.CanQueryDebugger && evnt.Button == 1 && GetCellAtPos ((int)evnt.X, (int)evnt.Y, out path, out col, out cr) && store.GetIter (out it, path)) {
if (cr == crpViewer) {
clickProcessed = true;
var node = GetNodeAtIter (it);
@@ -1370,9 +1355,9 @@ namespace MonoDevelop.Debugger
startPreviewCaret.X + 16 > evnt.X) {
clickProcessed = true;
if (compactView) {
- SetPreviewButtonIcon (PreviewButtonIcons.Active, it);
+ SetPreviewButtonIcon (PreviewButtonIcon.Active, it);
} else {
- SetPreviewButtonIcon (PreviewButtonIcons.Selected, it);
+ SetPreviewButtonIcon (PreviewButtonIcon.Selected, it);
}
DebuggingService.ShowPreviewVisualizer (val, this, startPreviewCaret);
closePreviewWindow = false;
@@ -1567,14 +1552,14 @@ namespace MonoDevelop.Debugger
return;
}
- TreePath [] sel = Selection.GetSelectedRows ();
- if (sel.Length == 0) {
+ var selectedRows = Selection.GetSelectedRows ();
+ if (selectedRows.Length == 0) {
cinfo.Enabled = false;
return;
}
- foreach (TreePath tp in sel) {
- if (tp.Depth > 1) {
+ foreach (var row in selectedRows) {
+ if (row.Depth > 1) {
cinfo.Enabled = false;
return;
}
@@ -1886,29 +1871,33 @@ namespace MonoDevelop.Debugger
return columnWidth;
}
- void RecalculateWidth ()
+ void CompactColumns ()
{
+ if (!compactView)
+ return;
+
if (!Model.GetIterFirst (out TreeIter iter))
return;
- foreach (var column in new [] { expCol, valueCol }) {//No need to calculate for Type and PinIcon columns
- // +1 is here because apperently when we calculate MaxWidth and set to FixedWidth
- // later GTK when cacluate needed width for Label it doesn't have enough space
- // and puts "..." to end of text thinking there is not enough space
- // I assume this is because rounding(floating point) calculation errors
- // hence do +1 and avoid such problems.
+ foreach (var column in new [] { expCol, valueCol }) {
+ // No need to calculate for Type and PinIcon columns
+ // +1 is here because apperently when we calculate MaxWidth and set to FixedWidth
+ // later GTK when cacluate needed width for Label it doesn't have enough space
+ // and puts "..." to end of text thinking there is not enough space
+ // I assume this is because rounding(floating point) calculation errors
+ // hence do +1 and avoid such problems.
column.FixedWidth = GetMaxWidth (column, iter) + 1;
}
}
- void SetPreviewButtonIcon (PreviewButtonIcons icon, TreeIter it = default (TreeIter))
+ void SetPreviewButtonIcon (PreviewButtonIcon icon, TreeIter it = default (TreeIter))
{
if (PreviewWindowManager.IsVisible || editing) {
return;
}
if (!it.Equals (TreeIter.Zero)) {
if (!ValidObjectForPreviewIcon (it)) {
- icon = PreviewButtonIcons.None;
+ icon = PreviewButtonIcon.None;
}
}
if (!currentHoverIter.Equals (it)) {
@@ -1920,52 +1909,30 @@ namespace MonoDevelop.Debugger
}
}
if (!it.Equals (TreeIter.Zero) && store.IterIsValid (it)) {
- if (icon == PreviewButtonIcons.Selected) {
- if ((currentIcon == PreviewButtonIcons.Active ||
- currentIcon == PreviewButtonIcons.Hover ||
- currentIcon == PreviewButtonIcons.RowHover) && it.Equals (TreeIter.Zero)) {
+ if (icon == PreviewButtonIcon.Selected) {
+ if ((currentIcon == PreviewButtonIcon.Active ||
+ currentIcon == PreviewButtonIcon.Hover ||
+ currentIcon == PreviewButtonIcon.RowHover) && it.Equals (TreeIter.Zero)) {
iconBeforeSelected = currentIcon;
}
- } else if (icon == PreviewButtonIcons.Active ||
- icon == PreviewButtonIcons.Hover ||
- icon == PreviewButtonIcons.RowHover) {
+ } else if (icon == PreviewButtonIcon.Active ||
+ icon == PreviewButtonIcon.Hover ||
+ icon == PreviewButtonIcon.RowHover) {
iconBeforeSelected = icon;
if (Selection.IterIsSelected (it)) {
- icon = PreviewButtonIcons.Selected;
+ icon = PreviewButtonIcon.Selected;
}
}
- switch (icon) {
- case PreviewButtonIcons.None:
- if (store.GetValue (it, PreviewIconColumn) != null)
- store.SetValue (it, PreviewIconColumn, null);
- break;
- case PreviewButtonIcons.Hidden:
- if ((string) store.GetValue (it, PreviewIconColumn) != "md-empty")
- store.SetValue (it, PreviewIconColumn, "md-empty");
- break;
- case PreviewButtonIcons.RowHover:
- if ((string) store.GetValue (it, PreviewIconColumn) != "md-preview-normal")
- store.SetValue (it, PreviewIconColumn, "md-preview-normal");
- break;
- case PreviewButtonIcons.Hover:
- if ((string) store.GetValue (it, PreviewIconColumn) != "md-preview-hover")
- store.SetValue (it, PreviewIconColumn, "md-preview-hover");
- break;
- case PreviewButtonIcons.Active:
- if ((string) store.GetValue (it, PreviewIconColumn) != "md-preview-active")
- store.SetValue (it, PreviewIconColumn, "md-preview-active");
- break;
- case PreviewButtonIcons.Selected:
- if ((string) store.GetValue (it, PreviewIconColumn) != "md-preview-selected") {
- store.SetValue (it, PreviewIconColumn, "md-preview-selected");
- }
- break;
- }
+ var name = ObjectValueTreeViewController.GetPreviewButtonIcon (icon);
+ var currentName = (string) store.GetValue (it, PreviewIconColumn);
+ if (currentName != name)
+ store.SetValue (it, PreviewIconColumn, name);
+
currentIcon = icon;
currentHoverIter = it;
} else {
- currentIcon = PreviewButtonIcons.None;
+ currentIcon = PreviewButtonIcon.None;
currentHoverIter = TreeIter.Zero;
}
}
@@ -1974,7 +1941,7 @@ namespace MonoDevelop.Debugger
{
if (!currentHoverIter.Equals (TreeIter.Zero) && store.IterIsValid (currentHoverIter)) {
if (Selection.IterIsSelected (currentHoverIter)) {
- SetPreviewButtonIcon (PreviewButtonIcons.Selected, currentHoverIter);
+ SetPreviewButtonIcon (PreviewButtonIcon.Selected, currentHoverIter);
} else {
SetPreviewButtonIcon (iconBeforeSelected, currentHoverIter);
}
@@ -2024,7 +1991,7 @@ namespace MonoDevelop.Debugger
void HandlePreviewWindowClosed (object sender, EventArgs e)
{
- SetPreviewButtonIcon (PreviewButtonIcons.Hidden);
+ SetPreviewButtonIcon (PreviewButtonIcon.Hidden);
}
void HandleCompletionWindowClosed (object sender, EventArgs e)
@@ -2330,6 +2297,5 @@ namespace MonoDevelop.Debugger
return false;
}
#endregion
-
}
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs
index 2bc96db71c..b297f786d7 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/IObjectValueTreeView.cs
@@ -25,6 +25,7 @@
// THE SOFTWARE.
using System;
+using System.Collections.Generic;
namespace MonoDevelop.Debugger
{
@@ -44,11 +45,6 @@ namespace MonoDevelop.Debugger
bool AllowExpanding { get; set; }
/// <summary>
- /// Gets or sets a value indicating whether the user should be able to add watch expressions to the tree
- /// </summary>
- bool AllowWatchExpressions { get; set; }
-
- /// <summary>
/// Gets or sets the pinned watch for the view. When a watch is pinned, the view should display only this value
/// </summary>
PinnedWatch PinnedWatch { get; set; }
@@ -59,9 +55,21 @@ namespace MonoDevelop.Debugger
int PinnedWatchOffset { get; }
/// <summary>
- /// Reloads the tree from the root node
+ /// Notifies the treeview that the tree has been cleared
+ /// </summary>
+ void Cleared ();
+
+ /// <summary>
+ /// Notifies the treeview that the specified node has been appended
+ /// </summary>
+ /// <param name="node">The appended node.</param>
+ void Appended (ObjectValueNode node);
+
+ /// <summary>
+ /// Notifies the treeview that the specified nodes have been appended
/// </summary>
- void Reload (ObjectValueNode root);
+ /// <param name="nodes">The appended nodes.</param>
+ void Appended (IList<ObjectValueNode> nodes);
/// <summary>
/// Informs the view to load the children of the given node. startIndex and count may specify a range of
@@ -74,7 +82,7 @@ namespace MonoDevelop.Debugger
/// the set of replacement nodes. Handles the case where, for example, the "locals" is replaced
/// with the set of local values
/// </summary>
- void LoadEvaluatedNode (ObjectValueNode node, ObjectValueNode [] replacementNodes);
+ void LoadEvaluatedNode (ObjectValueNode node, ObjectValueNode[] replacementNodes);
/// <summary>
/// Triggered when the view tries to expand a node. This may trigger a load of
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/LoadingObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/LoadingObjectValueNode.cs
new file mode 100644
index 0000000000..9cfe3aae44
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/LoadingObjectValueNode.cs
@@ -0,0 +1,38 @@
+//
+// LoadingObjectValueNode.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Debugger
+{
+ sealed class LoadingObjectValueNode : ObjectValueNode
+ {
+ public LoadingObjectValueNode (ObjectValueNode parent) : base (GettextCatalog.GetString ("Loading\u2026"))
+ {
+ Parent = parent;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectCellViewBase.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectCellViewBase.cs
new file mode 100644
index 0000000000..3d5413df6b
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectCellViewBase.cs
@@ -0,0 +1,204 @@
+//
+// MacDebuggerObjectCellViewBase.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using AppKit;
+using Foundation;
+using CoreGraphics;
+
+using Xwt.Drawing;
+
+using MonoDevelop.Ide;
+using MonoDevelop.Components;
+
+namespace MonoDevelop.Debugger
+{
+ abstract class MacDebuggerObjectCellViewBase : NSTableCellView
+ {
+ protected const int CompactImageSize = 12;
+ protected const int RowCellSpacing = 2;
+ protected const int ImageSize = 16;
+ protected const int MarginSize = 2;
+
+ protected MacDebuggerObjectCellViewBase (MacObjectValueTreeView treeView, string identifier)
+ {
+ Identifier = identifier;
+ TreeView = treeView;
+ }
+
+ protected MacDebuggerObjectCellViewBase (IntPtr handle) : base (handle)
+ {
+ }
+
+ protected MacObjectValueTreeView TreeView {
+ get; private set;
+ }
+
+ public override NSObject ObjectValue {
+ get { return base.ObjectValue; }
+ set {
+ var target = ((MacObjectValueNode)value)?.Target;
+
+ if (Node != target) {
+ if (target != null)
+ target.ValueChanged += OnValueChanged;
+
+ if (Node != null)
+ Node.ValueChanged -= OnValueChanged;
+
+ Node = target;
+ }
+
+ base.ObjectValue = value;
+
+ if (Superview != null)
+ UpdateContents ();
+ }
+ }
+
+ public nfloat OptimalWidth {
+ get; protected set;
+ }
+
+ public ObjectValueNode Node {
+ get; private set;
+ }
+
+ public nint Row {
+ get; set;
+ }
+
+ public bool IsShowMoreValues {
+ get { return Node is ShowMoreValuesObjectValueNode; }
+ }
+
+ public bool IsLoading {
+ get { return Node is LoadingObjectValueNode; }
+ }
+
+ protected static NSImage GetImage (string name, Gtk.IconSize size)
+ {
+ var icon = ImageService.GetIcon (name, size);
+
+ try {
+ return icon.ToNSImage ();
+ } catch (Exception ex) {
+ Core.LoggingService.LogError ($"Failed to load '{name}' as an NSImage", ex);
+ return icon.ToBitmap (NSScreen.MainScreen.BackingScaleFactor).ToNSImage ();
+ }
+ }
+
+ protected static NSImage GetImage (string name, Gtk.IconSize size, double alpha)
+ {
+ var icon = ImageService.GetIcon (name, size).WithAlpha (alpha);
+
+ try {
+ return icon.ToNSImage ();
+ } catch (Exception ex) {
+ Core.LoggingService.LogError ($"Failed to load '{name}' as an NSImage", ex);
+ return icon.ToBitmap (NSScreen.MainScreen.BackingScaleFactor).ToNSImage ();
+ }
+ }
+
+ protected static NSImage GetImage (string name, int width, int height)
+ {
+ var icon = ImageService.GetIcon (name).WithSize (width, height);
+
+ try {
+ return icon.ToNSImage ();
+ } catch (Exception ex) {
+ Core.LoggingService.LogError ($"Failed to load '{name}' as an NSImage", ex);
+ return icon.ToBitmap (NSScreen.MainScreen.BackingScaleFactor).ToNSImage ();
+ }
+ }
+
+ protected static CGColor GetCGColor (Color color)
+ {
+ return new CGColor ((nfloat) color.Red, (nfloat) color.Green, (nfloat) color.Blue);
+ }
+
+ protected static NSAttributedString GetAttributedString (string text, bool center = false)
+ {
+ var paragraphStyle = center ? new NSMutableParagraphStyle { Alignment = NSTextAlignment.Center } : null;
+
+ return new NSAttributedString (text ?? string.Empty, baselineOffset: 1, paragraphStyle: paragraphStyle);
+ }
+
+ protected static NSAttributedString GetAttributedPlaceholderString (string text)
+ {
+ return new NSAttributedString (text ?? string.Empty, baselineOffset: 1, strokeColor: NSColor.PlaceholderTextColor);
+ }
+
+ protected void UpdateFont (NSControl control, int sizeDelta = 0)
+ {
+ var font = TreeView.CustomFont ?? TreeView.Font;
+
+ if (sizeDelta != 0) {
+ control.Font = NSFont.FromDescription (font.FontDescriptor, font.PointSize + sizeDelta);
+ } else {
+ control.Font = font;
+ }
+ }
+
+ public override void ViewDidMoveToSuperview ()
+ {
+ base.ViewDidMoveToSuperview ();
+ UpdateContents ();
+ }
+
+ public override NSBackgroundStyle BackgroundStyle {
+ get { return base.BackgroundStyle; }
+ set {
+ base.BackgroundStyle = value;
+ UpdateContents ();
+ }
+ }
+
+ protected abstract void UpdateContents ();
+
+ protected void Refresh ()
+ {
+ UpdateContents ();
+ SetNeedsDisplayInRect (Frame);
+ }
+
+ void OnValueChanged (object sender, EventArgs e)
+ {
+ Refresh ();
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing && Node != null) {
+ Node.ValueChanged -= OnValueChanged;
+ Node = null;
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectNameView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectNameView.cs
new file mode 100644
index 0000000000..65dfc8bd56
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectNameView.cs
@@ -0,0 +1,288 @@
+//
+// MacDebuggerObjectNameView.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using AppKit;
+using Foundation;
+
+using MonoDevelop.Core;
+using MonoDevelop.Ide;
+
+using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// The NSTableViewCell used for the "Name" column.
+ /// </summary>
+ class MacDebuggerObjectNameView : MacDebuggerObjectCellViewBase
+ {
+ class EditableTextField : NSTextField
+ {
+ readonly MacDebuggerObjectNameView nameView;
+ string oldValue, newValue;
+ bool editing;
+
+ public EditableTextField (MacDebuggerObjectNameView nameView)
+ {
+ this.nameView = nameView;
+ }
+
+ public override void DidBeginEditing (NSNotification notification)
+ {
+ base.DidBeginEditing (notification);
+ nameView.TreeView.OnStartEditing ();
+ oldValue = newValue = StringValue.Trim ();
+ editing = true;
+ }
+
+ public override void DidChange (NSNotification notification)
+ {
+ newValue = StringValue.Trim ();
+ base.DidChange (notification);
+ }
+
+ public override void DidEndEditing (NSNotification notification)
+ {
+ base.DidEndEditing (notification);
+
+ if (!editing)
+ return;
+
+ editing = false;
+
+ nameView.TreeView.OnEndEditing ();
+
+ if (nameView.Node is AddNewExpressionObjectValueNode) {
+ if (newValue.Length > 0)
+ nameView.TreeView.OnExpressionAdded (newValue);
+ } else if (newValue != oldValue) {
+ nameView.TreeView.OnExpressionEdited (nameView.Node, newValue);
+ }
+
+ oldValue = newValue = null;
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing)
+ nameView.Dispose ();
+
+ base.Dispose (disposing);
+ }
+ }
+
+ readonly List<NSLayoutConstraint> constraints = new List<NSLayoutConstraint> ();
+ PreviewButtonIcon currentIcon;
+ bool previewIconVisible;
+ bool disposed;
+
+ public MacDebuggerObjectNameView (MacObjectValueTreeView treeView) : base (treeView, "name")
+ {
+ ImageView = new NSImageView {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ TextField = new EditableTextField (this) {
+ AutoresizingMask = NSViewResizingMask.WidthSizable,
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ BackgroundColor = NSColor.Clear,
+ Bordered = false,
+ Editable = false
+ };
+ TextField.Cell.UsesSingleLineMode = true;
+ TextField.Cell.Wraps = false;
+
+ AddSubview (ImageView);
+ AddSubview (TextField);
+
+ PreviewButton = new NSButton {
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ Image = GetImage ("md-empty", Gtk.IconSize.Menu),
+ BezelStyle = NSBezelStyle.Inline,
+ Bordered = false
+ };
+ PreviewButton.Activated += OnPreviewButtonClicked;
+ }
+
+ public MacDebuggerObjectNameView (IntPtr handle) : base (handle)
+ {
+ }
+
+ public NSButton PreviewButton {
+ get; private set;
+ }
+
+ protected override void UpdateContents ()
+ {
+ if (Node == null)
+ return;
+
+ foreach (var constraint in constraints) {
+ constraint.Active = false;
+ constraint.Dispose ();
+ }
+ constraints.Clear ();
+
+ OptimalWidth = MarginSize;
+
+ var iconName = ObjectValueTreeViewController.GetIcon (Node.Flags);
+ ImageView.Image = GetImage (iconName, Gtk.IconSize.Menu);
+ constraints.Add (ImageView.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor));
+ constraints.Add (ImageView.LeadingAnchor.ConstraintEqualToAnchor (LeadingAnchor, MarginSize));
+ constraints.Add (ImageView.WidthAnchor.ConstraintEqualToConstant (ImageSize));
+ constraints.Add (ImageView.HeightAnchor.ConstraintEqualToConstant (ImageSize));
+
+ OptimalWidth += ImageView.Image.Size.Width;
+ OptimalWidth += RowCellSpacing;
+
+ var editable = TreeView.AllowWatchExpressions && Node.Parent is RootObjectValueNode;
+ var textColor = NSColor.ControlText;
+ var placeholder = string.Empty;
+ var name = Node.Name;
+
+ if (Node.IsUnknown) {
+ if (TreeView.DebuggerService.Frame != null)
+ textColor = NSColor.FromCGColor (GetCGColor (Styles.ObjectValueTreeValueDisabledText));
+ } else if (Node.IsError || Node.IsNotSupported) {
+ } else if (Node.IsImplicitNotSupported) {
+ } else if (Node.IsEvaluating) {
+ if (Node.GetIsEvaluatingGroup ())
+ textColor = NSColor.FromCGColor (GetCGColor (Styles.ObjectValueTreeValueDisabledText));
+ } else if (Node.IsEnumerable) {
+ } else if (Node is AddNewExpressionObjectValueNode) {
+ placeholder = GettextCatalog.GetString ("Add new expression");
+ name = string.Empty;
+ editable = true;
+ } else if (TreeView.Controller.GetNodeHasChangedSinceLastCheckpoint (Node)) {
+ textColor = NSColor.FromCGColor (GetCGColor (Styles.ObjectValueTreeValueModifiedText));
+ }
+
+ TextField.PlaceholderAttributedString = GetAttributedPlaceholderString (placeholder);
+ TextField.AttributedStringValue = GetAttributedString (name);
+ TextField.TextColor = textColor;
+ TextField.Editable = editable;
+ UpdateFont (TextField);
+ TextField.SizeToFit ();
+
+ OptimalWidth += TextField.Frame.Width;
+
+ constraints.Add (TextField.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor));
+ constraints.Add (TextField.LeadingAnchor.ConstraintEqualToAnchor (ImageView.TrailingAnchor, RowCellSpacing));
+
+ if (MacObjectValueTreeView.ValidObjectForPreviewIcon (Node)) {
+ SetPreviewButtonIcon (PreviewButtonIcon.Hidden);
+
+ if (!previewIconVisible) {
+ AddSubview (PreviewButton);
+ previewIconVisible = true;
+ }
+
+ constraints.Add (TextField.WidthAnchor.ConstraintGreaterThanOrEqualToConstant (TextField.Frame.Width));
+ constraints.Add (PreviewButton.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor));
+ constraints.Add (PreviewButton.LeadingAnchor.ConstraintEqualToAnchor (TextField.TrailingAnchor, RowCellSpacing));
+ constraints.Add (PreviewButton.WidthAnchor.ConstraintEqualToConstant (ImageSize));
+ constraints.Add (PreviewButton.HeightAnchor.ConstraintEqualToConstant (ImageSize));
+
+ OptimalWidth += RowCellSpacing;
+ OptimalWidth += PreviewButton.Frame.Width;
+ } else {
+ if (previewIconVisible) {
+ PreviewButton.RemoveFromSuperview ();
+ previewIconVisible = false;
+ }
+
+ constraints.Add (TextField.TrailingAnchor.ConstraintEqualToAnchor (TrailingAnchor, -MarginSize));
+ }
+
+ foreach (var constraint in constraints)
+ constraint.Active = true;
+
+ OptimalWidth += MarginSize;
+ }
+
+ public void SetPreviewButtonIcon (PreviewButtonIcon icon)
+ {
+ if (!previewIconVisible || icon == currentIcon)
+ return;
+
+ var name = ObjectValueTreeViewController.GetPreviewButtonIcon (icon);
+ PreviewButton.Image = GetImage (name, Gtk.IconSize.Menu);
+ currentIcon = icon;
+
+ SetNeedsDisplayInRect (PreviewButton.Frame);
+ }
+
+ void OnPreviewButtonClicked (object sender, EventArgs e)
+ {
+ if (!TreeView.DebuggerService.CanQueryDebugger || PreviewWindowManager.IsVisible)
+ return;
+
+ if (!MacObjectValueTreeView.ValidObjectForPreviewIcon (Node))
+ return;
+
+ // convert the buttons frame to window coords
+ var buttonLocation = PreviewButton.ConvertPointToView (CoreGraphics.CGPoint.Empty, null);
+
+ // now convert the frame to absolute screen coordinates
+ buttonLocation = PreviewButton.Window.ConvertPointToScreen (buttonLocation);
+
+ var nativeRoot = MacInterop.GtkQuartz.GetWindow (IdeApp.Workbench.RootWindow);
+
+ // convert to root window coordinates
+ buttonLocation = nativeRoot.ConvertPointFromScreen (buttonLocation);
+ // the Cocoa Y axis is flipped, convert to Gtk
+ buttonLocation.Y = nativeRoot.Frame.Height - buttonLocation.Y;
+ // Gtk coords don't include the toolbar and decorations ofsset, so substract it
+ buttonLocation.Y -= nativeRoot.Frame.Height - nativeRoot.ContentView.Frame.Height;
+
+ int width = (int) PreviewButton.Frame.Width;
+ int height = (int) PreviewButton.Frame.Height;
+
+ var buttonArea = new Gdk.Rectangle ((int) buttonLocation.X, (int) buttonLocation.Y, width, height);
+ var val = Node.GetDebuggerObjectValue ();
+
+ SetPreviewButtonIcon (PreviewButtonIcon.Active);
+
+ DebuggingService.ShowPreviewVisualizer (val, IdeApp.Workbench.RootWindow, buttonArea);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing && !disposed) {
+ PreviewButton.Activated -= OnPreviewButtonClicked;
+ foreach (var constraint in constraints)
+ constraint.Dispose ();
+ constraints.Clear ();
+ disposed = true;
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectPinView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectPinView.cs
new file mode 100644
index 0000000000..45b31cc810
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectPinView.cs
@@ -0,0 +1,155 @@
+//
+// MacDebuggerObjectPinView.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using AppKit;
+
+using MonoDevelop.Core;
+using MonoDevelop.Ide;
+
+namespace MonoDevelop.Debugger
+{
+ class MacDebuggerObjectPinView : MacDebuggerObjectCellViewBase
+ {
+ static readonly NSImage unpinnedImage = GetImage ("md-pin-up", Gtk.IconSize.Menu);
+ static readonly NSImage pinnedImage = GetImage ("md-pin-down", Gtk.IconSize.Menu);
+ static readonly NSImage liveUpdateOnImage = GetImage ("md-live", Gtk.IconSize.Menu);
+ static readonly NSImage liveUpdateOffImage = GetImage ("md-live", Gtk.IconSize.Menu, 0.5);
+ static readonly NSImage none = GetImage ("md-empty", Gtk.IconSize.Menu);
+ public const int MinWidth = MarginSize + 16 + MarginSize;
+ public const int MaxWidth = MarginSize + 16 + RowCellSpacing + 16 + MarginSize;
+ bool disposed;
+ bool pinned;
+
+ public MacDebuggerObjectPinView (MacObjectValueTreeView treeView) : base (treeView, "pin")
+ {
+ PinButton = new NSButton {
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ BezelStyle = NSBezelStyle.Inline,
+ Image = none,
+ Bordered = false,
+ };
+ PinButton.AccessibilityTitle = GettextCatalog.GetString ("Pin to the editor");
+ PinButton.Activated += OnPinButtonClicked;
+ AddSubview (PinButton);
+
+ LiveUpdateButton = new NSButton {
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ BezelStyle = NSBezelStyle.Inline,
+ Image = liveUpdateOffImage,
+ Bordered = false
+ };
+ LiveUpdateButton.AccessibilityTitle = GettextCatalog.GetString ("Refresh value");
+ LiveUpdateButton.Activated += OnLiveUpdateButtonClicked;
+ AddSubview (LiveUpdateButton);
+
+ PinButton.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor).Active = true;
+ PinButton.LeadingAnchor.ConstraintEqualToAnchor (LeadingAnchor, MarginSize).Active = true;
+ PinButton.WidthAnchor.ConstraintEqualToConstant (ImageSize).Active = true;
+ PinButton.HeightAnchor.ConstraintEqualToConstant (ImageSize).Active = true;
+
+ LiveUpdateButton.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor).Active = true;
+ LiveUpdateButton.LeadingAnchor.ConstraintEqualToAnchor (PinButton.TrailingAnchor, RowCellSpacing).Active = true;
+ LiveUpdateButton.TrailingAnchor.ConstraintEqualToAnchor (TrailingAnchor, -MarginSize).Active = true;
+ LiveUpdateButton.WidthAnchor.ConstraintEqualToConstant (ImageSize).Active = true;
+ LiveUpdateButton.HeightAnchor.ConstraintEqualToConstant (ImageSize).Active = true;
+ }
+
+ public MacDebuggerObjectPinView (IntPtr handle) : base (handle)
+ {
+ OptimalWidth = MaxWidth;
+ }
+
+ public NSButton PinButton {
+ get; private set;
+ }
+
+ public NSButton LiveUpdateButton {
+ get; private set;
+ }
+
+ protected override void UpdateContents ()
+ {
+ if (Node == null)
+ return;
+
+ if (TreeView.PinnedWatch != null && Node.Parent == TreeView.Controller.Root) {
+ PinButton.Image = pinnedImage;
+ pinned = true;
+ } else {
+ PinButton.Image = none;
+ pinned = false;
+ }
+
+ if (pinned) {
+ if (TreeView.PinnedWatch.LiveUpdate)
+ LiveUpdateButton.Image = liveUpdateOnImage;
+ else
+ LiveUpdateButton.Image = liveUpdateOffImage;
+ } else {
+ LiveUpdateButton.Image = none;
+ }
+ }
+
+ void OnPinButtonClicked (object sender, EventArgs e)
+ {
+ if (pinned) {
+ TreeView.Unpin (Node);
+ } else {
+ TreeView.Pin (Node);
+ }
+ }
+
+ void OnLiveUpdateButtonClicked (object sender, EventArgs e)
+ {
+ if (pinned) {
+ DebuggingService.SetLiveUpdateMode (TreeView.PinnedWatch, !TreeView.PinnedWatch.LiveUpdate);
+ Refresh ();
+ }
+ }
+
+ public void SetMouseHover (bool hover)
+ {
+ if (pinned)
+ return;
+
+ PinButton.Image = hover || IdeServices.DesktopService.AccessibilityInUse ? unpinnedImage : none;
+ SetNeedsDisplayInRect (PinButton.Frame);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing && !disposed) {
+ LiveUpdateButton.Activated -= OnLiveUpdateButtonClicked;
+ PinButton.Activated -= OnPinButtonClicked;
+ disposed = true;
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectTypeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectTypeView.cs
new file mode 100644
index 0000000000..e21e3ca77f
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectTypeView.cs
@@ -0,0 +1,70 @@
+//
+// MacDebuggerObjectTypeView.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using AppKit;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// The NSTableViewCell used for the "Type" column.
+ /// </summary>
+ class MacDebuggerObjectTypeView : MacDebuggerObjectCellViewBase
+ {
+ public MacDebuggerObjectTypeView (MacObjectValueTreeView treeView) : base (treeView, "type")
+ {
+ TextField = new NSTextField {
+ AutoresizingMask = NSViewResizingMask.WidthSizable,
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ BackgroundColor = NSColor.Clear,
+ Bordered = false,
+ Editable = false
+ };
+ TextField.Cell.UsesSingleLineMode = true;
+ TextField.Cell.Wraps = false;
+
+ AddSubview (TextField);
+
+ TextField.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor).Active = true;
+ TextField.LeadingAnchor.ConstraintEqualToAnchor (LeadingAnchor, MarginSize).Active = true;
+ TextField.TrailingAnchor.ConstraintEqualToAnchor (TrailingAnchor, -MarginSize).Active = true;
+ }
+
+ public MacDebuggerObjectTypeView (IntPtr handle) : base (handle)
+ {
+ }
+
+ protected override void UpdateContents ()
+ {
+ TextField.AttributedStringValue = GetAttributedString (Node?.TypeName);
+ UpdateFont (TextField);
+ TextField.SizeToFit ();
+
+ OptimalWidth = MarginSize + TextField.Frame.Width + MarginSize;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectValueView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectValueView.cs
new file mode 100644
index 0000000000..07e0ed062a
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacDebuggerObjectValueView.cs
@@ -0,0 +1,381 @@
+//
+// MacDebuggerObjectValueView.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using AppKit;
+using Foundation;
+using CoreGraphics;
+
+using Xwt.Drawing;
+
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// The NSTableViewCell used for the "Value" column.
+ /// </summary>
+ class MacDebuggerObjectValueView : MacDebuggerObjectCellViewBase
+ {
+ class EditableTextField : NSTextField
+ {
+ readonly MacDebuggerObjectValueView valueView;
+ string oldValue, newValue;
+ bool editing;
+
+ public EditableTextField (MacDebuggerObjectValueView valueView)
+ {
+ this.valueView = valueView;
+ }
+
+ public override void DidBeginEditing (NSNotification notification)
+ {
+ base.DidBeginEditing (notification);
+ valueView.TreeView.OnStartEditing ();
+ oldValue = newValue = StringValue.Trim ();
+ editing = true;
+ }
+
+ public override void DidChange (NSNotification notification)
+ {
+ newValue = StringValue.Trim ();
+ base.DidChange (notification);
+ }
+
+ public override void DidEndEditing (NSNotification notification)
+ {
+ base.DidEndEditing (notification);
+
+ if (!editing)
+ return;
+
+ editing = false;
+
+ valueView.TreeView.OnEndEditing ();
+
+ if (newValue != oldValue && valueView.TreeView.GetEditValue (valueView.Node, newValue))
+ valueView.Refresh ();
+
+ oldValue = newValue = null;
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing)
+ valueView.Dispose ();
+
+ base.Dispose (disposing);
+ }
+ }
+
+ readonly List<NSLayoutConstraint> constraints = new List<NSLayoutConstraint> ();
+ NSImageView statusIcon;
+ bool statusIconVisible;
+ NSView colorPreview;
+ bool colorPreviewVisible;
+ NSButton valueButton;
+ bool valueButtonVisible;
+ NSButton viewerButton;
+ bool viewerButtonVisible;
+ bool disposed;
+
+ public MacDebuggerObjectValueView (MacObjectValueTreeView treeView) : base (treeView, "value")
+ {
+ statusIcon = new NSImageView {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ colorPreview = new NSView (new CGRect (0, 0, 16, 16)) {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ valueButton = new NSButton {
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ Title = GettextCatalog.GetString ("Show More"),
+ BezelStyle = NSBezelStyle.Inline
+ };
+ valueButton.Cell.UsesSingleLineMode = true;
+ UpdateFont (valueButton, -3);
+ valueButton.Activated += OnValueButtonActivated;
+
+ int imageSize = treeView.CompactView ? CompactImageSize : ImageSize;
+ viewerButton = new NSButton {
+ Image = GetImage (Gtk.Stock.Edit, imageSize, imageSize),
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+ viewerButton.BezelStyle = NSBezelStyle.Inline;
+ viewerButton.Bordered = false;
+ viewerButton.Activated += OnViewerButtonActivated;
+
+ TextField = new EditableTextField (this) {
+ AutoresizingMask = NSViewResizingMask.WidthSizable,
+ TranslatesAutoresizingMaskIntoConstraints = false,
+ BackgroundColor = NSColor.Clear,
+ Bordered = false,
+ Editable = false
+ };
+ TextField.Cell.UsesSingleLineMode = true;
+ TextField.Cell.Wraps = false;
+
+ AddSubview (TextField);
+ }
+
+ public MacDebuggerObjectValueView (IntPtr handle) : base (handle)
+ {
+ }
+
+ protected override void UpdateContents ()
+ {
+ if (Node == null)
+ return;
+
+ foreach (var constraint in constraints) {
+ constraint.Active = false;
+ constraint.Dispose ();
+ }
+ constraints.Clear ();
+
+ var editable = TreeView.GetCanEditNode (Node);
+ var textColor = NSColor.ControlText;
+ string evaluateStatusIcon = null;
+ string valueButtonText = null;
+ var showViewerButton = false;
+ Color? previewColor = null;
+ string strval;
+
+ if (Node.IsUnknown) {
+ if (TreeView.DebuggerService.Frame != null) {
+ strval = GettextCatalog.GetString ("The name '{0}' does not exist in the current context.", Node.Name);
+ } else {
+ strval = string.Empty;
+ }
+ evaluateStatusIcon = Ide.Gui.Stock.Warning;
+ } else if (Node.IsError || Node.IsNotSupported) {
+ evaluateStatusIcon = Ide.Gui.Stock.Warning;
+ strval = Node.Value;
+ int i = strval.IndexOf ('\n');
+ if (i != -1)
+ strval = strval.Substring (0, i);
+ textColor = NSColor.FromCGColor (GetCGColor (Styles.ObjectValueTreeValueErrorText));
+ } else if (Node.IsImplicitNotSupported) {
+ strval = "";//val.Value; with new "Show Value" button we don't want to display message "Implicit evaluation is disabled"
+ textColor = NSColor.FromCGColor (GetCGColor (Styles.ObjectValueTreeValueDisabledText));
+ if (Node.CanRefresh)
+ valueButtonText = GettextCatalog.GetString ("Show Value");
+ } else if (Node.IsEvaluating) {
+ strval = GettextCatalog.GetString ("Evaluating\u2026");
+
+ evaluateStatusIcon = "md-spinner-16";
+
+ textColor = NSColor.FromCGColor (GetCGColor (Styles.ObjectValueTreeValueDisabledText));
+ } else if (Node.IsEnumerable) {
+ if (Node is ShowMoreValuesObjectValueNode) {
+ valueButtonText = GettextCatalog.GetString ("Show More");
+ } else {
+ valueButtonText = GettextCatalog.GetString ("Show Values");
+ }
+ strval = "";
+ } else if (Node is AddNewExpressionObjectValueNode) {
+ strval = string.Empty;
+ editable = false;
+ } else {
+ strval = TreeView.Controller.GetDisplayValueWithVisualisers (Node, out showViewerButton);
+
+ if (TreeView.Controller.GetNodeHasChangedSinceLastCheckpoint (Node))
+ textColor = NSColor.FromCGColor (GetCGColor (Styles.ObjectValueTreeValueModifiedText));
+
+ var val = Node.GetDebuggerObjectValue ();
+ if (val != null && !val.IsNull && DebuggingService.HasGetConverter<Color> (val)) {
+ try {
+ previewColor = DebuggingService.GetGetConverter<Color> (val).GetValue (val);
+ } catch {
+ previewColor = null;
+ }
+ }
+ }
+
+ strval = strval.Replace ("\r\n", " ").Replace ("\n", " ");
+
+ var views = new List<NSView> ();
+
+ OptimalWidth = MarginSize;
+
+ // First item: Status Icon
+ if (evaluateStatusIcon != null) {
+ statusIcon.Image = GetImage (evaluateStatusIcon, Gtk.IconSize.Menu);
+
+ if (!statusIconVisible) {
+ AddSubview (statusIcon);
+ statusIconVisible = true;
+ }
+
+ constraints.Add (statusIcon.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor));
+ constraints.Add (statusIcon.WidthAnchor.ConstraintEqualToConstant (ImageSize));
+ constraints.Add (statusIcon.HeightAnchor.ConstraintEqualToConstant (ImageSize));
+ views.Add (statusIcon);
+
+ OptimalWidth += statusIcon.Image.Size.Width;
+ OptimalWidth += RowCellSpacing;
+ } else if (statusIconVisible) {
+ statusIcon.RemoveFromSuperview ();
+ statusIconVisible = false;
+ }
+
+ // Second Item: Color Preview
+ if (previewColor != null) {
+ colorPreview.Layer.BackgroundColor = GetCGColor (previewColor.Value);
+
+ if (!colorPreviewVisible) {
+ AddSubview (colorPreview);
+ colorPreviewVisible = true;
+ }
+
+ constraints.Add (colorPreview.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor));
+ constraints.Add (colorPreview.WidthAnchor.ConstraintEqualToConstant (ImageSize));
+ constraints.Add (colorPreview.HeightAnchor.ConstraintEqualToConstant (ImageSize));
+ views.Add (colorPreview);
+
+ OptimalWidth += colorPreview.Frame.Width;
+ OptimalWidth += RowCellSpacing;
+ } else if (colorPreviewVisible) {
+ colorPreview.RemoveFromSuperview ();
+ colorPreviewVisible = false;
+ }
+
+ // Third Item: Value Button
+ if (valueButtonText != null && !((MacObjectValueNode) ObjectValue).HideValueButton) {
+ valueButton.AttributedTitle = GetAttributedString (valueButtonText, true);
+ UpdateFont (valueButton, -3);
+ valueButton.SizeToFit ();
+
+ if (!valueButtonVisible) {
+ AddSubview (valueButton);
+ valueButtonVisible = true;
+ }
+
+ constraints.Add (valueButton.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor));
+ views.Add (valueButton);
+
+ OptimalWidth += valueButton.Frame.Width;
+ OptimalWidth += RowCellSpacing;
+ } else if (valueButtonVisible) {
+ valueButton.RemoveFromSuperview ();
+ valueButtonVisible = false;
+ }
+
+ // Fourth Item: Viewer Button
+ if (showViewerButton) {
+ if (!viewerButtonVisible) {
+ AddSubview (viewerButton);
+ viewerButtonVisible = true;
+ }
+
+ constraints.Add (viewerButton.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor));
+ constraints.Add (viewerButton.WidthAnchor.ConstraintEqualToConstant (viewerButton.Image.Size.Width));
+ constraints.Add (viewerButton.HeightAnchor.ConstraintEqualToConstant (viewerButton.Image.Size.Height));
+ views.Add (viewerButton);
+
+ OptimalWidth += viewerButton.Frame.Width;
+ OptimalWidth += RowCellSpacing;
+ } else if (viewerButtonVisible) {
+ viewerButton.RemoveFromSuperview ();
+ viewerButtonVisible = false;
+ }
+
+ // Fifth Item: Text Value
+ TextField.AttributedStringValue = GetAttributedString (strval);
+ TextField.TextColor = textColor;
+ TextField.Editable = editable;
+ UpdateFont (TextField);
+
+ constraints.Add (TextField.CenterYAnchor.ConstraintEqualToAnchor (CenterYAnchor));
+ views.Add (TextField);
+
+ // lay out our views...
+ var leadingAnchor = LeadingAnchor;
+
+ for (int i = 0; i < views.Count; i++) {
+ var view = views[i];
+
+ constraints.Add (view.LeadingAnchor.ConstraintEqualToAnchor (leadingAnchor, i == 0 ? MarginSize : RowCellSpacing));
+ leadingAnchor = view.TrailingAnchor;
+ }
+
+ constraints.Add (TextField.TrailingAnchor.ConstraintEqualToAnchor (TrailingAnchor, -MarginSize));
+
+ foreach (var constraint in constraints)
+ constraint.Active = true;
+
+ TextField.SizeToFit ();
+
+ OptimalWidth += TextField.Frame.Width;
+ OptimalWidth += MarginSize;
+ }
+
+ void OnValueButtonActivated (object sender, EventArgs e)
+ {
+ if (Node.IsEnumerable) {
+ if (Node is ShowMoreValuesObjectValueNode moreNode) {
+ TreeView.LoadMoreChildren (moreNode.EnumerableNode);
+ } else {
+ // use ExpandItem to expand so we see the loading message, expanding the node will trigger a fetch of the children
+ TreeView.ExpandItem (ObjectValue, false);
+ }
+ } else {
+ // this is likely to support IsImplicitNotSupported
+ TreeView.Refresh (Node);
+ }
+
+ ((MacObjectValueNode) ObjectValue).HideValueButton = true;
+ Refresh ();
+ }
+
+ void OnViewerButtonActivated (object sender, EventArgs e)
+ {
+ if (!TreeView.DebuggerService.CanQueryDebugger)
+ return;
+
+ if (TreeView.ShowVisualizer (Node))
+ Refresh ();
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing && !disposed) {
+ viewerButton.Activated -= OnViewerButtonActivated;
+ valueButton.Activated -= OnValueButtonActivated;
+ foreach (var constraint in constraints)
+ constraint.Dispose ();
+ constraints.Clear ();
+ disposed = true;
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueNode.cs
new file mode 100644
index 0000000000..12359aee74
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueNode.cs
@@ -0,0 +1,50 @@
+//
+// MacObjectValueNode.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using Foundation;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// NSObject wrapper for data items in the Cocoa implementation of the ObjectValueTreeView.
+ /// </summary>
+ class MacObjectValueNode : NSObject
+ {
+ public readonly List<MacObjectValueNode> Children = new List<MacObjectValueNode> ();
+ public readonly MacObjectValueNode Parent;
+ public readonly ObjectValueNode Target;
+ public bool HideValueButton;
+
+ public MacObjectValueNode (MacObjectValueNode parent, ObjectValueNode target)
+ {
+ Parent = parent;
+ Target = target;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeView.cs
new file mode 100644
index 0000000000..79f6f2741b
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeView.cs
@@ -0,0 +1,1030 @@
+//
+// MacObjectValueTreeView.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Text;
+using System.Collections.Generic;
+
+using AppKit;
+using Foundation;
+using CoreGraphics;
+
+using MonoDevelop.Core;
+using MonoDevelop.Ide.Commands;
+using MonoDevelop.Components.Commands;
+
+namespace MonoDevelop.Debugger
+{
+ public class MacObjectValueTreeView : NSOutlineView, IObjectValueTreeView
+ {
+ const int MinimumNameColumnWidth = 48;
+ const int MinimumValueColumnWidth = 64;
+ const int MinimumTypeColumnWidth = 48;
+
+ MacObjectValueTreeViewDelegate treeViewDelegate;
+ MacObjectValueTreeViewDataSource dataSource;
+
+ readonly NSTableColumn nameColumn;
+ readonly NSTableColumn valueColumn;
+ readonly NSTableColumn typeColumn;
+ readonly NSTableColumn pinColumn;
+ readonly bool allowPopupMenu;
+ readonly bool rootPinVisible;
+ readonly bool compactView;
+
+ PinnedWatch pinnedWatch;
+
+ PreviewButtonIcon currentHoverIcon;
+ nint currentHoverRow = -1;
+
+ double nameColumnWidth = 0.3;
+ double valueColumnWidth = 0.5;
+ double typeColumnWidth = 0.2;
+
+ bool allowEditing;
+ bool autoresizing;
+ bool disposed;
+
+ public MacObjectValueTreeView (
+ IObjectValueDebuggerService debuggerService,
+ ObjectValueTreeViewController controller,
+ bool allowEditing,
+ bool headersVisible,
+ bool compactView,
+ bool allowPinning,
+ bool allowPopupMenu,
+ bool rootPinVisible)
+ {
+ DebuggerService = debuggerService;
+ Controller = controller;
+
+ this.rootPinVisible = rootPinVisible;
+ this.allowPopupMenu = allowPopupMenu;
+ this.allowEditing = allowEditing;
+ this.compactView = compactView;
+
+ DataSource = dataSource = new MacObjectValueTreeViewDataSource (this, controller.Root, controller.AllowWatchExpressions);
+ Delegate = treeViewDelegate = new MacObjectValueTreeViewDelegate (this);
+ ColumnAutoresizingStyle = NSTableViewColumnAutoresizingStyle.None;
+ treeViewDelegate.SelectionChanged += OnSelectionChanged;
+ UsesAlternatingRowBackgroundColors = true;
+ FocusRingType = NSFocusRingType.None;
+ AutoresizesOutlineColumn = false;
+ AllowsColumnResizing = !compactView;
+
+ var resizingMask = compactView ? NSTableColumnResizing.None : NSTableColumnResizing.UserResizingMask;
+
+ nameColumn = new NSTableColumn ("name") { Editable = controller.AllowWatchExpressions, MinWidth = MinimumNameColumnWidth, ResizingMask = resizingMask };
+ nameColumn.Title = GettextCatalog.GetString ("Name");
+ AddColumn (nameColumn);
+
+ OutlineTableColumn = nameColumn;
+
+ valueColumn = new NSTableColumn ("value") { Editable = controller.AllowEditing, MinWidth = MinimumValueColumnWidth, ResizingMask = resizingMask };
+ valueColumn.Title = GettextCatalog.GetString ("Value");
+ if (compactView)
+ valueColumn.MaxWidth = 800;
+ AddColumn (valueColumn);
+
+ if (!compactView) {
+ typeColumn = new NSTableColumn ("type") { Editable = false, MinWidth = MinimumTypeColumnWidth, ResizingMask = resizingMask };
+ typeColumn.Title = GettextCatalog.GetString ("Type");
+ AddColumn (typeColumn);
+ }
+
+ if (allowPinning) {
+ pinColumn = new NSTableColumn ("pin") { Editable = false, ResizingMask = NSTableColumnResizing.None };
+ pinColumn.MinWidth = pinColumn.MaxWidth = pinColumn.Width = MacDebuggerObjectPinView.MinWidth;
+ AddColumn (pinColumn);
+ }
+
+ if (headersVisible) {
+ HeaderView.AlphaValue = 1.0f;
+ } else {
+ HeaderView = null;
+ }
+
+ PreviewWindowManager.WindowClosed += OnPreviewWindowClosed;
+
+ // disable implicit animations
+ WantsLayer = true;
+ Layer.Actions = new NSDictionary (
+ "actions", NSNull.Null,
+ "contents", NSNull.Null,
+ "hidden", NSNull.Null,
+ "onLayout", NSNull.Null,
+ "onOrderIn", NSNull.Null,
+ "onOrderOut", NSNull.Null,
+ "position", NSNull.Null,
+ "sublayers", NSNull.Null,
+ "transform", NSNull.Null,
+ "bounds", NSNull.Null);
+ }
+
+ public ObjectValueTreeViewController Controller {
+ get; private set;
+ }
+
+ public bool CompactView {
+ get { return compactView; }
+ }
+
+ internal NSFont CustomFont {
+ get; private set;
+ }
+
+ public IObjectValueDebuggerService DebuggerService {
+ get; private set;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the user should be able to edit values in the tree
+ /// </summary>
+ public bool AllowEditing {
+ get => allowEditing;
+ set {
+ if (allowEditing != value) {
+ allowEditing = value;
+ ReloadData ();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether or not the user should be able to expand nodes in the tree.
+ /// </summary>
+ public bool AllowExpanding { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether the user should be able to add watch expressions to the tree
+ /// </summary>
+ public bool AllowWatchExpressions {
+ get { return dataSource.AllowWatchExpressions; }
+ }
+
+ /// <summary>
+ /// Gets or sets the pinned watch for the view. When a watch is pinned, the view should display only this value
+ /// </summary>
+ public PinnedWatch PinnedWatch {
+ get => pinnedWatch;
+ set {
+ if (pinnedWatch != value) {
+ pinnedWatch = value;
+ Runtime.RunInMainThread (() => {
+ if (value == null) {
+ pinColumn.MinWidth = pinColumn.MaxWidth = pinColumn.Width = MacDebuggerObjectPinView.MinWidth;
+ } else {
+ pinColumn.MinWidth = pinColumn.MaxWidth = pinColumn.Width = MacDebuggerObjectPinView.MaxWidth;
+ }
+ }).Ignore ();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating the offset required for pinned watches
+ /// </summary>
+ public int PinnedWatchOffset {
+ get {
+ return (int) Frame.Height;
+ }
+ }
+
+ double GetVisibleTableWidth ()
+ {
+ var scrollView = EnclosingScrollView;
+
+ if (scrollView != null)
+ return scrollView.DocumentVisibleRect.Width;
+
+ return VisibleRect ().Width;
+ }
+
+ internal void OnColumnResized ()
+ {
+ if (autoresizing || compactView)
+ return;
+
+ var width = GetVisibleTableWidth ();
+
+ nameColumnWidth = nameColumn.Width / width;
+ valueColumnWidth = valueColumn.Width / width;
+ typeColumnWidth = typeColumn.Width / width;
+ }
+
+ // Note: this resizing method is the one used by the Locals pad and the Watches pad
+ void ScaleColumnSizesToFit ()
+ {
+ if (compactView || Superview == null || RowCount == 0)
+ return;
+
+ var totalWidth = GetVisibleTableWidth ();
+ int columnWidth;
+
+ try {
+ autoresizing = true;
+
+ columnWidth = Math.Max ((int) (totalWidth * valueColumnWidth), MinimumNameColumnWidth);
+ valueColumn.Width = columnWidth;
+
+ columnWidth = Math.Max ((int) (totalWidth * nameColumnWidth), MinimumValueColumnWidth);
+ nameColumn.Width = columnWidth;
+
+ columnWidth = Math.Max ((int) (totalWidth * typeColumnWidth), MinimumTypeColumnWidth);
+ typeColumn.Width = columnWidth;
+ } finally {
+ autoresizing = false;
+ }
+ }
+
+ // Note: this resizing method is the one used by debugger tooltips and pinned watches in the editor
+ void OptimizeColumnSizes (bool emitResized = true)
+ {
+ if (!compactView || Superview == null || RowCount == 0)
+ return;
+
+ nfloat nameWidth = MinimumNameColumnWidth;
+ nfloat valueWidth = MinimumValueColumnWidth;
+
+ for (nint row = 0; row < RowCount; row++) {
+ var rowView = GetRowView (row, true);
+
+ if (rowView == null)
+ continue;
+
+ var nameView = (MacDebuggerObjectNameView) rowView.ViewAtColumn (0);
+
+ if (nameView != null) {
+ // Note: the Name column's X-offset is the width of the expander which we need to take that into account
+ // when calculating the Name column's width.
+ var width = nameView.Frame.X + nameView.OptimalWidth;
+
+ if (width > nameWidth)
+ nameWidth = NMath.Min (width, nameColumn.MaxWidth);
+ }
+
+ var valueView = (MacDebuggerObjectValueView) rowView.ViewAtColumn (1);
+
+ if (valueView != null) {
+ var width = valueView.OptimalWidth;
+
+ if (width > valueWidth)
+ valueWidth = NMath.Min (width, valueColumn.MaxWidth);
+ }
+ }
+
+ try {
+ autoresizing = true;
+
+ bool changed = false;
+ if (nameColumn.Width != nameWidth) {
+ nameColumn.MinWidth = nameColumn.Width = nameWidth;
+ changed = true;
+ }
+ if (valueColumn.Width != valueWidth) {
+ valueColumn.MinWidth = valueColumn.Width = valueWidth;
+ changed = true;
+ }
+
+ if (changed) {
+ SizeToFit ();
+
+ if (emitResized)
+ OnResized ();
+ }
+
+ ReloadData ();
+ SetNeedsDisplayInRect (Frame);
+ } finally {
+ autoresizing = false;
+ }
+ }
+
+ void UpdateColumnSizes ()
+ {
+ if (compactView)
+ OptimizeColumnSizes ();
+ else
+ ScaleColumnSizesToFit ();
+ }
+
+ static NSFont GetNSFontFromPangoFontDescription (Pango.FontDescription fontDescription)
+ {
+ if (fontDescription == null)
+ return null;
+
+ return NSFontManager.SharedFontManager.FontWithFamilyWorkaround (
+ fontDescription.Family,
+ fontDescription.Style == Pango.Style.Italic || fontDescription.Style == Pango.Style.Oblique
+ ? NSFontTraitMask.Italic
+ : 0,
+ NormalizeWeight (fontDescription.Weight),
+ fontDescription.Size / (nfloat) Pango.Scale.PangoScale);
+
+ /// <summary>
+ /// Normalizes a Pango font weight (100-1000 scale) to a weight
+ /// suitable for NSFontDescription.FontWithFamily (0-15 scale).
+ /// </summary>
+ int NormalizeWeight (Pango.Weight pangoWeight)
+ {
+ double Normalize (double value, double inMin, double inMax, double outMin, double outMax)
+ => (outMax - outMin) / (inMax - inMin) * (value - inMax) + outMax;
+
+ return (int) Math.Round (Normalize ((int) pangoWeight, 100, 1000, 0, 15));
+ }
+ }
+
+ internal void SetCustomFont (Pango.FontDescription fontDescription)
+ {
+ if (fontDescription != null) {
+ CustomFont = GetNSFontFromPangoFontDescription (fontDescription);
+ } else {
+ CustomFont = null;
+ }
+
+ ReloadData ();
+ }
+
+ internal void QueueResize ()
+ {
+ }
+
+ public override void ViewDidMoveToSuperview ()
+ {
+ base.ViewDidMoveToSuperview ();
+ UpdateColumnSizes ();
+ }
+
+ public override void ViewDidMoveToWindow ()
+ {
+ base.ViewDidMoveToWindow ();
+
+ if (compactView)
+ OptimizeColumnSizes ();
+ }
+
+ public override void ViewDidEndLiveResize ()
+ {
+ base.ViewDidEndLiveResize ();
+ UpdateColumnSizes ();
+ }
+
+ public override void ViewDidUnhide ()
+ {
+ base.ViewDidHide ();
+ UpdateColumnSizes ();
+ }
+
+ public override void SetFrameSize (CGSize newSize)
+ {
+ base.SetFrameSize (newSize);
+
+ //if (!autoresizing && !compactView)
+ // ScaleColumnSizesToFit ();
+ }
+
+ /// <summary>
+ /// Triggered when the view tries to expand a node. This may trigger a load of
+ /// the node's children
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeExpand;
+
+ public void ExpandNode (ObjectValueNode node)
+ {
+ NodeExpand?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ /// <summary>
+ /// Triggered when the view tries to collapse a node.
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeCollapse;
+
+ public void CollapseNode (ObjectValueNode node)
+ {
+ NodeCollapse?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ OptimizeColumnSizes (false);
+ OnResized ();
+ }
+
+ /// <summary>
+ /// Triggered when the view requests a node to fetch more of it's children
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeLoadMoreChildren;
+
+ internal void LoadMoreChildren (ObjectValueNode node)
+ {
+ NodeLoadMoreChildren?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ /// <summary>
+ /// Triggered when the view needs the node to be refreshed
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeRefresh;
+
+ internal void Refresh (ObjectValueNode node)
+ {
+ NodeRefresh?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ /// <summary>
+ /// Triggered when the view needs to know if the node can be edited
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeGetCanEdit;
+
+ internal bool GetCanEditNode (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeGetCanEdit?.Invoke (this, args);
+ return args.Response is bool b && b;
+ }
+
+ /// <summary>
+ /// Triggered when the node's value has been edited by the user
+ /// </summary>
+ public event EventHandler<ObjectValueEditEventArgs> NodeEditValue;
+
+ internal bool GetEditValue (ObjectValueNode node, string newText)
+ {
+ var args = new ObjectValueEditEventArgs (node, newText);
+ NodeEditValue?.Invoke (this, args);
+ return args.Response is bool b && b;
+ }
+
+ /// <summary>
+ /// Triggered when the user removes a node (an expression)
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeRemoved;
+
+ /// <summary>
+ /// Triggered when the user pins the node
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodePinned;
+
+ void CreatePinnedWatch (ObjectValueNode node)
+ {
+ var expression = node.Expression;
+
+ if (string.IsNullOrEmpty (expression))
+ return;
+
+ if (PinnedWatch != null) {
+ // Note: the row that the user just pinned will no longer be visible once
+ // all of the root children are collapsed.
+ currentHoverRow = -1;
+
+ foreach (var child in dataSource.Root.Children)
+ CollapseItem (child, true);
+ }
+
+ NodePinned?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ public void Pin (ObjectValueNode node)
+ {
+ CreatePinnedWatch (node);
+ }
+
+ /// <summary>
+ /// Triggered when the pinned watch is removed by the user
+ /// </summary>
+ public event EventHandler<EventArgs> NodeUnpinned;
+
+ public void Unpin (ObjectValueNode node)
+ {
+ NodeUnpinned?.Invoke (this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Triggered when the visualiser for the node should be shown
+ /// </summary>
+ public event EventHandler<ObjectValueNodeEventArgs> NodeShowVisualiser;
+
+ internal bool ShowVisualizer (ObjectValueNode node)
+ {
+ var args = new ObjectValueNodeEventArgs (node);
+ NodeShowVisualiser?.Invoke (this, args);
+ return args.Response is bool b && b;
+ }
+
+ /// <summary>
+ /// Triggered when an expression is added to the tree by the user
+ /// </summary>
+ public event EventHandler<ObjectValueExpressionEventArgs> ExpressionAdded;
+
+ internal void OnExpressionAdded (string expression)
+ {
+ ExpressionAdded?.Invoke (this, new ObjectValueExpressionEventArgs (null, expression));
+ }
+
+ /// <summary>
+ /// Triggered when an expression is edited by the user
+ /// </summary>
+ public event EventHandler<ObjectValueExpressionEventArgs> ExpressionEdited;
+
+ internal void OnExpressionEdited (ObjectValueNode node, string expression)
+ {
+ ExpressionEdited?.Invoke (this, new ObjectValueExpressionEventArgs (node, expression));
+ }
+
+ /// <summary>
+ /// Triggered when the user starts editing a node
+ /// </summary>
+ public event EventHandler StartEditing;
+
+ internal void OnStartEditing ()
+ {
+ StartEditing?.Invoke (this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Triggered when the user stops editing a node
+ /// </summary>
+ public new event EventHandler EndEditing;
+
+ internal void OnEndEditing ()
+ {
+ EndEditing?.Invoke (this, EventArgs.Empty);
+ }
+
+ void OnEvaluationCompleted (ObjectValueNode node, ObjectValueNode[] replacementNodes)
+ {
+ if (disposed)
+ return;
+
+ dataSource.Replace (node, replacementNodes);
+ OptimizeColumnSizes (false);
+ OnResized ();
+ }
+
+ public void LoadEvaluatedNode (ObjectValueNode node, ObjectValueNode[] replacementNodes)
+ {
+ OnEvaluationCompleted (node, replacementNodes);
+ }
+
+ void OnChildrenLoaded (ObjectValueNode node, int startIndex, int count)
+ {
+ if (disposed)
+ return;
+
+ dataSource.ReloadChildren (node);
+ OptimizeColumnSizes (false);
+ OnResized ();
+ }
+
+ public void LoadNodeChildren (ObjectValueNode node, int startIndex, int count)
+ {
+ OnChildrenLoaded (node, startIndex, count);
+ }
+
+ public void OnNodeExpanded (ObjectValueNode node)
+ {
+ if (disposed)
+ return;
+
+ if (node.IsExpanded) {
+ // if the node is _still_ expanded then adjust UI and scroll
+ if (dataSource.TryGetValue (node, out var item)) {
+ if (!IsItemExpanded (item))
+ ExpandItem (item);
+ }
+
+ OptimizeColumnSizes (false);
+ OnResized ();
+
+ // TODO: all this scrolling kind of seems awkward
+ //if (path != null)
+ // ScrollToCell (path, expCol, true, 0f, 0f);
+ }
+ }
+
+ void IObjectValueTreeView.Cleared ()
+ {
+ dataSource.Clear ();
+ }
+
+ void IObjectValueTreeView.Appended (ObjectValueNode node)
+ {
+ dataSource.Append (node);
+ }
+
+ void IObjectValueTreeView.Appended (IList<ObjectValueNode> nodes)
+ {
+ dataSource.Append (nodes);
+ }
+
+ static CGPoint ConvertPointFromEvent (NSView view, NSEvent theEvent)
+ {
+ var point = theEvent.LocationInWindow;
+
+ if (view.Window != null && theEvent.WindowNumber != view.Window.WindowNumber) {
+ var rect = theEvent.Window.ConvertRectToScreen (new CGRect (point, new CGSize (1, 1)));
+ rect = view.Window.ConvertRectFromScreen (rect);
+ point = rect.Location;
+ }
+
+ return view.ConvertPointFromView (point, null);
+ }
+
+ void UpdatePreviewIcon (nint row, PreviewButtonIcon icon)
+ {
+ if (row >= RowCount)
+ return;
+
+ var rowView = GetRowView (row, false);
+
+ if (rowView != null) {
+ var nameView = (MacDebuggerObjectNameView) rowView.ViewAtColumn (0);
+
+ nameView.SetPreviewButtonIcon (icon);
+ }
+ }
+
+ void UpdatePinIcon (nint row, bool hover)
+ {
+ if (row >= RowCount)
+ return;
+
+ if (pinColumn == null)
+ return;
+
+ var rowView = GetRowView (row, false);
+
+ if (rowView != null) {
+ var pinView = (MacDebuggerObjectPinView) rowView.ViewAtColumn (ColumnCount - 1);
+
+ pinView.SetMouseHover (hover);
+ }
+ }
+
+ void UpdateCellViewIcons (NSEvent theEvent)
+ {
+ var point = ConvertPointFromEvent (this, theEvent);
+ var row = GetRow (point);
+
+ if (row != currentHoverRow) {
+ if (currentHoverRow != -1) {
+ UpdatePreviewIcon (currentHoverRow, PreviewButtonIcon.Hidden);
+ currentHoverIcon = PreviewButtonIcon.Hidden;
+ UpdatePinIcon (currentHoverRow, false);
+ }
+ currentHoverRow = row;
+ }
+
+ if (row == -1)
+ return;
+
+ PreviewButtonIcon icon;
+
+ if (GetColumn (point) == 0) {
+ icon = PreviewButtonIcon.Hover;
+ } else {
+ icon = PreviewButtonIcon.RowHover;
+ }
+
+ currentHoverIcon = icon;
+
+ if (IsRowSelected (row))
+ icon = PreviewButtonIcon.Selected;
+
+ UpdatePreviewIcon (row, icon);
+ UpdatePinIcon (row, true);
+ }
+
+ void OnPreviewWindowClosed (object sender, EventArgs args)
+ {
+ if (currentHoverRow != -1) {
+ UpdatePreviewIcon (currentHoverRow, PreviewButtonIcon.Hidden);
+ currentHoverIcon = PreviewButtonIcon.Hidden;
+ }
+ }
+
+ public override void MouseEntered (NSEvent theEvent)
+ {
+ UpdateCellViewIcons (theEvent);
+ base.MouseEntered (theEvent);
+ }
+
+ public override void MouseExited (NSEvent theEvent)
+ {
+ if (currentHoverRow != -1) {
+ UpdatePreviewIcon (currentHoverRow, PreviewButtonIcon.Hidden);
+ currentHoverIcon = PreviewButtonIcon.Hidden;
+ currentHoverRow = -1;
+
+ UpdatePinIcon (currentHoverRow, false);
+ }
+
+ base.MouseExited (theEvent);
+ }
+
+ public override void MouseMoved (NSEvent theEvent)
+ {
+ UpdateCellViewIcons (theEvent);
+ base.MouseMoved (theEvent);
+ }
+
+ internal static bool ValidObjectForPreviewIcon (ObjectValueNode node)
+ {
+ var obj = node.GetDebuggerObjectValue ();
+ if (obj == null)
+ return false;
+
+ if (obj.IsNull)
+ return false;
+
+ if (obj.IsPrimitive) {
+ //obj.DisplayValue.Contains ("|") is special case to detect enum with [Flags]
+ return obj.TypeName == "string" || (obj.DisplayValue != null && obj.DisplayValue.Contains ("|"));
+ }
+
+ if (string.IsNullOrEmpty (obj.TypeName))
+ return false;
+
+ return true;
+ }
+
+ void OnSelectionChanged (object sender, EventArgs e)
+ {
+ if (currentHoverRow == -1)
+ return;
+
+ var row = SelectedRow;
+
+ if (SelectedRowCount == 0 || row != currentHoverRow) {
+ // reset back to what the unselected icon would be
+ UpdatePreviewIcon (currentHoverRow, currentHoverIcon);
+ return;
+ }
+
+ UpdatePreviewIcon (currentHoverRow, PreviewButtonIcon.Selected);
+ }
+
+ public event EventHandler Resized;
+
+ void OnResized ()
+ {
+ Resized?.Invoke (this, EventArgs.Empty);
+ }
+
+ [CommandUpdateHandler (EditCommands.SelectAll)]
+ protected void UpdateSelectAll (CommandInfo cmd)
+ {
+ cmd.Enabled = Controller.Root.Children.Count > 0;
+ }
+
+ [CommandHandler (EditCommands.SelectAll)]
+ protected void OnSelectAll ()
+ {
+ SelectAll (this);
+ }
+
+ [CommandHandler (EditCommands.Copy)]
+ protected void OnCopy ()
+ {
+ if (SelectedRowCount == 0)
+ return;
+
+ var str = new StringBuilder ();
+ var needsNewLine = false;
+
+ var selectedRows = SelectedRows;
+ foreach (var row in selectedRows) {
+ var item = (MacObjectValueNode) ItemAtRow ((nint) row);
+
+ if (item.Target is AddNewExpressionObjectValueNode ||
+ item.Target is ShowMoreValuesObjectValueNode ||
+ item.Target is LoadingObjectValueNode)
+ break;
+
+ if (needsNewLine)
+ str.AppendLine ();
+
+ needsNewLine = true;
+
+ var value = item.Target.DisplayValue;
+ var type = item.Target.TypeName;
+
+ if (type == "string") {
+ var objVal = item.Target.GetDebuggerObjectValue ();
+
+ if (objVal != null) {
+ // HACK: we need a better abstraction of the stack frame, better yet would be to not really need it in the view
+ var opt = DebuggerService.Frame.GetStackFrame ().DebuggerSession.Options.EvaluationOptions.Clone ();
+ opt.EllipsizeStrings = false;
+ value = '"' + Mono.Debugging.Evaluation.ExpressionEvaluator.EscapeString ((string)objVal.GetRawValue (opt)) + '"';
+ }
+ }
+
+ str.Append (value);
+ }
+
+ var clipboard = NSPasteboard.GeneralPasteboard;
+
+ clipboard.ClearContents ();
+ clipboard.SetStringForType (str.ToString (), NSPasteboard.NSPasteboardTypeString);
+
+ //Gtk.Clipboard.Get (Gdk.Selection.Clipboard).Text = str.ToString ();
+ }
+
+ void OnCopy (object sender, EventArgs args)
+ {
+ OnCopy ();
+ }
+
+ [CommandHandler (EditCommands.Delete)]
+ [CommandHandler (EditCommands.DeleteKey)]
+ protected void OnDelete ()
+ {
+ var nodesToDelete = new List<ObjectValueNode> ();
+ var selectedRows = SelectedRows;
+
+ foreach (var row in selectedRows) {
+ var item = (MacObjectValueNode) ItemAtRow ((nint) row);
+
+ nodesToDelete.Add (item.Target);
+ }
+
+ foreach (var node in nodesToDelete)
+ NodeRemoved?.Invoke (this, new ObjectValueNodeEventArgs (node));
+ }
+
+ void OnDelete (object sender, EventArgs args)
+ {
+ OnDelete ();
+ }
+
+ bool CanDelete (out bool enabled)
+ {
+ enabled = false;
+
+ if (!AllowWatchExpressions)
+ return false;
+
+ if (SelectedRowCount == 0)
+ return false;
+
+ enabled = true;
+
+ var selectedRows = SelectedRows;
+ foreach (var row in selectedRows) {
+ var item = (MacObjectValueNode)ItemAtRow ((nint)row);
+
+ if (!(item.Target.Parent is RootObjectValueNode)) {
+ enabled = false;
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ [CommandUpdateHandler (EditCommands.Delete)]
+ [CommandUpdateHandler (EditCommands.DeleteKey)]
+ protected void OnUpdateDelete (CommandInfo cinfo)
+ {
+ cinfo.Visible = CanDelete (out bool enabled);
+ cinfo.Enabled = enabled;
+ }
+
+ [CommandHandler (DebugCommands.AddWatch)]
+ protected void OnAddWatch ()
+ {
+ var expressions = new List<string> ();
+ var selectedRows = SelectedRows;
+
+ foreach (var row in selectedRows) {
+ var item = (MacObjectValueNode) ItemAtRow ((nint) row);
+ var expression = item.Target.Expression;
+
+ if (!string.IsNullOrEmpty (expression))
+ expressions.Add (expression);
+ }
+
+ foreach (var expression in expressions)
+ DebuggingService.AddWatch (expression);
+ }
+
+ void OnAddWatch (object sender, EventArgs args)
+ {
+ OnAddWatch ();
+ }
+
+ bool CanAddWatch (out bool enabled)
+ {
+ enabled = SelectedRowCount > 0;
+
+ return true;
+ }
+
+ [CommandUpdateHandler (DebugCommands.AddWatch)]
+ protected void OnUpdateAddWatch (CommandInfo cinfo)
+ {
+ cinfo.Visible = CanAddWatch (out bool enabled);
+ cinfo.Enabled = enabled;
+ }
+
+ [CommandHandler (EditCommands.Rename)]
+ protected void OnRename ()
+ {
+ var nameView = (MacDebuggerObjectNameView) GetView (0, SelectedRow, false);
+
+ nameView.TextField.BecomeFirstResponder ();
+ }
+
+ void OnRename (object sender, EventArgs args)
+ {
+ OnRename ();
+ }
+
+ bool CanRename (out bool enabled)
+ {
+ enabled = SelectedRowCount == 1;
+
+ return AllowWatchExpressions;
+ }
+
+ [CommandUpdateHandler (EditCommands.Rename)]
+ protected void OnUpdateRename (CommandInfo cinfo)
+ {
+ cinfo.Visible = CanRename (out bool enabled);
+ cinfo.Enabled = enabled;
+ }
+
+ public override NSMenu MenuForEvent (NSEvent theEvent)
+ {
+ if (!allowPopupMenu)
+ return null;
+
+ var point = ConvertPointFromEvent (this, theEvent);
+ var row = GetRow (point);
+
+ if (row < 0)
+ return null;
+
+ var menu = new NSMenu ();
+ bool enabled;
+
+ if (CanAddWatch (out enabled)) {
+ menu.AddItem (new NSMenuItem (GettextCatalog.GetString ("Add Watch"), OnAddWatch) {
+ Enabled = enabled
+ });
+ menu.AddItem (NSMenuItem.SeparatorItem);
+ }
+
+ menu.AddItem (new NSMenuItem (GettextCatalog.GetString ("Copy"), OnCopy));
+
+ if (CanRename (out enabled)) {
+ menu.AddItem (new NSMenuItem (GettextCatalog.GetString ("Rename"), OnRename) {
+ Enabled = enabled
+ });
+ }
+
+ if (CanDelete (out enabled)) {
+ menu.AddItem (new NSMenuItem (GettextCatalog.GetString ("Delete"), OnDelete) {
+ Enabled = enabled
+ });
+ }
+
+ return menu;
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing && !disposed) {
+ PreviewWindowManager.WindowClosed -= OnPreviewWindowClosed;
+ PreviewWindowManager.DestroyWindow ();
+ treeViewDelegate.SelectionChanged -= OnSelectionChanged;
+ treeViewDelegate.Dispose ();
+ treeViewDelegate = null;
+ dataSource.Dispose ();
+ dataSource = null;
+ disposed = true;
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeViewDataSource.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeViewDataSource.cs
new file mode 100644
index 0000000000..52cc05da9f
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeViewDataSource.cs
@@ -0,0 +1,343 @@
+//
+// MacObjectValueTreeViewDataSource.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using AppKit;
+using Foundation;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// The data source for the Cocoa implementation of the ObjectValueTreeView.
+ /// </summary>
+ class MacObjectValueTreeViewDataSource : NSOutlineViewDataSource
+ {
+ readonly Dictionary<ObjectValueNode, MacObjectValueNode> mapping = new Dictionary<ObjectValueNode, MacObjectValueNode> ();
+ readonly MacObjectValueTreeView treeView;
+
+ public MacObjectValueTreeViewDataSource (MacObjectValueTreeView treeView, ObjectValueNode root, bool allowWatchExpressions)
+ {
+ AllowWatchExpressions = allowWatchExpressions;
+ this.treeView = treeView;
+
+ Root = new MacObjectValueNode (null, root);
+ mapping.Add (root, Root);
+
+ foreach (var child in root.Children)
+ Add (Root, child);
+
+ if (allowWatchExpressions)
+ Add (Root, new AddNewExpressionObjectValueNode ());
+ }
+
+ public MacObjectValueNode Root {
+ get; private set;
+ }
+
+ public bool AllowWatchExpressions {
+ get; private set;
+ }
+
+ public bool TryGetValue (ObjectValueNode node, out MacObjectValueNode value)
+ {
+ return mapping.TryGetValue (node, out value);
+ }
+
+ void Add (MacObjectValueNode parent, ObjectValueNode node)
+ {
+ var value = new MacObjectValueNode (parent, node);
+ mapping[node] = value;
+
+ parent.Children.Add (value);
+
+ foreach (var child in node.Children)
+ Add (value, child);
+
+ if (node.HasChildren && !node.ChildrenLoaded)
+ Add (value, new LoadingObjectValueNode (node));
+ }
+
+ void Insert (MacObjectValueNode parent, int index, ObjectValueNode node)
+ {
+ var value = new MacObjectValueNode (parent, node);
+ mapping[node] = value;
+
+ parent.Children.Insert (index, value);
+
+ foreach (var child in node.Children)
+ Add (value, child);
+
+ if (node.HasChildren && !node.ChildrenLoaded)
+ Add (value, new LoadingObjectValueNode (node));
+ }
+
+ void Remove (MacObjectValueNode node)
+ {
+ foreach (var child in node.Children)
+ Remove (child);
+
+ mapping.Remove (node.Target);
+ node.Children.Clear ();
+ node.Dispose ();
+ }
+
+ public void Replace (ObjectValueNode node, ObjectValueNode[] replacementNodes)
+ {
+ if (!TryGetValue (node, out var item))
+ return;
+
+ var parent = item.Parent;
+ int index = -1;
+
+ if (parent == null)
+ return;
+
+ for (int i = 0; i < parent.Children.Count; i++) {
+ if (parent.Children[i] == item) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1)
+ return;
+
+ parent.Children.RemoveAt (index);
+ mapping.Remove (item.Target);
+ item.Dispose ();
+
+ treeView.BeginUpdates ();
+
+ var indexes = new NSIndexSet (index);
+
+ if (parent.Target is RootObjectValueNode)
+ treeView.RemoveItems (indexes, null, NSTableViewAnimation.None);
+ else
+ treeView.RemoveItems (indexes, parent, NSTableViewAnimation.None);
+
+ if (replacementNodes.Length > 0) {
+ for (int i = 0; i < replacementNodes.Length; i++)
+ Insert (parent, index + i, replacementNodes [i]);
+
+ var range = new NSRange (index, replacementNodes.Length);
+ indexes = NSIndexSet.FromNSRange (range);
+
+ if (parent.Target is RootObjectValueNode)
+ treeView.InsertItems (indexes, null, NSTableViewAnimation.None);
+ else
+ treeView.InsertItems (indexes, parent, NSTableViewAnimation.None);
+
+ foreach (var n in replacementNodes) {
+ if (treeView.Controller.GetNodeWasExpandedAtLastCheckpoint (n)) {
+ if (TryGetValue (n, out MacObjectValueNode x)) {
+ treeView.ExpandItem (x);
+ }
+ }
+ }
+ }
+
+ treeView.EndUpdates ();
+ }
+
+ public void ReloadChildren (ObjectValueNode node)
+ {
+ if (!TryGetValue (node, out var parent))
+ return;
+
+ treeView.BeginUpdates ();
+
+ NSIndexSet indexes;
+ NSRange range;
+
+ if (parent.Children.Count > 0) {
+ range = new NSRange (0, parent.Children.Count);
+ indexes = NSIndexSet.FromNSRange (range);
+
+ foreach (var child in parent.Children) {
+ mapping.Remove (child.Target);
+ child.Dispose ();
+ }
+
+ parent.Children.Clear ();
+
+ if (parent.Target is RootObjectValueNode)
+ treeView.RemoveItems (indexes, null, NSTableViewAnimation.None);
+ else
+ treeView.RemoveItems (indexes, parent, NSTableViewAnimation.None);
+ }
+
+ for (int i = 0; i < node.Children.Count; i++)
+ Add (parent, node.Children[i]);
+
+ // if we did not load all the children, add a Show More node
+ if (!node.ChildrenLoaded)
+ Add (parent, new ShowMoreValuesObjectValueNode (node));
+
+ range = new NSRange (0, parent.Children.Count);
+ indexes = NSIndexSet.FromNSRange (range);
+
+ if (parent.Target is RootObjectValueNode)
+ treeView.InsertItems (indexes, null, NSTableViewAnimation.None);
+ else
+ treeView.InsertItems (indexes, parent, NSTableViewAnimation.None);
+
+ // if we loaded children and discovered that the node does not actually have any children,
+ // update the node and reload the data.
+ // TOOD: it would be nice to know this before the node is expanded so we don't see the "loading" node flash
+ if (!node.HasChildren) {
+ treeView.ReloadItem (parent);
+ } else {
+ // expand any items that we loaded that were expanded previously
+
+ foreach (var n in node.Children) {
+ if (treeView.Controller.GetNodeWasExpandedAtLastCheckpoint (n)) {
+ if (TryGetValue (n, out MacObjectValueNode x)) {
+ treeView.ExpandItem (x);
+ }
+ }
+ }
+ }
+
+ treeView.EndUpdates ();
+ }
+
+ public void Clear ()
+ {
+ int count = Root.Children.Count;
+
+ if (AllowWatchExpressions)
+ count--;
+
+ for (int i = count - 1; i >= 0; i--) {
+ var child = Root.Children[i];
+ Root.Children.RemoveAt (i);
+ Remove (child);
+ }
+
+ if (count <= 0)
+ return;
+
+ var range = new NSRange (0, count);
+ var indexes = NSIndexSet.FromNSRange (range);
+
+ treeView.RemoveItems (indexes, null, NSTableViewAnimation.None);
+ }
+
+ public void Append (ObjectValueNode node)
+ {
+ int index;
+
+ if (AllowWatchExpressions) {
+ index = Root.Children.Count - 1;
+ Core.LoggingService.LogInfo ("MacObjectValueTreeViewDataSource.Append: Inserting '{0}' at index {1}", node.Name, index);
+ Insert (Root, index, node);
+ } else {
+ index = Root.Children.Count;
+ Core.LoggingService.LogInfo ("MacObjectValueTreeViewDataSource.Append: Adding '{0}' at index {1}", node.Name, index);
+ Add (Root, node);
+ }
+
+ var indexes = NSIndexSet.FromIndex (index);
+
+ treeView.InsertItems (indexes, null, NSTableViewAnimation.None);
+
+ if (treeView.Controller.GetNodeWasExpandedAtLastCheckpoint (node)) {
+ if (TryGetValue(node, out MacObjectValueNode x)) {
+ treeView.ExpandItem (x);
+ }
+ }
+ }
+
+ public void Append (IList<ObjectValueNode> nodes)
+ {
+ int index;
+
+ if (AllowWatchExpressions) {
+ index = Root.Children.Count - 1;
+ for (int i = 0; i < nodes.Count; i++)
+ Insert (Root, index + i, nodes[i]);
+ } else {
+ index = Root.Children.Count;
+ for (int i = 0; i < nodes.Count; i++)
+ Add (Root, nodes[i]);
+ }
+
+ var range = new NSRange (index, nodes.Count);
+ var indexes = NSIndexSet.FromNSRange (range);
+
+ treeView.InsertItems (indexes, null, NSTableViewAnimation.None);
+
+ foreach (var node in nodes) {
+ if (treeView.Controller.GetNodeWasExpandedAtLastCheckpoint (node)) {
+ if (TryGetValue (node, out MacObjectValueNode x)) {
+ treeView.ExpandItem (x);
+ }
+ }
+ }
+ }
+
+ public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item)
+ {
+ var node = (item as MacObjectValueNode) ?? Root;
+
+ if (node == null)
+ return 0;
+
+ return node.Children.Count;
+ }
+
+ public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item)
+ {
+ var node = (item as MacObjectValueNode) ?? Root;
+
+ if (node == null || childIndex >= node.Children.Count)
+ return null;
+
+ return node.Children[(int) childIndex];
+ }
+
+ public override bool ItemExpandable (NSOutlineView outlineView, NSObject item)
+ {
+ var node = (item as MacObjectValueNode) ?? Root;
+
+ return node != null && node.Children.Count > 0;
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing) {
+ foreach (var kvp in mapping)
+ kvp.Value.Dispose ();
+ mapping.Clear ();
+ Root = null;
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeViewDelegate.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeViewDelegate.cs
new file mode 100644
index 0000000000..8fa739adb9
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/Mac/MacObjectValueTreeViewDelegate.cs
@@ -0,0 +1,155 @@
+//
+// MacObjectValueTreeViewDelegate.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using AppKit;
+using Foundation;
+
+namespace MonoDevelop.Debugger
+{
+ /// <summary>
+ /// The worker delegate for the Cocoa implementation of the ObjectValueTreeView.
+ /// </summary>
+ class MacObjectValueTreeViewDelegate : NSOutlineViewDelegate
+ {
+ static readonly NSString NSObjectKey = new NSString ("NSObject");
+ readonly MacObjectValueTreeView treeView;
+
+ public MacObjectValueTreeViewDelegate (MacObjectValueTreeView treeView)
+ {
+ this.treeView = treeView;
+ }
+
+ public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
+ {
+ var view = (MacDebuggerObjectCellViewBase) outlineView.MakeView (tableColumn.Identifier, this);
+
+ switch (tableColumn.Identifier) {
+ case "name":
+ if (view == null)
+ view = new MacDebuggerObjectNameView (treeView);
+ break;
+ case "value":
+ if (view == null)
+ view = new MacDebuggerObjectValueView (treeView);
+ break;
+ case "type":
+ if (view == null)
+ view = new MacDebuggerObjectTypeView (treeView);
+ break;
+ case "pin":
+ if (view == null)
+ view = new MacDebuggerObjectPinView (treeView);
+ break;
+ default:
+ return null;
+ }
+
+ view.Row = outlineView.RowForItem (item);
+ view.ObjectValue = item;
+
+ return view;
+ }
+
+ public override void ColumnDidResize (NSNotification notification)
+ {
+ treeView.OnColumnResized ();
+ }
+
+#if false
+ public override nfloat GetSizeToFitColumnWidth (NSOutlineView outlineView, nint column)
+ {
+ var columns = outlineView.TableColumns ();
+ var col = columns[column];
+ var columnWidth = col.MinWidth;
+
+ for (nint row = 0; row < outlineView.RowCount; row++) {
+ var rowView = outlineView.GetRowView (row, true);
+ var columnView = rowView.ViewAtColumn (column);
+ var width = columnView.Frame.Width;
+
+ if (column == 0)
+ width += columnView.Frame.X;
+
+ if (width > columnWidth)
+ columnWidth = NMath.Min (width, col.MaxWidth);
+ }
+
+ return columnWidth;
+ }
+#endif
+
+ public override void ItemDidCollapse (NSNotification notification)
+ {
+ //var outlineView = (NSOutlineView) notification.Object;
+
+ if (!notification.UserInfo.TryGetValue (NSObjectKey, out var value))
+ return;
+
+ var node = (value as MacObjectValueNode)?.Target;
+
+ if (node == null)
+ return;
+
+ treeView.CollapseNode (node);
+ }
+
+ public override void ItemDidExpand (NSNotification notification)
+ {
+ //var outlineView = (NSOutlineView) notification.Object;
+
+ if (!notification.UserInfo.TryGetValue (NSObjectKey, out var value))
+ return;
+
+ var node = value as MacObjectValueNode;
+
+ if (node == null)
+ return;
+
+ node.HideValueButton = true;
+ treeView.ReloadItem (node, false);
+ treeView.ExpandNode (node.Target);
+ }
+
+ public override bool ShouldExpandItem (NSOutlineView outlineView, NSObject item)
+ {
+ if (!treeView.AllowExpanding)
+ return false;
+
+ var node = (item as MacObjectValueNode)?.Target;
+
+ return node != null && node.HasChildren;
+ }
+
+ public event EventHandler SelectionChanged;
+
+ public override void SelectionDidChange (NSNotification notification)
+ {
+ SelectionChanged?.Invoke (this, EventArgs.Empty);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeView.cs
index e275996801..03e57b43cf 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeView.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeView.cs
@@ -40,7 +40,6 @@ using MonoDevelop.Ide.CodeCompletion;
using MonoDevelop.Components.Commands;
using MonoDevelop.Ide.Commands;
using MonoDevelop.Ide.Editor.Extension;
-using System.Linq;
using MonoDevelop.Ide.Fonts;
namespace MonoDevelop.Debugger
@@ -759,9 +758,8 @@ namespace MonoDevelop.Debugger
}
}
- public string PinnedWatchFile { get; set; }
- public int PinnedWatchLine { get; set; }
-
+ public PinnedWatchLocation PinnedWatchLocation { get; set; }
+
public bool CompactView {
get {
return compact;
@@ -2285,13 +2283,11 @@ namespace MonoDevelop.Debugger
if (PinnedWatch != null) {
CollapseAll ();
- watch.File = PinnedWatch.File;
- watch.Line = PinnedWatch.Line;
+ watch.Location = PinnedWatch.Location;
watch.OffsetX = PinnedWatch.OffsetX;
watch.OffsetY = PinnedWatch.OffsetY + SizeRequest ().Height + 5;
} else {
- watch.File = PinnedWatchFile;
- watch.Line = PinnedWatchLine;
+ watch.Location = PinnedWatchLocation;
watch.OffsetX = -1; // means that the watch should be placed at the line coordinates defined by watch.Line
watch.OffsetY = -1;
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs
index 42773a18c2..acf1c691c2 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewController.cs
@@ -37,6 +37,16 @@ using MonoDevelop.Components;
namespace MonoDevelop.Debugger
{
+ public enum PreviewButtonIcon
+ {
+ None,
+ Hidden,
+ RowHover,
+ Hover,
+ Active,
+ Selected,
+ }
+
public interface IObjectValueDebuggerService
{
bool CanQueryDebugger { get; }
@@ -71,8 +81,10 @@ namespace MonoDevelop.Debugger
/// </summary>
readonly Dictionary<string, CheckpointState> oldValues = new Dictionary<string, CheckpointState> ();
- public ObjectValueTreeViewController ()
+ public ObjectValueTreeViewController (bool allowWatchExpressions = false)
{
+ AllowWatchExpressions = allowWatchExpressions;
+ Root = new RootObjectValueNode ();
}
public IDebuggerService Debugger {
@@ -119,13 +131,7 @@ namespace MonoDevelop.Debugger
/// Gets a value indicating whether the user should be able to add watch expressions to the tree
/// </summary>
public bool AllowWatchExpressions {
- get => allowWatchExpressions;
- set {
- allowWatchExpressions = value;
- if (view != null) {
- view.AllowWatchExpressions = value;
- }
- }
+ get; private set;
}
public PinnedWatch PinnedWatch {
@@ -137,11 +143,7 @@ namespace MonoDevelop.Debugger
}
}
- public string PinnedWatchFile {
- get; set;
- }
-
- public int PinnedWatchLine {
+ public PinnedWatchLocation PinnedWatchLocation {
get; set;
}
@@ -151,16 +153,12 @@ namespace MonoDevelop.Debugger
}
}
- public object GetControl (bool headersVisible = true, bool compactView = false, bool allowPinning = false, bool allowPopupMenu = true, bool rootPinVisible = false)
+ protected void ConfigureView (IObjectValueTreeView control)
{
- if (view != null)
- throw new InvalidOperationException ("You can only get the control once for each controller instance");
-
- view = new GtkObjectValueTreeView (this, this, AllowEditing, headersVisible, AllowWatchExpressions, compactView, allowPinning, allowPopupMenu, rootPinVisible) {
- AllowExpanding = this.AllowExpanding,
- PinnedWatch = this.PinnedWatch,
- };
+ view = control;
+ view.AllowExpanding = AllowExpanding;
+ view.PinnedWatch = PinnedWatch;
view.NodeExpand += OnViewNodeExpand;
view.NodeCollapse += OnViewNodeCollapse;
@@ -174,8 +172,38 @@ namespace MonoDevelop.Debugger
view.NodePinned += OnViewNodePinned;
view.NodeUnpinned += OnViewNodeUnpinned;
view.NodeShowVisualiser += OnViewNodeShowVisualiser;
+ }
+
+ public GtkObjectValueTreeView GetGtkControl (bool headersVisible = true, bool compactView = false, bool allowPinning = false, bool allowPopupMenu = true, bool rootPinVisible = false)
+ {
+ if (view != null)
+ throw new InvalidOperationException ("You can only get the control once for each controller instance");
+
+ var control = new GtkObjectValueTreeView (this, this, AllowEditing, headersVisible, compactView, allowPinning, allowPopupMenu, rootPinVisible);
+
+ ConfigureView (control);
- return view;
+ return control;
+ }
+
+ public MacObjectValueTreeView GetMacControl (bool headersVisible = true, bool compactView = false, bool allowPinning = false, bool allowPopupMenu = true, bool rootPinVisible = false)
+ {
+ if (view != null)
+ throw new InvalidOperationException ("You can only get the control once for each controller instance");
+
+ var control = new MacObjectValueTreeView (this, this, AllowEditing, headersVisible, compactView, allowPinning, allowPopupMenu, rootPinVisible);
+
+ ConfigureView (control);
+
+ return control;
+ }
+
+ public Control GetControl (bool headersVisible = true, bool compactView = false, bool allowPinning = false, bool allowPopupMenu = true, bool rootPinVisible = false)
+ {
+ if (Platform.IsMac)
+ return GetMacControl (headersVisible, compactView, allowPinning, allowPopupMenu, rootPinVisible);
+
+ return GetGtkControl (headersVisible, compactView, allowPinning, allowPopupMenu, rootPinVisible);
}
public void CancelAsyncTasks ()
@@ -188,10 +216,10 @@ namespace MonoDevelop.Debugger
/// </summary>
public void ClearValues ()
{
- Root = OnCreateRoot ();
+ ((RootObjectValueNode) Root).Clear ();
Runtime.RunInMainThread (() => {
- view.Reload (Root);
+ view.Cleared ();
}).Ignore ();
}
@@ -209,15 +237,12 @@ namespace MonoDevelop.Debugger
/// </summary>
public void AddValue (ObjectValueNode value)
{
- if (Root == null) {
- Root = OnCreateRoot ();
- }
-
((RootObjectValueNode) Root).AddValue (value);
RegisterNode (value);
Runtime.RunInMainThread (() => {
- view.Reload (Root);
+ LoggingService.LogInfo ("Appending value '{0}' to tree view", value.Name);
+ view.Appended (value);
}).Ignore ();
}
@@ -226,23 +251,19 @@ namespace MonoDevelop.Debugger
/// </summary>
public void AddValues (IEnumerable<ObjectValueNode> values)
{
- if (Root == null) {
- Root = OnCreateRoot ();
- }
-
var nodes = values.ToList ();
+
((RootObjectValueNode) Root).AddValues (nodes);
- foreach (var node in nodes) {
+ foreach (var node in nodes)
RegisterNode (node);
- }
Runtime.RunInMainThread (() => {
- view.Reload (Root);
+ view.Appended (nodes);
}).Ignore ();
}
- public async Task<Mono.Debugging.Client.CompletionData> GetCompletionDataAsync (string expression, CancellationToken token)
+ public async Task<CompletionData> GetCompletionDataAsync (string expression, CancellationToken token)
{
if (CanQueryDebugger && Frame != null) {
// TODO: improve how we get at the underlying real stack frame
@@ -257,13 +278,11 @@ namespace MonoDevelop.Debugger
var watch = new PinnedWatch ();
if (PinnedWatch != null) {
- watch.File = PinnedWatch.File;
- watch.Line = PinnedWatch.Line;
+ watch.Location = PinnedWatch.Location;
watch.OffsetX = PinnedWatch.OffsetX;
watch.OffsetY = PinnedWatch.OffsetY + height + 5;
} else {
- watch.File = PinnedWatchFile;
- watch.Line = PinnedWatchLine;
+ watch.Location = PinnedWatchLocation;
watch.OffsetX = -1; // means that the watch should be placed at the line coordinates defined by watch.Line
watch.OffsetY = -1;
}
@@ -354,9 +373,12 @@ namespace MonoDevelop.Debugger
public void AddExpression (string expression)
{
- if (!AllowWatchExpressions)
+ if (!AllowWatchExpressions) {
+ LoggingService.LogInfo ("Watch expressions not allowed.");
return;
+ }
+ LoggingService.LogInfo ("Evaluating expression '{0}'", expression);
var node = Frame.EvaluateExpression (expression);
AddValue (node);
}
@@ -533,6 +555,7 @@ namespace MonoDevelop.Debugger
void OnViewExpressionAdded (object sender, ObjectValueExpressionEventArgs e)
{
+ LoggingService.LogInfo ("ObjectValueTreeViewController.OnViewExpressionAdded");
AddExpression (e.Expression);
}
@@ -602,9 +625,8 @@ namespace MonoDevelop.Debugger
}
await Runtime.RunInMainThread (() => {
- if (loadedCount > 0) {
- view.LoadNodeChildren (node, 0, node.Children.Count);
- }
+ // tell the view about the children, even if there are, in fact, none
+ view.LoadNodeChildren (node, 0, node.Children.Count);
view.OnNodeExpanded (node);
});
@@ -620,25 +642,25 @@ namespace MonoDevelop.Debugger
if (childFetchTasks.TryGetValue (node, out Task<int> task)) {
// there is already a task to fetch the children
return await task;
- } else {
- try {
- var oldCount = node.Children.Count;
- var result = await node.LoadChildrenAsync (MaxEnumerableChildrenToFetch, cancellationTokenSource.Token);
+ }
- // if any of them are still evaluating register for
- // a completion event so that we can tell the UI
- for (int i = oldCount; i < oldCount + result; i++) {
- var c = node.Children[i];
- RegisterNode (c);
- }
+ try {
+ var oldCount = node.Children.Count;
+ var result = await node.LoadChildrenAsync (MaxEnumerableChildrenToFetch, cancellationTokenSource.Token);
+
+ // if any of them are still evaluating register for
+ // a completion event so that we can tell the UI
+ for (int i = oldCount; i < oldCount + result; i++) {
+ var c = node.Children[i];
+ RegisterNode (c);
+ }
- // always send the event so that the UI can determine if the node has finished loading.
- OnChildrenLoaded (node, oldCount, result);
+ // always send the event so that the UI can determine if the node has finished loading.
+ OnChildrenLoaded (node, oldCount, result);
- return result;
- } finally {
- childFetchTasks.Remove (node);
- }
+ return result;
+ } finally {
+ childFetchTasks.Remove (node);
}
} catch (Exception ex) {
LoggingService.LogInternalError (ex);
@@ -661,33 +683,33 @@ namespace MonoDevelop.Debugger
if (childFetchTasks.TryGetValue (node, out Task<int> task)) {
// there is already a task to fetch the children
return await task;
- } else {
- try {
- int result = 0;
- if (count > 0) {
- var oldCount = node.Children.Count;
- result = await node.LoadChildrenAsync (count, cancellationToken);
-
- // if any of them are still evaluating register for
- // a completion event so that we can tell the UI
- for (int i = oldCount; i < oldCount + result; i++) {
- var c = node.Children[i];
- RegisterNode (c);
- }
- } else {
- result = await node.LoadChildrenAsync (cancellationToken);
-
- // if any of them are still evaluating register for
- // a completion event so that we can tell the UI
- foreach (var c in node.Children) {
- RegisterNode (c);
- }
+ }
+
+ try {
+ int result = 0;
+ if (count > 0) {
+ var oldCount = node.Children.Count;
+ result = await node.LoadChildrenAsync (count, cancellationToken);
+
+ // if any of them are still evaluating register for
+ // a completion event so that we can tell the UI
+ for (int i = oldCount; i < oldCount + result; i++) {
+ var c = node.Children[i];
+ RegisterNode (c);
}
+ } else {
+ result = await node.LoadChildrenAsync (cancellationToken);
- return result;
- } finally {
- childFetchTasks.Remove (node);
+ // if any of them are still evaluating register for
+ // a completion event so that we can tell the UI
+ foreach (var c in node.Children) {
+ RegisterNode (c);
+ }
}
+
+ return result;
+ } finally {
+ childFetchTasks.Remove (node);
}
} catch (Exception ex) {
LoggingService.LogInternalError (ex);
@@ -737,15 +759,6 @@ namespace MonoDevelop.Debugger
#endregion
-
- /// <summary>
- /// Called when clearing, by default sets the root to a new ObjectValueNode
- /// </summary>
- protected virtual ObjectValueNode OnCreateRoot ()
- {
- return new RootObjectValueNode ();
- }
-
protected virtual IDebuggerService OnGetDebuggerService ()
{
return new ObjectValueDebuggerService ();
@@ -881,6 +894,18 @@ namespace MonoDevelop.Debugger
return "md-" + access + global + source;
}
+
+ public static string GetPreviewButtonIcon (PreviewButtonIcon icon)
+ {
+ switch (icon) {
+ case PreviewButtonIcon.Hidden: return "md-empty";
+ case PreviewButtonIcon.RowHover: return "md-preview-normal";
+ case PreviewButtonIcon.Hover: return "md-preview-hover";
+ case PreviewButtonIcon.Active: return "md-preview-active";
+ case PreviewButtonIcon.Selected: return "md-preview-selected";
+ default: return null;
+ }
+ }
}
#region Extension methods and helpers
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs
index a01eda916a..d96e5b169c 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/ObjectValueTreeViewFakes.cs
@@ -34,7 +34,7 @@ namespace MonoDevelop.Debugger
/// <summary>
/// An AbstractObjectValueNode used for debugging
/// </summary>
- abstract class DebugObjectValueNode : ObjectValueNode
+ public abstract class DebugObjectValueNode : ObjectValueNode
{
protected DebugObjectValueNode (string name) : base (name)
{
@@ -50,7 +50,7 @@ namespace MonoDevelop.Debugger
/// <summary>
/// An AbstractObjectValueNode used for debugging
/// </summary>
- sealed class FakeIndexedObjectValueNode : DebugObjectValueNode
+ public sealed class FakeIndexedObjectValueNode : DebugObjectValueNode
{
public FakeIndexedObjectValueNode (int index) : base ($"indexed[{index}]")
{
@@ -67,7 +67,7 @@ namespace MonoDevelop.Debugger
/// <summary>
/// An AbstractObjectValueNode used for debugging
/// </summary>
- sealed class FakeIsImplicitNotSupportedObjectValueNode : DebugObjectValueNode
+ public sealed class FakeIsImplicitNotSupportedObjectValueNode : DebugObjectValueNode
{
string value;
bool isImplicitNotSupported;
@@ -95,7 +95,7 @@ namespace MonoDevelop.Debugger
/// <summary>
/// An AbstractObjectValueNode used for debugging
/// </summary>
- sealed class FakeObjectValueNode : DebugObjectValueNode
+ public sealed class FakeObjectValueNode : DebugObjectValueNode
{
bool hasChildren;
@@ -120,7 +120,7 @@ namespace MonoDevelop.Debugger
/// <summary>
/// An AbstractObjectValueNode used for debugging
/// </summary>
- sealed class FakeEnumerableObjectValueNode : DebugObjectValueNode
+ public sealed class FakeEnumerableObjectValueNode : DebugObjectValueNode
{
readonly int maxItems;
@@ -164,7 +164,7 @@ namespace MonoDevelop.Debugger
/// <summary>
/// An AbstractObjectValueNode used for debugging
/// </summary>
- sealed class FakeEvaluatingObjectValueNode : DebugObjectValueNode
+ public sealed class FakeEvaluatingObjectValueNode : DebugObjectValueNode
{
bool isEvaluating;
bool hasChildren;
@@ -201,7 +201,7 @@ namespace MonoDevelop.Debugger
/// <summary>
/// An AbstractObjectValueNode used for debugging
/// </summary>
- sealed class FakeEvaluatingGroupObjectValueNode : DebugObjectValueNode, IEvaluatingGroupObjectValueNode
+ public sealed class FakeEvaluatingGroupObjectValueNode : DebugObjectValueNode, IEvaluatingGroupObjectValueNode
{
bool isEvaluating;
int evalNodes;
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs
index c20c63fef5..5669837d20 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValue/RootObjectValueNode.cs
@@ -62,6 +62,11 @@ namespace MonoDevelop.Debugger
ReplaceChildAt (index, value);
}
+ public void Clear ()
+ {
+ ClearChildren ();
+ }
+
void ISupportChildObjectValueNodeReplacement.ReplaceChildNode (ObjectValueNode node, ObjectValueNode[] newNodes)
{
var index = Children.IndexOf (node);
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs
index 1d9732bc66..3558d1877a 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs
@@ -34,54 +34,100 @@ using Mono.Debugging.Client;
using MonoDevelop.Core;
using MonoDevelop.Ide.Gui;
using MonoDevelop.Components;
+using Foundation;
namespace MonoDevelop.Debugger
{
public class ObjectValuePad : PadContent
{
- protected readonly bool UseNewTreeView = PropertyService.Get ("MonoDevelop.Debugger.UseNewTreeView", false);
+ protected readonly bool UseNewTreeView = PropertyService.Get ("MonoDevelop.Debugger.UseNewTreeView", true);
protected ObjectValueTreeViewController controller;
protected ObjectValueTreeView tree;
- readonly ScrolledWindow scrolled;
+ readonly Control control;
+ PadFontChanger fontChanger;
+ StackFrame lastFrame;
bool needsUpdateValues;
bool needsUpdateFrame;
bool initialResume;
- StackFrame lastFrame;
- PadFontChanger fontChanger;
+ bool disposed;
public override Control Control {
- get {
- return scrolled;
- }
+ get { return control; }
}
- public ObjectValuePad ()
+ public ObjectValuePad (bool allowWatchExpressions = false)
{
- scrolled = new ScrolledWindow ();
- scrolled.HscrollbarPolicy = PolicyType.Automatic;
- scrolled.VscrollbarPolicy = PolicyType.Automatic;
-
if (UseNewTreeView) {
- controller = new ObjectValueTreeViewController ();
+ controller = new ObjectValueTreeViewController (allowWatchExpressions);
controller.AllowEditing = true;
- var treeView = controller.GetControl () as GtkObjectValueTreeView;
- fontChanger = new PadFontChanger (treeView, treeView.SetCustomFont, treeView.QueueResize);
+ if (Platform.IsMac) {
+ LoggingService.LogInfo ("Using MacObjectValueTreeView for {0}", allowWatchExpressions ? "Watch Pad" : "Locals Pad");
+ var treeView = controller.GetMacControl ();
+
+ fontChanger = new PadFontChanger (treeView, treeView.SetCustomFont, treeView.QueueResize);
+
+ var scrolled = new AppKit.NSScrollView {
+ DocumentView = treeView,
+ AutohidesScrollers = false,
+ HasVerticalScroller = true,
+ HasHorizontalScroller = true,
+ };
+
+ // disable implicit animations
+ scrolled.WantsLayer = true;
+ scrolled.Layer.Actions = new NSDictionary (
+ "actions", NSNull.Null,
+ "contents", NSNull.Null,
+ "hidden", NSNull.Null,
+ "onLayout", NSNull.Null,
+ "onOrderIn", NSNull.Null,
+ "onOrderOut", NSNull.Null,
+ "position", NSNull.Null,
+ "sublayers", NSNull.Null,
+ "transform", NSNull.Null,
+ "bounds", NSNull.Null);
+
+ var host = new GtkNSViewHost (scrolled);
+ host.ShowAll ();
+
+ control = host;
+ } else {
+ LoggingService.LogInfo ("Using GtkObjectValueTreeView for {0}", allowWatchExpressions ? "Watch Pad" : "Locals Pad");
+ var treeView = controller.GetGtkControl ();
+ treeView.Show ();
+
+ fontChanger = new PadFontChanger (treeView, treeView.SetCustomFont, treeView.QueueResize);
- scrolled.Add (treeView);
+ var scrolled = new ScrolledWindow {
+ HscrollbarPolicy = PolicyType.Automatic,
+ VscrollbarPolicy = PolicyType.Automatic
+ };
+ scrolled.Add (treeView);
+ scrolled.Show ();
+
+ control = scrolled;
+ }
} else {
+ LoggingService.LogInfo ("Using old ObjectValueTreeView for {0}", allowWatchExpressions ? "Watch Pad" : "Locals Pad");
tree = new ObjectValueTreeView ();
+ tree.AllowAdding = allowWatchExpressions;
tree.AllowEditing = true;
- tree.AllowAdding = false;
+ tree.Show ();
fontChanger = new PadFontChanger (tree, tree.SetCustomFont, tree.QueueResize);
+ var scrolled = new ScrolledWindow {
+ HscrollbarPolicy = PolicyType.Automatic,
+ VscrollbarPolicy = PolicyType.Automatic
+ };
scrolled.Add (tree);
- }
+ scrolled.Show ();
- scrolled.ShowAll ();
+ control = scrolled;
+ }
DebuggingService.CurrentFrameChanged += OnFrameChanged;
DebuggingService.PausedEvent += OnDebuggerPaused;
@@ -99,17 +145,23 @@ namespace MonoDevelop.Debugger
public override void Dispose ()
{
- if (fontChanger == null)
+ if (disposed)
return;
- fontChanger.Dispose ();
- fontChanger = null;
+ if (fontChanger != null) {
+ fontChanger.Dispose ();
+ fontChanger = null;
+ }
+
+ disposed = true;
+
DebuggingService.CurrentFrameChanged -= OnFrameChanged;
DebuggingService.PausedEvent -= OnDebuggerPaused;
DebuggingService.ResumedEvent -= OnDebuggerResumed;
DebuggingService.StoppedEvent -= OnDebuggerStopped;
DebuggingService.EvaluationOptionsChanged -= OnEvaluationOptionsChanged;
DebuggingService.VariableChanged -= OnVariableChanged;
+
base.Dispose ();
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatch.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatch.cs
index 679d5f4d0f..b30ad78980 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatch.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatch.cs
@@ -40,7 +40,16 @@ namespace MonoDevelop.Debugger
[ItemProperty]
int line;
-
+
+ [ItemProperty]
+ int column;
+
+ [ItemProperty]
+ int endLine;
+
+ [ItemProperty]
+ int endColumn;
+
[ItemProperty (DefaultValue = 0)]
int offsetX;
@@ -113,6 +122,42 @@ namespace MonoDevelop.Debugger
set { line = value; NotifyChanged (); }
}
+ public int Column {
+ get { return column; }
+ set { column = value; NotifyChanged (); }
+ }
+
+ public int EndLine {
+ get { return endLine; }
+ set { endLine = value; NotifyChanged (); }
+ }
+
+ public int EndColumn {
+ get { return endColumn; }
+ set { endColumn = value; NotifyChanged (); }
+ }
+
+ public PinnedWatchLocation Location {
+ get { return new PinnedWatchLocation (File, Line, Column, EndLine, EndColumn); }
+ set {
+ if (value != null) {
+ file = value.FileName;
+ line = value.Line;
+ column = value.Column;
+ endLine = value.EndLine;
+ endColumn = value.EndColumn;
+ } else {
+ file = null;
+ line = 0;
+ column = 0;
+ endLine = 0;
+ endColumn = 0;
+ }
+
+ NotifyChanged ();
+ }
+ }
+
public int OffsetX {
get { return offsetX; }
set { offsetX = value; NotifyChanged (); }
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatchLocation.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatchLocation.cs
new file mode 100644
index 0000000000..3835c5ec86
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/PinnedWatchLocation.cs
@@ -0,0 +1,65 @@
+//
+// PinnedWatchLocation.cs
+//
+// Author:
+// Jeffrey Stedfast <jestedfa@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corp.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace MonoDevelop.Debugger
+{
+ public class PinnedWatchLocation
+ {
+ public PinnedWatchLocation (string fileName)
+ {
+ FileName = fileName;
+ }
+
+ public PinnedWatchLocation (string fileName, int line, int column, int endLine, int endColumn)
+ {
+ FileName = fileName;
+ Line = line;
+ Column = column;
+ EndLine = endLine;
+ EndColumn = endColumn;
+ }
+
+ public string FileName {
+ get; private set;
+ }
+
+ public int Line {
+ get; set;
+ }
+
+ public int Column {
+ get; set;
+ }
+
+ public int EndLine {
+ get; set;
+ }
+
+ public int EndColumn {
+ get; set;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs
index ebba78d808..4437da61ec 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/WatchPad.cs
@@ -42,15 +42,12 @@ namespace MonoDevelop.Debugger
new Gtk.TargetEntry ("text/plain;charset=utf-8", Gtk.TargetFlags.App, 0)
};
readonly List<string> expressions = new List<string> ();
-
- public WatchPad ()
+
+ public WatchPad () : base (true)
{
- if (UseNewTreeView) {
- controller.AllowWatchExpressions = true;
- } else {
+ if (!UseNewTreeView) {
tree.EnableModelDragDest (DropTargets, Gdk.DragAction.Copy);
tree.DragDataReceived += HandleDragDataReceived;
- tree.AllowAdding = true;
}
}
@@ -74,6 +71,8 @@ namespace MonoDevelop.Debugger
public void AddWatch (string expression)
{
+ LoggingService.LogInfo ("Adding expression '{0}'", expression);
+
if (UseNewTreeView) {
controller.AddExpression (expression);
} else {
@@ -84,8 +83,8 @@ namespace MonoDevelop.Debugger
void ReloadValues ()
{
// clone the list of expressions
-// expressions.Clear ();
-// expressions.AddRange (controller.GetExpressions());
+ expressions.Clear ();
+ expressions.AddRange (controller.GetExpressions ());
// remove the expressions because we're going to rebuild them
controller.ClearAll ();
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs
index 1cec611cda..4fc65a8da2 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs
@@ -24,7 +24,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
-//
+
+// Note: This API is only used by the old (Gtk) TextEditor.
+// The new TextEditor uses MonoDevelop.Debugger.VSTextView.QuickInfo.DebuggerQuickInfoSourceProvider
using System;
using System.Threading;
@@ -39,6 +41,7 @@ using MonoDevelop.Ide.Editor;
namespace MonoDevelop.SourceEditor
{
+ [Obsolete ("This has been replaced by MonoDevelop.Debugger.VSTextView.QuickInfo.DebuggerQuickInfoSourceProvider")]
class DebugValueTooltipProvider: TooltipProvider
{
DebugValueWindow tooltip;
@@ -78,13 +81,14 @@ namespace MonoDevelop.SourceEditor
if (!DebuggingService.IsPaused)
return null;
- StackFrame frame = DebuggingService.CurrentFrame;
+ var frame = DebuggingService.CurrentFrame;
if (frame == null)
return null;
var ed = CompileErrorTooltipProvider.GetExtensibleTextEditor (editor);
if (ed == null)
return null;
+
string expression = null;
int startOffset;
@@ -129,7 +133,15 @@ namespace MonoDevelop.SourceEditor
public override Window CreateTooltipWindow (TextEditor editor, DocumentContext ctx, TooltipItem item, int offset, Xwt.ModifierKeys modifierState)
{
- var window = new DebugValueWindow ((Gtk.Window)(editor.GetNativeWidget<Gtk.Widget> ()).Toplevel, editor.FileName, editor.OffsetToLocation (offset).Line, DebuggingService.CurrentFrame, (ObjectValue)item.Item, null);
+ var position = editor.OffsetToLocation (offset);
+ var location = new PinnedWatchLocation (editor.FileName) {
+ Line = position.Line,
+ Column = position.Column,
+ EndLine = position.Line,
+ EndColumn = position.Column
+ };
+
+ var window = new DebugValueWindow ((Gtk.Window)(editor.GetNativeWidget<Gtk.Widget> ()).Toplevel, location, DebuggingService.CurrentFrame, (ObjectValue)item.Item, null);
IdeApp.CommandService.RegisterTopWindow (window);
return window;
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs
index 1f4cd28845..8372c179cb 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/PinnedWatchWidget.cs
@@ -89,7 +89,7 @@ namespace MonoDevelop.SourceEditor
controller = new ObjectValueTreeViewController ();
controller.AllowEditing = true;
- treeView = (TreeView) controller.GetControl (headersVisible: false, compactView: true, allowPinning: true);
+ treeView = (TreeView) controller.GetGtkControl (headersVisible: false, compactView: true, allowPinning: true);
controller.PinnedWatch = watch;
valueTree = null;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs
index 0fb6d6b324..413da95e39 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs
@@ -587,6 +587,20 @@ namespace MonoDevelop.Components
return retval;
}
+
+ protected override void OnShown ()
+ {
+ base.OnShown ();
+ #if MAC
+ if (Core.Platform.IsMac && (Type == Gtk.WindowType.Popup || TypeHint == WindowTypeHint.PopupMenu || TypeHint == WindowTypeHint.Tooltip)) {
+ var wndnative = Mac.GtkMacInterop.GetNSWindow (this);
+ // the native window level is initially NSWindowLevel.PopUpMenu, but
+ // for some reason it gets resetted to NSWindowLevel.Normal after the window
+ // has been shown, so reset it back:
+ wndnative.Level = AppKit.NSWindowLevel.PopUpMenu;
+ }
+ #endif
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/PadFontChanger.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/PadFontChanger.cs
index c88b74afbc..7d11eb4474 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/PadFontChanger.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/PadFontChanger.cs
@@ -35,7 +35,7 @@ namespace MonoDevelop.Ide.Gui
public sealed class PadFontChanger : IDisposable
{
- Gtk.Widget styleSource;
+ Control styleSource;
Action<FontDescription> updater;
Action resizer;