diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj
index 2dcb64d1b5..062bf4a636 100644
--- a/Perspex.Controls/Perspex.Controls.csproj
+++ b/Perspex.Controls/Perspex.Controls.csproj
@@ -36,6 +36,7 @@
4
+
diff --git a/Perspex.Controls/Presenters/TextPresenter.cs b/Perspex.Controls/Presenters/TextPresenter.cs
index a0776dad02..f6ff4abd46 100644
--- a/Perspex.Controls/Presenters/TextPresenter.cs
+++ b/Perspex.Controls/Presenters/TextPresenter.cs
@@ -140,9 +140,9 @@ namespace Perspex.Controls.Presenters
}
}
- protected override FormattedText CreateFormattedText()
+ protected override FormattedText CreateFormattedText(Size constraint)
{
- var result = base.CreateFormattedText();
+ var result = base.CreateFormattedText(constraint);
var selectionStart = this.SelectionStart;
var selectionEnd = this.SelectionEnd;
var start = Math.Min(selectionStart, selectionEnd);
diff --git a/Perspex.Controls/Primitives/AccessText.cs b/Perspex.Controls/Primitives/AccessText.cs
new file mode 100644
index 0000000000..2e74bfcc4d
--- /dev/null
+++ b/Perspex.Controls/Primitives/AccessText.cs
@@ -0,0 +1,85 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2015 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Controls.Primitives
+{
+ using Perspex.Media;
+
+ ///
+ /// A text block that displays a character prefixed with an underscore as an access key.
+ ///
+ public class AccessText : TextBlock
+ {
+ ///
+ /// Renders the to a drawing context.
+ ///
+ /// The drawing context.
+ public override void Render(IDrawingContext context)
+ {
+ base.Render(context);
+
+ int underscore = this.Text?.IndexOf('_') ?? -1;
+
+ if (underscore != -1)
+ {
+ var rect = this.FormattedText.HitTestTextPosition(underscore);
+ var offset = new Vector(0, -0.5);
+ context.DrawLine(
+ new Pen(this.Foreground, 1),
+ rect.BottomLeft + offset,
+ rect.BottomRight + offset);
+ }
+ }
+
+ ///
+ /// Creates the used to render the text.
+ ///
+ /// The constraint of the text.
+ /// A object.
+ protected override FormattedText CreateFormattedText(Size constraint)
+ {
+ var result = new FormattedText(
+ this.StripAccessKey(this.Text),
+ this.FontFamily,
+ this.FontSize,
+ this.FontStyle,
+ this.TextAlignment,
+ this.FontWeight);
+ result.Constraint = constraint;
+ return result;
+ }
+
+ ///
+ /// Measures the control.
+ ///
+ /// The available size for the control.
+ /// The desired size.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ var result = base.MeasureOverride(availableSize);
+ return result.WithHeight(result.Height + 1);
+ }
+
+ ///
+ /// Returns a string with the first underscore stripped.
+ ///
+ /// The text.
+ /// The text with the first underscore stripped.
+ private string StripAccessKey(string text)
+ {
+ var position = text.IndexOf('_');
+
+ if (position == -1)
+ {
+ return text;
+ }
+ else
+ {
+ return text.Substring(0, position) + text.Substring(position + 1);
+ }
+ }
+ }
+}
diff --git a/Perspex.Controls/TextBlock.cs b/Perspex.Controls/TextBlock.cs
index 7c03c3fdd7..911ed1727e 100644
--- a/Perspex.Controls/TextBlock.cs
+++ b/Perspex.Controls/TextBlock.cs
@@ -11,39 +11,78 @@ namespace Perspex.Controls
using System.Reactive.Linq;
using Perspex.Media;
+ ///
+ /// A control that displays a block of text.
+ ///
public class TextBlock : Control
{
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty BackgroundProperty =
Border.BackgroundProperty.AddOwner();
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty FontFamilyProperty =
PerspexProperty.RegisterAttached("FontFamily", inherits: true);
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty FontSizeProperty =
PerspexProperty.RegisterAttached("FontSize", inherits: true);
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty FontStyleProperty =
PerspexProperty.RegisterAttached("FontStyle", inherits: true);
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty FontWeightProperty =
PerspexProperty.RegisterAttached("FontWeight", inherits: true, defaultValue: FontWeight.Normal);
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty ForegroundProperty =
PerspexProperty.RegisterAttached("Foreground", new SolidColorBrush(0xff000000), inherits: true);
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty TextProperty =
PerspexProperty.Register("Text");
- public static readonly PerspexProperty TextWrappingProperty =
- PerspexProperty.Register("TextWrapping");
-
+ ///
+ /// Defines the property.
+ ///
public static readonly PerspexProperty TextAlignmentProperty =
PerspexProperty.Register("TextAlignment");
+ ///
+ /// Defines the property.
+ ///
+ public static readonly PerspexProperty TextWrappingProperty =
+ PerspexProperty.Register("TextWrapping");
+
+ ///
+ /// The formatted text used for rendering.
+ ///
private FormattedText formattedText;
+ ///
+ /// Stores the last constraint passed to MeasureOverride.
+ ///
private Size constraint;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public TextBlock()
{
Observable.Merge(
@@ -57,73 +96,107 @@ namespace Perspex.Controls
});
}
+ ///
+ /// Gets or sets a brush used to paint the control's background.
+ ///
public Brush Background
{
get { return this.GetValue(BackgroundProperty); }
set { this.SetValue(BackgroundProperty, value); }
}
+ ///
+ /// Gets or sets the text.
+ ///
public string Text
{
get { return this.GetValue(TextProperty); }
set { this.SetValue(TextProperty, value); }
}
+ ///
+ /// Gets or sets the font family.
+ ///
public string FontFamily
{
get { return this.GetValue(FontFamilyProperty); }
set { this.SetValue(FontFamilyProperty, value); }
}
+ ///
+ /// Gets or sets the font size.
+ ///
public double FontSize
{
get { return this.GetValue(FontSizeProperty); }
set { this.SetValue(FontSizeProperty, value); }
}
+ ///
+ /// Gets or sets the font style.
+ ///
public FontStyle FontStyle
{
get { return this.GetValue(FontStyleProperty); }
set { this.SetValue(FontStyleProperty, value); }
}
+ ///
+ /// Gets or sets the font weight.
+ ///
public FontWeight FontWeight
{
get { return this.GetValue(FontWeightProperty); }
set { this.SetValue(FontWeightProperty, value); }
}
+ ///
+ /// Gets or sets a brush used to paint the text.
+ ///
public Brush Foreground
{
get { return this.GetValue(ForegroundProperty); }
set { this.SetValue(ForegroundProperty, value); }
}
+ ///
+ /// Gets the used to render the text.
+ ///
public FormattedText FormattedText
{
get
{
if (this.formattedText == null)
{
- this.formattedText = this.CreateFormattedText();
+ this.formattedText = this.CreateFormattedText(this.constraint);
}
return this.formattedText;
}
}
+ ///
+ /// Gets or sets the control's text wrapping mode.
+ ///
public TextWrapping TextWrapping
{
get { return this.GetValue(TextWrappingProperty); }
set { this.SetValue(TextWrappingProperty, value); }
}
+ ///
+ /// Gets or sets the text alignment.
+ ///
public TextAlignment TextAlignment
{
get { return this.GetValue(TextAlignmentProperty); }
set { this.SetValue(TextAlignmentProperty, value); }
}
+ ///
+ /// Renders the to a drawing context.
+ ///
+ /// The drawing context.
public override void Render(IDrawingContext context)
{
Brush background = this.Background;
@@ -137,7 +210,12 @@ namespace Perspex.Controls
context.DrawText(this.Foreground, new Point(), this.FormattedText);
}
- protected virtual FormattedText CreateFormattedText()
+ ///
+ /// Creates the used to render the text.
+ ///
+ /// The constraint of the text.
+ /// A object.
+ protected virtual FormattedText CreateFormattedText(Size constraint)
{
var result = new FormattedText(
this.Text,
@@ -146,10 +224,13 @@ namespace Perspex.Controls
this.FontStyle,
this.TextAlignment,
this.FontWeight);
- result.Constraint = this.constraint;
+ result.Constraint = constraint;
return result;
}
+ ///
+ /// Invalidates .
+ ///
protected void InvalidateFormattedText()
{
if (this.formattedText != null)
@@ -162,6 +243,11 @@ namespace Perspex.Controls
this.InvalidateMeasure();
}
+ ///
+ /// Measures the control.
+ ///
+ /// The available size for the control.
+ /// The desired size.
protected override Size MeasureOverride(Size availableSize)
{
if (!string.IsNullOrEmpty(this.Text))
diff --git a/Perspex.Themes.Default/MenuItemStyle.cs b/Perspex.Themes.Default/MenuItemStyle.cs
index 3c2241218e..4733b60987 100644
--- a/Perspex.Themes.Default/MenuItemStyle.cs
+++ b/Perspex.Themes.Default/MenuItemStyle.cs
@@ -6,17 +6,21 @@
namespace Perspex.Themes.Default
{
+ using System.Linq;
using Perspex.Controls;
using Perspex.Controls.Presenters;
+ using Perspex.Controls.Primitives;
using Perspex.Controls.Shapes;
using Perspex.Input;
using Perspex.Layout;
using Perspex.Media;
using Perspex.Styling;
- using System.Linq;
public class MenuItemStyle : Styles
{
+ private static readonly DataTemplate AccessKeyDataTemplate =
+ new DataTemplate(x => new AccessText { Text = x });
+
public MenuItemStyle()
{
this.AddRange(new[]
@@ -79,6 +83,10 @@ namespace Perspex.Themes.Default
{
new ContentPresenter
{
+ DataTemplates = new DataTemplates
+ {
+ AccessKeyDataTemplate,
+ },
[~ContentPresenter.ContentProperty] = control[~MenuItem.HeaderProperty],
[~ContentPresenter.MarginProperty] = control[~MenuItem.PaddingProperty],
[Grid.ColumnProperty] = 1,
@@ -171,6 +179,10 @@ namespace Perspex.Themes.Default
},
new ContentPresenter
{
+ DataTemplates = new DataTemplates
+ {
+ AccessKeyDataTemplate,
+ },
VerticalAlignment = VerticalAlignment.Center,
[~ContentPresenter.ContentProperty] = control[~MenuItem.HeaderProperty],
[~ContentPresenter.MarginProperty] = control[~MenuItem.PaddingProperty],
diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs
index 1813d0db38..09f2a6f01e 100644
--- a/TestApplication/Program.cs
+++ b/TestApplication/Program.cs
@@ -144,12 +144,12 @@ namespace TestApplication
{
new MenuItem
{
- Header = "File",
+ Header = "_File",
Items = new[]
{
new MenuItem
{
- Header = "Open...",
+ Header = "_Open...",
Icon = new Image
{
Source = new Bitmap("github_icon.png"),
@@ -157,57 +157,57 @@ namespace TestApplication
},
new MenuItem
{
- Header = "Save",
+ Header = "_Save",
Items = new[]
{
new MenuItem
{
- Header = "Sub Item 1",
+ Header = "Sub Item _1",
},
new MenuItem
{
- Header = "Sub Item 2",
+ Header = "Sub Item _2",
},
}
},
new MenuItem
{
- Header = "Save As",
+ Header = "Save _As",
Items = new[]
{
new MenuItem
{
- Header = "Sub Item 1",
+ Header = "Sub Item _1",
},
new MenuItem
{
- Header = "Sub Item 2",
+ Header = "Sub Item _2",
},
}
},
new MenuItem
{
- Header = "Exit",
+ Header = "E_xit",
Command = testCommand,
},
}
},
new MenuItem
{
- Header = "Edit",
+ Header = "_Edit",
Items = new[]
{
new MenuItem
{
- Header = "Cut",
+ Header = "Cu_t",
},
new MenuItem
{
- Header = "Copy",
+ Header = "_Copy",
},
new MenuItem
{
- Header = "Paste",
+ Header = "_Paste",
},
}
}