浏览代码

Merge branch 'siyaoH/1.17/material' of https://github.com/Unity-Technologies/com.unity.uiwidgets into siyaoH/1.17/material

/siyaoH-1.17-PlatformMessage
siyao 4 年前
当前提交
610fc74b
共有 4 个文件被更改,包括 814 次插入0 次删除
  1. 416
      com.unity.uiwidgets/Runtime/material/pickers/date_picker_dialog.cs
  2. 160
      com.unity.uiwidgets/Runtime/material/pickers/date_picker_head.cs
  3. 238
      com.unity.uiwidgets/Runtime/material/pickers/input_date_picker.cs

416
com.unity.uiwidgets/Runtime/material/pickers/date_picker_dialog.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.material {
public static class DatePickerDialogUtils {
public static readonly Size _calendarPortraitDialogSize = new Size(330.0f, 518.0f);
public static readonly Size _calendarLandscapeDialogSize = new Size(496.0f, 346.0f);
public static readonly Size _inputPortraitDialogSize = new Size(330.0f, 270.0f);
public static readonly Size _inputLandscapeDialogSize = new Size(496f, 160.0f);
public static readonly TimeSpan _dialogSizeAnimationDuration = new TimeSpan(0, 0, 0, 0, 200);
public Future<DateTime> showDatePicker(
BuildContext context,
DateTime initialDate,
DateTime firstDate,
DateTime lastDate,
DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar,
SelectableDayPredicate selectableDayPredicate = null,
string helpText = null,
string cancelText = null,
string confirmText = null,
Locale locale = null,
bool useRootNavigator = true,
RouteSettings routeSettings = null,
TextDirection? textDirection = null,
TransitionBuilder builder = null,
DatePickerMode initialDatePickerMode = DatePickerMode.day,
string errorFormatText = null,
string errorInvalidText = null,
string fieldHintText = null,
string fieldLabelText = null
) {
D.assert(context != null);
initialDate = utils.dateOnly(initialDate);
firstDate = utils.dateOnly(firstDate);
lastDate = utils.dateOnly(lastDate);
D.assert(
!lastDate.isBefore(firstDate),
() => $"lastDate {lastDate} must be on or after firstDate {firstDate}."
);
D.assert(
!initialDate.isBefore(firstDate),
() => $"initialDate {initialDate} must be on or after firstDate {firstDate}."
);
D.assert(
!initialDate.isAfter(lastDate),
() => $"initialDate {initialDate} must be on or before lastDate {lastDate}."
);
D.assert(
selectableDayPredicate == null || selectableDayPredicate(initialDate),
() => $"Provided initialDate {initialDate} must satisfy provided selectableDayPredicate."
);
D.assert(initialEntryMode != null);
D.assert(initialDatePickerMode != null);
D.assert(material_.debugCheckHasMaterialLocalizations(context));
Widget dialog = new _DatePickerDialog(
initialDate: initialDate,
firstDate: firstDate,
lastDate: lastDate,
initialEntryMode: initialEntryMode,
selectableDayPredicate: selectableDayPredicate,
helpText: helpText,
cancelText: cancelText,
confirmText: confirmText,
initialCalendarMode: initialDatePickerMode,
errorFormatText: errorFormatText,
errorInvalidText: errorInvalidText,
fieldHintText: fieldHintText,
fieldLabelText: fieldLabelText
);
if (textDirection != null) {
dialog = new Directionality(
textDirection: textDirection.Value,
child: dialog
);
}
if (locale != null) {
dialog = Localizations.overrides(
context: context,
locale: locale,
child: dialog
);
}
return material_.showDialog<DateTime>(
context: context,
useRootNavigator: useRootNavigator,
routeSettings: routeSettings,
builder: (BuildContext subContext) => { return builder == null ? dialog : builder(subContext, dialog); }
);
}
}
class _DatePickerDialog : StatefulWidget {
public _DatePickerDialog(
Key key = null,
DateTime? initialDate = null,
DateTime? firstDate = null,
DateTime? lastDate = null,
DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar,
SelectableDayPredicate selectableDayPredicate = null,
string cancelText = null,
string confirmText = null,
string helpText = null,
DatePickerMode initialCalendarMode = DatePickerMode.day,
string errorFormatText = null,
string errorInvalidText = null,
string fieldHintText = null,
string fieldLabelText = null
) : base(key: key) {
D.assert(initialDate != null);
D.assert(firstDate != null);
D.assert(lastDate != null);
initialDate = utils.dateOnly(initialDate);
firstDate = utils.dateOnly(firstDate);
lastDate = utils.dateOnly(lastDate);
D.assert(
!lastDate.Value.isBefore(firstDate.Value),
() => $"lastDate {lastDate} must be on or after firstDate {firstDate}."
);
D.assert(
initialDate == null || !initialDate.Value.isBefore(firstDate.Value),
() => $"initialDate {initialDate} must be on or after firstDate {firstDate}."
);
D.assert(
initialDate == null || !initialDate.Value.isAfter(lastDate.Value),
() => $"initialDate {initialDate} must be on or before lastDate {lastDate}."
);
D.assert(
selectableDayPredicate == null || initialDate == null || selectableDayPredicate(initialDate),
() => $"Provided initialDate {initialDate} must satisfy provided selectableDayPredicate."
);
this.initialDate = initialDate.Value;
this.firstDate = firstDate.Value;
this.lastDate = lastDate.Value;
this.initialEntryMode = initialEntryMode;
this.selectableDayPredicate = selectableDayPredicate;
this.cancelText = cancelText;
this.confirmText = confirmText;
this.helpText = helpText;
this.initialCalendarMode = initialCalendarMode;
this.errorFormatText = errorFormatText;
this.errorInvalidText = errorInvalidText;
this.fieldHintText = fieldHintText;
this.fieldLabelText = fieldLabelText;
}
public readonly DateTime initialDate;
public readonly DateTime firstDate;
public readonly DateTime lastDate;
public readonly DatePickerEntryMode initialEntryMode;
public readonly SelectableDayPredicate selectableDayPredicate;
public readonly string cancelText;
public readonly string confirmText;
public readonly string helpText;
public readonly DatePickerMode initialCalendarMode;
public readonly string errorFormatText;
public readonly string errorInvalidText;
public readonly string fieldHintText;
public readonly string fieldLabelText;
public override State createState() {
return new _DatePickerDialogState();
}
}
class _DatePickerDialogState : State<_DatePickerDialog> {
DatePickerEntryMode _entryMode;
DateTime _selectedDate;
bool _autoValidate;
public readonly GlobalKey _calendarPickerKey = GlobalKey.key();
public readonly GlobalKey<FormState> _formKey = GlobalKey<FormState>.key();
public override void initState() {
base.initState();
_entryMode = widget.initialEntryMode;
_selectedDate = widget.initialDate;
_autoValidate = false;
}
void _handleOk() {
if (_entryMode == DatePickerEntryMode.input) {
FormState form = _formKey.currentState;
if (!form.validate()) {
setState(() => _autoValidate = true);
return;
}
form.save();
}
Navigator.pop(context, _selectedDate);
}
void _handleCancel() {
Navigator.pop<object>(context);
}
void _handelEntryModeToggle() {
setState(() => {
switch (_entryMode) {
case DatePickerEntryMode.calendar:
_autoValidate = false;
_entryMode = DatePickerEntryMode.input;
break;
case DatePickerEntryMode.input:
_formKey.currentState.save();
_entryMode = DatePickerEntryMode.calendar;
break;
}
});
}
void _handleDateChanged(DateTime date) {
setState(() => _selectedDate = date);
}
Size _dialogSize(BuildContext context) {
Orientation? orientation = MediaQuery.of(context).orientation;
switch (_entryMode) {
case DatePickerEntryMode.calendar:
switch (orientation) {
case Orientation.portrait:
return DatePickerDialogUtils._calendarPortraitDialogSize;
case Orientation.landscape:
return DatePickerDialogUtils._calendarLandscapeDialogSize;
}
break;
case DatePickerEntryMode.input:
switch (orientation) {
case Orientation.portrait:
return DatePickerDialogUtils._inputPortraitDialogSize;
case Orientation.landscape:
return DatePickerDialogUtils._inputLandscapeDialogSize;
}
break;
}
return null;
}
public override Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
ColorScheme colorScheme = theme.colorScheme;
MaterialLocalizations localizations = MaterialLocalizations.of(context);
Orientation? orientation = MediaQuery.of(context).orientation;
TextTheme textTheme = theme.textTheme;
float textScaleFactor = Mathf.Min(MediaQuery.of(context).textScaleFactor, 1.3f);
string dateText = _selectedDate != null
? localizations.formatMediumDate(_selectedDate)
: "Date";
Color dateColor = colorScheme.brightness == Brightness.light
? colorScheme.onPrimary
: colorScheme.onSurface;
TextStyle dateStyle = orientation == Orientation.landscape
? textTheme.headline5?.copyWith(color: dateColor)
: textTheme.headline4?.copyWith(color: dateColor);
Widget actions = new ButtonBar(
buttonTextTheme: ButtonTextTheme.primary,
layoutBehavior: ButtonBarLayoutBehavior.constrained,
children: new List<Widget> {
new FlatButton(
child: new Text(widget.cancelText ?? localizations.cancelButtonLabel),
onPressed: _handleCancel
),
new FlatButton(
child: new Text(widget.confirmText ?? localizations.okButtonLabel),
onPressed: _handleOk
),
}
);
Widget picker = null;
IconData entryModeIcon = null;
string entryModeTooltip = null;
switch (_entryMode) {
case DatePickerEntryMode.calendar:
picker = new CalendarDatePicker(
key: _calendarPickerKey,
initialDate: _selectedDate,
firstDate: widget.firstDate,
lastDate: widget.lastDate,
onDateChanged: _handleDateChanged,
selectableDayPredicate: widget.selectableDayPredicate,
initialCalendarMode: widget.initialCalendarMode
);
entryModeIcon = Icons.edit;
entryModeTooltip = "Switch to input";
break;
case DatePickerEntryMode.input:
picker = new Form(
key: _formKey,
autovalidate: _autoValidate,
child: new InputDatePickerFormField(
initialDate: _selectedDate,
firstDate: widget.firstDate,
lastDate: widget.lastDate,
onDateSubmitted: _handleDateChanged,
onDateSaved: _handleDateChanged,
selectableDayPredicate: widget.selectableDayPredicate,
errorFormatText: widget.errorFormatText,
errorInvalidText: widget.errorInvalidText,
fieldHintText: widget.fieldHintText,
fieldLabelText: widget.fieldLabelText,
autofocus: true
)
);
entryModeIcon = Icons.calendar_today;
entryModeTooltip = "Switch to calendar";
break;
}
Widget header = new DatePickerHeader(
helpText: widget.helpText ?? "SELECT DATE",
titleText: dateText,
titleStyle: dateStyle,
orientation: orientation,
isShort: orientation == Orientation.landscape,
icon: entryModeIcon,
iconTooltip: entryModeTooltip,
onIconPressed: _handelEntryModeToggle
);
Size dialogSize = _dialogSize(context) * textScaleFactor;
DialogTheme dialogTheme = Theme.of(context).dialogTheme;
return new Dialog(
child: new AnimatedContainer(
width: dialogSize.width,
height: dialogSize.height,
duration: DatePickerDialogUtils._dialogSizeAnimationDuration,
curve: Curves.easeIn,
child: new MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: textScaleFactor
),
child: new Builder(builder: (BuildContext subContext) => {
switch (orientation) {
case Orientation.portrait:
return new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: new List<Widget> {
header,
new Expanded(child: picker),
actions,
}
);
case Orientation.landscape:
return new Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: new List<Widget> {
header,
new Flexible(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: new List<Widget> {
new Expanded(child: picker),
actions,
}
)
),
}
);
}
return null;
})
)
),
insetPadding: EdgeInsets.symmetric(horizontal: 16.0f, vertical: 24.0f),
shape: dialogTheme.shape ?? new RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4.0f))
),
clipBehavior: Clip.antiAlias
);
}
}
}

160
com.unity.uiwidgets/Runtime/material/pickers/date_picker_head.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.material {
public static class DatePickerHeaderUtils {
public const float _datePickerHeaderLandscapeWidth = 152.0f;
public const float _datePickerHeaderPortraitHeight = 120.0f;
public const float _headerPaddingLandscape = 16.0f;
}
class DatePickerHeader : StatelessWidget {
public DatePickerHeader(
Key key = null,
string helpText = null,
string titleText = null,
TextStyle titleStyle = null,
Orientation? orientation = null,
bool isShort = false,
IconData icon = null,
string iconTooltip = null,
VoidCallback onIconPressed = null
) : base(key: key) {
D.assert(helpText != null);
D.assert(orientation != null);
this.helpText = helpText;
this.titleText = titleText;
this.titleStyle = titleStyle;
this.orientation = orientation.Value;
this.isShort = isShort;
this.icon = icon;
this.iconTooltip = iconTooltip;
this.onIconPressed = onIconPressed;
}
public readonly string helpText;
public readonly string titleText;
public readonly TextStyle titleStyle;
public readonly Orientation orientation;
public readonly bool isShort;
public readonly IconData icon;
public readonly string iconTooltip;
public readonly VoidCallback onIconPressed;
public override Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
ColorScheme colorScheme = theme.colorScheme;
TextTheme textTheme = theme.textTheme;
bool isDark = colorScheme.brightness == Brightness.dark;
Color primarySurfaceColor = isDark ? colorScheme.surface : colorScheme.primary;
Color onPrimarySurfaceColor = isDark ? colorScheme.onSurface : colorScheme.onPrimary;
TextStyle helpStyle = textTheme.overline?.copyWith(
color: onPrimarySurfaceColor
);
Text help = new Text(
helpText,
style: helpStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis
);
Text title = new Text(
titleText,
style: titleStyle,
maxLines: (isShort || orientation == Orientation.portrait) ? 1 : 2,
overflow: TextOverflow.ellipsis
);
IconButton icon = new IconButton(
icon: new Icon(this.icon),
color: onPrimarySurfaceColor,
tooltip: iconTooltip,
onPressed: onIconPressed
);
switch (orientation) {
case Orientation.portrait:
return new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget> {
new Container(
height: DatePickerHeaderUtils._datePickerHeaderPortraitHeight,
color: primarySurfaceColor,
//FixMe: uncomment this after EdgeInsetsGeometry is ready for use
/*padding: EdgeInsetsDirectional.only(
start: 24f,
end: 12f
),*/
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget> {
new SizedBox(height: 16f),
new Flexible(child: help),
new SizedBox(height: 38),
new Row(
children: new List<Widget> {
new Expanded(child: title),
icon,
}
),
}
)
)
}
);
case Orientation.landscape:
return new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget> {
new Container(
width: DatePickerHeaderUtils._datePickerHeaderLandscapeWidth,
color: primarySurfaceColor,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget> {
new SizedBox(height: 16),
new Padding(
padding: EdgeInsets.symmetric(
horizontal: DatePickerHeaderUtils._headerPaddingLandscape
),
child: help
),
new SizedBox(height: isShort ? 16 : 56),
new Padding(
padding: EdgeInsets.symmetric(
horizontal: DatePickerHeaderUtils._headerPaddingLandscape
),
child: title
),
new Spacer(),
new Padding(
padding: EdgeInsets.symmetric(
horizontal: 4
),
child: icon
),
}
)
),
}
);
}
return null;
}
}
}

238
com.unity.uiwidgets/Runtime/material/pickers/input_date_picker.cs


using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UIWidgets.Runtime.material;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.service;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.material {
public static class InputDatePickerUtils {
public const float _inputPortraitHeight = 98.0f;
public const float _inputLandscapeHeight = 108.0f;
public static bool isBefore(this DateTime t1, DateTime t2) {
return t1.CompareTo(t2) < 0;
}
public static bool isAfter(this DateTime t1, DateTime t2) {
return t1.CompareTo(t2) > 0;
}
}
class InputDatePickerFormField : StatefulWidget {
public InputDatePickerFormField(
Key key = null,
DateTime? initialDate = null,
DateTime? firstDate = null,
DateTime? lastDate = null,
ValueChanged<DateTime> onDateSubmitted = null,
ValueChanged<DateTime> onDateSaved = null,
SelectableDayPredicate selectableDayPredicate = null,
string errorFormatText = null,
string errorInvalidText = null,
string fieldHintText = null,
string fieldLabelText = null,
bool autofocus = false
) : base(key: key) {
D.assert(firstDate != null);
D.assert(lastDate != null);
initialDate = initialDate != null ? utils.dateOnly(initialDate) : null;
firstDate = utils.dateOnly(firstDate);
lastDate = utils.dateOnly(lastDate);
D.assert(
!lastDate.Value.isBefore(firstDate.Value),
() => $"lastDate {lastDate} must be on or after firstDate {firstDate}."
);
D.assert(
initialDate == null || !initialDate.Value.isBefore(firstDate.Value),
() => $"initialDate {initialDate} must be on or after firstDate {firstDate}."
);
D.assert(
initialDate == null || !initialDate.Value.isAfter(lastDate.Value),
() => $"initialDate {initialDate} must be on or before lastDate {lastDate}."
);
D.assert(
selectableDayPredicate == null || initialDate == null || selectableDayPredicate(initialDate),
() => $"Provided initialDate {initialDate} must satisfy provided selectableDayPredicate."
);
this.initialDate = initialDate;
this.firstDate = firstDate;
this.lastDate = lastDate;
this.onDateSubmitted = onDateSubmitted;
this.onDateSaved = onDateSaved;
this.selectableDayPredicate = selectableDayPredicate;
this.errorFormatText = errorFormatText;
this.errorInvalidText = errorInvalidText;
this.fieldHintText = fieldHintText;
this.fieldLabelText = fieldLabelText;
this.autofocus = autofocus;
}
public readonly DateTime? initialDate;
public readonly DateTime firstDate;
public readonly DateTime lastDate;
public readonly ValueChanged<DateTime> onDateSubmitted;
public readonly ValueChanged<DateTime> onDateSaved;
public readonly SelectableDayPredicate selectableDayPredicate;
public readonly string errorFormatText;
public readonly string errorInvalidText;
public readonly string fieldHintText;
public readonly string fieldLabelText;
public readonly bool autofocus;
public override State createState() {
return new _InputDatePickerFormFieldState();
}
}
class _InputDatePickerFormFieldState : State<InputDatePickerFormField> {
public readonly TextEditingController _controller = new TextEditingController();
DateTime? _selectedDate;
string _inputText;
bool _autoSelected = false;
public override void initState() {
base.initState();
_selectedDate = widget.initialDate;
}
public override void dispose() {
_controller.dispose();
base.dispose();
}
public override void didChangeDependencies() {
base.didChangeDependencies();
if (_selectedDate != null) {
MaterialLocalizations localizations = MaterialLocalizations.of(context);
_inputText = localizations.formatCompactDate(_selectedDate.Value);
TextEditingValue textEditingValue = _controller.value.copyWith(text: _inputText);
if (widget.autofocus && !_autoSelected) {
textEditingValue = textEditingValue.copyWith(selection: new TextSelection(
baseOffset: 0,
extentOffset: _inputText.Length
));
_autoSelected = true;
}
_controller.value = textEditingValue;
}
}
DateTime? _parseDate(string text) {
MaterialLocalizations localizations = MaterialLocalizations.of(context);
return localizations.parseCompactDate(text);
}
bool _isValidAcceptableDate(DateTime? date) {
return
date != null &&
!date.Value.isBefore(widget.firstDate) &&
!date.Value.isAfter(widget.lastDate) &&
(widget.selectableDayPredicate == null || widget.selectableDayPredicate(date));
}
string _validateDate(string text) {
DateTime? date = _parseDate(text);
if (date == null) {
return widget.errorFormatText ?? "Invalid format.";
}
else if (!_isValidAcceptableDate(date)) {
return widget.errorInvalidText ?? "Out of range.";
}
return null;
}
void _handleSaved(string text) {
if (widget.onDateSaved != null) {
DateTime? date = _parseDate(text);
if (_isValidAcceptableDate(date)) {
_selectedDate = date;
_inputText = text;
widget.onDateSaved(date.Value);
}
}
}
void _handleSubmitted(string text) {
if (widget.onDateSubmitted != null) {
DateTime? date = _parseDate(text);
if (_isValidAcceptableDate(date)) {
_selectedDate = date;
_inputText = text;
widget.onDateSubmitted(date.Value);
}
}
}
public override Widget build(BuildContext context) {
return new OrientationBuilder(builder: (BuildContext subContext, Orientation orientation) => {
return new Container(
padding: EdgeInsets.symmetric(horizontal: 24f),
height: orientation == Orientation.portrait
? InputDatePickerUtils._inputPortraitHeight
: InputDatePickerUtils._inputLandscapeHeight,
child: new Column(
children: new List<Widget> {
new Spacer(),
new TextFormField(
decoration: new InputDecoration(
border: new UnderlineInputBorder(),
filled: true,
hintText: widget.fieldHintText ?? "mm/dd/yyyy",
labelText: widget.fieldLabelText ?? "Enter Date"
),
validator: _validateDate,
inputFormatters: new List<TextInputFormatter> {
new _DateTextInputFormatter("/")
},
keyboardType: TextInputType.datetime,
onSaved: _handleSaved,
onFieldSubmitted: _handleSubmitted,
autofocus: widget.autofocus,
controller: _controller
),
new Spacer(),
}
)
);
});
}
}
class _DateTextInputFormatter : TextInputFormatter {
public _DateTextInputFormatter(string separator) {
this.separator = separator;
}
public readonly string separator;
public readonly WhitelistingTextInputFormatter _filterFormatter =
// Only allow digits and separators (slash, dot, comma, hyphen, space).
new WhitelistingTextInputFormatter(new Regex(@"[\d\/\.,-\s]+"));
public override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
TextEditingValue filteredValue = _filterFormatter.formatEditUpdate(oldValue, newValue);
return filteredValue.copyWith(
text: Regex.Replace(filteredValue.text, @"[\D]", separator)
);
}
}
}
正在加载...
取消
保存