feat: initialize marquee, add demo.
This commit is contained in:
20
demo/Ursa.Demo/Pages/MarqueeDemo.axaml
Normal file
20
demo/Ursa.Demo/Pages/MarqueeDemo.axaml
Normal file
@@ -0,0 +1,20 @@
|
||||
<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:EnumSelector Name="direction" EnumType="{x:Type u:Direction}" />
|
||||
<u:Marquee
|
||||
Background="Red"
|
||||
Height="100"
|
||||
Direction="{Binding #direction.Value}" >
|
||||
<TextBlock Text="Hello World" VerticalAlignment="Center"/>
|
||||
</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.MenuKeyKeyGestureInput => new KeyGestureInputDemoViewModel(),
|
||||
MenuKeys.MenuKeyLoading => new LoadingDemoViewModel(),
|
||||
MenuKeys.MenuKeyMarquee => new MarqueeDemoViewModel(),
|
||||
MenuKeys.MenuKeyMessageBox => new MessageBoxDemoViewModel(),
|
||||
MenuKeys.MenuKeyMultiComboBox => new MultiComboBoxDemoViewModel(),
|
||||
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 = "KeyGestureInput", Key = MenuKeys.MenuKeyKeyGestureInput },
|
||||
new() { MenuHeader = "Loading", Key = MenuKeys.MenuKeyLoading },
|
||||
new() { MenuHeader = "Marquee", Key = MenuKeys.MenuKeyMarquee },
|
||||
new() { MenuHeader = "Message Box", Key = MenuKeys.MenuKeyMessageBox },
|
||||
new() { MenuHeader = "MultiComboBox", Key = MenuKeys.MenuKeyMultiComboBox, Status = "Updated" },
|
||||
new() { MenuHeader = "Nav Menu", Key = MenuKeys.MenuKeyNavMenu },
|
||||
@@ -89,6 +90,7 @@ public static class MenuKeys
|
||||
public const string MenuKeyIpBox = "IPv4Box";
|
||||
public const string MenuKeyKeyGestureInput = "KeyGestureInput";
|
||||
public const string MenuKeyLoading = "Loading";
|
||||
public const string MenuKeyMarquee = "Marquee";
|
||||
public const string MenuKeyMessageBox = "MessageBox";
|
||||
public const string MenuKeyMultiComboBox = "MultiComboBox";
|
||||
public const string MenuKeyNavMenu = "NavMenu";
|
||||
|
||||
19
src/Ursa.Themes.Semi/Controls/Marquee.axaml
Normal file
19
src/Ursa.Themes.Semi/Controls/Marquee.axaml
Normal file
@@ -0,0 +1,19 @@
|
||||
<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>
|
||||
<Canvas Background="{TemplateBinding Background}">
|
||||
<ContentPresenter
|
||||
Name="{x:Static iri:PartNames.PART_ContentPresenter}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
||||
</Canvas>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
@@ -27,6 +27,7 @@
|
||||
<ResourceInclude Source="IPv4Box.axaml" />
|
||||
<ResourceInclude Source="KeyGestureInput.axaml" />
|
||||
<ResourceInclude Source="Loading.axaml" />
|
||||
<ResourceInclude Source="Marquee.axaml" />
|
||||
<ResourceInclude Source="MessageBox.axaml" />
|
||||
<ResourceInclude Source="MultiComboBox.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,
|
||||
}
|
||||
150
src/Ursa/Controls/Marquee/Marquee.cs
Normal file
150
src/Ursa/Controls/Marquee/Marquee.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
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));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="MarqueeMode" /> 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);
|
||||
|
||||
private readonly Timer _timer;
|
||||
|
||||
static Marquee()
|
||||
{
|
||||
ClipToBoundsProperty.OverrideDefaultValue<Marquee>(true);
|
||||
}
|
||||
|
||||
public Marquee()
|
||||
{
|
||||
_timer = new Timer();
|
||||
_timer.Interval = 1000 / 60.0;
|
||||
_timer.Elapsed += TimerOnTick;
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public double Speed
|
||||
{
|
||||
get => GetValue(SpeedProperty);
|
||||
set => SetValue(SpeedProperty, value);
|
||||
}
|
||||
|
||||
private void TimerOnTick(object sender, System.EventArgs e)
|
||||
{
|
||||
Dispatcher.UIThread.Post(UpdateLocation, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
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 void UpdateLocation()
|
||||
{
|
||||
if (Presenter is null) return;
|
||||
var horizontalOffset = Direction switch
|
||||
{
|
||||
Direction.Up or Direction.Down => 0,
|
||||
Direction.Left or Direction.Right => Canvas.GetLeft(Presenter),
|
||||
};
|
||||
var verticalOffset = Direction switch
|
||||
{
|
||||
Direction.Up or Direction.Down => Canvas.GetTop(Presenter),
|
||||
Direction.Left or Direction.Right => 0,
|
||||
};
|
||||
if (horizontalOffset is double.NaN) horizontalOffset = 0.0;
|
||||
if (verticalOffset is double.NaN) verticalOffset = 0.0;
|
||||
var speed = Speed / 60.0;
|
||||
var diff = Direction switch
|
||||
{
|
||||
Direction.Up => -speed,
|
||||
Direction.Down => speed,
|
||||
Direction.Left => -speed,
|
||||
Direction.Right => speed,
|
||||
_ => 0
|
||||
};
|
||||
switch (Direction)
|
||||
{
|
||||
case Direction.Up:
|
||||
case Direction.Down:
|
||||
verticalOffset += diff;
|
||||
break;
|
||||
case Direction.Left:
|
||||
case Direction.Right:
|
||||
horizontalOffset += diff;
|
||||
break;
|
||||
}
|
||||
switch (Direction)
|
||||
{
|
||||
case Direction.Down:
|
||||
if (verticalOffset > Bounds.Height) verticalOffset = -Presenter.Bounds.Height;
|
||||
verticalOffset = MathHelpers.SafeClamp(verticalOffset, -Presenter.Bounds.Height, Bounds.Height);
|
||||
break;
|
||||
case Direction.Up:
|
||||
if (verticalOffset < -Presenter.Bounds.Height) verticalOffset = Bounds.Height;
|
||||
verticalOffset = MathHelpers.SafeClamp(verticalOffset, -Presenter.Bounds.Height, Bounds.Height);
|
||||
break;
|
||||
case Direction.Right:
|
||||
if (horizontalOffset > Bounds.Width) horizontalOffset = -Presenter.Bounds.Width;
|
||||
horizontalOffset = MathHelpers.SafeClamp(horizontalOffset, -Presenter.Bounds.Width, Bounds.Width);
|
||||
break;
|
||||
case Direction.Left:
|
||||
if (horizontalOffset < -Presenter.Bounds.Width) horizontalOffset = Bounds.Width;
|
||||
horizontalOffset = MathHelpers.SafeClamp(horizontalOffset, -Presenter.Bounds.Width, Bounds.Width);
|
||||
break;
|
||||
}
|
||||
Canvas.SetTop(Presenter, verticalOffset);
|
||||
Canvas.SetLeft(Presenter, horizontalOffset);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user