# By Nikita Tsukanov (39) and others # Via Nikita Tsukanov (10) and others * 'master' of https://github.com/grokys/Perspex: (86 commits) Moving to anchor tag for linking, allowing new tab Updated video image Fix code and remove useless null check. Adding thumbnail to YT video, adding screenshot Fixing URL stable(ish) points to Minor fixes alpha2 -> stable-ish Add RadialGradientBrush support for Direct2D and Cairo backend. Adding NuGet blurb to README.md Add Canvas Right and Bottom attached property and support during arrange. Add Canvas under Layout tab Updated some docs for alpha2 Updated nuget version Fixing stuff including accidentally Finalizing Gallery work Clarified documentation for Visual.Bounds More progress WIP Restored previous AssetLoader behavior Use correct character for child selector. ...pull/174/head
@ -0,0 +1,11 @@ |
|||
; This file is for unifying the coding style for different editors and IDEs. |
|||
; More information at http://EditorConfig.org |
|||
|
|||
root = true |
|||
|
|||
[*] |
|||
end_of_line = CRLF |
|||
|
|||
[*.cs] |
|||
indent_style = space |
|||
indent_size = 4 |
|||
|
Before Width: | Height: | Size: 928 B |
|
Before Width: | Height: | Size: 924 B |
|
Before Width: | Height: | Size: 934 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 928 B |
|
Before Width: | Height: | Size: 263 B |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 233 KiB |
@ -0,0 +1,15 @@ |
|||
# Getting Started |
|||
|
|||
## Windows |
|||
|
|||
 |
|||
|
|||
The easiest way to try out Perspex is to install the Visual Studio Extension. |
|||
|
|||
This will add a Perspex project template and a Window template to the standard Visual Studo “Add” dialog (yes, icons still to come :) ): |
|||
|
|||
## OSX / Linux |
|||
|
|||
It is a little more manual on non-Windows platforms, but using Xamarin Studio you can install the Perspex NuGet package. |
|||
|
|||
 |
|||
|
After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 104 KiB |
@ -0,0 +1,93 @@ |
|||
# Styling in Perspex |
|||
|
|||
The main difference between Perspex and existing XAML toolkits such as WPF and |
|||
UWP is in styling. Styling in Perspex uses a CSS-like system that aims to be |
|||
more powerful and flexible than existing XAML styling systems. For convenience |
|||
for the rest of this document we'll refer to existing XAML toolkit's styling as |
|||
"WPF styling" as that's where it originated. |
|||
|
|||
## Basics |
|||
|
|||
- Styles are defined on the `Control.Styles` collection (as opposed to in |
|||
`ResourceDictionaries` in WPF). |
|||
- Styles have a `Selector` and a collection of `Setter`s |
|||
- Selector works like a CSS selector. |
|||
- Setters function like WPF's setters. |
|||
- Styles are applied to a control and all its descendants, depending on whether |
|||
the selector matches. |
|||
|
|||
## Simple example |
|||
|
|||
Make all `Button`s in a `StackPanel` have a blue `Background`: |
|||
|
|||
<StackPanel> |
|||
<StackPanel.Styles> |
|||
<Style Selector="Button"> |
|||
<Setter Property="Button.Background" Value="Blue"/> |
|||
</Style> |
|||
</StackPanel.Styles> |
|||
<Button>I will have a blue background.</Button> |
|||
</StackPanel> |
|||
|
|||
This is very similar to WPF, except `TargetType` is replaced by `Selector`. |
|||
|
|||
*Note that currently (as of Alpha 2) you **always** need to specify the fully |
|||
qualified property name (i.e. `Button.Background` instead of simply |
|||
`Background`). This restriction will be lifted in future.* |
|||
|
|||
## Style Classes |
|||
|
|||
As in CSS, controls can be given *style classes* which can be used in selectors: |
|||
|
|||
<StackPanel> |
|||
<StackPanel.Styles> |
|||
<Style Selector="Button.blue"> |
|||
<Setter Property="Button.Background" Value="Blue"/> |
|||
</Style> |
|||
</StackPanel.Styles> |
|||
<Button Classes="blue">I will have a blue background.</Button> |
|||
<Button>I will not.</Button> |
|||
</StackPanel> |
|||
|
|||
## Pseudoclasses |
|||
|
|||
Also as in CSS, controls can have pseudoclasses; these are classes that are |
|||
defined by the control itself rather than by the user. Pseudoclasses start |
|||
with a `:` character. |
|||
|
|||
One example of a pseudoclass is the `:pointerover` |
|||
pseudoclass (`:hover` in CSS - we may change to that in future). |
|||
|
|||
Pseudoclasses provide the functionality of `Triggers` in WPF and |
|||
`VisualStateManager` in UWP: |
|||
|
|||
<StackPanel> |
|||
<StackPanel.Styles> |
|||
<Style Selector="Button:pointerover"> |
|||
<Setter Property="Button.Foreground" Value="Red"/> |
|||
</Style> |
|||
</StackPanel.Styles> |
|||
<Button>I will have red text when hovered.</Button> |
|||
</StackPanel> |
|||
|
|||
Other pseudoclasses include `:focus`, `:disabled`, `:pressed` for buttons, |
|||
`:checked` for checkboxes etc. |
|||
|
|||
## Named Controls |
|||
|
|||
Named controls can be selected using `#` as in CSS, e.g. `Button#Name`. |
|||
|
|||
## Children |
|||
|
|||
As with CSS, you can select children and descendants: |
|||
|
|||
- `StackPanel > Button#Foo` selects a `Button` named `"Foo"` that is the child |
|||
of a `StackPanel`. |
|||
- `StackPanel Button.foo` selects all `Button`s with the `foo` class that are |
|||
descendants of a `StackPanel`. |
|||
|
|||
## Templates |
|||
|
|||
You can select controls in the template of a lookless control by using the |
|||
`/template/` selector, so `Button /template/ Border#outline` selects `Border` |
|||
controls named `"outline"` in the template of a `Button`. |
|||
@ -1,7 +1,2 @@ |
|||
- build/ |
|||
|
|||
**/lib/**/*.dll |
|||
**/lib/**/*.xml |
|||
**/build/**/*.dll |
|||
**/build/**/*.xml |
|||
Perspex |
|||
*.nupkg |
|||
@ -0,0 +1,33 @@ |
|||
$scriptpath = $MyInvocation.MyCommand.Path |
|||
$dir = Split-Path $scriptpath |
|||
Push-Location $dir |
|||
|
|||
|
|||
sv version $env:APPVEYOR_BUILD_NUMBER |
|||
#sv version "1-debug" |
|||
|
|||
sv version 9999.0.$version-nightly |
|||
sv key $env:myget_key |
|||
|
|||
sv file Perspex.$version.nupkg |
|||
|
|||
.\build-version.ps1 $version |
|||
|
|||
|
|||
sv reponame $env:APPVEYOR_REPO_NAME |
|||
sv repobranch $env:APPVEYOR_REPO_BRANCH |
|||
sv pullreq $env:APPVEYOR_PULL_REQUEST_NUMBER |
|||
|
|||
echo "Checking for publishing" |
|||
echo $reponame $repobranch $pullreq |
|||
if ($reponame -eq "Perspex/Perspex") |
|||
{ |
|||
echo Repo name matched |
|||
if($repobranch -eq "master") |
|||
{ |
|||
echo Repo branch matched |
|||
nuget.exe push $file $key -Source https://www.myget.org/F/perspex-nightly/api/v2/package |
|||
} |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,47 @@ |
|||
rm -Force -Recurse .\Perspex -ErrorAction SilentlyContinue |
|||
rm -Force -Recurse *.nupkg -ErrorAction SilentlyContinue |
|||
Copy-Item template Perspex -Recurse |
|||
sv lib "Perspex\lib\portable-windows8+net45" |
|||
sv build "Perspex\build\net45" |
|||
mkdir $lib -ErrorAction SilentlyContinue |
|||
mkdir $build -ErrorAction SilentlyContinue |
|||
|
|||
Copy-Item ..\src\Perspex.Animation\bin\Release\Perspex.Animation.dll $lib |
|||
Copy-Item ..\src\Perspex.Animation\bin\Release\Perspex.Animation.xml $lib |
|||
Copy-Item ..\src\Perspex.Application\bin\Release\Perspex.Application.dll $lib |
|||
Copy-Item ..\src\Perspex.Application\bin\Release\Perspex.Application.xml $lib |
|||
Copy-Item ..\src\Perspex.Base\bin\Release\Perspex.Base.dll $lib |
|||
Copy-Item ..\src\Perspex.Base\bin\Release\Perspex.Base.xml $lib |
|||
Copy-Item ..\src\Perspex.Controls\bin\Release\Perspex.Controls.dll $lib |
|||
Copy-Item ..\src\Perspex.Controls\bin\Release\Perspex.Controls.xml $lib |
|||
Copy-Item ..\src\Perspex.Diagnostics\bin\Release\\Perspex.Diagnostics.dll $lib |
|||
Copy-Item ..\src\Perspex.Diagnostics\bin\Release\\Perspex.Diagnostics.xml $lib |
|||
Copy-Item ..\src\Perspex.Input\bin\Release\Perspex.Input.dll $lib |
|||
Copy-Item ..\src\Perspex.Input\bin\Release\Perspex.Input.xml $lib |
|||
Copy-Item ..\src\Perspex.Interactivity\bin\Release\Perspex.Interactivity.dll $lib |
|||
Copy-Item ..\src\Perspex.Interactivity\bin\Release\Perspex.Interactivity.xml $lib |
|||
Copy-Item ..\src\Perspex.Layout\bin\Release\Perspex.Layout.dll $lib |
|||
Copy-Item ..\src\Perspex.Layout\bin\Release\Perspex.Layout.xml $lib |
|||
Copy-Item ..\src\Perspex.SceneGraph\bin\Release\Perspex.SceneGraph.dll $lib |
|||
Copy-Item ..\src\Perspex.SceneGraph\bin\Release\Perspex.SceneGraph.xml $lib |
|||
Copy-Item ..\src\Perspex.Styling\bin\Release\Perspex.Styling.dll $lib |
|||
Copy-Item ..\src\Perspex.Styling\bin\Release\Perspex.Styling.xml $lib |
|||
Copy-Item ..\src\Perspex.Themes.Default\bin\Release\Perspex.Themes.Default.dll $lib |
|||
Copy-Item ..\src\Perspex.Themes.Default\bin\Release\Perspex.Themes.Default.xml $lib |
|||
Copy-Item ..\src\Markup\Perspex.Markup.Xaml\bin\Release\Perspex.Markup.Xaml.dll $lib |
|||
Copy-Item ..\src\Markup\Perspex.Markup.Xaml\bin\Release\Perspex.Markup.Xaml.xml $lib |
|||
Copy-Item ..\src\Perspex.HtmlRenderer\bin\Release\Perspex.HtmlRenderer.dll $lib |
|||
Copy-Item ..\src\NGenerics\bin\Release\NGenerics.dll $lib |
|||
|
|||
Copy-Item ..\src\Windows\Perspex.Direct2D1\bin\Release\Perspex.Direct2D1.dll $build |
|||
Copy-Item ..\src\Windows\Perspex.Direct2D1\bin\Release\SharpDX.dll $build |
|||
Copy-Item ..\src\Windows\Perspex.Direct2D1\bin\Release\SharpDX.Direct2D1.dll $build |
|||
Copy-Item ..\src\Windows\Perspex.Direct2D1\bin\Release\SharpDX.DXGI.dll $build |
|||
Copy-Item ..\src\Windows\Perspex.Win32\bin\Release\Perspex.Win32.dll $build |
|||
Copy-Item ..\src\Gtk\Perspex.Gtk\bin\Release\Perspex.Gtk.dll $build |
|||
Copy-Item ..\src\Gtk\Perspex.Cairo\bin\Release\Perspex.Cairo.dll $build |
|||
|
|||
(gc Perspex\Perspex.nuspec).replace('#VERSION#', $args[0]) | sc Perspex\Perspex.nuspec |
|||
|
|||
nuget.exe pack Perspex\Perspex.nuspec |
|||
rm -Force -Recurse .\Perspex |
|||
@ -1,39 +0,0 @@ |
|||
SET lib="Perspex\lib\portable-windows8+net45" |
|||
SET build="Perspex\build\net45" |
|||
|
|||
mkdir %lib% |
|||
mkdir %build% |
|||
|
|||
copy ..\src\Perspex.Animation\bin\Release\Perspex.Animation.dll %lib% |
|||
copy ..\src\Perspex.Animation\bin\Release\Perspex.Animation.xml %lib% |
|||
copy ..\src\Perspex.Application\bin\Release\Perspex.Application.dll %lib% |
|||
copy ..\src\Perspex.Application\bin\Release\Perspex.Application.xml %lib% |
|||
copy ..\src\Perspex.Base\bin\Release\Perspex.Base.dll %lib% |
|||
copy ..\src\Perspex.Base\bin\Release\Perspex.Base.xml %lib% |
|||
copy ..\src\Perspex.Controls\bin\Release\Perspex.Controls.dll %lib% |
|||
copy ..\src\Perspex.Controls\bin\Release\Perspex.Controls.xml %lib% |
|||
copy ..\src\Perspex.Diagnostics\bin\Release\\Perspex.Diagnostics.dll %lib% |
|||
copy ..\src\Perspex.Diagnostics\bin\Release\\Perspex.Diagnostics.xml %lib% |
|||
copy ..\src\Perspex.Input\bin\Release\Perspex.Input.dll %lib% |
|||
copy ..\src\Perspex.Input\bin\Release\Perspex.Input.xml %lib% |
|||
copy ..\src\Perspex.Interactivity\bin\Release\Perspex.Interactivity.dll %lib% |
|||
copy ..\src\Perspex.Interactivity\bin\Release\Perspex.Interactivity.xml %lib% |
|||
copy ..\src\Perspex.Layout\bin\Release\Perspex.Layout.dll %lib% |
|||
copy ..\src\Perspex.Layout\bin\Release\Perspex.Layout.xml %lib% |
|||
copy ..\src\Perspex.SceneGraph\bin\Release\Perspex.SceneGraph.dll %lib% |
|||
copy ..\src\Perspex.SceneGraph\bin\Release\Perspex.SceneGraph.xml %lib% |
|||
copy ..\src\Perspex.Styling\bin\Release\Perspex.Styling.dll %lib% |
|||
copy ..\src\Perspex.Styling\bin\Release\Perspex.Styling.xml %lib% |
|||
copy ..\src\Perspex.Themes.Default\bin\Release\Perspex.Themes.Default.dll %lib% |
|||
copy ..\src\Perspex.Themes.Default\bin\Release\Perspex.Themes.Default.xml %lib% |
|||
copy ..\src\Markup\Perspex.Markup.Xaml\bin\Release\Perspex.Markup.Xaml.dll %lib% |
|||
copy ..\src\Markup\Perspex.Markup.Xaml\bin\Release\Perspex.Markup.Xaml.xml %lib% |
|||
copy ..\src\NGenerics\bin\Release\NGenerics.dll %lib% |
|||
|
|||
copy ..\src\Windows\Perspex.Direct2D1\bin\Release\Perspex.Direct2D1.dll %build% |
|||
copy ..\src\Windows\Perspex.Direct2D1\bin\Release\SharpDX.dll %build% |
|||
copy ..\src\Windows\Perspex.Direct2D1\bin\Release\SharpDX.Direct2D1.dll %build% |
|||
copy ..\src\Windows\Perspex.Direct2D1\bin\Release\SharpDX.DXGI.dll %build% |
|||
copy ..\src\Windows\Perspex.Win32\bin\Release\Perspex.Win32.dll %build% |
|||
|
|||
nuget.exe pack Perspex\Perspex.nuspec |
|||
@ -0,0 +1 @@ |
|||
.\build-version.ps1 0.1.0-alpha2 |
|||
@ -0,0 +1,123 @@ |
|||
using Perspex; |
|||
using Perspex.Controls; |
|||
using Perspex.Controls.Presenters; |
|||
using Perspex.Controls.Primitives; |
|||
using Perspex.Controls.Templates; |
|||
using Perspex.Media; |
|||
using Perspex.Styling; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace TestApplication |
|||
{ |
|||
internal class SampleTabStyle : Styles |
|||
{ |
|||
public SampleTabStyle() |
|||
{ |
|||
this.AddRange(new[] |
|||
{ |
|||
new Style (s => s.Class(":container").OfType<TabControl> ()) |
|||
{ |
|||
Setters = new[] |
|||
{ |
|||
new Setter (TemplatedControl.TemplateProperty, new ControlTemplate<TabControl> (TabControlTemplate)) |
|||
} |
|||
}, |
|||
|
|||
new Style(s => s.Class(":container").OfType<TabControl>().Child().Child().Child().Child().Child().OfType<TabItem>()) |
|||
{ |
|||
Setters = new[] |
|||
{ |
|||
new Setter (TemplatedControl.TemplateProperty, new ControlTemplate<TabItem> (TabItemTemplate)), |
|||
} |
|||
}, |
|||
|
|||
new Style(s => s.Name("internalStrip").OfType<TabStrip>().Child().OfType<TabItem>()) |
|||
{ |
|||
Setters = new[] |
|||
{ |
|||
new Setter(TemplatedControl.FontSizeProperty, 14.0), |
|||
new Setter(TemplatedControl.ForegroundProperty, Brushes.White) |
|||
} |
|||
}, |
|||
|
|||
new Style(s => s.Name("internalStrip").OfType<TabStrip>().Child().OfType<TabItem>().Class("selected")) |
|||
{ |
|||
Setters = new[] |
|||
{ |
|||
new Setter(TemplatedControl.ForegroundProperty, Brushes.White), |
|||
new Setter(TemplatedControl.BackgroundProperty, new SolidColorBrush(Colors.White) { Opacity = 0.1 }), |
|||
}, |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
public static Control TabItemTemplate(TabItem control) |
|||
{ |
|||
return new ContentPresenter |
|||
{ |
|||
DataTemplates = new DataTemplates |
|||
{ |
|||
new DataTemplate<string>(x => new Border |
|||
{ |
|||
[~Border.BackgroundProperty] = control[~TemplatedControl.BackgroundProperty], |
|||
Padding = new Thickness(10), |
|||
Child = new TextBlock |
|||
{ |
|||
VerticalAlignment = Perspex.Layout.VerticalAlignment.Center, |
|||
Text = x |
|||
} |
|||
}) |
|||
}, |
|||
Name = "headerPresenter", |
|||
[~ContentPresenter.ContentProperty] = control[~HeaderedContentControl.HeaderProperty], |
|||
}; |
|||
} |
|||
|
|||
public static Control TabControlTemplate(TabControl control) |
|||
{ |
|||
return new Grid |
|||
{ |
|||
ColumnDefinitions = new ColumnDefinitions |
|||
{ |
|||
new ColumnDefinition(GridLength.Auto), |
|||
new ColumnDefinition(new GridLength(1, GridUnitType.Star)), |
|||
}, |
|||
Children = new Controls |
|||
{ |
|||
new Border |
|||
{ |
|||
Width = 190, |
|||
Background = SolidColorBrush.Parse("#1976D2"), |
|||
Child = new ScrollViewer |
|||
{ |
|||
Content = new TabStrip |
|||
{ |
|||
ItemsPanel = new FuncTemplate<IPanel>(() => new StackPanel { Orientation = Orientation.Vertical, Gap = 4 }), |
|||
Margin = new Thickness(0, 10, 0, 0), |
|||
Name = "internalStrip", |
|||
[!ItemsControl.ItemsProperty] = control[!ItemsControl.ItemsProperty], |
|||
[!!SelectingItemsControl.SelectedItemProperty] = control[!!SelectingItemsControl.SelectedItemProperty], |
|||
} |
|||
} |
|||
}, |
|||
new Deck |
|||
{ |
|||
Name = "deck", |
|||
DataTemplates = new DataTemplates |
|||
{ |
|||
new DataTemplate<TabItem>(x => (Control)control.MaterializeDataTemplate(x.Content)), |
|||
}, |
|||
[~Deck.TransitionProperty] = control[~TabControl.TransitionProperty], |
|||
[!ItemsControl.ItemsProperty] = control[!ItemsControl.ItemsProperty], |
|||
[!SelectingItemsControl.SelectedItemProperty] = control[!SelectingItemsControl.SelectedItemProperty], |
|||
[Grid.ColumnProperty] = 1, |
|||
} |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace TestApplication |
|||
{ |
|||
internal class Item |
|||
{ |
|||
public string Name { get; set; } |
|||
public string Value { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using Perspex.Collections; |
|||
|
|||
namespace TestApplication |
|||
{ |
|||
internal class Node |
|||
{ |
|||
public Node() |
|||
{ |
|||
Children = new PerspexList<Node>(); |
|||
} |
|||
|
|||
public string Name { get; set; } |
|||
public PerspexList<Node> Children { get; set; } |
|||
} |
|||
|
|||
} |
|||
|
After Width: | Height: | Size: 133 KiB |
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using global::Cairo; |
|||
|
|||
namespace Perspex.Cairo |
|||
{ |
|||
public abstract class BrushImpl : IDisposable |
|||
{ |
|||
public Pattern PlatformBrush { get; protected set; } |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if (this.PlatformBrush != null) |
|||
this.PlatformBrush.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using global::Cairo; |
|||
|
|||
namespace Perspex.Cairo.Media |
|||
{ |
|||
public class ImageBrushImpl : BrushImpl |
|||
{ |
|||
public ImageBrushImpl(Perspex.Media.ImageBrush brush, Size destinationSize) |
|||
{ |
|||
this.PlatformBrush = TileBrushes.CreateImageBrush(brush, destinationSize); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using global::Cairo; |
|||
|
|||
namespace Perspex.Cairo |
|||
{ |
|||
public class LinearGradientBrushImpl : BrushImpl |
|||
{ |
|||
public LinearGradientBrushImpl(Perspex.Media.LinearGradientBrush brush, Size destinationSize) |
|||
{ |
|||
var start = brush.StartPoint.ToPixels(destinationSize); |
|||
var end = brush.EndPoint.ToPixels(destinationSize); |
|||
|
|||
this.PlatformBrush = new LinearGradient(start.X, start.Y, end.X, end.Y); |
|||
|
|||
foreach (var stop in brush.GradientStops) |
|||
((LinearGradient)this.PlatformBrush).AddColorStop(stop.Offset, stop.Color.ToCairo()); |
|||
|
|||
((LinearGradient)this.PlatformBrush).Extend = Extend.Pad; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using global::Cairo; |
|||
|
|||
namespace Perspex.Cairo |
|||
{ |
|||
public class RadialGradientBrushImpl : BrushImpl |
|||
{ |
|||
public RadialGradientBrushImpl(Perspex.Media.RadialGradientBrush brush, Size destinationSize) |
|||
{ |
|||
var center = brush.Center.ToPixels(destinationSize); |
|||
var gradientOrigin = brush.GradientOrigin.ToPixels(destinationSize); |
|||
var radius = brush.Radius; |
|||
|
|||
this.PlatformBrush = new RadialGradient(center.X, center.Y, radius, gradientOrigin.X, gradientOrigin.Y, radius); |
|||
|
|||
foreach (var stop in brush.GradientStops) |
|||
{ |
|||
((LinearGradient)this.PlatformBrush).AddColorStop(stop.Offset, stop.Color.ToCairo()); |
|||
} |
|||
|
|||
((LinearGradient)this.PlatformBrush).Extend = Extend.Pad; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using global::Cairo; |
|||
|
|||
namespace Perspex.Cairo |
|||
{ |
|||
public class SolidColorBrushImpl : BrushImpl |
|||
{ |
|||
public SolidColorBrushImpl(Perspex.Media.SolidColorBrush brush, double opacityOverride = 1.0f) |
|||
{ |
|||
var color = brush?.Color.ToCairo() ?? new Color(); |
|||
|
|||
if (brush != null) |
|||
color.A = Math.Min(brush.Opacity, color.A); |
|||
|
|||
if (opacityOverride < 1.0f) |
|||
color.A = Math.Min(opacityOverride, color.A); |
|||
|
|||
this.PlatformBrush = new SolidPattern(color); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,224 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using Cairo; |
|||
using Perspex.Cairo.Media.Imaging; |
|||
using Perspex.Layout; |
|||
using Perspex.Media; |
|||
using Perspex.Platform; |
|||
|
|||
namespace Perspex.Cairo.Media |
|||
{ |
|||
internal static class TileBrushes |
|||
{ |
|||
public static SurfacePattern CreateImageBrush(ImageBrush brush, Size targetSize) |
|||
{ |
|||
if (brush.Source == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
// TODO: This is directly ported from Direct2D and could probably be made more
|
|||
// efficient on cairo by taking advantage of the fact that cairo has Extend.None.
|
|||
var image = ((BitmapImpl)brush.Source.PlatformImpl).Surface; |
|||
var imageSize = new Size(brush.Source.PixelWidth, brush.Source.PixelHeight); |
|||
var tileMode = brush.TileMode; |
|||
var sourceRect = brush.SourceRect.ToPixels(imageSize); |
|||
var destinationRect = brush.DestinationRect.ToPixels(targetSize); |
|||
var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); |
|||
var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); |
|||
var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size); |
|||
|
|||
var intermediate = new ImageSurface (Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height); |
|||
using (var context = new Context(intermediate)) |
|||
{ |
|||
Rect drawRect; |
|||
var transform = CalculateIntermediateTransform( |
|||
tileMode, |
|||
sourceRect, |
|||
destinationRect, |
|||
scale, |
|||
translate, |
|||
out drawRect); |
|||
context.Rectangle(drawRect.ToCairo()); |
|||
context.Clip(); |
|||
context.Transform(transform.ToCairo()); |
|||
Gdk.CairoHelper.SetSourcePixbuf(context, image, 0, 0); |
|||
context.Rectangle(0, 0, imageSize.Width, imageSize.Height); |
|||
context.Fill(); |
|||
|
|||
var result = new SurfacePattern(intermediate); |
|||
|
|||
if ((brush.TileMode & TileMode.FlipXY) != 0) |
|||
{ |
|||
// TODO: Currently always FlipXY as that's all cairo supports natively.
|
|||
// Support separate FlipX and FlipY by drawing flipped images to intermediate
|
|||
// surface.
|
|||
result.Extend = Extend.Reflect; |
|||
} |
|||
else |
|||
{ |
|||
result.Extend = Extend.Repeat; |
|||
} |
|||
|
|||
if (brush.TileMode != TileMode.None) |
|||
{ |
|||
var matrix = result.Matrix; |
|||
matrix.InitTranslate(-destinationRect.X, -destinationRect.Y); |
|||
result.Matrix = matrix; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public static SurfacePattern CreateVisualBrush(VisualBrush brush, Size targetSize) |
|||
{ |
|||
var visual = brush.Visual; |
|||
|
|||
if (visual == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var layoutable = visual as ILayoutable; |
|||
|
|||
if (layoutable?.IsArrangeValid == false) |
|||
{ |
|||
layoutable.Measure(Size.Infinity); |
|||
layoutable.Arrange(new Rect(layoutable.DesiredSize)); |
|||
} |
|||
|
|||
// TODO: This is directly ported from Direct2D and could probably be made more
|
|||
// efficient on cairo by taking advantage of the fact that cairo has Extend.None.
|
|||
var tileMode = brush.TileMode; |
|||
var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size); |
|||
var destinationRect = brush.DestinationRect.ToPixels(targetSize); |
|||
var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); |
|||
var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); |
|||
var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size); |
|||
|
|||
using (var intermediate = new ImageSurface(Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height)) |
|||
using (var context = new Context(intermediate)) |
|||
{ |
|||
Rect drawRect; |
|||
var transform = CalculateIntermediateTransform( |
|||
tileMode, |
|||
sourceRect, |
|||
destinationRect, |
|||
scale, |
|||
translate, |
|||
out drawRect); |
|||
var renderer = new Renderer(intermediate); |
|||
|
|||
context.Rectangle(drawRect.ToCairo()); |
|||
context.Clip(); |
|||
context.Transform(transform.ToCairo()); |
|||
renderer.Render(visual, new PlatformHandle(IntPtr.Zero, "RTB"), transform, drawRect); |
|||
|
|||
var result = new SurfacePattern(intermediate); |
|||
|
|||
if ((brush.TileMode & TileMode.FlipXY) != 0) |
|||
{ |
|||
// TODO: Currently always FlipXY as that's all cairo supports natively.
|
|||
// Support separate FlipX and FlipY by drawing flipped images to intermediate
|
|||
// surface.
|
|||
result.Extend = Extend.Reflect; |
|||
} |
|||
else |
|||
{ |
|||
result.Extend = Extend.Repeat; |
|||
} |
|||
|
|||
if (brush.TileMode != TileMode.None) |
|||
{ |
|||
var matrix = result.Matrix; |
|||
matrix.InitTranslate(-destinationRect.X, -destinationRect.Y); |
|||
result.Matrix = matrix; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates a translate based on a <see cref="TileBrush"/>, a source and destination
|
|||
/// rectangle and a scale.
|
|||
/// </summary>
|
|||
/// <param name="brush">The brush.</param>
|
|||
/// <param name="sourceRect">The source rectangle.</param>
|
|||
/// <param name="destinationRect">The destination rectangle.</param>
|
|||
/// <param name="scale">The scale factor.</param>
|
|||
/// <returns>A vector with the X and Y translate.</returns>
|
|||
private static Vector CalculateTranslate( |
|||
TileBrush brush, |
|||
Rect sourceRect, |
|||
Rect destinationRect, |
|||
Vector scale) |
|||
{ |
|||
var x = 0.0; |
|||
var y = 0.0; |
|||
var size = sourceRect.Size * scale; |
|||
|
|||
switch (brush.AlignmentX) |
|||
{ |
|||
case AlignmentX.Center: |
|||
x += (destinationRect.Width - size.Width) / 2; |
|||
break; |
|||
case AlignmentX.Right: |
|||
x += destinationRect.Width - size.Width; |
|||
break; |
|||
} |
|||
|
|||
switch (brush.AlignmentY) |
|||
{ |
|||
case AlignmentY.Center: |
|||
y += (destinationRect.Height - size.Height) / 2; |
|||
break; |
|||
case AlignmentY.Bottom: |
|||
y += destinationRect.Height - size.Height; |
|||
break; |
|||
} |
|||
|
|||
return new Vector(x, y); |
|||
} |
|||
|
|||
private static Size CalculateIntermediateSize( |
|||
TileMode tileMode, |
|||
Size targetSize, |
|||
Size destinationSize) |
|||
{ |
|||
var result = tileMode == TileMode.None ? targetSize : destinationSize; |
|||
return result; |
|||
} |
|||
|
|||
private static Matrix CalculateIntermediateTransform( |
|||
TileMode tileMode, |
|||
Rect sourceRect, |
|||
Rect destinationRect, |
|||
Vector scale, |
|||
Vector translate, |
|||
out Rect drawRect) |
|||
{ |
|||
var transform = Matrix.CreateTranslation(-sourceRect.Position) * |
|||
Matrix.CreateScale(scale) * |
|||
Matrix.CreateTranslation(translate); |
|||
Rect dr; |
|||
|
|||
if (tileMode == TileMode.None) |
|||
{ |
|||
dr = destinationRect; |
|||
transform *= Matrix.CreateTranslation(destinationRect.Position); |
|||
} |
|||
else |
|||
{ |
|||
dr = new Rect(destinationRect.Size); |
|||
} |
|||
|
|||
drawRect = dr; |
|||
|
|||
return transform; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using global::Cairo; |
|||
|
|||
namespace Perspex.Cairo.Media |
|||
{ |
|||
public class VisualBrushImpl : BrushImpl |
|||
{ |
|||
public VisualBrushImpl(Perspex.Media.VisualBrush brush, Size destinationSize) |
|||
{ |
|||
this.PlatformBrush = TileBrushes.CreateVisualBrush(brush, destinationSize); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1 +1 @@ |
|||
Subproject commit 7fb1dba8af30f07e0a9a34e99ca0d80e569ef3ef |
|||
Subproject commit 586e2e521c85a4b111c394e8aee178bbdfc9c1df |
|||
@ -0,0 +1,202 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using Perspex.Input; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
public class Canvas : Panel, INavigableContainer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the <see cref="Left"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<double> LeftProperty = |
|||
PerspexProperty.RegisterAttached<StackPanel, Control, double>("Left"); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="Top"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<double> TopProperty = |
|||
PerspexProperty.RegisterAttached<StackPanel, Control, double>("Top"); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="Right"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<double> RightProperty = |
|||
PerspexProperty.RegisterAttached<StackPanel, Control, double>("Right"); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="Bottom"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<double> BottomProperty = |
|||
PerspexProperty.RegisterAttached<StackPanel, Control, double>("Bottom"); |
|||
|
|||
/// <summary>
|
|||
/// Initializes static members of the <see cref="Canvas"/> class.
|
|||
/// </summary>
|
|||
static Canvas() |
|||
{ |
|||
AffectsArrange(LeftProperty); |
|||
AffectsArrange(TopProperty); |
|||
AffectsArrange(RightProperty); |
|||
AffectsArrange(BottomProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the Left attached property for a control.
|
|||
/// </summary>
|
|||
/// <param name="element">The control.</param>
|
|||
/// <returns>The control's left coordinate.</returns>
|
|||
public static double GetLeft(PerspexObject element) |
|||
{ |
|||
return element.GetValue(LeftProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the value of the Left attached property for a control.
|
|||
/// </summary>
|
|||
/// <param name="element">The control.</param>
|
|||
/// <param name="value">The left value.</param>
|
|||
public static void SetLeft(PerspexObject element, double value) |
|||
{ |
|||
element.SetValue(LeftProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the Top attached property for a control.
|
|||
/// </summary>
|
|||
/// <param name="element">The control.</param>
|
|||
/// <returns>The control's top coordinate.</returns>
|
|||
public static double GetTop(PerspexObject element) |
|||
{ |
|||
return element.GetValue(TopProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the value of the Top attached property for a control.
|
|||
/// </summary>
|
|||
/// <param name="element">The control.</param>
|
|||
/// <param name="value">The top value.</param>
|
|||
public static void SetTop(PerspexObject element, double value) |
|||
{ |
|||
element.SetValue(TopProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the Right attached property for a control.
|
|||
/// </summary>
|
|||
/// <param name="element">The control.</param>
|
|||
/// <returns>The control's right coordinate.</returns>
|
|||
public static double GetRight(PerspexObject element) |
|||
{ |
|||
return element.GetValue(RightProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the value of the Right attached property for a control.
|
|||
/// </summary>
|
|||
/// <param name="element">The control.</param>
|
|||
/// <param name="value">The right value.</param>
|
|||
public static void SetRight(PerspexObject element, double value) |
|||
{ |
|||
element.SetValue(RightProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the Bottom attached property for a control.
|
|||
/// </summary>
|
|||
/// <param name="element">The control.</param>
|
|||
/// <returns>The control's bottom coordinate.</returns>
|
|||
public static double GetBottom(PerspexObject element) |
|||
{ |
|||
return element.GetValue(BottomProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the value of the Bottom attached property for a control.
|
|||
/// </summary>
|
|||
/// <param name="element">The control.</param>
|
|||
/// <param name="value">The bottom value.</param>
|
|||
public static void SetBottom(PerspexObject element, double value) |
|||
{ |
|||
element.SetValue(BottomProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the next control in the specified direction.
|
|||
/// </summary>
|
|||
/// <param name="direction">The movement direction.</param>
|
|||
/// <param name="from">The control from which movement begins.</param>
|
|||
/// <returns>The control.</returns>
|
|||
IInputElement INavigableContainer.GetControl(FocusNavigationDirection direction, IInputElement from) |
|||
{ |
|||
// TODO: Implement this
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Measures the control.
|
|||
/// </summary>
|
|||
/// <param name="availableSize">The available size.</param>
|
|||
/// <returns>The desired size of the control.</returns>
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity); |
|||
|
|||
foreach (Control child in Children) |
|||
{ |
|||
child.Measure(availableSize); |
|||
} |
|||
|
|||
return new Size(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Arranges the control's children.
|
|||
/// </summary>
|
|||
/// <param name="finalSize">The size allocated to the control.</param>
|
|||
/// <returns>The space taken.</returns>
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
foreach (Control child in Children) |
|||
{ |
|||
double x = 0.0; |
|||
double y = 0.0; |
|||
double elementLeft = GetLeft(child); |
|||
|
|||
if (!double.IsNaN(elementLeft)) |
|||
{ |
|||
x = elementLeft; |
|||
} |
|||
else |
|||
{ |
|||
// Arrange with right.
|
|||
double elementRight = GetRight(child); |
|||
if (!double.IsNaN(elementRight)) |
|||
{ |
|||
x = finalSize.Width - child.DesiredSize.Width - elementRight; |
|||
} |
|||
} |
|||
|
|||
double elementTop = GetTop(child); |
|||
if (!double.IsNaN(elementTop) ) |
|||
{ |
|||
y = elementTop; |
|||
} |
|||
else |
|||
{ |
|||
double elementBottom = GetBottom(child); |
|||
if (!double.IsNaN(elementBottom)) |
|||
{ |
|||
y = finalSize.Height - child.DesiredSize.Height - elementBottom; |
|||
} |
|||
} |
|||
|
|||
child.Arrange(new Rect(new Point(x, y), child.DesiredSize)); |
|||
} |
|||
|
|||
return finalSize; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using Perspex.Media.Imaging; |
|||
|
|||
namespace Perspex.Media |
|||
{ |
|||
/// <summary>
|
|||
/// Paints an area with an <see cref="IBitmap"/>.
|
|||
/// </summary>
|
|||
public class ImageBrush : TileBrush |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the <see cref="Visual"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<IBitmap> SourceProperty = |
|||
PerspexProperty.Register<ImageBrush, IBitmap>("Source"); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VisualBrush"/> class.
|
|||
/// </summary>
|
|||
public ImageBrush() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="VisualBrush"/> class.
|
|||
/// </summary>
|
|||
/// <param name="source">The image to draw.</param>
|
|||
public ImageBrush(IBitmap source) |
|||
{ |
|||
Source = source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the image to draw.
|
|||
/// </summary>
|
|||
public IBitmap Source |
|||
{ |
|||
get { return GetValue(SourceProperty); } |
|||
set { SetValue(SourceProperty, value); } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Media |
|||
{ |
|||
/// <summary>
|
|||
/// Paints an area with a radial gradient. A focal point defines the beginning of the gradient,
|
|||
/// and a circle defines the end point of the gradient.
|
|||
/// </summary>
|
|||
public sealed class RadialGradientBrush : GradientBrush |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the <see cref="Center"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<RelativePoint> CenterProperty = |
|||
PerspexProperty.Register<RadialGradientBrush, RelativePoint>( |
|||
nameof(Center), |
|||
RelativePoint.Center); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="GradientOrigin"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<RelativePoint> GradientOriginProperty = |
|||
PerspexProperty.Register<RadialGradientBrush, RelativePoint>( |
|||
nameof(GradientOrigin), |
|||
RelativePoint.Center); |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="Radius"/> property.
|
|||
/// </summary>
|
|||
public static readonly PerspexProperty<double> RadiusProperty = |
|||
PerspexProperty.Register<RadialGradientBrush, double>( |
|||
nameof(Radius), |
|||
0.5); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the start point for the gradient.
|
|||
/// </summary>
|
|||
public RelativePoint Center |
|||
{ |
|||
get { return GetValue(CenterProperty); } |
|||
set { SetValue(CenterProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the location of the two-dimensional focal point that defines the beginning of the gradient.
|
|||
/// </summary>
|
|||
public RelativePoint GradientOrigin |
|||
{ |
|||
get { return GetValue(GradientOriginProperty); } |
|||
set { SetValue(GradientOriginProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the horizontal and vertical radius of the outermost circle of the radial gradient.
|
|||
/// </summary>
|
|||
public double Radius |
|||
{ |
|||
get { return GetValue(RadiusProperty); } |
|||
set { SetValue(RadiusProperty, value); } |
|||
} |
|||
} |
|||
} |
|||