From aa8a45d71be1c474490836c6c5e7123146d625bc Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Sat, 9 Dec 2017 09:44:07 -0600 Subject: [PATCH 01/33] lineNumberCommands:GUI:Pulled over initial code Starting the initial code for textboxes where commands can be entered per line --- .../LineNumberDisplay.xaml | 38 ++++ .../LineNumberDisplay.xaml.cs | 64 +++++++ .../LineNumberDisplayModel.cs | 34 ++++ .../LineNumberMarginAdorner.cs | 137 ++++++++++++++ .../LineNumberMarginWithCommands.cs | 154 ++++++++++++++++ ILEditor/Classes/WPFHelpers/ViewModelBase.cs | 168 ++++++++++++++++++ ILEditor/UserTools/SourceEditor.cs | 2 + 7 files changed, 597 insertions(+) create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs create mode 100644 ILEditor/Classes/WPFHelpers/ViewModelBase.cs diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml new file mode 100644 index 0000000..b6ee068 --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs new file mode 100644 index 0000000..e908c1e --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin +{ + /// + /// Interaction logic for LineNumberDisplay.xaml + /// + public partial class LineNumberDisplay : UserControl + { + + #region Model DP + + public static readonly DependencyProperty ModelProperty = DependencyProperty.Register("Model", typeof(LineNumberDisplayModel), typeof(LineNumberDisplay)); + + public LineNumberDisplayModel Model + { + get { return (LineNumberDisplayModel)this.GetValue(ModelProperty); } + set { this.SetValue(ModelProperty, value); } + } + + #endregion + + public LineNumberDisplay() + { + InitializeComponent(); + + this.Model = new LineNumberDisplayModel(); + this.Model.LineNumber = 99; + } + + + private void lineNumberTextBlock_MouseEnter(object sender, MouseEventArgs e) + { + var tb = sender as TextBlock; + this.Model.IsCommandMode = true; + } + + private void lineNumberTextBox_MouseLeave(object sender, MouseEventArgs e) + { + var tb = sender as TextBox; + this.Model.IsCommandMode = false; + + } + + + + } + + + +} diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs new file mode 100644 index 0000000..02b8f2e --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin +{ + public class LineNumberDisplayModel : WPFHelpers.ViewModelBase + { + + public int LineNumber + { + get { return this.GetValue(() => this.LineNumber); } + set { this.SetValue(() => this.LineNumber, value); } + } + + + public bool IsCommandMode + { + get { return this.GetValue(() => this.IsCommandMode); } + set { this.SetValue(() => this.IsCommandMode, value); } + } + + public string CommandText + { + get { return this.GetValue(() => this.CommandText); } + set { this.SetValue(() => this.CommandText, value); } + } + + + + } +} diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs new file mode 100644 index 0000000..b63b870 --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Media; + +namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin +{ + public class LineNumberMarginAdorner : Adorner + { + + public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement) + : base(marginElement) + { + marginElement.LineNumbersChangedDelayedEvent += MarginElement_LineNumbersChangedDelayedEvent; + + } + + public class LineNumberDisplayControlInfo + { + public LineNumberMarginWithCommands.LineInfo LineInfo { get; set; } + public LineNumberDisplay Control { get; set; } + } + + private List Controls = new List(); + private List visualChildTracker = new List(); + + private void ClearVisualChilds() + { + foreach (var _child in this.visualChildTracker.ToList()) + { + this.visualChildTracker.Remove(_child); + this.RemoveVisualChild(_child); + } + } + + private void AddVisualChildWithTracking(Visual _child) + { + this.AddVisualChild(_child); + this.visualChildTracker.Add(_child); + } + + private void MarginElement_LineNumbersChangedDelayedEvent(object sendor, EventArgs args) + { + this.Controls.Clear(); + this.ClearVisualChilds(); + var margin = sendor as LineNumberMarginWithCommands; + if (margin != null && margin.uiLineInfoList != null) + { + foreach (var info in margin.uiLineInfoList) + { + var ctrl = new LineNumberDisplayControlInfo + { + LineInfo = info + }; + + ctrl.Control = new LineNumberDisplay(); + ctrl.Control.Model.LineNumber = ctrl.LineInfo.Number; + + this.Controls.Add(ctrl); + this.AddVisualChildWithTracking(ctrl.Control); + } + + // update the adorner layer + AdornerLayer.GetAdornerLayer(margin).Update(); + } + } + + + + + protected override int VisualChildrenCount + { + get + { + if (this.Controls.Any()) + { + return this.Controls.Count; + } + else + { + return 0; + } + } + } + + protected override Visual GetVisualChild(int index) + { + return this.Controls[index].Control; + } + + + protected override Size MeasureOverride(Size constraint) + { + if (this.Controls.Any()) + { + var last = this.Controls.Last(); + foreach (var ctrl in this.Controls) + { + ctrl.Control.Measure(constraint); + } + return last.Control.DesiredSize; + } + else + { + return new Size(0, 0); + } + } + + + protected override Size ArrangeOverride(Size finalSize) + { + + if (this.Controls.Any()) + { + var last = this.Controls.Last(); + + foreach (var ctrl in this.Controls) + { + ctrl.Control.Arrange(new Rect(new Point(0, ctrl.LineInfo.uiYPos), finalSize)); + } + + return new Size(last.Control.ActualWidth, last.Control.ActualHeight); + } + else + { + return new Size(0, 0); + } + + } + + } +} diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs new file mode 100644 index 0000000..09bfe9f --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; +using ICSharpCode.AvalonEdit; +using ICSharpCode.AvalonEdit.Editing; +using ICSharpCode.AvalonEdit.Rendering; +using System.Windows; +using System.Windows.Controls; +using System.Globalization; +using System.Windows.Threading; +using System.Windows.Documents; +using System.Windows.Shapes; +using System.Windows.Data; + +namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin +{ + public static class Extensions + { + public static Typeface CreateTypeface(this FrameworkElement fe) + { + return new Typeface((FontFamily)fe.GetValue(TextBlock.FontFamilyProperty), + (FontStyle)fe.GetValue(TextBlock.FontStyleProperty), + (FontWeight)fe.GetValue(TextBlock.FontWeightProperty), + (FontStretch)fe.GetValue(TextBlock.FontStretchProperty)); + } + } + + // idea from: http://community.icsharpcode.net/forums/t/11706.aspx + public class LineNumberMarginWithCommands : LineNumberMargin + { + + public static void Install(TextEditor _editor) + { + var me = new LineNumberMarginWithCommands(_editor); + + me.Loaded += (_sender, args) => + { + var adorner1 = new LineNumberMarginAdorner(me); + // it's got to be displayed before adorning I think + // adorn it + AdornerLayer.GetAdornerLayer(me).Add(adorner1); + }; + + _editor.ShowLineNumbers = false; // turn off the built in + + addLineNumberMarching(_editor, me); + + } + + + private static void addLineNumberMarching(TextEditor _editor, LineNumberMargin lineNumbers) + { + var leftMargins = _editor.TextArea.LeftMargins; + + Line line = (Line)DottedLineMargin.Create(); + leftMargins.Insert(0, lineNumbers); + leftMargins.Insert(1, line); + var lineNumbersForeground = new Binding("LineNumbersForeground") { Source = _editor }; + line.SetBinding(Line.StrokeProperty, lineNumbersForeground); + lineNumbers.SetBinding(Control.ForegroundProperty, lineNumbersForeground); + + } + + public LineNumberMarginWithCommands(TextEditor _editor) + { + + } + + + Typeface typeface; + double emSize; + + + protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) + { + typeface = this.CreateTypeface(); + emSize = (double)GetValue(TextBlock.FontSizeProperty); + + FormattedText text = new FormattedText( + new string('9', maxLineNumberLength), + System.Globalization.CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, + emSize, + (Brush)GetValue(Control.ForegroundProperty) + ); + return new Size(text.Width, 0); + } + + + public class LineInfo + { + public int Number { get; set; } + public double uiXPos { get; set; } + public double uiYPos { get; set; } + + public double uiTotalAvailableWidth { get; set; } + } + + public List uiLineInfoList { get; set; } = new List(); + + + // do a delayed event when the line info list is updated + public event Action LineNumbersChangedDelayedEvent; + + + + protected override void OnRender(DrawingContext drawingContext) + { + //lineNumbersChangedDelayTimer.Stop(); // if we have ticks going then stop it because we've rendered line numbers again + this.uiLineInfoList.Clear(); + TextView textView = this.TextView; + Size renderSize = this.RenderSize; + if (textView != null && textView.VisualLinesValid) + { + var foreground = (Brush)GetValue(Control.ForegroundProperty); + foreach (VisualLine line in textView.VisualLines) + { + var info = new LineInfo(); + info.Number = line.FirstDocumentLine.LineNumber; + info.uiYPos = line.VisualTop - textView.VerticalOffset; + + + FormattedText text = new FormattedText( + info.Number.ToString(CultureInfo.CurrentCulture), + CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, + typeface, emSize, foreground + ); + + info.uiTotalAvailableWidth = renderSize.Width; + info.uiXPos = renderSize.Width - text.Width; + + //drawingContext.DrawText(text, new Point(info.uiXPos,info.uiYPos)); + + this.uiLineInfoList.Add(info); + } + + // finished processing line numbers + + if (this.LineNumbersChangedDelayedEvent != null) + { + this.LineNumbersChangedDelayedEvent(this, new EventArgs()); + } + + + } + } + + } +} diff --git a/ILEditor/Classes/WPFHelpers/ViewModelBase.cs b/ILEditor/Classes/WPFHelpers/ViewModelBase.cs new file mode 100644 index 0000000..7cb93b3 --- /dev/null +++ b/ILEditor/Classes/WPFHelpers/ViewModelBase.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace ILEditor.Classes.WPFHelpers +{ + /** + * This was included with some other code I got: http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/ + */ + public abstract class ViewModelBase : INotifyPropertyChanged + { + + + /// + /// Internal store for the property values. + /// + private Dictionary values; + + public ViewModelBase() + { + this.values = new Dictionary(); + } + + + #region INotifyPropertyChanged Members + + /// + /// Occurs when a property value changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Called when [property changed]. + /// + /// Name of the property. + protected void OnPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + // root event source first + PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName); + handler(this, args); + } + } + + #endregion + + // Unfortunately, C# currently does not support property delegates, in other words, a reference to a property. + private PropertyInfo GetPropertyInfo(LambdaExpression lambda) + { + MemberExpression memberExpression; + if (lambda.Body is UnaryExpression) + { + var unaryExpression = lambda.Body as UnaryExpression; + memberExpression = unaryExpression.Operand as MemberExpression; + } + else + { + memberExpression = lambda.Body as MemberExpression; + } + var constantExpression = memberExpression.Expression as ConstantExpression; + var propertyInfo = memberExpression.Member as PropertyInfo; + return propertyInfo; + } + + protected void SetValue(Expression> property, T value) + { + LambdaExpression lambda = property as LambdaExpression; + + if (lambda == null) + throw new ArgumentException("Invalid view model property definition."); + + PropertyInfo propertyInfo = this.GetPropertyInfo(lambda); + + T existingValue = GetValueInternal(propertyInfo.Name); + + if (!object.Equals(existingValue, value)) + { + this.values[propertyInfo.Name] = value; + this.OnPropertyChanged(propertyInfo.Name); + } + } + + /// + /// This very will allways fire OnPropertyChanged + /// - Should not be used unless you know what your doing + /// + /// + /// + /// + protected void SetValueAllwaysNotify(Expression> property, T value) + { + LambdaExpression lambda = property as LambdaExpression; + + if (lambda == null) + throw new ArgumentException("Invalid view model property definition."); + + PropertyInfo propertyInfo = this.GetPropertyInfo(lambda); + + T existingValue = GetValueInternal(propertyInfo.Name); + + this.values[propertyInfo.Name] = value; + this.OnPropertyChanged(propertyInfo.Name); + } + + + + + protected T GetValue(Expression> property) + { + LambdaExpression lambda = property as LambdaExpression; + + if (lambda == null) + throw new ArgumentException("Invalid view model property definition."); + + PropertyInfo propertyInfo = this.GetPropertyInfo(lambda); + + T value = GetValueInternal(propertyInfo.Name); + + // auto create instance for common list types + if (value == null && !propertyInfo.CanWrite) + { + if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ObservableCollection<>)) + { + value = (T)Activator.CreateInstance(propertyInfo.PropertyType); + } + else if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) + { + value = (T)Activator.CreateInstance(propertyInfo.PropertyType); + } + else if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + Type observableCollectionType = typeof(ObservableCollection<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments()); + value = (T)Activator.CreateInstance(observableCollectionType); + } + else if (propertyInfo.PropertyType == typeof(System.Collections.IEnumerable)) + { + Type observableCollectionType = typeof(ObservableCollection<>).MakeGenericType(typeof(object)); + value = (T)Activator.CreateInstance(observableCollectionType); + } + + this.values[propertyInfo.Name] = value; + } + + return value; + + } + + internal T GetValueInternal(string propertyName) + { + object value; + if (values.TryGetValue(propertyName, out value)) + return (T)value; + else + return default(T); + } + + }// end of view model base class + + +} diff --git a/ILEditor/UserTools/SourceEditor.cs b/ILEditor/UserTools/SourceEditor.cs index 19e75a7..7f94e25 100644 --- a/ILEditor/UserTools/SourceEditor.cs +++ b/ILEditor/UserTools/SourceEditor.cs @@ -77,6 +77,8 @@ public SourceEditor(String LocalFile, ILELanguage Language = ILELanguage.None, i //SearchPanel.Install(textEditor); SearchReplacePanel.Install(textEditor); + Classes.AvalonEdit.LineNumberCommandMargin.LineNumberMarginWithCommands.Install(textEditor); + string lang = ""; bool DarkMode = (Program.Config.GetValue("darkmode") == "true"); From bf3fb0fe09afef9b5096a204e2690cef80ae96f0 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 07:05:43 -0600 Subject: [PATCH 02/33] lineNumberCommands:Adding files into project --- ILEditor/ILEditor.csproj | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ILEditor/ILEditor.csproj b/ILEditor/ILEditor.csproj index f4c6a3d..262428f 100644 --- a/ILEditor/ILEditor.csproj +++ b/ILEditor/ILEditor.csproj @@ -109,6 +109,12 @@ + + LineNumberDisplay.xaml + + + + @@ -122,6 +128,7 @@ + Form @@ -517,6 +524,10 @@ + + MSBuild:Compile + Designer + Designer MSBuild:Compile From ade2c378f4028e0fbe63dec412f2fe220b831750 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 07:47:25 -0600 Subject: [PATCH 03/33] lineNumberCommands:Changing it to display a textbox next to line number Don't hide the textbox anymore --- .../LineNumberDisplay.xaml | 34 ++++---------- .../LineNumberDisplay.xaml.cs | 14 ------ .../LineNumberDisplayModel.cs | 7 --- .../LineNumberMarginWithCommands.cs | 46 +++++++++---------- 4 files changed, 31 insertions(+), 70 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml index b6ee068..b134f8b 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml @@ -8,31 +8,17 @@ x:Name="lineNumberDisplayCtl" d:DesignHeight="300" d:DesignWidth="300"> - - - - - - - - - + > + + + diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs index e908c1e..4734399 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs @@ -42,20 +42,6 @@ public LineNumberDisplay() } - private void lineNumberTextBlock_MouseEnter(object sender, MouseEventArgs e) - { - var tb = sender as TextBlock; - this.Model.IsCommandMode = true; - } - - private void lineNumberTextBox_MouseLeave(object sender, MouseEventArgs e) - { - var tb = sender as TextBox; - this.Model.IsCommandMode = false; - - } - - } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs index 02b8f2e..48b62ea 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs @@ -15,13 +15,6 @@ public int LineNumber set { this.SetValue(() => this.LineNumber, value); } } - - public bool IsCommandMode - { - get { return this.GetValue(() => this.IsCommandMode); } - set { this.SetValue(() => this.IsCommandMode, value); } - } - public string CommandText { get { return this.GetValue(() => this.CommandText); } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs index 09bfe9f..7f37459 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs @@ -70,24 +70,30 @@ public LineNumberMarginWithCommands(TextEditor _editor) } - Typeface typeface; - double emSize; + private int previousMaxLineNumberLength = -1; + private System.Windows.Size previousLineNumberDisplaySize; // only change this when line number length changes + private static System.Windows.Size recalculateLineNumberDisplayControlSize(int maxLineNumberLength, + System.Windows.Size availableSize) + { + var lineNumberDisplayExampleCtrl = new LineNumberDisplay(); + lineNumberDisplayExampleCtrl.Model.LineNumber = Convert.ToInt32(new string('9', maxLineNumberLength)); + lineNumberDisplayExampleCtrl.Measure(availableSize); + availableSize.Width = 1; + lineNumberDisplayExampleCtrl.Arrange(new Rect(availableSize)); + + return lineNumberDisplayExampleCtrl.RenderSize; + } + protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) { - typeface = this.CreateTypeface(); - emSize = (double)GetValue(TextBlock.FontSizeProperty); - - FormattedText text = new FormattedText( - new string('9', maxLineNumberLength), - System.Globalization.CultureInfo.CurrentCulture, - FlowDirection.LeftToRight, - typeface, - emSize, - (Brush)GetValue(Control.ForegroundProperty) - ); - return new Size(text.Width, 0); + if( this.maxLineNumberLength != this.previousMaxLineNumberLength) + { + this.previousLineNumberDisplaySize = recalculateLineNumberDisplayControlSize(this.maxLineNumberLength, availableSize); + } + + return new Size(this.previousLineNumberDisplaySize.Width, 0); } @@ -110,7 +116,6 @@ public class LineInfo protected override void OnRender(DrawingContext drawingContext) { - //lineNumbersChangedDelayTimer.Stop(); // if we have ticks going then stop it because we've rendered line numbers again this.uiLineInfoList.Clear(); TextView textView = this.TextView; Size renderSize = this.RenderSize; @@ -124,17 +129,8 @@ protected override void OnRender(DrawingContext drawingContext) info.uiYPos = line.VisualTop - textView.VerticalOffset; - FormattedText text = new FormattedText( - info.Number.ToString(CultureInfo.CurrentCulture), - CultureInfo.CurrentCulture, - FlowDirection.LeftToRight, - typeface, emSize, foreground - ); - info.uiTotalAvailableWidth = renderSize.Width; - info.uiXPos = renderSize.Width - text.Width; - - //drawingContext.DrawText(text, new Point(info.uiXPos,info.uiYPos)); + info.uiXPos = 0; this.uiLineInfoList.Add(info); } From c0677a2fdc3b99cf305a9179a18e565225b91ce9 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 09:42:54 -0600 Subject: [PATCH 04/33] lineNumberCommands:Moving towards switching list numbers to a listview so that it's extremely fast --- .../LineInfo.cs | 16 ++++ .../LineNumberDisplay.xaml | 2 +- .../LineNumberDisplay.xaml.cs | 4 +- .../LineNumberDisplayModel.cs | 7 +- .../LineNumberMarginAdorner.cs | 93 +++++-------------- .../LineNumberMarginWithCommands.cs | 53 ++++++----- .../LineNumbersListView.xaml | 17 ++++ .../LineNumbersListView.xaml.cs | 43 +++++++++ .../MaxLineNumberLengthChangedEventArgs.cs | 13 +++ ILEditor/ILEditor.csproj | 9 ++ 10 files changed, 157 insertions(+), 100 deletions(-) create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineInfo.cs create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml.cs create mode 100644 ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/MaxLineNumberLengthChangedEventArgs.cs diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineInfo.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineInfo.cs new file mode 100644 index 0000000..984d5d2 --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineInfo.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin +{ + public class LineInfo + { + public int Number { get; set; } + public System.Windows.Size RenderSize { get; set; } + + + } +} diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml index b134f8b..01636d2 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml @@ -7,7 +7,7 @@ mc:Ignorable="d" x:Name="lineNumberDisplayCtl" d:DesignHeight="300" d:DesignWidth="300"> - + this.CommandText, value); } } - + // each line number is a specific height + public double ControlHeight + { + get { return this.GetValue(() => this.ControlHeight); } + set { this.SetValue(() => this.ControlHeight, value); } + } } } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs index b63b870..39d3edb 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs @@ -13,56 +13,37 @@ namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin public class LineNumberMarginAdorner : Adorner { - public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement) + public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement, + System.Windows.Size lineNumberDisplaySize) : base(marginElement) { + this.listView = new LineNumbersListView(); + this.listView.Width = lineNumberDisplaySize.Width; // constrain width marginElement.LineNumbersChangedDelayedEvent += MarginElement_LineNumbersChangedDelayedEvent; + marginElement.MaxLineNumberLengthChanged += MarginElement_MaxLineNumberLengthChanged; } - public class LineNumberDisplayControlInfo + private void MarginElement_MaxLineNumberLengthChanged(object sender, MaxLineNumberLengthChangedEventArgs args) { - public LineNumberMarginWithCommands.LineInfo LineInfo { get; set; } - public LineNumberDisplay Control { get; set; } + // width of list needs to change + this.listView.Width = args.NewSize.Width; } - private List Controls = new List(); - private List visualChildTracker = new List(); - - private void ClearVisualChilds() - { - foreach (var _child in this.visualChildTracker.ToList()) - { - this.visualChildTracker.Remove(_child); - this.RemoveVisualChild(_child); - } - } - - private void AddVisualChildWithTracking(Visual _child) - { - this.AddVisualChild(_child); - this.visualChildTracker.Add(_child); - } private void MarginElement_LineNumbersChangedDelayedEvent(object sendor, EventArgs args) { - this.Controls.Clear(); - this.ClearVisualChilds(); + this.listView.LineNumbers.Clear(); var margin = sendor as LineNumberMarginWithCommands; if (margin != null && margin.uiLineInfoList != null) { foreach (var info in margin.uiLineInfoList) { - var ctrl = new LineNumberDisplayControlInfo + this.listView.LineNumbers.Add(new LineNumberDisplayModel { - LineInfo = info - }; - - ctrl.Control = new LineNumberDisplay(); - ctrl.Control.Model.LineNumber = ctrl.LineInfo.Number; - - this.Controls.Add(ctrl); - this.AddVisualChildWithTracking(ctrl.Control); + LineNumber = info.Number, + ControlHeight = info.RenderSize.Height + }); } // update the adorner layer @@ -71,66 +52,36 @@ private void MarginElement_LineNumbersChangedDelayedEvent(object sendor, EventAr } + private LineNumbersListView listView; + protected override int VisualChildrenCount { get { - if (this.Controls.Any()) - { - return this.Controls.Count; - } - else - { - return 0; - } + return 1; } } protected override Visual GetVisualChild(int index) { - return this.Controls[index].Control; + if (index != 0) throw new ArgumentOutOfRangeException(); + return listView; } protected override Size MeasureOverride(Size constraint) { - if (this.Controls.Any()) - { - var last = this.Controls.Last(); - foreach (var ctrl in this.Controls) - { - ctrl.Control.Measure(constraint); - } - return last.Control.DesiredSize; - } - else - { - return new Size(0, 0); - } + listView.Measure(constraint); + return listView.DesiredSize; } protected override Size ArrangeOverride(Size finalSize) { - - if (this.Controls.Any()) - { - var last = this.Controls.Last(); - - foreach (var ctrl in this.Controls) - { - ctrl.Control.Arrange(new Rect(new Point(0, ctrl.LineInfo.uiYPos), finalSize)); - } - - return new Size(last.Control.ActualWidth, last.Control.ActualHeight); - } - else - { - return new Size(0, 0); - } - + listView.Arrange(new Rect(new Point(0, 0), finalSize)); + return new Size(listView.ActualWidth, listView.ActualHeight); } } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs index 7f37459..74c6fad 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs @@ -17,16 +17,6 @@ namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin { - public static class Extensions - { - public static Typeface CreateTypeface(this FrameworkElement fe) - { - return new Typeface((FontFamily)fe.GetValue(TextBlock.FontFamilyProperty), - (FontStyle)fe.GetValue(TextBlock.FontStyleProperty), - (FontWeight)fe.GetValue(TextBlock.FontWeightProperty), - (FontStretch)fe.GetValue(TextBlock.FontStretchProperty)); - } - } // idea from: http://community.icsharpcode.net/forums/t/11706.aspx public class LineNumberMarginWithCommands : LineNumberMargin @@ -38,7 +28,7 @@ public static void Install(TextEditor _editor) me.Loaded += (_sender, args) => { - var adorner1 = new LineNumberMarginAdorner(me); + var adorner1 = new LineNumberMarginAdorner(me, me.previousLineNumberDisplaySize); // it's got to be displayed before adorning I think // adorn it AdornerLayer.GetAdornerLayer(me).Add(adorner1); @@ -75,36 +65,47 @@ public LineNumberMarginWithCommands(TextEditor _editor) private static System.Windows.Size recalculateLineNumberDisplayControlSize(int maxLineNumberLength, + double lineHeight, System.Windows.Size availableSize) { var lineNumberDisplayExampleCtrl = new LineNumberDisplay(); + lineNumberDisplayExampleCtrl.Model = new LineNumberDisplayModel(); + lineNumberDisplayExampleCtrl.Model.ControlHeight = lineHeight; lineNumberDisplayExampleCtrl.Model.LineNumber = Convert.ToInt32(new string('9', maxLineNumberLength)); + lineNumberDisplayExampleCtrl.Measure(availableSize); availableSize.Width = 1; lineNumberDisplayExampleCtrl.Arrange(new Rect(availableSize)); - return lineNumberDisplayExampleCtrl.RenderSize; + return new System.Windows.Size(lineNumberDisplayExampleCtrl.ActualWidth, lineHeight); } protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) { if( this.maxLineNumberLength != this.previousMaxLineNumberLength) { - this.previousLineNumberDisplaySize = recalculateLineNumberDisplayControlSize(this.maxLineNumberLength, availableSize); + double lineHeight = 1; + if(this.TextView != null && this.TextView.VisualLinesValid) + { + lineHeight = this.TextView.VisualLines.First().Height; + } + + this.previousLineNumberDisplaySize = recalculateLineNumberDisplayControlSize(this.maxLineNumberLength, lineHeight, availableSize); + + if( this.MaxLineNumberLengthChanged != null) + { + this.MaxLineNumberLengthChanged(this, new MaxLineNumberLengthChangedEventArgs + { + NewSize = this.previousLineNumberDisplaySize + }); + } } return new Size(this.previousLineNumberDisplaySize.Width, 0); } - public class LineInfo - { - public int Number { get; set; } - public double uiXPos { get; set; } - public double uiYPos { get; set; } - public double uiTotalAvailableWidth { get; set; } - } public List uiLineInfoList { get; set; } = new List(); @@ -114,23 +115,25 @@ public class LineInfo + public event Action MaxLineNumberLengthChanged; + + + protected override void OnRender(DrawingContext drawingContext) { this.uiLineInfoList.Clear(); TextView textView = this.TextView; - Size renderSize = this.RenderSize; if (textView != null && textView.VisualLinesValid) { - var foreground = (Brush)GetValue(Control.ForegroundProperty); foreach (VisualLine line in textView.VisualLines) { var info = new LineInfo(); info.Number = line.FirstDocumentLine.LineNumber; + /* info.uiYPos = line.VisualTop - textView.VerticalOffset; - - - info.uiTotalAvailableWidth = renderSize.Width; info.uiXPos = 0; + */ + info.RenderSize = previousLineNumberDisplaySize; this.uiLineInfoList.Add(info); } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml new file mode 100644 index 0000000..69e5f76 --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml.cs new file mode 100644 index 0000000..90d74bd --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin +{ + /// + /// Interaction logic for LineNumbersListView.xaml + /// + public partial class LineNumbersListView : UserControl + { + + #region LineNumbers DP + + public static readonly DependencyProperty LineNumbersProperty = DependencyProperty.Register("LineNumbers", typeof(ObservableCollection), typeof(LineNumbersListView)); + + public ObservableCollection LineNumbers + { + get { return (ObservableCollection)this.GetValue(LineNumbersProperty); } + set { this.SetValue(LineNumbersProperty, value); } + } + + #endregion + + public LineNumbersListView() + { + InitializeComponent(); + this.LineNumbers = new ObservableCollection(); + } + } +} diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/MaxLineNumberLengthChangedEventArgs.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/MaxLineNumberLengthChangedEventArgs.cs new file mode 100644 index 0000000..4b9d322 --- /dev/null +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/MaxLineNumberLengthChangedEventArgs.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin +{ + public class MaxLineNumberLengthChangedEventArgs : EventArgs + { + public System.Windows.Size NewSize { get; set; } + } +} diff --git a/ILEditor/ILEditor.csproj b/ILEditor/ILEditor.csproj index 262428f..2355b45 100644 --- a/ILEditor/ILEditor.csproj +++ b/ILEditor/ILEditor.csproj @@ -109,12 +109,17 @@ + LineNumberDisplay.xaml + + LineNumbersListView.xaml + + @@ -528,6 +533,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + Designer MSBuild:Compile From ed82b4d188ecaba0899041200bde9d60ffc71627 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 09:47:42 -0600 Subject: [PATCH 05/33] lineNumbersCommands:Changed from ListView to ItemsControl Got it to look right, but it's really slow and the textbox isn't clickable --- .../LineNumbersListView.xaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml index 69e5f76..fc7a3b1 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumbersListView.xaml @@ -7,11 +7,11 @@ mc:Ignorable="d" x:Name="meListView" d:DesignHeight="300" d:DesignWidth="300"> - - + + - - + + From 152e9058aef13771b8cf7357fd80688c8b4f065f Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 09:56:12 -0600 Subject: [PATCH 06/33] lineNumberCommands:Add the listview to the adorner visual children It wasn't interactive because it wasn't added to the visual children --- .../LineNumberMarginAdorner.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs index 39d3edb..e000b82 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs @@ -19,6 +19,7 @@ public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement, { this.listView = new LineNumbersListView(); this.listView.Width = lineNumberDisplaySize.Width; // constrain width + this.AddVisualChild(this.listView); // this has to be there for events and interaction to work marginElement.LineNumbersChangedDelayedEvent += MarginElement_LineNumbersChangedDelayedEvent; marginElement.MaxLineNumberLengthChanged += MarginElement_MaxLineNumberLengthChanged; From 04700624b0ce2700013baa8a3f8d1fa180e1b6cf Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 09:59:15 -0600 Subject: [PATCH 07/33] lineNumberCommands:Only need the adorner layer update once --- .../LineNumberMarginAdorner.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs index e000b82..90573c2 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs @@ -20,6 +20,10 @@ public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement, this.listView = new LineNumbersListView(); this.listView.Width = lineNumberDisplaySize.Width; // constrain width this.AddVisualChild(this.listView); // this has to be there for events and interaction to work + // update the adorner layer + AdornerLayer.GetAdornerLayer(marginElement).Update(); + + // setup events that we will need to use to modify our list of line numbers marginElement.LineNumbersChangedDelayedEvent += MarginElement_LineNumbersChangedDelayedEvent; marginElement.MaxLineNumberLengthChanged += MarginElement_MaxLineNumberLengthChanged; @@ -47,8 +51,8 @@ private void MarginElement_LineNumbersChangedDelayedEvent(object sendor, EventAr }); } - // update the adorner layer - AdornerLayer.GetAdornerLayer(margin).Update(); + + } } From 48783cb176349ac17816cd459727501139521804 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 11:04:50 -0600 Subject: [PATCH 08/33] lineNumberCommands:Only redraw line number commands if lines have changed --- .../LineNumberMarginWithCommands.cs | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs index 74c6fad..988fa88 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs @@ -118,21 +118,41 @@ protected override System.Windows.Size MeasureOverride(System.Windows.Size avail public event Action MaxLineNumberLengthChanged; + private bool doWeNeedToRedoLineNumberDisplay(IReadOnlyCollection visualLines, + List linesDisplayed) + { + var firstVisual = visualLines.FirstOrDefault(); + var lastVisual = visualLines.LastOrDefault(); + + var firstLine = linesDisplayed.FirstOrDefault(); + var lastLine = linesDisplayed.LastOrDefault(); + + if(firstVisual?.FirstDocumentLine?.LineNumber == firstLine?.Number + && lastVisual?.FirstDocumentLine?.LineNumber == lastLine?.Number + ) + { + return false; + } + + return true; + } protected override void OnRender(DrawingContext drawingContext) { - this.uiLineInfoList.Clear(); + TextView textView = this.TextView; - if (textView != null && textView.VisualLinesValid) + if (textView != null + && textView.VisualLinesValid + && doWeNeedToRedoLineNumberDisplay(textView.VisualLines, this.uiLineInfoList) + ) { + + // repopulate the ui list + this.uiLineInfoList.Clear(); foreach (VisualLine line in textView.VisualLines) { var info = new LineInfo(); info.Number = line.FirstDocumentLine.LineNumber; - /* - info.uiYPos = line.VisualTop - textView.VerticalOffset; - info.uiXPos = 0; - */ info.RenderSize = previousLineNumberDisplaySize; this.uiLineInfoList.Add(info); From 0c3d22866d1fd4838f7893b03f11c1d0dc16e3cb Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 11:34:25 -0600 Subject: [PATCH 09/33] lineNumbersCommand:New Idea:Dont clear just collapse out of view line numbers This resullted in major speed improvement. This will also allow keeping commands on non visible line numbers. Lots more options are availble with this type approach --- .../LineNumberDisplay.xaml | 12 ++++ .../LineNumberDisplayModel.cs | 7 +++ .../LineNumberMarginAdorner.cs | 55 ++++++++++++++----- .../LineNumberMarginWithCommands.cs | 1 + 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml index 01636d2..1daaf30 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml @@ -7,6 +7,18 @@ mc:Ignorable="d" x:Name="lineNumberDisplayCtl" d:DesignHeight="300" d:DesignWidth="300"> + + + this.ControlHeight, value); } } + + public bool IsInView + { + get { return this.GetValue(() => this.IsInView); } + set { this.SetValue(() => this.IsInView, value); } + } + } } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs index 90573c2..7515fa7 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -36,24 +37,50 @@ private void MarginElement_MaxLineNumberLengthChanged(object sender, MaxLineNumb } - private void MarginElement_LineNumbersChangedDelayedEvent(object sendor, EventArgs args) + private void populateLineNumbers(List textLineInfoList, + ObservableCollection visualLines) { - this.listView.LineNumbers.Clear(); - var margin = sendor as LineNumberMarginWithCommands; - if (margin != null && margin.uiLineInfoList != null) + // determine what needs to be created and what needs hidden + var nonExistantTextLines = from t in textLineInfoList + where !visualLines.Any(v => v.LineNumber == t.Number) + select t; + + var visualsToHide = from v in visualLines + where !textLineInfoList.Any(t => t.Number == v.LineNumber) + select v; + + var visualsToShow = from v in visualLines + where textLineInfoList.Any(t => t.Number == v.LineNumber) + select v; + + // create + foreach( var t in nonExistantTextLines) { - foreach (var info in margin.uiLineInfoList) + visualLines.Add(new LineNumberDisplayModel { - this.listView.LineNumbers.Add(new LineNumberDisplayModel - { - LineNumber = info.Number, - ControlHeight = info.RenderSize.Height - }); - } - - - + IsInView = true, + LineNumber = t.Number, + ControlHeight = t.RenderSize.Height + }); + } + + // hide + foreach( var v in visualsToHide) + { + v.IsInView = false; } + + // show + foreach(var v in visualsToShow) + { + v.IsInView = true; + } + } + + private void MarginElement_LineNumbersChangedDelayedEvent(object sendor, EventArgs args) + { + var margin = sendor as LineNumberMarginWithCommands; + populateLineNumbers(margin.uiLineInfoList, this.listView.LineNumbers); } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs index 988fa88..f6ee4c9 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs @@ -71,6 +71,7 @@ private static System.Windows.Size recalculateLineNumberDisplayControlSize(int m var lineNumberDisplayExampleCtrl = new LineNumberDisplay(); lineNumberDisplayExampleCtrl.Model = new LineNumberDisplayModel(); lineNumberDisplayExampleCtrl.Model.ControlHeight = lineHeight; + lineNumberDisplayExampleCtrl.Model.IsInView = true; lineNumberDisplayExampleCtrl.Model.LineNumber = Convert.ToInt32(new string('9', maxLineNumberLength)); lineNumberDisplayExampleCtrl.Measure(availableSize); From 0b9ff4fb4ba33175fb24fb409b88046b34d09ab1 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Wed, 13 Dec 2017 11:40:50 -0600 Subject: [PATCH 10/33] lineNumberCommands:Populate line number display for initial text lines --- .../LineNumberMarginAdorner.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs index 7515fa7..cc11c92 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs @@ -28,6 +28,8 @@ public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement, marginElement.LineNumbersChangedDelayedEvent += MarginElement_LineNumbersChangedDelayedEvent; marginElement.MaxLineNumberLengthChanged += MarginElement_MaxLineNumberLengthChanged; + // need to initially populate line numbers that are already there + populateLineNumbers(marginElement.uiLineInfoList, this.listView.LineNumbers); } private void MarginElement_MaxLineNumberLengthChanged(object sender, MaxLineNumberLengthChangedEventArgs args) From 6fdd8c5a395e3ada633e961414db42195ed37475 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Mon, 18 Dec 2017 12:07:29 -0600 Subject: [PATCH 11/33] lineNumberCommands:Making progress towards listview width controling line number margin width Trying to rely on wpf to do width as much as possible. --- .../LineInfo.cs | 2 +- .../LineNumberDisplayModel.cs | 7 ++ .../LineNumberMarginAdorner.cs | 44 ++++++++++--- .../LineNumberMarginWithCommands.cs | 65 ++++++------------- 4 files changed, 63 insertions(+), 55 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineInfo.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineInfo.cs index 984d5d2..269d185 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineInfo.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineInfo.cs @@ -9,7 +9,7 @@ namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin public class LineInfo { public int Number { get; set; } - public System.Windows.Size RenderSize { get; set; } + public double LineHeight { get; set; } } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs index 841707d..2d476e5 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs @@ -35,5 +35,12 @@ public bool IsInView set { this.SetValue(() => this.IsInView, value); } } + // this decided wether to show textbox or label for command + public bool IsCommandEntryVisible + { + get { return this.GetValue(() => this.IsCommandEntryVisible); } + set { this.SetValue(() => this.IsCommandEntryVisible, value); } + } + } } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs index cc11c92..720150c 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs @@ -14,29 +14,26 @@ namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin public class LineNumberMarginAdorner : Adorner { - public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement, - System.Windows.Size lineNumberDisplaySize) + public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement) : base(marginElement) { this.listView = new LineNumbersListView(); - this.listView.Width = lineNumberDisplaySize.Width; // constrain width this.AddVisualChild(this.listView); // this has to be there for events and interaction to work + + this.listView.Loaded += (_sender, _args) => + { + trackListViewWidth(); + }; // update the adorner layer AdornerLayer.GetAdornerLayer(marginElement).Update(); // setup events that we will need to use to modify our list of line numbers marginElement.LineNumbersChangedDelayedEvent += MarginElement_LineNumbersChangedDelayedEvent; - marginElement.MaxLineNumberLengthChanged += MarginElement_MaxLineNumberLengthChanged; // need to initially populate line numbers that are already there populateLineNumbers(marginElement.uiLineInfoList, this.listView.LineNumbers); } - private void MarginElement_MaxLineNumberLengthChanged(object sender, MaxLineNumberLengthChangedEventArgs args) - { - // width of list needs to change - this.listView.Width = args.NewSize.Width; - } private void populateLineNumbers(List textLineInfoList, @@ -62,7 +59,7 @@ where textLineInfoList.Any(t => t.Number == v.LineNumber) { IsInView = true, LineNumber = t.Number, - ControlHeight = t.RenderSize.Height + ControlHeight = t.LineHeight }); } @@ -104,10 +101,37 @@ protected override Visual GetVisualChild(int index) return listView; } + double previousListViewWidth = 0; + + public event Action LineNumberListViewWidthChanged; + + public class LineNumberMarginListViewWidthChangedEventArgs : EventArgs + { + public double Width { get; set; } + } + + + private void trackListViewWidth() + { + if (listView.DesiredSize.Width != previousListViewWidth) + { + previousListViewWidth = listView.DesiredSize.Width; + // fire off that width of listview changed + if (this.LineNumberListViewWidthChanged != null) + { + this.LineNumberListViewWidthChanged(this, new LineNumberMarginListViewWidthChangedEventArgs + { + Width = previousListViewWidth + }); + } + } + } protected override Size MeasureOverride(Size constraint) { listView.Measure(constraint); + trackListViewWidth(); + return listView.DesiredSize; } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs index f6ee4c9..9a47ea0 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs @@ -21,6 +21,15 @@ namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin // idea from: http://community.icsharpcode.net/forums/t/11706.aspx public class LineNumberMarginWithCommands : LineNumberMargin { + private double lineNumberListViewWidth = 0; + public void UpdateLineNumberListWidthFromAdorner( double width) + { + if( width != lineNumberListViewWidth) + { + this.lineNumberListViewWidth = width; + this.InvalidateMeasure(); + } + } public static void Install(TextEditor _editor) { @@ -28,9 +37,14 @@ public static void Install(TextEditor _editor) me.Loaded += (_sender, args) => { - var adorner1 = new LineNumberMarginAdorner(me, me.previousLineNumberDisplaySize); + var adorner1 = new LineNumberMarginAdorner(me); // it's got to be displayed before adorning I think // adorn it + adorner1.LineNumberListViewWidthChanged += (_sender2, _args2) => + { + me.UpdateLineNumberListWidthFromAdorner(_args2.Width); + }; + AdornerLayer.GetAdornerLayer(me).Add(adorner1); }; @@ -59,50 +73,18 @@ public LineNumberMarginWithCommands(TextEditor _editor) } + private double lineHeight = 1; - private int previousMaxLineNumberLength = -1; - private System.Windows.Size previousLineNumberDisplaySize; // only change this when line number length changes - - - private static System.Windows.Size recalculateLineNumberDisplayControlSize(int maxLineNumberLength, - double lineHeight, - System.Windows.Size availableSize) - { - var lineNumberDisplayExampleCtrl = new LineNumberDisplay(); - lineNumberDisplayExampleCtrl.Model = new LineNumberDisplayModel(); - lineNumberDisplayExampleCtrl.Model.ControlHeight = lineHeight; - lineNumberDisplayExampleCtrl.Model.IsInView = true; - lineNumberDisplayExampleCtrl.Model.LineNumber = Convert.ToInt32(new string('9', maxLineNumberLength)); - - lineNumberDisplayExampleCtrl.Measure(availableSize); - availableSize.Width = 1; - lineNumberDisplayExampleCtrl.Arrange(new Rect(availableSize)); - - return new System.Windows.Size(lineNumberDisplayExampleCtrl.ActualWidth, lineHeight); - } + // need to determine widest visible control protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) { - if( this.maxLineNumberLength != this.previousMaxLineNumberLength) + if (this.TextView != null && this.TextView.VisualLinesValid) { - double lineHeight = 1; - if(this.TextView != null && this.TextView.VisualLinesValid) - { - lineHeight = this.TextView.VisualLines.First().Height; - } - - this.previousLineNumberDisplaySize = recalculateLineNumberDisplayControlSize(this.maxLineNumberLength, lineHeight, availableSize); - - if( this.MaxLineNumberLengthChanged != null) - { - this.MaxLineNumberLengthChanged(this, new MaxLineNumberLengthChangedEventArgs - { - NewSize = this.previousLineNumberDisplaySize - }); - } + lineHeight = this.TextView.VisualLines.First().Height; } - return new Size(this.previousLineNumberDisplaySize.Width, 0); + return new Size(this.lineNumberListViewWidth, 0); } @@ -114,11 +96,6 @@ protected override System.Windows.Size MeasureOverride(System.Windows.Size avail // do a delayed event when the line info list is updated public event Action LineNumbersChangedDelayedEvent; - - - public event Action MaxLineNumberLengthChanged; - - private bool doWeNeedToRedoLineNumberDisplay(IReadOnlyCollection visualLines, List linesDisplayed) { @@ -154,7 +131,7 @@ protected override void OnRender(DrawingContext drawingContext) { var info = new LineInfo(); info.Number = line.FirstDocumentLine.LineNumber; - info.RenderSize = previousLineNumberDisplaySize; + info.LineHeight = this.lineHeight; this.uiLineInfoList.Add(info); } From 45988262662997bdd3c57959df52ec64b9ecaceb Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Fri, 22 Dec 2017 12:37:37 -0600 Subject: [PATCH 12/33] lineNumberCommands:Rearranging to move this closer to OnMeasure --- .../LineNumberMarginWithCommands.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs index 9a47ea0..958c0a4 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginWithCommands.cs @@ -21,15 +21,7 @@ namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin // idea from: http://community.icsharpcode.net/forums/t/11706.aspx public class LineNumberMarginWithCommands : LineNumberMargin { - private double lineNumberListViewWidth = 0; - public void UpdateLineNumberListWidthFromAdorner( double width) - { - if( width != lineNumberListViewWidth) - { - this.lineNumberListViewWidth = width; - this.InvalidateMeasure(); - } - } + public static void Install(TextEditor _editor) { @@ -75,7 +67,17 @@ public LineNumberMarginWithCommands(TextEditor _editor) private double lineHeight = 1; - // need to determine widest visible control + private double lineNumberListViewWidth = 0; + public void UpdateLineNumberListWidthFromAdorner(double width) + { + if (width != lineNumberListViewWidth) + { + this.lineNumberListViewWidth = width; + this.InvalidateMeasure(); + } + } + + protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize) { From 9b4e187d6f2eff1b9064fcd457975707bc4a7ed8 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Fri, 22 Dec 2017 13:08:47 -0600 Subject: [PATCH 13/33] lineNumberCommands:Width of listview will now control margin width Using the SizeChanged event on listview to remeasure and give notice that width has changed, if it has... --- .../LineNumberMarginAdorner.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs index 720150c..c0f88bb 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberMarginAdorner.cs @@ -20,10 +20,12 @@ public LineNumberMarginAdorner(LineNumberMarginWithCommands marginElement) this.listView = new LineNumbersListView(); this.AddVisualChild(this.listView); // this has to be there for events and interaction to work - this.listView.Loaded += (_sender, _args) => + this.listView.SizeChanged += (_sender, _args) => { trackListViewWidth(); }; + + // update the adorner layer AdornerLayer.GetAdornerLayer(marginElement).Update(); @@ -130,7 +132,6 @@ private void trackListViewWidth() protected override Size MeasureOverride(Size constraint) { listView.Measure(constraint); - trackListViewWidth(); return listView.DesiredSize; } From 7d483ce180ffd0712c107d38d008fc7cc669f725 Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Fri, 22 Dec 2017 15:02:09 -0600 Subject: [PATCH 14/33] lineNumberCommands:Fixing data triggers This is to get it to display command textbox when mouse over line number textblock --- .../LineNumberDisplay.xaml | 41 +++++++++++++++++-- .../LineNumberDisplay.xaml.cs | 29 ++++++++++++- .../LineNumberDisplayModel.cs | 13 +++++- 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml index 1daaf30..c3bcff7 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml @@ -9,10 +9,8 @@ d:DesignHeight="300" d:DesignWidth="300"> + + + + + + + + diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs index 2af8ad7..99e8e68 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml.cs @@ -12,6 +12,7 @@ using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using System.Windows.Threading; namespace ILEditor.Classes.AvalonEdit.LineNumberCommandMargin { @@ -37,12 +38,36 @@ public LineNumberDisplay() { InitializeComponent(); - //this.Model = new LineNumberDisplayModel(); - //this.Model.LineNumber = 99; + turnOffCommandEntryTimer.Tick += TurnOffCommandEntryTimer_Tick; + } + + DispatcherTimer turnOffCommandEntryTimer = new DispatcherTimer + { + Interval = new TimeSpan(0, 0, 2) + }; + + private void TurnOffCommandEntryTimer_Tick(object sender, EventArgs e) + { + turnOffCommandEntryTimer.Stop(); + var model = this.DataContext as LineNumberDisplayModel; + model.IsCommandEntryVisible = false; } + private void lineNumberTextBlock_MouseEnter(object sender, MouseEventArgs e) + { + var model = this.DataContext as LineNumberDisplayModel; + + model.IsCommandEntryVisible = true; + } + + private void lineNumberCommandTextBox_KeyDown(object sender, KeyEventArgs e) + { + // keep command entry going while they are typing + turnOffCommandEntryTimer.Stop(); + turnOffCommandEntryTimer.Start(); + } } diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs index 2d476e5..4ba99e0 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplayModel.cs @@ -18,7 +18,18 @@ public int LineNumber public string CommandText { get { return this.GetValue(() => this.CommandText); } - set { this.SetValue(() => this.CommandText, value); } + set { + this.SetValue(() => this.CommandText, value); + OnPropertyChanged(nameof(HasCommandText)); + } + } + + public bool HasCommandText + { + get + { + return !string.IsNullOrWhiteSpace(CommandText); + } } // each line number is a specific height From 20b88dbdd47a75068537c61f2801e05c7a3bfdba Mon Sep 17 00:00:00 2001 From: Nathaniel Collier Date: Thu, 4 Jan 2018 13:42:23 -0600 Subject: [PATCH 15/33] lineNumberCommands:Got the command entry on/off figured out --- .../LineNumberDisplay.xaml | 5 +-- .../LineNumberDisplay.xaml.cs | 35 ++++++++----------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml index c3bcff7..1abd8a7 100644 --- a/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml +++ b/ILEditor/Classes/AvalonEdit.LineNumberCommandMargin/LineNumberDisplay.xaml @@ -22,7 +22,7 @@ MinWidth="25" Text="{Binding Path=Model.CommandText, ElementName=lineNumberDisplayCtl}" Name="lineNumberCommandTextBox" - KeyDown="lineNumberCommandTextBox_KeyDown" + LostFocus="lineNumberCommandTextBox_LostFocus" >