Calendar improvements cycle 2

This commit is contained in:
Burak Kaan Köse
2026-03-25 15:49:14 +01:00
parent 8c492bb094
commit e3c3b341e5
16 changed files with 332 additions and 62 deletions
@@ -37,9 +37,6 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
{ {
#region Quick Event Creation #region Quick Event Creation
[ObservableProperty]
public partial bool IsQuickEventDialogOpen { get; set; }
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedFor(nameof(SelectedQuickEventAccountCalendarName))] [NotifyPropertyChangedFor(nameof(SelectedQuickEventAccountCalendarName))]
[NotifyCanExecuteChangedFor(nameof(SaveQuickEventCommand))] [NotifyCanExecuteChangedFor(nameof(SaveQuickEventCommand))]
@@ -252,10 +249,16 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
} }
private void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e) private void AccountCalendarStateCollectivelyChanged(object sender, GroupedAccountCalendarViewModel e)
=> _ = ReloadCurrentVisibleRangeAsync(); {
EnsureSelectedQuickEventAccountCalendar();
_ = ReloadCurrentVisibleRangeAsync();
}
private void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e) private void UpdateAccountCalendarRequested(object sender, AccountCalendarViewModel e)
=> _ = ReloadCurrentVisibleRangeAsync(); {
EnsureSelectedQuickEventAccountCalendar();
_ = ReloadCurrentVisibleRangeAsync();
}
[RelayCommand(CanExecute = nameof(CanJoinOnline))] [RelayCommand(CanExecute = nameof(CanJoinOnline))]
private async Task JoinOnlineAsync() private async Task JoinOnlineAsync()
@@ -273,9 +276,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
AttachSubscriptions(); AttachSubscriptions();
RefreshSettings(); RefreshSettings();
IsCalendarEnabled = true; IsCalendarEnabled = true;
EnsureSelectedQuickEventAccountCalendar();
SelectedQuickEventAccountCalendar = AccountCalendarStateService.ActiveCalendars.FirstOrDefault(a => a.IsPrimary)
?? AccountCalendarStateService.ActiveCalendars.FirstOrDefault();
} }
public override void OnNavigatedFrom(NavigationMode mode, object parameters) public override void OnNavigatedFrom(NavigationMode mode, object parameters)
@@ -318,7 +319,6 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
DisplayDetailsCalendarItemViewModel = null; DisplayDetailsCalendarItemViewModel = null;
SelectedQuickEventAccountCalendar = null; SelectedQuickEventAccountCalendar = null;
SelectedQuickEventDate = null; SelectedQuickEventDate = null;
IsQuickEventDialogOpen = false;
HourSelectionStrings = []; HourSelectionStrings = [];
CurrentVisibleRange = null; CurrentVisibleRange = null;
VisibleDateRangeText = string.Empty; VisibleDateRangeText = string.Empty;
@@ -469,8 +469,6 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
RecurrenceSummary = string.Empty RecurrenceSummary = string.Empty
}; };
IsQuickEventDialogOpen = false;
var preparationRequest = new CalendarOperationPreparationRequest( var preparationRequest = new CalendarOperationPreparationRequest(
CalendarSynchronizerOperation.CreateEvent, CalendarSynchronizerOperation.CreateEvent,
ComposeResult: composeResult); ComposeResult: composeResult);
@@ -507,8 +505,6 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
endDate = SelectedQuickEventDate.Value.Date.AddDays(1); endDate = SelectedQuickEventDate.Value.Date.AddDays(1);
} }
IsQuickEventDialogOpen = false;
_navigationService.Navigate(WinoPage.CalendarEventComposePage, new CalendarEventComposeNavigationArgs _navigationService.Navigate(WinoPage.CalendarEventComposePage, new CalendarEventComposeNavigationArgs
{ {
SelectedCalendarId = SelectedQuickEventAccountCalendar?.Id, SelectedCalendarId = SelectedQuickEventAccountCalendar?.Id,
@@ -602,6 +598,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
CalendarItems = loadedItems; CalendarItems = loadedItems;
} }
EnsureSelectedQuickEventAccountCalendar();
CurrentVisibleRange = visibleRange; CurrentVisibleRange = visibleRange;
LoadedDateWindow = loadedDateWindow; LoadedDateWindow = loadedDateWindow;
VisibleDateRangeText = _calendarRangeTextFormatter.Format(visibleRange, _dateContextProvider); VisibleDateRangeText = _calendarRangeTextFormatter.Format(visibleRange, _dateContextProvider);
@@ -660,7 +657,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
var loadedItems = new Dictionary<Guid, CalendarItemViewModel>(); var loadedItems = new Dictionary<Guid, CalendarItemViewModel>();
var loadPeriod = new TimeRange(loadedDateWindow.StartDate, loadedDateWindow.EndDate); var loadPeriod = new TimeRange(loadedDateWindow.StartDate, loadedDateWindow.EndDate);
foreach (var calendarViewModel in AccountCalendarStateService.AllCalendars) foreach (var calendarViewModel in AccountCalendarStateService.ActiveCalendars)
{ {
if (!IsPageActive(lifetimeVersion)) if (!IsPageActive(lifetimeVersion))
return []; return [];
@@ -711,6 +708,17 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
private bool IsCalendarActive(Guid? calendarId) private bool IsCalendarActive(Guid? calendarId)
=> calendarId.HasValue && AccountCalendarStateService.ActiveCalendars.Any(calendar => calendar.Id == calendarId.Value); => calendarId.HasValue && AccountCalendarStateService.ActiveCalendars.Any(calendar => calendar.Id == calendarId.Value);
private void EnsureSelectedQuickEventAccountCalendar()
{
if (SelectedQuickEventAccountCalendar != null && IsCalendarActive(SelectedQuickEventAccountCalendar.Id))
{
return;
}
SelectedQuickEventAccountCalendar = AccountCalendarStateService.ActiveCalendars.FirstOrDefault(a => a.IsPrimary)
?? AccountCalendarStateService.ActiveCalendars.FirstOrDefault();
}
public async void Receive(LoadCalendarMessage message) public async void Receive(LoadCalendarMessage message)
=> await ApplyDisplayRequestAsync(message.DisplayRequest, message.ForceReload); => await ApplyDisplayRequestAsync(message.DisplayRequest, message.ForceReload);
@@ -742,9 +750,7 @@ public partial class CalendarPageViewModel : CalendarBaseViewModel,
DisplayDetailsCalendarItemViewModel = null; DisplayDetailsCalendarItemViewModel = null;
} }
SelectedQuickEventAccountCalendar = AccountCalendarStateService.ActiveCalendars.FirstOrDefault(a => a.IsPrimary) EnsureSelectedQuickEventAccountCalendar();
?? AccountCalendarStateService.ActiveCalendars.FirstOrDefault();
await ReloadCurrentVisibleRangeAsync().ConfigureAwait(false); await ReloadCurrentVisibleRangeAsync().ConfigureAwait(false);
} }
+91 -17
View File
@@ -94,6 +94,67 @@ public class CalendarPageViewModelTests
calendarService.Verify(service => service.GetCalendarEventsAsync(It.IsAny<IAccountCalendar>(), It.IsAny<ITimePeriod>()), Times.Exactly(2)); calendarService.Verify(service => service.GetCalendarEventsAsync(It.IsAny<IAccountCalendar>(), It.IsAny<ITimePeriod>()), Times.Exactly(2));
} }
[Fact]
public async Task ApplyDisplayRequestAsync_LoadsOnlyActiveCalendars()
{
var settings = CreateSettings();
var preferencesService = CreatePreferencesService(settings);
var calendarService = new Mock<ICalendarService>();
var account = new MailAccount
{
Id = Guid.NewGuid(),
Name = "Primary",
SenderName = "Primary",
Address = "primary@example.com",
ProviderType = MailProviderType.Outlook
};
var visibleCalendar = CreateCalendar(account, "Visible calendar");
var hiddenCalendar = CreateCalendar(account, "Hidden calendar");
var visibleCalendarViewModel = new AccountCalendarViewModel(account, visibleCalendar);
var hiddenCalendarViewModel = new AccountCalendarViewModel(account, hiddenCalendar);
hiddenCalendarViewModel.IsChecked = false;
calendarService
.Setup(service => service.GetCalendarEventsAsync(It.Is<IAccountCalendar>(calendar => calendar.Id == visibleCalendar.Id), It.IsAny<ITimePeriod>()))
.ReturnsAsync([
new CalendarItem
{
Id = Guid.NewGuid(),
CalendarId = visibleCalendar.Id,
StartDate = new DateTime(2026, 3, 20, 9, 0, 0),
DurationInSeconds = TimeSpan.FromMinutes(30).TotalSeconds,
Title = "Visible event"
}
]);
calendarService
.Setup(service => service.GetCalendarEventsAsync(It.Is<IAccountCalendar>(calendar => calendar.Id == hiddenCalendar.Id), It.IsAny<ITimePeriod>()))
.ReturnsAsync([
new CalendarItem
{
Id = Guid.NewGuid(),
CalendarId = hiddenCalendar.Id,
StartDate = new DateTime(2026, 3, 20, 10, 0, 0),
DurationInSeconds = TimeSpan.FromMinutes(30).TotalSeconds,
Title = "Hidden event"
}
]);
var accountCalendarStateService = new FakeAccountCalendarStateService(
[visibleCalendarViewModel, hiddenCalendarViewModel],
[visibleCalendarViewModel]);
var viewModel = CreateViewModel(calendarService.Object, preferencesService.Object, new DateOnly(2026, 3, 20), accountCalendarStateService);
await viewModel.ApplyDisplayRequestAsync(new CalendarDisplayRequest(CalendarDisplayType.Day, new DateOnly(2026, 3, 20)));
viewModel.CalendarItems.Should().ContainSingle(item => item.CalendarItem.CalendarId == visibleCalendar.Id);
calendarService.Verify(service => service.GetCalendarEventsAsync(It.Is<IAccountCalendar>(calendar => calendar.Id == visibleCalendar.Id), It.IsAny<ITimePeriod>()), Times.Once);
calendarService.Verify(service => service.GetCalendarEventsAsync(It.Is<IAccountCalendar>(calendar => calendar.Id == hiddenCalendar.Id), It.IsAny<ITimePeriod>()), Times.Never);
}
private static CalendarPageViewModel CreateViewModel( private static CalendarPageViewModel CreateViewModel(
ICalendarService calendarService, ICalendarService calendarService,
IPreferencesService preferencesService, IPreferencesService preferencesService,
@@ -108,24 +169,19 @@ public class CalendarPageViewModelTests
ProviderType = MailProviderType.Outlook ProviderType = MailProviderType.Outlook
}; };
var calendar = new AccountCalendar var calendar = CreateCalendar(account, "Calendar");
{
Id = Guid.NewGuid(),
AccountId = account.Id,
Name = "Calendar",
RemoteCalendarId = "calendar",
SynchronizationDeltaToken = string.Empty,
TextColorHex = "#000000",
BackgroundColorHex = "#ffffff",
TimeZone = TimeZoneInfo.Utc.Id,
IsExtended = true,
IsPrimary = true,
IsSynchronizationEnabled = true
};
var accountCalendarViewModel = new AccountCalendarViewModel(account, calendar); var accountCalendarViewModel = new AccountCalendarViewModel(account, calendar);
var accountCalendarStateService = new FakeAccountCalendarStateService([accountCalendarViewModel]); var accountCalendarStateService = new FakeAccountCalendarStateService([accountCalendarViewModel]);
return CreateViewModel(calendarService, preferencesService, today, accountCalendarStateService);
}
private static CalendarPageViewModel CreateViewModel(
ICalendarService calendarService,
IPreferencesService preferencesService,
DateOnly today,
IAccountCalendarStateService accountCalendarStateService)
{
var statePersistenceService = new Mock<IStatePersistanceService>(); var statePersistenceService = new Mock<IStatePersistanceService>();
statePersistenceService.SetupAllProperties(); statePersistenceService.SetupAllProperties();
statePersistenceService.Object.ApplicationMode = WinoApplicationMode.Calendar; statePersistenceService.Object.ApplicationMode = WinoApplicationMode.Calendar;
@@ -145,6 +201,22 @@ public class CalendarPageViewModelTests
new CalendarRangeTextFormatter()); new CalendarRangeTextFormatter());
} }
private static AccountCalendar CreateCalendar(MailAccount account, string name)
=> new()
{
Id = Guid.NewGuid(),
AccountId = account.Id,
Name = name,
RemoteCalendarId = "calendar",
SynchronizationDeltaToken = string.Empty,
TextColorHex = "#000000",
BackgroundColorHex = "#ffffff",
TimeZone = TimeZoneInfo.Utc.Id,
IsExtended = true,
IsPrimary = true,
IsSynchronizationEnabled = true
};
private static Mock<IPreferencesService> CreatePreferencesService(CalendarSettings settings) private static Mock<IPreferencesService> CreatePreferencesService(CalendarSettings settings)
=> CreatePreferencesService(() => settings); => CreatePreferencesService(() => settings);
@@ -177,11 +249,13 @@ public class CalendarPageViewModelTests
private sealed class FakeAccountCalendarStateService : IAccountCalendarStateService private sealed class FakeAccountCalendarStateService : IAccountCalendarStateService
{ {
private readonly List<AccountCalendarViewModel> _calendars; private readonly List<AccountCalendarViewModel> _calendars;
private readonly List<AccountCalendarViewModel> _activeCalendars;
private readonly ObservableCollection<GroupedAccountCalendarViewModel> _groupedCalendars = []; private readonly ObservableCollection<GroupedAccountCalendarViewModel> _groupedCalendars = [];
public FakeAccountCalendarStateService(IEnumerable<AccountCalendarViewModel> calendars) public FakeAccountCalendarStateService(IEnumerable<AccountCalendarViewModel> calendars, IEnumerable<AccountCalendarViewModel>? activeCalendars = null)
{ {
_calendars = calendars.ToList(); _calendars = calendars.ToList();
_activeCalendars = (activeCalendars ?? _calendars.Where(calendar => calendar.IsChecked)).ToList();
GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_groupedCalendars); GroupedAccountCalendars = new ReadOnlyObservableCollection<GroupedAccountCalendarViewModel>(_groupedCalendars);
} }
@@ -206,7 +280,7 @@ public class CalendarPageViewModelTests
remove { } remove { }
} }
public IEnumerable<AccountCalendarViewModel> ActiveCalendars => _calendars; public IEnumerable<AccountCalendarViewModel> ActiveCalendars => _activeCalendars;
public IEnumerable<AccountCalendarViewModel> AllCalendars => _calendars; public IEnumerable<AccountCalendarViewModel> AllCalendars => _calendars;
public ReadOnlyObservableGroupedCollection<MailAccount, AccountCalendarViewModel> GroupedCalendars { get; set; } = null!; public ReadOnlyObservableGroupedCollection<MailAccount, AccountCalendarViewModel> GroupedCalendars { get; set; } = null!;
+2
View File
@@ -15,6 +15,7 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#B2FCFCFC</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#B2FCFCFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#D9ECEFF1</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#D9ECEFF1</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
<local:AcrylicBrush <local:AcrylicBrush
x:Key="WinoApplicationBackgroundColor" x:Key="WinoApplicationBackgroundColor"
@@ -27,6 +28,7 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#2C2C2C</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#2C2C2C</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#662C2C2C</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#662C2C2C</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#992C2C2C</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#992C2C2C</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
<local:AcrylicBrush <local:AcrylicBrush
x:Key="WinoApplicationBackgroundColor" x:Key="WinoApplicationBackgroundColor"
+2
View File
@@ -14,6 +14,7 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSeperatorBrush">#222f3e</SolidColorBrush> <SolidColorBrush x:Key="CalendarSeperatorBrush">#222f3e</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
@@ -21,6 +22,7 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b2dffc</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B2DFFC</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSeperatorBrush">#222f3e</SolidColorBrush> <SolidColorBrush x:Key="CalendarSeperatorBrush">#222f3e</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
+2
View File
@@ -24,6 +24,7 @@
<Color x:Key="MainCustomThemeColor">#D9FFFFFF</Color> <Color x:Key="MainCustomThemeColor">#D9FFFFFF</Color>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" /> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" />
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" /> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" />
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
<SolidColorBrush x:Key="AppBarBackgroundColor" Color="{StaticResource MainCustomThemeColor}" /> <SolidColorBrush x:Key="AppBarBackgroundColor" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" /> <SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
@@ -37,6 +38,7 @@
<Color x:Key="MainCustomThemeColor">#E61F1F1F</Color> <Color x:Key="MainCustomThemeColor">#E61F1F1F</Color>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" /> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.55" />
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" /> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush" Color="{StaticResource MainCustomThemeColor}" Opacity="0.85" />
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
<!-- Reading Pane Background --> <!-- Reading Pane Background -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush" Color="{StaticResource MainCustomThemeColor}" /> <SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush" Color="{StaticResource MainCustomThemeColor}" />
+2
View File
@@ -15,11 +15,13 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#F7F9FA</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#F7F9FA</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#DFE4EA</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#DFE4EA</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Name="Dark"> <ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#1F1F1F</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#1F1F1F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#262626</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#262626</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary> </ResourceDictionary>
+2
View File
@@ -14,11 +14,13 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#A800D608</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#A800D608</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2200D608</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2200D608</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#4D00D608</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#4D00D608</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Name="Dark"> <ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#59001C01</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#59001C01</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#22001C01</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#22001C01</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59001C01</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59001C01</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary> </ResourceDictionary>
+2
View File
@@ -14,6 +14,7 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26DCFAD8</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26DCFAD8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59DCFAD8</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59DCFAD8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSeperatorBrush">#576574</SolidColorBrush> <SolidColorBrush x:Key="CalendarSeperatorBrush">#576574</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
@@ -22,6 +23,7 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#dcfad8</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26576574</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#26576574</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59576574</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#59576574</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary> </ResourceDictionary>
+2
View File
@@ -15,12 +15,14 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#fdcb6e</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#fdcb6e</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33FDCB6E</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33FDCB6E</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66FDCB6E</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66FDCB6E</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Name="Dark"> <ResourceDictionary x:Name="Dark">
<!-- Brushes --> <!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2213191F</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#2213191F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#5413191F</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#5413191F</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary> </ResourceDictionary>
+2
View File
@@ -15,12 +15,14 @@
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#4D0078D4</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Name="Dark"> <ResourceDictionary x:Name="Dark">
<!-- Brushes --> <!-- Brushes -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush> <SolidColorBrush x:Key="MailListHeaderBackgroundColor">#b0c6dd</SolidColorBrush>
<SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush> <SolidColorBrush x:Key="CalendarDefaultHourBackgroundBrush">#33B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush> <SolidColorBrush x:Key="CalendarWorkHourBackgroundBrush">#66B0C6DD</SolidColorBrush>
<SolidColorBrush x:Key="CalendarSelectedHourBackgroundBrush">#66399BFF</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary> </ResourceDictionary>
@@ -6,14 +6,14 @@ namespace Wino.Calendar.Controls;
public sealed class CalendarEmptySlotTappedEventArgs : EventArgs public sealed class CalendarEmptySlotTappedEventArgs : EventArgs
{ {
public CalendarEmptySlotTappedEventArgs(DateTime clickedDate, Point positionerPoint, Size cellSize) public CalendarEmptySlotTappedEventArgs(DateTime clickedDate, Point anchorPoint, Size cellSize)
{ {
ClickedDate = clickedDate; ClickedDate = clickedDate;
PositionerPoint = positionerPoint; AnchorPoint = anchorPoint;
CellSize = cellSize; CellSize = cellSize;
} }
public DateTime ClickedDate { get; } public DateTime ClickedDate { get; }
public Point PositionerPoint { get; } public Point AnchorPoint { get; }
public Size CellSize { get; } public Size CellSize { get; }
} }
@@ -9,7 +9,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:skia="using:SkiaSharp.Views.Windows" xmlns:skia="using:SkiaSharp.Views.Windows"
xmlns:viewModels="using:Wino.Calendar.ViewModels.Data" xmlns:viewModels="using:Wino.Calendar.ViewModels.Data"
x:Name="Root" x:Name="Root"
SizeChanged="ControlSizeChanged" SizeChanged="ControlSizeChanged"
mc:Ignorable="d"> mc:Ignorable="d">
@@ -22,10 +21,7 @@
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="MonthEventTemplate" x:DataType="viewModels:CalendarItemViewModel"> <DataTemplate x:Key="MonthEventTemplate" x:DataType="viewModels:CalendarItemViewModel">
<local:CalendarItemControl <local:CalendarItemControl CalendarItem="{x:Bind}" IsCustomEventArea="True" />
Margin="0,2,0,0"
CalendarItem="{x:Bind}"
IsCustomEventArea="True" />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="TimedHeaderTemplate" x:DataType="local:HeaderTextLayout"> <DataTemplate x:Key="TimedHeaderTemplate" x:DataType="local:HeaderTextLayout">
@@ -77,8 +73,8 @@
x:Name="TimedHeaderHost" x:Name="TimedHeaderHost"
Grid.Row="0" Grid.Row="0"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
Margin="64,0,0,0"
Height="44" Height="44"
Margin="64,0,0,0"
Background="{ThemeResource LayerFillColorDefaultBrush}"> Background="{ThemeResource LayerFillColorDefaultBrush}">
<skia:SKXamlCanvas x:Name="TimedHeaderCanvas" PaintSurface="TimedHeaderCanvasPaintSurface" /> <skia:SKXamlCanvas x:Name="TimedHeaderCanvas" PaintSurface="TimedHeaderCanvasPaintSurface" />
<ItemsControl <ItemsControl
@@ -159,7 +155,13 @@
Background="Transparent" Background="Transparent"
Tapped="MonthInteractionLayerTapped" /> Tapped="MonthInteractionLayerTapped" />
<Canvas x:Name="MonthCellLabelsCanvas" IsHitTestVisible="False" /> <Canvas x:Name="MonthCellLabelsCanvas" IsHitTestVisible="False" />
<Canvas x:Name="MonthItemsCanvas" /> <Canvas x:Name="MonthItemsCanvas">
<Canvas.Transitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</Canvas.Transitions>
</Canvas>
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
@@ -35,6 +35,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
private const double TimedHourColumnWidth = 64d; private const double TimedHourColumnWidth = 64d;
private const double TimedGridIntervalMinutes = 30d; private const double TimedGridIntervalMinutes = 30d;
private const double TimedSelectionIntervalMinutes = 30d; private const double TimedSelectionIntervalMinutes = 30d;
private const double TimedItemRightSpacing = 10d;
private VisibleDateRange _currentRange = new( private VisibleDateRange _currentRange = new(
CalendarDisplayType.Month, CalendarDisplayType.Month,
DateOnly.FromDateTime(DateTime.Today), DateOnly.FromDateTime(DateTime.Today),
@@ -78,6 +79,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
[GeneratedDependencyProperty] [GeneratedDependencyProperty]
public partial Brush? WorkHourBackground { get; set; } public partial Brush? WorkHourBackground { get; set; }
[GeneratedDependencyProperty]
public partial Brush? SelectedSlotBackground { get; set; }
[GeneratedDependencyProperty]
public partial DateTime? SelectedDateTime { get; set; }
public CalendarPeriodControl() public CalendarPeriodControl()
{ {
InitializeComponent(); InitializeComponent();
@@ -153,6 +160,8 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
partial void OnVisibleRangeChanged(VisibleDateRange? newValue) => RequestRefresh(); partial void OnVisibleRangeChanged(VisibleDateRange? newValue) => RequestRefresh();
partial void OnCalendarSettingsChanged(CalendarSettings? newValue) => RequestRefresh(); partial void OnCalendarSettingsChanged(CalendarSettings? newValue) => RequestRefresh();
partial void OnTimedHeaderDateFormatChanged(string? newValue) => RequestRefresh(); partial void OnTimedHeaderDateFormatChanged(string? newValue) => RequestRefresh();
partial void OnSelectedSlotBackgroundChanged(Brush? newValue) => InvalidateStructureCanvases();
partial void OnSelectedDateTimeChanged(DateTime? newValue) => InvalidateStructureCanvases();
partial void OnCalendarItemsChanged(IReadOnlyList<CalendarItemViewModel>? newValue) partial void OnCalendarItemsChanged(IReadOnlyList<CalendarItemViewModel>? newValue)
{ {
@@ -231,6 +240,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
QueueRefresh(); QueueRefresh();
} }
private void InvalidateStructureCanvases()
{
TimedStructureCanvas.Invalidate();
MonthStructureCanvas.Invalidate();
}
private void Refresh() private void Refresh()
{ {
if (!_refreshPending || !IsLoaded || ActualWidth <= 0 || VisibleRange is null || CalendarSettings is null) if (!_refreshPending || !IsLoaded || ActualWidth <= 0 || VisibleRange is null || CalendarSettings is null)
@@ -461,6 +476,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
using var minorLinePaint = CreateMinorLinePaint(); using var minorLinePaint = CreateMinorLinePaint();
using var defaultFillPaint = CreateFillPaint(DefaultHourBackground ?? new SolidColorBrush(Colors.Transparent)); using var defaultFillPaint = CreateFillPaint(DefaultHourBackground ?? new SolidColorBrush(Colors.Transparent));
using var workFillPaint = CreateFillPaint(WorkHourBackground ?? new SolidColorBrush(Colors.Transparent)); using var workFillPaint = CreateFillPaint(WorkHourBackground ?? new SolidColorBrush(Colors.Transparent));
using var selectedFillPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
var canvas = e.Surface.Canvas; var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.Transparent); canvas.Clear(SKColors.Transparent);
@@ -499,6 +515,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
} }
} }
var selectedTimedSlotRect = GetSelectedTimedSlotRect(dayWidth, intervalHeight, intervalCount);
if (selectedTimedSlotRect.HasValue && selectedFillPaint.Color.Alpha > 0)
{
canvas.DrawRect(selectedTimedSlotRect.Value, selectedFillPaint);
}
for (var intervalIndex = 0; intervalIndex <= intervalCount; intervalIndex++) for (var intervalIndex = 0; intervalIndex <= intervalCount; intervalIndex++)
{ {
var y = intervalIndex * intervalHeight; var y = intervalIndex * intervalHeight;
@@ -522,6 +544,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
Color = new SKColor(0, 120, 215, 26), Color = new SKColor(0, 120, 215, 26),
IsAntialias = true IsAntialias = true
}; };
using var selectedPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
var canvas = e.Surface.Canvas; var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.Transparent); canvas.Clear(SKColors.Transparent);
@@ -545,6 +568,12 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
canvas.DrawRect((float)cell.Bounds.X, (float)cell.Bounds.Y, (float)cell.Bounds.Width, (float)cell.Bounds.Height, todayPaint); canvas.DrawRect((float)cell.Bounds.X, (float)cell.Bounds.Y, (float)cell.Bounds.Width, (float)cell.Bounds.Height, todayPaint);
} }
var selectedMonthCellRect = GetSelectedMonthCellRect();
if (selectedMonthCellRect.HasValue && selectedPaint.Color.Alpha > 0)
{
canvas.DrawRect(selectedMonthCellRect.Value, selectedPaint);
}
for (var row = 0; row <= MonthCalendarLayoutCalculator.RowCount; row++) for (var row = 0; row <= MonthCalendarLayoutCalculator.RowCount; row++)
{ {
var y = row * cellHeight; var y = row * cellHeight;
@@ -604,7 +633,7 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
{ {
var presenter = new ContentPresenter var presenter = new ContentPresenter
{ {
Width = item.Bounds.Width, Width = Math.Max(0d, item.Bounds.Width - TimedItemRightSpacing),
Height = item.Bounds.Height, Height = item.Bounds.Height,
Content = item.Item, Content = item.Item,
ContentTemplate = item.Template ContentTemplate = item.Template
@@ -669,12 +698,13 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
var slotIndex = Math.Clamp((int)(position.Y / intervalHeight), 0, (int)((24d * 60d / TimedSelectionIntervalMinutes) - 1)); var slotIndex = Math.Clamp((int)(position.Y / intervalHeight), 0, (int)((24d * 60d / TimedSelectionIntervalMinutes) - 1));
var slotStart = TimeSpan.FromMinutes(slotIndex * TimedSelectionIntervalMinutes); var slotStart = TimeSpan.FromMinutes(slotIndex * TimedSelectionIntervalMinutes);
var clickedDate = _timedLayout.VisibleDates[dayIndex].ToDateTime(TimeOnly.MinValue).Add(slotStart); var clickedDate = _timedLayout.VisibleDates[dayIndex].ToDateTime(TimeOnly.MinValue).Add(slotStart);
var anchorPoint = TimedViewport.TransformToVisual(Root).TransformPoint(position);
EmptySlotTapped?.Invoke( EmptySlotTapped?.Invoke(
this, this,
new CalendarEmptySlotTappedEventArgs( new CalendarEmptySlotTappedEventArgs(
clickedDate, clickedDate,
new Point(dayIndex * _timedLayout.DayWidth, slotIndex * intervalHeight), anchorPoint,
new Size(_timedLayout.DayWidth, intervalHeight))); new Size(_timedLayout.DayWidth, intervalHeight)));
} }
@@ -690,15 +720,75 @@ public sealed partial class CalendarPeriodControl : UserControl, INotifyProperty
var row = Math.Clamp((int)(position.Y / _monthLayout.CellHeight), 0, MonthCalendarLayoutCalculator.RowCount - 1); var row = Math.Clamp((int)(position.Y / _monthLayout.CellHeight), 0, MonthCalendarLayoutCalculator.RowCount - 1);
var cellIndex = Math.Clamp((row * MonthCalendarLayoutCalculator.ColumnCount) + column, 0, _monthLayout.Cells.Count - 1); var cellIndex = Math.Clamp((row * MonthCalendarLayoutCalculator.ColumnCount) + column, 0, _monthLayout.Cells.Count - 1);
var cell = _monthLayout.Cells[cellIndex]; var cell = _monthLayout.Cells[cellIndex];
var anchorPoint = MonthViewport.TransformToVisual(Root).TransformPoint(position);
EmptySlotTapped?.Invoke( EmptySlotTapped?.Invoke(
this, this,
new CalendarEmptySlotTappedEventArgs( new CalendarEmptySlotTappedEventArgs(
cell.Date.ToDateTime(TimeOnly.MinValue), cell.Date.ToDateTime(TimeOnly.MinValue),
new Point(cell.Bounds.X, cell.Bounds.Y), anchorPoint,
new Size(cell.Bounds.Width, cell.Bounds.Height))); new Size(cell.Bounds.Width, cell.Bounds.Height)));
} }
private SKRect? GetSelectedTimedSlotRect(float dayWidth, float intervalHeight, int intervalCount)
{
if (SelectedDateTime is not DateTime selectedDateTime || _timedLayout.VisibleDates.Count == 0)
{
return null;
}
var dayIndex = FindVisibleDateIndex(DateOnly.FromDateTime(selectedDateTime));
if (dayIndex < 0)
{
return null;
}
var slotIndex = (int)Math.Floor(selectedDateTime.TimeOfDay.TotalMinutes / TimedSelectionIntervalMinutes);
slotIndex = Math.Clamp(slotIndex, 0, intervalCount - 1);
var x = dayIndex * dayWidth;
var y = slotIndex * intervalHeight;
return new SKRect(x, y, x + dayWidth, y + intervalHeight);
}
private SKRect? GetSelectedMonthCellRect()
{
if (SelectedDateTime is not DateTime selectedDateTime)
{
return null;
}
var selectedDate = DateOnly.FromDateTime(selectedDateTime);
foreach (var cell in _monthLayout.Cells)
{
if (cell.Date != selectedDate)
{
continue;
}
return new SKRect(
(float)cell.Bounds.X,
(float)cell.Bounds.Y,
(float)(cell.Bounds.X + cell.Bounds.Width),
(float)(cell.Bounds.Y + cell.Bounds.Height));
}
return null;
}
private int FindVisibleDateIndex(DateOnly date)
{
for (var index = 0; index < _timedLayout.VisibleDates.Count; index++)
{
if (_timedLayout.VisibleDates[index] == date)
{
return index;
}
}
return -1;
}
private double GetTimedSurfaceWidth() => Math.Max(0d, ActualWidth - TimedHourColumnWidth); private double GetTimedSurfaceWidth() => Math.Max(0d, ActualWidth - TimedHourColumnWidth);
private string GetTimedHeaderText(DateOnly date) private string GetTimedHeaderText(DateOnly date)
@@ -66,7 +66,8 @@ internal static class MonthCalendarLayoutCalculator
private const double CellPadding = 4d; private const double CellPadding = 4d;
private const double DayLabelHeight = 20d; private const double DayLabelHeight = 20d;
private const double ItemHeight = 18d; private const double RegularItemHeight = 18d;
private const double ExpandedItemHeight = 30d;
private const double ItemGap = 2d; private const double ItemGap = 2d;
public static MonthCalendarLayoutResult Calculate(VisibleDateRange range, IEnumerable<CalendarItemViewModel> items, double availableWidth, double availableHeight) public static MonthCalendarLayoutResult Calculate(VisibleDateRange range, IEnumerable<CalendarItemViewModel> items, double availableWidth, double availableHeight)
@@ -92,12 +93,13 @@ internal static class MonthCalendarLayoutCalculator
foreach (var cell in cells) foreach (var cell in cells)
{ {
var cellItems = GetCellItems(items, cell.Date).ToList(); var cellItems = GetCellItems(items, cell.Date).ToList();
var nextItemY = cell.Bounds.Y + DayLabelHeight + CellPadding;
for (var index = 0; index < cellItems.Count; index++) for (var index = 0; index < cellItems.Count; index++)
{ {
var y = cell.Bounds.Y + DayLabelHeight + CellPadding + (index * (ItemHeight + ItemGap)); var itemHeight = GetItemHeight(cellItems[index]);
if (y + ItemHeight > cell.Bounds.Y + cell.Bounds.Height - CellPadding) if (nextItemY + itemHeight > cell.Bounds.Y + cell.Bounds.Height - CellPadding)
{ {
break; break;
} }
@@ -108,9 +110,11 @@ internal static class MonthCalendarLayoutCalculator
cell.Date, cell.Date,
new LayoutRect( new LayoutRect(
cell.Bounds.X + CellPadding, cell.Bounds.X + CellPadding,
y, nextItemY,
Math.Max(0, cell.Bounds.Width - (CellPadding * 2)), Math.Max(0, cell.Bounds.Width - (CellPadding * 2)),
ItemHeight))); itemHeight)));
nextItemY += itemHeight + ItemGap;
} }
} }
@@ -143,4 +147,9 @@ internal static class MonthCalendarLayoutCalculator
} }
} }
} }
private static double GetItemHeight(CalendarItemViewModel item)
=> item.IsAllDayEvent || item.IsMultiDayEvent
? ExpandedItemHeight
: RegularItemHeight;
} }
@@ -44,6 +44,8 @@
DefaultHourBackground="{ThemeResource CalendarDefaultHourBackgroundBrush}" DefaultHourBackground="{ThemeResource CalendarDefaultHourBackgroundBrush}"
EmptySlotTapped="CalendarSurfaceEmptySlotTapped" EmptySlotTapped="CalendarSurfaceEmptySlotTapped"
IsEnabled="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}" IsEnabled="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}"
SelectedDateTime="{x:Bind ViewModel.SelectedQuickEventDate, Mode=OneWay}"
SelectedSlotBackground="{ThemeResource CalendarSelectedHourBackgroundBrush}"
VisibleRange="{x:Bind ViewModel.CurrentVisibleRange, Mode=OneWay}" VisibleRange="{x:Bind ViewModel.CurrentVisibleRange, Mode=OneWay}"
WorkHourBackground="{ThemeResource CalendarWorkHourBackgroundBrush}" /> WorkHourBackground="{ThemeResource CalendarWorkHourBackgroundBrush}" />
</Border> </Border>
@@ -64,7 +66,6 @@
Closed="QuickEventPopupClosed" Closed="QuickEventPopupClosed"
DesiredPlacement="{x:Bind helpers:XamlHelpers.GetPlaccementModeForCalendarType(ViewModel.StatePersistanceService.CalendarDisplayType), Mode=OneWay}" DesiredPlacement="{x:Bind helpers:XamlHelpers.GetPlaccementModeForCalendarType(ViewModel.StatePersistanceService.CalendarDisplayType), Mode=OneWay}"
IsLightDismissEnabled="True" IsLightDismissEnabled="True"
IsOpen="{x:Bind ViewModel.IsQuickEventDialogOpen, Mode=TwoWay}"
PlacementTarget="{x:Bind TeachingTipPositionerGrid}"> PlacementTarget="{x:Bind TeachingTipPositionerGrid}">
<Popup.ChildTransitions> <Popup.ChildTransitions>
<TransitionCollection> <TransitionCollection>
@@ -226,14 +227,17 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Command="{x:Bind ViewModel.MoreDetailsCommand}" --> <Button
<Button HorizontalAlignment="Stretch" Content="{x:Bind domain:Translator.QuickEventDialogMoreDetailsButtonText}" /> HorizontalAlignment="Stretch"
Click="MoreDetailsClicked"
Content="{x:Bind domain:Translator.QuickEventDialogMoreDetailsButtonText}" />
<Button <Button
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Command="{x:Bind ViewModel.SaveQuickEventCommand}" Click="SaveQuickEventClicked"
Content="{x:Bind domain:Translator.Buttons_Save}" Content="{x:Bind domain:Translator.Buttons_Save}"
IsEnabled="{x:Bind ViewModel.CanSaveQuickEvent, Mode=OneWay}"
Style="{ThemeResource AccentButtonStyle}" /> Style="{ThemeResource AccentButtonStyle}" />
</Grid> </Grid>
</Grid> </Grid>
@@ -12,6 +12,7 @@ using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Navigation; using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation; using Windows.Foundation;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Calendar.Controls; using Wino.Calendar.Controls;
using Wino.Calendar.Views.Abstract; using Wino.Calendar.Views.Abstract;
using Wino.Core.Domain.Entities.Calendar; using Wino.Core.Domain.Entities.Calendar;
@@ -30,6 +31,7 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
private ICalendarShellClient CalendarShellClient { get; } = WinoApplication.Current.Services.GetRequiredService<ICalendarShellClient>(); private ICalendarShellClient CalendarShellClient { get; } = WinoApplication.Current.Services.GetRequiredService<ICalendarShellClient>();
private CancellationTokenSource? _searchCancellationTokenSource; private CancellationTokenSource? _searchCancellationTokenSource;
private long _calendarTypeSelectorChangedToken; private long _calendarTypeSelectorChangedToken;
private bool _suppressSelectionResetOnPopupClose;
public ObservableCollection<TitleBarSearchSuggestion> SearchSuggestions { get; } = []; public ObservableCollection<TitleBarSearchSuggestion> SearchSuggestions { get; } = [];
@@ -52,6 +54,7 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{ {
CloseQuickEventPopup(clearSelection: true);
base.OnNavigatingFrom(e); base.OnNavigatingFrom(e);
} }
@@ -146,21 +149,28 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
} }
ViewModel.SelectedQuickEventDate = e.ClickedDate; ViewModel.SelectedQuickEventDate = e.ClickedDate;
ViewModel.IsAllDay = ViewModel.CurrentVisibleRange?.DisplayType == CalendarDisplayType.Month;
var transform = CalendarSurface.TransformToVisual(CalendarOverlayCanvas); var transform = CalendarSurface.TransformToVisual(CalendarOverlayCanvas);
var canvasPoint = transform.TransformPoint(e.PositionerPoint); var canvasPoint = transform.TransformPoint(e.AnchorPoint);
TeachingTipPositionerGrid.Width = e.CellSize.Width; TeachingTipPositionerGrid.Width = 1;
TeachingTipPositionerGrid.Height = e.CellSize.Height; TeachingTipPositionerGrid.Height = 1;
Canvas.SetLeft(TeachingTipPositionerGrid, canvasPoint.X); Canvas.SetLeft(TeachingTipPositionerGrid, canvasPoint.X);
Canvas.SetTop(TeachingTipPositionerGrid, canvasPoint.Y); Canvas.SetTop(TeachingTipPositionerGrid, canvasPoint.Y);
var startTime = e.ClickedDate.TimeOfDay; if (!ViewModel.IsAllDay)
var endTime = startTime.Add(TimeSpan.FromMinutes(30)); {
ViewModel.SelectQuickEventTimeRange(startTime, endTime); var startTime = e.ClickedDate.TimeOfDay;
var endTime = startTime.Add(TimeSpan.FromMinutes(30));
ViewModel.SelectQuickEventTimeRange(startTime, endTime);
}
_suppressSelectionResetOnPopupClose = true;
QuickEventPopupDialog.IsOpen = false;
QuickEventPopupDialog.IsOpen = true; QuickEventPopupDialog.IsOpen = true;
_suppressSelectionResetOnPopupClose = false;
} }
private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e) private void QuickEventAccountSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -168,6 +178,10 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
private void QuickEventPopupClosed(object sender, object e) private void QuickEventPopupClosed(object sender, object e)
{ {
if (!_suppressSelectionResetOnPopupClose)
{
ViewModel.SelectedQuickEventDate = null;
}
} }
private void PopupPlacementChanged(object sender, object e) private void PopupPlacementChanged(object sender, object e)
@@ -257,6 +271,7 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
private void CalendarPageUnloaded(object sender, RoutedEventArgs e) private void CalendarPageUnloaded(object sender, RoutedEventArgs e)
{ {
CloseQuickEventPopup(clearSelection: true);
CalendarToolbar.UnregisterSelectedTypeChanged(_calendarTypeSelectorChangedToken); CalendarToolbar.UnregisterSelectedTypeChanged(_calendarTypeSelectorChangedToken);
CalendarToolbar.PreviousDateRequested -= CalendarToolbarPreviousDateRequested; CalendarToolbar.PreviousDateRequested -= CalendarToolbarPreviousDateRequested;
CalendarToolbar.NextDateRequested -= CalendarToolbarNextDateRequested; CalendarToolbar.NextDateRequested -= CalendarToolbarNextDateRequested;
@@ -267,4 +282,58 @@ public sealed partial class CalendarPage : CalendarPageAbstract, ITitleBarSearch
_searchCancellationTokenSource?.Dispose(); _searchCancellationTokenSource?.Dispose();
Unloaded -= CalendarPageUnloaded; Unloaded -= CalendarPageUnloaded;
} }
private async void SaveQuickEventClicked(object sender, RoutedEventArgs e)
{
if (!ViewModel.SaveQuickEventCommand.CanExecute(null))
{
return;
}
_suppressSelectionResetOnPopupClose = true;
try
{
QuickEventPopupDialog.IsOpen = false;
await ViewModel.SaveQuickEventCommand.ExecuteAsync(null);
}
finally
{
_suppressSelectionResetOnPopupClose = false;
ViewModel.SelectedQuickEventDate = null;
}
}
private void MoreDetailsClicked(object sender, RoutedEventArgs e)
{
if (!ViewModel.GoToEventComposePageCommand.CanExecute(null))
{
return;
}
_suppressSelectionResetOnPopupClose = true;
try
{
QuickEventPopupDialog.IsOpen = false;
ViewModel.GoToEventComposePageCommand.Execute(null);
}
finally
{
_suppressSelectionResetOnPopupClose = false;
ViewModel.SelectedQuickEventDate = null;
}
}
private void CloseQuickEventPopup(bool clearSelection)
{
_suppressSelectionResetOnPopupClose = !clearSelection;
QuickEventPopupDialog.IsOpen = false;
_suppressSelectionResetOnPopupClose = false;
if (clearSelection)
{
ViewModel.SelectedQuickEventDate = null;
}
}
} }