diff --git a/Cairo/Perspex.Cairo/Media/FormattedTextImpl.cs b/Cairo/Perspex.Cairo/Media/FormattedTextImpl.cs index 26dd56363c..873ac450be 100644 --- a/Cairo/Perspex.Cairo/Media/FormattedTextImpl.cs +++ b/Cairo/Perspex.Cairo/Media/FormattedTextImpl.cs @@ -98,5 +98,10 @@ namespace Perspex.Cairo.Media this.Layout.GetPixelSize(out width, out height); return new Size(width, height); } + + public void SetForegroundBrush(Brush brush, int startIndex, int count) + { + // TODO: Implement. + } } } diff --git a/Perspex.Controls/TextBlock.cs b/Perspex.Controls/TextBlock.cs index e1911ba152..32a8bfaee3 100644 --- a/Perspex.Controls/TextBlock.cs +++ b/Perspex.Controls/TextBlock.cs @@ -43,13 +43,7 @@ namespace Perspex.Controls this.GetObservable(FontStyleProperty).Select(_ => Unit.Default)) .Subscribe(_ => { - if (this.formattedText != null) - { - this.formattedText.Dispose(); - this.formattedText = null; - } - - this.InvalidateMeasure(); + this.InvalidateFormattedText(); }); } @@ -89,18 +83,13 @@ namespace Perspex.Controls { if (this.formattedText == null) { - this.formattedText = new FormattedText( - this.Text, - this.FontFamily, - this.FontSize, - this.FontStyle); + this.formattedText = this.CreateFormattedText(); } return this.formattedText; } } - public override void Render(IDrawingContext context) { Brush background = this.Background; @@ -113,6 +102,26 @@ namespace Perspex.Controls context.DrawText(this.Foreground, new Point(), this.FormattedText); } + protected virtual FormattedText CreateFormattedText() + { + return new FormattedText( + this.Text, + this.FontFamily, + this.FontSize, + this.FontStyle); + } + + protected void InvalidateFormattedText() + { + if (this.formattedText != null) + { + this.formattedText.Dispose(); + this.formattedText = null; + } + + this.InvalidateMeasure(); + } + protected override Size MeasureOverride(Size availableSize) { if (!string.IsNullOrEmpty(this.Text)) diff --git a/Perspex.Controls/TextBoxView.cs b/Perspex.Controls/TextBoxView.cs index 15ccc0fbfd..30d94afde4 100644 --- a/Perspex.Controls/TextBoxView.cs +++ b/Perspex.Controls/TextBoxView.cs @@ -31,7 +31,7 @@ namespace Perspex.Controls Observable.Merge( this.parent.GetObservable(TextBox.SelectionStartProperty), this.parent.GetObservable(TextBox.SelectionEndProperty)) - .Subscribe(_ => this.InvalidateVisual()); + .Subscribe(_ => this.InvalidateFormattedText()); parent.GetObservable(TextBox.CaretIndexProperty).Subscribe(_ => this.CaretMoved()); } @@ -95,6 +95,21 @@ namespace Perspex.Controls } } + protected override FormattedText CreateFormattedText() + { + var result = base.CreateFormattedText(); + var selectionStart = this.parent.SelectionStart; + var selectionEnd = this.parent.SelectionEnd; + var start = Math.Min(selectionStart, selectionEnd); + var length = Math.Max(selectionStart, selectionEnd) - start; + + if (length > 0) + { + result.SetForegroundBrush(Brushes.White, start, length); + } + return result; + } + internal void CaretMoved() { this.caretBlink = true; diff --git a/Perspex.SceneGraph/Media/FormattedText.cs b/Perspex.SceneGraph/Media/FormattedText.cs index af1f9ff030..4db72f576f 100644 --- a/Perspex.SceneGraph/Media/FormattedText.cs +++ b/Perspex.SceneGraph/Media/FormattedText.cs @@ -93,5 +93,10 @@ namespace Perspex.Media { return this.PlatformImpl.Measure(); } + + public void SetForegroundBrush(Brush brush, int startIndex, int count) + { + this.PlatformImpl.SetForegroundBrush(brush, startIndex, count); + } } } diff --git a/Perspex.SceneGraph/Platform/IFormattedTextImpl.cs b/Perspex.SceneGraph/Platform/IFormattedTextImpl.cs index e69e65858f..eb1db49959 100644 --- a/Perspex.SceneGraph/Platform/IFormattedTextImpl.cs +++ b/Perspex.SceneGraph/Platform/IFormattedTextImpl.cs @@ -23,5 +23,7 @@ namespace Perspex.Platform IEnumerable HitTestTextRange(int index, int length, Point origin); Size Measure(); + + void SetForegroundBrush(Brush brush, int startIndex, int count); } } diff --git a/Windows/Perspex.Direct2D1/Media/BrushWrapper.cs b/Windows/Perspex.Direct2D1/Media/BrushWrapper.cs new file mode 100644 index 0000000000..43f8e5c21e --- /dev/null +++ b/Windows/Perspex.Direct2D1/Media/BrushWrapper.cs @@ -0,0 +1,15 @@ +namespace Perspex.Direct2D1.Media +{ + using Perspex.Media; + using SharpDX; + + internal class BrushWrapper : ComObject + { + public BrushWrapper(Brush brush) + { + this.Brush = brush; + } + + public Brush Brush { get; private set; } + } +} diff --git a/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index 9d2db473dc..2279dc6cd2 100644 --- a/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -8,7 +8,6 @@ namespace Perspex.Direct2D1.Media { using System; using System.Reactive.Disposables; - using Perspex.Direct2D1.Media; using Perspex.Media; using SharpDX; using SharpDX.Direct2D1; @@ -142,12 +141,9 @@ namespace Perspex.Direct2D1.Media { var impl = (FormattedTextImpl)text.PlatformImpl; - using (var brush = foreground.ToDirect2D(this.renderTarget)) + using (var renderer = new PerspexTextRenderer(this.renderTarget, foreground.ToDirect2D(this.renderTarget))) { - this.renderTarget.DrawTextLayout( - origin.ToSharpDX(), - impl.TextLayout, - brush); + impl.TextLayout.Draw(renderer, (float)origin.X, (float)origin.Y); } } } diff --git a/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs b/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs index 614eaab374..157051a743 100644 --- a/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs +++ b/Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs @@ -113,5 +113,12 @@ namespace Perspex.Direct2D1.Media this.TextLayout.Metrics.WidthIncludingTrailingWhitespace, this.TextLayout.Metrics.Height); } + + public void SetForegroundBrush(Brush brush, int startIndex, int count) + { + this.TextLayout.SetDrawingEffect( + new BrushWrapper(brush), + new DWrite.TextRange(startIndex, count)); + } } } diff --git a/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs b/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs new file mode 100644 index 0000000000..f1f02cb1c3 --- /dev/null +++ b/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs @@ -0,0 +1,97 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Direct2D1.Media +{ + using System; + using SharpDX; + using SharpDX.Direct2D1; + using SharpDX.DirectWrite; + + internal class PerspexTextRenderer : TextRenderer + { + private RenderTarget renderTarget; + + private Brush foreground; + + public PerspexTextRenderer( + RenderTarget target, + Brush foreground) + { + this.renderTarget = target; + this.foreground = foreground; + } + + public IDisposable Shadow + { + get; + set; + } + + public void Dispose() + { + this.foreground.Dispose(); + } + + public Result DrawGlyphRun( + object clientDrawingContext, + float baselineOriginX, + float baselineOriginY, + MeasuringMode measuringMode, + GlyphRun glyphRun, + GlyphRunDescription glyphRunDescription, + ComObject clientDrawingEffect) + { + var wrapper = clientDrawingEffect as BrushWrapper; + var brush = (wrapper == null) ? + this.foreground : + wrapper.Brush.ToDirect2D(this.renderTarget); + + this.renderTarget.DrawGlyphRun( + new Vector2(baselineOriginX, baselineOriginY), + glyphRun, + brush, + measuringMode); + + if (wrapper != null) + { + brush.Dispose(); + } + + return Result.Ok; + } + + public Result DrawInlineObject(object clientDrawingContext, float originX, float originY, InlineObject inlineObject, bool isSideways, bool isRightToLeft, ComObject clientDrawingEffect) + { + throw new NotImplementedException(); + } + + public Result DrawStrikethrough(object clientDrawingContext, float baselineOriginX, float baselineOriginY, ref Strikethrough strikethrough, ComObject clientDrawingEffect) + { + throw new NotImplementedException(); + } + + public Result DrawUnderline(object clientDrawingContext, float baselineOriginX, float baselineOriginY, ref Underline underline, ComObject clientDrawingEffect) + { + throw new NotImplementedException(); + } + + public Matrix3x2 GetCurrentTransform(object clientDrawingContext) + { + return this.renderTarget.Transform; + } + + public float GetPixelsPerDip(object clientDrawingContext) + { + return this.renderTarget.DotsPerInch.Width / 96; + } + + public bool IsPixelSnappingDisabled(object clientDrawingContext) + { + return false; + } + } +} diff --git a/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj index 6985f9c11e..2cb46de7b2 100644 --- a/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj +++ b/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj @@ -67,9 +67,11 @@ + +