Browse Source

Something resembling a menu now...

pull/58/head
Steven Kirk 11 years ago
parent
commit
f7d271f846
  1. 2
      Perspex.Controls/Menu.cs
  2. 2
      Perspex.Styling/Selector.cs
  3. 38
      Perspex.Styling/Selectors.cs
  4. 88
      Perspex.Themes.Default/MenuItemStyle.cs
  5. 18
      TestApplication/Program.cs
  6. 2
      Tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj
  7. 23
      Tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs

2
Perspex.Controls/Menu.cs

@ -4,6 +4,8 @@
// </copyright>
// -----------------------------------------------------------------------
using Perspex.Controls.Generators;
namespace Perspex.Controls
{
public class Menu : ItemsControl

2
Perspex.Styling/Selector.cs

@ -21,7 +21,7 @@ namespace Perspex.Styling
public Selector()
{
this.evaluate = _ => new SelectorMatch(true);
this.evaluate = _ => SelectorMatch.True;
}
public Selector(

38
Perspex.Styling/Selectors.cs

@ -13,6 +13,13 @@ namespace Perspex.Styling
public static class Selectors
{
public static Selector Child(this Selector previous)
{
Contract.Requires<ArgumentNullException>(previous != null);
return new Selector(previous, x => MatchChild(x, previous), " < ", stopTraversal: true);
}
public static Selector Class(this Selector previous, string name)
{
Contract.Requires<ArgumentNullException>(previous != null);
@ -59,13 +66,6 @@ namespace Perspex.Styling
return previous.OfType(typeof(T));
}
public static Selector Parent(this Selector previous)
{
Contract.Requires<ArgumentNullException>(previous != null);
return new Selector(previous, x => MatchParent(x, previous), " < ", stopTraversal: true);
}
public static Selector PropertyEquals<T>(this Selector previous, PerspexProperty<T> property, object value)
{
Contract.Requires<ArgumentNullException>(previous != null);
@ -86,6 +86,20 @@ namespace Perspex.Styling
stopTraversal: true);
}
private static SelectorMatch MatchChild(IStyleable control, Selector previous)
{
var parent = ((ILogical)control).LogicalParent;
if (parent != null)
{
return previous.Match((IStyleable)parent);
}
else
{
return SelectorMatch.False;
}
}
private static SelectorMatch MatchClass(IStyleable control, string name)
{
return new SelectorMatch(
@ -111,7 +125,7 @@ namespace Perspex.Styling
{
if (match.ImmediateResult == true)
{
return new SelectorMatch(true);
return SelectorMatch.True;
}
}
else
@ -143,17 +157,11 @@ namespace Perspex.Styling
return new SelectorMatch(controlType == type);
}
private static SelectorMatch MatchParent(IStyleable control, Selector previous)
{
var parent = ((ILogical)control).LogicalParent;
return previous.Match((IStyleable)parent);
}
private static SelectorMatch MatchPropertyEquals<T>(IStyleable x, PerspexProperty<T> property, object value)
{
if (!x.IsRegistered(property))
{
return new SelectorMatch(false);
return SelectorMatch.False;
}
else
{

88
Perspex.Themes.Default/MenuItemStyle.cs

@ -27,7 +27,14 @@ namespace Perspex.Themes.Default
{
new Setter(MenuItem.BorderThicknessProperty, 1.0),
new Setter(MenuItem.PaddingProperty, new Thickness(6, 0)),
new Setter(MenuItem.TemplateProperty, ControlTemplate.Create<MenuItem>(this.Template)),
new Setter(MenuItem.TemplateProperty, ControlTemplate.Create<MenuItem>(this.PopupTemplate)),
},
},
new Style(x => x.OfType<Menu>().Child().OfType<MenuItem>())
{
Setters = new[]
{
new Setter(MenuItem.TemplateProperty, ControlTemplate.Create<MenuItem>(this.TopLevelTemplate)),
},
},
new Style(x => x.OfType<MenuItem>().Class(":pointerover").Template().Name("root"))
@ -37,11 +44,77 @@ namespace Perspex.Themes.Default
new Setter(Border.BackgroundProperty, new SolidColorBrush(0x3d26a0da)),
new Setter(Border.BorderBrushProperty, new SolidColorBrush(0xff26a0da)),
},
}
},
});
}
private Control Template(MenuItem control)
private Control TopLevelTemplate(MenuItem control)
{
Popup popup;
var result = new Border
{
Name = "root",
[~Border.BackgroundProperty] = control[~MenuItem.BackgroundProperty],
[~Border.BorderBrushProperty] = control[~MenuItem.BorderBrushProperty],
[~Border.BorderThicknessProperty] = control[~MenuItem.BorderThicknessProperty],
Content = new Panel
{
Children = new Controls
{
new ContentPresenter
{
[~ContentPresenter.ContentProperty] = control[~MenuItem.HeaderProperty],
[~ContentPresenter.MarginProperty] = control[~MenuItem.PaddingProperty],
[Grid.ColumnProperty] = 1,
},
(popup = new Popup
{
Name = "popup",
StaysOpen = false,
[!!Popup.IsOpenProperty] = control[!!MenuItem.IsSubMenuOpenProperty],
Child = new Border
{
Background = new SolidColorBrush(0xfff0f0f0),
BorderBrush = new SolidColorBrush(0xff999999),
BorderThickness = 1,
Padding = new Thickness(2),
Content = new ScrollViewer
{
Content = new Panel
{
Children = new Controls
{
new Rectangle
{
Name = "iconSeparator",
Fill = new SolidColorBrush(0xffd7d7d7),
HorizontalAlignment = HorizontalAlignment.Left,
Margin = new Thickness(29, 2, 0, 2),
Width = 1,
},
new ItemsPresenter
{
Name = "itemsPresenter",
[~ItemsPresenter.ItemsProperty] = control[~Menu.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~Menu.ItemsPanelProperty],
[KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.Cycle,
}
}
}
}
}
})
},
}
};
popup.PlacementTarget = result;
return result;
}
private Control PopupTemplate(MenuItem control)
{
Popup popup;
@ -55,8 +128,10 @@ namespace Perspex.Themes.Default
{
ColumnDefinitions = new ColumnDefinitions
{
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(GridLength.Auto),
new ColumnDefinition(22, GridUnitType.Pixel),
new ColumnDefinition(13, GridUnitType.Pixel),
new ColumnDefinition(1, GridUnitType.Star),
new ColumnDefinition(20, GridUnitType.Pixel),
},
Children = new Controls
{
@ -80,9 +155,10 @@ namespace Perspex.Themes.Default
},
new ContentPresenter
{
VerticalAlignment = VerticalAlignment.Center,
[~ContentPresenter.ContentProperty] = control[~MenuItem.HeaderProperty],
[~ContentPresenter.MarginProperty] = control[~MenuItem.PaddingProperty],
[Grid.ColumnProperty] = 1,
[Grid.ColumnProperty] = 2,
},
(popup = new Popup
{

18
TestApplication/Program.cs

@ -141,43 +141,43 @@ namespace TestApplication
{
new MenuItem
{
Header = "_File",
Header = "File",
Items = new[]
{
new MenuItem
{
Header = "_Open...",
Header = "Open...",
},
new MenuItem
{
Header = "_Save",
Header = "Save",
},
new MenuItem
{
Header = "Save _As",
Header = "Save As",
},
new MenuItem
{
Header = "E_xit",
Header = "Exit",
},
}
},
new MenuItem
{
Header = "_Edit",
Header = "Edit",
Items = new[]
{
new MenuItem
{
Header = "Cu_t",
Header = "Cut",
},
new MenuItem
{
Header = "_Copy",
Header = "Copy",
},
new MenuItem
{
Header = "_Paste",
Header = "Paste",
},
}
}

2
Tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj

@ -83,7 +83,7 @@
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SelectorTests_Class.cs" />
<Compile Include="SelectorTests_Parent.cs" />
<Compile Include="SelectorTests_Child.cs" />
<Compile Include="SelectorTests_Descendent.cs" />
<Compile Include="SelectorTests_Name.cs" />
<Compile Include="SelectorTests_Multiple.cs" />

23
Tests/Perspex.Styling.UnitTests/SelectorTests_Parent.cs → Tests/Perspex.Styling.UnitTests/SelectorTests_Child.cs

@ -14,23 +14,23 @@ namespace Perspex.Styling.UnitTests
using Perspex.Styling;
using Xunit;
public class SelectorTests_Parent
public class SelectorTests_Child
{
[Fact]
public void Parent_Matches_Control_When_It_Is_Child_OfType()
public void Child_Matches_Control_When_It_Is_Child_OfType()
{
var parent = new TestLogical1();
var child = new TestLogical2();
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Parent().OfType<TestLogical2>();
var selector = new Selector().OfType<TestLogical1>().Child().OfType<TestLogical2>();
Assert.True(selector.Match(child).ImmediateResult);
}
[Fact]
public void Parent_Doesnt_Match_Control_When_It_Is_Grandchild_OfType()
public void Child_Doesnt_Match_Control_When_It_Is_Grandchild_OfType()
{
var grandparent = new TestLogical1();
var parent = new TestLogical2();
@ -39,20 +39,20 @@ namespace Perspex.Styling.UnitTests
parent.LogicalParent = grandparent;
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Parent().OfType<TestLogical3>();
var selector = new Selector().OfType<TestLogical1>().Child().OfType<TestLogical3>();
Assert.False(selector.Match(child).ImmediateResult);
}
[Fact]
public async Task Parent_Matches_Control_When_It_Is_Child_OfType_And_Class()
public async Task Child_Matches_Control_When_It_Is_Child_OfType_And_Class()
{
var parent = new TestLogical1();
var child = new TestLogical2();
child.LogicalParent = parent;
var selector = new Selector().OfType<TestLogical1>().Class("foo").Parent().OfType<TestLogical2>();
var selector = new Selector().OfType<TestLogical1>().Class("foo").Child().OfType<TestLogical2>();
var activator = selector.Match(child).ObservableResult;
Assert.False(await activator.Take(1));
@ -62,6 +62,15 @@ namespace Perspex.Styling.UnitTests
Assert.False(await activator.Take(1));
}
[Fact]
public void Child_Doesnt_Match_Control_When_It_Has_No_Parent()
{
var control = new TestLogical3();
var selector = new Selector().OfType<TestLogical1>().Child().OfType<TestLogical3>();
Assert.False(selector.Match(control).ImmediateResult);
}
public abstract class TestLogical : ILogical, IStyleable
{
public TestLogical()
Loading…
Cancel
Save