feat: implement month syncing.

This commit is contained in:
rabbitism
2024-06-23 04:34:23 +08:00
parent c88258cdd5
commit d8119aaeaa
6 changed files with 90 additions and 30 deletions

View File

@@ -1,8 +1,9 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui" <ResourceDictionary
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="https://github.com/avaloniaui"
xmlns:u="https://irihi.tech/ursa"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
<!-- Add Resources Here --> xmlns:u="https://irihi.tech/ursa">
<ControlTheme TargetType="u:DatePicker" x:Key="{x:Type u:DatePicker}"> <!-- Add Resources Here -->
<ControlTheme x:Key="{x:Type u:DatePicker}" TargetType="u:DatePicker">
<Setter Property="Background" Value="{DynamicResource CalendarDatePickerBackground}" /> <Setter Property="Background" Value="{DynamicResource CalendarDatePickerBackground}" />
<Setter Property="Foreground" Value="{DynamicResource CalendarDatePickerForeground}" /> <Setter Property="Foreground" Value="{DynamicResource CalendarDatePickerForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource CalendarDatePickerBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource CalendarDatePickerBorderBrush}" />
@@ -25,7 +26,7 @@
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"/> CornerRadius="{TemplateBinding CornerRadius}" />
<Grid <Grid
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
@@ -40,20 +41,18 @@
Background="Transparent" Background="Transparent"
BorderBrush="Transparent" BorderBrush="Transparent"
BorderThickness="0" BorderThickness="0"
Watermark="{TemplateBinding Watermark}"
InnerLeftContent="{TemplateBinding InnerLeftContent}"
InnerRightContent="{TemplateBinding InnerRightContent}"
CornerRadius="{TemplateBinding CornerRadius}" CornerRadius="{TemplateBinding CornerRadius}"
Foreground="{TemplateBinding Foreground}" Foreground="{TemplateBinding Foreground}"
InnerLeftContent="{TemplateBinding InnerLeftContent}"
InnerRightContent="{TemplateBinding InnerRightContent}"
Theme="{DynamicResource LooklessTextBox}" Theme="{DynamicResource LooklessTextBox}"
> Watermark="{TemplateBinding Watermark}" />
</TextBox>
<Button <Button
Name="ClearButton" Name="ClearButton"
Grid.Column="1" Grid.Column="1"
Padding="0,0,8,0" Padding="0,0,8,0"
Content="{DynamicResource IconButtonClearData}"
Command="{Binding $parent[u:DatePicker].Clear}" Command="{Binding $parent[u:DatePicker].Clear}"
Content="{DynamicResource IconButtonClearData}"
Focusable="False" Focusable="False"
IsVisible="False" IsVisible="False"
Theme="{DynamicResource InnerIconButton}" /> Theme="{DynamicResource InnerIconButton}" />
@@ -67,25 +66,25 @@
<Popup <Popup
Name="PART_Popup" Name="PART_Popup"
Grid.Column="0" Grid.Column="0"
HorizontalOffset="-8" HorizontalOffset="-4"
IsLightDismissEnabled="True" IsLightDismissEnabled="True"
PlacementTarget="{TemplateBinding}"
Placement="BottomEdgeAlignedLeft"
IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropdownOpen, Mode=TwoWay}" IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropdownOpen, Mode=TwoWay}"
VerticalOffset="-4"> Placement="BottomEdgeAlignedLeft"
PlacementTarget="{TemplateBinding}">
<Border <Border
Margin="8" Margin="8"
Padding="8" Padding="8"
Background="{DynamicResource SemiColorBackground0}" Background="{DynamicResource ComboBoxPopupBackground}"
BoxShadow="{DynamicResource CalendarDatePickerPopupBoxShadows}" BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}"
BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}"
BorderThickness="{DynamicResource ComboBoxPopupBorderThickness}"
CornerRadius="{DynamicResource CalendarCornerRadius}"> CornerRadius="{DynamicResource CalendarCornerRadius}">
<u:CalendarView <u:CalendarView
Name="PART_Calendar" Name="PART_Calendar"
BorderThickness="0" BorderThickness="0"
CornerRadius="{Binding $parent[Border].CornerRadius}" CornerRadius="{Binding $parent[Border].CornerRadius}"
FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}" FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}"
IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}"> IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}" />
</u:CalendarView>
</Border> </Border>
</Popup> </Popup>
</Grid> </Grid>

View File

@@ -72,21 +72,21 @@
</Grid> </Grid>
<Popup <Popup
Name="{x:Static contracts:PartNames.PART_Popup}" Name="{x:Static contracts:PartNames.PART_Popup}"
HorizontalOffset="-4"
IsLightDismissEnabled="True" IsLightDismissEnabled="True"
IsOpen="{TemplateBinding IsDropdownOpen, IsOpen="{TemplateBinding IsDropdownOpen,
Mode=TwoWay}" Mode=TwoWay}"
Placement="BottomEdgeAlignedLeft" Placement="BottomEdgeAlignedLeft"
PlacementTarget="Background"> PlacementTarget="Background">
<Border <Border
Margin="0,4" Margin="8"
Padding="8" Padding="8"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="{DynamicResource ComboBoxPopupBackground}" Background="{DynamicResource ComboBoxPopupBackground}"
BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}" BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}"
BorderThickness="{DynamicResource ComboBoxPopupBorderThickness}" BorderThickness="{DynamicResource ComboBoxPopupBorderThickness}"
BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}" BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}"
ClipToBounds="True" CornerRadius="{DynamicResource CalendarCornerRadius}">
CornerRadius="6">
<DockPanel> <DockPanel>
<!-- <!--
<StackPanel DockPanel.Dock="Bottom" IsVisible="{TemplateBinding NeedConfirmation}"> <StackPanel DockPanel.Dock="Bottom" IsVisible="{TemplateBinding NeedConfirmation}">

View File

@@ -1,6 +1,6 @@
namespace Ursa.Controls; namespace Ursa.Controls;
public sealed class CalendarContext(int? year = null, int? month = null, int? startYear = null, int? endYear = null) public sealed class CalendarContext(int? year = null, int? month = null, int? startYear = null, int? endYear = null): IComparable<CalendarContext>
{ {
public int? Year { get; } = year; public int? Year { get; } = year;
public int? Month { get; } = month; public int? Month { get; } = month;
@@ -64,4 +64,13 @@ public sealed class CalendarContext(int? year = null, int? month = null, int? st
{ {
return new CalendarContext(Year - 1, Month, StartYear, EndYear); return new CalendarContext(Year - 1, Month, StartYear, EndYear);
} }
public int CompareTo(CalendarContext? other)
{
if (ReferenceEquals(this, other)) return 0;
if (ReferenceEquals(null, other)) return 1;
var yearComparison = Nullable.Compare(Year, other.Year);
if (yearComparison != 0) return yearComparison;
return Nullable.Compare(Month, other.Month);
}
} }

View File

@@ -81,7 +81,10 @@ public class CalendarView : TemplatedControl
private void OnContextDateChanged(AvaloniaPropertyChangedEventArgs<CalendarContext> args) private void OnContextDateChanged(AvaloniaPropertyChangedEventArgs<CalendarContext> args)
{ {
ContextDateChanged?.Invoke(this, args.NewValue.Value); if (!_dateContextSyncing)
{
ContextDateChanged?.Invoke(this, args.NewValue.Value);
}
//UpdateDayButtons(); //UpdateDayButtons();
//UpdateYearButtons(); //UpdateYearButtons();
} }
@@ -571,4 +574,19 @@ public class CalendarView : TemplatedControl
base.OnPointerExited(e); base.OnPointerExited(e);
DatePreviewed?.Invoke(this, new CalendarDayButtonEventArgs(null)); DatePreviewed?.Invoke(this, new CalendarDayButtonEventArgs(null));
} }
private bool _dateContextSyncing = false;
/// <summary>
/// Used for syncing the context date for DateRangePicker. mark a flag to avoid infinitely loop.
/// </summary>
/// <param name="context"></param>
internal void SyncContextDate(CalendarContext? context)
{
if (context is null) return;
_dateContextSyncing = true;
ContextDate = context;
_dateContextSyncing = false;
UpdateDayButtons();
UpdateYearButtons();
}
} }

View File

@@ -139,7 +139,7 @@ public class DatePicker: DatePickerBase, IClearControl
if (_calendar is not null) if (_calendar is not null)
{ {
var d = SelectedDate ?? DateTime.Today; var d = SelectedDate ?? DateTime.Today;
_calendar.ContextDate = new CalendarContext(date.Year, date.Month); _calendar.ContextDate = _calendar.ContextDate.With(year: date.Year, month: date.Month);
_calendar.UpdateDayButtons(); _calendar.UpdateDayButtons();
} }
_calendar?.MarkDates(startDate: date, endDate: date); _calendar?.MarkDates(startDate: date, endDate: date);
@@ -152,7 +152,7 @@ public class DatePicker: DatePickerBase, IClearControl
if (_calendar is not null) if (_calendar is not null)
{ {
var date = SelectedDate ?? DateTime.Today; var date = SelectedDate ?? DateTime.Today;
_calendar.ContextDate = new CalendarContext(date.Year, date.Month); _calendar.ContextDate = _calendar.ContextDate.With(year: date.Year, month: date.Month);
_calendar.UpdateDayButtons(); _calendar.UpdateDayButtons();
} }
SetCurrentValue(IsDropdownOpenProperty, true); SetCurrentValue(IsDropdownOpenProperty, true);

View File

@@ -32,6 +32,15 @@ public class DateRangePicker : DatePickerBase
AvaloniaProperty.Register<DateRangePicker, DateTime?>( AvaloniaProperty.Register<DateRangePicker, DateTime?>(
nameof(SelectedEndDate)); nameof(SelectedEndDate));
public static readonly StyledProperty<bool> EnableMonthSyncProperty = AvaloniaProperty.Register<DateRangePicker, bool>(
nameof(EnableMonthSync));
public bool EnableMonthSync
{
get => GetValue(EnableMonthSyncProperty);
set => SetValue(EnableMonthSyncProperty, value);
}
private Button? _button; private Button? _button;
private CalendarView? _endCalendar; private CalendarView? _endCalendar;
private TextBox? _endTextBox; private TextBox? _endTextBox;
@@ -103,11 +112,13 @@ public class DateRangePicker : DatePickerBase
{ {
_startCalendar.DateSelected -= OnDateSelected; _startCalendar.DateSelected -= OnDateSelected;
_startCalendar.DatePreviewed -= OnDatePreviewed; _startCalendar.DatePreviewed -= OnDatePreviewed;
_startCalendar.ContextDateChanged -= OnContextDateChanged;
} }
if (_endCalendar != null) if (_endCalendar != null)
{ {
_endCalendar.DateSelected -= OnDateSelected; _endCalendar.DateSelected -= OnDateSelected;
_endCalendar.DatePreviewed -= OnDatePreviewed; _endCalendar.DatePreviewed -= OnDatePreviewed;
_endCalendar.ContextDateChanged -= OnContextDateChanged;
} }
_button = e.NameScope.Find<Button>(PART_Button); _button = e.NameScope.Find<Button>(PART_Button);
_popup = e.NameScope.Find<Popup>(PART_Popup); _popup = e.NameScope.Find<Popup>(PART_Popup);
@@ -125,11 +136,33 @@ public class DateRangePicker : DatePickerBase
{ {
_startCalendar.DateSelected += OnDateSelected; _startCalendar.DateSelected += OnDateSelected;
_startCalendar.DatePreviewed += OnDatePreviewed; _startCalendar.DatePreviewed += OnDatePreviewed;
_startCalendar.ContextDateChanged += OnContextDateChanged;
} }
if (_endCalendar != null) if (_endCalendar != null)
{ {
_endCalendar.DateSelected += OnDateSelected; _endCalendar.DateSelected += OnDateSelected;
_endCalendar.DatePreviewed += OnDatePreviewed; _endCalendar.DatePreviewed += OnDatePreviewed;
_endCalendar.ContextDateChanged += OnContextDateChanged;
}
}
private void OnContextDateChanged(object sender, CalendarContext e)
{
if(sender == _startCalendar)
{
bool needsUpdate = EnableMonthSync || _startCalendar?.ContextDate.CompareTo(_endCalendar?.ContextDate) >= 0;
if (needsUpdate)
{
_endCalendar?.SyncContextDate(_startCalendar?.ContextDate.NextMonth());
}
}
else if(sender == _endCalendar)
{
bool needsUpdate = EnableMonthSync || _endCalendar?.ContextDate.CompareTo(_startCalendar?.ContextDate) <= 0;
if (needsUpdate)
{
_startCalendar?.SyncContextDate(_endCalendar?.ContextDate.PreviousMonth());
}
} }
} }
@@ -296,7 +329,8 @@ public class DateRangePicker : DatePickerBase
} }
if (_endCalendar is not null) if (_endCalendar is not null)
{ {
var date2 = SelectedEndDate ?? DateTime.Today; var date2 = SelectedEndDate ?? SelectedStartDate ?? DateTime.Today;
if (SelectedEndDate is null) date2 = date2.AddMonths(1);
_endCalendar.ContextDate = new CalendarContext(date2.Year, date2.Month); _endCalendar.ContextDate = new CalendarContext(date2.Year, date2.Month);
_endCalendar.UpdateDayButtons(); _endCalendar.UpdateDayButtons();
_endCalendar?.MarkDates(SelectedStartDate, SelectedEndDate, _previewStart, _previewEnd); _endCalendar?.MarkDates(SelectedStartDate, SelectedEndDate, _previewStart, _previewEnd);
@@ -311,7 +345,7 @@ public class DateRangePicker : DatePickerBase
{ {
var date = SelectedStartDate ?? DateTime.Today; var date = SelectedStartDate ?? DateTime.Today;
_startCalendar.ContextDate = new CalendarContext(date.Year, date.Month); _startCalendar.ContextDate = new CalendarContext(date.Year, date.Month);
// _startCalendar.UpdateDayButtons(); _startCalendar.UpdateDayButtons();
} }
if (_endCalendar is not null) if (_endCalendar is not null)
@@ -319,7 +353,7 @@ public class DateRangePicker : DatePickerBase
var date2 = SelectedStartDate ?? DateTime.Today; var date2 = SelectedStartDate ?? DateTime.Today;
date2 = date2.AddMonths(1); date2 = date2.AddMonths(1);
_endCalendar.ContextDate = new CalendarContext(date2.Year, date2.Month); _endCalendar.ContextDate = new CalendarContext(date2.Year, date2.Month);
// _endCalendar.UpdateDayButtons(); _endCalendar.UpdateDayButtons();
} }
SetCurrentValue(IsDropdownOpenProperty, true); SetCurrentValue(IsDropdownOpenProperty, true);
} }