feat: wip: implement year view notification.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
x:Class="Ursa.Demo.Pages.DatePickerDemo">
|
||||
<StackPanel Margin="20" HorizontalAlignment="Left">
|
||||
<u:CalendarMonthView />
|
||||
<u:CalendarYearView />
|
||||
<u:Calendar/>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
xmlns:u="https://irihi.tech/ursa">
|
||||
<Design.PreviewWith>
|
||||
<StackPanel Margin="20" Spacing="5">
|
||||
<u:CalendarMonthView/>
|
||||
<u:CalendarMonthView />
|
||||
<u:Calendar />
|
||||
</StackPanel>
|
||||
</Design.PreviewWith>
|
||||
@@ -42,13 +42,13 @@
|
||||
<Setter Property="CornerRadius" Value="0" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style Selector="^:today">
|
||||
<Setter Property="Background" Value="{DynamicResource SemiGrey1}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiBlue5}" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style Selector="^:not-current-month">
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiGrey3}" />
|
||||
</Style>
|
||||
@@ -121,6 +121,7 @@
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:Calendar}" TargetType="u:Calendar">
|
||||
<Setter Property="MinHeight" Value="300"></Setter>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:Calendar">
|
||||
<Panel>
|
||||
@@ -152,13 +153,20 @@
|
||||
Foreground="{DynamicResource CalendarItemIconForeground}" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Name="PART_HeaderButton"
|
||||
Grid.Column="2"
|
||||
HorizontalContentAlignment="Center"
|
||||
Content="Apr 2024"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Theme="{DynamicResource BorderlessButton}" />
|
||||
<Grid ColumnDefinitions="*, *" Grid.Column="2">
|
||||
<Button Grid.Column="0"
|
||||
Name="{x:Static u:Calendar.PART_YearButton}"
|
||||
HorizontalContentAlignment="Center"
|
||||
Content="2024"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Theme="{DynamicResource BorderlessButton}" />
|
||||
<Button Grid.Column="1"
|
||||
Name="{x:Static u:Calendar.PART_MonthButton}"
|
||||
HorizontalContentAlignment="Center"
|
||||
Content="Apr"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Theme="{DynamicResource BorderlessButton}" />
|
||||
</Grid>
|
||||
|
||||
<Button
|
||||
Name="{x:Static u:Calendar.PART_NextButton}"
|
||||
@@ -185,28 +193,95 @@
|
||||
Foreground="{DynamicResource CalendarItemIconForeground}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<u:CalendarMonthView Grid.Row="1" Name="{x:Static u:Calendar.PART_MonthView}"></u:CalendarMonthView>
|
||||
<u:CalendarMonthView
|
||||
Name="{x:Static u:Calendar.PART_MonthView}"
|
||||
Grid.Row="1"
|
||||
VerticalAlignment="Top"
|
||||
IsVisible="{TemplateBinding IsMonthMode}" />
|
||||
<u:CalendarYearView
|
||||
Name="{x:Static u:Calendar.PART_YearView}"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
IsVisible="{TemplateBinding IsMonthMode,
|
||||
Converter={x:Static BoolConverters.Not}}" />
|
||||
</Grid>
|
||||
|
||||
|
||||
</Panel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme TargetType="u:CalendarMonthView" x:Key="{x:Type u:CalendarMonthView}">
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:CalendarMonthView}" TargetType="u:CalendarMonthView">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid Name="{x:Static u:CalendarMonthView.PART_Grid}" ColumnDefinitions="*, *, *, *, *, *, *" RowDefinitions="*, Auto, *, *, *, *, *, *">
|
||||
<Grid
|
||||
Name="{x:Static u:CalendarMonthView.PART_Grid}"
|
||||
ColumnDefinitions="*, *, *, *, *, *, *"
|
||||
RowDefinitions="*, Auto, *, *, *, *, *, *">
|
||||
<Rectangle
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="7"
|
||||
Height="1"
|
||||
Margin="8 8 8 0"
|
||||
Margin="8,8,8,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Fill="{DynamicResource SemiGrey2}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:CalendarYearView}" TargetType="u:CalendarYearView">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid
|
||||
Name="{x:Static u:CalendarYearView.PART_Grid}"
|
||||
ColumnDefinitions="*, *, *"
|
||||
RowDefinitions="*, *, *, *" />
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:CalendarYearButton}" TargetType="u:CalendarYearButton">
|
||||
<Setter Property="MinWidth" Value="32" />
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="Margin" Value="0 2" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="3" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:CalendarDayButton">
|
||||
<Panel>
|
||||
<Border
|
||||
Name="PART_Background"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<ContentPresenter
|
||||
Name="PART_ContentPresenter"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Content="{TemplateBinding Content}"
|
||||
Foreground="{TemplateBinding Foreground}" />
|
||||
</Border>
|
||||
</Panel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^:pointerover">
|
||||
<Setter Property="Background" Value="{DynamicResource SemiGrey1}" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
</Style>
|
||||
<Style Selector="^:selected">
|
||||
<Setter Property="Background" Value="{DynamicResource SemiBlue5}" />
|
||||
<Setter Property="CornerRadius" Value="3" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Style Selector="^:pointerover">
|
||||
<Setter Property="Background" Value="{DynamicResource SemiBlue6}" />
|
||||
</Style>
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -4,6 +4,7 @@ using Avalonia.Controls;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
using Irihi.Avalonia.Shared.Helpers;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
@@ -11,8 +12,9 @@ namespace Ursa.Controls;
|
||||
[TemplatePart(PART_PreviousYearButton, typeof(Button))]
|
||||
[TemplatePart(PART_NextButton, typeof(Button))]
|
||||
[TemplatePart(PART_PreviousButton, typeof(Button))]
|
||||
[TemplatePart(PART_YearButton, typeof(Button))]
|
||||
[TemplatePart(PART_MonthButton, typeof(Button))]
|
||||
[TemplatePart(PART_HeaderButton, typeof(Button))]
|
||||
[TemplatePart(PART_BackButton, typeof(Button))]
|
||||
[TemplatePart(PART_MonthView, typeof(CalendarMonthView))]
|
||||
[TemplatePart(PART_YearView, typeof(CalendarYearView))]
|
||||
public class Calendar: TemplatedControl
|
||||
@@ -21,14 +23,18 @@ public class Calendar: TemplatedControl
|
||||
public const string PART_PreviousYearButton = "PART_PreviousYearButton";
|
||||
public const string PART_NextButton = "PART_NextButton";
|
||||
public const string PART_PreviousButton = "PART_PreviousButton";
|
||||
public const string PART_HeaderButton = "PART_HeaderButton";
|
||||
public const string PART_BackButton = "PART_BackButton";
|
||||
public const string PART_YearButton = "PART_YearButton";
|
||||
public const string PART_MonthButton = "PART_MonthButton";
|
||||
public const string PART_MonthView = "PART_MonthView";
|
||||
public const string PART_YearView = "PART_YearView";
|
||||
public const string PART_HeaderButton = "PART_HeaderButton";
|
||||
|
||||
private CalendarMonthView? _monthView;
|
||||
private CalendarYearView? _yearView;
|
||||
private DatePickerState _state = DatePickerState.None;
|
||||
private Button? _yearButton;
|
||||
private Button? _monthButton;
|
||||
private Button? _headerButton;
|
||||
|
||||
|
||||
public static readonly StyledProperty<DateTime> SelectedDateProperty = AvaloniaProperty.Register<Calendar, DateTime>(nameof(SelectedDate), DateTime.Now);
|
||||
@@ -73,6 +79,17 @@ public class Calendar: TemplatedControl
|
||||
set => SetValue(BlackoutDateRuleProperty, value);
|
||||
}
|
||||
|
||||
private bool _isMonthMode = true;
|
||||
|
||||
public static readonly DirectProperty<Calendar, bool> IsMonthModeProperty = AvaloniaProperty.RegisterDirect<Calendar, bool>(
|
||||
nameof(IsMonthMode), o => o.IsMonthMode, (o, v) => o.IsMonthMode = v);
|
||||
|
||||
public bool IsMonthMode
|
||||
{
|
||||
get => _isMonthMode;
|
||||
set => SetAndRaise(IsMonthModeProperty, ref _isMonthMode, value);
|
||||
}
|
||||
|
||||
internal DateTime? StartDate;
|
||||
internal DateTime? EndDate;
|
||||
|
||||
@@ -84,13 +101,50 @@ public class Calendar: TemplatedControl
|
||||
_monthView.OnDateSelected -= OnDateSelected;
|
||||
_monthView.OnDatePreviewed -= OnDatePreviewed;
|
||||
}
|
||||
|
||||
if (_yearView is not null)
|
||||
{
|
||||
_yearView.OnMonthSelected -= OnMonthSelected;
|
||||
}
|
||||
Button.ClickEvent.RemoveHandler(OnYearButtonClick, _yearButton);
|
||||
Button.ClickEvent.RemoveHandler(OnMonthButtonClick, _monthButton);
|
||||
_monthView = e.NameScope.Find<CalendarMonthView>(PART_MonthView);
|
||||
_yearView = e.NameScope.Find<CalendarYearView>(PART_YearView);
|
||||
_yearButton = e.NameScope.Find<Button>(PART_YearButton);
|
||||
_monthButton = e.NameScope.Find<Button>(PART_MonthButton);
|
||||
|
||||
if(_monthView is not null)
|
||||
{
|
||||
_monthView.OnDateSelected += OnDateSelected;
|
||||
_monthView.OnDatePreviewed += OnDatePreviewed;
|
||||
}
|
||||
|
||||
if (_yearView is not null)
|
||||
{
|
||||
_yearView.OnMonthSelected += OnMonthSelected;
|
||||
}
|
||||
Button.ClickEvent.AddHandler(OnYearButtonClick, _yearButton);
|
||||
Button.ClickEvent.AddHandler(OnMonthButtonClick, _monthButton);
|
||||
}
|
||||
|
||||
private void OnMonthSelected(object sender, CalendarYearButtonEventArgs e)
|
||||
{
|
||||
SetCurrentValue(IsMonthModeProperty, true);
|
||||
}
|
||||
|
||||
private void OnMonthButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SetCurrentValue(IsMonthModeProperty, false);
|
||||
if (_yearView is null) return;
|
||||
_yearView.Mode = CalendarYearViewMode.Month;
|
||||
}
|
||||
|
||||
private void OnYearButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_yearView is null) return;
|
||||
_yearView.Mode = CalendarYearViewMode.Year;
|
||||
SetCurrentValue(IsMonthModeProperty, false);
|
||||
|
||||
}
|
||||
|
||||
private void OnDatePreviewed(object sender, CalendarDayButtonEventArgs e)
|
||||
|
||||
@@ -107,7 +107,10 @@ public class CalendarMonthView : TemplatedControl
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set day buttons according to context date.
|
||||
/// </summary>
|
||||
/// <param name="date"></param>
|
||||
private void SetDayButtons(DateTime date)
|
||||
{
|
||||
if (_grid is null) return;
|
||||
@@ -149,8 +152,7 @@ public class CalendarMonthView : TemplatedControl
|
||||
if (children[i] is CalendarDayButton { DataContext: DateTime d } button && d.Month != _contextDate.Month)
|
||||
button.IsNotCurrentMonth = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public event EventHandler<CalendarDayButtonEventArgs>? OnDateSelected;
|
||||
public event EventHandler<CalendarDayButtonEventArgs>? OnDatePreviewed;
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class CalendarStateMachine
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public enum CalendarState
|
||||
{
|
||||
PreviewingStartDate,
|
||||
PreviewingEndDate,
|
||||
SelectingStartDate,
|
||||
SelectingEndDate,
|
||||
}
|
||||
@@ -1,17 +1,25 @@
|
||||
using System.Diagnostics;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Irihi.Avalonia.Shared.Common;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
internal enum CalendarYearViewMode
|
||||
{
|
||||
Month,
|
||||
Year,
|
||||
// The button represents ten year, with one year before and one year after, 12 in total.
|
||||
// The button represents 10 years.
|
||||
YearRange,
|
||||
}
|
||||
|
||||
[PseudoClasses(PC_Range, PseudoClassName.PC_Selected)]
|
||||
public class CalendarYearButton: ContentControl
|
||||
{
|
||||
public const string PC_Range = ":range";
|
||||
static CalendarYearButton()
|
||||
{
|
||||
PressedMixin.Attach<CalendarYearButton>();
|
||||
@@ -26,6 +34,15 @@ public class CalendarYearButton: ContentControl
|
||||
internal int EndYear { get; private set; }
|
||||
|
||||
internal CalendarYearViewMode Mode { get; private set; }
|
||||
|
||||
public static readonly RoutedEvent<CalendarYearButtonEventArgs> ItemSelectedEvent = RoutedEvent.Register<CalendarYearButton, CalendarYearButtonEventArgs>(
|
||||
nameof(ItemSelected), RoutingStrategies.Bubble);
|
||||
|
||||
public event EventHandler<CalendarDayButtonEventArgs> ItemSelected
|
||||
{
|
||||
add => AddHandler(ItemSelectedEvent, value);
|
||||
remove => RemoveHandler(ItemSelectedEvent, value);
|
||||
}
|
||||
|
||||
internal void SetValues(CalendarYearViewMode mode, DateTime contextDate, int? month = null, int? year = null, int? startYear = null, int? endYear = null)
|
||||
{
|
||||
@@ -43,4 +60,12 @@ public class CalendarYearButton: ContentControl
|
||||
_ => Content
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||
{
|
||||
base.OnPointerPressed(e);
|
||||
RaiseEvent(new CalendarYearButtonEventArgs(Mode, Year, Month, StartYear, EndYear)
|
||||
{ RoutedEvent = ItemSelectedEvent, Source = this });
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,17 @@ namespace Ursa.Controls;
|
||||
|
||||
public class CalendarYearButtonEventArgs: RoutedEventArgs
|
||||
{
|
||||
|
||||
internal int? Year { get; }
|
||||
internal int? Month { get; }
|
||||
internal int? StartYear { get; }
|
||||
internal int? EndYear { get; }
|
||||
internal CalendarYearViewMode Mode { get; }
|
||||
internal CalendarYearButtonEventArgs( CalendarYearViewMode mode, int? year, int? month, int? startYear, int? endYear )
|
||||
{
|
||||
Year = year;
|
||||
Month = month;
|
||||
StartYear = startYear;
|
||||
EndYear = endYear;
|
||||
Mode = mode;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using Avalonia.Controls;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Irihi.Avalonia.Shared.Helpers;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
@@ -15,36 +16,21 @@ namespace Ursa.Controls;
|
||||
public class CalendarYearView: TemplatedControl
|
||||
{
|
||||
public const string PART_Grid = "PART_Grid";
|
||||
|
||||
private CalendarYearViewMode _mode = CalendarYearViewMode.Month;
|
||||
private readonly CalendarYearButton[] _buttons = new CalendarYearButton[12];
|
||||
|
||||
internal CalendarYearViewMode Mode
|
||||
{
|
||||
get => _mode;
|
||||
set
|
||||
{
|
||||
_mode = value;
|
||||
RefreshButtons();
|
||||
}
|
||||
}
|
||||
public event EventHandler<CalendarYearButtonEventArgs>? OnMonthSelected;
|
||||
|
||||
private DateTime _contextDate = DateTime.Today;
|
||||
internal DateTime ContextDate
|
||||
{
|
||||
get => _contextDate;
|
||||
set
|
||||
{
|
||||
_contextDate = value;
|
||||
RefreshButtons();
|
||||
}
|
||||
}
|
||||
internal CalendarYearViewMode Mode { get; set; } = CalendarYearViewMode.Month;
|
||||
internal DateTime ContextDate { get; set; } = DateTime.Today;
|
||||
|
||||
private Grid? _grid;
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
ContextDate = DateTime.Today;
|
||||
base.OnApplyTemplate(e);
|
||||
_grid = e.NameScope.Find<Grid>(PART_Grid);
|
||||
GenerateGridElements();
|
||||
RefreshButtons();
|
||||
}
|
||||
|
||||
public void GenerateGridElements()
|
||||
@@ -59,7 +45,43 @@ public class CalendarYearView: TemplatedControl
|
||||
var button = new CalendarYearButton();
|
||||
Grid.SetRow(button, i / 3);
|
||||
Grid.SetColumn(button, i % 3);
|
||||
button.AddHandler(CalendarYearButton.ItemSelectedEvent, OnItemSelected);
|
||||
_grid.Children.Add(button);
|
||||
_buttons[i] = button;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemSelected(object sender, CalendarYearButtonEventArgs e)
|
||||
{
|
||||
if (_grid is null) return;
|
||||
var buttons = _grid.Children.OfType<CalendarYearButton>().ToList();
|
||||
if (e.Mode == CalendarYearViewMode.Month)
|
||||
{
|
||||
if (e.Month is null) return;
|
||||
var day = MathHelpers.SafeClamp(e.Month.Value, 0, DateTime.DaysInMonth(ContextDate.Year, e.Month.Value+1));
|
||||
ContextDate = new DateTime(ContextDate.Year, e.Month.Value+1, day+1);
|
||||
OnMonthSelected?.Invoke(this, e);
|
||||
}
|
||||
else if (e.Mode == CalendarYearViewMode.Year)
|
||||
{
|
||||
// Set CalendarYearView to Month mode
|
||||
for (var i = 0; i < 12; i++)
|
||||
{
|
||||
buttons[i].SetValues(CalendarYearViewMode.Month, ContextDate, month: i);
|
||||
}
|
||||
ContextDate = new DateTime(e.Year!.Value, ContextDate.Month, 1);
|
||||
Mode = CalendarYearViewMode.Month;
|
||||
}
|
||||
else if (e.Mode == CalendarYearViewMode.YearRange)
|
||||
{
|
||||
// Set CalendarYearView to Year mode
|
||||
for (var i = 0; i < 12; i++)
|
||||
{
|
||||
if (e.StartYear is null || e.EndYear is null) continue;
|
||||
var year = e.StartYear.Value - 1 + i;
|
||||
buttons[i].SetValues(CalendarYearViewMode.Year, ContextDate, year: year);
|
||||
}
|
||||
Mode = CalendarYearViewMode.Year;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,4 +111,11 @@ public class CalendarYearView: TemplatedControl
|
||||
}
|
||||
}
|
||||
|
||||
public void ShiftMode()
|
||||
{
|
||||
if (Mode == CalendarYearViewMode.Month)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)"/>
|
||||
<PackageReference Include="Irihi.Avalonia.Shared" Version="0.1.7" />
|
||||
<PackageReference Include="Irihi.Avalonia.Shared" Version="0.1.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user