Ground work for Wino Calendar. (#475)

Wino Calendar abstractions.
This commit is contained in:
Burak Kaan Köse
2024-11-10 23:28:25 +01:00
committed by GitHub
parent a979e8430f
commit d1d6f12f05
486 changed files with 7969 additions and 2708 deletions

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Wino.Activation;
using Wino.Calendar.Views;
namespace Wino.Calendar.Activation
{
public class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
{
protected override Task HandleInternalAsync(IActivatedEventArgs args)
{
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
return Task.CompletedTask;
}
// Only navigate if Frame content doesn't exist.
protected override bool CanHandleInternal(IActivatedEventArgs args)
=> (Window.Current?.Content as Frame)?.Content == null;
}
}

View File

@@ -1,7 +1,34 @@
<Application
<core:WinoApplication
x:Class="Wino.Calendar.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Wino.Calendar">
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
xmlns:core="using:Wino.Core.UWP"
xmlns:coreStyles="using:Wino.Core.UWP.Styles"
xmlns:local="using:Wino.Calendar"
xmlns:styles="using:Wino.Calendar.Styles">
<Application.Resources>
<controls:XamlControlsResources>
<controls:XamlControlsResources.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<core:CoreGeneric />
<!-- Calendar Specific -->
<styles:WinoCalendarResources />
<ResourceDictionary Source="Styles/CalendarRenderStyles.xaml" />
<ResourceDictionary Source="Styles/CalendarDayItemsControl.xaml" />
<ResourceDictionary Source="Styles/DayHeaderControl.xaml" />
<ResourceDictionary Source="Styles/WinoDayTimelineCanvas.xaml" />
<ResourceDictionary Source="Styles/DayColumnControl.xaml" />
<ResourceDictionary Source="Styles/WinoCalendarView.xaml" />
<ResourceDictionary Source="Styles/WinoCalendarTypeSelectorControl.xaml" />
<styles:CalendarItemControlResources />
</controls:XamlControlsResources.MergedDictionaries>
</controls:XamlControlsResources>
</Application.Resources>
</core:WinoApplication>
</Application>

View File

@@ -1,100 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Microsoft.Extensions.DependencyInjection;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Core.Preview;
using Wino.Activation;
using Wino.Calendar.Activation;
using Wino.Calendar.Services;
using Wino.Calendar.ViewModels;
using Wino.Core;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP;
namespace Wino.Calendar
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
public sealed partial class App : WinoApplication
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public override string AppCenterKey => "dfdad6ab-95f9-44cc-9112-45ec6730c49e";
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
InitializeComponent();
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
public override IServiceProvider ConfigureServices()
{
Frame rootFrame = Window.Current.Content as Frame;
var services = new ServiceCollection();
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
services.RegisterCoreServices();
services.RegisterCoreUWPServices();
services.RegisterCoreViewModels();
RegisterUWPServices(services);
RegisterViewModels(services);
RegisterActivationHandlers(services);
return services.BuildServiceProvider();
}
#region Dependency Injection
private void RegisterActivationHandlers(IServiceCollection services)
{
//services.AddTransient<ProtocolActivationHandler>();
//services.AddTransient<ToastNotificationActivationHandler>();
//services.AddTransient<FileActivationHandler>();
}
private void RegisterUWPServices(IServiceCollection services)
{
services.AddSingleton<INavigationService, NavigationService>();
services.AddSingleton<ICalendarDialogService, DialogService>();
services.AddTransient<ISettingsBuilderService, SettingsBuilderService>();
}
private void RegisterViewModels(IServiceCollection services)
{
services.AddSingleton(typeof(AppShellViewModel));
services.AddTransient(typeof(CalendarPageViewModel));
services.AddTransient(typeof(CalendarSettingsPageViewModel));
services.AddTransient(typeof(AccountManagementViewModel));
}
#endregion
protected override void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
// TODO: Check server running.
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}");
if (!args.PrelaunchActivated)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
await ActivateWinoAsync(args);
}
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
protected override IEnumerable<ActivationHandler> GetActivationHandlers()
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
return null;
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
protected override ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler()
=> new DefaultActivationHandler();
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Windows.Foundation;
namespace Wino.Calendar.Args
{
/// <summary>
/// When a new timeline cell is selected.
/// </summary>
public class TimelineCellSelectedArgs : EventArgs
{
public TimelineCellSelectedArgs(DateTime clickedDate, Point canvasPoint, Point positionerPoint, Size cellSize)
{
ClickedDate = clickedDate;
CanvasPoint = canvasPoint;
PositionerPoint = positionerPoint;
CellSize = cellSize;
}
/// <summary>
/// Clicked date and time information for the cell.
/// </summary>
public DateTime ClickedDate { get; set; }
/// <summary>
/// Position relative to the cell drawing part of the canvas.
/// Used to detect clicked cell from the position.
/// </summary>
public Point CanvasPoint { get; }
/// <summary>
/// Position relative to the main root positioner element of the drawing canvas.
/// Used to show the create event dialog teaching tip in correct position.
/// </summary>
public Point PositionerPoint { get; }
/// <summary>
/// Size of the cell.
/// </summary>
public Size CellSize { get; }
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace Wino.Calendar.Args
{
/// <summary>
/// When selected timeline cell is unselected.
/// </summary>
public class TimelineCellUnselectedArgs : EventArgs { }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,132 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls
{
public class CalendarDayItemsControl : Control
{
private const string PART_CalendarPanel = nameof(PART_CalendarPanel);
private WinoCalendarPanel CalendarPanel;
public CalendarDayModel DayModel
{
get { return (CalendarDayModel)GetValue(DayModelProperty); }
set { SetValue(DayModelProperty, value); }
}
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(CalendarDayItemsControl), new PropertyMetadata(null, new PropertyChangedCallback(OnRepresentingDateChanged)));
private static void OnRepresentingDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CalendarDayItemsControl control)
{
if (e.OldValue != null && e.OldValue is CalendarDayModel oldCalendarDayModel)
{
control.DetachCollection(oldCalendarDayModel.Events);
}
if (e.NewValue != null && e.NewValue is CalendarDayModel newCalendarDayModel)
{
control.AttachCollection(newCalendarDayModel.Events);
}
control.ResetItems();
control.RenderEvents();
}
}
public CalendarDayItemsControl()
{
DefaultStyleKey = typeof(CalendarDayItemsControl);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
CalendarPanel = GetTemplateChild(PART_CalendarPanel) as WinoCalendarPanel;
RenderEvents();
}
private void ResetItems()
{
if (CalendarPanel == null) return;
CalendarPanel.Children.Clear();
}
private void RenderEvents()
{
if (CalendarPanel == null || CalendarPanel.DayModel == null) return;
RenderCalendarItems();
}
private void AttachCollection(ObservableCollection<ICalendarItem> newCollection)
=> newCollection.CollectionChanged += CalendarItemsChanged;
private void DetachCollection(ObservableCollection<ICalendarItem> oldCollection)
=> oldCollection.CollectionChanged -= CalendarItemsChanged;
private void CalendarItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (ICalendarItem item in e.NewItems)
{
AddItem(item);
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (ICalendarItem item in e.OldItems)
{
var control = GetCalendarItemControl(item);
if (control != null)
{
CalendarPanel.Children.Remove(control);
}
}
break;
case NotifyCollectionChangedAction.Reset:
ResetItems();
break;
default:
break;
}
}
private CalendarItemControl GetCalendarItemControl(ICalendarItem item)
=> CalendarPanel.Children.Where(c => c is CalendarItemControl calendarItemControl && calendarItemControl.Item == item).FirstOrDefault() as CalendarItemControl;
private void RenderCalendarItems()
{
if (DayModel == null || DayModel.Events == null || DayModel.Events.Count == 0)
{
ResetItems();
return;
}
foreach (var item in DayModel.Events)
{
AddItem(item);
}
}
private void AddItem(ICalendarItem item)
{
CalendarPanel.Children.Add(new CalendarItemControl()
{
Item = item
});
}
}
}

View File

@@ -0,0 +1,42 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Interfaces;
namespace Wino.Calendar.Controls
{
public class CalendarItemControl : Control
{
public ICalendarItem Item
{
get { return (ICalendarItem)GetValue(ItemProperty); }
set { SetValue(ItemProperty, value); }
}
public static readonly DependencyProperty ItemProperty = DependencyProperty.Register(nameof(Item), typeof(ICalendarItem), typeof(CalendarItemControl), new PropertyMetadata(null, new PropertyChangedCallback(OnItemChanged)));
public CalendarItemControl()
{
DefaultStyleKey = typeof(CalendarItemControl);
}
private static void OnItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CalendarItemControl control)
{
control.UpdateDateRendering();
}
}
private void UpdateDateRendering()
{
if (Item == null) return;
UpdateLayout();
}
public override string ToString()
{
return Item?.Name ?? "NA";
}
}
}

View File

@@ -0,0 +1,42 @@
using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Controls;
namespace Wino.Calendar.Controls
{
/// <summary>
/// FlipView that hides the navigation buttons and exposes methods to navigate to the next and previous items with animations.
/// </summary>
public class CustomCalendarFlipView : FlipView
{
private const string PART_PreviousButton = "PreviousButtonHorizontal";
private const string PART_NextButton = "NextButtonHorizontal";
private Button PreviousButton;
private Button NextButton;
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
PreviousButton = GetTemplateChild(PART_PreviousButton) as Button;
NextButton = GetTemplateChild(PART_NextButton) as Button;
// Hide navigation buttons
PreviousButton.Opacity = NextButton.Opacity = 0;
PreviousButton.IsHitTestVisible = NextButton.IsHitTestVisible = false;
}
public void GoPreviousFlip()
{
var backPeer = new ButtonAutomationPeer(PreviousButton);
backPeer.Invoke();
}
public void GoNextFlip()
{
var nextPeer = new ButtonAutomationPeer(NextButton);
nextPeer.Invoke();
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls
{
public class DayColumnControl : Control
{
private const string PART_HeaderDateDayText = nameof(PART_HeaderDateDayText);
private const string PART_IsTodayBorder = nameof(PART_IsTodayBorder);
private const string PART_ColumnHeaderText = nameof(PART_ColumnHeaderText);
private const string TodayState = nameof(TodayState);
private const string NotTodayState = nameof(NotTodayState);
private TextBlock HeaderDateDayText;
private TextBlock ColumnHeaderText;
private Border IsTodayBorder;
public CalendarDayModel DayModel
{
get { return (CalendarDayModel)GetValue(DayModelProperty); }
set { SetValue(DayModelProperty, value); }
}
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(DayColumnControl), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
public DayColumnControl()
{
DefaultStyleKey = typeof(DayColumnControl);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
HeaderDateDayText = GetTemplateChild(PART_HeaderDateDayText) as TextBlock;
ColumnHeaderText = GetTemplateChild(PART_ColumnHeaderText) as TextBlock;
IsTodayBorder = GetTemplateChild(PART_IsTodayBorder) as Border;
UpdateValues();
}
private static void OnRenderingPropertiesChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
{
if (control is DayColumnControl columnControl)
{
columnControl.UpdateValues();
}
}
private void UpdateValues()
{
if (HeaderDateDayText == null || ColumnHeaderText == null || IsTodayBorder == null || DayModel == null) return;
HeaderDateDayText.Text = DayModel.RepresentingDate.Day.ToString();
ColumnHeaderText.Text = DayModel.RepresentingDate.ToString("dddd", DayModel.CalendarRenderOptions.CalendarSettings.CultureInfo);
bool isToday = DayModel.RepresentingDate.Date == DateTime.Now.Date;
VisualStateManager.GoToState(this, isToday ? TodayState : NotTodayState, false);
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Calendar.Controls
{
public class DayHeaderControl : Control
{
private const string PART_DayHeaderTextBlock = nameof(PART_DayHeaderTextBlock);
private TextBlock HeaderTextblock;
public DayHeaderDisplayType DisplayType
{
get { return (DayHeaderDisplayType)GetValue(DisplayTypeProperty); }
set { SetValue(DisplayTypeProperty, value); }
}
public DateTime Date
{
get { return (DateTime)GetValue(DateProperty); }
set { SetValue(DateProperty, value); }
}
public static readonly DependencyProperty DateProperty = DependencyProperty.Register(nameof(Date), typeof(DateTime), typeof(DayHeaderControl), new PropertyMetadata(default(DateTime), new PropertyChangedCallback(OnHeaderPropertyChanged)));
public static readonly DependencyProperty DisplayTypeProperty = DependencyProperty.Register(nameof(DisplayType), typeof(DayHeaderDisplayType), typeof(DayHeaderControl), new PropertyMetadata(DayHeaderDisplayType.TwentyFourHour, new PropertyChangedCallback(OnHeaderPropertyChanged)));
public DayHeaderControl()
{
DefaultStyleKey = typeof(DayHeaderControl);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
HeaderTextblock = GetTemplateChild(PART_DayHeaderTextBlock) as TextBlock;
UpdateHeaderText();
}
private static void OnHeaderPropertyChanged(DependencyObject control, DependencyPropertyChangedEventArgs e)
{
if (control is DayHeaderControl headerControl)
{
headerControl.UpdateHeaderText();
}
}
private void UpdateHeaderText()
{
if (HeaderTextblock != null)
{
HeaderTextblock.Text = DisplayType == DayHeaderDisplayType.TwelveHour ? Date.ToString("h tt") : Date.ToString("HH:mm");
}
}
}
}

View File

@@ -0,0 +1,144 @@
using System;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Messaging;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Calendar.Args;
using Wino.Core.Domain.Models.Calendar;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Controls
{
public class WinoCalendarControl : Control
{
private const string PART_WinoFlipView = nameof(PART_WinoFlipView);
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
#region Dependency Properties
public static readonly DependencyProperty DayRangesProperty = DependencyProperty.Register(nameof(DayRanges), typeof(ObservableCollection<DayRangeRenderModel>), typeof(WinoCalendarControl), new PropertyMetadata(null));
public static readonly DependencyProperty SelectedFlipViewIndexProperty = DependencyProperty.Register(nameof(SelectedFlipViewIndex), typeof(int), typeof(WinoCalendarControl), new PropertyMetadata(-1));
public static readonly DependencyProperty SelectedFlipViewDayRangeProperty = DependencyProperty.Register(nameof(SelectedFlipViewDayRange), typeof(DayRangeRenderModel), typeof(WinoCalendarControl), new PropertyMetadata(null));
public DayRangeRenderModel SelectedFlipViewDayRange
{
get { return (DayRangeRenderModel)GetValue(SelectedFlipViewDayRangeProperty); }
set { SetValue(SelectedFlipViewDayRangeProperty, value); }
}
/// <summary>
/// Gets or sets the collection of day ranges to render.
/// Each day range usually represents a week, but it may support other ranges.
/// </summary>
public ObservableCollection<DayRangeRenderModel> DayRanges
{
get { return (ObservableCollection<DayRangeRenderModel>)GetValue(DayRangesProperty); }
set { SetValue(DayRangesProperty, value); }
}
public int SelectedFlipViewIndex
{
get { return (int)GetValue(SelectedFlipViewIndexProperty); }
set { SetValue(SelectedFlipViewIndexProperty, value); }
}
#endregion
private WinoDayTimelineCanvas _activeCanvas;
public WinoDayTimelineCanvas ActiveCanvas
{
get { return _activeCanvas; }
set
{
// FlipView's timeline is changing.
// Make sure to unregister from the old one.
if (_activeCanvas != null)
{
// Dismiss any selection on the old canvas.
_activeCanvas.SelectedDateTime = null;
_activeCanvas.TimelineCellSelected -= ActiveTimelineCellSelected;
_activeCanvas.TimelineCellUnselected -= ActiveTimelineCellUnselected;
}
_activeCanvas = value;
if (_activeCanvas != null)
{
_activeCanvas.TimelineCellSelected += ActiveTimelineCellSelected;
_activeCanvas.TimelineCellUnselected += ActiveTimelineCellUnselected;
// Raise visible date range change to shell.
WeakReferenceMessenger.Default.Send(new VisibleDateRangeChangedMessage(_activeCanvas.RenderOptions.DateRange));
}
}
}
private WinoCalendarFlipView InternalFlipView;
public WinoCalendarControl()
{
DefaultStyleKey = typeof(WinoCalendarControl);
SizeChanged += CalendarSizeChanged;
}
private void CalendarSizeChanged(object sender, SizeChangedEventArgs e)
{
if (ActiveCanvas == null) return;
ActiveCanvas.SelectedDateTime = null;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
InternalFlipView = GetTemplateChild(PART_WinoFlipView) as WinoCalendarFlipView;
// Each FlipViewItem will have 1 timeline canvas to draw hour cells in the background that supports selection of them.
// When the selection changes, we need to stop listening to the old canvas and start listening to the new one to catch events.
InternalFlipView.ActiveTimelineCanvasChanged += FlipViewsActiveTimelineCanvasChanged;
}
private void FlipViewsActiveTimelineCanvasChanged(object sender, WinoDayTimelineCanvas e)
{
ActiveCanvas = e;
SelectedFlipViewDayRange = InternalFlipView.SelectedItem as DayRangeRenderModel;
}
private void ActiveTimelineCellUnselected(object sender, TimelineCellUnselectedArgs e)
=> TimelineCellUnselected?.Invoke(this, e);
private void ActiveTimelineCellSelected(object sender, TimelineCellSelectedArgs e)
=> TimelineCellSelected?.Invoke(this, e);
public void NavigateToDay(DateTime dateTime) => InternalFlipView.NavigateToDay(dateTime);
public void ResetTimelineSelection()
{
if (ActiveCanvas == null) return;
ActiveCanvas.SelectedDateTime = null;
}
public void GoNextRange()
{
if (InternalFlipView == null) return;
InternalFlipView.GoNextFlip();
}
public void GoPreviousRange()
{
if (InternalFlipView == null) return;
InternalFlipView.GoPreviousFlip();
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.WinUI;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.MenuItems;
namespace Wino.Calendar.Controls
{
public class WinoCalendarFlipView : CustomCalendarFlipView
{
public event EventHandler<WinoDayTimelineCanvas> ActiveTimelineCanvasChanged;
public WinoCalendarFlipView()
{
SelectionChanged += CalendarDisplayRangeChanged;
}
private async void CalendarDisplayRangeChanged(object sender, SelectionChangedEventArgs e)
{
if (SelectedIndex < 0)
ActiveTimelineCanvasChanged?.Invoke(this, null);
else
{
// TODO: Refactor this mechanism by listening to PrepareContainerForItemOverride and Loaded events together.
while (ContainerFromIndex(SelectedIndex) == null)
{
await Task.Delay(250);
}
if (ContainerFromIndex(SelectedIndex) is FlipViewItem flipViewItem)
{
var canvas = flipViewItem.FindDescendant<WinoDayTimelineCanvas>();
ActiveTimelineCanvasChanged?.Invoke(this, canvas);
}
}
}
/// <summary>
/// Navigates to the specified date in the calendar.
/// </summary>
/// <param name="dateTime">Date to navigate.</param>
public async void NavigateToDay(DateTime dateTime)
{
// Find the day range that contains the date.
var dayRange = GetItemsSource()?.FirstOrDefault(a => a.CalendarDays.Any(b => b.RepresentingDate.Date == dateTime.Date));
if (dayRange != null)
{
var navigationItemIndex = GetItemsSource().IndexOf(dayRange);
if (Math.Abs(navigationItemIndex - SelectedIndex) > 4)
{
// Difference between dates are high.
// No need to animate this much, just go without animating.
SelectedIndex = navigationItemIndex;
}
else
{
// Until we reach the day in the flip, simulate next-prev button clicks.
// This will make sure the FlipView animations are triggered.
// Setting SelectedIndex directly doesn't trigger the animations.
while (SelectedIndex != navigationItemIndex)
{
if (SelectedIndex > navigationItemIndex)
{
GoPreviousFlip();
}
else
{
GoNextFlip();
}
}
}
}
}
public void NavigateHour(TimeSpan hourTimeSpan)
{
// Total height of the FlipViewItem is the same as vertical ScrollViewer to position day headers.
// Find the day range that contains the hour.
}
private ObservableRangeCollection<DayRangeRenderModel> GetItemsSource()
=> ItemsSource as ObservableRangeCollection<DayRangeRenderModel>;
}
}

View File

@@ -0,0 +1,254 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Calendar.Models;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls
{
public class WinoCalendarPanel : Panel
{
private const double LastItemRightExtraMargin = 12d;
// Store each ICalendarItem measurements by their Id.
private readonly Dictionary<Guid, CalendarItemMeasurement> _measurements = new Dictionary<Guid, CalendarItemMeasurement>();
public static readonly DependencyProperty EventItemMarginProperty = DependencyProperty.Register(nameof(EventItemMargin), typeof(Thickness), typeof(WinoCalendarPanel), new PropertyMetadata(new Thickness(0, 0, 0, 0)));
public static readonly DependencyProperty DayModelProperty = DependencyProperty.Register(nameof(DayModel), typeof(CalendarDayModel), typeof(WinoCalendarPanel), new PropertyMetadata(null, new PropertyChangedCallback(OnDayChanged)));
public CalendarDayModel DayModel
{
get { return (CalendarDayModel)GetValue(DayModelProperty); }
set { SetValue(DayModelProperty, value); }
}
public Thickness EventItemMargin
{
get { return (Thickness)GetValue(EventItemMarginProperty); }
set { SetValue(EventItemMarginProperty, value); }
}
private static void OnDayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WinoCalendarPanel control)
{
// We need to listen for new events being added or removed from the collection to reset measurements.
if (e.OldValue is CalendarDayModel oldDayModel)
{
control.DetachCollection(oldDayModel.Events);
}
if (e.NewValue is CalendarDayModel newDayModel)
{
control.AttachCollection(newDayModel.Events);
}
control.ResetMeasurements();
control.UpdateLayout();
}
}
private void AttachCollection(IEnumerable<ICalendarItem> events)
{
if (events is INotifyCollectionChanged collection)
{
collection.CollectionChanged += EventCollectionChanged;
}
}
private void DetachCollection(IEnumerable<ICalendarItem> events)
{
if (events is INotifyCollectionChanged collection)
{
collection.CollectionChanged -= EventCollectionChanged;
}
}
private void ResetMeasurements() => _measurements.Clear();
// No need to handle actions. Each action requires a full measurement update.
private void EventCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => ResetMeasurements();
private double GetChildTopMargin(DateTime childStart, double availableHeight)
{
double totalMinutes = 1440;
double minutesFromStart = (childStart - DayModel.RepresentingDate).TotalMinutes;
return (minutesFromStart / totalMinutes) * availableHeight;
}
private double GetChildWidth(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
{
return (calendarItemMeasurement.Right - calendarItemMeasurement.Left) * availableWidth;
}
private double GetChildLeftMargin(CalendarItemMeasurement calendarItemMeasurement, double availableWidth)
=> availableWidth * calendarItemMeasurement.Left;
private double GetChildHeight(DateTime childStart, DateTime childEnd)
{
double totalMinutes = 1440;
double availableHeight = DayModel.CalendarRenderOptions.CalendarSettings.HourHeight * 24;
double childDuration = (childEnd - childStart).TotalMinutes;
return (childDuration / totalMinutes) * availableHeight;
}
protected override Size ArrangeOverride(Size finalSize)
{
// Measure/arrange each child height and width.
// This is a vertical calendar. Therefore the height of each child is the duration of the event.
// Children weights for left and right will be saved if they don't exist.
// This is important because we don't want to measure the weights again.
// They don't change until new event is added or removed.
// Width of the each child may depend on the rectangle packing algorithm.
// Children are first categorized into columns. Then each column is shifted to the left until
// no overlap occurs. The width of each child is calculated based on the number of columns it spans.
double availableHeight = finalSize.Height;
double availableWidth = finalSize.Width;
var calendarControls = Children.Cast<CalendarItemControl>();
if (_measurements.Count == 0 && DayModel.Events.Count > 0)
{
// We keep track of this collection when event is added/removed/reset etc.
// So if the collection is empty, we must fill it up again for proper calculations.
LayoutEvents(DayModel.Events);
}
foreach (var child in calendarControls)
{
// We can't arrange this child. It doesn't have a valid ICalendarItem or measurement.
if (child.Item == null || !_measurements.ContainsKey(child.Item.Id)) continue;
var childMeasurement = _measurements[child.Item.Id];
double childHeight = Math.Max(0, GetChildHeight(child.Item.StartTime, child.Item.EndTime));
double childWidth = Math.Max(0, GetChildWidth(childMeasurement, finalSize.Width));
double childTop = Math.Max(0, GetChildTopMargin(child.Item.StartTime, availableHeight));
double childLeft = Math.Max(0, GetChildLeftMargin(childMeasurement, availableWidth));
bool isHorizontallyLastItem = childMeasurement.Right == 1;
// Add additional right margin to items that falls on the right edge of the panel.
// Max of 5% of the width or 20px.
var extraRightMargin = isHorizontallyLastItem ? Math.Max(LastItemRightExtraMargin, finalSize.Width * 5 / 100) : 0;
var finalChildWidth = childWidth - extraRightMargin;
if (finalChildWidth < 0) finalChildWidth = 1;
child.Measure(new Size(childWidth, childHeight));
var arrangementRect = new Rect(childLeft + EventItemMargin.Left, childTop + EventItemMargin.Top, childWidth - extraRightMargin, childHeight);
child.Arrange(arrangementRect);
}
return finalSize;
}
#region ColumSpanning and Packing Algorithm
private void AddOrUpdateMeasurement(ICalendarItem calendarItem, CalendarItemMeasurement measurement)
{
if (_measurements.ContainsKey(calendarItem.Id))
{
_measurements[calendarItem.Id] = measurement;
}
else
{
_measurements.Add(calendarItem.Id, measurement);
}
}
// Pick the left and right positions of each event, such that there are no overlap.
private void LayoutEvents(IEnumerable<ICalendarItem> events)
{
var columns = new List<List<ICalendarItem>>();
DateTime? lastEventEnding = null;
foreach (var ev in events.OrderBy(ev => ev.Period.Start).ThenBy(ev => ev.Period.End))
{
if (ev.Period.Start >= lastEventEnding)
{
PackEvents(columns);
columns.Clear();
lastEventEnding = null;
}
bool placed = false;
foreach (var col in columns)
{
if (!col.Last().Period.OverlapsWith(ev.Period))
{
col.Add(ev);
placed = true;
break;
}
}
if (!placed)
{
columns.Add(new List<ICalendarItem> { ev });
}
if (lastEventEnding == null || ev.Period.End > lastEventEnding.Value)
{
lastEventEnding = ev.Period.End;
}
}
if (columns.Count > 0)
{
PackEvents(columns);
}
}
// Set the left and right positions for each event in the connected group.
private void PackEvents(List<List<ICalendarItem>> columns)
{
float numColumns = columns.Count;
int iColumn = 0;
foreach (var col in columns)
{
foreach (var ev in col)
{
int colSpan = ExpandEvent(ev, iColumn, columns);
var leftWeight = iColumn / numColumns;
var rightWeight = (iColumn + colSpan) / numColumns;
AddOrUpdateMeasurement(ev, new CalendarItemMeasurement(leftWeight, rightWeight));
}
iColumn++;
}
}
// Checks how many columns the event can expand into, without colliding with other events.
private int ExpandEvent(ICalendarItem ev, int iColumn, List<List<ICalendarItem>> columns)
{
int colSpan = 1;
foreach (var col in columns.Skip(iColumn + 1))
{
foreach (var ev1 in col)
{
if (ev1.Period.OverlapsWith(ev.Period)) return colSpan;
}
colSpan++;
}
return colSpan;
}
#endregion
}
}

View File

@@ -0,0 +1,92 @@
using System.Windows.Input;
using CommunityToolkit.Diagnostics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Calendar.Controls
{
public class WinoCalendarTypeSelectorControl : Control
{
private const string PART_TodayButton = nameof(PART_TodayButton);
private const string PART_DayToggle = nameof(PART_DayToggle);
private const string PART_WeekToggle = nameof(PART_WeekToggle);
private const string PART_MonthToggle = nameof(PART_MonthToggle);
private const string PART_YearToggle = nameof(PART_YearToggle);
public static readonly DependencyProperty SelectedTypeProperty = DependencyProperty.Register(nameof(SelectedType), typeof(CalendarDisplayType), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(CalendarDisplayType.Week));
public static readonly DependencyProperty DisplayDayCountProperty = DependencyProperty.Register(nameof(DisplayDayCount), typeof(int), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(0));
public static readonly DependencyProperty TodayClickedCommandProperty = DependencyProperty.Register(nameof(TodayClickedCommand), typeof(ICommand), typeof(WinoCalendarTypeSelectorControl), new PropertyMetadata(null));
public ICommand TodayClickedCommand
{
get { return (ICommand)GetValue(TodayClickedCommandProperty); }
set { SetValue(TodayClickedCommandProperty, value); }
}
public CalendarDisplayType SelectedType
{
get { return (CalendarDisplayType)GetValue(SelectedTypeProperty); }
set { SetValue(SelectedTypeProperty, value); }
}
public int DisplayDayCount
{
get { return (int)GetValue(DisplayDayCountProperty); }
set { SetValue(DisplayDayCountProperty, value); }
}
private AppBarButton _todayButton;
private AppBarToggleButton _dayToggle;
private AppBarToggleButton _weekToggle;
private AppBarToggleButton _monthToggle;
private AppBarToggleButton _yearToggle;
public WinoCalendarTypeSelectorControl()
{
DefaultStyleKey = typeof(WinoCalendarTypeSelectorControl);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_todayButton = GetTemplateChild(PART_TodayButton) as AppBarButton;
_dayToggle = GetTemplateChild(PART_DayToggle) as AppBarToggleButton;
_weekToggle = GetTemplateChild(PART_WeekToggle) as AppBarToggleButton;
_monthToggle = GetTemplateChild(PART_MonthToggle) as AppBarToggleButton;
_yearToggle = GetTemplateChild(PART_YearToggle) as AppBarToggleButton;
Guard.IsNotNull(_todayButton, nameof(_todayButton));
Guard.IsNotNull(_dayToggle, nameof(_dayToggle));
Guard.IsNotNull(_weekToggle, nameof(_weekToggle));
Guard.IsNotNull(_monthToggle, nameof(_monthToggle));
Guard.IsNotNull(_yearToggle, nameof(_yearToggle));
_todayButton.Click += TodayClicked;
_dayToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Day); };
_weekToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Week); };
_monthToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Month); };
_yearToggle.Click += (s, e) => { SetSelectedType(CalendarDisplayType.Year); };
UpdateToggleButtonStates();
}
private void TodayClicked(object sender, RoutedEventArgs e) => TodayClickedCommand?.Execute(null);
private void SetSelectedType(CalendarDisplayType type)
{
SelectedType = type;
UpdateToggleButtonStates();
}
private void UpdateToggleButtonStates()
{
_dayToggle.IsChecked = SelectedType == CalendarDisplayType.Day;
_weekToggle.IsChecked = SelectedType == CalendarDisplayType.Week;
_monthToggle.IsChecked = SelectedType == CalendarDisplayType.Month;
_yearToggle.IsChecked = SelectedType == CalendarDisplayType.Year;
}
}
}

View File

@@ -0,0 +1,145 @@
using System;
using System.Windows.Input;
using CommunityToolkit.Diagnostics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Wino.Core.Domain.Models.Calendar;
using Wino.Helpers;
namespace Wino.Calendar.Controls
{
public class WinoCalendarView : Control
{
private const string PART_DayViewItemBorder = nameof(PART_DayViewItemBorder);
private const string PART_CalendarView = nameof(PART_CalendarView);
public static readonly DependencyProperty HighlightedDateRangeProperty = DependencyProperty.Register(nameof(HighlightedDateRange), typeof(DateRange), typeof(WinoCalendarView), new PropertyMetadata(null, new PropertyChangedCallback(OnHighlightedDateRangeChanged)));
public static readonly DependencyProperty VisibleDateBackgroundProperty = DependencyProperty.Register(nameof(VisibleDateBackground), typeof(Brush), typeof(WinoCalendarView), new PropertyMetadata(null, new PropertyChangedCallback(OnPropertiesChanged)));
public static readonly DependencyProperty TodayBackgroundBrushProperty = DependencyProperty.Register(nameof(TodayBackgroundBrush), typeof(Brush), typeof(WinoCalendarView), new PropertyMetadata(null));
public static readonly DependencyProperty DateClickedCommandProperty = DependencyProperty.Register(nameof(DateClickedCommand), typeof(ICommand), typeof(WinoCalendarView), new PropertyMetadata(null));
/// <summary>
/// Gets or sets the command to execute when a date is picked.
/// Unused.
/// </summary>
public ICommand DateClickedCommand
{
get { return (ICommand)GetValue(DateClickedCommandProperty); }
set { SetValue(DateClickedCommandProperty, value); }
}
/// <summary>
/// Gets or sets the highlighted range of dates.
/// </summary>
public DateRange HighlightedDateRange
{
get { return (DateRange)GetValue(HighlightedDateRangeProperty); }
set { SetValue(HighlightedDateRangeProperty, value); }
}
public Brush VisibleDateBackground
{
get { return (Brush)GetValue(VisibleDateBackgroundProperty); }
set { SetValue(VisibleDateBackgroundProperty, value); }
}
public Brush TodayBackgroundBrush
{
get { return (Brush)GetValue(TodayBackgroundBrushProperty); }
set { SetValue(TodayBackgroundBrushProperty, value); }
}
private CalendarView CalendarView;
public WinoCalendarView()
{
DefaultStyleKey = typeof(WinoCalendarView);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
CalendarView = GetTemplateChild(PART_CalendarView) as CalendarView;
Guard.IsNotNull(CalendarView, nameof(CalendarView));
CalendarView.SelectedDatesChanged -= InternalCalendarViewSelectionChanged;
CalendarView.SelectedDatesChanged += InternalCalendarViewSelectionChanged;
// TODO: Should come from settings.
CalendarView.FirstDayOfWeek = Windows.Globalization.DayOfWeek.Monday;
// Everytime display mode changes, update the visible date range backgrounds.
// If users go back from year -> month -> day, we need to update the visible date range backgrounds.
CalendarView.RegisterPropertyChangedCallback(CalendarView.DisplayModeProperty, (s, e) => UpdateVisibleDateRangeBackgrounds());
}
private void InternalCalendarViewSelectionChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args)
{
if (args.AddedDates?.Count > 0)
{
var clickedDate = args.AddedDates[0].Date;
SetInnerDisplayDate(clickedDate);
var clickArgs = new CalendarViewDayClickedEventArgs(clickedDate);
DateClickedCommand?.Execute(clickArgs);
}
// Reset selection, we don't show selected dates but react to them.
CalendarView.SelectedDates.Clear();
}
private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WinoCalendarView control)
{
control.UpdateVisibleDateRangeBackgrounds();
}
}
private void SetInnerDisplayDate(DateTime dateTime) => CalendarView?.SetDisplayDate(dateTime);
// Changing selected dates will trigger the selection changed event.
// It will behave like user clicked the date.
public void GoToDay(DateTime dateTime) => CalendarView.SelectedDates.Add(dateTime);
private static void OnHighlightedDateRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WinoCalendarView control)
{
control.SetInnerDisplayDate(control.HighlightedDateRange.StartDate);
control.UpdateVisibleDateRangeBackgrounds();
}
}
public void UpdateVisibleDateRangeBackgrounds()
{
if (HighlightedDateRange == null || VisibleDateBackground == null || TodayBackgroundBrush == null || CalendarView == null) return;
var markDateCalendarDayItems = WinoVisualTreeHelper.FindDescendants<CalendarViewDayItem>(CalendarView);
foreach (var calendarDayItem in markDateCalendarDayItems)
{
var border = WinoVisualTreeHelper.GetChildObject<Border>(calendarDayItem, PART_DayViewItemBorder);
if (border == null) return;
if (calendarDayItem.Date.Date == DateTime.Today.Date)
{
border.Background = TodayBackgroundBrush;
}
else if (calendarDayItem.Date.Date >= HighlightedDateRange.StartDate.Date && calendarDayItem.Date.Date < HighlightedDateRange.EndDate.Date)
{
border.Background = VisibleDateBackground;
}
else
{
border.Background = null;
}
}
}
}
}

View File

@@ -0,0 +1,277 @@
using System;
using System.Diagnostics;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Windows.Foundation;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Wino.Calendar.Args;
using Wino.Core.Domain.Models.Calendar;
namespace Wino.Calendar.Controls
{
public class WinoDayTimelineCanvas : Control, IDisposable
{
public event EventHandler<TimelineCellSelectedArgs> TimelineCellSelected;
public event EventHandler<TimelineCellUnselectedArgs> TimelineCellUnselected;
private const string PART_InternalCanvas = nameof(PART_InternalCanvas);
private CanvasControl Canvas;
public static readonly DependencyProperty RenderOptionsProperty = DependencyProperty.Register(nameof(RenderOptions), typeof(CalendarRenderOptions), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
public static readonly DependencyProperty SeperatorColorProperty = DependencyProperty.Register(nameof(SeperatorColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
public static readonly DependencyProperty HalfHourSeperatorColorProperty = DependencyProperty.Register(nameof(HalfHourSeperatorColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
public static readonly DependencyProperty SelectedCellBackgroundBrushProperty = DependencyProperty.Register(nameof(SelectedCellBackgroundBrush), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
public static readonly DependencyProperty WorkingHourCellBackgroundColorProperty = DependencyProperty.Register(nameof(WorkingHourCellBackgroundColor), typeof(SolidColorBrush), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnRenderingPropertiesChanged)));
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(nameof(SelectedDateTime), typeof(DateTime?), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedDateTimeChanged)));
public static readonly DependencyProperty PositionerUIElementProperty = DependencyProperty.Register(nameof(PositionerUIElement), typeof(UIElement), typeof(WinoDayTimelineCanvas), new PropertyMetadata(null));
public UIElement PositionerUIElement
{
get { return (UIElement)GetValue(PositionerUIElementProperty); }
set { SetValue(PositionerUIElementProperty, value); }
}
public CalendarRenderOptions RenderOptions
{
get { return (CalendarRenderOptions)GetValue(RenderOptionsProperty); }
set { SetValue(RenderOptionsProperty, value); }
}
public SolidColorBrush HalfHourSeperatorColor
{
get { return (SolidColorBrush)GetValue(HalfHourSeperatorColorProperty); }
set { SetValue(HalfHourSeperatorColorProperty, value); }
}
public SolidColorBrush SeperatorColor
{
get { return (SolidColorBrush)GetValue(SeperatorColorProperty); }
set { SetValue(SeperatorColorProperty, value); }
}
public SolidColorBrush WorkingHourCellBackgroundColor
{
get { return (SolidColorBrush)GetValue(WorkingHourCellBackgroundColorProperty); }
set { SetValue(WorkingHourCellBackgroundColorProperty, value); }
}
public SolidColorBrush SelectedCellBackgroundBrush
{
get { return (SolidColorBrush)GetValue(SelectedCellBackgroundBrushProperty); }
set { SetValue(SelectedCellBackgroundBrushProperty, value); }
}
public DateTime? SelectedDateTime
{
get { return (DateTime?)GetValue(SelectedDateTimeProperty); }
set { SetValue(SelectedDateTimeProperty, value); }
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
Canvas = GetTemplateChild(PART_InternalCanvas) as CanvasControl;
Canvas.Draw += OnCanvasDraw;
Canvas.PointerPressed += OnCanvasPointerPressed;
ForceDraw();
}
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WinoDayTimelineCanvas control)
{
if (e.OldValue != null && e.NewValue == null)
{
control.RaiseCellUnselected();
}
control.ForceDraw();
}
}
private void RaiseCellUnselected()
{
TimelineCellUnselected?.Invoke(this, new TimelineCellUnselectedArgs());
}
private void OnCanvasPointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
if (RenderOptions == null) return;
var hourHeight = RenderOptions.CalendarSettings.HourHeight;
// When users click to cell we need to find the day, hour and minutes (first 30 minutes or second 30 minutes) that it represents on the timeline.
PointerPoint positionerRootPoint = e.GetCurrentPoint(PositionerUIElement);
PointerPoint canvasPointerPoint = e.GetCurrentPoint(Canvas);
Point touchPoint = canvasPointerPoint.Position;
var singleDayWidth = (Canvas.ActualWidth / RenderOptions.TotalDayCount);
int day = (int)(touchPoint.X / singleDayWidth);
int hour = (int)(touchPoint.Y / hourHeight);
bool isSecondHalf = touchPoint.Y % hourHeight > (hourHeight / 2);
var diffX = positionerRootPoint.Position.X - touchPoint.X;
var diffY = positionerRootPoint.Position.Y - touchPoint.Y;
var cellStartRelativePositionX = diffX + (day * singleDayWidth);
var cellEndRelativePositionX = cellStartRelativePositionX + singleDayWidth;
var cellStartRelativePositionY = diffY + (hour * hourHeight) + (isSecondHalf ? hourHeight / 2 : 0);
var cellEndRelativePositionY = cellStartRelativePositionY + (isSecondHalf ? (hourHeight / 2) : hourHeight);
var cellSize = new Size(cellEndRelativePositionX - cellStartRelativePositionX, hourHeight / 2);
var positionerPoint = new Point(cellStartRelativePositionX, cellStartRelativePositionY);
var clickedDateTime = RenderOptions.DateRange.StartDate.AddDays(day).AddHours(hour).AddMinutes(isSecondHalf ? 30 : 0);
// If there is already a selected date, in order to mimic the popup behavior, we need to dismiss the previous selection first.
// Next click will be a new selection.
// Raise the events directly here instead of DP to not lose pointer position.
if (clickedDateTime == SelectedDateTime || SelectedDateTime != null)
{
SelectedDateTime = null;
}
else
{
TimelineCellSelected?.Invoke(this, new TimelineCellSelectedArgs(clickedDateTime, touchPoint, positionerPoint, cellSize));
SelectedDateTime = clickedDateTime;
}
Debug.WriteLine($"Clicked: {clickedDateTime}");
}
public WinoDayTimelineCanvas()
{
DefaultStyleKey = typeof(WinoDayTimelineCanvas);
}
private static void OnRenderingPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WinoDayTimelineCanvas control)
{
control.ForceDraw();
}
}
private void ForceDraw() => Canvas?.Invalidate();
private bool CanDrawTimeline()
{
return RenderOptions != null
&& Canvas != null
&& Canvas.ReadyToDraw
&& WorkingHourCellBackgroundColor != null
&& SeperatorColor != null
&& HalfHourSeperatorColor != null
&& SelectedCellBackgroundBrush != null;
}
private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
{
if (!CanDrawTimeline()) return;
int hours = 24;
double canvasWidth = Canvas.ActualWidth;
double canvasHeight = Canvas.ActualHeight;
if (canvasWidth == 0 || canvasHeight == 0) return;
// Calculate the width of each rectangle (1 day column)
// Equal distribution of the whole width.
double rectWidth = canvasWidth / RenderOptions.TotalDayCount;
// Calculate the height of each rectangle (1 hour row)
double rectHeight = RenderOptions.CalendarSettings.HourHeight;
// Define stroke and fill colors
var strokeColor = SeperatorColor.Color;
float strokeThickness = 0.5f;
for (int day = 0; day < RenderOptions.TotalDayCount; day++)
{
var currentDay = RenderOptions.DateRange.StartDate.AddDays(day);
bool isWorkingDay = RenderOptions.CalendarSettings.WorkingDays.Contains(currentDay.DayOfWeek);
// Loop through each hour (rows)
for (int hour = 0; hour < hours; hour++)
{
var renderTime = TimeSpan.FromHours(hour);
var representingDateTime = currentDay.AddHours(hour);
// Calculate the position and size of the rectangle
double x = day * rectWidth;
double y = hour * rectHeight;
var rectangle = new Rect(x, y, rectWidth, rectHeight);
// Draw the rectangle border.
// This is the main rectangle.
args.DrawingSession.DrawRectangle(rectangle, strokeColor, strokeThickness);
// Fill another rectangle with the working hour background color
// This rectangle must be placed with -1 margin to prevent invisible borders of the main rectangle.
if (isWorkingDay && renderTime >= RenderOptions.CalendarSettings.WorkingHourStart && renderTime <= RenderOptions.CalendarSettings.WorkingHourEnd)
{
var backgroundRectangle = new Rect(x + 1, y + 1, rectWidth - 1, rectHeight - 1);
args.DrawingSession.DrawRectangle(backgroundRectangle, strokeColor, strokeThickness);
args.DrawingSession.FillRectangle(backgroundRectangle, WorkingHourCellBackgroundColor.Color);
}
// Draw a line in the center of the rectangle for representing half hours.
double lineY = y + rectHeight / 2;
args.DrawingSession.DrawLine((float)x, (float)lineY, (float)(x + rectWidth), (float)lineY, HalfHourSeperatorColor.Color, strokeThickness, new CanvasStrokeStyle()
{
DashStyle = CanvasDashStyle.Dot
});
}
// Draw selected item background color for the date if possible.
if (SelectedDateTime != null)
{
var selectedDateTime = SelectedDateTime.Value;
if (selectedDateTime.Date == currentDay.Date)
{
var selectionRectHeight = rectHeight / 2;
var selectedY = selectedDateTime.Hour * rectHeight + (selectedDateTime.Minute / 60) * rectHeight;
// Second half of the hour is selected.
if (selectedDateTime.TimeOfDay.Minutes == 30)
{
selectedY += rectHeight / 2;
}
var selectedRectangle = new Rect(day * rectWidth, selectedY, rectWidth, selectionRectHeight);
args.DrawingSession.FillRectangle(selectedRectangle, SelectedCellBackgroundBrush.Color);
}
}
}
}
public void Dispose()
{
if (Canvas == null) return;
Canvas.Draw -= OnCanvasDraw;
Canvas.PointerPressed -= OnCanvasPointerPressed;
Canvas.RemoveFromVisualTree();
Canvas = null;
}
}
}

View File

@@ -2,13 +2,11 @@
x:Class="Wino.Calendar.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Wino.Calendar"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Wino.Calendar"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
</Grid>
<Grid />
</Page>

View File

@@ -0,0 +1,17 @@
namespace Wino.Calendar.Models
{
public struct CalendarItemMeasurement
{
// Where to start?
public double Left { get; set; }
// Extend until where?
public double Right { get; set; }
public CalendarItemMeasurement(double left, double right)
{
Left = left;
Right = right;
}
}
}

View File

@@ -18,7 +18,7 @@
<Identity
Name="58272BurakKSE.WinoCalendar"
Publisher="CN=51FBDAF3-E212-4149-89A2-A2636B3BC911"
Version="1.0.0.0" />
Version="1.0.7.0" />
<mp:PhoneIdentity PhoneProductId="f047b7dd-96ec-4d54-a862-9321e271e449" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
@@ -46,7 +46,7 @@
Square44x44Logo="Assets\Square44x44Logo.png"
Description="Wino.Calendar"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" Square71x71Logo="Assets\SmallTile.png" Square310x310Logo="Assets\LargeTile.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>

View File

@@ -21,11 +21,24 @@
An Assembly element with Name="*Application*" applies to all assemblies in
the application package. The asterisks are not wildcards.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Add your application specific runtime directives here. -->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Reduce memory footprint when building with Microsoft.Graph -->
<Assembly Name="Microsoft.Graph" Serialize="Excluded" />
<Assembly Name="Microsoft.Kiota.Abstractions" Dynamic="Public" />
<Assembly Name="Microsoft.Kiota.Authentication.Azure" Dynamic="Public" />
<Assembly Name="Microsoft.Kiota.Http.HttpClientLibrary" Dynamic="Public" />
<Assembly Name="Microsoft.Kiota.Serialization.Form" Dynamic="Public" />
<Assembly Name="Microsoft.Kiota.Serialization.Json" Dynamic="Public" />
<Assembly Name="Microsoft.Kiota.Serialization.Multipart" Dynamic="Public" />
<!-- Add your application specific runtime directives here. -->
<Type Name="Windows.Foundation.TypedEventHandler{Microsoft.UI.Xaml.Controls.NavigationView,Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs}" MarshalObject="Public" />
<Type Name="Microsoft.UI.Xaml.Controls.NavigationView">
<Event Name="ItemInvoked" Dynamic="Required"/>
</Type>
</Application>
</Directives>
</Directives>

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Wino.Calendar.Services
{
internal class ApplicationResourceManager
{
}
}

View File

@@ -0,0 +1,15 @@
using Windows.UI.Xaml;
using Wino.Core.Domain.Interfaces;
using Wino.Core.UWP.Services;
namespace Wino.Calendar.Services
{
public class DialogService : DialogServiceBase, ICalendarDialogService
{
public DialogService(IThemeService themeService,
IConfigurationService configurationService,
IApplicationResourceManager<ResourceDictionary> applicationResourceManager) : base(themeService, configurationService, applicationResourceManager)
{
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Calendar.Views;
using Wino.Calendar.Views.Account;
using Wino.Calendar.Views.Settings;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.UWP.Services;
using Wino.Views;
namespace Wino.Calendar.Services
{
public class NavigationService : NavigationServiceBase, INavigationService
{
public Type GetPageType(WinoPage winoPage)
{
switch (winoPage)
{
case WinoPage.CalendarPage:
return typeof(CalendarPage);
case WinoPage.SettingsPage:
return typeof(SettingsPage);
case WinoPage.CalendarSettingsPage:
return typeof(CalendarSettingsPage);
case WinoPage.AccountManagementPage:
return typeof(AccountManagementPage);
default:
throw new Exception("Page is not implemented yet.");
}
}
public bool Navigate(WinoPage page, object parameter = null, NavigationReferenceFrame frame = NavigationReferenceFrame.ShellFrame, NavigationTransitionType transition = NavigationTransitionType.None)
{
// All navigations are performed on shell frame for calendar.
if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage)
{
var shellFrame = shellPage.GetShellFrame();
var pageType = GetPageType(page);
shellFrame.Navigate(pageType, parameter);
return true;
}
return false;
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Calendar.Controls">
<!-- Calendar day items. -->
<Style TargetType="controls:CalendarDayItemsControl">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:CalendarDayItemsControl">
<controls:WinoCalendarPanel
x:Name="PART_CalendarPanel"
DayModel="{TemplateBinding DayModel}"
EventItemMargin="0" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,29 @@
<ResourceDictionary
x:Class="Wino.Calendar.Styles.CalendarItemControlResources"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Calendar.Controls">
<!-- CalendarItemControl -->
<Style TargetType="controls:CalendarItemControl">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:CalendarItemControl">
<Grid
Margin="1,1,1,1"
Background="#cf2b36"
BorderBrush="Gray"
CornerRadius="3">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{x:Bind Item.Name, Mode=OneWay}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,12 @@
using Windows.UI.Xaml;
namespace Wino.Calendar.Styles
{
public sealed partial class CalendarItemControlResources : ResourceDictionary
{
public CalendarItemControlResources()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,26 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StaticResource x:Key="WinoCalendarViewTodayBackgroundBrush" ResourceKey="SystemControlBackgroundAccentBrush" />
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="CalendarSeperatorBrush">#D9D9D9</SolidColorBrush>
<SolidColorBrush x:Key="CalendarFieldWorkingHoursBackgroundBrush">#ffffff</SolidColorBrush>
<SolidColorBrush x:Key="CalendarFieldWorkingNonHoursBackgroundBrush">#f9f9f9</SolidColorBrush>
<SolidColorBrush x:Key="CalendarFieldSelectedBackgroundBrush">#ebebeb</SolidColorBrush>
<SolidColorBrush x:Key="WinoCalendarViewBorderBrush">Yellow</SolidColorBrush>
<SolidColorBrush x:Key="WinoCalendarViewVisibleDayBackgroundBrush">#dfe4ea</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="CalendarSeperatorBrush">#000000</SolidColorBrush>
<SolidColorBrush x:Key="CalendarFieldWorkingHoursBackgroundBrush">#262626</SolidColorBrush>
<SolidColorBrush x:Key="CalendarFieldWorkingNonHoursBackgroundBrush">#1a1a1a</SolidColorBrush>
<SolidColorBrush x:Key="CalendarFieldSelectedBackgroundBrush">#121212</SolidColorBrush>
<SolidColorBrush x:Key="WinoCalendarViewBorderBrush">#3d3d3d</SolidColorBrush>
<SolidColorBrush x:Key="WinoCalendarViewVisibleDayBackgroundBrush">#2f3542</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,93 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Calendar.Controls">
<!-- Top column header DayColumnControl -->
<Style TargetType="controls:DayColumnControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:DayColumnControl">
<Grid MinHeight="100" MaxHeight="150">
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="7" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Name of the day. Monday, Tuesday etc. at the top. -->
<TextBlock
x:Name="PART_ColumnHeaderText"
Margin="8,0,0,0"
FontSize="16"
TextTrimming="CharacterEllipsis" />
<Grid
Grid.Row="2"
Grid.RowSpan="2"
BorderBrush="{ThemeResource CalendarSeperatorBrush}"
BorderThickness="1,1,0,1" />
<!-- Border for today indication. -->
<Border
x:Name="PART_IsTodayBorder"
Grid.Row="1"
Height="5"
Margin="2,0,2,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Background="{ThemeResource SystemAccentColor}"
CornerRadius="2"
Visibility="Collapsed" />
<!-- Place where full day events go. -->
<Grid
x:Name="PART_DayDataAreaGrid"
Grid.Row="2"
Padding="6"
BorderBrush="{ThemeResource CalendarSeperatorBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Day number -->
<TextBlock x:Name="PART_HeaderDateDayText" FontSize="17" />
<!-- Extras -->
<StackPanel Grid.Column="1" HorizontalAlignment="Right" />
<!-- Events -->
<ScrollViewer
Grid.Row="1"
Grid.ColumnSpan="2"
VerticalScrollBarVisibility="Hidden" />
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="TodayOrNotStates">
<VisualState x:Name="NotTodayState" />
<VisualState x:Name="TodayState">
<VisualState.Setters>
<Setter Target="PART_IsTodayBorder.Visibility" Value="Visible" />
<Setter Target="PART_HeaderDateDayText.Foreground" Value="{ThemeResource SystemAccentColor}" />
<Setter Target="PART_HeaderDateDayText.FontWeight" Value="Semibold" />
<Setter Target="PART_ColumnHeaderText.FontWeight" Value="Semibold" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,23 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Calendar.Controls">
<!-- Left day header DayHeaderControl -->
<Style TargetType="controls:DayHeaderControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:DayHeaderControl">
<Grid>
<TextBlock
x:Name="PART_DayHeaderTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="Top"
FontSize="12" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,128 @@
<ResourceDictionary
x:Class="Wino.Calendar.Styles.WinoCalendarResources"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Calendar.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:WinoCalendarControl.Styles"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:Wino.Core.Domain.Models.Calendar"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:toolkitControls="using:CommunityToolkit.WinUI.Controls">
<!-- 08:00 or 8 AM/PM on the left etc. -->
<DataTemplate x:Key="DayCalendarHourHeaderTemplate" x:DataType="models:DayHeaderRenderModel">
<Grid Height="{x:Bind HourHeight}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Top"
Text="{x:Bind DayHeader}" />
</Grid>
</DataTemplate>
<!-- Vertical panel that renders items on canvas. -->
<DataTemplate x:Key="DayCalendarItemVerticalRenderTemplate" x:DataType="models:CalendarDayModel">
<controls:CalendarDayItemsControl DayModel="{x:Bind}" />
</DataTemplate>
<!-- Equally distributed days of week representation in FlipView. -->
<DataTemplate x:Key="FlipTemplate" x:DataType="models:DayRangeRenderModel">
<Grid
x:Name="RootGrid"
ColumnSpacing="0"
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl
Grid.Column="1"
Margin="50,0,16,0"
ItemsSource="{x:Bind CalendarDays}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:CalendarDayModel">
<controls:DayColumnControl DayModel="{x:Bind}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkitControls:UniformGrid
Columns="{Binding CalendarRenderOptions.TotalDayCount}"
Orientation="Horizontal"
Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ScrollViewer
Grid.Row="1"
Grid.ColumnSpan="2"
Margin="0"
Padding="0,0,16,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Rendering left hour headers. -->
<ItemsControl ItemTemplate="{StaticResource DayCalendarHourHeaderTemplate}" ItemsSource="{x:Bind DayHeaders}" />
<!-- Drawing canvas for timeline. -->
<controls:WinoDayTimelineCanvas
Grid.Column="1"
HalfHourSeperatorColor="{ThemeResource CalendarSeperatorBrush}"
PositionerUIElement="{Binding ElementName=RootGrid}"
RenderOptions="{x:Bind CalendarRenderOptions}"
SelectedCellBackgroundBrush="{ThemeResource CalendarFieldSelectedBackgroundBrush}"
SeperatorColor="{ThemeResource CalendarSeperatorBrush}"
WorkingHourCellBackgroundColor="{ThemeResource CalendarFieldWorkingHoursBackgroundBrush}" />
<!-- Each vertical day grids that renders events. -->
<ItemsControl
Grid.Column="1"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ItemTemplate="{StaticResource DayCalendarItemVerticalRenderTemplate}"
ItemsSource="{x:Bind CalendarDays}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkitControls:UniformGrid
Columns="{Binding CalendarRenderOptions.TotalDayCount}"
Orientation="Horizontal"
Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</ScrollViewer>
</Grid>
</DataTemplate>
<!-- Default style for WinoCalendarControl -->
<Style TargetType="controls:WinoCalendarControl">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:WinoCalendarControl">
<controls:WinoCalendarFlipView
x:Name="PART_WinoFlipView"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsTabStop="False"
ItemTemplate="{StaticResource FlipTemplate}"
ItemsSource="{TemplateBinding DayRanges}"
SelectedIndex="{Binding SelectedFlipViewIndex, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
<!-- Teaching tip dialog sizing. -->
<x:Double x:Key="TeachingTipMinWidth">1000</x:Double>
<x:Double x:Key="TeachingTipMaxWidth">1000</x:Double>
</ResourceDictionary>

View File

@@ -0,0 +1,12 @@
using Windows.UI.Xaml;
namespace Wino.Calendar.Styles
{
public sealed partial class WinoCalendarResources : ResourceDictionary
{
public WinoCalendarResources()
{
this.InitializeComponent();
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Calendar.Controls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CalendarRenderStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="controls:WinoCalendarView">
<Setter Property="VisibleDateBackground" Value="{ThemeResource WinoCalendarViewVisibleDayBackgroundBrush}" />
<Setter Property="TodayBackgroundBrush" Value="{ThemeResource SystemColorControlAccentBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:WinoCalendarView">
<CalendarView
x:Name="PART_CalendarView"
BorderBrush="{ThemeResource WinoCalendarViewBorderBrush}"
CalendarItemCornerRadius="5"
CornerRadius="4"
DayItemMargin="0"
IsTodayHighlighted="False"
SelectionMode="Single">
<CalendarView.CalendarViewDayItemStyle>
<Style TargetType="CalendarViewDayItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CalendarViewDayItem">
<Grid>
<Border x:Name="PART_DayViewItemBorder" CornerRadius="5">
<ContentPresenter />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</CalendarView.CalendarViewDayItemStyle>
</CalendarView>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,19 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
xmlns:controls="using:Wino.Calendar.Controls">
<!-- Background Timeline Canvas -->
<Style TargetType="controls:WinoDayTimelineCanvas">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:WinoDayTimelineCanvas">
<canvas:CanvasControl x:Name="PART_InternalCanvas" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,7 @@
using Wino.Calendar.ViewModels;
using Wino.Core.UWP;
namespace Wino.Calendar.Views.Abstract
{
public class AccountManagementPageAbstract : BasePage<AccountManagementViewModel> { }
}

View File

@@ -0,0 +1,7 @@
using Wino.Calendar.ViewModels;
using Wino.Core.UWP;
namespace Wino.Calendar.Views.Abstract
{
public abstract class AppShellAbstract : BasePage<AppShellViewModel> { }
}

View File

@@ -0,0 +1,7 @@
using Wino.Calendar.ViewModels;
using Wino.Core.UWP;
namespace Wino.Calendar.Views.Abstract
{
public abstract class CalendarPageAbstract : BasePage<CalendarPageViewModel> { }
}

View File

@@ -0,0 +1,7 @@
using Wino.Calendar.ViewModels;
using Wino.Core.UWP;
namespace Wino.Calendar.Views.Abstract
{
public abstract class CalendarSettingsPageAbstract : BasePage<CalendarSettingsPageViewModel> { }
}

View File

@@ -0,0 +1,12 @@
<abstract:AccountManagementPageAbstract
x:Class="Wino.Calendar.Views.Account.AccountManagementPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Calendar.Views.Abstract"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Wino.Calendar.Views.Account"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid />
</abstract:AccountManagementPageAbstract>

View File

@@ -0,0 +1,12 @@
using Wino.Calendar.Views.Abstract;
namespace Wino.Calendar.Views.Account
{
public sealed partial class AccountManagementPage : AccountManagementPageAbstract
{
public AccountManagementPage()
{
this.InitializeComponent();
}
}
}

View File

@@ -0,0 +1,251 @@
<abstract:AppShellAbstract
x:Class="Wino.Calendar.Views.AppShell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Calendar.Views.Abstract"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
xmlns:calendarControls="using:Wino.Calendar.Controls"
xmlns:communityControls="using:CommunityToolkit.WinUI.Controls"
xmlns:coreControls="using:Wino.Core.UWP.Controls"
xmlns:coreSelectors="using:Wino.Core.UWP.Selectors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Wino.Calendar.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
muxc:BackdropMaterial.ApplyToRootOrPageBackground="{ThemeResource UseMica}"
mc:Ignorable="d">
<Page.Resources>
<coreSelectors:NavigationMenuTemplateSelector
x:Key="NavigationMenuTemplateSelector"
AccountManagementTemplate="{StaticResource ManageAccountsTemplate}"
RatingItemTemplate="{StaticResource RatingItemTemplate}"
SeperatorTemplate="{StaticResource SeperatorTemplate}"
SettingsItemTemplate="{StaticResource SettingsItemTemplate}" />
<Style
x:Key="CalendarNavigationButtonStyle"
BasedOn="{StaticResource DefaultButtonStyle}"
TargetType="Button">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="Padding" Value="8,4,8,6" />
<Setter Property="CornerRadius" Value="6" />
<Setter Property="Width" Value="40" />
</Style>
</Page.Resources>
<Grid
x:Name="RootGrid"
Padding="0"
ColumnSpacing="0"
RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- CoreWindowText="{x:Bind ViewModel.StatePersistenceService.CoreWindowTitle, Mode=OneWay}" -->
<coreControls:WinoAppTitleBar
x:Name="RealAppBar"
Grid.ColumnSpan="2"
BackButtonClicked="BackButtonClicked"
Canvas.ZIndex="150"
ConnectionStatus="{x:Bind ViewModel.ActiveConnectionStatus, Mode=OneWay}"
CoreWindowText="Wino Calendar"
IsBackButtonVisible="{x:Bind ViewModel.StatePersistenceService.IsBackButtonVisible, Mode=OneWay}"
IsNavigationPaneOpen="{x:Bind MainNavigationView.IsPaneOpen, Mode=TwoWay}"
NavigationViewDisplayMode="{x:Bind MainNavigationView.DisplayMode, Mode=OneWay}"
OpenPaneLength="{x:Bind ViewModel.StatePersistenceService.OpenPaneLength, Mode=OneWay}"
ReconnectCommand="{x:Bind ViewModel.ReconnectServerCommand}"
ShrinkShellContentOnExpansion="False"
SystemReserved="180">
<coreControls:WinoAppTitleBar.ShellFrameContent>
<Grid Margin="4,0,0,0" ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid
x:Name="DragArea"
Grid.ColumnSpan="3"
Background="Transparent" />
<Grid ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="7*" />
</Grid.ColumnDefinitions>
<AutoSuggestBox
x:Name="SearchBox"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
BorderBrush="Transparent"
PlaceholderText="Search" />
<StackPanel
x:Name="NavigationTitleStack"
Grid.Column="1"
Margin="0,0,12,4"
Orientation="Horizontal"
Spacing="6">
<Button
x:Name="PreviousDateButton"
Click="PreviousDateClicked"
Style="{StaticResource CalendarNavigationButtonStyle}">
<PathIcon x:Name="PreviousDateButtonPathIcon" Data="F1 M 8.72 18.599998 C 8.879999 18.733334 9.059999 18.799999 9.26 18.799999 C 9.459999 18.799999 9.633332 18.719999 9.78 18.559999 C 9.926666 18.4 10 18.219999 10 18.019999 C 10 17.82 9.92 17.653332 9.76 17.52 L 4.52 12.559999 L 17.24 12.559999 C 17.453333 12.559999 17.633331 12.486667 17.779999 12.339999 C 17.926666 12.193334 18 12.013333 18 11.799999 C 18 11.586666 17.926666 11.406667 17.779999 11.259999 C 17.633331 11.113333 17.453333 11.039999 17.24 11.039999 L 4.52 11.039999 L 9.76 6.08 C 9.973333 5.893333 10.046666 5.653332 9.98 5.359999 C 9.913333 5.066666 9.74 4.880001 9.46 4.799999 C 9.179999 4.720001 8.933332 4.786667 8.72 5 L 2.32 11.08 C 2.16 11.24 2.053333 11.426666 2 11.639999 C 1.973333 11.746666 1.973333 11.853333 2 11.959999 C 2.053333 12.173333 2.16 12.360001 2.32 12.52 Z " />
</Button>
<Button
x:Name="NextDateButton"
Click="NextDateClicked"
Style="{StaticResource CalendarNavigationButtonStyle}">
<PathIcon x:Name="NextDateButtonPathIcon" Data="F1 M 11.28 5 C 11.12 4.866667 10.94 4.806667 10.74 4.82 C 10.539999 4.833334 10.366666 4.913334 10.219999 5.059999 C 10.073333 5.206665 10 5.379999 10 5.58 C 10 5.779999 10.08 5.946667 10.24 6.08 L 15.48 11.039999 L 2.76 11.039999 C 2.546667 11.039999 2.366667 11.113333 2.22 11.259999 C 2.073333 11.406667 2 11.586666 2 11.799999 C 2 12.013333 2.073333 12.193334 2.22 12.339999 C 2.366667 12.486667 2.546667 12.559999 2.76 12.559999 L 15.48 12.559999 L 10.24 17.52 C 10.026667 17.706665 9.953333 17.946667 10.02 18.24 C 10.086666 18.533333 10.259999 18.719999 10.54 18.799999 C 10.82 18.879999 11.066667 18.813334 11.28 18.599998 L 17.68 12.52 C 17.84 12.360001 17.946667 12.173333 18 11.959999 C 18 11.853333 18 11.746666 18 11.639999 C 17.946667 11.426666 17.84 11.24 17.68 11.08 Z " />
</Button>
<calendarControls:CustomCalendarFlipView
x:Name="DayHeaderNavigationItemsFlipView"
MaxHeight="30"
Margin="8,4,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
Background="Transparent"
FontSize="14"
FontWeight="Normal"
IsHitTestVisible="False"
ItemsSource="{x:Bind ViewModel.DateNavigationHeaderItems}"
SelectedIndex="{x:Bind ViewModel.SelectedDateNavigationHeaderIndex, Mode=OneWay}">
<FlipView.ItemTemplate>
<DataTemplate>
<TextBlock
VerticalAlignment="Center"
FontSize="18"
Style="{StaticResource BodyTextBlockStyle}"
Text="{Binding}" />
</DataTemplate>
</FlipView.ItemTemplate>
</calendarControls:CustomCalendarFlipView>
</StackPanel>
</Grid>
<calendarControls:WinoCalendarTypeSelectorControl
Grid.Column="2"
HorizontalAlignment="Right"
DisplayDayCount="{x:Bind ViewModel.StatePersistenceService.DayDisplayCount, Mode=OneWay}"
SelectedType="{x:Bind ViewModel.StatePersistenceService.CalendarDisplayType, Mode=TwoWay}"
TodayClickedCommand="{x:Bind ViewModel.TodayClickedCommand}" />
</Grid>
</coreControls:WinoAppTitleBar.ShellFrameContent>
</coreControls:WinoAppTitleBar>
<Grid
Grid.RowSpan="2"
Grid.ColumnSpan="2"
Background="{ThemeResource WinoApplicationBackgroundColor}"
IsHitTestVisible="False" />
<muxc:NavigationView
x:Name="MainNavigationView"
Grid.Row="1"
Grid.ColumnSpan="3"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
AlwaysShowHeader="True"
FooterMenuItemsSource="{x:Bind ViewModel.FooterItems, Mode=OneWay}"
IsBackButtonVisible="Collapsed"
IsPaneOpen="{x:Bind ViewModel.PreferencesService.IsNavigationPaneOpened, Mode=TwoWay}"
IsPaneToggleButtonVisible="False"
IsSettingsVisible="False"
IsTabStop="True"
IsTitleBarAutoPaddingEnabled="False"
MenuItemTemplateSelector="{StaticResource NavigationMenuTemplateSelector}"
MenuItemsSource="{x:Bind ViewModel.MenuItems, Mode=OneWay}"
OpenPaneLength="{x:Bind ViewModel.StatePersistenceService.OpenPaneLength, Mode=TwoWay}"
SelectedItem="{x:Bind ViewModel.SelectedMenuItem, Mode=TwoWay}">
<muxc:NavigationView.ContentTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</muxc:NavigationView.ContentTransitions>
<muxc:NavigationView.PaneHeader>
<calendarControls:WinoCalendarView
x:Name="CalendarView"
Margin="2,0"
HorizontalAlignment="Center"
DateClickedCommand="{x:Bind ViewModel.DateClickedCommand}"
HighlightedDateRange="{x:Bind ViewModel.HighlightedDateRange, Mode=OneWay}" />
</muxc:NavigationView.PaneHeader>
<muxc:NavigationView.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Main Content -->
<Frame
x:Name="ShellFrame"
Padding="0,0,7,7"
IsNavigationStackEnabled="False"
Navigated="ShellFrameContentNavigated">
<Frame.ContentTransitions>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
<!-- InfoBar -->
<coreControls:WinoInfoBar
x:Name="ShellInfoBar"
MaxWidth="700"
Margin="0,60,25,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
IsClosable="False"
IsOpen="False" />
</Grid>
</muxc:NavigationView.Content>
</muxc:NavigationView>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LowResolutionStates">
<VisualState x:Name="BigScreen">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1200" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="SmallScreen">
<VisualState.Setters>
<Setter Target="NavigationTitleStack.Visibility" Value="Collapsed" />
<Setter Target="SearchBox.(Grid.ColumnSpan)" Value="2" />
</VisualState.Setters>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CalendarOrientationStates">
<VisualState x:Name="HorizontalCalendar" />
<VisualState x:Name="VerticalCalendar">
<VisualState.Setters>
<Setter Target="DayHeaderNavigationItemsFlipView.ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Target="PreviousDateButtonPathIcon.Data" Value="F1 M 16.799999 13.079999 C 16.933332 12.92 16.993332 12.74 16.98 12.539999 C 16.966665 12.34 16.886665 12.166667 16.74 12.02 C 16.593334 11.873333 16.42 11.799999 16.219999 11.799999 C 16.02 11.799999 15.853333 11.879999 15.719999 12.039999 L 10.76 17.279999 L 10.76 4.559999 C 10.76 4.346666 10.686666 4.166668 10.54 4.02 C 10.393332 3.873333 10.213333 3.799999 10 3.799999 C 9.786666 3.799999 9.606666 3.873333 9.46 4.02 C 9.313333 4.166668 9.24 4.346666 9.24 4.559999 L 9.24 17.279999 L 4.28 12.039999 C 4.146667 11.879999 3.98 11.799999 3.78 11.799999 C 3.58 11.799999 3.4 11.873333 3.24 12.02 C 3.08 12.166667 3 12.34 3 12.539999 C 3 12.74 3.066667 12.92 3.2 13.079999 L 9.28 19.48 C 9.439999 19.639999 9.626666 19.746666 9.84 19.799999 C 9.946667 19.799999 10.053333 19.799999 10.16 19.799999 C 10.373333 19.746666 10.559999 19.639999 10.719999 19.48 Z " />
<Setter Target="NextDateButtonPathIcon.Data" Value="F1 M 3.2 10.52 C 2.986666 10.733333 2.92 10.98 3 11.259999 C 3.08 11.54 3.266666 11.713333 3.56 11.78 C 3.853333 11.846666 4.093333 11.773333 4.28 11.559999 L 9.24 6.32 L 9.24 19.039999 C 9.24 19.253332 9.313333 19.433332 9.46 19.58 C 9.606666 19.726665 9.786666 19.799999 10 19.799999 C 10.213333 19.799999 10.393332 19.726665 10.54 19.58 C 10.686666 19.433332 10.76 19.253332 10.76 19.039999 L 10.76 6.32 L 15.719999 11.559999 C 15.906666 11.773333 16.139999 11.846666 16.42 11.78 C 16.700001 11.713333 16.886665 11.54 16.98 11.259999 C 17.073332 10.98 17.013332 10.733333 16.799999 10.52 L 10.719999 4.119999 C 10.559999 3.959999 10.373333 3.853333 10.16 3.799999 C 10.053333 3.799999 9.946667 3.799999 9.84 3.799999 C 9.626666 3.853333 9.439999 3.959999 9.28 4.119999 Z " />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</abstract:AppShellAbstract>

View File

@@ -0,0 +1,57 @@
using CommunityToolkit.Mvvm.Messaging;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Calendar.Views.Abstract;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views
{
public sealed partial class AppShell : AppShellAbstract,
IRecipient<GoToCalendarDayMessage>
{
private const string STATE_HorizontalCalendar = "HorizontalCalendar";
private const string STATE_VerticalCalendar = "VerticalCalendar";
public Frame GetShellFrame() => ShellFrame;
public AppShell()
{
InitializeComponent();
Window.Current.SetTitleBar(DragArea);
ViewModel.DisplayTypeChanged += CalendarDisplayTypeChanged;
}
private void CalendarDisplayTypeChanged(object sender, Core.Domain.Enums.CalendarDisplayType e)
{
// Go to different states based on the display type.
if (ViewModel.IsVerticalCalendar)
{
VisualStateManager.GoToState(this, STATE_VerticalCalendar, false);
}
else
{
VisualStateManager.GoToState(this, STATE_HorizontalCalendar, false);
}
}
private void ShellFrameContentNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
}
private void BackButtonClicked(Core.UWP.Controls.WinoAppTitleBar sender, Windows.UI.Xaml.RoutedEventArgs args)
{
}
public void Receive(GoToCalendarDayMessage message)
{
CalendarView.GoToDay(message.DateTime);
}
private void PreviousDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoPreviousDateRequestedMessage());
private void NextDateClicked(object sender, RoutedEventArgs e) => WeakReferenceMessenger.Default.Send(new GoNextDateRequestedMessage());
}
}

View File

@@ -0,0 +1,75 @@
<abstract:CalendarPageAbstract
x:Class="Wino.Calendar.Views.CalendarPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Calendar.Views.Abstract"
xmlns:calendarControls="using:Wino.Calendar.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Wino.Calendar.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<!--<Page.Resources>
<x:Double x:Key="TeachingTipMinWidth">500</x:Double>
<x:Double x:Key="TeachingTipMaxWidth">500</x:Double>
</Page.Resources>-->
<Border
Margin="0,0,7,7"
Background="{ThemeResource WinoContentZoneBackgroud}"
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="7">
<Grid>
<calendarControls:WinoCalendarControl
x:Name="CalendarControl"
DayRanges="{x:Bind ViewModel.DayRanges}"
IsHitTestVisible="{x:Bind ViewModel.IsCalendarEnabled, Mode=OneWay}"
SelectedFlipViewDayRange="{x:Bind ViewModel.SelectedDayRange, Mode=TwoWay}"
SelectedFlipViewIndex="{x:Bind ViewModel.SelectedDateRangeIndex, Mode=TwoWay}"
TimelineCellSelected="CellSelected"
TimelineCellUnselected="CellUnselected" />
<Canvas x:Name="CalendarOverlayCanvas" IsHitTestVisible="False">
<!-- Invisible target UI element for teaching tip display. -->
<Grid
x:Name="TeachingTipPositionerGrid"
Background="Transparent"
Visibility="Visible" />
<!-- Single teaching tip to display create event dialog. -->
<muxc:TeachingTip
x:Name="NewEventTip"
Width="500"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Closed="CreateEventTipClosed"
IsOpen="False"
PreferredPlacement="Right"
Target="{x:Bind TeachingTipPositionerGrid}">
<muxc:TeachingTip.Content>
<Grid Margin="0,24" RowSpacing="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TimePicker x:Name="EventTimePicker" ClockIdentifier="24HourClock" />
<!-- Create events dialog -->
<Button
x:Name="AddEvent"
Grid.Row="1"
Click="AddEventClicked"
Content="Add Event" />
</Grid>
</muxc:TeachingTip.Content>
</muxc:TeachingTip>
</Canvas>
</Grid>
</Border>
</abstract:CalendarPageAbstract>

View File

@@ -0,0 +1,77 @@
using System;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls;
using Wino.Calendar.Args;
using Wino.Calendar.Views.Abstract;
using Wino.Core.Domain.Models.Calendar;
using Wino.Messaging.Client.Calendar;
namespace Wino.Calendar.Views
{
public sealed partial class CalendarPage : CalendarPageAbstract,
IRecipient<ScrollToDateMessage>,
IRecipient<GoNextDateRequestedMessage>,
IRecipient<GoPreviousDateRequestedMessage>
{
private DateTime? selectedDateTime;
public CalendarPage()
{
InitializeComponent();
}
public void Receive(ScrollToDateMessage message) => CalendarControl.NavigateToDay(message.Date);
public void Receive(GoNextDateRequestedMessage message) => CalendarControl.GoNextRange();
public void Receive(GoPreviousDateRequestedMessage message) => CalendarControl.GoPreviousRange();
private void CellSelected(object sender, TimelineCellSelectedArgs e)
{
selectedDateTime = e.ClickedDate;
TeachingTipPositionerGrid.Width = e.CellSize.Width;
TeachingTipPositionerGrid.Height = e.CellSize.Height;
Canvas.SetLeft(TeachingTipPositionerGrid, e.PositionerPoint.X);
Canvas.SetTop(TeachingTipPositionerGrid, e.PositionerPoint.Y);
WeakReferenceMessenger.Default.Send(new CalendarEventAdded(new CalendarItem(selectedDateTime.Value, default)));
//var t = new Flyout()
//{
// Content = new TextBlock() { Text = "Create event" }
//};
//t.ShowAt(TeachingTipPositionerGrid, new FlyoutShowOptions()
//{
// ShowMode = FlyoutShowMode.Transient,
// Placement = FlyoutPlacementMode.Right
//});
NewEventTip.IsOpen = true;
}
private void CellUnselected(object sender, TimelineCellUnselectedArgs e)
{
NewEventTip.IsOpen = false;
selectedDateTime = null;
}
private void CreateEventTipClosed(TeachingTip sender, TeachingTipClosedEventArgs args)
{
// Reset the timeline selection when the tip is closed.
CalendarControl.ResetTimelineSelection();
}
private void AddEventClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if (selectedDateTime == null) return;
var eventEndDate = selectedDateTime.Value.Add(EventTimePicker.Time);
// Create the event.
WeakReferenceMessenger.Default.Send(new CalendarEventAdded(new CalendarItem(selectedDateTime.Value, eventEndDate)));
}
}
}

View File

@@ -0,0 +1,197 @@
<abstract:CalendarSettingsPageAbstract
x:Class="Wino.Calendar.Views.Settings.CalendarSettingsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:abstract="using:Wino.Calendar.Views.Abstract"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:controls1="using:Microsoft.UI.Xaml.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:local="using:Wino.Calendar.Views.Settings"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkitExt="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Grid>
<StackPanel>
<!-- First day of week -->
<controls:SettingsCard Description="Adjust the day that week starts." Header="First day of week">
<controls:SettingsCard.HeaderIcon>
<PathIcon Data="F1 M 16.328125 2.5 C 16.816406 2.5 17.283527 2.599285 17.729492 2.797852 C 18.175455 2.99642 18.56608 3.263348 18.901367 3.598633 C 19.236652 3.93392 19.50358 4.324545 19.702148 4.770508 C 19.900715 5.216473 20 5.683595 20 6.171875 L 20 13.828125 C 20 14.316406 19.900715 14.783529 19.702148 15.229492 C 19.50358 15.675456 19.236652 16.066082 18.901367 16.401367 C 18.56608 16.736654 18.175455 17.00358 17.729492 17.202148 C 17.283527 17.400717 16.816406 17.5 16.328125 17.5 L 3.671875 17.5 C 3.183594 17.5 2.716471 17.400717 2.270508 17.202148 C 1.824544 17.00358 1.433919 16.736654 1.098633 16.401367 C 0.763346 16.066082 0.496419 15.675456 0.297852 15.229492 C 0.099284 14.783529 0 14.316406 0 13.828125 L 0 6.171875 C 0 5.683595 0.099284 5.216473 0.297852 4.770508 C 0.496419 4.324545 0.763346 3.93392 1.098633 3.598633 C 1.433919 3.263348 1.824544 2.99642 2.270508 2.797852 C 2.716471 2.599285 3.183594 2.5 3.671875 2.5 Z M 18.75 6.201172 C 18.75 5.875651 18.683268 5.564779 18.549805 5.268555 C 18.41634 4.972332 18.237305 4.711915 18.012695 4.487305 C 17.788086 4.262696 17.527668 4.08366 17.231445 3.950195 C 16.935221 3.816732 16.624348 3.75 16.298828 3.75 L 3.701172 3.75 C 3.375651 3.75 3.064779 3.816732 2.768555 3.950195 C 2.472331 4.08366 2.211914 4.262696 1.987305 4.487305 C 1.762695 4.711915 1.583659 4.972332 1.450195 5.268555 C 1.316732 5.564779 1.25 5.875651 1.25 6.201172 L 1.25 13.798828 C 1.25 14.130859 1.318359 14.446615 1.455078 14.746094 C 1.591797 15.045573 1.774089 15.30599 2.001953 15.527344 C 2.229818 15.748698 2.495117 15.924479 2.797852 16.054688 C 3.100586 16.184896 3.417969 16.25 3.75 16.25 L 3.75 8.125 C 3.75 7.871094 3.798828 7.630209 3.896484 7.402344 C 3.99414 7.174479 4.129231 6.974284 4.301758 6.801758 C 4.474284 6.629232 4.674479 6.494141 4.902344 6.396484 C 5.130208 6.298828 5.371094 6.25 5.625 6.25 L 14.375 6.25 C 14.628906 6.25 14.869791 6.298828 15.097656 6.396484 C 15.32552 6.494141 15.525715 6.629232 15.698242 6.801758 C 15.870768 6.974284 16.005859 7.174479 16.103516 7.402344 C 16.201172 7.630209 16.25 7.871094 16.25 8.125 L 16.25 16.25 L 16.298828 16.25 C 16.624348 16.25 16.935221 16.18327 17.231445 16.049805 C 17.527668 15.916342 17.788086 15.737305 18.012695 15.512695 C 18.237305 15.288086 18.41634 15.02767 18.549805 14.731445 C 18.683268 14.435222 18.75 14.12435 18.75 13.798828 Z M 15 16.25 L 15 8.125 C 14.999999 7.95573 14.93815 7.809246 14.814453 7.685547 C 14.690755 7.56185 14.544271 7.5 14.375 7.5 L 5.625 7.5 C 5.455729 7.5 5.309245 7.56185 5.185547 7.685547 C 5.061849 7.809246 5 7.95573 5 8.125 L 5 16.25 Z M 9.0625 9.6875 C 9.0625 9.947917 8.971354 10.169271 8.789062 10.351562 C 8.606771 10.533854 8.385416 10.625 8.125 10.625 C 7.864583 10.625 7.643229 10.533854 7.460938 10.351562 C 7.278646 10.169271 7.1875 9.947917 7.1875 9.6875 C 7.1875 9.427084 7.278646 9.205729 7.460938 9.023438 C 7.643229 8.841146 7.864583 8.75 8.125 8.75 C 8.385416 8.75 8.606771 8.841146 8.789062 9.023438 C 8.971354 9.205729 9.0625 9.427084 9.0625 9.6875 Z M 12.8125 9.6875 C 12.8125 9.947917 12.721354 10.169271 12.539062 10.351562 C 12.356771 10.533854 12.135416 10.625 11.875 10.625 C 11.614583 10.625 11.393229 10.533854 11.210938 10.351562 C 11.028646 10.169271 10.9375 9.947917 10.9375 9.6875 C 10.9375 9.427084 11.028646 9.205729 11.210938 9.023438 C 11.393229 8.841146 11.614583 8.75 11.875 8.75 C 12.135416 8.75 12.356771 8.841146 12.539062 9.023438 C 12.721354 9.205729 12.8125 9.427084 12.8125 9.6875 Z M 9.0625 13.4375 C 9.0625 13.697917 8.971354 13.919271 8.789062 14.101562 C 8.606771 14.283854 8.385416 14.375 8.125 14.375 C 7.864583 14.375 7.643229 14.283854 7.460938 14.101562 C 7.278646 13.919271 7.1875 13.697917 7.1875 13.4375 C 7.1875 13.177084 7.278646 12.955729 7.460938 12.773438 C 7.643229 12.591146 7.864583 12.5 8.125 12.5 C 8.385416 12.5 8.606771 12.591146 8.789062 12.773438 C 8.971354 12.955729 9.0625 13.177084 9.0625 13.4375 Z M 12.8125 13.4375 C 12.8125 13.697917 12.721354 13.919271 12.539062 14.101562 C 12.356771 14.283854 12.135416 14.375 11.875 14.375 C 11.614583 14.375 11.393229 14.283854 11.210938 14.101562 C 11.028646 13.919271 10.9375 13.697917 10.9375 13.4375 C 10.9375 13.177084 11.028646 12.955729 11.210938 12.773438 C 11.393229 12.591146 11.614583 12.5 11.875 12.5 C 12.135416 12.5 12.356771 12.591146 12.539062 12.773438 C 12.721354 12.955729 12.8125 13.177084 12.8125 13.4375 Z " />
</controls:SettingsCard.HeaderIcon>
<controls:SettingsCard.Content>
<ComboBox ItemsSource="{x:Bind ViewModel.DayNames, Mode=OneWay}" SelectedIndex="{x:Bind ViewModel.SelectedFirstDayOfWeekIndex, Mode=TwoWay}" />
</controls:SettingsCard.Content>
</controls:SettingsCard>
<!-- Working days/hours -->
<controls:SettingsExpander Description="Set the day range for your working hours." Header="Working days">
<controls:SettingsExpander.HeaderIcon>
<PathIcon Data="F1 M 18.75 8.056641 L 18.75 14.443359 C 18.75 14.853516 18.666992 15.244141 18.500977 15.615234 C 18.334961 15.986328 18.113605 16.310221 17.836914 16.586914 C 17.560221 16.863607 17.236328 17.084961 16.865234 17.250977 C 16.494141 17.416992 16.103516 17.5 15.693359 17.5 L 4.306641 17.5 C 3.896484 17.5 3.505859 17.416992 3.134766 17.250977 C 2.763672 17.084961 2.439779 16.863607 2.163086 16.586914 C 1.886393 16.310221 1.665039 15.986328 1.499023 15.615234 C 1.333008 15.244141 1.25 14.853516 1.25 14.443359 L 1.25 8.056641 C 1.25 7.633464 1.334635 7.236328 1.503906 6.865234 C 1.673177 6.494141 1.901042 6.170248 2.1875 5.893555 C 2.473958 5.616862 2.80599 5.398764 3.183594 5.239258 C 3.561198 5.079754 3.958333 5.000001 4.375 5 L 6.25 5 L 6.25 2.5 C 6.25 2.324219 6.282552 2.161459 6.347656 2.011719 C 6.41276 1.86198 6.502278 1.730145 6.616211 1.616211 C 6.730143 1.502279 6.861979 1.412762 7.011719 1.347656 C 7.161458 1.282553 7.324218 1.25 7.5 1.25 L 12.5 1.25 C 12.675781 1.25 12.840169 1.282553 12.993164 1.347656 C 13.146158 1.412762 13.277994 1.500652 13.388672 1.611328 C 13.499349 1.722006 13.587239 1.853842 13.652344 2.006836 C 13.717447 2.159832 13.75 2.324219 13.75 2.5 L 13.75 5 L 15.693359 5 C 16.103516 5.000001 16.494141 5.083009 16.865234 5.249023 C 17.236328 5.41504 17.560221 5.636395 17.836914 5.913086 C 18.113605 6.189779 18.334961 6.513672 18.500977 6.884766 C 18.666992 7.255859 18.75 7.646484 18.75 8.056641 Z M 12.5 2.5 L 7.5 2.5 L 7.5 5 L 12.5 5 Z M 17.5 8.125 C 17.5 7.871094 17.451172 7.630209 17.353516 7.402344 C 17.255859 7.174479 17.120768 6.974284 16.948242 6.801758 C 16.775715 6.629232 16.57552 6.494141 16.347656 6.396484 C 16.119791 6.298828 15.878906 6.25 15.625 6.25 L 4.375 6.25 C 4.121094 6.25 3.880208 6.298828 3.652344 6.396484 C 3.424479 6.494141 3.224284 6.629232 3.051758 6.801758 C 2.879232 6.974284 2.744141 7.174479 2.646484 7.402344 C 2.548828 7.630209 2.5 7.871094 2.5 8.125 L 2.5 14.375 C 2.5 14.628906 2.548828 14.869792 2.646484 15.097656 C 2.744141 15.325521 2.879232 15.525717 3.051758 15.698242 C 3.224284 15.870769 3.424479 16.005859 3.652344 16.103516 C 3.880208 16.201172 4.121094 16.25 4.375 16.25 L 15.625 16.25 C 15.878906 16.25 16.119791 16.201172 16.347656 16.103516 C 16.57552 16.005859 16.775715 15.870769 16.948242 15.698242 C 17.120768 15.525717 17.255859 15.325521 17.353516 15.097656 C 17.451172 14.869792 17.5 14.628906 17.5 14.375 Z " />
</controls:SettingsExpander.HeaderIcon>
<controls:SettingsExpander.Items>
<controls:SettingsCard
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ContentAlignment="Vertical">
<Grid RowSpacing="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
VerticalAlignment="Center"
FontWeight="Bold"
Text="From" />
<ComboBox
Grid.Column="1"
ItemsSource="{x:Bind ViewModel.DayNames, Mode=OneWay}"
SelectedIndex="{x:Bind ViewModel.WorkingDayStartIndex, Mode=TwoWay}" />
<TimePicker
x:Name="WorkHourStartPicker"
Grid.Column="2"
Margin="12,0"
Time="{x:Bind ViewModel.WorkingHourStart, Mode=TwoWay}" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
VerticalAlignment="Center"
FontWeight="Bold"
Text="To" />
<ComboBox
Grid.Column="1"
ItemsSource="{x:Bind ViewModel.DayNames, Mode=OneWay}"
SelectedIndex="{x:Bind ViewModel.WorkingDayEndIndex, Mode=TwoWay}" />
<TimePicker
x:Name="WorkEndStartPicker"
Grid.Column="2"
Margin="12,0"
Time="{x:Bind ViewModel.WorkingHourEnd, Mode=TwoWay}" />
</Grid>
</Grid>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<!-- Cell hour height -->
<controls:SettingsExpander Description="Adjust calendar timeline rendering options." Header="Calendar rendering">
<controls:SettingsExpander.HeaderIcon>
<PathIcon Data="F1 M 15.078125 1.25 C 15.566406 1.25 16.033527 1.349285 16.479492 1.547852 C 16.925455 1.74642 17.31608 2.013348 17.651367 2.348633 C 17.986652 2.68392 18.25358 3.074545 18.452148 3.520508 C 18.650715 3.966473 18.75 4.433594 18.75 4.921875 L 18.75 15.078125 C 18.75 15.566406 18.650715 16.033529 18.452148 16.479492 C 18.25358 16.925455 17.986652 17.31608 17.651367 17.651367 C 17.31608 17.986654 16.925455 18.25358 16.479492 18.452148 C 16.033527 18.650717 15.566406 18.75 15.078125 18.75 L 4.921875 18.75 C 4.433594 18.75 3.966471 18.650717 3.520508 18.452148 C 3.074544 18.25358 2.683919 17.986654 2.348633 17.651367 C 2.013346 17.31608 1.746419 16.925455 1.547852 16.479492 C 1.349284 16.033529 1.25 15.566406 1.25 15.078125 L 1.25 4.921875 C 1.25 4.433594 1.349284 3.966473 1.547852 3.520508 C 1.746419 3.074545 2.013346 2.68392 2.348633 2.348633 C 2.683919 2.013348 3.074544 1.74642 3.520508 1.547852 C 3.966471 1.349285 4.433594 1.25 4.921875 1.25 Z M 4.951172 2.5 C 4.625651 2.5 4.314778 2.566732 4.018555 2.700195 C 3.722331 2.83366 3.461914 3.012695 3.237305 3.237305 C 3.012695 3.461914 2.833659 3.722332 2.700195 4.018555 C 2.566732 4.314779 2.5 4.625651 2.5 4.951172 L 2.5 5 L 17.5 5 L 17.5 4.951172 C 17.5 4.625651 17.433268 4.314779 17.299805 4.018555 C 17.16634 3.722332 16.987305 3.461914 16.762695 3.237305 C 16.538086 3.012695 16.277668 2.83366 15.981445 2.700195 C 15.685221 2.566732 15.374349 2.5 15.048828 2.5 Z M 15.048828 17.5 C 15.374349 17.5 15.685221 17.433268 15.981445 17.299805 C 16.277668 17.166342 16.538086 16.987305 16.762695 16.762695 C 16.987305 16.538086 17.16634 16.27767 17.299805 15.981445 C 17.433268 15.685222 17.5 15.37435 17.5 15.048828 L 17.5 6.25 L 2.5 6.25 L 2.5 15.048828 C 2.5 15.37435 2.566732 15.685222 2.700195 15.981445 C 2.833659 16.27767 3.012695 16.538086 3.237305 16.762695 C 3.461914 16.987305 3.722331 17.166342 4.018555 17.299805 C 4.314778 17.433268 4.625651 17.5 4.951172 17.5 Z M 16.25 9.375 C 16.25 9.544271 16.18815 9.690756 16.064453 9.814453 C 15.940754 9.938151 15.79427 10 15.625 10 L 13.642578 10 C 13.577474 10.188803 13.486328 10.359701 13.369141 10.512695 C 13.251953 10.66569 13.115234 10.797526 12.958984 10.908203 C 12.802734 11.018881 12.633463 11.103516 12.451172 11.162109 C 12.26888 11.220703 12.076822 11.25 11.875 11.25 C 11.673177 11.25 11.481119 11.220703 11.298828 11.162109 C 11.116536 11.103516 10.947266 11.018881 10.791016 10.908203 C 10.634766 10.797526 10.498047 10.66569 10.380859 10.512695 C 10.263672 10.359701 10.172525 10.188803 10.107422 10 L 4.375 10 C 4.205729 10 4.059245 9.938151 3.935547 9.814453 C 3.811849 9.690756 3.75 9.544271 3.75 9.375 C 3.75 9.205729 3.811849 9.059245 3.935547 8.935547 C 4.059245 8.81185 4.205729 8.75 4.375 8.75 L 10.107422 8.75 C 10.172525 8.561198 10.263672 8.3903 10.380859 8.237305 C 10.498047 8.084311 10.634766 7.952475 10.791016 7.841797 C 10.947266 7.73112 11.116536 7.646484 11.298828 7.587891 C 11.481119 7.529297 11.673177 7.5 11.875 7.5 C 12.076822 7.5 12.26888 7.529297 12.451172 7.587891 C 12.633463 7.646484 12.802734 7.73112 12.958984 7.841797 C 13.115234 7.952475 13.251953 8.084311 13.369141 8.237305 C 13.486328 8.3903 13.577474 8.561198 13.642578 8.75 L 15.625 8.75 C 15.79427 8.75 15.940754 8.81185 16.064453 8.935547 C 16.18815 9.059245 16.25 9.205729 16.25 9.375 Z M 16.25 14.375 C 16.25 14.544271 16.18815 14.690756 16.064453 14.814453 C 15.940754 14.938151 15.79427 15 15.625 15 L 9.892578 15 C 9.827474 15.188803 9.736328 15.359701 9.619141 15.512695 C 9.501953 15.66569 9.365234 15.797526 9.208984 15.908203 C 9.052734 16.018881 8.883463 16.103516 8.701172 16.162109 C 8.51888 16.220703 8.326822 16.25 8.125 16.25 C 7.923177 16.25 7.731119 16.220703 7.548828 16.162109 C 7.366536 16.103516 7.197266 16.018881 7.041016 15.908203 C 6.884766 15.797526 6.748047 15.66569 6.630859 15.512695 C 6.513672 15.359701 6.422526 15.188803 6.357422 15 L 4.375 15 C 4.205729 15 4.059245 14.938151 3.935547 14.814453 C 3.811849 14.690756 3.75 14.544271 3.75 14.375 C 3.75 14.205729 3.811849 14.059245 3.935547 13.935547 C 4.059245 13.81185 4.205729 13.75 4.375 13.75 L 6.357422 13.75 C 6.422526 13.561198 6.513672 13.3903 6.630859 13.237305 C 6.748047 13.084311 6.884766 12.952475 7.041016 12.841797 C 7.197266 12.73112 7.366536 12.646484 7.548828 12.587891 C 7.731119 12.529297 7.923177 12.5 8.125 12.5 C 8.326822 12.5 8.51888 12.529297 8.701172 12.587891 C 8.883463 12.646484 9.052734 12.73112 9.208984 12.841797 C 9.365234 12.952475 9.501953 13.084311 9.619141 13.237305 C 9.736328 13.3903 9.827474 13.561198 9.892578 13.75 L 15.625 13.75 C 15.79427 13.75 15.940754 13.81185 16.064453 13.935547 C 16.18815 14.059245 16.25 14.205729 16.25 14.375 Z " />
</controls:SettingsExpander.HeaderIcon>
<controls:SettingsExpander.Items>
<controls:SettingsCard
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ContentAlignment="Vertical"
Description="How many pixels should 1 hour representation occupy in daily/weekly calendars."
Header="Hour height">
<controls:SettingsCard.HeaderIcon>
<PathIcon Data="F1 M 4.921875 18.75 C 4.433594 18.75 3.966471 18.650717 3.520508 18.452148 C 3.074544 18.25358 2.683919 17.986654 2.348633 17.651367 C 2.013346 17.31608 1.746419 16.925455 1.547852 16.479492 C 1.349284 16.033529 1.25 15.566406 1.25 15.078125 L 1.25 4.921875 C 1.25 4.433594 1.349284 3.966473 1.547852 3.520508 C 1.746419 3.074545 2.013346 2.68392 2.348633 2.348633 C 2.683919 2.013348 3.074544 1.74642 3.520508 1.547852 C 3.966471 1.349285 4.433594 1.25 4.921875 1.25 L 15.078125 1.25 C 15.566406 1.25 16.033527 1.349285 16.479492 1.547852 C 16.925455 1.74642 17.31608 2.013348 17.651367 2.348633 C 17.986652 2.68392 18.25358 3.074545 18.452148 3.520508 C 18.650715 3.966473 18.75 4.433594 18.75 4.921875 L 18.75 15.078125 C 18.75 15.566406 18.650715 16.033529 18.452148 16.479492 C 18.25358 16.925455 17.986652 17.31608 17.651367 17.651367 C 17.31608 17.986654 16.925455 18.25358 16.479492 18.452148 C 16.033527 18.650717 15.566406 18.75 15.078125 18.75 Z M 2.5 10 L 17.5 10 L 17.5 4.951172 C 17.5 4.625651 17.433268 4.314779 17.299805 4.018555 C 17.16634 3.722332 16.987305 3.461914 16.762695 3.237305 C 16.538086 3.012695 16.277668 2.83366 15.981445 2.700195 C 15.685221 2.566732 15.374349 2.5 15.048828 2.5 L 4.951172 2.5 C 4.625651 2.5 4.314778 2.566732 4.018555 2.700195 C 3.722331 2.83366 3.461914 3.012695 3.237305 3.237305 C 3.012695 3.461914 2.833659 3.722332 2.700195 4.018555 C 2.566732 4.314779 2.5 4.625651 2.5 4.951172 Z " />
</controls:SettingsCard.HeaderIcon>
<Grid
Padding="0,20"
ColumnSpacing="12"
RowSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Margin="0,5,0,0" VerticalAlignment="Top">
<Run Text="{x:Bind ViewModel.CellHourHeight, Mode=OneWay}" />
<Run Text="px" />
</TextBlock>
<Slider
x:Name="HourCellSlider"
Grid.Column="1"
Maximum="200"
Minimum="60"
Value="{x:Bind ViewModel.CellHourHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<!-- Hour cell demo -->
<Grid
Grid.Column="2"
HorizontalAlignment="Center"
ColumnSpacing="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Right" Text="00:00" />
<TextBlock
Grid.Row="2"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Text="01:00" />
<Grid
Grid.Column="1"
Width="100"
Height="{x:Bind HourCellSlider.Value, Mode=OneWay}"
VerticalAlignment="Center"
BorderBrush="Black"
BorderThickness="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="1" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle
Grid.Row="1"
HorizontalAlignment="Stretch"
Stroke="Black"
StrokeThickness="0.5" />
</Grid>
</Grid>
</Grid>
</controls:SettingsCard>
<controls:SettingsCard Description="Set whether you want to use AM/PM or 24 hour clock identifier." Header="Clock identifier for headers">
<controls:SettingsCard.HeaderIcon>
<PathIcon Data="F1 M 4.921875 18.75 C 4.433594 18.75 3.966471 18.650717 3.520508 18.452148 C 3.074544 18.25358 2.683919 17.986654 2.348633 17.651367 C 2.013346 17.31608 1.746419 16.925455 1.547852 16.479492 C 1.349284 16.033529 1.25 15.566406 1.25 15.078125 L 1.25 4.921875 C 1.25 4.433594 1.349284 3.966473 1.547852 3.520508 C 1.746419 3.074545 2.013346 2.68392 2.348633 2.348633 C 2.683919 2.013348 3.074544 1.74642 3.520508 1.547852 C 3.966471 1.349285 4.433594 1.25 4.921875 1.25 L 15.078125 1.25 C 15.566406 1.25 16.033527 1.349285 16.479492 1.547852 C 16.925455 1.74642 17.31608 2.013348 17.651367 2.348633 C 17.986652 2.68392 18.25358 3.074545 18.452148 3.520508 C 18.650715 3.966473 18.75 4.433594 18.75 4.921875 L 18.75 15.078125 C 18.75 15.566406 18.650715 16.033529 18.452148 16.479492 C 18.25358 16.925455 17.986652 17.31608 17.651367 17.651367 C 17.31608 17.986654 16.925455 18.25358 16.479492 18.452148 C 16.033527 18.650717 15.566406 18.75 15.078125 18.75 Z M 2.5 10 L 17.5 10 L 17.5 4.951172 C 17.5 4.625651 17.433268 4.314779 17.299805 4.018555 C 17.16634 3.722332 16.987305 3.461914 16.762695 3.237305 C 16.538086 3.012695 16.277668 2.83366 15.981445 2.700195 C 15.685221 2.566732 15.374349 2.5 15.048828 2.5 L 4.951172 2.5 C 4.625651 2.5 4.314778 2.566732 4.018555 2.700195 C 3.722331 2.83366 3.461914 3.012695 3.237305 3.237305 C 3.012695 3.461914 2.833659 3.722332 2.700195 4.018555 C 2.566732 4.314779 2.5 4.625651 2.5 4.951172 Z " />
</controls:SettingsCard.HeaderIcon>
<ToggleSwitch
IsOn="{x:Bind ViewModel.Is24HourHeaders, Mode=TwoWay}"
OffContent="12h"
OnContent="24h" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</StackPanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ClockIdentifierStates">
<VisualState x:Name="TwelweHour" />
<VisualState x:Name="TwentyFourHour">
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ViewModel.Is24HourHeaders, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="WorkStartStartPicker.ClockIdentifier" Value="24HourClock" />
<Setter Target="WorkEndStartPicker.ClockIdentifier" Value="24HourClock" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</abstract:CalendarSettingsPageAbstract>

View File

@@ -0,0 +1,13 @@
using Wino.Calendar.Views.Abstract;
namespace Wino.Calendar.Views.Settings
{
public sealed partial class CalendarSettingsPage : CalendarSettingsPageAbstract
{
public CalendarSettingsPage()
{
InitializeComponent();
}
}
}

View File

@@ -1,6 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<!-- UWP WAM Authentication on Xbox needs this. -->
<UseDotNetNativeSharedAssemblyFrameworkPackage>false</UseDotNetNativeSharedAssemblyFrameworkPackage>
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
<AppxSymbolPackageEnabled>False</AppxSymbolPackageEnabled>
<GenerateTestArtifacts>True</GenerateTestArtifacts>
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x64</AppxBundlePlatforms>
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
@@ -115,17 +129,51 @@
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="Activation\DefaultActivationHandler.cs" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Args\TimelineCellSelectedArgs.cs" />
<Compile Include="Args\TimelineCellUnselectedArgs.cs" />
<Compile Include="Controls\CalendarDayItemsControl.cs" />
<Compile Include="Controls\CalendarItemControl.cs" />
<Compile Include="Controls\CustomCalendarFlipView.cs" />
<Compile Include="Controls\DayColumnControl.cs" />
<Compile Include="Controls\DayHeaderControl.cs" />
<Compile Include="Controls\WinoCalendarControl.cs" />
<Compile Include="Controls\WinoCalendarFlipView.cs" />
<Compile Include="Controls\WinoCalendarPanel.cs" />
<Compile Include="Controls\WinoCalendarTypeSelectorControl.cs" />
<Compile Include="Controls\WinoCalendarView.cs" />
<Compile Include="Controls\WinoDayTimelineCanvas.cs" />
<Compile Include="MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Models\CalendarItemMeasurement.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\ApplicationResourceManager.cs" />
<Compile Include="Services\DialogService.cs" />
<Compile Include="Services\NavigationService.cs" />
<Compile Include="Services\SettingsBuilderService.cs" />
<Compile Include="Styles\CalendarItemControlResources.xaml.cs" />
<Compile Include="Styles\WinoCalendarResources.xaml.cs" />
<Compile Include="Views\Abstract\AccountManagementPageAbstract.cs" />
<Compile Include="Views\Abstract\AppShellAbstract.cs" />
<Compile Include="Views\Abstract\CalendarPageAbstract.cs" />
<Compile Include="Views\Abstract\CalendarSettingsPageAbstract.cs" />
<Compile Include="Views\Account\AccountManagementPage.xaml.cs">
<DependentUpon>AccountManagementPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\AppShell.xaml.cs">
<DependentUpon>AppShell.xaml</DependentUpon>
</Compile>
<Compile Include="Views\CalendarPage.xaml.cs">
<DependentUpon>CalendarPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Settings\CalendarSettingsPage.xaml.cs">
<DependentUpon>CalendarSettingsPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
@@ -134,13 +182,57 @@
</ItemGroup>
<ItemGroup>
<None Include="Package.StoreAssociation.xml" />
<Content Include="Assets\LargeTile.scale-100.png" />
<Content Include="Assets\LargeTile.scale-125.png" />
<Content Include="Assets\LargeTile.scale-150.png" />
<Content Include="Assets\LargeTile.scale-200.png" />
<Content Include="Assets\LargeTile.scale-400.png" />
<Content Include="Assets\SmallTile.scale-100.png" />
<Content Include="Assets\SmallTile.scale-125.png" />
<Content Include="Assets\SmallTile.scale-150.png" />
<Content Include="Assets\SmallTile.scale-200.png" />
<Content Include="Assets\SmallTile.scale-400.png" />
<Content Include="Assets\SplashScreen.scale-100.png" />
<Content Include="Assets\SplashScreen.scale-125.png" />
<Content Include="Assets\SplashScreen.scale-150.png" />
<Content Include="Assets\SplashScreen.scale-400.png" />
<Content Include="Assets\Square150x150Logo.scale-100.png" />
<Content Include="Assets\Square150x150Logo.scale-125.png" />
<Content Include="Assets\Square150x150Logo.scale-150.png" />
<Content Include="Assets\Square150x150Logo.scale-400.png" />
<Content Include="Assets\Square44x44Logo.altform-lightunplated_targetsize-16.png" />
<Content Include="Assets\Square44x44Logo.altform-lightunplated_targetsize-24.png" />
<Content Include="Assets\Square44x44Logo.altform-lightunplated_targetsize-256.png" />
<Content Include="Assets\Square44x44Logo.altform-lightunplated_targetsize-32.png" />
<Content Include="Assets\Square44x44Logo.altform-lightunplated_targetsize-48.png" />
<Content Include="Assets\Square44x44Logo.altform-unplated_targetsize-16.png" />
<Content Include="Assets\Square44x44Logo.altform-unplated_targetsize-256.png" />
<Content Include="Assets\Square44x44Logo.altform-unplated_targetsize-32.png" />
<Content Include="Assets\Square44x44Logo.altform-unplated_targetsize-48.png" />
<Content Include="Assets\Square44x44Logo.scale-100.png" />
<Content Include="Assets\Square44x44Logo.scale-125.png" />
<Content Include="Assets\Square44x44Logo.scale-150.png" />
<Content Include="Assets\Square44x44Logo.scale-400.png" />
<Content Include="Assets\Square44x44Logo.targetsize-16.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24.png" />
<Content Include="Assets\Square44x44Logo.targetsize-256.png" />
<Content Include="Assets\Square44x44Logo.targetsize-32.png" />
<Content Include="Assets\Square44x44Logo.targetsize-48.png" />
<Content Include="Assets\StoreLogo.scale-100.png" />
<Content Include="Assets\StoreLogo.scale-125.png" />
<Content Include="Assets\StoreLogo.scale-150.png" />
<Content Include="Assets\StoreLogo.scale-200.png" />
<Content Include="Assets\StoreLogo.scale-400.png" />
<Content Include="Assets\Wide310x150Logo.scale-100.png" />
<Content Include="Assets\Wide310x150Logo.scale-125.png" />
<Content Include="Assets\Wide310x150Logo.scale-150.png" />
<Content Include="Assets\Wide310x150Logo.scale-400.png" />
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
@@ -152,11 +244,91 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\CalendarDayItemsControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\CalendarItemControlResources.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\CalendarRenderStyles.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\DayColumnControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\DayHeaderControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\WinoCalendarResources.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Styles\WinoCalendarTypeSelectorControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\WinoCalendarView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\WinoDayTimelineCanvas.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\Account\AccountManagementPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\AppShell.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\CalendarPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Settings\CalendarSettingsPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Uwp.Controls.Primitives">
<Version>8.1.240916</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.14</Version>
</PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.28.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wino.Calendar.ViewModels\Wino.Calendar.ViewModels.csproj">
<Project>{039affa8-c1cc-4e3b-8a31-6814d7557f74}</Project>
<Name>Wino.Calendar.ViewModels</Name>
</ProjectReference>
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj">
<Project>{cf3312e5-5da0-4867-9945-49ea7598af1f}</Project>
<Name>Wino.Core.Domain</Name>
</ProjectReference>
<ProjectReference Include="..\Wino.Core.ViewModels\Wino.Core.ViewModels.csproj">
<Project>{53723ae8-7e7e-4d54-adab-0a6033255cc8}</Project>
<Name>Wino.Core.ViewModels</Name>
</ProjectReference>
<ProjectReference Include="..\Wino.Core\Wino.Core.csproj">
<Project>{e6b1632a-8901-41e8-9ddf-6793c7698b0b}</Project>
<Name>Wino.Core</Name>
</ProjectReference>
<ProjectReference Include="..\Wino.Messages\Wino.Messaging.csproj">
<Project>{0c307d7e-256f-448c-8265-5622a812fbcc}</Project>
<Name>Wino.Messaging</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>