Browse Source

Show access keys in menus.

Currently always shown, whether user pressed alt/f10 or not.
pull/58/head
Steven Kirk 12 years ago
parent
commit
c65c9fb7c6
  1. 1
      Perspex.Controls/Perspex.Controls.csproj
  2. 4
      Perspex.Controls/Presenters/TextPresenter.cs
  3. 85
      Perspex.Controls/Primitives/AccessText.cs
  4. 98
      Perspex.Controls/TextBlock.cs
  5. 14
      Perspex.Themes.Default/MenuItemStyle.cs
  6. 26
      TestApplication/Program.cs

1
Perspex.Controls/Perspex.Controls.csproj

@ -36,6 +36,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="Primitives\AccessText.cs" />
<Compile Include="Border.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="MenuItem.cs" />

4
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);

85
Perspex.Controls/Primitives/AccessText.cs

@ -0,0 +1,85 @@
// -----------------------------------------------------------------------
// <copyright file="AccessText.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Controls.Primitives
{
using Perspex.Media;
/// <summary>
/// A text block that displays a character prefixed with an underscore as an access key.
/// </summary>
public class AccessText : TextBlock
{
/// <summary>
/// Renders the <see cref="AccessText"/> to a drawing context.
/// </summary>
/// <param name="context">The drawing context.</param>
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);
}
}
/// <summary>
/// Creates the <see cref="FormattedText"/> used to render the text.
/// </summary>
/// <param name="constraint">The constraint of the text.</param>
/// <returns>A <see cref="FormattedText"/> object.</returns>
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;
}
/// <summary>
/// Measures the control.
/// </summary>
/// <param name="availableSize">The available size for the control.</param>
/// <returns>The desired size.</returns>
protected override Size MeasureOverride(Size availableSize)
{
var result = base.MeasureOverride(availableSize);
return result.WithHeight(result.Height + 1);
}
/// <summary>
/// Returns a string with the first underscore stripped.
/// </summary>
/// <param name="text">The text.</param>
/// <returns>The text with the first underscore stripped.</returns>
private string StripAccessKey(string text)
{
var position = text.IndexOf('_');
if (position == -1)
{
return text;
}
else
{
return text.Substring(0, position) + text.Substring(position + 1);
}
}
}
}

98
Perspex.Controls/TextBlock.cs

@ -11,39 +11,78 @@ namespace Perspex.Controls
using System.Reactive.Linq;
using Perspex.Media;
/// <summary>
/// A control that displays a block of text.
/// </summary>
public class TextBlock : Control
{
/// <summary>
/// Defines the <see cref="Background"/> property.
/// </summary>
public static readonly PerspexProperty<Brush> BackgroundProperty =
Border.BackgroundProperty.AddOwner<TextBlock>();
/// <summary>
/// Defines the <see cref="FontFamily"/> property.
/// </summary>
public static readonly PerspexProperty<string> FontFamilyProperty =
PerspexProperty.RegisterAttached<TextBlock, Control, string>("FontFamily", inherits: true);
/// <summary>
/// Defines the <see cref="FontSize"/> property.
/// </summary>
public static readonly PerspexProperty<double> FontSizeProperty =
PerspexProperty.RegisterAttached<TextBlock, Control, double>("FontSize", inherits: true);
/// <summary>
/// Defines the <see cref="FontStyle"/> property.
/// </summary>
public static readonly PerspexProperty<FontStyle> FontStyleProperty =
PerspexProperty.RegisterAttached<TextBlock, Control, FontStyle>("FontStyle", inherits: true);
/// <summary>
/// Defines the <see cref="FontWeight"/> property.
/// </summary>
public static readonly PerspexProperty<FontWeight> FontWeightProperty =
PerspexProperty.RegisterAttached<TextBlock, Control, FontWeight>("FontWeight", inherits: true, defaultValue: FontWeight.Normal);
/// <summary>
/// Defines the <see cref="Foreground"/> property.
/// </summary>
public static readonly PerspexProperty<Brush> ForegroundProperty =
PerspexProperty.RegisterAttached<TextBlock, Control, Brush>("Foreground", new SolidColorBrush(0xff000000), inherits: true);
/// <summary>
/// Defines the <see cref="Text"/> property.
/// </summary>
public static readonly PerspexProperty<string> TextProperty =
PerspexProperty.Register<TextBlock, string>("Text");
public static readonly PerspexProperty<TextWrapping> TextWrappingProperty =
PerspexProperty.Register<TextBlock, TextWrapping>("TextWrapping");
/// <summary>
/// Defines the <see cref="TextAlignment"/> property.
/// </summary>
public static readonly PerspexProperty<TextAlignment> TextAlignmentProperty =
PerspexProperty.Register<TextBlock, TextAlignment>("TextAlignment");
/// <summary>
/// Defines the <see cref="TextWrapping"/> property.
/// </summary>
public static readonly PerspexProperty<TextWrapping> TextWrappingProperty =
PerspexProperty.Register<TextBlock, TextWrapping>("TextWrapping");
/// <summary>
/// The formatted text used for rendering.
/// </summary>
private FormattedText formattedText;
/// <summary>
/// Stores the last constraint passed to MeasureOverride.
/// </summary>
private Size constraint;
/// <summary>
/// Initializes a new instance of the <see cref="TextBlock"/> class.
/// </summary>
public TextBlock()
{
Observable.Merge(
@ -57,73 +96,107 @@ namespace Perspex.Controls
});
}
/// <summary>
/// Gets or sets a brush used to paint the control's background.
/// </summary>
public Brush Background
{
get { return this.GetValue(BackgroundProperty); }
set { this.SetValue(BackgroundProperty, value); }
}
/// <summary>
/// Gets or sets the text.
/// </summary>
public string Text
{
get { return this.GetValue(TextProperty); }
set { this.SetValue(TextProperty, value); }
}
/// <summary>
/// Gets or sets the font family.
/// </summary>
public string FontFamily
{
get { return this.GetValue(FontFamilyProperty); }
set { this.SetValue(FontFamilyProperty, value); }
}
/// <summary>
/// Gets or sets the font size.
/// </summary>
public double FontSize
{
get { return this.GetValue(FontSizeProperty); }
set { this.SetValue(FontSizeProperty, value); }
}
/// <summary>
/// Gets or sets the font style.
/// </summary>
public FontStyle FontStyle
{
get { return this.GetValue(FontStyleProperty); }
set { this.SetValue(FontStyleProperty, value); }
}
/// <summary>
/// Gets or sets the font weight.
/// </summary>
public FontWeight FontWeight
{
get { return this.GetValue(FontWeightProperty); }
set { this.SetValue(FontWeightProperty, value); }
}
/// <summary>
/// Gets or sets a brush used to paint the text.
/// </summary>
public Brush Foreground
{
get { return this.GetValue(ForegroundProperty); }
set { this.SetValue(ForegroundProperty, value); }
}
/// <summary>
/// Gets the <see cref="FormattedText"/> used to render the text.
/// </summary>
public FormattedText FormattedText
{
get
{
if (this.formattedText == null)
{
this.formattedText = this.CreateFormattedText();
this.formattedText = this.CreateFormattedText(this.constraint);
}
return this.formattedText;
}
}
/// <summary>
/// Gets or sets the control's text wrapping mode.
/// </summary>
public TextWrapping TextWrapping
{
get { return this.GetValue(TextWrappingProperty); }
set { this.SetValue(TextWrappingProperty, value); }
}
/// <summary>
/// Gets or sets the text alignment.
/// </summary>
public TextAlignment TextAlignment
{
get { return this.GetValue(TextAlignmentProperty); }
set { this.SetValue(TextAlignmentProperty, value); }
}
/// <summary>
/// Renders the <see cref="TextBlock"/> to a drawing context.
/// </summary>
/// <param name="context">The drawing context.</param>
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()
/// <summary>
/// Creates the <see cref="FormattedText"/> used to render the text.
/// </summary>
/// <param name="constraint">The constraint of the text.</param>
/// <returns>A <see cref="FormattedText"/> object.</returns>
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;
}
/// <summary>
/// Invalidates <see cref="FormattedText"/>.
/// </summary>
protected void InvalidateFormattedText()
{
if (this.formattedText != null)
@ -162,6 +243,11 @@ namespace Perspex.Controls
this.InvalidateMeasure();
}
/// <summary>
/// Measures the control.
/// </summary>
/// <param name="availableSize">The available size for the control.</param>
/// <returns>The desired size.</returns>
protected override Size MeasureOverride(Size availableSize)
{
if (!string.IsNullOrEmpty(this.Text))

14
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<string>(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],

26
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",
},
}
}

Loading…
Cancel
Save