Browse Source

More refactoring of FormattedText.

pull/10/head
Steven Kirk 12 years ago
parent
commit
2be668db1a
  1. 71
      Perspex.Controls/TextBlock.cs
  2. 5
      Perspex.Controls/TextBox.cs
  3. 51
      Perspex.Controls/TextBoxView.cs
  4. 15
      Perspex.SceneGraph/Media/FontStyle.cs
  5. 45
      Perspex.SceneGraph/Media/FormattedText.cs
  6. 4
      Perspex.SceneGraph/Media/IDrawingContext.cs
  7. 1
      Perspex.SceneGraph/Perspex.SceneGraph.csproj
  8. 11
      Perspex.SceneGraph/Platform/IFormattedTextImpl.cs
  9. 9
      Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs
  10. 11
      Windows/Perspex.Direct2D1/Direct2D1Platform.cs
  11. 11
      Windows/Perspex.Direct2D1/Media/DrawingContext.cs
  12. 135
      Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs

71
Perspex.Controls/TextBlock.cs

@ -7,12 +7,17 @@
namespace Perspex.Controls namespace Perspex.Controls
{ {
using System; using System;
using System.Reactive;
using System.Reactive.Linq;
using Perspex.Media; using Perspex.Media;
using Perspex.Platform; using Perspex.Platform;
using Splat; using Splat;
public class TextBlock : Control public class TextBlock : Control
{ {
public static readonly PerspexProperty<string> FontFamilyProperty =
PerspexProperty.Register<Control, string>("FontFamily", "Segoe UI", inherits: true);
public static readonly PerspexProperty<double> FontSizeProperty = public static readonly PerspexProperty<double> FontSizeProperty =
PerspexProperty.Register<Control, double>( PerspexProperty.Register<Control, double>(
"FontSize", "FontSize",
@ -25,27 +30,24 @@ namespace Perspex.Controls
public static readonly PerspexProperty<string> TextProperty = public static readonly PerspexProperty<string> TextProperty =
PerspexProperty.Register<TextBlock, string>("Text"); PerspexProperty.Register<TextBlock, string>("Text");
private FormattedText formattedText = new FormattedText(); private FormattedText formattedText;
public TextBlock() public TextBlock()
{ {
this.GetObservable(TextProperty).Subscribe(x => Observable.Merge(
{ this.GetObservable(TextProperty).Select(_ => Unit.Default),
this.formattedText.Text = x; this.GetObservable(FontSizeProperty).Select(_ => Unit.Default),
this.InvalidateMeasure(); this.GetObservable(FontStyleProperty).Select(_ => Unit.Default))
}); .Subscribe(_ =>
{
this.GetObservable(FontSizeProperty).Subscribe(x => if (this.formattedText != null)
{ {
this.formattedText.FontSize = x; this.formattedText.Dispose();
this.InvalidateMeasure(); this.formattedText = null;
}); }
this.GetObservable(FontStyleProperty).Subscribe(x => this.InvalidateMeasure();
{ });
this.formattedText.FontStyle = x;
this.InvalidateMeasure();
});
} }
public string Text public string Text
@ -54,6 +56,12 @@ namespace Perspex.Controls
set { this.SetValue(TextProperty, value); } set { this.SetValue(TextProperty, value); }
} }
public string FontFamily
{
get { return this.GetValue(FontFamilyProperty); }
set { this.SetValue(FontFamilyProperty, value); }
}
public double FontSize public double FontSize
{ {
get { return this.GetValue(FontSizeProperty); } get { return this.GetValue(FontSizeProperty); }
@ -66,6 +74,24 @@ namespace Perspex.Controls
set { this.SetValue(FontStyleProperty, value); } set { this.SetValue(FontStyleProperty, value); }
} }
protected FormattedText FormattedText
{
get
{
if (this.formattedText == null)
{
this.formattedText = new FormattedText(
this.Text,
this.FontFamily,
this.FontSize,
this.FontStyle);
}
return this.formattedText;
}
}
public override void Render(IDrawingContext context) public override void Render(IDrawingContext context)
{ {
Brush background = this.Background; Brush background = this.Background;
@ -75,18 +101,15 @@ namespace Perspex.Controls
context.FillRectange(background, new Rect(this.ActualSize)); context.FillRectange(background, new Rect(this.ActualSize));
} }
context.DrawText( context.DrawText(this.Foreground, new Point(), this.FormattedText);
this.Foreground,
new Rect(this.ActualSize),
this.formattedText);
} }
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
if (!string.IsNullOrEmpty(this.Text)) if (!string.IsNullOrEmpty(this.Text))
{ {
this.formattedText.Constraint = availableSize; this.FormattedText.Constraint = availableSize;
return this.formattedText.Measure(); return this.FormattedText.Measure();
} }
return new Size(); return new Size();

5
Perspex.Controls/TextBox.cs

@ -10,9 +10,7 @@ namespace Perspex.Controls
using System.Linq; using System.Linq;
using Perspex.Controls.Primitives; using Perspex.Controls.Primitives;
using Perspex.Input; using Perspex.Input;
using Perspex.Platform;
using Perspex.Styling; using Perspex.Styling;
using Splat;
public class TextBox : TemplatedControl public class TextBox : TemplatedControl
{ {
@ -160,8 +158,7 @@ namespace Perspex.Controls
private void OnPointerPressed(object sender, PointerEventArgs e) private void OnPointerPressed(object sender, PointerEventArgs e)
{ {
var point = e.GetPosition(this.textBoxView); var point = e.GetPosition(this.textBoxView);
var hit = this.textBoxView.FormattedText.HitTestPoint(point); this.CaretIndex = this.textBoxView.GetCaretIndex(point);
this.CaretIndex = hit.TextPosition + (hit.IsTrailing ? 1 : 0);
} }
} }
} }

51
Perspex.Controls/TextBoxView.cs

@ -8,55 +8,29 @@ namespace Perspex.Controls
{ {
using System; using System;
using Perspex.Media; using Perspex.Media;
using Perspex.Platform;
using Perspex.Threading; using Perspex.Threading;
using Splat;
internal class TextBoxView : Control internal class TextBoxView : TextBlock
{ {
private TextBox parent; private TextBox parent;
private FormattedText formattedText;
private DispatcherTimer caretTimer; private DispatcherTimer caretTimer;
private bool caretBlink; private bool caretBlink;
public TextBoxView(TextBox parent) public TextBoxView(TextBox parent)
{ {
this.FormattedText = new FormattedText();
// TODO: Implement TextBlock.FontFamilyName.
this.FormattedText.FontFamilyName = "Segoe UI";
parent.GetObservable(TextBox.TextProperty).Subscribe(x =>
{
this.FormattedText.Text = x;
this.InvalidateMeasure();
});
this.GetObservable(TextBlock.FontSizeProperty).Subscribe(x =>
{
this.FormattedText.FontSize = x;
this.InvalidateMeasure();
});
this.GetObservable(TextBlock.FontStyleProperty).Subscribe(x =>
{
this.FormattedText.FontStyle = x;
this.InvalidateMeasure();
});
this.caretTimer = new DispatcherTimer(); this.caretTimer = new DispatcherTimer();
this.caretTimer.Interval = TimeSpan.FromMilliseconds(500); this.caretTimer.Interval = TimeSpan.FromMilliseconds(500);
this.caretTimer.Tick += this.CaretTimerTick; this.caretTimer.Tick += this.CaretTimerTick;
this.parent = parent; this.parent = parent;
this[!TextProperty] = parent[!TextProperty];
} }
public FormattedText FormattedText public int GetCaretIndex(Point point)
{ {
get; var hit = this.FormattedText.HitTestPoint(point);
private set; return hit.TextPosition + (hit.IsTrailing ? 1 : 0);
} }
public new void GotFocus() public new void GotFocus()
@ -73,9 +47,7 @@ namespace Perspex.Controls
public override void Render(IDrawingContext context) public override void Render(IDrawingContext context)
{ {
Rect rect = new Rect(this.ActualSize); base.Render(context);
context.DrawText(Brushes.Black, rect, this.FormattedText);
if (this.parent.IsFocused) if (this.parent.IsFocused)
{ {
@ -97,17 +69,6 @@ namespace Perspex.Controls
this.InvalidateVisual(); this.InvalidateVisual();
} }
protected override Size MeasureOverride(Size constraint)
{
if (!string.IsNullOrEmpty(this.parent.Text))
{
this.FormattedText.Constraint = constraint;
return this.FormattedText.Measure();
}
return new Size();
}
private void CaretTimerTick(object sender, EventArgs e) private void CaretTimerTick(object sender, EventArgs e)
{ {
this.caretBlink = !this.caretBlink; this.caretBlink = !this.caretBlink;

15
Perspex.SceneGraph/Media/FontStyle.cs

@ -0,0 +1,15 @@
// -----------------------------------------------------------------------
// <copyright file="FontStyle.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
public enum FontStyle
{
Normal,
Oblique,
Italic,
}
}

45
Perspex.SceneGraph/Media/FormattedText.cs

@ -6,21 +6,25 @@
namespace Perspex.Media namespace Perspex.Media
{ {
using System;
using Perspex.Platform; using Perspex.Platform;
using Splat; using Splat;
public enum FontStyle public class FormattedText : IDisposable
{ {
Normal, public FormattedText(
Oblique, string text,
Italic, string fontFamilyName,
} double fontSize,
FontStyle fontStyle)
public class FormattedText
{
public FormattedText()
{ {
this.PlatformImpl = Locator.Current.GetService<IFormattedTextImpl>(); this.Text = text;
this.FontFamilyName = fontFamilyName;
this.FontSize = fontSize;
this.FontStyle = fontStyle;
var platform = Locator.Current.GetService<IPlatformRenderInterface>();
this.PlatformImpl = platform.CreateFormattedText(text, fontFamilyName, fontSize, fontStyle);
} }
public Size Constraint public Size Constraint
@ -31,26 +35,26 @@ namespace Perspex.Media
public string FontFamilyName public string FontFamilyName
{ {
get { return this.PlatformImpl.FontFamilyName; } get;
set { this.PlatformImpl.FontFamilyName = value; } private set;
} }
public double FontSize public double FontSize
{ {
get { return this.PlatformImpl.FontSize; } get;
set { this.PlatformImpl.FontSize = value; } private set;
} }
public FontStyle FontStyle public FontStyle FontStyle
{ {
get { return this.PlatformImpl.FontStyle; } get;
set { this.PlatformImpl.FontStyle = value; } private set;
} }
public string Text public string Text
{ {
get { return this.PlatformImpl.Text; } get;
set { this.PlatformImpl.Text = value; } private set;
} }
public IFormattedTextImpl PlatformImpl public IFormattedTextImpl PlatformImpl
@ -59,6 +63,11 @@ namespace Perspex.Media
private set; private set;
} }
public void Dispose()
{
this.PlatformImpl.Dispose();
}
public TextHitTestResult HitTestPoint(Point point) public TextHitTestResult HitTestPoint(Point point)
{ {
return this.PlatformImpl.HitTestPoint(point); return this.PlatformImpl.HitTestPoint(point);

4
Perspex.SceneGraph/Media/IDrawingContext.cs

@ -45,9 +45,9 @@ namespace Perspex.Media
/// Draws text. /// Draws text.
/// </summary> /// </summary>
/// <param name="foreground">The foreground brush.</param> /// <param name="foreground">The foreground brush.</param>
/// <param name="rect">The bounding rectangle.</param> /// <param name="origin">The upper-left corner of the text.</param>
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
void DrawText(Brush foreground, Rect rect, FormattedText text); void DrawText(Brush foreground, Point origin, FormattedText text);
/// <summary> /// <summary>
/// Draws a filled rectangle. /// Draws a filled rectangle.

1
Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -45,6 +45,7 @@
<Compile Include="Media\Brushes.cs" /> <Compile Include="Media\Brushes.cs" />
<Compile Include="Media\Color.cs" /> <Compile Include="Media\Color.cs" />
<Compile Include="Media\Colors.cs" /> <Compile Include="Media\Colors.cs" />
<Compile Include="Media\FontStyle.cs" />
<Compile Include="Media\FormattedText.cs" /> <Compile Include="Media\FormattedText.cs" />
<Compile Include="Media\Geometry.cs" /> <Compile Include="Media\Geometry.cs" />
<Compile Include="Media\IDrawingContext.cs" /> <Compile Include="Media\IDrawingContext.cs" />

11
Perspex.SceneGraph/Platform/IFormattedTextImpl.cs

@ -6,20 +6,13 @@
namespace Perspex.Platform namespace Perspex.Platform
{ {
using System;
using Perspex.Media; using Perspex.Media;
public interface IFormattedTextImpl public interface IFormattedTextImpl : IDisposable
{ {
Size Constraint { get; set; } Size Constraint { get; set; }
string FontFamilyName { get; set; }
double FontSize { get; set; }
FontStyle FontStyle { get; set; }
string Text { get; set; }
TextHitTestResult HitTestPoint(Point point); TextHitTestResult HitTestPoint(Point point);
Rect HitTestTextPosition(int index); Rect HitTestTextPosition(int index);

9
Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs

@ -6,13 +6,18 @@
namespace Perspex.Platform namespace Perspex.Platform
{ {
using System; using Perspex.Media;
using Perspex.Threading;
public interface IPlatformRenderInterface public interface IPlatformRenderInterface
{ {
IBitmapImpl CreateBitmap(int width, int height); IBitmapImpl CreateBitmap(int width, int height);
IFormattedTextImpl CreateFormattedText(
string text,
string fontFamily,
double fontSize,
FontStyle fontStyle);
IStreamGeometryImpl CreateStreamGeometry(); IStreamGeometryImpl CreateStreamGeometry();
IRenderer CreateRenderer(IPlatformHandle handle, double width, double height); IRenderer CreateRenderer(IPlatformHandle handle, double width, double height);

11
Windows/Perspex.Direct2D1/Direct2D1Platform.cs

@ -8,6 +8,7 @@ namespace Perspex.Direct2D1
{ {
using System; using System;
using Perspex.Direct2D1.Media; using Perspex.Direct2D1.Media;
using Perspex.Media;
using Perspex.Platform; using Perspex.Platform;
using Perspex.Threading; using Perspex.Threading;
using Splat; using Splat;
@ -29,7 +30,6 @@ namespace Perspex.Direct2D1
locator.Register(() => d2d1Factory, typeof(SharpDX.Direct2D1.Factory)); locator.Register(() => d2d1Factory, typeof(SharpDX.Direct2D1.Factory));
locator.Register(() => dwfactory, typeof(SharpDX.DirectWrite.Factory)); locator.Register(() => dwfactory, typeof(SharpDX.DirectWrite.Factory));
locator.Register(() => imagingFactory, typeof(SharpDX.WIC.ImagingFactory)); locator.Register(() => imagingFactory, typeof(SharpDX.WIC.ImagingFactory));
locator.Register(() => new FormattedTextImpl(), typeof(IFormattedTextImpl));
} }
public IBitmapImpl CreateBitmap(int width, int height) public IBitmapImpl CreateBitmap(int width, int height)
@ -37,6 +37,15 @@ namespace Perspex.Direct2D1
return new BitmapImpl(imagingFactory, width, height); return new BitmapImpl(imagingFactory, width, height);
} }
public IFormattedTextImpl CreateFormattedText(
string text,
string fontFamily,
double fontSize,
FontStyle fontStyle)
{
return new FormattedTextImpl(text, fontFamily, fontSize, fontStyle);
}
public IRenderer CreateRenderer(IPlatformHandle handle, double width, double height) public IRenderer CreateRenderer(IPlatformHandle handle, double width, double height)
{ {
if (handle.HandleDescriptor == "HWND") if (handle.HandleDescriptor == "HWND")

11
Windows/Perspex.Direct2D1/Media/DrawingContext.cs

@ -134,9 +134,9 @@ namespace Perspex.Direct2D1.Media
/// Draws text. /// Draws text.
/// </summary> /// </summary>
/// <param name="foreground">The foreground brush.</param> /// <param name="foreground">The foreground brush.</param>
/// <param name="rect">The output rectangle.</param> /// <param name="origin">The upper-left corner of the text.</param>
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
public void DrawText(Perspex.Media.Brush foreground, Rect rect, FormattedText text) public void DrawText(Perspex.Media.Brush foreground, Perspex.Point origin, FormattedText text)
{ {
if (!string.IsNullOrEmpty(text.Text)) if (!string.IsNullOrEmpty(text.Text))
{ {
@ -144,10 +144,9 @@ namespace Perspex.Direct2D1.Media
using (SharpDX.Direct2D1.SolidColorBrush brush = this.Convert(foreground)) using (SharpDX.Direct2D1.SolidColorBrush brush = this.Convert(foreground))
{ {
this.renderTarget.DrawText( this.renderTarget.DrawTextLayout(
text.Text, origin.ToSharpDX(),
impl.Layout, impl.TextLayout,
this.Convert(rect),
brush); brush);
} }
} }

135
Windows/Perspex.Direct2D1/Media/FormattedTextImpl.cs

@ -14,121 +14,45 @@ namespace Perspex.Direct2D1.Media
public class FormattedTextImpl : IFormattedTextImpl public class FormattedTextImpl : IFormattedTextImpl
{ {
private string text; public FormattedTextImpl(
string text,
private string fontFamilyName = "Ariel"; string fontFamily,
double fontSize,
private double fontSize = 10; FontStyle fontStyle)
private FontStyle fontStyle;
private DWrite.Factory factory;
private DWrite.TextLayout layout;
public FormattedTextImpl()
{ {
this.factory = Locator.Current.GetService<DWrite.Factory>(); var factory = Locator.Current.GetService<DWrite.Factory>();
this.TextLayout = new DWrite.TextLayout(
factory,
text ?? string.Empty,
new DWrite.TextFormat(factory, fontFamily, (float)fontSize),
float.MaxValue,
float.MaxValue);
} }
public Size Constraint public Size Constraint
{ {
get get
{ {
return new Size(this.Layout.MaxWidth, this.Layout.MaxHeight); return new Size(this.TextLayout.MaxWidth, this.TextLayout.MaxHeight);
}
set
{
this.Layout.MaxWidth = (float)value.Width;
this.Layout.MaxHeight = (float)value.Height;
}
}
public string FontFamilyName
{
get
{
return this.fontFamilyName;
}
set
{
if (this.fontFamilyName != value)
{
this.fontFamilyName = value;
this.DisposeLayout();
}
}
}
public double FontSize
{
get
{
return this.fontSize;
} }
set set
{ {
if (this.fontSize != value) this.TextLayout.MaxWidth = (float)value.Width;
{ this.TextLayout.MaxHeight = (float)value.Height;
this.fontSize = value;
this.DisposeLayout();
}
} }
} }
public FontStyle FontStyle public DWrite.TextLayout TextLayout
{ {
get get;
{ private set;
return this.fontStyle;
}
set
{
if (this.fontStyle != value)
{
this.fontStyle = value;
this.DisposeLayout();
}
}
}
public string Text
{
get
{
return this.text;
}
set
{
if (this.text != value)
{
this.text = value;
this.DisposeLayout();
}
}
} }
public DWrite.TextLayout Layout public void Dispose()
{ {
get this.TextLayout.Dispose();
{
if (this.layout == null)
{
this.layout = new DWrite.TextLayout(
this.factory,
this.text ?? string.Empty,
new DWrite.TextFormat(this.factory, this.fontFamilyName, (float)this.fontSize),
float.MaxValue,
float.MaxValue);
}
return this.layout;
}
} }
public TextHitTestResult HitTestPoint(Point point) public TextHitTestResult HitTestPoint(Point point)
@ -136,7 +60,7 @@ namespace Perspex.Direct2D1.Media
SharpDX.Bool isTrailingHit; SharpDX.Bool isTrailingHit;
SharpDX.Bool isInside; SharpDX.Bool isInside;
DWrite.HitTestMetrics result = layout.HitTestPoint( DWrite.HitTestMetrics result = this.TextLayout.HitTestPoint(
(float)point.X, (float)point.X,
(float)point.Y, (float)point.Y,
out isTrailingHit, out isTrailingHit,
@ -154,7 +78,7 @@ namespace Perspex.Direct2D1.Media
float x; float x;
float y; float y;
DWrite.HitTestMetrics result = layout.HitTestTextPosition( DWrite.HitTestMetrics result = this.TextLayout.HitTestTextPosition(
index, index,
false, false,
out x, out x,
@ -166,17 +90,8 @@ namespace Perspex.Direct2D1.Media
public Size Measure() public Size Measure()
{ {
return new Size( return new Size(
layout.Metrics.WidthIncludingTrailingWhitespace, this.TextLayout.Metrics.WidthIncludingTrailingWhitespace,
layout.Metrics.Height); this.TextLayout.Metrics.Height);
}
private void DisposeLayout()
{
if (this.layout != null)
{
this.layout.Dispose();
this.layout = null;
}
} }
} }
} }

Loading…
Cancel
Save