Calendar rendering.
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Wino.Calendar.ViewModels.Data;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Calendar.Controls;
|
||||
|
||||
public sealed class MonthCellLayout
|
||||
{
|
||||
public MonthCellLayout(DateOnly date, int index, int row, int column, LayoutRect bounds)
|
||||
{
|
||||
Date = date;
|
||||
Index = index;
|
||||
Row = row;
|
||||
Column = column;
|
||||
Bounds = bounds;
|
||||
}
|
||||
|
||||
public DateOnly Date { get; set; }
|
||||
public int Index { get; set; }
|
||||
public int Row { get; set; }
|
||||
public int Column { get; set; }
|
||||
public LayoutRect Bounds { get; set; }
|
||||
}
|
||||
|
||||
public sealed class MonthItemLayout
|
||||
{
|
||||
public MonthItemLayout(CalendarItemViewModel item, int cellIndex, DateOnly date, LayoutRect bounds, DataTemplate? template = null)
|
||||
{
|
||||
Item = item;
|
||||
CellIndex = cellIndex;
|
||||
Date = date;
|
||||
Bounds = bounds;
|
||||
Template = template;
|
||||
}
|
||||
|
||||
public CalendarItemViewModel Item { get; set; }
|
||||
public int CellIndex { get; set; }
|
||||
public DateOnly Date { get; set; }
|
||||
public LayoutRect Bounds { get; set; }
|
||||
public DataTemplate? Template { get; set; }
|
||||
}
|
||||
|
||||
public sealed class MonthCellLabelLayout
|
||||
{
|
||||
public MonthCellLabelLayout(string dayText, double labelOpacity, LayoutRect bounds)
|
||||
{
|
||||
DayText = dayText;
|
||||
LabelOpacity = labelOpacity;
|
||||
Bounds = bounds;
|
||||
}
|
||||
|
||||
public string DayText { get; set; }
|
||||
public double LabelOpacity { get; set; }
|
||||
public LayoutRect Bounds { get; set; }
|
||||
}
|
||||
|
||||
internal sealed record MonthCalendarLayoutResult(double CellWidth, double CellHeight, IReadOnlyList<MonthCellLayout> Cells, IReadOnlyList<MonthItemLayout> Items);
|
||||
|
||||
internal static class MonthCalendarLayoutCalculator
|
||||
{
|
||||
public const int ColumnCount = 7;
|
||||
public const int RowCount = 6;
|
||||
|
||||
private const double CellPadding = 4d;
|
||||
private const double DayLabelHeight = 20d;
|
||||
private const double ItemHeight = 18d;
|
||||
private const double ItemGap = 2d;
|
||||
|
||||
public static MonthCalendarLayoutResult Calculate(VisibleDateRange range, IEnumerable<CalendarItemViewModel> items, double availableWidth, double availableHeight)
|
||||
{
|
||||
var cellWidth = availableWidth <= 0 ? 0d : availableWidth / ColumnCount;
|
||||
var cellHeight = availableHeight <= 0 ? 0d : availableHeight / RowCount;
|
||||
var cells = range.Dates
|
||||
.Select((date, index) =>
|
||||
{
|
||||
var row = index / ColumnCount;
|
||||
var column = index % ColumnCount;
|
||||
return new MonthCellLayout(
|
||||
date,
|
||||
index,
|
||||
row,
|
||||
column,
|
||||
new LayoutRect(column * cellWidth, row * cellHeight, cellWidth, cellHeight));
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
var itemLayouts = new List<MonthItemLayout>();
|
||||
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
var cellItems = GetCellItems(items, cell.Date).ToList();
|
||||
|
||||
for (var index = 0; index < cellItems.Count; index++)
|
||||
{
|
||||
var y = cell.Bounds.Y + DayLabelHeight + CellPadding + (index * (ItemHeight + ItemGap));
|
||||
|
||||
if (y + ItemHeight > cell.Bounds.Y + cell.Bounds.Height - CellPadding)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
itemLayouts.Add(new MonthItemLayout(
|
||||
cellItems[index],
|
||||
cell.Index,
|
||||
cell.Date,
|
||||
new LayoutRect(
|
||||
cell.Bounds.X + CellPadding,
|
||||
y,
|
||||
Math.Max(0, cell.Bounds.Width - (CellPadding * 2)),
|
||||
ItemHeight)));
|
||||
}
|
||||
}
|
||||
|
||||
return new MonthCalendarLayoutResult(cellWidth, cellHeight, cells, itemLayouts);
|
||||
}
|
||||
|
||||
private static IEnumerable<CalendarItemViewModel> GetCellItems(IEnumerable<CalendarItemViewModel> items, DateOnly date)
|
||||
{
|
||||
var dayStart = date.ToDateTime(TimeOnly.MinValue);
|
||||
var dayEnd = dayStart.AddDays(1);
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (!CalendarItemAccessor.TryGetTimeRange(item, out var start, out var end))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var localStart = start.LocalDateTime;
|
||||
var localEnd = end.LocalDateTime;
|
||||
|
||||
if (localEnd <= localStart)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (localStart < dayEnd && localEnd > dayStart)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user