From 271e46b02b77e5a0b5ed17eff790c0fb0ff4a1ec Mon Sep 17 00:00:00 2001 From: Souryo Date: Thu, 24 Nov 2016 19:47:59 -0500 Subject: [PATCH] Debugger: Better tooltip for mouseover on labels/addresses --- .../Controls/ctrlDebuggerCode.Designer.cs | 3 +- GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs | 117 ++++++++++++++--- .../Controls/ctrlScrollableTextbox.cs | 4 + GUI.NET/Debugger/Controls/ctrlWatch.cs | 5 +- GUI.NET/Debugger/frmCodeTooltip.Designer.cs | 91 +++++++++++++ GUI.NET/Debugger/frmCodeTooltip.cs | 56 ++++++++ GUI.NET/Debugger/frmCodeTooltip.resx | 120 ++++++++++++++++++ GUI.NET/Debugger/frmDebugger.Designer.cs | 4 +- GUI.NET/Debugger/frmDebugger.cs | 4 +- GUI.NET/GUI.NET.csproj | 9 ++ 10 files changed, 389 insertions(+), 24 deletions(-) create mode 100644 GUI.NET/Debugger/frmCodeTooltip.Designer.cs create mode 100644 GUI.NET/Debugger/frmCodeTooltip.cs create mode 100644 GUI.NET/Debugger/frmCodeTooltip.resx diff --git a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs index 8e101595..fa760aa2 100644 --- a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs +++ b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs @@ -59,7 +59,7 @@ this.mnuGoToLocation, this.mnuAddToWatch}); this.contextMenuCode.Name = "contextMenuWatch"; - this.contextMenuCode.Size = new System.Drawing.Size(259, 170); + this.contextMenuCode.Size = new System.Drawing.Size(259, 148); this.contextMenuCode.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuCode_Opening); // // mnuShowNextStatement @@ -131,6 +131,7 @@ this.ctrlCodeViewer.ShowLineNumberNotes = false; this.ctrlCodeViewer.Size = new System.Drawing.Size(379, 218); this.ctrlCodeViewer.TabIndex = 1; + this.ctrlCodeViewer.ScrollPositionChanged += new System.EventHandler(this.ctrlCodeViewer_ScrollPositionChanged); this.ctrlCodeViewer.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseUp); this.ctrlCodeViewer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseMove); this.ctrlCodeViewer.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseDown); diff --git a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs index 25982b7a..871f6083 100644 --- a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs +++ b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs @@ -15,10 +15,13 @@ namespace Mesen.GUI.Debugger public partial class ctrlDebuggerCode : BaseScrollableTextboxUserControl { public delegate void AddressEventHandler(AddressEventArgs args); - public event AddressEventHandler OnWatchAdded; + public delegate void WatchEventHandler(WatchEventArgs args); + public event WatchEventHandler OnWatchAdded; public event AddressEventHandler OnSetNextStatement; private DebugViewInfo _config; + private frmCodeTooltip _codeTooltip = null; + public ctrlDebuggerCode() { InitializeComponent(); @@ -164,6 +167,30 @@ namespace Mesen.GUI.Debugger #region Events private Point _previousLocation; + private bool _preventCloseTooltip = false; + private string _hoverLastWord = ""; + + private void ShowTooltip(string word, Dictionary values) + { + if(_hoverLastWord != word || _codeTooltip == null) { + if(!_preventCloseTooltip && _codeTooltip != null) { + _codeTooltip.Close(); + _codeTooltip = null; + } + _codeTooltip = new frmCodeTooltip(values); + _codeTooltip.Width = 0; + _codeTooltip.Height = 0; + _codeTooltip.Visible = false; + _codeTooltip.Show(this); + _codeTooltip.Visible = true; + } + _codeTooltip.Left = Cursor.Position.X + 10; + _codeTooltip.Top = Cursor.Position.Y + 10; + + _preventCloseTooltip = true; + _hoverLastWord = word; + } + private void ctrlCodeViewer_MouseMove(object sender, MouseEventArgs e) { if(e.Location.X < this.ctrlCodeViewer.CodeMargin / 5) { @@ -173,28 +200,42 @@ namespace Mesen.GUI.Debugger } if(_previousLocation != e.Location) { + if(!_preventCloseTooltip && _codeTooltip != null) { + _codeTooltip.Close(); + _codeTooltip = null; + } + _preventCloseTooltip = false; + string word = GetWordUnderLocation(e.Location); if(word.StartsWith("$")) { try { UInt32 address = UInt32.Parse(word.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier); Byte memoryValue = InteropEmu.DebugGetMemoryValue(address); - string valueText = "$" + memoryValue.ToString("X"); - toolTip.Show(valueText, ctrlCodeViewer, e.Location.X + 5, e.Location.Y - 20, 3000); + + var values = new Dictionary() { + { "Address", "$" + address.ToString("X4") }, + { "Value", "$" + memoryValue.ToString("X2") }, + }; + + ShowTooltip(word, values); } catch { } } else { CodeLabel label = LabelManager.GetLabel(word); - if(label == null) { - toolTip.Hide(ctrlCodeViewer); - } else { + if(label != null) { Int32 relativeAddress = InteropEmu.DebugGetRelativeAddress(label.Address, label.AddressType); Int32 memoryValue = relativeAddress >= 0 ? InteropEmu.DebugGetMemoryValue((UInt32)relativeAddress) : -1; - toolTip.Show( - "Label: " + label.Label + Environment.NewLine + - "Address: $" + InteropEmu.DebugGetRelativeAddress(label.Address, label.AddressType).ToString("X4") + Environment.NewLine + - "Value: " + (memoryValue >= 0 ? ("$" + memoryValue.ToString("X2")) : "n/a") + Environment.NewLine + - "Comment: " + (label.Comment.Contains(Environment.NewLine) ? (Environment.NewLine + label.Comment) : label.Comment) - , ctrlCodeViewer, e.Location.X + 5, e.Location.Y - 60 - label.Comment.Split('\n').Length * 14, 3000); + var values = new Dictionary() { + { "Label", label.Label }, + { "Address", "$" + InteropEmu.DebugGetRelativeAddress(label.Address, label.AddressType).ToString("X4") }, + { "Value", (memoryValue >= 0 ? ("$" + memoryValue.ToString("X2")) : "n/a") }, + }; + + if(!string.IsNullOrWhiteSpace(label.Comment)) { + values["Comment"] = label.Comment; + } + + ShowTooltip(word, values); } } _previousLocation = e.Location; @@ -202,11 +243,27 @@ namespace Mesen.GUI.Debugger } UInt32 _lastClickedAddress = UInt32.MaxValue; + string _newWatchValue = string.Empty; private void ctrlCodeViewer_MouseUp(object sender, MouseEventArgs e) { string word = GetWordUnderLocation(e.Location); - if(word.StartsWith("$")) { - _lastClickedAddress = UInt32.Parse(word.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier); + if(word.StartsWith("$") || LabelManager.GetLabel(word) != null) { + if(word.StartsWith("$")) { + _lastClickedAddress = UInt32.Parse(word.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier); + _newWatchValue = "[$" + _lastClickedAddress.ToString("X") + "]"; + } else { + _lastClickedAddress = (UInt32)InteropEmu.DebugGetRelativeAddress(LabelManager.GetLabel(word).Address, LabelManager.GetLabel(word).AddressType); + _newWatchValue = "[" + word + "]"; + } + + if(e.Button == MouseButtons.Left) { + if(ModifierKeys.HasFlag(Keys.Control)) { + GoToLocation(); + } else if(ModifierKeys.HasFlag(Keys.Shift)) { + AddWatch(); + } + } + mnuGoToLocation.Enabled = true; mnuGoToLocation.Text = "Go to Location (" + word + ")"; @@ -223,6 +280,11 @@ namespace Mesen.GUI.Debugger Breakpoint _lineBreakpoint = null; private void ctrlCodeViewer_MouseDown(object sender, MouseEventArgs e) { + if(_codeTooltip != null) { + _codeTooltip.Close(); + _codeTooltip = null; + } + int address = ctrlCodeViewer.GetLineNumberAtPosition(e.Y); _lineBreakpoint = BreakpointManager.GetMatchingBreakpoint(address); @@ -239,7 +301,15 @@ namespace Mesen.GUI.Debugger } } } - + + private void ctrlCodeViewer_ScrollPositionChanged(object sender, EventArgs e) + { + if(_codeTooltip != null) { + _codeTooltip.Close(); + _codeTooltip = null; + } + } + private void ctrlCodeViewer_MouseDoubleClick(object sender, MouseEventArgs e) { int relativeAddress = ctrlCodeViewer.GetLineNumberAtPosition(e.Y); @@ -313,14 +383,24 @@ namespace Mesen.GUI.Debugger } private void mnuGoToLocation_Click(object sender, EventArgs e) + { + GoToLocation(); + } + + private void GoToLocation() { this.ctrlCodeViewer.ScrollToLineNumber((int)_lastClickedAddress); } private void mnuAddToWatch_Click(object sender, EventArgs e) + { + AddWatch(); + } + + private void AddWatch() { if(this.OnWatchAdded != null) { - this.OnWatchAdded(new AddressEventArgs() { Address = _lastClickedAddress}); + this.OnWatchAdded(new WatchEventArgs() { WatchValue = _newWatchValue }); } } @@ -341,6 +421,11 @@ namespace Mesen.GUI.Debugger #endregion } + public class WatchEventArgs : EventArgs + { + public string WatchValue { get; set; } + } + public class AddressEventArgs : EventArgs { public UInt32 Address { get; set; } diff --git a/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs b/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs index 020970d5..89142e23 100644 --- a/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs +++ b/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs @@ -12,6 +12,8 @@ namespace Mesen.GUI.Debugger { public partial class ctrlScrollableTextbox : UserControl { + public event EventHandler ScrollPositionChanged; + public new event MouseEventHandler MouseUp { add { this.ctrlTextbox.MouseUp += value; } @@ -80,6 +82,8 @@ namespace Mesen.GUI.Debugger this.vScrollBar.Value = this.ctrlTextbox.ScrollPosition; this.hScrollBar.Value = this.ctrlTextbox.HorizontalScrollPosition; UpdateHorizontalScrollbar(); + + ScrollPositionChanged?.Invoke(null, null); } private void UpdateHorizontalScrollbar() diff --git a/GUI.NET/Debugger/Controls/ctrlWatch.cs b/GUI.NET/Debugger/Controls/ctrlWatch.cs index 80230b7b..4e929d0e 100644 --- a/GUI.NET/Debugger/Controls/ctrlWatch.cs +++ b/GUI.NET/Debugger/Controls/ctrlWatch.cs @@ -128,10 +128,9 @@ namespace Mesen.GUI.Debugger } } - public void AddWatch(UInt32 address) + public void AddWatch(string watchValue) { - ListViewItem item = lstWatch.Items.Insert(lstWatch.Items.Count - 1, "[$" + address.ToString("X") + "]"); - item.Tag = address; + ListViewItem item = lstWatch.Items.Insert(lstWatch.Items.Count - 1, watchValue); UpdateWatch(); } diff --git a/GUI.NET/Debugger/frmCodeTooltip.Designer.cs b/GUI.NET/Debugger/frmCodeTooltip.Designer.cs new file mode 100644 index 00000000..3ca38326 --- /dev/null +++ b/GUI.NET/Debugger/frmCodeTooltip.Designer.cs @@ -0,0 +1,91 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmCodeTooltip + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panel1 = new System.Windows.Forms.Panel(); + this.tlpMain = new System.Windows.Forms.TableLayoutPanel(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.AutoSize = true; + this.panel1.BackColor = System.Drawing.SystemColors.Info; + this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel1.Controls.Add(this.tlpMain); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(10, 10); + this.panel1.TabIndex = 0; + // + // tlpMain + // + this.tlpMain.AutoSize = true; + this.tlpMain.ColumnCount = 2; + this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.tlpMain.Location = new System.Drawing.Point(0, 0); + this.tlpMain.Name = "tlpMain"; + this.tlpMain.RowCount = 2; + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpMain.Size = new System.Drawing.Size(8, 8); + this.tlpMain.TabIndex = 0; + // + // frmCodeTooltip + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.ClientSize = new System.Drawing.Size(10, 10); + this.ControlBox = false; + this.Controls.Add(this.panel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "frmCodeTooltip"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "frmCodeTooltip"; + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.TableLayoutPanel tlpMain; + } +} \ No newline at end of file diff --git a/GUI.NET/Debugger/frmCodeTooltip.cs b/GUI.NET/Debugger/frmCodeTooltip.cs new file mode 100644 index 00000000..0cfd2983 --- /dev/null +++ b/GUI.NET/Debugger/frmCodeTooltip.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Debugger +{ + public partial class frmCodeTooltip : Form + { + private Dictionary _values; + + protected override bool ShowWithoutActivation + { + get { return true; } + } + + public frmCodeTooltip(Dictionary values) + { + _values = values; + InitializeComponent(); + } + + protected override void OnShown(EventArgs e) + { + base.OnShown(e); + + int i = 0; + foreach(KeyValuePair kvp in _values) { + tlpMain.RowStyles.Insert(1, new RowStyle()); + Label lbl = new Label(); + lbl.Margin = new Padding(2); + lbl.Text = kvp.Key + ":"; + lbl.Font = new Font(lbl.Font, FontStyle.Bold); + lbl.AutoSize = true; + tlpMain.SetRow(lbl, i); + tlpMain.SetColumn(lbl, 0); + tlpMain.Controls.Add(lbl); + + lbl = new Label(); + lbl.Margin = new Padding(2); + lbl.AutoSize = true; + lbl.Text = kvp.Value; + tlpMain.SetRow(lbl, i); + tlpMain.SetColumn(lbl, 1); + tlpMain.Controls.Add(lbl); + + i++; + } + } + } +} diff --git a/GUI.NET/Debugger/frmCodeTooltip.resx b/GUI.NET/Debugger/frmCodeTooltip.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/GUI.NET/Debugger/frmCodeTooltip.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GUI.NET/Debugger/frmDebugger.Designer.cs b/GUI.NET/Debugger/frmDebugger.Designer.cs index c1642791..29df4392 100644 --- a/GUI.NET/Debugger/frmDebugger.Designer.cs +++ b/GUI.NET/Debugger/frmDebugger.Designer.cs @@ -214,7 +214,7 @@ this.ctrlDebuggerCode.Name = "ctrlDebuggerCode"; this.ctrlDebuggerCode.Size = new System.Drawing.Size(510, 381); this.ctrlDebuggerCode.TabIndex = 2; - this.ctrlDebuggerCode.OnWatchAdded += new Mesen.GUI.Debugger.ctrlDebuggerCode.AddressEventHandler(this.ctrlDebuggerCode_OnWatchAdded); + this.ctrlDebuggerCode.OnWatchAdded += new Mesen.GUI.Debugger.ctrlDebuggerCode.WatchEventHandler(this.ctrlDebuggerCode_OnWatchAdded); this.ctrlDebuggerCode.OnSetNextStatement += new Mesen.GUI.Debugger.ctrlDebuggerCode.AddressEventHandler(this.ctrlDebuggerCode_OnSetNextStatement); this.ctrlDebuggerCode.Enter += new System.EventHandler(this.ctrlDebuggerCode_Enter); // @@ -236,7 +236,7 @@ this.ctrlDebuggerCodeSplit.Size = new System.Drawing.Size(1, 381); this.ctrlDebuggerCodeSplit.TabIndex = 4; this.ctrlDebuggerCodeSplit.Visible = false; - this.ctrlDebuggerCodeSplit.OnWatchAdded += new Mesen.GUI.Debugger.ctrlDebuggerCode.AddressEventHandler(this.ctrlDebuggerCode_OnWatchAdded); + this.ctrlDebuggerCodeSplit.OnWatchAdded += new Mesen.GUI.Debugger.ctrlDebuggerCode.WatchEventHandler(this.ctrlDebuggerCode_OnWatchAdded); this.ctrlDebuggerCodeSplit.OnSetNextStatement += new Mesen.GUI.Debugger.ctrlDebuggerCode.AddressEventHandler(this.ctrlDebuggerCode_OnSetNextStatement); this.ctrlDebuggerCodeSplit.Enter += new System.EventHandler(this.ctrlDebuggerCodeSplit_Enter); // diff --git a/GUI.NET/Debugger/frmDebugger.cs b/GUI.NET/Debugger/frmDebugger.cs index 6bc8ceb7..29aa7292 100644 --- a/GUI.NET/Debugger/frmDebugger.cs +++ b/GUI.NET/Debugger/frmDebugger.cs @@ -307,9 +307,9 @@ namespace Mesen.GUI.Debugger InteropEmu.DebugPpuStep(89341); } - private void ctrlDebuggerCode_OnWatchAdded(AddressEventArgs args) + private void ctrlDebuggerCode_OnWatchAdded(WatchEventArgs args) { - this.ctrlWatch.AddWatch(args.Address); + this.ctrlWatch.AddWatch(args.WatchValue); } private void ctrlDebuggerCode_OnSetNextStatement(AddressEventArgs args) diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index 0e5cd52f..13f54e39 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -349,6 +349,12 @@ ctrlWatch.cs + + Form + + + frmCodeTooltip.cs + Form @@ -628,6 +634,9 @@ ctrlWatch.cs + + frmCodeTooltip.cs + frmEditLabel.cs