浏览代码

Merge pull request #341 from UnityTech/dev

Dev
/main
GitHub 5 年前
当前提交
e27405d3
共有 22 个文件被更改,包括 3272 次插入38 次删除
  1. 11
      Runtime/cupertino/app.cs
  2. 2
      Runtime/cupertino/localization.cs
  3. 9
      Runtime/material/app.cs
  4. 56
      Runtime/painting/matrix_utils.cs
  5. 15
      Runtime/service/keyboard.cs
  6. 7
      Runtime/service/text_formatter.cs
  7. 6
      Runtime/service/text_input.cs
  8. 9
      Runtime/ui/renderer/compositeCanvas/flow/raster_cache.cs
  9. 8
      Runtime/widgets/editable_text.cs
  10. 11
      Runtime/widgets/scroll_metrics.cs
  11. 5
      Runtime/widgets/scroll_physics.cs
  12. 16
      Samples/UIWidgetsGallery/gallery/demos.cs
  13. 1001
      Runtime/cupertino/date_picker.cs
  14. 11
      Runtime/cupertino/date_picker.cs.meta
  15. 262
      Runtime/cupertino/picker.cs
  16. 11
      Runtime/cupertino/picker.cs.meta
  17. 751
      Runtime/rendering/list_wheel_viewport.cs
  18. 11
      Runtime/rendering/list_wheel_viewport.cs.meta
  19. 803
      Runtime/widgets/list_wheel_scroll_view.cs
  20. 11
      Runtime/widgets/list_wheel_scroll_view.cs.meta
  21. 283
      Samples/UIWidgetsGallery/demo/cupertino/cupertino_picker_demo.cs
  22. 11
      Samples/UIWidgetsGallery/demo/cupertino/cupertino_picker_demo.cs.meta

11
Runtime/cupertino/app.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;

}
}
// Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
// if (widget.localizationsDelegates != null)
// yield* widget.localizationsDelegates;
// yield DefaultCupertinoLocalizations.delegate;
// }
List<LocalizationsDelegate<CupertinoLocalizations>> _delegates =
new List<LocalizationsDelegate<CupertinoLocalizations>>();
var _delegates = new List<LocalizationsDelegate>();
_delegates.Add(DefaultMaterialLocalizations.del);
return new List<LocalizationsDelegate>(_delegates);
}
}

2
Runtime/cupertino/localization.cs


public override string datePickerMediumDate(DateTime date) {
var day = _shortWeekdays[((int) date.DayOfWeek + 6) % 7];
var month = _shortMonths[date.Month - 1];
return $"{day}, {month} {date.Day.ToString().PadRight(2)} ";
return $"{day} {month} {date.Day.ToString().PadRight(2)} ";
}
public override DatePickerDateOrder datePickerDateOrder {

9
Runtime/material/app.cs


using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.cupertino;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.material {

List<LocalizationsDelegate> _localizationsDelegates {
get {
List<LocalizationsDelegate<MaterialLocalizations>> _delegates =
new List<LocalizationsDelegate<MaterialLocalizations>>();
var _delegates = new List<LocalizationsDelegate>();
_delegates.Add(DefaultCupertinoLocalizations.del);
_delegates.Add(DefaultMaterialLocalizations.del);
return new List<LocalizationsDelegate>(_delegates);
}

56
Runtime/painting/matrix_utils.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.painting {
public static class MatrixUtils {

return result;
}
public static Vector3 perspectiveTransform(this Matrix4x4 m4, Vector3 arg) {
List<float> argStorage = new List<float> {arg[0], arg[1], arg[2]};
float x_ = (m4[0] * argStorage[0]) +
(m4[4] * argStorage[1]) +
(m4[8] * argStorage[2]) +
m4[12];
float y_ = (m4[1] * argStorage[0]) +
(m4[5] * argStorage[1]) +
(m4[9] * argStorage[2]) +
m4[13];
float z_ = (m4[2] * argStorage[0]) +
(m4[6] * argStorage[1]) +
(m4[10] * argStorage[2]) +
m4[14];
float w_ = 1.0f /
((m4[3] * argStorage[0]) +
(m4[7] * argStorage[1]) +
(m4[11] * argStorage[2]) +
m4[15]);
argStorage[0] = x_ * w_;
argStorage[1] = y_ * w_;
argStorage[2] = z_ * w_;
return arg;
}
public static Offset transformPoint(Matrix4x4 transform, Offset point) {
Vector3 position3 = new Vector3(point.dx, point.dy, 0.0f);
Vector3 transformed3 = transform.perspectiveTransform(position3);
return new Offset(transformed3.x, transformed3.y);
}
public static Rect transformRect(Matrix4x4 transform, Rect rect) {
Offset point1 = transformPoint(transform, rect.topLeft);
Offset point2 = transformPoint(transform, rect.topRight);
Offset point3 = transformPoint(transform, rect.bottomLeft);
Offset point4 = transformPoint(transform, rect.bottomRight);
return Rect.fromLTRB(
_min4(point1.dx, point2.dx, point3.dx, point4.dx),
_min4(point1.dy, point2.dy, point3.dy, point4.dy),
_max4(point1.dx, point2.dx, point3.dx, point4.dx),
_max4(point1.dy, point2.dy, point3.dy, point4.dy)
);
}
static float _min4(float a, float b, float c, float d) {
return Mathf.Min(a, Mathf.Min(b, Mathf.Min(c, d)));
}
static float _max4(float a, float b, float c, float d) {
return Mathf.Max(a, Mathf.Max(b, Mathf.Max(c, d)));
}
public static Matrix4x4 toMatrix4x4(this Matrix3 matrix3) {
var matrix = Matrix4x4.identity;

object defaultValue = null,
DiagnosticLevel level = DiagnosticLevel.info
) : base(name, value, showName: showName, defaultValue: defaultValue ?? Diagnostics.kNoDefaultValue,
level: level) {
}
level: level) { }
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
if (parentConfiguration != null && !parentConfiguration.lineBreakProperties) {

15
Runtime/service/keyboard.cs


return true;
}
bool isIMEInput = false;
public void OnGUI() {
if (TouchScreenKeyboard.isSupported) {
return;

if (_validateCharacter(ch)) {
this._value = this._value.insert(new string(ch, 1));
}
} else if (!string.IsNullOrEmpty(Input.compositionString)) {
}
else if (!string.IsNullOrEmpty(Input.compositionString)) {
this.isIMEInput = true;
this._value = this._value.compose(Input.compositionString);
}

if (this._value != oldValue) {
Window.instance.run(() => { TextInput._updateEditingState(this._client, this._value); });
if (this.isIMEInput) {
var isIMEInput = this.isIMEInput;
Window.instance.run(() => { TextInput._updateEditingState(this._client, this._value, isIMEInput); });
this.isIMEInput = false;
}
else {
Window.instance.run(() => { TextInput._updateEditingState(this._client, this._value); });
}
}
}

7
Runtime/service/text_formatter.cs


public override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
if (this.maxLength != null && this.maxLength > 0 && newValue.text.Length > this.maxLength) {
if (Input.compositionString.Length > 0) {
return newValue;
}
TextSelection newSelection = newValue.selection.copyWith(
baseOffset: Mathf.Min(newValue.selection.start, this.maxLength.Value),
extentOffset: Mathf.Min(newValue.selection.end, this.maxLength.Value)

composing: TextRange.empty
);
}
}
}
static class Util {
internal static TextEditingValue _selectionAwareTextManipulation(TextEditingValue value,

6
Runtime/service/text_input.cs


}
public interface TextInputClient {
void updateEditingValue(TextEditingValue value);
void updateEditingValue(TextEditingValue value, bool isIMEInput);
void performAction(TextInputAction action);

}
}
internal static void _updateEditingState(int client, TextEditingValue value) {
internal static void _updateEditingState(int client, TextEditingValue value, bool isIMEInput = false) {
if (_currentConnection == null) {
return;
}

}
_currentConnection._client.updateEditingValue(value);
_currentConnection._client.updateEditingValue(value, isIMEInput);
}
internal static void _performAction(int client, TextInputAction action) {

9
Runtime/ui/renderer/compositeCanvas/flow/raster_cache.cs


D.assert(() => {
var textureWidth = Mathf.CeilToInt(bounds.width * this.devicePixelRatio);
var textureHeight = Mathf.CeilToInt(bounds.height * this.devicePixelRatio);
D.assert(this.image.width == textureWidth);
D.assert(this.image.height == textureHeight);
//it is possible that there is a minor difference between the bound size and the image size (1 pixel at
//most) due to the roundOut operation when calculating the bounds if the elements in the canvas transform
//is not all integer
D.assert(Mathf.Abs(this.image.width - textureWidth) <= 1);
D.assert(Mathf.Abs(this.image.height - textureHeight) <= 1);
return true;
});

8
Runtime/widgets/editable_text.cs


TextEditingValue _lastKnownRemoteTextEditingValue;
public void updateEditingValue(TextEditingValue value) {
public void updateEditingValue(TextEditingValue value, bool isIMEInput) {
if (value.text != this._value.text) {
this._hideSelectionOverlayIfNeeded();
this._showCaretOnScreen();

}
this._lastKnownRemoteTextEditingValue = value;
this._formatAndSetValue(value);
this._formatAndSetValue(value, isIMEInput);
this._stopCursorTimer(resetCharTicks: false);
this._startCursorTimer();

return Promise<bool>.Resolved(false);
}
void _formatAndSetValue(TextEditingValue value) {
var textChanged = this._value?.text != value?.text;
void _formatAndSetValue(TextEditingValue value, bool isIMEInput = false) {
var textChanged = this._value?.text != value?.text || isIMEInput;
if (textChanged && this.widget.inputFormatters != null && this.widget.inputFormatters.isNotEmpty()) {
foreach (var formatter in this.widget.inputFormatters) {
value = formatter.formatEditUpdate(this._value, value);

11
Runtime/widgets/scroll_metrics.cs


);
}
if (it is IFixedExtentMetrics) {
return new FixedExtentMetrics(
minScrollExtent: minScrollExtent ?? it.minScrollExtent,
maxScrollExtent: maxScrollExtent ?? it.maxScrollExtent,
pixels: pixels ?? it.pixels,
viewportDimension: viewportDimension ?? it.viewportDimension,
axisDirection: axisDirection ?? it.axisDirection,
itemIndex: ((IFixedExtentMetrics) it).itemIndex
);
}
return new FixedScrollMetrics(
minScrollExtent: minScrollExtent ?? it.minScrollExtent,
maxScrollExtent: maxScrollExtent ?? it.maxScrollExtent,

5
Runtime/widgets/scroll_physics.cs


}
}
// todo: Handle the case of the device pixel ratio changing. use 1 as devicePixelRatio for now.
velocity: 1.0f / (0.050f * 1),
distance: 1.0f / 1
velocity: 1.0f / (0.050f * Window.instance.devicePixelRatio),
distance: 1.0f / Window.instance.devicePixelRatio
);
public virtual Tolerance tolerance {

16
Samples/UIWidgetsGallery/gallery/demos.cs


documentationUrl: "https://docs.flutter.io/flutter/cupertino/CupertinoTabScaffold-class.html",
buildRoute: (BuildContext context) => new CupertinoNavigationDemo()
),
// new GalleryDemo(
// title: "Pickers",
// icon: GalleryIcons.@event,
// category: GalleryDemoCategory._kCupertinoComponents,
// routeName: CupertinoPickerDemo.routeName,
// documentationUrl: "https://docs.flutter.io/flutter/cupertino/CupertinoPicker-class.html",
// buildRoute: (BuildContext context) => CupertinoPickerDemo()
// ),
new GalleryDemo(
title: "Pickers",
icon: GalleryIcons.@event,
category: _kCupertinoComponents,
routeName: CupertinoPickerDemo.routeName,
documentationUrl: "https://docs.flutter.io/flutter/cupertino/CupertinoPicker-class.html",
buildRoute: (BuildContext context) => new CupertinoPickerDemo()
),
// new GalleryDemo(
// title: "Pull to refresh",
// icon: GalleryIcons.cupertino_pull_to_refresh,

1001
Runtime/cupertino/date_picker.cs
文件差异内容过多而无法显示
查看文件

11
Runtime/cupertino/date_picker.cs.meta


fileFormatVersion: 2
guid: b8c8750ecccc84021b9aafc99e9ee64b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

262
Runtime/cupertino/picker.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;
namespace Unity.UIWidgets.cupertino {
static class CupertinoPickerUtils {
public static Color _kHighlighterBorder = new Color(0xFF7F7F7F);
public static Color _kDefaultBackground = new Color(0xFFD2D4DB);
public const float _kDefaultDiameterRatio = 1.35f;
public const float _kDefaultPerspective = 0.004f;
public const float _kForegroundScreenOpacityFraction = 0.7f;
}
public class CupertinoPicker : StatefulWidget {
public CupertinoPicker(
float itemExtent,
ValueChanged<int> onSelectedItemChanged,
List<Widget> children = null,
Key key = null,
float diameterRatio = CupertinoPickerUtils._kDefaultDiameterRatio,
Color backgroundColor = null,
float offAxisFraction = 0.0f,
bool useMagnifier = false,
float magnification = 1.0f,
FixedExtentScrollController scrollController = null,
bool looping = false,
ListWheelChildDelegate childDelegate = null
) : base(key: key) {
D.assert(children != null || childDelegate != null);
D.assert(diameterRatio > 0.0, () => RenderListWheelViewport.diameterRatioZeroMessage);
D.assert(magnification > 0);
D.assert(itemExtent > 0);
this.childDelegate = childDelegate ?? (looping
? (ListWheelChildDelegate) new ListWheelChildLoopingListDelegate(
children: children)
: (ListWheelChildDelegate) new ListWheelChildListDelegate(children: children));
this.itemExtent = itemExtent;
this.onSelectedItemChanged = onSelectedItemChanged;
this.diameterRatio = diameterRatio;
this.backgroundColor = backgroundColor ?? CupertinoPickerUtils._kDefaultBackground;
this.offAxisFraction = offAxisFraction;
this.useMagnifier = useMagnifier;
this.magnification = magnification;
this.scrollController = scrollController;
}
public static CupertinoPicker builder(
float itemExtent,
ValueChanged<int> onSelectedItemChanged,
IndexedWidgetBuilder itemBuilder,
Key key = null,
float diameterRatio = CupertinoPickerUtils._kDefaultDiameterRatio,
Color backgroundColor = null,
float offAxisFraction = 0.0f,
bool useMagnifier = false,
float magnification = 1.0f,
FixedExtentScrollController scrollController = null,
int? childCount = null
) {
D.assert(itemBuilder != null);
D.assert(diameterRatio > 0.0f, () => RenderListWheelViewport.diameterRatioZeroMessage);
D.assert(magnification > 0);
D.assert(itemExtent > 0);
return new CupertinoPicker(
itemExtent: itemExtent,
onSelectedItemChanged: onSelectedItemChanged,
key: key,
diameterRatio: diameterRatio,
backgroundColor: backgroundColor,
offAxisFraction: offAxisFraction,
useMagnifier: useMagnifier,
magnification: magnification,
scrollController: scrollController,
childDelegate: new ListWheelChildBuilderDelegate(builder: itemBuilder, childCount: childCount)
);
}
public readonly float diameterRatio;
public readonly Color backgroundColor;
public readonly float offAxisFraction;
public readonly bool useMagnifier;
public readonly float magnification;
public readonly FixedExtentScrollController scrollController;
public readonly float itemExtent;
public readonly ValueChanged<int> onSelectedItemChanged;
public readonly ListWheelChildDelegate childDelegate;
public override State createState() {
return new _CupertinoPickerState();
}
}
class _CupertinoPickerState : State<CupertinoPicker> {
FixedExtentScrollController _controller;
public override void initState() {
base.initState();
if (this.widget.scrollController == null) {
this._controller = new FixedExtentScrollController();
}
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
if (this.widget.scrollController != null && ((CupertinoPicker) oldWidget).scrollController == null) {
this._controller = null;
}
else if (this.widget.scrollController == null && ((CupertinoPicker) oldWidget).scrollController != null) {
D.assert(this._controller == null);
this._controller = new FixedExtentScrollController();
}
base.didUpdateWidget(oldWidget);
}
public override void dispose() {
this._controller?.dispose();
base.dispose();
}
void _handleSelectedItemChanged(int index) {
if (this.widget.onSelectedItemChanged != null) {
this.widget.onSelectedItemChanged(index);
}
}
Widget _buildGradientScreen() {
if (this.widget.backgroundColor != null && this.widget.backgroundColor.alpha < 255) {
return new Container();
}
Color widgetBackgroundColor = this.widget.backgroundColor ?? new Color(0xFFFFFFFF);
return Positioned.fill(
child: new IgnorePointer(
child: new Container(
decoration: new BoxDecoration(
gradient: new LinearGradient(
colors: new List<Color> {
widgetBackgroundColor,
widgetBackgroundColor.withAlpha(0xF2),
widgetBackgroundColor.withAlpha(0xDD),
widgetBackgroundColor.withAlpha(0),
widgetBackgroundColor.withAlpha(0),
widgetBackgroundColor.withAlpha(0xDD),
widgetBackgroundColor.withAlpha(0xF2),
widgetBackgroundColor,
},
stops: new List<float> {
0.0f, 0.05f, 0.09f, 0.22f, 0.78f, 0.91f, 0.95f, 1.0f
},
begin: Alignment.topCenter,
end: Alignment.bottomCenter
)
)
)
)
);
}
Widget _buildMagnifierScreen() {
Color foreground = this.widget.backgroundColor?.withAlpha(
(int) (this.widget.backgroundColor.alpha * CupertinoPickerUtils._kForegroundScreenOpacityFraction)
);
return new IgnorePointer(
child: new Column(
children: new List<Widget> {
new Expanded(
child: new Container(
color: foreground
)
),
new Container(
decoration: new BoxDecoration(
border: new Border(
top: new BorderSide(width: 0.0f, color: CupertinoPickerUtils._kHighlighterBorder),
bottom: new BorderSide(width: 0.0f, color: CupertinoPickerUtils._kHighlighterBorder)
)
),
constraints: BoxConstraints.expand(
height: this.widget.itemExtent * this.widget.magnification
)
),
new Expanded(
child: new Container(
color: foreground
)
),
}
)
);
}
Widget _buildUnderMagnifierScreen() {
Color foreground = this.widget.backgroundColor?.withAlpha(
(int) (this.widget.backgroundColor.alpha * CupertinoPickerUtils._kForegroundScreenOpacityFraction)
);
return new Column(
children: new List<Widget> {
new Expanded(child: new Container()),
new Container(
color: foreground,
constraints: BoxConstraints.expand(
height: this.widget.itemExtent * this.widget.magnification
)
),
new Expanded(child: new Container())
}
);
}
Widget _addBackgroundToChild(Widget child) {
return new DecoratedBox(
decoration: new BoxDecoration(
color: this.widget.backgroundColor
),
child: child
);
}
public override Widget build(BuildContext context) {
Widget result = new Stack(
children: new List<Widget> {
Positioned.fill(
child: ListWheelScrollView.useDelegate(
controller: this.widget.scrollController ?? this._controller,
physics: new FixedExtentScrollPhysics(),
diameterRatio: this.widget.diameterRatio,
perspective: CupertinoPickerUtils._kDefaultPerspective,
offAxisFraction: this.widget.offAxisFraction,
useMagnifier: this.widget.useMagnifier,
magnification: this.widget.magnification,
itemExtent: this.widget.itemExtent,
onSelectedItemChanged: this._handleSelectedItemChanged,
childDelegate: this.widget.childDelegate
)
),
this._buildGradientScreen(),
this._buildMagnifierScreen()
}
);
if (this.widget.backgroundColor != null && this.widget.backgroundColor.alpha < 255) {
result = new Stack(
children: new List<Widget> {
this._buildUnderMagnifierScreen(), this._addBackgroundToChild(result),
}
);
}
else {
result = this._addBackgroundToChild(result);
}
return result;
}
}
}

11
Runtime/cupertino/picker.cs.meta


fileFormatVersion: 2
guid: e1f9860a10b464e9da0c6258c08fa91e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

751
Runtime/rendering/list_wheel_viewport.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using UnityEngine;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.rendering {
delegate float ___ChildSizingFunction(RenderBox child);
public interface IListWheelChildManager {
int? childCount { get; }
bool childExistsAt(int index);
void createChild(int index, RenderBox after);
void removeChild(RenderBox child);
}
public class ListWheelParentData : ContainerBoxParentData<RenderBox> {
public int index;
}
public class RenderListWheelViewport : ContainerRenderObjectMixinRenderBox<RenderBox, ListWheelParentData>,
RenderAbstractViewport {
public RenderListWheelViewport(
IListWheelChildManager childManager,
ViewportOffset offset,
float itemExtent,
float diameterRatio = defaultDiameterRatio,
float perspective = defaultPerspective,
float offAxisFraction = 0.0f,
bool useMagnifier = false,
float magnification = 1.0f,
bool clipToSize = true,
bool renderChildrenOutsideViewport = false,
List<RenderBox> children = null
) {
D.assert(childManager != null);
D.assert(offset != null);
D.assert(diameterRatio > 0, () => diameterRatioZeroMessage);
D.assert(perspective > 0);
D.assert(perspective <= 0.01f, () => perspectiveTooHighMessage);
D.assert(magnification > 0);
D.assert(itemExtent > 0);
D.assert(
!renderChildrenOutsideViewport || !clipToSize,
() => clipToSizeAndRenderChildrenOutsideViewportConflict
);
this.childManager = childManager;
this._offset = offset;
this._diameterRatio = diameterRatio;
this._perspective = perspective;
this._offAxisFraction = offAxisFraction;
this._useMagnifier = useMagnifier;
this._magnification = magnification;
this._itemExtent = itemExtent;
this._clipToSize = clipToSize;
this._renderChildrenOutsideViewport = renderChildrenOutsideViewport;
this.addAll(children);
}
public const float defaultDiameterRatio = 2.0f;
public const float defaultPerspective = 0.003f;
public const string diameterRatioZeroMessage = "You can't set a diameterRatio " +
"of 0 or of a negative number. It would imply a cylinder of 0 in diameter " +
"in which case nothing will be drawn.";
public const string perspectiveTooHighMessage = "A perspective too high will " +
"be clipped in the z-axis and therefore not renderable. Value must be " +
"between 0 and 0.0f1.";
public const string clipToSizeAndRenderChildrenOutsideViewportConflict =
"Cannot renderChildrenOutsideViewport and clipToSize since children " +
"rendered outside will be clipped anyway.";
public readonly IListWheelChildManager childManager;
public ViewportOffset offset {
get { return this._offset; }
set {
D.assert(value != null);
if (value == this._offset) {
return;
}
if (this.attached) {
this._offset.removeListener(this._hasScrolled);
}
this._offset = value;
if (this.attached) {
this._offset.addListener(this._hasScrolled);
}
this.markNeedsLayout();
}
}
ViewportOffset _offset;
public float diameterRatio {
get { return this._diameterRatio; }
set {
D.assert(
value > 0,
() => diameterRatioZeroMessage
);
this._diameterRatio = value;
this.markNeedsPaint();
}
}
float _diameterRatio;
public float perspective {
get { return this._perspective; }
set {
D.assert(value > 0);
D.assert(
value <= 0.01f,
() => perspectiveTooHighMessage
);
if (value == this._perspective) {
return;
}
this._perspective = value;
this.markNeedsPaint();
}
}
float _perspective;
public float offAxisFraction {
get { return this._offAxisFraction; }
set {
if (value == this._offAxisFraction) {
return;
}
this._offAxisFraction = value;
this.markNeedsPaint();
}
}
float _offAxisFraction = 0.0f;
public bool useMagnifier {
get { return this._useMagnifier; }
set {
if (value == this._useMagnifier) {
return;
}
this._useMagnifier = value;
this.markNeedsPaint();
}
}
bool _useMagnifier = false;
public float magnification {
get { return this._magnification; }
set {
D.assert(value > 0);
if (value == this._magnification) {
return;
}
this._magnification = value;
this.markNeedsPaint();
}
}
float _magnification = 1.0f;
public float itemExtent {
get { return this._itemExtent; }
set {
D.assert(value > 0);
if (value == this._itemExtent) {
return;
}
this._itemExtent = value;
this.markNeedsLayout();
}
}
float _itemExtent;
public bool clipToSize {
get { return this._clipToSize; }
set {
D.assert(
!this.renderChildrenOutsideViewport || !this.clipToSize,
() => clipToSizeAndRenderChildrenOutsideViewportConflict
);
if (value == this._clipToSize) {
return;
}
this._clipToSize = value;
this.markNeedsPaint();
}
}
bool _clipToSize;
public bool renderChildrenOutsideViewport {
get { return this._renderChildrenOutsideViewport; }
set {
D.assert(
!this.renderChildrenOutsideViewport || !this.clipToSize,
() => clipToSizeAndRenderChildrenOutsideViewportConflict
);
if (value == this._renderChildrenOutsideViewport) {
return;
}
this._renderChildrenOutsideViewport = value;
this.markNeedsLayout();
}
}
bool _renderChildrenOutsideViewport;
void _hasScrolled() {
this.markNeedsLayout();
}
public override void setupParentData(RenderObject child) {
if (!(child.parentData is ListWheelParentData)) {
child.parentData = new ListWheelParentData();
}
}
public override void attach(object owner) {
base.attach(owner);
this._offset.addListener(this._hasScrolled);
}
public override void detach() {
this._offset.removeListener(this._hasScrolled);
base.detach();
}
public override bool isRepaintBoundary {
get { return true; }
}
float _viewportExtent {
get {
D.assert(this.hasSize);
return this.size.height;
}
}
float _minEstimatedScrollExtent {
get {
D.assert(this.hasSize);
if (this.childManager.childCount == null) {
return float.NegativeInfinity;
}
return 0.0f;
}
}
float _maxEstimatedScrollExtent {
get {
D.assert(this.hasSize);
if (this.childManager.childCount == null) {
return float.PositiveInfinity;
}
return Mathf.Max(0.0f, ((this.childManager.childCount ?? 0) - 1) * this._itemExtent);
}
}
float _topScrollMarginExtent {
get {
D.assert(this.hasSize);
return -this.size.height / 2.0f + this._itemExtent / 2.0f;
}
}
float _getUntransformedPaintingCoordinateY(float layoutCoordinateY) {
return layoutCoordinateY - this._topScrollMarginExtent - this.offset.pixels;
}
float _maxVisibleRadian {
get {
if (this._diameterRatio < 1.0f) {
return Mathf.PI / 2.0f;
}
return Mathf.Asin(1.0f / this._diameterRatio);
}
}
float _getIntrinsicCrossAxis(___ChildSizingFunction childSize) {
float extent = 0.0f;
RenderBox child = this.firstChild;
while (child != null) {
extent = Mathf.Max(extent, childSize(child));
child = this.childAfter(child);
}
return extent;
}
protected override float computeMinIntrinsicWidth(float height) {
return this._getIntrinsicCrossAxis(
(RenderBox child) => child.getMinIntrinsicWidth(height)
);
}
protected override float computeMaxIntrinsicWidth(float height) {
return this._getIntrinsicCrossAxis(
(RenderBox child) => child.getMaxIntrinsicWidth(height)
);
}
protected override float computeMinIntrinsicHeight(float width) {
if (this.childManager.childCount == null) {
return 0.0f;
}
return (this.childManager.childCount ?? 0) * this._itemExtent;
}
protected internal override float computeMaxIntrinsicHeight(float width) {
if (this.childManager.childCount == null) {
return 0.0f;
}
return (this.childManager.childCount ?? 0) * this._itemExtent;
}
protected override bool sizedByParent {
get { return true; }
}
protected override void performResize() {
this.size = this.constraints.biggest;
}
public int indexOf(RenderBox child) {
D.assert(child != null);
ListWheelParentData childParentData = (ListWheelParentData) child.parentData;
return childParentData.index;
}
public int scrollOffsetToIndex(float scrollOffset) {
return (scrollOffset / this.itemExtent).floor();
}
public float indexToScrollOffset(int index) {
return index * this.itemExtent;
}
void _createChild(int index,
RenderBox after = null
) {
this.invokeLayoutCallback<BoxConstraints>((BoxConstraints constraints) => {
D.assert(this.constraints == this.constraints);
this.childManager.createChild(index, after: after);
});
}
void _destroyChild(RenderBox child) {
this.invokeLayoutCallback<BoxConstraints>((BoxConstraints constraints) => {
D.assert(this.constraints == this.constraints);
this.childManager.removeChild(child);
});
}
void _layoutChild(RenderBox child, BoxConstraints constraints, int index) {
child.layout(constraints, parentUsesSize: true);
ListWheelParentData childParentData = (ListWheelParentData) child.parentData;
float crossPosition = this.size.width / 2.0f - child.size.width / 2.0f;
childParentData.offset = new Offset(crossPosition, this.indexToScrollOffset(index));
}
protected override void performLayout() {
BoxConstraints childConstraints = this.constraints.copyWith(
minHeight: this._itemExtent,
maxHeight: this._itemExtent,
minWidth: 0.0f
);
float visibleHeight = this.size.height;
if (this.renderChildrenOutsideViewport) {
visibleHeight *= 2;
}
float firstVisibleOffset = this.offset.pixels + this._itemExtent / 2 - visibleHeight / 2;
float lastVisibleOffset = firstVisibleOffset + visibleHeight;
int targetFirstIndex = this.scrollOffsetToIndex(firstVisibleOffset);
int targetLastIndex = this.scrollOffsetToIndex(lastVisibleOffset);
if (targetLastIndex * this._itemExtent == lastVisibleOffset) {
targetLastIndex--;
}
while (!this.childManager.childExistsAt(targetFirstIndex) && targetFirstIndex <= targetLastIndex) {
targetFirstIndex++;
}
while (!this.childManager.childExistsAt(targetLastIndex) && targetFirstIndex <= targetLastIndex) {
targetLastIndex--;
}
if (targetFirstIndex > targetLastIndex) {
while (this.firstChild != null) {
this._destroyChild(this.firstChild);
}
return;
}
if (this.childCount > 0 &&
(this.indexOf(this.firstChild) > targetLastIndex || this.indexOf(this.lastChild) < targetFirstIndex)) {
while (this.firstChild != null) {
this._destroyChild(this.firstChild);
}
}
if (this.childCount == 0) {
this._createChild(targetFirstIndex);
this._layoutChild(this.firstChild, childConstraints, targetFirstIndex);
}
int currentFirstIndex = this.indexOf(this.firstChild);
int currentLastIndex = this.indexOf(this.lastChild);
while (currentFirstIndex < targetFirstIndex) {
this._destroyChild(this.firstChild);
currentFirstIndex++;
}
while (currentLastIndex > targetLastIndex) {
this._destroyChild(this.lastChild);
currentLastIndex--;
}
RenderBox child = this.firstChild;
while (child != null) {
child.layout(childConstraints, parentUsesSize: true);
child = this.childAfter(child);
}
while (currentFirstIndex > targetFirstIndex) {
this._createChild(currentFirstIndex - 1);
this._layoutChild(this.firstChild, childConstraints, --currentFirstIndex);
}
while (currentLastIndex < targetLastIndex) {
this._createChild(currentLastIndex + 1, after: this.lastChild);
this._layoutChild(this.lastChild, childConstraints, ++currentLastIndex);
}
this.offset.applyViewportDimension(this._viewportExtent);
float minScrollExtent = this.childManager.childExistsAt(targetFirstIndex - 1)
? this._minEstimatedScrollExtent
: this.indexToScrollOffset(targetFirstIndex);
float maxScrollExtent = this.childManager.childExistsAt(targetLastIndex + 1)
? this._maxEstimatedScrollExtent
: this.indexToScrollOffset(targetLastIndex);
this.offset.applyContentDimensions(minScrollExtent, maxScrollExtent);
}
bool _shouldClipAtCurrentOffset() {
float highestUntransformedPaintY = this._getUntransformedPaintingCoordinateY(0.0f);
return highestUntransformedPaintY < 0.0f
|| this.size.height < highestUntransformedPaintY + this._maxEstimatedScrollExtent + this._itemExtent;
}
public override void paint(PaintingContext context, Offset offset) {
if (this.childCount > 0) {
if (this._clipToSize && this._shouldClipAtCurrentOffset()) {
context.pushClipRect(
this.needsCompositing,
offset,
Offset.zero & this.size, this._paintVisibleChildren
);
}
else {
this._paintVisibleChildren(context, offset);
}
}
}
void _paintVisibleChildren(PaintingContext context, Offset offset) {
RenderBox childToPaint = this.firstChild;
ListWheelParentData childParentData = (ListWheelParentData) childToPaint?.parentData;
while (childParentData != null) {
this._paintTransformedChild(childToPaint, context, offset, childParentData.offset);
childToPaint = this.childAfter(childToPaint);
childParentData = (ListWheelParentData) childToPaint?.parentData;
}
}
void _paintTransformedChild(RenderBox child, PaintingContext context, Offset offset, Offset layoutOffset) {
Offset untransformedPaintingCoordinates = offset + new Offset(
layoutOffset.dx,
this._getUntransformedPaintingCoordinateY(layoutOffset.dy)
);
float fractionalY = (untransformedPaintingCoordinates.dy + this._itemExtent / 2.0f) / this.size.height;
float angle = -(fractionalY - 0.5f) * 2.0f * this._maxVisibleRadian;
if (angle > Mathf.PI / 2.0f || angle < -Mathf.PI / 2.0f) {
return;
}
var radius = this.size.height * this._diameterRatio / 2.0f;
var deltaY = radius * Mathf.Sin(angle);
Matrix3 transform = Matrix3.I();
// Matrix4x4 transform2 = MatrixUtils.createCylindricalProjectionTransform(
// radius: this.size.height * this._diameterRatio / 2.0f,
// angle: angle,
// perspective: this._perspective
// );
// Offset offsetToCenter = new Offset(untransformedPaintingCoordinates.dx, -this._topScrollMarginExtent);
Offset offsetToCenter =
new Offset(untransformedPaintingCoordinates.dx, -deltaY - this._topScrollMarginExtent);
if (!this.useMagnifier) {
this._paintChildCylindrically(context, offset, child, transform, offsetToCenter);
}
else {
this._paintChildWithMagnifier(
context,
offset,
child,
transform,
offsetToCenter,
untransformedPaintingCoordinates
);
}
}
void _paintChildWithMagnifier(
PaintingContext context,
Offset offset,
RenderBox child,
// Matrix4x4 cylindricalTransform,
Matrix3 cylindricalTransform,
Offset offsetToCenter,
Offset untransformedPaintingCoordinates
) {
float magnifierTopLinePosition = this.size.height / 2 - this._itemExtent * this._magnification / 2;
float magnifierBottomLinePosition = this.size.height / 2 + this._itemExtent * this._magnification / 2;
bool isAfterMagnifierTopLine = untransformedPaintingCoordinates.dy
>= magnifierTopLinePosition - this._itemExtent * this._magnification;
bool isBeforeMagnifierBottomLine = untransformedPaintingCoordinates.dy
<= magnifierBottomLinePosition;
if (isAfterMagnifierTopLine && isBeforeMagnifierBottomLine) {
Rect centerRect = Rect.fromLTWH(
0.0f,
magnifierTopLinePosition, this.size.width, this._itemExtent * this._magnification);
Rect topHalfRect = Rect.fromLTWH(
0.0f,
0.0f, this.size.width,
magnifierTopLinePosition);
Rect bottomHalfRect = Rect.fromLTWH(
0.0f,
magnifierBottomLinePosition, this.size.width,
magnifierTopLinePosition);
context.pushClipRect(
false,
offset,
centerRect,
(PaintingContext context1, Offset offset1) => {
context1.pushTransform(
false,
offset1,
cylindricalTransform,
// this._centerOriginTransform(cylindricalTransform),
(PaintingContext context2, Offset offset2) => {
context2.paintChild(
child,
offset2 + untransformedPaintingCoordinates);
});
});
context.pushClipRect(
false,
offset,
untransformedPaintingCoordinates.dy <= magnifierTopLinePosition
? topHalfRect
: bottomHalfRect,
(PaintingContext context1, Offset offset1) => {
this._paintChildCylindrically(
context1,
offset1,
child,
cylindricalTransform,
offsetToCenter
);
}
);
}
else {
this._paintChildCylindrically(
context,
offset,
child,
cylindricalTransform,
offsetToCenter
);
}
}
void _paintChildCylindrically(
PaintingContext context,
Offset offset,
RenderBox child,
// Matrix4x4 cylindricalTransform,
Matrix3 cylindricalTransform,
Offset offsetToCenter
) {
context.pushTransform(
false,
offset,
cylindricalTransform,
// this._centerOriginTransform(cylindricalTransform),
(PaintingContext _context, Offset _offset) => { _context.paintChild(child, _offset + offsetToCenter); }
);
}
Matrix4x4 _magnifyTransform() {
Matrix4x4 magnify = Matrix4x4.identity;
magnify.translate(this.size.width * (-this._offAxisFraction + 0.5f), this.size.height / 2f);
magnify.scale(this._magnification, this._magnification, this._magnification);
magnify.translate(-this.size.width * (-this._offAxisFraction + 0.5f), -this.size.height / 2f);
return magnify;
}
Matrix3 _centerOriginTransform(Matrix3 originalMatrix) {
Matrix3 result = Matrix3.I();
Offset centerOriginTranslation = Alignment.center.alongSize(this.size);
result.setTranslate(centerOriginTranslation.dx * (-this._offAxisFraction * 2 + 1),
centerOriginTranslation.dy);
result.multiply(originalMatrix);
result.setTranslate(-centerOriginTranslation.dx * (-this._offAxisFraction * 2 + 1),
-centerOriginTranslation.dy);
return result;
}
Matrix4x4 _centerOriginTransform(Matrix4x4 originalMatrix) {
Matrix4x4 result = Matrix4x4.identity;
Offset centerOriginTranslation = Alignment.center.alongSize(this.size);
result.translate(centerOriginTranslation.dx * (-this._offAxisFraction * 2 + 1),
centerOriginTranslation.dy);
result.multiply(originalMatrix);
result.translate(-centerOriginTranslation.dx * (-this._offAxisFraction * 2 + 1),
-centerOriginTranslation.dy);
return result;
}
public void applyPaintTransform(RenderBox child, Matrix4x4 transform) {
ListWheelParentData parentData = (ListWheelParentData) child?.parentData;
transform.translate(0.0f, this._getUntransformedPaintingCoordinateY(parentData.offset.dy));
}
public override Rect describeApproximatePaintClip(RenderObject child) {
if (child != null && this._shouldClipAtCurrentOffset()) {
return Offset.zero & this.size;
}
return null;
}
protected override bool hitTestChildren(HitTestResult result, Offset position = null
) {
return false;
}
public RevealedOffset getOffsetToReveal(RenderObject target, float alignment,
Rect rect = null
) {
rect = rect ?? target.paintBounds;
RenderObject child = target;
while (child.parent != this) {
child = (RenderObject) child.parent;
}
ListWheelParentData parentData = (ListWheelParentData) child.parentData;
float targetOffset = parentData.offset.dy;
Matrix4x4 transform = target.getTransformTo(this).toMatrix4x4();
Rect bounds = MatrixUtils.transformRect(transform, rect);
Rect targetRect = bounds.translate(0.0f, (this.size.height - this.itemExtent) / 2);
return new RevealedOffset(offset: targetOffset, rect: targetRect);
}
public new RenderObject parent {
get { return (RenderObject) base.parent; }
}
public new void showOnScreen(
RenderObject descendant = null,
Rect rect = null,
TimeSpan? duration = null,
Curve curve = null
) {
duration = duration ?? TimeSpan.Zero;
curve = curve ?? Curves.ease;
if (descendant != null) {
RevealedOffset revealedOffset = this.getOffsetToReveal(descendant, 0.5f, rect: rect);
if (duration == TimeSpan.Zero) {
this.offset.jumpTo(revealedOffset.offset);
}
else {
this.offset.animateTo(revealedOffset.offset, duration: (TimeSpan) duration, curve: curve);
}
rect = revealedOffset.rect;
}
base.showOnScreen(
rect: rect,
duration: duration,
curve: curve
);
}
}
}

11
Runtime/rendering/list_wheel_viewport.cs.meta


fileFormatVersion: 2
guid: 19267e4bc9a214b5eacd791e58a63f11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

803
Runtime/widgets/list_wheel_scroll_view.cs


using System;
using System.Collections.Generic;
using RSG;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.physics;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace Unity.UIWidgets.widgets {
public interface ListWheelChildDelegate {
Widget build(BuildContext context, int index);
int? estimatedChildCount { get; }
int trueIndexOf(int index);
bool shouldRebuild(ListWheelChildDelegate oldDelegate);
}
public class ListWheelChildListDelegate : ListWheelChildDelegate {
public ListWheelChildListDelegate(
List<Widget> children
) {
D.assert(children != null);
this.children = children;
}
public readonly List<Widget> children;
public int? estimatedChildCount {
get { return this.children.Count; }
}
public Widget build(BuildContext context, int index) {
if (index < 0 || index >= this.children.Count) {
return null;
}
return new Container(child: this.children[index]);
}
public int trueIndexOf(int index) {
return index;
}
public bool shouldRebuild(ListWheelChildDelegate oldDelegate) {
return this.children != ((ListWheelChildListDelegate) oldDelegate).children;
}
}
public class ListWheelChildLoopingListDelegate : ListWheelChildDelegate {
public ListWheelChildLoopingListDelegate(
List<Widget> children
) {
D.assert(children != null);
this.children = children;
}
public readonly List<Widget> children;
public int? estimatedChildCount {
get { return null; }
}
public int trueIndexOf(int index) {
while (index < 0) {
index += this.children.Count;
}
return index % this.children.Count;
}
public Widget build(BuildContext context, int index) {
if (this.children.isEmpty()) {
return null;
}
while (index < 0) {
index += this.children.Count;
}
return new Container(child: this.children[index % this.children.Count]);
}
public bool shouldRebuild(ListWheelChildDelegate oldDelegate) {
return this.children != ((ListWheelChildLoopingListDelegate) oldDelegate).children;
}
}
public class ListWheelChildBuilderDelegate : ListWheelChildDelegate {
public ListWheelChildBuilderDelegate(
IndexedWidgetBuilder builder,
int? childCount = null
) {
D.assert(builder != null);
this.builder = builder;
this.childCount = childCount;
}
public readonly IndexedWidgetBuilder builder;
public readonly int? childCount;
public int? estimatedChildCount {
get { return this.childCount; }
}
public Widget build(BuildContext context, int index) {
if (this.childCount == null) {
Widget child = this.builder(context, index);
return child == null ? null : new Container(child: child);
}
if (index < 0 || index >= this.childCount) {
return null;
}
return new Container(child: this.builder(context, index));
}
public int trueIndexOf(int index) {
return index;
}
public bool shouldRebuild(ListWheelChildDelegate oldDelegate) {
return this.builder != ((ListWheelChildBuilderDelegate) oldDelegate).builder ||
this.childCount != ((ListWheelChildBuilderDelegate) oldDelegate).childCount;
}
}
class ListWheelScrollViewUtils {
public static int _getItemFromOffset(
float offset,
float itemExtent,
float minScrollExtent,
float maxScrollExtent
) {
return (_clipOffsetToScrollableRange(offset, minScrollExtent, maxScrollExtent) / itemExtent).round();
}
public static float _clipOffsetToScrollableRange(
float offset,
float minScrollExtent,
float maxScrollExtent
) {
return Mathf.Min(Mathf.Max(offset, minScrollExtent), maxScrollExtent);
}
}
public class FixedExtentScrollController : ScrollController {
public FixedExtentScrollController(
int initialItem = 0
) {
this.initialItem = initialItem;
}
public readonly int initialItem;
public int selectedItem {
get {
D.assert(this.positions.isNotEmpty(),
() =>
"FixedExtentScrollController.selectedItem cannot be accessed before a scroll view is built with it."
);
D.assert(this.positions.Count == 1,
() =>
"The selectedItem property cannot be read when multiple scroll views are attached to the same FixedExtentScrollController."
);
_FixedExtentScrollPosition position = (_FixedExtentScrollPosition) this.position;
return position.itemIndex;
}
}
public IPromise animateToItem(
int itemIndex,
TimeSpan duration,
Curve curve
) {
if (!this.hasClients) {
return Promise.Resolved();
}
List<IPromise> futures = new List<IPromise>();
foreach (_FixedExtentScrollPosition position in this.positions) {
futures.Add(position.animateTo(
itemIndex * position.itemExtent,
duration: duration,
curve: curve
));
}
return Promise.All(futures);
}
public void jumpToItem(int itemIndex) {
foreach (_FixedExtentScrollPosition position in this.positions) {
position.jumpTo(itemIndex * position.itemExtent);
}
}
public override ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context,
ScrollPosition oldPosition) {
return new _FixedExtentScrollPosition(
physics: physics,
context: context,
initialItem: this.initialItem,
oldPosition: oldPosition
);
}
}
public interface IFixedExtentMetrics {
int itemIndex { set; get; }
FixedExtentMetrics copyWith(
float? minScrollExtent = null,
float? maxScrollExtent = null,
float? pixels = null,
float? viewportDimension = null,
AxisDirection? axisDirection = null,
int? itemIndex = null
);
}
public class FixedExtentMetrics : FixedScrollMetrics, IFixedExtentMetrics {
public FixedExtentMetrics(
int itemIndex,
float minScrollExtent = 0.0f,
float maxScrollExtent = 0.0f,
float pixels = 0.0f,
float viewportDimension = 0.0f,
AxisDirection axisDirection = AxisDirection.down
) : base(
minScrollExtent: minScrollExtent,
maxScrollExtent: maxScrollExtent,
pixels: pixels,
viewportDimension: viewportDimension,
axisDirection: axisDirection
) {
this.itemIndex = itemIndex;
}
public int itemIndex { get; set; }
public FixedExtentMetrics copyWith(
float? minScrollExtent = null,
float? maxScrollExtent = null,
float? pixels = null,
float? viewportDimension = null,
AxisDirection? axisDirection = null,
int? itemIndex = null
) {
return new FixedExtentMetrics(
minScrollExtent: minScrollExtent ?? this.minScrollExtent,
maxScrollExtent: maxScrollExtent ?? this.maxScrollExtent,
pixels: pixels ?? this.pixels,
viewportDimension: viewportDimension ?? this.viewportDimension,
axisDirection: axisDirection ?? this.axisDirection,
itemIndex: itemIndex ?? this.itemIndex
);
}
}
class _FixedExtentScrollPosition : ScrollPositionWithSingleContext, IFixedExtentMetrics {
public _FixedExtentScrollPosition(
ScrollPhysics physics,
ScrollContext context,
int initialItem,
bool keepScrollOffset = true,
ScrollPosition oldPosition = null,
string debugLabel = null
) : base(
physics: physics,
context: context,
initialPixels: _getItemExtentFromScrollContext(context) * initialItem,
keepScrollOffset: keepScrollOffset,
oldPosition: oldPosition,
debugLabel: debugLabel
) {
D.assert(
context is _FixedExtentScrollableState,
() => "FixedExtentScrollController can only be used with ListWheelScrollViews"
);
}
static float _getItemExtentFromScrollContext(ScrollContext context) {
_FixedExtentScrollableState scrollable = (_FixedExtentScrollableState) context;
return scrollable.itemExtent;
}
public float itemExtent {
get { return _getItemExtentFromScrollContext(this.context); }
}
public int itemIndex {
get {
return ListWheelScrollViewUtils._getItemFromOffset(
offset: this.pixels,
itemExtent: this.itemExtent,
minScrollExtent: this.minScrollExtent,
maxScrollExtent: this.maxScrollExtent
);
}
set { }
}
public FixedExtentMetrics copyWith(
float? minScrollExtent = null,
float? maxScrollExtent = null,
float? pixels = null,
float? viewportDimension = null,
AxisDirection? axisDirection = null,
int? itemIndex = null
) {
return new FixedExtentMetrics(
minScrollExtent: minScrollExtent ?? this.minScrollExtent,
maxScrollExtent: maxScrollExtent ?? this.maxScrollExtent,
pixels: pixels ?? this.pixels,
viewportDimension: viewportDimension ?? this.viewportDimension,
axisDirection: axisDirection ?? this.axisDirection,
itemIndex: itemIndex ?? this.itemIndex
);
}
}
class _FixedExtentScrollable : Scrollable {
public _FixedExtentScrollable(
float itemExtent,
ViewportBuilder viewportBuilder,
Key key = null,
AxisDirection axisDirection = AxisDirection.down,
ScrollController controller = null,
ScrollPhysics physics = null
) : base(
key: key,
axisDirection: axisDirection,
controller: controller,
physics: physics,
viewportBuilder: viewportBuilder
) {
this.itemExtent = itemExtent;
}
public readonly float itemExtent;
public override State createState() {
return new _FixedExtentScrollableState();
}
}
class _FixedExtentScrollableState : ScrollableState {
public float itemExtent {
get {
_FixedExtentScrollable actualWidget = (_FixedExtentScrollable) this.widget;
return actualWidget.itemExtent;
}
}
}
public class FixedExtentScrollPhysics : ScrollPhysics {
public FixedExtentScrollPhysics(
ScrollPhysics parent = null
) : base(parent: parent) { }
public override ScrollPhysics applyTo(ScrollPhysics ancestor) {
return new FixedExtentScrollPhysics(parent: this.buildParent(ancestor));
}
public override Simulation createBallisticSimulation(ScrollMetrics position, float velocity) {
D.assert(
position is _FixedExtentScrollPosition,
() => "FixedExtentScrollPhysics can only be used with Scrollables that uses " +
"the FixedExtentScrollController"
);
_FixedExtentScrollPosition metrics = (_FixedExtentScrollPosition) position;
if ((velocity <= 0.0f && metrics.pixels <= metrics.minScrollExtent) ||
(velocity >= 0.0f && metrics.pixels >= metrics.maxScrollExtent)) {
return base.createBallisticSimulation(metrics, velocity);
}
Simulation testFrictionSimulation =
base.createBallisticSimulation(metrics, velocity);
if (testFrictionSimulation != null
&& (testFrictionSimulation.x(float.PositiveInfinity) == metrics.minScrollExtent
|| testFrictionSimulation.x(float.PositiveInfinity) == metrics.maxScrollExtent)) {
return base.createBallisticSimulation(metrics, velocity);
}
int settlingItemIndex = ListWheelScrollViewUtils._getItemFromOffset(
offset: testFrictionSimulation?.x(float.PositiveInfinity) ?? metrics.pixels,
itemExtent: metrics.itemExtent,
minScrollExtent: metrics.minScrollExtent,
maxScrollExtent: metrics.maxScrollExtent
);
float settlingPixels = settlingItemIndex * metrics.itemExtent;
if (velocity.abs() < this.tolerance.velocity
&& (settlingPixels - metrics.pixels).abs() < this.tolerance.distance) {
return null;
}
if (settlingItemIndex == metrics.itemIndex) {
return new SpringSimulation(this.spring,
metrics.pixels,
settlingPixels,
velocity,
tolerance: this.tolerance
);
}
return FrictionSimulation.through(
metrics.pixels,
settlingPixels,
velocity, this.tolerance.velocity * velocity.sign()
);
}
}
public class ListWheelScrollView : StatefulWidget {
public ListWheelScrollView(
float itemExtent,
List<Widget> children = null,
Key key = null,
ScrollController controller = null,
ScrollPhysics physics = null,
float diameterRatio = RenderListWheelViewport.defaultDiameterRatio,
float perspective = RenderListWheelViewport.defaultPerspective,
float offAxisFraction = 0.0f,
bool useMagnifier = false,
float magnification = 1.0f,
ValueChanged<int> onSelectedItemChanged = null,
bool clipToSize = true,
bool renderChildrenOutsideViewport = false,
ListWheelChildDelegate childDelegate = null
) : base(key: key) {
D.assert(children != null || childDelegate != null);
D.assert(diameterRatio > 0.0, () => RenderListWheelViewport.diameterRatioZeroMessage);
D.assert(perspective > 0);
D.assert(perspective <= 0.01f, () => RenderListWheelViewport.perspectiveTooHighMessage);
D.assert(magnification > 0);
D.assert(itemExtent > 0);
D.assert(
!renderChildrenOutsideViewport || !clipToSize,
() => RenderListWheelViewport.clipToSizeAndRenderChildrenOutsideViewportConflict
);
this.childDelegate = childDelegate ?? new ListWheelChildListDelegate(children: children);
this.itemExtent = itemExtent;
this.controller = controller;
this.physics = physics;
this.diameterRatio = diameterRatio;
this.perspective = perspective;
this.offAxisFraction = offAxisFraction;
this.useMagnifier = useMagnifier;
this.magnification = magnification;
this.onSelectedItemChanged = onSelectedItemChanged;
this.clipToSize = clipToSize;
this.renderChildrenOutsideViewport = renderChildrenOutsideViewport;
}
public static ListWheelScrollView useDelegate(
float itemExtent,
List<Widget> children = null,
ListWheelChildDelegate childDelegate = null,
Key key = null,
ScrollController controller = null,
ScrollPhysics physics = null,
float diameterRatio = RenderListWheelViewport.defaultDiameterRatio,
float perspective = RenderListWheelViewport.defaultPerspective,
float offAxisFraction = 0.0f,
bool useMagnifier = false,
float magnification = 1.0f,
ValueChanged<int> onSelectedItemChanged = null,
bool clipToSize = true,
bool renderChildrenOutsideViewport = false
) {
return new ListWheelScrollView(
itemExtent: itemExtent,
children: children,
childDelegate: childDelegate,
key: key,
controller: controller,
physics: physics,
diameterRatio: diameterRatio,
perspective: perspective,
offAxisFraction: offAxisFraction,
useMagnifier: useMagnifier,
magnification: magnification,
onSelectedItemChanged: onSelectedItemChanged,
clipToSize: clipToSize,
renderChildrenOutsideViewport: renderChildrenOutsideViewport
);
}
public readonly ScrollController controller;
public readonly ScrollPhysics physics;
public readonly float diameterRatio;
public readonly float perspective;
public readonly float offAxisFraction;
public readonly bool useMagnifier;
public readonly float magnification;
public readonly float itemExtent;
public readonly ValueChanged<int> onSelectedItemChanged;
public readonly bool clipToSize;
public readonly bool renderChildrenOutsideViewport;
public readonly ListWheelChildDelegate childDelegate;
public override State createState() {
return new _ListWheelScrollViewState();
}
}
class _ListWheelScrollViewState : State<ListWheelScrollView> {
int _lastReportedItemIndex = 0;
ScrollController scrollController;
public override void initState() {
base.initState();
this.scrollController = this.widget.controller ?? new FixedExtentScrollController();
if (this.widget.controller is FixedExtentScrollController controller) {
this._lastReportedItemIndex = controller.initialItem;
}
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
base.didUpdateWidget(oldWidget);
if (this.widget.controller != null && this.widget.controller != this.scrollController) {
ScrollController oldScrollController = this.scrollController;
SchedulerBinding.instance.addPostFrameCallback((_) => { oldScrollController.dispose(); });
this.scrollController = this.widget.controller;
}
}
public override Widget build(BuildContext context) {
return new NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) => {
if (notification.depth == 0
&& this.widget.onSelectedItemChanged != null
&& notification is ScrollUpdateNotification
&& notification.metrics is FixedExtentMetrics metrics) {
int currentItemIndex = metrics.itemIndex;
if (currentItemIndex != this._lastReportedItemIndex) {
this._lastReportedItemIndex = currentItemIndex;
int trueIndex = this.widget.childDelegate.trueIndexOf(currentItemIndex);
this.widget.onSelectedItemChanged(trueIndex);
}
}
return false;
},
child: new _FixedExtentScrollable(
controller: this.scrollController,
physics: this.widget.physics,
itemExtent: this.widget.itemExtent,
viewportBuilder: (BuildContext _context, ViewportOffset _offset) => {
return new ListWheelViewport(
diameterRatio: this.widget.diameterRatio,
perspective: this.widget.perspective,
offAxisFraction: this.widget.offAxisFraction,
useMagnifier: this.widget.useMagnifier,
magnification: this.widget.magnification,
itemExtent: this.widget.itemExtent,
clipToSize: this.widget.clipToSize,
renderChildrenOutsideViewport: this.widget.renderChildrenOutsideViewport,
offset: _offset,
childDelegate: this.widget.childDelegate
);
}
)
);
}
}
public class ListWheelElement : RenderObjectElement, IListWheelChildManager {
public ListWheelElement(ListWheelViewport widget) : base(widget) { }
public new ListWheelViewport widget {
get { return (ListWheelViewport) base.widget; }
}
public new RenderListWheelViewport renderObject {
get { return (RenderListWheelViewport) base.renderObject; }
}
readonly Dictionary<int, Widget> _childWidgets = new Dictionary<int, Widget>();
readonly SplayTree<int, Element> _childElements = new SplayTree<int, Element>();
public override void update(Widget newWidget) {
ListWheelViewport oldWidget = this.widget;
base.update(newWidget);
ListWheelChildDelegate newDelegate = ((ListWheelViewport) newWidget).childDelegate;
ListWheelChildDelegate oldDelegate = oldWidget.childDelegate;
if (newDelegate != oldDelegate &&
(newDelegate.GetType() != oldDelegate.GetType() || newDelegate.shouldRebuild(oldDelegate))) {
this.performRebuild();
}
}
public int? childCount {
get { return this.widget.childDelegate.estimatedChildCount; }
}
protected override void performRebuild() {
this._childWidgets.Clear();
base.performRebuild();
if (this._childElements.isEmpty()) {
return;
}
int firstIndex = this._childElements.First().Key;
int lastIndex = this._childElements.Last().Key;
for (int index = firstIndex; index <= lastIndex; ++index) {
Element newChild = this.updateChild(this._childElements[index], this.retrieveWidget(index), index);
if (newChild != null) {
this._childElements[index] = newChild;
}
else {
this._childElements.Remove(index);
}
}
}
Widget retrieveWidget(int index) {
return this._childWidgets.putIfAbsent(index,
() => { return this.widget.childDelegate.build(this, index); });
}
public bool childExistsAt(int index) {
return this.retrieveWidget(index) != null;
}
public void createChild(int index, RenderBox after) {
this.owner.buildScope(this, () => {
bool insertFirst = after == null;
D.assert(insertFirst || this._childElements[index - 1] != null);
// Debug.Log($"{index}: {this._childElements.getOrDefault(index)}");
Element newChild = this.updateChild(this._childElements.getOrDefault(index), this.retrieveWidget(index),
index);
// Debug.Log(newChild);
if (newChild != null) {
this._childElements[index] = newChild;
}
else {
this._childElements.Remove(index);
}
});
}
public void removeChild(RenderBox child) {
int index = this.renderObject.indexOf(child);
this.owner.buildScope(this, () => {
D.assert(this._childElements.ContainsKey(index));
Element result = this.updateChild(this._childElements[index], null, index);
D.assert(result == null);
this._childElements.Remove(index);
D.assert(!this._childElements.ContainsKey(index));
});
}
protected override Element updateChild(Element child, Widget newWidget, object newSlot) {
ListWheelParentData oldParentData = (ListWheelParentData) child?.renderObject?.parentData;
Element newChild = base.updateChild(child, newWidget, newSlot);
ListWheelParentData newParentData = (ListWheelParentData) newChild?.renderObject?.parentData;
if (newParentData != null) {
newParentData.index = (int) newSlot;
if (oldParentData != null) {
newParentData.offset = oldParentData.offset;
}
}
return newChild;
}
protected override void insertChildRenderObject(RenderObject child, object slot) {
RenderListWheelViewport renderObject = this.renderObject;
D.assert(renderObject.debugValidateChild(child));
renderObject.insert((RenderBox) child,
(RenderBox) this._childElements.getOrDefault((int) slot - 1)?.renderObject);
// Debug.Log($"insert: {this._childElements.getOrDefault((int) slot - 1)}");
D.assert(renderObject == this.renderObject);
}
protected override void moveChildRenderObject(RenderObject child, dynamic slot) {
const string moveChildRenderObjectErrorMessage =
"Currently we maintain the list in contiguous increasing order, so " +
"moving children around is not allowed.";
D.assert(false, () => moveChildRenderObjectErrorMessage);
}
protected override void removeChildRenderObject(RenderObject child) {
D.assert(child.parent == this.renderObject);
this.renderObject.remove((RenderBox) child);
}
public override void visitChildren(ElementVisitor visitor) {
foreach (var item in this._childElements) {
visitor(item.Value);
}
}
protected override void forgetChild(Element child) {
this._childElements.Remove((int) (child.slot));
}
}
public class ListWheelViewport : RenderObjectWidget {
public ListWheelViewport(
float itemExtent,
ViewportOffset offset,
ListWheelChildDelegate childDelegate,
Key key = null,
float diameterRatio = RenderListWheelViewport.defaultDiameterRatio,
float perspective = RenderListWheelViewport.defaultPerspective,
float offAxisFraction = 0.0f,
bool useMagnifier = false,
float magnification = 1.0f,
bool clipToSize = true,
bool renderChildrenOutsideViewport = false
) : base(key: key) {
D.assert(childDelegate != null);
D.assert(offset != null);
D.assert(diameterRatio > 0, () => RenderListWheelViewport.diameterRatioZeroMessage);
D.assert(perspective > 0);
D.assert(perspective <= 0.01, () => RenderListWheelViewport.perspectiveTooHighMessage);
D.assert(itemExtent > 0);
D.assert(
!renderChildrenOutsideViewport || !clipToSize,
() => RenderListWheelViewport.clipToSizeAndRenderChildrenOutsideViewportConflict
);
this.itemExtent = itemExtent;
this.offset = offset;
this.childDelegate = childDelegate;
this.diameterRatio = diameterRatio;
this.perspective = perspective;
this.offAxisFraction = offAxisFraction;
this.useMagnifier = useMagnifier;
this.magnification = magnification;
this.clipToSize = clipToSize;
this.renderChildrenOutsideViewport = renderChildrenOutsideViewport;
}
public readonly float diameterRatio;
public readonly float perspective;
public readonly float offAxisFraction;
public readonly bool useMagnifier;
public readonly float magnification;
public readonly float itemExtent;
public readonly bool clipToSize;
public readonly bool renderChildrenOutsideViewport;
public readonly ViewportOffset offset;
public readonly ListWheelChildDelegate childDelegate;
public override Element createElement() {
return new ListWheelElement(this);
}
public override RenderObject createRenderObject(BuildContext context) {
ListWheelElement childManager = (ListWheelElement) context;
return new RenderListWheelViewport(
childManager: childManager,
offset: this.offset,
diameterRatio: this.diameterRatio,
perspective: this.perspective,
offAxisFraction: this.offAxisFraction,
useMagnifier: this.useMagnifier,
magnification: this.magnification,
itemExtent: this.itemExtent,
clipToSize: this.clipToSize,
renderChildrenOutsideViewport: this.renderChildrenOutsideViewport
);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
var viewport = (RenderListWheelViewport) renderObject;
viewport.offset = this.offset;
viewport.diameterRatio = this.diameterRatio;
viewport.perspective = this.perspective;
viewport.offAxisFraction = this.offAxisFraction;
viewport.useMagnifier = this.useMagnifier;
viewport.magnification = this.magnification;
viewport.itemExtent = this.itemExtent;
viewport.clipToSize = this.clipToSize;
viewport.renderChildrenOutsideViewport = this.renderChildrenOutsideViewport;
}
}
}

11
Runtime/widgets/list_wheel_scroll_view.cs.meta


fileFormatVersion: 2
guid: 5f33e2b8a13bf4308b91f226530921e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

283
Samples/UIWidgetsGallery/demo/cupertino/cupertino_picker_demo.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.gallery {
class CupertinoPickerDemoUtils {
public const float _kPickerSheetHeight = 216.0f;
public const float _kPickerItemHeight = 32.0f;
}
class CupertinoPickerDemo : StatefulWidget {
public const string routeName = "/cupertino/picker";
public override State createState() {
return new _CupertinoPickerDemoState();
}
}
class _CupertinoPickerDemoState : State<CupertinoPickerDemo> {
int _selectedColorIndex = 0;
TimeSpan timer = new TimeSpan();
// Value that is shown in the date picker in date mode.
DateTime date = DateTime.Now;
// Value that is shown in the date picker in time mode.
DateTime time = DateTime.Now;
// Value that is shown in the date picker in dateAndTime mode.
DateTime dateTime = DateTime.Now;
Widget _buildMenu(List<Widget> children) {
return new Container(
decoration: new BoxDecoration(
color: CupertinoTheme.of(this.context).scaffoldBackgroundColor,
border: new Border(
top: new BorderSide(color: new Color(0xFFBCBBC1), width: 0.0f),
bottom: new BorderSide(color: new Color(0xFFBCBBC1), width: 0.0f)
)
),
height: 44.0f,
child: new Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0f),
child: new SafeArea(
top: false,
bottom: false,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: children
)
)
)
);
}
Widget _buildBottomPicker(Widget picker) {
return new Container(
height: CupertinoPickerDemoUtils._kPickerSheetHeight,
padding: EdgeInsets.only(top: 6.0f),
color: CupertinoColors.white,
child: new DefaultTextStyle(
style: new TextStyle(
color: CupertinoColors.black,
fontSize: 22.0f
),
child: new GestureDetector(
// Blocks taps from propagating to the modal sheet and popping.
onTap: () => { },
child: new SafeArea(
top: false,
child: picker
)
)
)
);
}
Widget _buildColorPicker(BuildContext context) {
FixedExtentScrollController scrollController =
new FixedExtentScrollController(initialItem: this._selectedColorIndex);
List<Widget> generateList() {
var list = new List<Widget>();
foreach (var item in CupertinoNavigationDemoUtils.coolColorNames) {
list.Add(new Center(child:
new Text(item)
));
}
return list;
}
return new GestureDetector(
onTap: () => {
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
builder: (BuildContext _context) => {
return this._buildBottomPicker(
new CupertinoPicker(
scrollController: scrollController,
itemExtent: CupertinoPickerDemoUtils._kPickerItemHeight,
backgroundColor: CupertinoColors.white,
onSelectedItemChanged: (int index) => {
this.setState(() => this._selectedColorIndex = index);
},
children: generateList()
)
);
}
);
},
child: this._buildMenu(new List<Widget> {
new Text("Favorite Color"),
new Text(
CupertinoNavigationDemoUtils.coolColorNames[this._selectedColorIndex],
style: new TextStyle(
color: CupertinoColors.inactiveGray
)
)
}
)
);
}
Widget _buildCountdownTimerPicker(BuildContext context) {
return new GestureDetector(
onTap: () => {
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
builder: (BuildContext _context) => {
return this._buildBottomPicker(
new CupertinoTimerPicker(
initialTimerDuration: this.timer,
onTimerDurationChanged: (TimeSpan newTimer) => {
this.setState(() => this.timer = newTimer);
}
)
);
}
);
},
child: this._buildMenu(new List<Widget> {
new Text("Countdown Timer"),
new Text(
$"{this.timer.Hours}:" +
$"{(this.timer.Minutes % 60).ToString("00")}:" +
$"{(this.timer.Seconds % 60).ToString("00")}",
style: new TextStyle(color: CupertinoColors.inactiveGray)
)
}
)
);
}
Widget _buildDatePicker(BuildContext context) {
return new GestureDetector(
onTap: () => {
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
builder: (BuildContext _context) => {
return this._buildBottomPicker(
new CupertinoDatePicker(
mode: CupertinoDatePickerMode.date,
initialDateTime: this.date,
onDateTimeChanged: (DateTime newDateTime) => {
this.setState(() => this.date = newDateTime);
}
)
);
}
);
},
child: this._buildMenu(new List<Widget> {
new Text("Date"),
new Text(
this.date.ToString("MMMM dd, yyyy"),
style: new TextStyle(color: CupertinoColors.inactiveGray)
)
}
)
);
}
Widget _buildTimePicker(BuildContext context) {
return new GestureDetector(
onTap: () => {
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
builder: (BuildContext _context) => {
return this._buildBottomPicker(
new CupertinoDatePicker(
mode: CupertinoDatePickerMode.time,
initialDateTime: this.time,
onDateTimeChanged: (DateTime newDateTime) => {
this.setState(() => this.time = newDateTime);
}
)
);
}
);
},
child: this._buildMenu(new List<Widget> {
new Text("Time"),
new Text(
this.time.ToString("h:mm tt"),
style: new TextStyle(color: CupertinoColors.inactiveGray)
)
}
)
);
}
Widget _buildDateAndTimePicker(BuildContext context) {
return new GestureDetector(
onTap: () => {
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
builder: (BuildContext _context) => {
return this._buildBottomPicker(
new CupertinoDatePicker(
mode: CupertinoDatePickerMode.dateAndTime,
initialDateTime: this.dateTime,
onDateTimeChanged: (DateTime newDateTime) => {
this.setState(() => this.dateTime = newDateTime);
}
)
);
}
);
},
child: this._buildMenu(new List<Widget> {
new Text("Date and Time"),
new Text(
this.dateTime.ToString("MMMM dd, yyyy h:mm tt"),
style: new TextStyle(color: CupertinoColors.inactiveGray)
)
}
)
);
}
public override Widget build(BuildContext context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
middle: new Text("Picker"),
// We"re specifying a back label here because the previous page is a
// Material page. CupertinoPageRoutes could auto-populate these back
// labels.
previousPageTitle: "Cupertino",
trailing: new CupertinoDemoDocumentationButton(CupertinoPickerDemo.routeName)
),
child: new DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: new DecoratedBox(
decoration: new BoxDecoration(
color: CupertinoTheme.of(context).brightness == Brightness.light
? CupertinoColors.extraLightBackgroundGray
: CupertinoColors.darkBackgroundGray
),
child: new SafeArea(
child: new ListView(
children: new List<Widget> {
new Padding(padding: EdgeInsets.only(top: 32.0f)),
this._buildColorPicker(context),
this._buildCountdownTimerPicker(context),
this._buildDatePicker(context),
this._buildTimePicker(context),
this._buildDateAndTimePicker(context)
}
)
)
)
)
);
}
}
}

11
Samples/UIWidgetsGallery/demo/cupertino/cupertino_picker_demo.cs.meta


fileFormatVersion: 2
guid: 6cb09763e9a60447490569afa586f063
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存