From 40114cfe8b2edf1e72585e8f31a8ed27c435ee9e Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 9 Jan 2016 14:25:19 +0300 Subject: [PATCH 01/16] Binding source should always implement INotifyPropertyChanged. --- ConsoleFramework/Xaml/BindingMarkupExtension.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ConsoleFramework/Xaml/BindingMarkupExtension.cs b/ConsoleFramework/Xaml/BindingMarkupExtension.cs index f1f081a..a409961 100644 --- a/ConsoleFramework/Xaml/BindingMarkupExtension.cs +++ b/ConsoleFramework/Xaml/BindingMarkupExtension.cs @@ -29,7 +29,10 @@ public BindingMarkupExtension(string path) { public object ProvideValue(IMarkupExtensionContext context) { Object realSource = Source ?? context.DataContext; - if (null != realSource && realSource is INotifyPropertyChanged) { + if ( null != realSource && !( realSource is INotifyPropertyChanged ) ) { + throw new ArgumentException("Source must be INotifyPropertyChanged to use bindings"); + } + if (null != realSource) { BindingMode mode = BindingMode.Default; if ( Path != null ) { Type enumType = typeof ( BindingMode ); From eeea091079d80c64cc3b638e13e67a27c24472e4 Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 9 Jan 2016 14:26:18 +0300 Subject: [PATCH 02/16] Fixed bug with controls with Visibility=Hidden. --- ConsoleFramework/Rendering/Renderer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ConsoleFramework/Rendering/Renderer.cs b/ConsoleFramework/Rendering/Renderer.cs index 327e5b3..b51d2f9 100644 --- a/ConsoleFramework/Rendering/Renderer.cs +++ b/ConsoleFramework/Rendering/Renderer.cs @@ -508,6 +508,12 @@ private RenderingBuffer getOrCreateFullBufferForControl(Control control) { /// Это необходимо для определения контрола, который станет источником события мыши. /// internal int getControlOpacityAt( Control control, int x, int y ) { + // Если контрол, над которым водят мышью, имеет невидимых сыновей, которые ни разу + // не отрисовывались, то в словаре буферов для таких сыновей ничего не окажется. + // Возвращаем для таких детей 6 - как будто они полностью прозрачны + if ( !buffers.ContainsKey( control ) ) { + return 6; + } return buffers[ control ].GetOpacityAt( x, y ); } From 7f5b987fc6b000dd47dbc393d98d259283b3dea7 Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 9 Jan 2016 14:27:01 +0300 Subject: [PATCH 03/16] Minor improvements. --- ConsoleFramework/Controls/Button.cs | 2 +- ConsoleFramework/Controls/ListBox.cs | 2 +- ConsoleFramework/Controls/MessageBox.cs | 4 +++- ConsoleFramework/Controls/TextBlock.cs | 18 +++++++++++++++++- Xaml/XamlParser.cs | 2 +- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/ConsoleFramework/Controls/Button.cs b/ConsoleFramework/Controls/Button.cs index 7174a3f..8e6d3da 100644 --- a/ConsoleFramework/Controls/Button.cs +++ b/ConsoleFramework/Controls/Button.cs @@ -20,7 +20,7 @@ public string Caption { protected override Size MeasureOverride(Size availableSize) { if (!string.IsNullOrEmpty(caption)) { - Size minButtonSize = new Size(caption.Length + 14, 2); + Size minButtonSize = new Size(caption.Length + 10, 2); return minButtonSize; } else return new Size(8, 2); } diff --git a/ConsoleFramework/Controls/ListBox.cs b/ConsoleFramework/Controls/ListBox.cs index dca3e38..bb6e0cf 100644 --- a/ConsoleFramework/Controls/ListBox.cs +++ b/ConsoleFramework/Controls/ListBox.cs @@ -21,7 +21,7 @@ public class ListBox : Control public int? PageSize { get; set; } private readonly ObservableList items = new ObservableList(new List()); - public IList Items { + public ObservableList Items { get { return items; } } diff --git a/ConsoleFramework/Controls/MessageBox.cs b/ConsoleFramework/Controls/MessageBox.cs index aa2cb15..87805d0 100644 --- a/ConsoleFramework/Controls/MessageBox.cs +++ b/ConsoleFramework/Controls/MessageBox.cs @@ -43,7 +43,9 @@ public static void Show( string title, string text, MessageBoxClosedEventHandler messageBox.Title = title; messageBox.Text = text; messageBox.AddHandler( ClosedEvent, new EventHandler(( sender, args ) => { - onClosed(MessageBoxResult.Button1); + if ( null != onClosed ) { + onClosed( MessageBoxResult.Button1 ); + } }) ); //messageBox.X = windowsHost.ShowModal( messageBox ); diff --git a/ConsoleFramework/Controls/TextBlock.cs b/ConsoleFramework/Controls/TextBlock.cs index 6a330c4..9e25336 100644 --- a/ConsoleFramework/Controls/TextBlock.cs +++ b/ConsoleFramework/Controls/TextBlock.cs @@ -1,9 +1,11 @@ using ConsoleFramework.Core; using ConsoleFramework.Native; using ConsoleFramework.Rendering; +using Xaml; namespace ConsoleFramework.Controls { + [ContentProperty("Text")] public class TextBlock : Control { private string text; @@ -14,6 +16,20 @@ public TextBlock() { initialize(); } + private Color color = Color.Black; + + public Color Color + { + get { return color; } + set + { + if ( color != value ) { + color = value; + Invalidate( ); + } + } + } + public string Text { get { return text; @@ -33,7 +49,7 @@ protected override Size MeasureOverride(Size availableSize) { } public override void Render(RenderingBuffer buffer) { - Attr attr = Colors.Blend(Color.Black, Color.DarkYellow); + Attr attr = Colors.Blend(color, Color.DarkYellow); buffer.FillRectangle( 0, 0, ActualWidth, ActualHeight, ' ', attr); for (int x = 0; x < ActualWidth; ++x) { for (int y = 0; y < ActualHeight; ++y) { diff --git a/Xaml/XamlParser.cs b/Xaml/XamlParser.cs index 105aaaa..dd19367 100644 --- a/Xaml/XamlParser.cs +++ b/Xaml/XamlParser.cs @@ -305,7 +305,7 @@ private object createFromXaml( string xaml, object dataContext ) { } } - // После обработки всех элементов последний раз обращаемся к + // После обработки всех элементов в последний раз обращаемся к // расширениям разметки, ожидающим свои forward-references processFixupTokens(); From 7e4704d2abc6094aad12030b18bc128ba216428b Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 9 Jan 2016 14:27:44 +0300 Subject: [PATCH 04/16] Visibility change affects rendering now. --- ConsoleFramework/Controls/Control.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ConsoleFramework/Controls/Control.cs b/ConsoleFramework/Controls/Control.cs index 54a6bc0..edabd6a 100644 --- a/ConsoleFramework/Controls/Control.cs +++ b/ConsoleFramework/Controls/Control.cs @@ -230,9 +230,16 @@ public T FindDirectChildByName< T >( string name ) where T:Control { internal LayoutInfo layoutInfo = new LayoutInfo(); internal LayoutInfo lastLayoutInfo = new LayoutInfo(); + private Visibility visibility; + public Visibility Visibility { - get; - set; + get { return visibility; } + set { + if ( visibility != value ) { + visibility = value; + Invalidate(); + } + } } /// From f7ab8aab85ba9bce2c542bfe748ca65c3b52b418 Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 9 Jan 2016 14:28:32 +0300 Subject: [PATCH 05/16] Added OnCreated method to allow access to children controls at initialization. --- ConsoleFramework/ConsoleApplication.cs | 5 ++++- ConsoleFramework/Controls/Control.cs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ConsoleFramework/ConsoleApplication.cs b/ConsoleFramework/ConsoleApplication.cs index 1176e79..4ed1e79 100644 --- a/ConsoleFramework/ConsoleApplication.cs +++ b/ConsoleFramework/ConsoleApplication.cs @@ -191,12 +191,15 @@ public static Control LoadFromXaml( string xamlResourceName, object dataContext } using ( StreamReader reader = new StreamReader( stream ) ) { string result = reader.ReadToEnd( ); - return XamlParser.CreateFromXaml(result, dataContext, new List() + Control control = XamlParser.CreateFromXaml(result, dataContext, new List() { "clr-namespace:Xaml;assembly=Xaml", "clr-namespace:ConsoleFramework.Xaml;assembly=ConsoleFramework", "clr-namespace:ConsoleFramework.Controls;assembly=ConsoleFramework", }); + control.DataContext = dataContext; + control.Created( ); + return control; } } } diff --git a/ConsoleFramework/Controls/Control.cs b/ConsoleFramework/Controls/Control.cs index edabd6a..85fe983 100644 --- a/ConsoleFramework/Controls/Control.cs +++ b/ConsoleFramework/Controls/Control.cs @@ -1352,5 +1352,24 @@ protected void PassFocusToChildUnderPoint( MouseEventArgs args ) { ConsoleApplication.Instance.FocusManager.SetFocus(this, tofocus); } } + + /// + /// This method is called after control has been created and filled with children. + /// todo : think about avoiding reentrant Created() calls + /// + public void Created( ) { + foreach ( var child in Children ) { + child.Created( ); + } + OnCreated( ); + } + + /// + /// This method is invoked after control has been created and all children + /// controls are created too (and children' OnCreated called). So, you can + /// find any child control in this method and subscribe for events. + /// + protected virtual void OnCreated( ) { + } } } From d3055c1f9a0838e99a63aeb7cd39c2ab206159b3 Mon Sep 17 00:00:00 2001 From: david Date: Thu, 21 Jan 2016 12:53:54 +0100 Subject: [PATCH 06/16] making Examples buildable --- .vs/ConsoleFramework/v14/.suo | Bin 0 -> 79872 bytes Examples/Examples.csproj | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 .vs/ConsoleFramework/v14/.suo diff --git a/.vs/ConsoleFramework/v14/.suo b/.vs/ConsoleFramework/v14/.suo new file mode 100644 index 0000000000000000000000000000000000000000..af718a11d1e1da622158fdbcd68a60a023368ab5 GIT binary patch literal 79872 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 literal 0 HcmV?d00001 diff --git a/Examples/Examples.csproj b/Examples/Examples.csproj index 6a93438..6973a34 100644 --- a/Examples/Examples.csproj +++ b/Examples/Examples.csproj @@ -74,6 +74,10 @@ + + {e1cd529b-e3f1-4660-aa4f-1670e0992553} + Binding + {2A59C284-2995-4F37-8D65-411C25C85493} ConsoleFramework From 21847e1211d9b92bb95c361131cbfc061adab74d Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 23 Jan 2016 00:35:58 +0300 Subject: [PATCH 07/16] Removed redundant suo file. --- .vs/ConsoleFramework/v14/.suo | Bin 79872 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .vs/ConsoleFramework/v14/.suo diff --git a/.vs/ConsoleFramework/v14/.suo b/.vs/ConsoleFramework/v14/.suo deleted file mode 100644 index af718a11d1e1da622158fdbcd68a60a023368ab5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79872 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 From 43c7503c60e3d461d72d21d8e45f7cb6ea0b314b Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 30 Jan 2016 13:06:50 +0300 Subject: [PATCH 08/16] Added context menu. Fixed incorrect mouse events handling for controls with opacity >= 4. --- ConsoleFramework/ConsoleFramework.csproj | 1 + ConsoleFramework/Controls/ContextMenu.cs | 46 ++++++++++++++++++++++++ ConsoleFramework/Controls/Control.cs | 21 +++++++++-- ConsoleFramework/Controls/Panel.cs | 1 + ConsoleFramework/Controls/Window.cs | 3 +- ConsoleFramework/Events/EventManager.cs | 25 +++++++++---- 6 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 ConsoleFramework/Controls/ContextMenu.cs diff --git a/ConsoleFramework/ConsoleFramework.csproj b/ConsoleFramework/ConsoleFramework.csproj index eb51f4a..d734f2f 100644 --- a/ConsoleFramework/ConsoleFramework.csproj +++ b/ConsoleFramework/ConsoleFramework.csproj @@ -61,6 +61,7 @@ + diff --git a/ConsoleFramework/Controls/ContextMenu.cs b/ConsoleFramework/Controls/ContextMenu.cs new file mode 100644 index 0000000..230b3ed --- /dev/null +++ b/ConsoleFramework/Controls/ContextMenu.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Binding.Observables; +using ConsoleFramework.Core; +using Xaml; + +namespace ConsoleFramework.Controls +{ + [ContentProperty( "Items" )] + public class ContextMenu + { + private readonly ObservableList< MenuItemBase > items = new ObservableList< MenuItemBase >( + new List< MenuItemBase >( ) ); + + public IList< MenuItemBase > Items { + get { return items; } + } + + private MenuItem.Popup popup; + private bool expanded; + + private bool popupShadow = true; + public bool PopupShadow { + get { return popupShadow; } + set { popupShadow = value; } + } + + public void OpenMenu( WindowsHost windowsHost, Point point ) { + if ( expanded ) return; + + if ( null == popup ) { + popup = new MenuItem.Popup( this.Items, this.popupShadow, 0 ); + popup.AddHandler( Window.ClosedEvent, new EventHandler( onPopupClosed ) ); + } + popup.X = point.X; + popup.Y = point.Y; + windowsHost.ShowModal( popup, true ); + expanded = true; + } + + private void onPopupClosed( object sender, EventArgs eventArgs ) { + if (!expanded) throw new InvalidOperationException("This shouldn't happen"); + expanded = false; + } + } +} \ No newline at end of file diff --git a/ConsoleFramework/Controls/Control.cs b/ConsoleFramework/Controls/Control.cs index 85fe983..4fd40cd 100644 --- a/ConsoleFramework/Controls/Control.cs +++ b/ConsoleFramework/Controls/Control.cs @@ -1331,6 +1331,7 @@ protected static void assert( bool assertion ) { /// и передаёт на него фокус, если он - Focusable и Visible. /// protected void PassFocusToChildUnderPoint( MouseEventArgs args ) { + Control topControl = null; Control tofocus = null; Control parent = this; Control hitTested = null; @@ -1341,9 +1342,11 @@ protected void PassFocusToChildUnderPoint( MouseEventArgs args ) { if (null != hitTested) { parent = hitTested; - if (hitTested.Visibility == Visibility.Visible && hitTested.Focusable) - { - tofocus = hitTested; + if (hitTested.Visibility == Visibility.Visible) { + topControl = hitTested; + if ( hitTested.Focusable ) { + tofocus = hitTested; + } } } } while (hitTested != null); @@ -1351,6 +1354,12 @@ protected void PassFocusToChildUnderPoint( MouseEventArgs args ) { { ConsoleApplication.Instance.FocusManager.SetFocus(this, tofocus); } + if ( topControl != null && args.RightButton == MouseButtonState.Pressed ) { + if ( topControl.ContextMenu != null ) { + var windowsHost = VisualTreeHelper.FindClosestParent(this); + topControl.ContextMenu.OpenMenu(windowsHost, args.GetPosition(windowsHost)); + } + } } /// @@ -1371,5 +1380,11 @@ public void Created( ) { /// protected virtual void OnCreated( ) { } + + private ContextMenu contextMenu; + public ContextMenu ContextMenu { + get { return contextMenu; } + set { contextMenu = value; } + } } } diff --git a/ConsoleFramework/Controls/Panel.cs b/ConsoleFramework/Controls/Panel.cs index b091ccd..ea2e08a 100644 --- a/ConsoleFramework/Controls/Panel.cs +++ b/ConsoleFramework/Controls/Panel.cs @@ -131,6 +131,7 @@ public override void Render(RenderingBuffer buffer) { buffer.SetPixel(x, y, ' ', Attr.BACKGROUND_BLUE | Attr.BACKGROUND_GREEN | Attr.BACKGROUND_RED | Attr.FOREGROUND_BLUE | Attr.FOREGROUND_GREEN | Attr.FOREGROUND_RED | Attr.FOREGROUND_INTENSITY); + buffer.SetOpacity( x, y, 4 ); } } } diff --git a/ConsoleFramework/Controls/Window.cs b/ConsoleFramework/Controls/Window.cs index 02f9088..2e87276 100644 --- a/ConsoleFramework/Controls/Window.cs +++ b/ConsoleFramework/Controls/Window.cs @@ -43,8 +43,7 @@ protected virtual void initialize( ) { /// По клику мышки ищет конечный Focusable контрол, который размещён /// на месте нажатия и устанавливает на нём фокус. /// - private void Window_OnPreviewMouseDown(object sender, MouseButtonEventArgs e) - { + private void Window_OnPreviewMouseDown(object sender, MouseButtonEventArgs e) { PassFocusToChildUnderPoint( e ); } diff --git a/ConsoleFramework/Events/EventManager.cs b/ConsoleFramework/Events/EventManager.cs index 0495e18..5ace10f 100644 --- a/ConsoleFramework/Events/EventManager.cs +++ b/ConsoleFramework/Events/EventManager.cs @@ -255,6 +255,9 @@ public void ParseInputEvent(INPUT_RECORD inputRecord, Control rootElement) { // контрола, над которым событие было зарегистрировано. Такой механизм необходим, // например, для корректной обработки перемещений окон (вверх или в стороны) Control source = (inputCaptureStack.Count != 0) ? inputCaptureStack.Peek() : topMost; + + // No sense to further process event with no source control + if ( source == null ) return; if (mouseEvent.dwEventFlags == MouseEventFlags.MOUSE_MOVED) { MouseButtonState leftMouseButtonState = getLeftButtonState(mouseEvent.dwButtonState); @@ -602,22 +605,30 @@ private bool processRoutedEvent(RoutedEvent routedEvent, RoutedEventArgs args) { /// /// /// RootElement для проверки всего визуального дерева. - /// + /// Элемент управления или null, если событие мыши было за границами всех контролов, или + /// если все контролы были прозрачны для событий мыши private Control findSource(Point rawPoint, Control control) { if (control.Children.Count != 0) { IList childrenOrderedByZIndex = control.GetChildrenOrderedByZIndex(); for (int i = childrenOrderedByZIndex.Count - 1; i >= 0; i--) { Control child = childrenOrderedByZIndex[i]; if (Control.HitTest(rawPoint, control, child)) { - Point childPoint = Control.TranslatePoint( null, rawPoint, child ); - int opacity = ConsoleApplication.Instance.Renderer.getControlOpacityAt( child, childPoint.X, childPoint.Y ); - if ( opacity >= 4 && opacity <= 7 ) { - continue; - } - return findSource(rawPoint, child); + Control foundSource = findSource(rawPoint, child); + if ( null != foundSource ) return foundSource; } } } + Point localPoint = Control.TranslatePoint(null, rawPoint, control); + Rect controlRect = new Rect(new Point(0, 0), control.RenderSize); + if ( !controlRect.Contains( localPoint ) ) { + return null; + } else { + int _opacity = ConsoleApplication.Instance.Renderer + .getControlOpacityAt( control, localPoint.X, localPoint.Y ); + if ( _opacity >= 4 && _opacity <= 7 ) { + return null; + } + } return control; } From 3c8e9eed121107b9b6941507f2bf7d2c852dcf68 Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 30 Jan 2016 13:56:14 +0300 Subject: [PATCH 09/16] Improved code that opens ContextMenu. --- ConsoleFramework/Controls/Control.cs | 38 ++++++-------------- ConsoleFramework/Core/VisualTreeHelper.cs | 43 +++++++++++++++++++++++ ConsoleFramework/Events/EventManager.cs | 40 ++------------------- Examples/Program.cs | 4 +-- 4 files changed, 59 insertions(+), 66 deletions(-) diff --git a/ConsoleFramework/Controls/Control.cs b/ConsoleFramework/Controls/Control.cs index 4fd40cd..069a7b3 100644 --- a/ConsoleFramework/Controls/Control.cs +++ b/ConsoleFramework/Controls/Control.cs @@ -1328,36 +1328,20 @@ protected static void assert( bool assertion ) { /// /// Определяет дочерний элемент, находящийся под курсором мыши, - /// и передаёт на него фокус, если он - Focusable и Visible. + /// и передаёт на него фокус, если он - Focusable. А если нажата правая кнопка мыши и у + /// контрола есть контекстное меню, активизирует его. /// protected void PassFocusToChildUnderPoint( MouseEventArgs args ) { - Control topControl = null; - Control tofocus = null; - Control parent = this; - Control hitTested = null; - do - { - Point position = args.GetPosition(parent); - hitTested = parent.GetTopChildAtPoint(position); - if (null != hitTested) - { - parent = hitTested; - if (hitTested.Visibility == Visibility.Visible) { - topControl = hitTested; - if ( hitTested.Focusable ) { - tofocus = hitTested; - } - } + Control topControl = VisualTreeHelper.FindTopControlUnderMouse( this, args.GetPosition( this ) ); + if ( topControl != null ) { + if ( topControl.Focusable ) { + ConsoleApplication.Instance.FocusManager.SetFocus(this, topControl); } - } while (hitTested != null); - if (tofocus != null) - { - ConsoleApplication.Instance.FocusManager.SetFocus(this, tofocus); - } - if ( topControl != null && args.RightButton == MouseButtonState.Pressed ) { - if ( topControl.ContextMenu != null ) { - var windowsHost = VisualTreeHelper.FindClosestParent(this); - topControl.ContextMenu.OpenMenu(windowsHost, args.GetPosition(windowsHost)); + if ( args.RightButton == MouseButtonState.Pressed ) { + if ( topControl.ContextMenu != null ) { + var windowsHost = VisualTreeHelper.FindClosestParent< WindowsHost >( this ); + topControl.ContextMenu.OpenMenu( windowsHost, args.GetPosition( windowsHost ) ); + } } } } diff --git a/ConsoleFramework/Core/VisualTreeHelper.cs b/ConsoleFramework/Core/VisualTreeHelper.cs index aee5132..0b04f6c 100644 --- a/ConsoleFramework/Core/VisualTreeHelper.cs +++ b/ConsoleFramework/Core/VisualTreeHelper.cs @@ -85,5 +85,48 @@ public static T FindClosestParent< T >( Control control ) where T : Control { if (tmp is T) return (T) tmp; return null; } + + /// + /// Находит самый верхний элемент под указателем мыши с координатами rawPoint. + /// Учитывается прозрачность элементов - если пиксель, куда указывает мышь, отмечен как + /// прозрачный для событий мыши (opacity от 4 до 7), то они будут проходить насквозь, + /// к следующему контролу. Также учитывается видимость элементов - Hidden и Collapsed элементы + /// будут проигнорированы. + /// Так обрабатываются, например, тени окошек и прозрачные места контролов (первый столбец Combobox). + /// + /// Координаты относительно control + /// RootElement для проверки всего визуального дерева. + /// Элемент управления или null, если событие мыши было за границами всех контролов, или + /// если все контролы были прозрачны для событий мыши + public static Control FindTopControlUnderMouse(Control control, Point localPoint) { + if (null == control) throw new ArgumentNullException("control"); + + Point rawPoint = Control.TranslatePoint( control, localPoint, null ); + + if (control.Children.Count != 0) { + IList childrenOrderedByZIndex = control.GetChildrenOrderedByZIndex(); + for (int i = childrenOrderedByZIndex.Count - 1; i >= 0; i--) { + Control child = childrenOrderedByZIndex[i]; + if (Control.HitTest(rawPoint, control, child)) { + Control foundSource = FindTopControlUnderMouse(child, + Control.TranslatePoint( control, localPoint, child )); + if ( null != foundSource ) return foundSource; + } + } + } + Rect controlRect = new Rect(new Point(0, 0), control.RenderSize); + if ( !controlRect.Contains( localPoint ) ) { + return null; + } else { + if ( control.Visibility != Visibility.Visible ) + return null; + int _opacity = ConsoleApplication.Instance.Renderer + .getControlOpacityAt( control, localPoint.X, localPoint.Y ); + if ( _opacity >= 4 && _opacity <= 7 ) { + return null; + } + } + return control; + } } } \ No newline at end of file diff --git a/ConsoleFramework/Events/EventManager.cs b/ConsoleFramework/Events/EventManager.cs index 5ace10f..40202f8 100644 --- a/ConsoleFramework/Events/EventManager.cs +++ b/ConsoleFramework/Events/EventManager.cs @@ -248,7 +248,9 @@ public void ParseInputEvent(INPUT_RECORD inputRecord, Control rootElement) { // вынуждены сохранять координаты, полученные при предыдущем событии мыши rawPosition = lastMousePosition; } - Control topMost = findSource(rawPosition, rootElement); + + Control topMost = VisualTreeHelper.FindTopControlUnderMouse(rootElement, + Control.TranslatePoint(null, rawPosition, rootElement)); // если мышь захвачена контролом, то события перемещения мыши доставляются только ему, // события, связанные с нажатием мыши - тоже доставляются только ему, вместо того @@ -596,42 +598,6 @@ private bool processRoutedEvent(RoutedEvent routedEvent, RoutedEventArgs args) { return args.Handled; } - /// - /// Находит самый верхний элемент под указателем мыши с координатами rawPoint. - /// Учитывается прозрачность элементов - если пиксель, куда указывает мышь, отмечен как - /// прозрачный для событий мыши (opacity от 4 до 7), то они будут проходить насквозь, - /// к следующему контролу. - /// Так обрабатываются, например, тени окошек и прозрачные места контролов (первый столбец Combobox). - /// - /// - /// RootElement для проверки всего визуального дерева. - /// Элемент управления или null, если событие мыши было за границами всех контролов, или - /// если все контролы были прозрачны для событий мыши - private Control findSource(Point rawPoint, Control control) { - if (control.Children.Count != 0) { - IList childrenOrderedByZIndex = control.GetChildrenOrderedByZIndex(); - for (int i = childrenOrderedByZIndex.Count - 1; i >= 0; i--) { - Control child = childrenOrderedByZIndex[i]; - if (Control.HitTest(rawPoint, control, child)) { - Control foundSource = findSource(rawPoint, child); - if ( null != foundSource ) return foundSource; - } - } - } - Point localPoint = Control.TranslatePoint(null, rawPoint, control); - Rect controlRect = new Rect(new Point(0, 0), control.RenderSize); - if ( !controlRect.Contains( localPoint ) ) { - return null; - } else { - int _opacity = ConsoleApplication.Instance.Renderer - .getControlOpacityAt( control, localPoint.X, localPoint.Y ); - if ( _opacity >= 4 && _opacity <= 7 ) { - return null; - } - } - return control; - } - /// /// Adds specified routed event to event queue. This event will be processed in next pass. /// diff --git a/Examples/Program.cs b/Examples/Program.cs index a4649b1..304b092 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -187,8 +187,8 @@ private static void Main(string[] args) { Title = "Очень длинное название окна", Content = groupBox }); -// windowsHost.Show(window1); -// windowsHost.Show(createdFromXaml); + windowsHost.Show(window1); + windowsHost.Show(createdFromXaml); //textBox.SetFocus(); todo : научиться задавать фокусный элемент до добавления в визуальное дерево //application.TerminalSizeChanged += ( sender, eventArgs ) => { // application.CanvasSize = new Size(eventArgs.Width, eventArgs.Height); From 30d9cba3a176fcc01ca324d1515dbbee54bfcd97 Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Wed, 17 Feb 2016 13:18:40 +0300 Subject: [PATCH 10/16] Corrected context menu behaviour. --- ConsoleFramework/Controls/ContextMenu.cs | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/ConsoleFramework/Controls/ContextMenu.cs b/ConsoleFramework/Controls/ContextMenu.cs index 230b3ed..8a40b58 100644 --- a/ConsoleFramework/Controls/ContextMenu.cs +++ b/ConsoleFramework/Controls/ContextMenu.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Binding.Observables; using ConsoleFramework.Core; +using ConsoleFramework.Events; +using ConsoleFramework.Native; using Xaml; namespace ConsoleFramework.Controls @@ -25,9 +28,58 @@ public bool PopupShadow { set { popupShadow = value; } } + /// + /// Forces all open submenus to be closed. + /// + public void CloseAllSubmenus( ) { + List expandedSubmenus = new List< MenuItem >(); + MenuItem currentItem = ( MenuItem ) this.Items.SingleOrDefault( + item => item is MenuItem && ((MenuItem)item).expanded); + while ( null != currentItem ) { + expandedSubmenus.Add( currentItem ); + currentItem = (MenuItem)currentItem.Items.SingleOrDefault( + item => item is MenuItem && ((MenuItem)item).expanded); + } + expandedSubmenus.Reverse( ); + foreach ( MenuItem expandedSubmenu in expandedSubmenus ) { + expandedSubmenu.Close( ); + } + } + + private WindowsHost windowsHost; + private RoutedEventHandler windowsHostClick; + private KeyEventHandler windowsHostControlKeyPressed; + public void OpenMenu( WindowsHost windowsHost, Point point ) { if ( expanded ) return; + // Вешаем на WindowsHost обработчик события MenuItem.ClickEvent, + // чтобы ловить момент выбора пункта меню в одном из модальных всплывающих окошек + // Дело в том, что эти окошки не являются дочерними элементами контрола Menu, + // а напрямую являются дочерними элементами WindowsHost (т.к. именно он создаёт + // окна). И событие выбора пункта меню из всплывающего окошка может быть поймано + // в WindowsHost, но не в Menu. А нам нужно повесить обработчик, который закроет + // все показанные попапы. + EventManager.AddHandler( windowsHost, MenuItem.ClickEvent, + windowsHostClick = ( sender, args ) => { + CloseAllSubmenus( ); + popup.Close( ); + }, true ); + + EventManager.AddHandler( windowsHost, MenuItem.Popup.ControlKeyPressedEvent, + windowsHostControlKeyPressed = ( sender, args ) => { + CloseAllSubmenus( ); + // + //ConsoleApplication.Instance.FocusManager.SetFocusScope(this); + if ( args.wVirtualKeyCode == VirtualKeys.Right ) + ConsoleApplication.Instance.FocusManager.MoveFocusNext( ); + else if ( args.wVirtualKeyCode == VirtualKeys.Left ) + ConsoleApplication.Instance.FocusManager.MoveFocusPrev( ); + MenuItem focusedItem = ( MenuItem ) this.Items.SingleOrDefault( + item => item is MenuItem && item.HasFocus ); + focusedItem.Expand( ); + } ); + if ( null == popup ) { popup = new MenuItem.Popup( this.Items, this.popupShadow, 0 ); popup.AddHandler( Window.ClosedEvent, new EventHandler( onPopupClosed ) ); @@ -36,11 +88,15 @@ public void OpenMenu( WindowsHost windowsHost, Point point ) { popup.Y = point.Y; windowsHost.ShowModal( popup, true ); expanded = true; + this.windowsHost = windowsHost; } private void onPopupClosed( object sender, EventArgs eventArgs ) { if (!expanded) throw new InvalidOperationException("This shouldn't happen"); expanded = false; + EventManager.RemoveHandler( windowsHost, MenuItem.ClickEvent, windowsHostClick ); + EventManager.RemoveHandler( windowsHost, MenuItem.Popup.ControlKeyPressedEvent, + windowsHostControlKeyPressed ); } } } \ No newline at end of file From d436191d44433785250aff947d520bf29af164b7 Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Wed, 24 Feb 2016 13:00:41 +0300 Subject: [PATCH 11/16] Added context menu to MainMenu example. --- Examples/MainMenu/Program.cs | 43 +++++++++++++++++++++++++++--------- Examples/MainMenu/main.xml | 13 ++++++++--- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/Examples/MainMenu/Program.cs b/Examples/MainMenu/Program.cs index 5bb090f..1c4fb8b 100644 --- a/Examples/MainMenu/Program.cs +++ b/Examples/MainMenu/Program.cs @@ -1,27 +1,50 @@ using System.Collections.Generic; +using System.ComponentModel; using ConsoleFramework; using ConsoleFramework.Controls; using ConsoleFramework.Core; +using ConsoleFramework.Events; namespace Examples.MainMenu { class Program { + // Example of binding menu item to command + private sealed class DataContext : INotifyPropertyChanged + { + public DataContext( ) { + command = new RelayCommand( + parameter => MessageBox.Show( "Information", "Command executed !", result => { } ), + parameter => true ); + } + + private readonly RelayCommand command; + + public ICommand MyCommand { + get { return command; } + } + + public event PropertyChangedEventHandler PropertyChanged; + } + public static void Main( string[ ] args ) { WindowsHost windowsHost = ( WindowsHost ) ConsoleApplication.LoadFromXaml( "Examples.MainMenu.windows-host.xml", null ); - Window mainWindow = ( Window ) ConsoleApplication.LoadFromXaml( "Examples.MainMenu.main.xml", null ); + DataContext dataContext = new DataContext( ); + Window mainWindow = ( Window ) ConsoleApplication.LoadFromXaml( + "Examples.MainMenu.main.xml", dataContext ); windowsHost.Show( mainWindow ); - CheckBox checkBox = mainWindow.FindChildByName("checkbox"); - checkBox.OnClick += ( sender, eventArgs ) => { - eventArgs.Handled = true; - }; + + // Example of direct subscribing to Click event List< Control > menuItems = VisualTreeHelper.FindAllChilds( windowsHost.MainMenu, control => control is MenuItem ); foreach ( Control menuItem in menuItems ) { - ( ( MenuItem ) menuItem ).Click += ( sender, eventArgs ) => { - MessageBox.Show( "", "", result => { - // - } ); - }; + MenuItem item = ( ( MenuItem ) menuItem ); + if ( item.Title == "Go" ) { + item.Click += ( sender, eventArgs ) => { + MessageBox.Show( "", "", result => { + // + } ); + }; + } } ConsoleApplication.Instance.Run( windowsHost ); } diff --git a/Examples/MainMenu/main.xml b/Examples/MainMenu/main.xml index 2631fde..6d3c21d 100644 --- a/Examples/MainMenu/main.xml +++ b/Examples/MainMenu/main.xml @@ -1,6 +1,13 @@ - + + + + + + + + + - - + Press right mouse button to open the Context Menu \ No newline at end of file From 75e61bfa2cf3602f0e994422b3b6221caeca3739 Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sat, 27 Feb 2016 00:49:06 +0300 Subject: [PATCH 12/16] Fixed two bugs in ComboBox and ProgressBar. --- ConsoleFramework/Controls/ComboBox.cs | 3 ++- ConsoleFramework/Controls/ProgressBar.cs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ConsoleFramework/Controls/ComboBox.cs b/ConsoleFramework/Controls/ComboBox.cs index 0e6e0ac..37d3c89 100644 --- a/ConsoleFramework/Controls/ComboBox.cs +++ b/ConsoleFramework/Controls/ComboBox.cs @@ -208,7 +208,8 @@ private void OnKeyDown( object sender, KeyEventArgs args ) { } private void OnMouseDown( object sender, MouseButtonEventArgs mouseButtonEventArgs ) { - openPopup( ); + if ( !opened ) + openPopup( ); } private void OnPopupClosed( object o, EventArgs args ) { diff --git a/ConsoleFramework/Controls/ProgressBar.cs b/ConsoleFramework/Controls/ProgressBar.cs index e59963d..654f783 100644 --- a/ConsoleFramework/Controls/ProgressBar.cs +++ b/ConsoleFramework/Controls/ProgressBar.cs @@ -1,4 +1,5 @@ -using ConsoleFramework.Core; +using System; +using ConsoleFramework.Core; using ConsoleFramework.Native; using ConsoleFramework.Rendering; @@ -25,7 +26,7 @@ public override void Render( RenderingBuffer buffer ) { Attr attr = Colors.Blend( Color.DarkCyan, Color.DarkBlue ); buffer.FillRectangle(0, 0, ActualWidth, ActualHeight, UnicodeTable.MediumShade, attr); int filled = ( int ) ( ActualWidth*( Percent*0.01 ) ); - buffer.FillRectangle(0, 0, filled, ActualHeight, UnicodeTable.DarkShade, attr); + buffer.FillRectangle(0, 0, Math.Min( filled, ActualWidth ), ActualHeight, UnicodeTable.DarkShade, attr); } } } From 581e2c3d046a27ecfa06789402619542a330f974 Mon Sep 17 00:00:00 2001 From: Igor Kostromin Date: Sun, 28 Feb 2016 17:34:46 +0300 Subject: [PATCH 13/16] + Added TabControl. #28 --- ConsoleFramework/ConsoleFramework.csproj | 1 + ConsoleFramework/Controls/Panel.cs | 1 - ConsoleFramework/Controls/TabControl.cs | 208 ++++++++++++++++++ ConsoleFramework/Rendering/RenderingBuffer.cs | 21 ++ Examples/Examples.csproj | 6 +- Examples/TabControl/Program.cs | 15 ++ Examples/TabControl/main.xml | 20 ++ 7 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 ConsoleFramework/Controls/TabControl.cs create mode 100644 Examples/TabControl/Program.cs create mode 100644 Examples/TabControl/main.xml diff --git a/ConsoleFramework/ConsoleFramework.csproj b/ConsoleFramework/ConsoleFramework.csproj index d734f2f..885c97a 100644 --- a/ConsoleFramework/ConsoleFramework.csproj +++ b/ConsoleFramework/ConsoleFramework.csproj @@ -70,6 +70,7 @@ + diff --git a/ConsoleFramework/Controls/Panel.cs b/ConsoleFramework/Controls/Panel.cs index ea2e08a..b54050d 100644 --- a/ConsoleFramework/Controls/Panel.cs +++ b/ConsoleFramework/Controls/Panel.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using ConsoleFramework.Core; using ConsoleFramework.Native; using ConsoleFramework.Rendering; diff --git a/ConsoleFramework/Controls/TabControl.cs b/ConsoleFramework/Controls/TabControl.cs new file mode 100644 index 0000000..87751ec --- /dev/null +++ b/ConsoleFramework/Controls/TabControl.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ConsoleFramework.Core; +using ConsoleFramework.Events; +using ConsoleFramework.Native; +using ConsoleFramework.Rendering; +using Xaml; + +namespace ConsoleFramework.Controls +{ + public class TabDefinition + { + public string Title { get; set; } + } + + /// + /// Control that presents a tabbed layout. + /// + [ContentProperty( "Controls" )] + public class TabControl : Control + { + private readonly List< TabDefinition > tabDefinitions = new List< TabDefinition >( ); + private readonly UIElementCollection controls; + + public TabControl( ) { + controls = new UIElementCollection( this ); + AddHandler(MouseDownEvent, new MouseButtonEventHandler(mouseDown)); + } + + private void mouseDown( object sender, MouseButtonEventArgs args ) { + Point pos = args.GetPosition( this ); + if ( pos.y > 2 ) return; + + int x = 0; + for ( int i = 0; i < tabDefinitions.Count; i++ ) { + TabDefinition tabDefinition = tabDefinitions[ i ]; + if ( pos.X > x && pos.X <= x + tabDefinition.Title.Length + 2 ) { + activeTabIndex = i; + Invalidate( ); + break; + } + x += tabDefinition.Title.Length + 2 + 1; // Two spaces around + one vertical border + } + args.Handled = true; + } + + public List< TabDefinition > TabDefinitions { + get { return tabDefinitions; } + } + + public UIElementCollection Controls { + get { return controls; } + } + + private int activeTabIndex; + + public int ActiveTabIndex { + get { return activeTabIndex; } + set { + if ( value != activeTabIndex ) { + if (value < 0 || value >= tabDefinitions.Count) + throw new ArgumentException("Tab index out of bounds"); + activeTabIndex = value; + Invalidate( ); + } + } + } + + protected override Size MeasureOverride( Size availableSize ) { + // Get children max desired size to determine the tab content desired size + Size childrenAvailableSize = new Size( + Math.Max( availableSize.Width - 2, 0 ), + Math.Max( availableSize.Height - 4, 0 ) ); + int maxDesiredWidth = 0; + int maxDesiredHeight = 0; + for ( int i = 0; i < Math.Min( Children.Count, tabDefinitions.Count ); i++ ) { + Control child = Children[ i ]; + child.Measure( childrenAvailableSize ); + if ( child.DesiredSize.Width > maxDesiredWidth ) maxDesiredWidth = child.DesiredSize.Width; + if ( child.DesiredSize.Height > maxDesiredHeight ) maxDesiredHeight = child.DesiredSize.Height; + } + + // Get tab header desired size + var tabHeaderWidth = getTabHeaderWidth( ); + + // Calculate final size = min(availableSize, controlWithChildrenDesiredSize) + Size controlWithChildrenDesiredSize = new Size( + Math.Max( maxDesiredWidth + 2, tabHeaderWidth ), + maxDesiredHeight + 4 + ); + Size finalAvailableSize = new Size( + Math.Min( availableSize.Width, controlWithChildrenDesiredSize.Width ), + Math.Min( availableSize.Height, controlWithChildrenDesiredSize.Height ) + ); + + // Invoke children.Measure() with final size + for ( int i = 0; i < Children.Count; i++ ) { + Control child = Children[ i ]; + if ( activeTabIndex == i ) { + child.Measure( new Size( + Math.Max( 0, finalAvailableSize.Width - 2 ), + Math.Max( 0, finalAvailableSize.Height - 4 ) + ) ); + } else { + child.Measure( Size.Empty ); + } + } + + return finalAvailableSize; + } + + private int getTabHeaderWidth( ) { + // Two spaces around + one vertical border per tab, plus extra one vertical border + return 1 + tabDefinitions.Sum( tabDefinition => tabDefinition.Title.Length + 2 + 1 ); + } + + protected override Size ArrangeOverride( Size finalSize ) { + for ( int i = 0; i < Children.Count; i++ ) { + Control child = Children[ i ]; + if ( activeTabIndex == i ) { + child.Arrange( new Rect( + new Point( 1, 3 ), + new Size( Math.Max( 0, finalSize.Width - 2 ), + Math.Max( 0, finalSize.Height - 4 ) + ) ) ); + } else { + child.Arrange( Rect.Empty ); + } + } + return finalSize; + } + + private void renderBorderSafe( RenderingBuffer buffer, int x, int y, int x2, int y2 ) { + if ( ActualWidth > x && ActualHeight > y ) + buffer.SetPixel( x, y, UnicodeTable.SingleFrameTopLeftCorner ); + if ( ActualWidth > x && ActualHeight > y2 && y2 > y ) + buffer.SetPixel( x, y2, UnicodeTable.SingleFrameBottomLeftCorner ); + if ( ActualHeight > y ) + for ( int i = x + 1; i <= Math.Min( x2 - 1, ActualWidth - 1 ); i++ ) + buffer.SetPixel( i, y, UnicodeTable.SingleFrameHorizontal ); + if ( ActualHeight > y2 ) + for ( int i = x + 1; i <= Math.Min( x2 - 1, ActualWidth - 1 ); i++ ) + buffer.SetPixel( i, y2, UnicodeTable.SingleFrameHorizontal ); + if ( ActualWidth > x ) + for ( int j = y + 1; j <= Math.Min( y2 - 1, ActualHeight - 1 ); j++ ) + buffer.SetPixel( x, j, UnicodeTable.SingleFrameVertical ); + if ( ActualWidth > x2 ) + for ( int j = y + 1; j <= Math.Min( y2 - 1, ActualHeight - 1 ); j++ ) + buffer.SetPixel( x2, j, UnicodeTable.SingleFrameVertical ); + if ( ActualWidth > x2 && ActualHeight > y && x2 > x ) + buffer.SetPixel( x2, y, UnicodeTable.SingleFrameTopRightCorner ); + if ( ActualWidth > x2 && ActualHeight > y2 && y2 > y && x2 > x ) + buffer.SetPixel( x2, y2, UnicodeTable.SingleFrameBottomRightCorner ); + } + + public override void Render( RenderingBuffer buffer ) { + Attr attr = Colors.Blend( Color.Black, Color.DarkGreen ); + Attr inactiveAttr = Colors.Blend( Color.DarkGray, Color.DarkGreen ); + + // Transparent background for borders + buffer.SetOpacityRect( 0, 0, ActualWidth, ActualHeight, 3 ); + + buffer.FillRectangle( 0, 0, ActualWidth, ActualHeight, ' ', attr ); + + // Transparent child content part + if ( ActualWidth > 2 && ActualHeight > 3 ) + buffer.SetOpacityRect( 1, 3, ActualWidth - 2, ActualHeight - 4, 2 ); + + renderBorderSafe( buffer, 0, 2, Math.Max( getTabHeaderWidth( ) - 1, ActualWidth - 1 ), ActualHeight - 1 ); + + // Start to render header + buffer.FillRectangle( 0, 0, ActualWidth, Math.Min( 2, ActualHeight ), ' ', attr ); + + int x = 0; + + // Render tabs before active tab + for ( int tab = 0; tab < tabDefinitions.Count; x += TabDefinitions[ tab++ ].Title.Length + 3 ) { + var tabDefinition = TabDefinitions[ tab ]; + if ( tab <= activeTabIndex ) { + buffer.SetPixelSafe( x, 0, UnicodeTable.SingleFrameTopLeftCorner ); + buffer.SetPixelSafe( x, 1, UnicodeTable.SingleFrameVertical ); + } + if ( tab == activeTabIndex ) { + buffer.SetPixelSafe( x, 2, + activeTabIndex == 0 ? UnicodeTable.SingleFrameVertical : UnicodeTable.SingleFrameBottomRightCorner ); + } + for ( int i = 0; i < tabDefinition.Title.Length + 2; i++ ) { + buffer.SetPixelSafe( x + 1 + i, 0, UnicodeTable.SingleFrameHorizontal ); + if ( tab == activeTabIndex ) + buffer.SetPixelSafe( x + 1 + i, 2, ' ' ); + } + buffer.RenderStringSafe( " " + tabDefinition.Title + " ", x + 1, 1, + activeTabIndex == tab ? attr : inactiveAttr ); + if ( tab >= activeTabIndex ) { + buffer.SetPixelSafe( x + tabDefinition.Title.Length + 3, 0, UnicodeTable.SingleFrameTopRightCorner ); + buffer.SetPixelSafe( x + tabDefinition.Title.Length + 3, 1, UnicodeTable.SingleFrameVertical ); + } + if ( tab == activeTabIndex ) { + buffer.SetPixelSafe( x + tabDefinition.Title.Length + 3, 2, + activeTabIndex == tabDefinitions.Count - 1 && ActualWidth - 1 == x + tabDefinition.Title.Length + 3 + ? UnicodeTable.SingleFrameVertical + : UnicodeTable.SingleFrameBottomLeftCorner ); + } + } + } + } +} \ No newline at end of file diff --git a/ConsoleFramework/Rendering/RenderingBuffer.cs b/ConsoleFramework/Rendering/RenderingBuffer.cs index ee6255a..8d67181 100644 --- a/ConsoleFramework/Rendering/RenderingBuffer.cs +++ b/ConsoleFramework/Rendering/RenderingBuffer.cs @@ -175,6 +175,21 @@ public void ApplyChild(RenderingBuffer childBuffer, Vector actualOffset, } } + public void SetPixelSafe( int x, int y, char c ) { + if (buffer.GetLength( 0 ) > x && buffer.GetLength( 1 ) > y) + SetPixel( x, y, c ); + } + + public void SetPixelSafe(int x, int y, Attr attr) { + if (buffer.GetLength(0) > x && buffer.GetLength(1) > y) + SetPixel(x, y, attr); + } + + public void SetPixelSafe(int x, int y, char c, Attr attr) { + if (buffer.GetLength(0) > x && buffer.GetLength(1) > y) + SetPixel(x, y, c, attr); + } + public void SetPixel(int x, int y, char c) { buffer[x, y].UnicodeChar = c; } @@ -285,5 +300,11 @@ public bool ContainsOpacity(Rect affectedRect) { public int GetOpacityAt( int x, int y ) { return opacityMatrix[ x, y ]; } + + public void RenderStringSafe( string s, int x, int y, Attr attr ) { + for ( int i = 0; i < s.Length; i++ ) { + SetPixelSafe( x + i, y, s[i], attr ); + } + } } } diff --git a/Examples/Examples.csproj b/Examples/Examples.csproj index 6973a34..2a8a4dc 100644 --- a/Examples/Examples.csproj +++ b/Examples/Examples.csproj @@ -35,7 +35,7 @@ 4 - Examples.Program + Examples.TabControl.Program true @@ -68,6 +68,7 @@ + @@ -131,6 +132,9 @@ + + +