Merge pull request #514 from irihitech/runningtext
New control: Marquee
This commit is contained in:
54
demo/Ursa.Demo/Pages/MarqueeDemo.axaml
Normal file
54
demo/Ursa.Demo/Pages/MarqueeDemo.axaml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="Ursa.Demo.Pages.MarqueeDemo"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:u="https://irihi.tech/ursa"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<StackPanel>
|
||||||
|
<u:Form>
|
||||||
|
<TextBox Width="300" Name="content" u:FormItem.Label="Content" Text="Hello Avalonia"></TextBox>
|
||||||
|
<u:EnumSelector
|
||||||
|
u:FormItem.Label="Direction"
|
||||||
|
Name="direction"
|
||||||
|
EnumType="{x:Type u:Direction}"
|
||||||
|
Value="{x:Static u:Direction.Left}" />
|
||||||
|
<u:EnumSelector
|
||||||
|
Name="horizontal"
|
||||||
|
u:FormItem.Label="HorizontalContentAlignment"
|
||||||
|
EnumType="{x:Type HorizontalAlignment}"
|
||||||
|
Value="{x:Static HorizontalAlignment.Center}" />
|
||||||
|
<u:EnumSelector
|
||||||
|
Name="vertical"
|
||||||
|
u:FormItem.Label="VerticalContentAlignment"
|
||||||
|
EnumType="{x:Type VerticalAlignment}"
|
||||||
|
Value="{x:Static VerticalAlignment.Center}" />
|
||||||
|
<u:NumericDoubleUpDown
|
||||||
|
Name="speed"
|
||||||
|
AllowDrag="True"
|
||||||
|
u:FormItem.Label="Speed"
|
||||||
|
Minimum="-100"
|
||||||
|
Maximum="300"
|
||||||
|
Value="60"/>
|
||||||
|
<ToggleSwitch
|
||||||
|
Name="running"
|
||||||
|
u:FormItem.Label="IsRunning"
|
||||||
|
IsChecked="True" />
|
||||||
|
</u:Form>
|
||||||
|
|
||||||
|
<u:Marquee
|
||||||
|
Height="100"
|
||||||
|
HorizontalContentAlignment="{Binding #horizontal.Value}"
|
||||||
|
VerticalContentAlignment="{Binding #vertical.Value}"
|
||||||
|
Speed="{Binding #speed.Value}"
|
||||||
|
Background="{DynamicResource SemiBlue1}"
|
||||||
|
Direction="{Binding #direction.Value}"
|
||||||
|
Content="{Binding #content.Text}"
|
||||||
|
IsRunning="{Binding #running.IsChecked}">
|
||||||
|
|
||||||
|
</u:Marquee>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
13
demo/Ursa.Demo/Pages/MarqueeDemo.axaml.cs
Normal file
13
demo/Ursa.Demo/Pages/MarqueeDemo.axaml.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace Ursa.Demo.Pages;
|
||||||
|
|
||||||
|
public partial class MarqueeDemo : UserControl
|
||||||
|
{
|
||||||
|
public MarqueeDemo()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,6 +54,7 @@ public partial class MainViewViewModel : ViewModelBase
|
|||||||
MenuKeys.MenuKeyIpBox => new IPv4BoxDemoViewModel(),
|
MenuKeys.MenuKeyIpBox => new IPv4BoxDemoViewModel(),
|
||||||
MenuKeys.MenuKeyKeyGestureInput => new KeyGestureInputDemoViewModel(),
|
MenuKeys.MenuKeyKeyGestureInput => new KeyGestureInputDemoViewModel(),
|
||||||
MenuKeys.MenuKeyLoading => new LoadingDemoViewModel(),
|
MenuKeys.MenuKeyLoading => new LoadingDemoViewModel(),
|
||||||
|
MenuKeys.MenuKeyMarquee => new MarqueeDemoViewModel(),
|
||||||
MenuKeys.MenuKeyMessageBox => new MessageBoxDemoViewModel(),
|
MenuKeys.MenuKeyMessageBox => new MessageBoxDemoViewModel(),
|
||||||
MenuKeys.MenuKeyMultiComboBox => new MultiComboBoxDemoViewModel(),
|
MenuKeys.MenuKeyMultiComboBox => new MultiComboBoxDemoViewModel(),
|
||||||
MenuKeys.MenuKeyNavMenu => new NavMenuDemoViewModel(),
|
MenuKeys.MenuKeyNavMenu => new NavMenuDemoViewModel(),
|
||||||
|
|||||||
8
demo/Ursa.Demo/ViewModels/MarqueeDemoViewModel.cs
Normal file
8
demo/Ursa.Demo/ViewModels/MarqueeDemoViewModel.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
|
public class MarqueeDemoViewModel: ViewModelBase
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ public class MenuViewModel : ViewModelBase
|
|||||||
new() { MenuHeader = "IPv4Box", Key = MenuKeys.MenuKeyIpBox },
|
new() { MenuHeader = "IPv4Box", Key = MenuKeys.MenuKeyIpBox },
|
||||||
new() { MenuHeader = "KeyGestureInput", Key = MenuKeys.MenuKeyKeyGestureInput },
|
new() { MenuHeader = "KeyGestureInput", Key = MenuKeys.MenuKeyKeyGestureInput },
|
||||||
new() { MenuHeader = "Loading", Key = MenuKeys.MenuKeyLoading },
|
new() { MenuHeader = "Loading", Key = MenuKeys.MenuKeyLoading },
|
||||||
|
new() { MenuHeader = "Marquee", Key = MenuKeys.MenuKeyMarquee },
|
||||||
new() { MenuHeader = "Message Box", Key = MenuKeys.MenuKeyMessageBox },
|
new() { MenuHeader = "Message Box", Key = MenuKeys.MenuKeyMessageBox },
|
||||||
new() { MenuHeader = "MultiComboBox", Key = MenuKeys.MenuKeyMultiComboBox, Status = "Updated" },
|
new() { MenuHeader = "MultiComboBox", Key = MenuKeys.MenuKeyMultiComboBox, Status = "Updated" },
|
||||||
new() { MenuHeader = "Nav Menu", Key = MenuKeys.MenuKeyNavMenu },
|
new() { MenuHeader = "Nav Menu", Key = MenuKeys.MenuKeyNavMenu },
|
||||||
@@ -89,6 +90,7 @@ public static class MenuKeys
|
|||||||
public const string MenuKeyIpBox = "IPv4Box";
|
public const string MenuKeyIpBox = "IPv4Box";
|
||||||
public const string MenuKeyKeyGestureInput = "KeyGestureInput";
|
public const string MenuKeyKeyGestureInput = "KeyGestureInput";
|
||||||
public const string MenuKeyLoading = "Loading";
|
public const string MenuKeyLoading = "Loading";
|
||||||
|
public const string MenuKeyMarquee = "Marquee";
|
||||||
public const string MenuKeyMessageBox = "MessageBox";
|
public const string MenuKeyMessageBox = "MessageBox";
|
||||||
public const string MenuKeyMultiComboBox = "MultiComboBox";
|
public const string MenuKeyMultiComboBox = "MultiComboBox";
|
||||||
public const string MenuKeyNavMenu = "NavMenu";
|
public const string MenuKeyNavMenu = "NavMenu";
|
||||||
|
|||||||
26
src/Ursa.Themes.Semi/Controls/Marquee.axaml
Normal file
26
src/Ursa.Themes.Semi/Controls/Marquee.axaml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<ResourceDictionary
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:iri="https://irihi.tech/shared"
|
||||||
|
xmlns:u="https://irihi.tech/ursa">
|
||||||
|
<!-- Add Resources Here -->
|
||||||
|
<ControlTheme x:Key="{x:Type u:Marquee}" TargetType="u:Marquee">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}">
|
||||||
|
<Canvas>
|
||||||
|
<ContentPresenter
|
||||||
|
Name="{x:Static iri:PartNames.PART_ContentPresenter}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
||||||
|
</Canvas>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</ControlTheme>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
<ResourceInclude Source="IPv4Box.axaml" />
|
<ResourceInclude Source="IPv4Box.axaml" />
|
||||||
<ResourceInclude Source="KeyGestureInput.axaml" />
|
<ResourceInclude Source="KeyGestureInput.axaml" />
|
||||||
<ResourceInclude Source="Loading.axaml" />
|
<ResourceInclude Source="Loading.axaml" />
|
||||||
|
<ResourceInclude Source="Marquee.axaml" />
|
||||||
<ResourceInclude Source="MessageBox.axaml" />
|
<ResourceInclude Source="MessageBox.axaml" />
|
||||||
<ResourceInclude Source="MultiComboBox.axaml" />
|
<ResourceInclude Source="MultiComboBox.axaml" />
|
||||||
<ResourceInclude Source="NavMenu.axaml" />
|
<ResourceInclude Source="NavMenu.axaml" />
|
||||||
|
|||||||
11
src/Ursa/Controls/Marquee/Direction.cs
Normal file
11
src/Ursa/Controls/Marquee/Direction.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public enum Direction
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
278
src/Ursa/Controls/Marquee/Marquee.cs
Normal file
278
src/Ursa/Controls/Marquee/Marquee.cs
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using Irihi.Avalonia.Shared.Helpers;
|
||||||
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public class Marquee : ContentControl
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the <see cref="IsRunning" /> property.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly StyledProperty<bool> IsRunningProperty = AvaloniaProperty.Register<Marquee, bool>(
|
||||||
|
nameof(IsRunning), true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the <see cref="Direction" /> property.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly StyledProperty<Direction> DirectionProperty = AvaloniaProperty.Register<Marquee, Direction>(
|
||||||
|
nameof(Direction));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the <see cref="Speed" /> property.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly StyledProperty<double> SpeedProperty = AvaloniaProperty.Register<Marquee, double>(
|
||||||
|
nameof(Speed), 60.0, coerce: OnCoerceSpeed);
|
||||||
|
|
||||||
|
private static double OnCoerceSpeed(AvaloniaObject arg1, double arg2)
|
||||||
|
{
|
||||||
|
if (arg2 < 0) return 0;
|
||||||
|
return arg2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Timer? _timer;
|
||||||
|
|
||||||
|
static Marquee()
|
||||||
|
{
|
||||||
|
ClipToBoundsProperty.OverrideDefaultValue<Marquee>(true);
|
||||||
|
HorizontalContentAlignmentProperty.OverrideDefaultValue<Marquee>(HorizontalAlignment.Center);
|
||||||
|
VerticalContentAlignmentProperty.OverrideDefaultValue<Marquee>(VerticalAlignment.Center);
|
||||||
|
HorizontalContentAlignmentProperty.Changed.AddClassHandler<Marquee>((o,_)=>o.InvalidatePresenterPosition());
|
||||||
|
VerticalContentAlignmentProperty.Changed.AddClassHandler<Marquee>((o,_)=>o.InvalidatePresenterPosition());
|
||||||
|
IsRunningProperty.Changed.AddClassHandler<Marquee, bool>((o, args) => o.OnIsRunningChanged(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnIsRunningChanged(AvaloniaPropertyChangedEventArgs<bool> args)
|
||||||
|
{
|
||||||
|
if (args.NewValue.Value)
|
||||||
|
{
|
||||||
|
_timer?.Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_timer?.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Marquee()
|
||||||
|
{
|
||||||
|
_timer = new Timer();
|
||||||
|
_timer.Interval = 1000 / 60.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnAttachedToVisualTree(e);
|
||||||
|
if (Presenter is not null)
|
||||||
|
{
|
||||||
|
Presenter.SizeChanged+= OnPresenterSizeChanged;
|
||||||
|
}
|
||||||
|
_timer?.Stop();
|
||||||
|
_timer?.Dispose();
|
||||||
|
_timer = new Timer();
|
||||||
|
_timer.Interval = 1000 / 60.0;
|
||||||
|
_timer.Elapsed += TimerOnTick;
|
||||||
|
if (IsRunning)
|
||||||
|
{
|
||||||
|
_timer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnDetachedFromVisualTree(e);
|
||||||
|
if (_timer is not null)
|
||||||
|
{
|
||||||
|
_timer.Elapsed -= TimerOnTick;
|
||||||
|
_timer.Stop();
|
||||||
|
_timer.Dispose();
|
||||||
|
}
|
||||||
|
if (Presenter is not null)
|
||||||
|
{
|
||||||
|
Presenter.SizeChanged -= OnPresenterSizeChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the marquee is running.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRunning
|
||||||
|
{
|
||||||
|
get => GetValue(IsRunningProperty);
|
||||||
|
set => SetValue(IsRunningProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the direction of the marquee.
|
||||||
|
/// </summary>
|
||||||
|
public Direction Direction
|
||||||
|
{
|
||||||
|
get => GetValue(DirectionProperty);
|
||||||
|
set => SetValue(DirectionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the speed of the marquee. Point per second.
|
||||||
|
/// </summary>
|
||||||
|
public double Speed
|
||||||
|
{
|
||||||
|
get => GetValue(SpeedProperty);
|
||||||
|
set => SetValue(SpeedProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPresenterSizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
InvalidatePresenterPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void TimerOnTick(object sender, System.EventArgs e)
|
||||||
|
{
|
||||||
|
if (Presenter is null) return;
|
||||||
|
var layoutValues = Dispatcher.UIThread.Invoke(GetLayoutValues);
|
||||||
|
var location = UpdateLocation(layoutValues);
|
||||||
|
if (location is null) return;
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
Canvas.SetTop(Presenter, location.Value.top);
|
||||||
|
Canvas.SetLeft(Presenter, location.Value.left);
|
||||||
|
}, DispatcherPriority.Render);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InvalidatePresenterPosition()
|
||||||
|
{
|
||||||
|
if (Presenter is null) return;
|
||||||
|
var layoutValues = GetLayoutValues();
|
||||||
|
var location = UpdateLocation(layoutValues);
|
||||||
|
if (location is null) return;
|
||||||
|
Canvas.SetTop(Presenter, location.Value.top);
|
||||||
|
Canvas.SetLeft(Presenter, location.Value.left);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
|
{
|
||||||
|
var result = base.MeasureOverride(availableSize);
|
||||||
|
var presenter = Presenter;
|
||||||
|
if (presenter is null) return result;
|
||||||
|
var size = presenter.DesiredSize;
|
||||||
|
if (double.IsInfinity(result.Width) || result.Width == 0)
|
||||||
|
{
|
||||||
|
result = result.WithWidth(size.Width);
|
||||||
|
}
|
||||||
|
if (double.IsInfinity(result.Height) || result.Height == 0)
|
||||||
|
{
|
||||||
|
result = result.WithHeight(size.Height);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private (double top, double left)? UpdateLocation(LayoutValues values)
|
||||||
|
{
|
||||||
|
var horizontalOffset = values.Direction switch
|
||||||
|
{
|
||||||
|
Direction.Up or Direction.Down => GetHorizontalOffset(values.Bounds, values.PresenterSize, values.HorizontalAlignment),
|
||||||
|
Direction.Left or Direction.Right => values.Left,
|
||||||
|
};
|
||||||
|
var verticalOffset = values.Direction switch
|
||||||
|
{
|
||||||
|
Direction.Up or Direction.Down => values.Top,
|
||||||
|
Direction.Left or Direction.Right => GetVerticalOffset(values.Bounds, values.PresenterSize, values.VerticalAlignment),
|
||||||
|
};
|
||||||
|
if (horizontalOffset is double.NaN) horizontalOffset = 0.0;
|
||||||
|
if (verticalOffset is double.NaN) verticalOffset = 0.0;
|
||||||
|
var speed = values.Diff;
|
||||||
|
var diff = values.Direction switch
|
||||||
|
{
|
||||||
|
Direction.Up => -speed,
|
||||||
|
Direction.Down => speed,
|
||||||
|
Direction.Left => -speed,
|
||||||
|
Direction.Right => speed,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
switch (values.Direction)
|
||||||
|
{
|
||||||
|
case Direction.Up:
|
||||||
|
case Direction.Down:
|
||||||
|
verticalOffset += diff;
|
||||||
|
break;
|
||||||
|
case Direction.Left:
|
||||||
|
case Direction.Right:
|
||||||
|
horizontalOffset += diff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (values.Direction)
|
||||||
|
{
|
||||||
|
case Direction.Down:
|
||||||
|
if (verticalOffset > values.Bounds.Height) verticalOffset = -values.PresenterSize.Height;
|
||||||
|
break;
|
||||||
|
case Direction.Up:
|
||||||
|
if (verticalOffset < -values.PresenterSize.Height) verticalOffset = values.Bounds.Height;
|
||||||
|
break;
|
||||||
|
case Direction.Right:
|
||||||
|
if (horizontalOffset > values.Bounds.Width) horizontalOffset = -values.PresenterSize.Width;
|
||||||
|
break;
|
||||||
|
case Direction.Left:
|
||||||
|
if (horizontalOffset < -values.PresenterSize.Width) horizontalOffset = values.Bounds.Width;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
verticalOffset = MathHelpers.SafeClamp(verticalOffset, -values.PresenterSize.Height, values.Bounds.Height);
|
||||||
|
horizontalOffset = MathHelpers.SafeClamp(horizontalOffset, -values.PresenterSize.Width, values.Bounds.Width);
|
||||||
|
return (verticalOffset, horizontalOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private double GetHorizontalOffset(Size bounds, Size presenterBounds, HorizontalAlignment horizontalAlignment)
|
||||||
|
{
|
||||||
|
return horizontalAlignment switch
|
||||||
|
{
|
||||||
|
HorizontalAlignment.Left => 0,
|
||||||
|
HorizontalAlignment.Center => (bounds.Width - presenterBounds.Width) / 2,
|
||||||
|
HorizontalAlignment.Right => bounds.Width - presenterBounds.Width,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private double GetVerticalOffset(Size bounds, Size presenterBounds, VerticalAlignment verticalAlignment)
|
||||||
|
{
|
||||||
|
return verticalAlignment switch
|
||||||
|
{
|
||||||
|
VerticalAlignment.Top => 0,
|
||||||
|
VerticalAlignment.Center => (bounds.Height - presenterBounds.Height) / 2,
|
||||||
|
VerticalAlignment.Bottom => bounds.Height - presenterBounds.Height,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private LayoutValues GetLayoutValues()
|
||||||
|
{
|
||||||
|
return new LayoutValues
|
||||||
|
{
|
||||||
|
Bounds = Bounds.Size,
|
||||||
|
PresenterSize = Presenter?.Bounds.Size ?? new Size(),
|
||||||
|
Left = Presenter is null? 0 : Canvas.GetLeft(Presenter),
|
||||||
|
Top = Presenter is null? 0 : Canvas.GetTop(Presenter),
|
||||||
|
Diff = IsRunning ? Speed / 60.0 : 0,
|
||||||
|
HorizontalAlignment = HorizontalContentAlignment,
|
||||||
|
VerticalAlignment = VerticalContentAlignment,
|
||||||
|
Direction = Direction
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LayoutValues
|
||||||
|
{
|
||||||
|
public Size Bounds { get; set; }
|
||||||
|
public Size PresenterSize { get; set; }
|
||||||
|
public double Left { get; set; }
|
||||||
|
public double Top { get; set; }
|
||||||
|
public double Diff { get; set; }
|
||||||
|
public Direction Direction { get; set; }
|
||||||
|
public HorizontalAlignment HorizontalAlignment { get; set; }
|
||||||
|
public VerticalAlignment VerticalAlignment { get; set; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user