From cfbc5d19f6f95c444ddf95b2c543c40d02c7c2f8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 26 Jun 2014 22:14:33 +0200 Subject: [PATCH] Display (not-yet-blinking) caret in TextBox. --- Perspex.Direct2D1/Direct2D1Platform.cs | 10 ++--- Perspex.Direct2D1/Media/DrawingContext.cs | 19 +++++++++- Perspex.Direct2D1/PrimitiveExtensions.cs | 5 --- Perspex.Direct2D1/TextService.cs | 40 ++++++++++++++++++-- Perspex.Windows/Input/WindowsMouseDevice.cs | 10 +++++ Perspex/Controls/TextBox.cs | 8 ++-- Perspex/Controls/TextBoxView.cs | 42 ++++++++------------- Perspex/Input/IPointerDevice.cs | 2 + Perspex/Input/MouseDevice.cs | 16 ++++++++ Perspex/Input/PointerEventArgs.cs | 5 +++ Perspex/Media/FormattedText.cs | 3 +- Perspex/Media/IDrawingContext.cs | 8 ++++ Perspex/Platform/IPlatformInterface.cs | 4 +- Perspex/Platform/ITextService.cs | 4 ++ Perspex/Themes/Default/TextBoxStyle.cs | 1 + 15 files changed, 129 insertions(+), 48 deletions(-) diff --git a/Perspex.Direct2D1/Direct2D1Platform.cs b/Perspex.Direct2D1/Direct2D1Platform.cs index fd5e9bd376..b280a31ba6 100644 --- a/Perspex.Direct2D1/Direct2D1Platform.cs +++ b/Perspex.Direct2D1/Direct2D1Platform.cs @@ -32,6 +32,11 @@ namespace Perspex.Direct2D1 locator.Register(() => imagingFactory, typeof(SharpDX.WIC.ImagingFactory)); } + public ITextService TextService + { + get { return textService; } + } + public IBitmapImpl CreateBitmap(int width, int height) { return new BitmapImpl(imagingFactory, width, height); @@ -51,10 +56,5 @@ namespace Perspex.Direct2D1 { return new StreamGeometryImpl(); } - - public ITextService GetTextService() - { - return textService; - } } } diff --git a/Perspex.Direct2D1/Media/DrawingContext.cs b/Perspex.Direct2D1/Media/DrawingContext.cs index 17b20e1d70..00fb0bdaae 100644 --- a/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/Perspex.Direct2D1/Media/DrawingContext.cs @@ -51,6 +51,23 @@ namespace Perspex.Direct2D1.Media this.renderTarget.EndDraw(); } + /// + /// Draws a line. + /// + /// The stroke pen. + /// The first point of the line. + /// The second point of the line. + public void DrawLine(Pen pen, Perspex.Point p1, Perspex.Point p2) + { + if (pen != null) + { + using (SharpDX.Direct2D1.SolidColorBrush d2dBrush = this.Convert(pen.Brush)) + { + this.renderTarget.DrawLine(p1.ToSharpDX(), p2.ToSharpDX(), d2dBrush); + } + } + } + /// /// Draws a geometry. /// @@ -105,7 +122,7 @@ namespace Perspex.Direct2D1.Media if (!string.IsNullOrEmpty(text.Text)) { using (SharpDX.Direct2D1.SolidColorBrush brush = this.Convert(foreground)) - using (SharpDX.DirectWrite.TextFormat format = TextService.Convert(this.directWriteFactory, text)) + using (SharpDX.DirectWrite.TextFormat format = TextService.GetTextFormat(this.directWriteFactory, text)) { this.renderTarget.DrawText( text.Text, diff --git a/Perspex.Direct2D1/PrimitiveExtensions.cs b/Perspex.Direct2D1/PrimitiveExtensions.cs index a7b01ee5db..d3469d78a0 100644 --- a/Perspex.Direct2D1/PrimitiveExtensions.cs +++ b/Perspex.Direct2D1/PrimitiveExtensions.cs @@ -6,11 +6,6 @@ namespace Perspex.Direct2D1 { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; using SharpDX; public static class PrimitiveExtensions diff --git a/Perspex.Direct2D1/TextService.cs b/Perspex.Direct2D1/TextService.cs index fc03940292..948d9efef2 100644 --- a/Perspex.Direct2D1/TextService.cs +++ b/Perspex.Direct2D1/TextService.cs @@ -6,6 +6,7 @@ namespace Perspex.Direct2D1 { + using System; using Perspex.Media; using Perspex.Platform; using SharpDX.DirectWrite; @@ -19,7 +20,7 @@ namespace Perspex.Direct2D1 this.factory = factory; } - public static TextFormat Convert(Factory factory, FormattedText text) + public static TextFormat GetTextFormat(Factory factory, FormattedText text) { return new TextFormat( factory, @@ -27,10 +28,43 @@ namespace Perspex.Direct2D1 (float)text.FontSize); } + public TextLayout GetTextLayout(Factory factory, FormattedText text) + { + return new TextLayout( + factory, + text.Text, + GetTextFormat(factory, text), + float.MaxValue, + float.MaxValue); + } + + public int GetCaretIndex(FormattedText text, Point point) + { + TextLayout layout = GetTextLayout(this.factory, text); + SharpDX.Bool isTrailingHit; + SharpDX.Bool isInside; + + HitTestMetrics result = layout.HitTestPoint( + (float)point.X, + (float)point.Y, + out isTrailingHit, + out isInside); + + return result.TextPosition + (isTrailingHit ? 1 : 0); + } + + public Point GetCaretPosition(FormattedText text, int caretIndex) + { + TextLayout layout = GetTextLayout(this.factory, text); + float x; + float y; + layout.HitTestTextPosition(caretIndex, false, out x, out y); + return new Point(x, y); + } + public Size Measure(FormattedText text) { - TextFormat f = Convert(this.factory, text); - TextLayout layout = new TextLayout(this.factory, text.Text, f, float.MaxValue, float.MaxValue); + TextLayout layout = GetTextLayout(this.factory, text); return new Size( layout.Metrics.WidthIncludingTrailingWhitespace, layout.Metrics.Height); diff --git a/Perspex.Windows/Input/WindowsMouseDevice.cs b/Perspex.Windows/Input/WindowsMouseDevice.cs index cc675e5975..3ff7ef5de8 100644 --- a/Perspex.Windows/Input/WindowsMouseDevice.cs +++ b/Perspex.Windows/Input/WindowsMouseDevice.cs @@ -6,7 +6,9 @@ namespace Perspex.Windows.Input { + using System; using Perspex.Input; + using Perspex.Windows.Interop; public class WindowsMouseDevice : MouseDevice { @@ -28,6 +30,14 @@ namespace Perspex.Windows.Input get { return base.Position; } internal set { base.Position = value; } } + + protected override Point GetClientPosition() + { + UnmanagedMethods.POINT p; + UnmanagedMethods.GetCursorPos(out p); + UnmanagedMethods.ScreenToClient(this.CurrentWindow.Handle, ref p); + return new Point(p.X, p.Y); + } } } diff --git a/Perspex/Controls/TextBox.cs b/Perspex/Controls/TextBox.cs index c190435292..8b3e5a2417 100644 --- a/Perspex/Controls/TextBox.cs +++ b/Perspex/Controls/TextBox.cs @@ -123,10 +123,10 @@ namespace Perspex.Controls private void OnPointerPressed(object sender, PointerEventArgs e) { - //IPlatformInterface platform = Locator.Current.GetService(); - //this.CaretIndex = platform.GetTextService().GetCaretIndex( - // this.textBoxView.FormattedText, - // e.GetPosition(this.textBoxView)); + IPlatformInterface platform = Locator.Current.GetService(); + this.CaretIndex = platform.TextService.GetCaretIndex( + this.textBoxView.FormattedText, + e.GetPosition(this.textBoxView)); } } } diff --git a/Perspex/Controls/TextBoxView.cs b/Perspex/Controls/TextBoxView.cs index 4bdfcc7999..7668c8f4cf 100644 --- a/Perspex/Controls/TextBoxView.cs +++ b/Perspex/Controls/TextBoxView.cs @@ -9,6 +9,8 @@ namespace Perspex.Controls using System; using System.Globalization; using Perspex.Media; + using Perspex.Platform; + using Splat; internal class TextBoxView : Control { @@ -73,32 +75,20 @@ namespace Perspex.Controls context.DrawText(Brushes.Black, rect, this.FormattedText); - //if (this.parent.IsKeyboardFocused) - //{ - // Point caretPos = this.FormattedText.GetCaretPosition(this.parent.CaretIndex); - // Brush caretBrush = this.parent.CaretBrush; - - // if (caretBrush == null) - // { - // Color color = Colors.Black; - // SolidColorBrush background = this.parent.Background as SolidColorBrush; - - // if (background != null) - // { - // color = Color.FromUInt32(0x00ffffffu ^ background.Color.ToUint32()); - // } - - // caretBrush = new SolidColorBrush(color); - // } - - // if (this.caretBlink) - // { - // drawingContext.DrawLine( - // new Pen(caretBrush, 1), - // caretPos, - // caretPos + new Vector(0, this.FormattedText.Height)); - // } - //} + if (this.parent.IsFocused) + { + IPlatformInterface platform = Locator.Current.GetService(); + Point caretPos = platform.TextService.GetCaretPosition(this.formattedText, this.parent.CaretIndex); + Brush caretBrush = Brushes.Black; + + //if (!this.caretBlink) + { + context.DrawLine( + new Pen(caretBrush, 1), + caretPos, + new Point(caretPos.X, caretPos.Y + this.FormattedText.Size.Height)); + } + } } protected override Size MeasureOverride(Size constraint) diff --git a/Perspex/Input/IPointerDevice.cs b/Perspex/Input/IPointerDevice.cs index ed93439f6d..338b325c56 100644 --- a/Perspex/Input/IPointerDevice.cs +++ b/Perspex/Input/IPointerDevice.cs @@ -13,5 +13,7 @@ namespace Perspex.Input Interactive Captured { get; } void Capture(Interactive visual); + + Point GetPosition(IVisual relativeTo); } } diff --git a/Perspex/Input/MouseDevice.cs b/Perspex/Input/MouseDevice.cs index 9eaf573b82..54d986ca18 100644 --- a/Perspex/Input/MouseDevice.cs +++ b/Perspex/Input/MouseDevice.cs @@ -45,6 +45,22 @@ namespace Perspex.Input this.Captured = visual; } + public Point GetPosition(IVisual relativeTo) + { + Point p = this.GetClientPosition(); + IVisual v = relativeTo; + + while (v != null) + { + p -= v.Bounds.Position; + v = v.VisualParent; + } + + return p; + } + + protected abstract Point GetClientPosition(); + private void ProcessRawEvent(RawMouseEventArgs e) { this.Position = e.Position; diff --git a/Perspex/Input/PointerEventArgs.cs b/Perspex/Input/PointerEventArgs.cs index 056328f5a1..4417c59949 100644 --- a/Perspex/Input/PointerEventArgs.cs +++ b/Perspex/Input/PointerEventArgs.cs @@ -11,5 +11,10 @@ namespace Perspex.Input public class PointerEventArgs : RoutedEventArgs { public IPointerDevice Device { get; set; } + + public Point GetPosition(IVisual relativeTo) + { + return this.Device.GetPosition(relativeTo); + } } } diff --git a/Perspex/Media/FormattedText.cs b/Perspex/Media/FormattedText.cs index 12a1cd1b1d..e1b890f87f 100644 --- a/Perspex/Media/FormattedText.cs +++ b/Perspex/Media/FormattedText.cs @@ -22,8 +22,7 @@ namespace Perspex.Media get { IPlatformInterface factory = Locator.Current.GetService(); - ITextService service = factory.GetTextService(); - return service.Measure(this); + return factory.TextService.Measure(this); } } } diff --git a/Perspex/Media/IDrawingContext.cs b/Perspex/Media/IDrawingContext.cs index 92a001169a..c22fbfe172 100644 --- a/Perspex/Media/IDrawingContext.cs +++ b/Perspex/Media/IDrawingContext.cs @@ -13,6 +13,14 @@ namespace Perspex.Media /// public interface IDrawingContext : IDisposable { + /// + /// Draws a line. + /// + /// The stroke pen. + /// The first point of the line. + /// The second point of the line. + void DrawLine(Pen pen, Point p1, Point p2); + /// /// Draws a geometry. /// diff --git a/Perspex/Platform/IPlatformInterface.cs b/Perspex/Platform/IPlatformInterface.cs index 5138df4846..0964ec3ad7 100644 --- a/Perspex/Platform/IPlatformInterface.cs +++ b/Perspex/Platform/IPlatformInterface.cs @@ -10,6 +10,8 @@ namespace Perspex.Platform public interface IPlatformInterface { + ITextService TextService { get; } + IBitmapImpl CreateBitmap(int width, int height); IStreamGeometryImpl CreateStreamGeometry(); @@ -17,7 +19,5 @@ namespace Perspex.Platform IRenderer CreateRenderer(IntPtr handle, double width, double height); IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height); - - ITextService GetTextService(); } } diff --git a/Perspex/Platform/ITextService.cs b/Perspex/Platform/ITextService.cs index 0e6e36e745..d9e05ce4c6 100644 --- a/Perspex/Platform/ITextService.cs +++ b/Perspex/Platform/ITextService.cs @@ -11,5 +11,9 @@ namespace Perspex.Platform public interface ITextService { Size Measure(FormattedText text); + + int GetCaretIndex(FormattedText text, Point point); + + Point GetCaretPosition(FormattedText text, int caretIndex); } } diff --git a/Perspex/Themes/Default/TextBoxStyle.cs b/Perspex/Themes/Default/TextBoxStyle.cs index 7b36d60a18..574eae6261 100644 --- a/Perspex/Themes/Default/TextBoxStyle.cs +++ b/Perspex/Themes/Default/TextBoxStyle.cs @@ -43,6 +43,7 @@ namespace Perspex.Themes.Default Border result = new Border { Id = "border", + Padding = new Thickness(2), [~Border.BackgroundProperty] = control[~TextBox.BackgroundProperty], [~Border.BorderBrushProperty] = control[~TextBox.BorderBrushProperty], [~Border.BorderThicknessProperty] = control[~TextBox.BorderThicknessProperty],