1using System.Collections.ObjectModel;
2using System.Globalization;
3using System.Threading.Tasks;
4using CommunityToolkit.Maui.Core.Extensions;
5using CommunityToolkit.Mvvm.Input;
10using Path =
Microsoft.Maui.Controls.Shapes.Path;
17 private readonly VerticalStackLayout durationsContainer;
18 private readonly Label titleLabel;
19 private readonly Label descriptionLabel;
20 private readonly CheckBox negateCheckBox;
21 private ObservableCollection<DurationUnits> durationUnits = [
41 new RowDefinition { Height = GridLength.Auto },
42 new RowDefinition { Height = GridLength.Auto },
43 new RowDefinition { Height = GridLength.Auto },
44 new RowDefinition { Height = GridLength.Auto }
46 RowSpacing = AppStyles.SmallSpacing
50 Grid TopTextGrid =
new()
54 new RowDefinition { Height = GridLength.Auto },
55 new RowDefinition { Height = GridLength.Auto }
57 RowSpacing = AppStyles.SmallSpacing
60 this.titleLabel =
new();
61 this.titleLabel.SetBinding(Label.StyleProperty,
new Binding(nameof(this.
TitleLabelStyle), source:
this));
62 this.titleLabel.SetBinding(Label.TextProperty,
new Binding(nameof(this.
TitleLabelText), source:
this));
63 TopTextGrid.Add(this.titleLabel, 0, 0);
65 this.descriptionLabel =
new();
66 this.descriptionLabel.SetBinding(Label.StyleProperty,
new Binding(nameof(this.
DescriptionLabelStyle), source:
this));
67 this.descriptionLabel.SetBinding(Label.TextProperty,
new Binding(nameof(this.
DescriptionLabelText), source:
this));
68 this.descriptionLabel.Margin = 0;
69 TopTextGrid.Add(this.descriptionLabel, 0, 1);
71 MainGrid.Add(TopTextGrid, 0, 0);
74 this.durationsContainer = [];
75 this.durationsContainer.Spacing =
AppStyles.SmallSpacing;
76 MainGrid.Add(this.durationsContainer, 0, 1);
92 HorizontalOptions = LayoutOptions.Fill,
93 VerticalOptions = LayoutOptions.Fill,
99 new ColumnDefinition { Width = GridLength.Star },
100 new ColumnDefinition { Width = GridLength.Auto }
108 HorizontalOptions = LayoutOptions.Start
114 VerticalOptions = LayoutOptions.Center,
115 HorizontalOptions = LayoutOptions.End,
118 Aspect = Stretch.Uniform
125 AddDurationButton.Clicked += (sender, args) => this.AddDurationButton_Clicked();
126 MainGrid.Add(AddDurationButton, 0, 2);
129 Grid NegateGrid =
new()
133 new ColumnDefinition { Width = GridLength.Auto },
134 new ColumnDefinition { Width = GridLength.Auto },
136 HorizontalOptions = LayoutOptions.Center
140 Label NegateLabel =
new()
143 HorizontalOptions = LayoutOptions.Center,
144 VerticalOptions = LayoutOptions.Center,
145 Style = AppStyles.InfoLabel
147 NegateGrid.Add(NegateLabel, 0, 0);
150 this.negateCheckBox =
new()
153 HorizontalOptions = LayoutOptions.Center,
154 VerticalOptions = LayoutOptions.Center,
155 Color = AppColors.ButtonAccessPrimarybg
158 this.negateCheckBox.CheckedChanged += (sender, args) => this.UpdateDuration();
160 NegateGrid.Add(this.negateCheckBox, 1, 0);
162 MainGrid.Add(NegateGrid, 0, 3);
164 this.Content = MainGrid;
174 void PopulateDurationsContainer(List<DurationUnits?> PreviousUnits)
176 List<DurationUnits> AvaliableUnits = [
182 DurationUnits.Seconds
188 this.AddUnit(
DurationUnits.Years,
this.DurationValue.Years.ToString(CultureInfo.InvariantCulture));
196 this.AddUnit(
DurationUnits.Months,
this.DurationValue.Months.ToString(CultureInfo.InvariantCulture));
204 this.AddUnit(
DurationUnits.Days,
this.DurationValue.Days.ToString(CultureInfo.InvariantCulture));
212 this.AddUnit(
DurationUnits.Hours,
this.DurationValue.Hours.ToString(CultureInfo.InvariantCulture));
220 this.AddUnit(
DurationUnits.Minutes,
this.DurationValue.Minutes.ToString(CultureInfo.InvariantCulture));
228 this.AddUnit(
DurationUnits.Seconds,
this.DurationValue.Seconds.ToString(CultureInfo.InvariantCulture));
235 this.durationUnits = AvaliableUnits.ToObservableCollection<
DurationUnits>();
245 CompositeEntry DurationEntry =
new()
250 EntryData = (Value ==
"0") ?
"" : Value
254 DurationLabel UnitLabel =
new()
260 LineBreakMode = LineBreakMode.NoWrap,
264 DurationEntry.LeftView = UnitLabel;
266 this.durationUnits.Remove(Unit);
268 DurationEntry.SetBinding(CompositeEntry.IsValidProperty,
new Binding(nameof(this.
IsValid), source:
this));
270 DurationEntry.TextChanged += (sender, args) => this.UpdateDuration();
272 DurationEntry.Keyboard = Keyboard.Numeric;
276 ImageButton DeleteButton =
new()
280 VerticalOptions = LayoutOptions.Center,
281 HorizontalOptions = LayoutOptions.End,
282 Command =
new RelayCommand(() => this.DeleteUnit(Unit, DurationEntry))
285 DurationEntry.RightView = DeleteButton;
288 this.durationsContainer.Add(DurationEntry);
291 List<CompositeEntry> SortedDurationEntries = [.. this.durationsContainer.Children
292 .OfType<CompositeEntry>()
293 .OrderBy(x => (x.LeftView as DurationLabel)?.Unit)];
295 this.durationsContainer.Clear();
296 foreach (CompositeEntry Entry
in SortedDurationEntries)
297 MainThread.BeginInvokeOnMainThread(() => this.durationsContainer.Add(Entry));
299 MainThread.BeginInvokeOnMainThread(async () => {
300 await Task.Delay(500);
301 DurationEntry.Focus();
308 private void DeleteUnit(
DurationUnits Unit, CompositeEntry DurationView)
310 if (!this.durationUnits.Contains(Unit))
311 this.durationUnits.Add(Unit);
312 this.durationUnits = this.durationUnits.OrderBy(x => x).ToObservableCollection<
DurationUnits>();
314 this.durationsContainer.Remove(DurationView);
316 this.UpdateDuration();
335 void UpdateDuration()
337 Duration NewDuration =
new();
338 CultureInfo Culture = CultureInfo.InvariantCulture;
346 string FilteredText =
new(Data.Where(
char.IsDigit).ToArray());
347 if (Data != FilteredText)
349 DurationEntry.EntryData = FilteredText;
353 if (
string.IsNullOrEmpty(Data))
356 switch (UnitLabel.Unit)
359 NewDuration.Years =
int.Parse(Data, Culture);
362 NewDuration.Months =
int.Parse(Data, Culture);
365 NewDuration.Days =
int.Parse(Data, Culture);
368 NewDuration.Hours =
int.Parse(Data, Culture);
371 NewDuration.Minutes =
int.Parse(Data, Culture);
374 NewDuration.Seconds =
int.Parse(Data, Culture);
379 NewDuration.Negation = this.negateCheckBox.IsChecked;
381 this.DurationValue = NewDuration;
391 async
void AddDurationButton_Clicked()
393 if (this.durationUnits.Count == 0)
401 this.AddUnit(Unit,
"");
406 #region Bindable Properties
477 propertyChanged: OnDurationChanged);
480 public static readonly BindableProperty IsValidProperty =
488 get => (bool)this.GetValue(IsValidProperty);
489 set => this.SetValue(IsValidProperty, value);
498 static void OnDurationChanged(BindableObject bindable,
object oldValue,
object newValue)
502 List<DurationUnits?> PreviousUnits = [.. Picker.durationsContainer.Children
506 Picker.PopulateDurationsContainer(PreviousUnits);
521 #region Helper classes
550 set => this.unit = value;
553 public int CompareTo(
object? other)
A strongly-typed resource class, for looking up localized strings, etc.
static string NegateDuration
Looks up a localized string similar to Negative duration: .
static string AddDuration
Looks up a localized string similar to Add duration.
Base class that references services in the app.
static IPopupService PopupService
Popup service for presenting application popups.
static IReportingStringLocalizer Localizer
Localization service
Static class that gives access to app-specific styles
static Style TransparentTemplateButtonBorder
Style for transparent template button border
static Style TransparentTemplateButtonPath
Style for transparent template button path
static Style RegularCompositeEntry
Style for borders in a regular composte entry control.
static Style ImageOnlyButton
Style for buttons containing only an image.
static Style TransparentTemplateButtonLabel
Style for transparent template button label
static Style SectionTitleLabel
Style of section title labels
async Task< DurationUnits?> OpenDurationPopup()
Open QR Popup
static readonly BindableProperty DescriptionLabelStyleProperty
Bindable property for the style applied to the description label.
static readonly BindableProperty TitleLabelTextProperty
Bindable property for the text of the top label.
static readonly BindableProperty TitleLabelStyleProperty
Bindable property for the style applied to the top label.
static readonly BindableProperty DescriptionLabelTextProperty
Bindable Property for the text of the description label.
Duration DurationValue
Gets or sets the text displayed in the entry.
static readonly BindableProperty DurationValueProperty
Bindable property for the text displayed in the entry.
Style DescriptionLabelStyle
Gets or sets the style applied to the description label.
string TitleLabelText
Gets or sets the text applied to the top label.
CompositeDurationPicker()
Constructor for the CompositeDurationPicker. Initializes the layout and the components needed to add ...
string DescriptionLabelText
Gets or sets the text applied to the description label.
bool IsValid
Gets or sets the validity of the DurationPicker
Style TitleLabelStyle
Gets or sets the style applied to the label above the entry.
A customizable entry control that allows setting views on the left and right sides of the entry.
string EntryData
Gets or sets the text displayed in the entry.
Custom class to be able to access the unit used in and entry
Static class containing SVG Paths for symbols used in the app.
static readonly Geometry ArrowRightPath
Arrow right
static readonly Geometry CancelPath
Cancel button glyph
DurationUnits
Duration units enumeration
TextAlignment
Text alignment of contents.
Represents a duration value, as defined by the xsd:duration data type: http://www....