Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TimePicker Handlers #454

Merged
merged 19 commits into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public abstract class TimePickerRendererBase<TControl> : ViewRenderer<TimePicker
AlertDialog _dialog;
bool _disposed;

[PortHandler]
bool Is24HourView
{
get => (DateFormat.Is24HourFormat(Context) && Element.Format == (string)TimePicker.FormatProperty.DefaultValue) || Element.Format?.Contains('H') == true;
Expand Down Expand Up @@ -154,6 +155,7 @@ void OnCancelButtonClicked(object sender, EventArgs e)
Element.Unfocus();
}

[PortHandler]
void SetTime(TimeSpan time)
{
if (String.IsNullOrEmpty(Element.Format))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public TimePickerRenderer()

}

[PortHandler]
protected override UITextField CreateNativeControl()
{
return new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect };
Expand Down Expand Up @@ -201,6 +202,7 @@ void UpdateCharacterSpacing()
Control.AttributedText = textAttr;
}

[PortHandler]
void UpdateTime()
{
_picker.Date = new DateTime(1, 1, 1).Add(Element.Time).ToNSDate();
Expand Down Expand Up @@ -237,6 +239,7 @@ void UpdateTime()
Element.InvalidateMeasureNonVirtual(Controls.Internals.InvalidationTrigger.MeasureChanged);
}

[PortHandler]
void UpdateElementTime()
{
ElementController.SetValueFromRenderer(TimePicker.TimeProperty, _picker.Date.ToDateTime() - new DateTime(1, 1, 1));
Expand Down
1 change: 0 additions & 1 deletion src/Controls/samples/Controls.Sample/Pages/MainPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

namespace Maui.Controls.Sample.Pages
{

public class MainPage : ContentPage, IPage
{
MainPageViewModel _viewModel;
Expand Down
7 changes: 7 additions & 0 deletions src/Controls/src/Core/HandlerImpl/TimePicker.Impl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Microsoft.Maui.Controls
{
public partial class TimePicker : ITimePicker
{

}
}
2 changes: 1 addition & 1 deletion src/Controls/src/Core/TimePicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Microsoft.Maui.Controls
{
public class TimePicker : View, IFontElement, ITextElement, IElementConfiguration<TimePicker>
public partial class TimePicker : View, IFontElement, ITextElement, IElementConfiguration<TimePicker>
{
public static readonly BindableProperty FormatProperty = BindableProperty.Create(nameof(Format), typeof(string), typeof(TimePicker), "t");

Expand Down
10 changes: 10 additions & 0 deletions src/Core/src/Core/ITimePicker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace Microsoft.Maui
{
public interface ITimePicker : IView
{
string Format { get; }
TimeSpan Time { get; set; }
}
}
84 changes: 84 additions & 0 deletions src/Core/src/Handlers/TimePicker/TimePickerHandler.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using Android.App;
using Android.Text.Format;

namespace Microsoft.Maui.Handlers
{
public partial class TimePickerHandler : AbstractViewHandler<ITimePicker, MauiTimePicker>
{
MauiTimePicker? _timePicker;
AlertDialog? _dialog;

protected override MauiTimePicker CreateNativeView()
{
_timePicker = new MauiTimePicker(Context)
{
ShowPicker = ShowPickerDialog,
HidePicker = HidePickerDialog
};

return _timePicker;
}

protected override void DisconnectHandler(MauiTimePicker nativeView)
{
if (_dialog != null)
{
_dialog.Hide();
_dialog = null;
}
}

protected virtual TimePickerDialog CreateTimePickerDialog(int hour, int minute)
{
void onTimeSetCallback(object? obj, TimePickerDialog.TimeSetEventArgs args)
hartez marked this conversation as resolved.
Show resolved Hide resolved
{
if (VirtualView == null || TypedNativeView == null)
return;

VirtualView.Time = new TimeSpan(args.HourOfDay, args.Minute, 0);
}

var dialog = new TimePickerDialog(Context!, onTimeSetCallback, hour, minute, Use24HourView);

return dialog;
}

public static void MapFormat(TimePickerHandler handler, ITimePicker timePicker)
{
handler.TypedNativeView?.UpdateFormat(timePicker);
}

public static void MapTime(TimePickerHandler handler, ITimePicker timePicker)
{
handler.TypedNativeView?.UpdateTime(timePicker);
}

void ShowPickerDialog()
{
if (VirtualView == null)
return;

var time = VirtualView.Time;
ShowPickerDialog(time.Hours, time.Minutes);
}

// This overload is here so we can pass in the current values from the dialog
// on an orientation change (so that orientation changes don't cause the user's date selection progress
// to be lost). Not useful until we have orientation changed events.
void ShowPickerDialog(int hour, int minute)
{
_dialog = CreateTimePickerDialog(hour, minute);
_dialog.Show();
}

void HidePickerDialog()
{
_dialog?.Hide();
hartez marked this conversation as resolved.
Show resolved Hide resolved
_dialog = null;
}

bool Use24HourView => VirtualView != null && (DateFormat.Is24HourFormat(TypedNativeView?.Context)
&& VirtualView.Format == "t" || VirtualView.Format == "HH:mm");
}
}
12 changes: 12 additions & 0 deletions src/Core/src/Handlers/TimePicker/TimePickerHandler.Standard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Microsoft.Maui.Handlers
{
public partial class TimePickerHandler : AbstractViewHandler<ITimePicker, object>
{
protected override object CreateNativeView() => throw new NotImplementedException();

public static void MapFormat(TimePickerHandler handler, ITimePicker view) { }
public static void MapTime(TimePickerHandler handler, ITimePicker view) { }
}
}
21 changes: 21 additions & 0 deletions src/Core/src/Handlers/TimePicker/TimePickerHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Microsoft.Maui.Handlers
{
public partial class TimePickerHandler
{
public static PropertyMapper<ITimePicker, TimePickerHandler> TimePickerMapper = new PropertyMapper<ITimePicker, TimePickerHandler>(ViewHandler.ViewMapper)
{
[nameof(ITimePicker.Format)] = MapFormat,
[nameof(ITimePicker.Time)] = MapTime
};

public TimePickerHandler() : base(TimePickerMapper)
{

}

public TimePickerHandler(PropertyMapper mapper) : base(mapper ?? TimePickerMapper)
{

}
}
}
54 changes: 54 additions & 0 deletions src/Core/src/Handlers/TimePicker/TimePickerHandler.iOS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;

namespace Microsoft.Maui.Handlers
{
public partial class TimePickerHandler : AbstractViewHandler<ITimePicker, MauiTimePicker>
{
protected override MauiTimePicker CreateNativeView()
{
return new MauiTimePicker(() => {
SetVirtualViewTime();
TypedNativeView?.ResignFirstResponder();
});
}

protected override void ConnectHandler(MauiTimePicker nativeView)
{
if (nativeView != null)
nativeView.ValueChanged += OnValueChanged;
}

protected override void DisconnectHandler(MauiTimePicker nativeView)
{
if (nativeView != null)
{
nativeView.RemoveFromSuperview();
nativeView.ValueChanged -= OnValueChanged;
nativeView.Dispose();
}
}

public static void MapFormat(TimePickerHandler handler, ITimePicker timePicker)
{
handler.TypedNativeView?.UpdateFormat(timePicker);
}

public static void MapTime(TimePickerHandler handler, ITimePicker timePicker)
{
handler.TypedNativeView?.UpdateTime(timePicker);
}

void OnValueChanged(object? sender, EventArgs e)
{
SetVirtualViewTime();
}

void SetVirtualViewTime()
{
if (VirtualView == null || TypedNativeView == null)
return;

VirtualView.Time = TypedNativeView.Date.ToDateTime() - new DateTime(1, 1, 1);
}
}
}
3 changes: 2 additions & 1 deletion src/Core/src/Hosting/AppHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public static IAppHostBuilder UseMauiHandlers(this IAppHostBuilder builder)
{ typeof(ILabel), typeof(LabelHandler) },
{ typeof(IProgress), typeof(ProgressBarHandler) },
{ typeof(ISlider), typeof(SliderHandler) },
{ typeof(ISwitch), typeof(SwitchHandler) }
{ typeof(ISwitch), typeof(SwitchHandler) },
{ typeof(ITimePicker), typeof(TimePickerHandler) }
});

return builder;
Expand Down
46 changes: 46 additions & 0 deletions src/Core/src/Platform/Android/MauiTimePicker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using Android.Content;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using static Android.Views.View;

namespace Microsoft.Maui
{
public class MauiTimePicker : EditText, IOnClickListener
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some overlap with the Picker? I see some derived types here: https://github.com/dotnet/maui/pull/433/files#diff-df34a6df84bad7762b1ed8a016db8d80eeb0e1c94f25562446e52424f94a196a

Also, there is a PickerManager that might have some things that we may want/need? https://github.com/dotnet/maui/pull/433/files#diff-ba5788b29220792f8b9f67a65077c6b25781a9cfd8b59ef06896671af9b24efe

Should these both be using a shared base type that is a PickerEditText and then each type of picker just ahs a thin wrapper for it?

{
public MauiTimePicker(Context? context) : base(context)
{
Initialize();
}

public MauiTimePicker(Context? context, IAttributeSet attrs) : base(context, attrs)
{
Initialize();
}

public MauiTimePicker(Context? context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
Initialize();
}

protected MauiTimePicker(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}

private void Initialize()
{
Focusable = true;
SetOnClickListener(this);
}

public Action? ShowPicker { get; set; }
public Action? HidePicker { get; set; }

public void OnClick(View? v)
{
ShowPicker?.Invoke();
}
}
}
28 changes: 28 additions & 0 deletions src/Core/src/Platform/Android/TimeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;

namespace Microsoft.Maui
{
public static class TimeExtensions
{
public static string ToFormattedString(this ITimePicker timePicker)
{
var time = timePicker.Time;
var format = timePicker.Format;

return time.ToFormattedString(format);
}

public static string ToFormattedString(this TimeSpan time, string format)
{
if (string.IsNullOrEmpty(format))
{
var timeFormat = "t";
return DateTime.Today.Add(time).ToString(timeFormat);
}
else
{
return DateTime.Today.Add(time).ToString(format);
}
}
}
}
23 changes: 23 additions & 0 deletions src/Core/src/Platform/Android/TimePickerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Microsoft.Maui
{
public static class TimePickerExtensions
{
public static void UpdateFormat(this MauiTimePicker mauiTimePicker, ITimePicker view)
{
mauiTimePicker.SetTime(view);
}

public static void UpdateTime(this MauiTimePicker mauiTimePicker, ITimePicker view)
{
mauiTimePicker.SetTime(view);
}

internal static void SetTime(this MauiTimePicker mauiTimePicker, ITimePicker timePicker)
{
var time = timePicker.Time;
var format = timePicker.Format;

mauiTimePicker.Text = time.ToFormattedString(format);
}
}
}
20 changes: 20 additions & 0 deletions src/Core/src/Platform/MaciOS/DateExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using Foundation;

namespace Microsoft.Maui
{
public static class DateExtensions
{
internal static DateTime ReferenceDate = new DateTime(2001, 1, 1, 0, 0, 0);

public static DateTime ToDateTime(this NSDate date)
{
return ReferenceDate.AddSeconds(date.SecondsSinceReferenceDate);
}

public static NSDate ToNSDate(this DateTime date)
{
return NSDate.FromTimeIntervalSinceReferenceDate((date - ReferenceDate).TotalSeconds);
}
}
}
Loading