浏览代码
Merge branch 'zgh/cupertino/fix' of github.com:Unity-Technologies/com.unity.uiwidgets into zxw/gallery
/zgh-devtools
Merge branch 'zgh/cupertino/fix' of github.com:Unity-Technologies/com.unity.uiwidgets into zxw/gallery
/zgh-devtools
xingweizhu
4 年前
当前提交
c6dc195e
共有 16 个文件被更改,包括 3143 次插入 和 850 次删除
-
506Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_alert_demo.cs
-
135Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_buttons_demo.cs
-
518Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_picker_demo.cs
-
105Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_slider_demo.cs
-
117Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_switch_demo.cs
-
339Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_text_field_demo.cs
-
41com.unity.uiwidgets/Runtime/cupertino/route.cs
-
3com.unity.uiwidgets/Runtime/cupertino/tab_scaffold.cs
-
8com.unity.uiwidgets/Runtime/ui2/geometry.cs
-
5com.unity.uiwidgets/Runtime/widgets/heroes.cs
-
2com.unity.uiwidgets/Runtime/widgets/navigator.cs
-
237Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_refresh_demo.cs
-
151Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_segmented_control_demo.cs
-
343com.unity.uiwidgets/Runtime/cupertino/refresh.cs
-
616com.unity.uiwidgets/Runtime/cupertino/segmented_control.cs
-
867com.unity.uiwidgets/Runtime/cupertino/sliding_segmented_control.cs
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.async2; |
|||
using Unity.UIWidgets.cupertino; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
using Random = System.Random; |
|||
|
|||
namespace UIWidgetsGallery.gallery { |
|||
class CupertinoRefreshControlDemo : StatefulWidget { |
|||
public static string routeName = "/cupertino/refresh"; |
|||
|
|||
public override State createState() |
|||
{ |
|||
return new _CupertinoRefreshControlDemoState(); |
|||
} |
|||
} |
|||
|
|||
class _CupertinoRefreshControlDemoState : State<CupertinoRefreshControlDemo> { |
|||
|
|||
|
|||
List<List<string>> contacts = new List<List<string>>{ |
|||
new List<string>{"George Washington", "Westmoreland County", " 4/30/1789"}, |
|||
new List<string>{"John Adams", "Braintree", " 3/4/1797"}, |
|||
new List<string>{"Thomas Jefferson", "Shadwell", " 3/4/1801"}, |
|||
new List<string>{"James Madison", "Port Conway", " 3/4/1809"}, |
|||
new List<string>{"James Monroe", "Monroe Hall", " 3/4/1817"}, |
|||
new List<string>{"Andrew Jackson", "Waxhaws Region South/North", " 3/4/1829"}, |
|||
new List<string>{"John Quincy Adams", "Braintree", " 3/4/1825"}, |
|||
new List<string>{"William Henry Harrison", "Charles City County", " 3/4/1841"}, |
|||
new List<string>{"Martin Van Buren", "Kinderhook New", " 3/4/1837"}, |
|||
new List<string>{"Zachary Taylor", "Barboursville", " 3/4/1849"}, |
|||
new List<string>{"John Tyler", "Charles City County", " 4/4/1841"}, |
|||
new List<string>{"James Buchanan", "Cove Gap", " 3/4/1857"}, |
|||
new List<string>{"James K. Polk", "Pineville North", " 3/4/1845"}, |
|||
new List<string>{"Millard Fillmore", "Summerhill New", "7/9/1850"}, |
|||
new List<string>{"Franklin Pierce", "Hillsborough New", " 3/4/1853"}, |
|||
new List<string>{"Andrew Johnson", "Raleigh North", " 4/15/1865"}, |
|||
new List<string>{"Abraham Lincoln", "Sinking Spring", " 3/4/1861"}, |
|||
new List<string>{"Ulysses S. Grant", "Point Pleasant", " 3/4/1869"}, |
|||
new List<string>{"Rutherford B. Hayes", "Delaware", " 3/4/1877"}, |
|||
new List<string>{"Chester A. Arthur", "Fairfield", " 9/19/1881"}, |
|||
new List<string>{"James A. Garfield", "Moreland Hills", " 3/4/1881"}, |
|||
new List<string>{"Benjamin Harrison", "North Bend", " 3/4/1889"}, |
|||
new List<string>{"Grover Cleveland", "Caldwell New", " 3/4/1885"}, |
|||
new List<string>{"William McKinley", "Niles", " 3/4/1897"}, |
|||
new List<string>{"Woodrow Wilson", "Staunton", " 3/4/1913"}, |
|||
new List<string>{"William H. Taft", "Cincinnati", " 3/4/1909"}, |
|||
new List<string>{"Theodore Roosevelt", "New York City New", " 9/14/1901"}, |
|||
new List<string>{"Warren G. Harding", "Blooming Grove", " 3/4/1921"}, |
|||
new List<string>{"Calvin Coolidge", "Plymouth", "8/2/1923"}, |
|||
new List<string>{"Herbert Hoover", "West Branch", " 3/4/1929"}, |
|||
new List<string>{"Franklin D. Roosevelt", "Hyde Park New", " 3/4/1933"}, |
|||
new List<string>{"Harry S. Truman", "Lamar", " 4/12/1945"}, |
|||
new List<string>{"Dwight D. Eisenhower", "Denison", " 1/20/1953"}, |
|||
new List<string>{"Lyndon B. Johnson", "Stonewall", "11/22/1963"}, |
|||
new List<string>{"Ronald Reagan", "Tampico", " 1/20/1981"}, |
|||
new List<string>{"Richard Nixon", "Yorba Linda", " 1/20/1969"}, |
|||
new List<string>{"Gerald Ford", "Omaha", "August 9/1974"}, |
|||
new List<string>{"John F. Kennedy", "Brookline", " 1/20/1961"}, |
|||
new List<string>{"George H. W. Bush", "Milton", " 1/20/1989"}, |
|||
new List<string>{"Jimmy Carter", "Plains", " 1/20/1977"}, |
|||
new List<string>{"George W. Bush", "New Haven", " 1/20, 2001"}, |
|||
new List<string>{"Bill Clinton", "Hope", " 1/20/1993"}, |
|||
new List<string>{"Barack Obama", "Honolulu", " 1/20/2009"}, |
|||
new List<string>{"Donald J. Trump", "New York City", " 1/20/2017"} |
|||
}; |
|||
|
|||
|
|||
|
|||
List<List<string>> randomizedContacts; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
repopulateList(); |
|||
} |
|||
|
|||
void repopulateList() { |
|||
Random random = new Random(); |
|||
for (int index = 0; index < 100; index++ ){ |
|||
contacts[random.Next()].Add(random.Next()%2 == 0 ? true.ToString() : false.ToString()); |
|||
randomizedContacts.Add(contacts[random.Next()]); |
|||
} |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new DefaultTextStyle( |
|||
style: CupertinoTheme.of(context).textTheme.textStyle, |
|||
child: new CupertinoPageScaffold( |
|||
backgroundColor: CupertinoColors.systemGroupedBackground, |
|||
child: new CustomScrollView( |
|||
|
|||
physics: new BouncingScrollPhysics(parent: new AlwaysScrollableScrollPhysics()), |
|||
slivers: new List<Widget>{ |
|||
new CupertinoSliverNavigationBar( |
|||
largeTitle: new Text("Refresh"), |
|||
|
|||
previousPageTitle: "Cupertino" |
|||
//trailing: CupertinoDemoDocumentationButton(CupertinoRefreshControlDemo.routeName),
|
|||
), |
|||
new CupertinoSliverRefreshControl( |
|||
onRefresh: () =>{ |
|||
return Future.delayed(new TimeSpan(0,0,0,2)).then((_)=> { |
|||
if (mounted) { |
|||
setState(() => repopulateList()); |
|||
} |
|||
}); |
|||
} |
|||
), |
|||
new SliverSafeArea( |
|||
top: false, // Top safe area is consumed by the navigation bar.
|
|||
sliver: new SliverList( |
|||
del: new SliverChildBuilderDelegate( |
|||
(BuildContext context1, int index)=> { |
|||
return new _ListItem( |
|||
name: randomizedContacts[index][0], |
|||
place: randomizedContacts[index][1], |
|||
date: randomizedContacts[index][2], |
|||
called: randomizedContacts[index][3] == "true" |
|||
); |
|||
}, |
|||
childCount: 20 |
|||
) |
|||
) |
|||
), |
|||
} |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _ListItem : StatelessWidget { |
|||
public _ListItem( |
|||
string name = null, |
|||
string place = null, |
|||
string date = null, |
|||
bool? called = null |
|||
) |
|||
{ |
|||
this.name = name; |
|||
this.place = place; |
|||
this.date = date; |
|||
this.called = called; |
|||
} |
|||
|
|||
public readonly string name; |
|||
public readonly string place; |
|||
public readonly string date; |
|||
public readonly bool? called; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new Container( |
|||
color: CupertinoDynamicColor.resolve(CupertinoColors.systemBackground, context), |
|||
height: 60.0f, |
|||
padding: EdgeInsets.only(top: 9.0f), |
|||
child: new Row( |
|||
children: new List<Widget>{ |
|||
new Container( |
|||
width: 38.0f, |
|||
child: (bool)called |
|||
? new Align( |
|||
alignment: Alignment.topCenter, |
|||
child: new Icon( |
|||
CupertinoIcons.phone_solid, |
|||
color: CupertinoColors.inactiveGray.resolveFrom(context), |
|||
size: 18.0f |
|||
) |
|||
) |
|||
: null |
|||
), |
|||
new Expanded( |
|||
child: new Container( |
|||
decoration: new BoxDecoration( |
|||
border: new Border( |
|||
bottom: new BorderSide(color: new Color(0xFFBCBBC1), width: 0.0f) |
|||
) |
|||
), |
|||
padding: EdgeInsets.only(left: 1.0f, bottom: 9.0f, right: 10.0f), |
|||
child: new Row( |
|||
children: new List<Widget>{ |
|||
new Expanded( |
|||
child: new Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|||
children: new List<Widget>{ |
|||
new Text( |
|||
name, |
|||
maxLines: 1, |
|||
overflow: TextOverflow.ellipsis, |
|||
style: new TextStyle( |
|||
fontWeight: FontWeight.w600, |
|||
letterSpacing: -0.18f |
|||
) |
|||
), |
|||
new Text( |
|||
place, |
|||
maxLines: 1, |
|||
overflow: TextOverflow.ellipsis, |
|||
style: new TextStyle( |
|||
fontSize: 15.0f, |
|||
letterSpacing: -0.24f, |
|||
color: CupertinoColors.inactiveGray.resolveFrom(context) |
|||
) |
|||
), |
|||
} |
|||
) |
|||
), |
|||
new Text( |
|||
date, |
|||
style: new TextStyle( |
|||
color: CupertinoColors.inactiveGray.resolveFrom(context), |
|||
fontSize: 15.0f, |
|||
letterSpacing: -0.41f |
|||
) |
|||
), |
|||
new Padding( |
|||
padding: EdgeInsets.only(left: 9.0f), |
|||
child: new Icon( |
|||
CupertinoIcons.info, |
|||
color: CupertinoTheme.of(context).primaryColor |
|||
) |
|||
) |
|||
} |
|||
) |
|||
) |
|||
) |
|||
} |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using uiwidgets; |
|||
using Unity.UIWidgets.async2; |
|||
using Unity.UIWidgets.cupertino; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
using Random = System.Random; |
|||
using Unity.UIWidgets.material; |
|||
|
|||
namespace UIWidgetsGallery.gallery |
|||
{ |
|||
public class CupertinoSegmentedControlDemo : StatefulWidget { |
|||
public static string routeName = "cupertino/segmented_control"; |
|||
|
|||
public override State createState() |
|||
{ |
|||
return new _CupertinoSegmentedControlDemoState(); |
|||
|
|||
} |
|||
} |
|||
public class _CupertinoSegmentedControlDemoState : State<CupertinoSegmentedControlDemo> { |
|||
Color _kKeyUmbraOpacity = new Color(0x33000000); // alpha = 0.2
|
|||
Color _kKeyPenumbraOpacity = new Color(0x24000000); // alpha = 0.14
|
|||
Color _kAmbientShadowOpacity = new Color(0x1F000000); // alpha = 0.12
|
|||
public readonly Dictionary<int, Widget> children = new Dictionary<int, Widget>{ |
|||
{0, new Text("Midnight")}, |
|||
{1, new Text("Viridian")}, |
|||
{2, new Text("Cerulean")}, |
|||
}; |
|||
public readonly Dictionary<int, Widget> icons = new Dictionary<int, Widget>{ |
|||
{0, new Center( |
|||
child: new Icon( |
|||
CupertinoIcons.home, |
|||
color: Colors.indigo, |
|||
size: 200.0f |
|||
|
|||
) |
|||
)}, |
|||
{1, new Center( |
|||
child: new Icon( |
|||
CupertinoIcons.bell, |
|||
color: Colors.teal, |
|||
size: 200.0f |
|||
) |
|||
)}, |
|||
{2, new Center( |
|||
child: new Icon( |
|||
CupertinoIcons.back, |
|||
color: Colors.cyan, |
|||
size: 200.0f |
|||
) |
|||
)}, |
|||
}; |
|||
int currentSegment = 0; |
|||
void onValueChanged(int newValue) { |
|||
setState(()=> { |
|||
currentSegment = newValue; |
|||
}); |
|||
} |
|||
public override Widget build(BuildContext context) { |
|||
return new CupertinoPageScaffold( |
|||
navigationBar: new CupertinoNavigationBar( |
|||
middle: new Text("Segmented Control"), |
|||
previousPageTitle: "Cupertino" |
|||
|
|||
), |
|||
child: new DefaultTextStyle( |
|||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(fontSize: 13), |
|||
child: new SafeArea( |
|||
child: new Column( |
|||
children: new List<Widget>{ |
|||
new Padding(padding: EdgeInsets.all(16.0f)), |
|||
new SizedBox( |
|||
width: 500.0f, |
|||
child: new CupertinoSegmentedControl<int>( |
|||
children: children, |
|||
onValueChanged: onValueChanged, |
|||
groupValue: currentSegment |
|||
) |
|||
), |
|||
new SizedBox( |
|||
width: 500, |
|||
child: new Padding( |
|||
padding: EdgeInsets.all(16.0f), |
|||
child: new CupertinoSlidingSegmentedControl<int>( |
|||
children: children, |
|||
onValueChanged: onValueChanged, |
|||
groupValue: currentSegment |
|||
) |
|||
) |
|||
), |
|||
new Expanded( |
|||
child: new Padding( |
|||
padding: EdgeInsets.symmetric( |
|||
vertical: 32.0f, |
|||
horizontal: 16.0f |
|||
), |
|||
child: new CupertinoUserInterfaceLevel( |
|||
data: CupertinoUserInterfaceLevelData.elevatedlayer, |
|||
child: new Builder( |
|||
builder: (BuildContext context1) =>{ |
|||
return new Container( |
|||
padding: EdgeInsets.symmetric( |
|||
vertical: 64.0f, |
|||
horizontal: 16.0f |
|||
), |
|||
decoration: new BoxDecoration( |
|||
color: CupertinoTheme.of(context1).scaffoldBackgroundColor, |
|||
borderRadius: BorderRadius.circular(3.0f), |
|||
boxShadow: new List<BoxShadow>{ |
|||
new BoxShadow( |
|||
offset: new Offset(0.0f, 3.0f), |
|||
blurRadius: 5.0f, |
|||
spreadRadius: -1.0f, |
|||
color: _kKeyUmbraOpacity |
|||
), |
|||
new BoxShadow( |
|||
offset: new Offset(0.0f, 6.0f), |
|||
blurRadius: 10.0f, |
|||
spreadRadius: 0.0f, |
|||
color: _kKeyPenumbraOpacity |
|||
), |
|||
new BoxShadow( |
|||
offset: new Offset(0.0f, 1.0f), |
|||
blurRadius: 18.0f, |
|||
spreadRadius: 0.0f, |
|||
color: _kAmbientShadowOpacity |
|||
) |
|||
} |
|||
), |
|||
child: icons[currentSegment] |
|||
); |
|||
} |
|||
) |
|||
) |
|||
) |
|||
) |
|||
} |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|
|||
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.scheduler2; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
using UnityEngine; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
class _CupertinoSliverRefresh : SingleChildRenderObjectWidget { |
|||
public _CupertinoSliverRefresh( |
|||
Key key = null, |
|||
float refreshIndicatorLayoutExtent = 0.0f, |
|||
bool hasLayoutExtent = false, |
|||
Widget child = null |
|||
) : base(key: key, child: child) { |
|||
D.assert(refreshIndicatorLayoutExtent != null); |
|||
D.assert(refreshIndicatorLayoutExtent >= 0.0); |
|||
D.assert(hasLayoutExtent != null); |
|||
this.refreshIndicatorLayoutExtent = refreshIndicatorLayoutExtent; |
|||
this.hasLayoutExtent = hasLayoutExtent; |
|||
} |
|||
public readonly float refreshIndicatorLayoutExtent; |
|||
public readonly bool hasLayoutExtent; |
|||
|
|||
public override RenderObject createRenderObject(BuildContext context) { |
|||
return new _RenderCupertinoSliverRefresh( |
|||
refreshIndicatorExtent: refreshIndicatorLayoutExtent, |
|||
hasLayoutExtent: hasLayoutExtent |
|||
); |
|||
} |
|||
public override void updateRenderObject(BuildContext context, RenderObject renderObject) { |
|||
renderObject = (_RenderCupertinoSliverRefresh)renderObject; |
|||
((_RenderCupertinoSliverRefresh) renderObject).refreshIndicatorLayoutExtent = refreshIndicatorLayoutExtent; |
|||
((_RenderCupertinoSliverRefresh) renderObject).hasLayoutExtent = hasLayoutExtent; |
|||
} |
|||
} |
|||
class _RenderCupertinoSliverRefresh : RenderObjectWithChildMixinRenderSliver<RenderBox> { |
|||
public _RenderCupertinoSliverRefresh( |
|||
float refreshIndicatorExtent, |
|||
bool hasLayoutExtent = false, |
|||
RenderBox child = null) { |
|||
|
|||
D.assert(refreshIndicatorExtent >= 0.0f); |
|||
_refreshIndicatorExtent = refreshIndicatorExtent; |
|||
_hasLayoutExtent = hasLayoutExtent; |
|||
this.child = child; |
|||
} |
|||
|
|||
|
|||
public float refreshIndicatorLayoutExtent { |
|||
get { return _refreshIndicatorExtent; } |
|||
set { |
|||
D.assert(value != null); |
|||
D.assert(value >= 0.0); |
|||
if (value == _refreshIndicatorExtent) |
|||
return; |
|||
_refreshIndicatorExtent = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
float _refreshIndicatorExtent; |
|||
|
|||
public bool hasLayoutExtent { |
|||
get { return _hasLayoutExtent; } |
|||
set { |
|||
D.assert(value != null); |
|||
if (value == _hasLayoutExtent) |
|||
return; |
|||
_hasLayoutExtent = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
bool _hasLayoutExtent; |
|||
float layoutExtentOffsetCompensation = 0.0f; |
|||
|
|||
protected override void performLayout() { |
|||
SliverConstraints constraints = this.constraints; |
|||
D.assert(constraints.axisDirection == AxisDirection.down); |
|||
D.assert(constraints.growthDirection == GrowthDirection.forward); |
|||
float layoutExtent = |
|||
(_hasLayoutExtent ? 1.0f : 0.0f) * _refreshIndicatorExtent; |
|||
if (layoutExtent != layoutExtentOffsetCompensation) { |
|||
geometry = new SliverGeometry( |
|||
scrollOffsetCorrection: layoutExtent - layoutExtentOffsetCompensation |
|||
); |
|||
layoutExtentOffsetCompensation = layoutExtent; |
|||
|
|||
} |
|||
bool active = constraints.overlap < 0.0 || layoutExtent > 0.0; |
|||
float overscrolledExtent = |
|||
constraints.overlap < 0.0f ? constraints.overlap.abs() : 0.0f; |
|||
child.layout( |
|||
constraints.asBoxConstraints( |
|||
maxExtent: layoutExtent + overscrolledExtent |
|||
), |
|||
parentUsesSize: true |
|||
); |
|||
if (active) { |
|||
geometry = new SliverGeometry( |
|||
scrollExtent: layoutExtent, |
|||
paintOrigin: -overscrolledExtent - constraints.scrollOffset, |
|||
paintExtent: Mathf.Max( |
|||
Mathf.Max(child.size.height, layoutExtent) - constraints.scrollOffset, 0.0f), |
|||
maxPaintExtent: Mathf.Max( |
|||
Mathf.Max(child.size.height, layoutExtent) - constraints.scrollOffset, 0.0f), |
|||
layoutExtent: Mathf.Max(layoutExtent - constraints.scrollOffset, 0.0f)); |
|||
} else { |
|||
geometry = SliverGeometry.zero; |
|||
} |
|||
} |
|||
public override void paint(PaintingContext paintContext, Offset offset) { |
|||
if (constraints.overlap < 0.0 || constraints.scrollOffset + child.size.height > 0) { |
|||
paintContext.paintChild(child, offset); |
|||
} |
|||
} |
|||
public override void applyPaintTransform(RenderObject child, Matrix4 transform) { } |
|||
} |
|||
|
|||
public enum RefreshIndicatorMode { |
|||
/// Initial state, when not being overscrolled into, or after the overscroll
|
|||
/// is canceled or after done and the sliver retracted away.
|
|||
inactive, |
|||
|
|||
/// While being overscrolled but not far enough yet to trigger the refresh.
|
|||
drag, |
|||
|
|||
/// Dragged far enough that the onRefresh callback will run and the dragged
|
|||
/// displacement is not yet at the final refresh resting state.
|
|||
armed, |
|||
|
|||
/// While the onRefresh task is running.
|
|||
refresh, |
|||
|
|||
/// While the indicator is animating away after refreshing.
|
|||
done, |
|||
} |
|||
public delegate Widget RefreshControlIndicatorBuilder( |
|||
BuildContext context, |
|||
RefreshIndicatorMode refreshState, |
|||
float pulledExtent, |
|||
float refreshTriggerPullDistance, |
|||
float refreshIndicatorExtent |
|||
); |
|||
|
|||
public delegate Future RefreshCallback(); |
|||
|
|||
public class CupertinoSliverRefreshControl : StatefulWidget { |
|||
public CupertinoSliverRefreshControl( |
|||
Key key = null, |
|||
float refreshTriggerPullDistance = _defaultRefreshTriggerPullDistance, |
|||
float refreshIndicatorExtent = _defaultRefreshIndicatorExtent, |
|||
RefreshControlIndicatorBuilder builder = null, |
|||
RefreshCallback onRefresh = null |
|||
) : base(key: key) { |
|||
D.assert(refreshTriggerPullDistance != null); |
|||
D.assert(refreshTriggerPullDistance > 0.0); |
|||
D.assert(refreshIndicatorExtent != null); |
|||
D.assert(refreshIndicatorExtent >= 0.0); |
|||
D.assert( |
|||
refreshTriggerPullDistance >= refreshIndicatorExtent, () => |
|||
"The refresh indicator cannot take more space in its final state " + |
|||
"than the amount initially created by overscrolling." |
|||
); |
|||
this.refreshIndicatorExtent = refreshIndicatorExtent; |
|||
this.refreshTriggerPullDistance = refreshTriggerPullDistance; |
|||
this.builder = builder ?? buildSimpleRefreshIndicator; |
|||
this.onRefresh = onRefresh; |
|||
|
|||
|
|||
} |
|||
public readonly float refreshTriggerPullDistance; |
|||
public readonly float refreshIndicatorExtent; |
|||
public readonly RefreshControlIndicatorBuilder builder; |
|||
public readonly RefreshCallback onRefresh; |
|||
const float _defaultRefreshTriggerPullDistance = 100.0f; |
|||
const float _defaultRefreshIndicatorExtent = 60.0f; |
|||
|
|||
static RefreshIndicatorMode state(BuildContext context) { |
|||
_CupertinoSliverRefreshControlState state = context.findAncestorStateOfType<_CupertinoSliverRefreshControlState>(); |
|||
return state.refreshState; |
|||
} |
|||
public static Widget buildSimpleRefreshIndicator( |
|||
BuildContext context, |
|||
RefreshIndicatorMode refreshState, |
|||
float pulledExtent, |
|||
float refreshTriggerPullDistance, |
|||
float refreshIndicatorExtent |
|||
) { |
|||
Curve opacityCurve = new Interval(0.4f, 0.8f, curve: Curves.easeInOut); |
|||
|
|||
return new Align( |
|||
alignment: Alignment.bottomCenter, |
|||
child: new Padding( |
|||
padding: EdgeInsets.only(bottom: 16.0f), |
|||
child: refreshState == RefreshIndicatorMode.drag |
|||
? new Opacity( |
|||
opacity: opacityCurve.transform(Mathf.Min(pulledExtent / refreshTriggerPullDistance, 1.0f) |
|||
), |
|||
child: new Icon( |
|||
CupertinoIcons.down_arrow, |
|||
color: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context), |
|||
size: 36.0f |
|||
) |
|||
) |
|||
: new Opacity( |
|||
opacity: opacityCurve.transform( |
|||
Mathf.Min(pulledExtent / refreshIndicatorExtent, 1.0f) |
|||
), |
|||
child: new CupertinoActivityIndicator(radius: 14.0f) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
|
|||
public override State createState() { |
|||
return new _CupertinoSliverRefreshControlState(); |
|||
|
|||
} |
|||
} |
|||
public class _CupertinoSliverRefreshControlState : State<CupertinoSliverRefreshControl> { |
|||
public const float _inactiveResetOverscrollFraction = 0.1f; |
|||
public RefreshIndicatorMode refreshState; |
|||
Future refreshTask; |
|||
float latestIndicatorBoxExtent = 0.0f; |
|||
bool hasSliverLayoutExtent = false; |
|||
public override void initState() { |
|||
base.initState(); |
|||
refreshState = RefreshIndicatorMode.inactive; |
|||
} |
|||
RefreshIndicatorMode transitionNextState() { |
|||
RefreshIndicatorMode nextState = RefreshIndicatorMode.refresh; |
|||
|
|||
void goToDone() { |
|||
nextState = RefreshIndicatorMode.done; |
|||
|
|||
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) { |
|||
setState(() => hasSliverLayoutExtent = false); |
|||
} else { |
|||
SchedulerBinding.instance.addPostFrameCallback((timestamp)=> { |
|||
setState(() => hasSliverLayoutExtent = false); |
|||
}); |
|||
} |
|||
} |
|||
switch (refreshState) { |
|||
case RefreshIndicatorMode.inactive: |
|||
if (latestIndicatorBoxExtent <= 0) { |
|||
return RefreshIndicatorMode.inactive; |
|||
} else { |
|||
nextState = RefreshIndicatorMode.drag; |
|||
} |
|||
|
|||
goto case RefreshIndicatorMode.drag; |
|||
|
|||
case RefreshIndicatorMode.drag: |
|||
if (latestIndicatorBoxExtent == 0) { |
|||
return RefreshIndicatorMode.inactive; |
|||
} else if (latestIndicatorBoxExtent < widget.refreshTriggerPullDistance) { |
|||
return RefreshIndicatorMode.drag; |
|||
} else { |
|||
if (widget.onRefresh != null) { //HapticFeedback.mediumImpact();
|
|||
|
|||
SchedulerBinding.instance.addPostFrameCallback((timestamp)=> { |
|||
refreshTask = widget.onRefresh().whenComplete(() =>{ |
|||
if (mounted) { |
|||
setState(() => refreshTask = null); |
|||
|
|||
refreshState = transitionNextState(); |
|||
} |
|||
}); |
|||
setState(() => hasSliverLayoutExtent = true); |
|||
}); |
|||
} |
|||
return RefreshIndicatorMode.armed; |
|||
} |
|||
|
|||
break; |
|||
case RefreshIndicatorMode.armed: |
|||
if (refreshState == RefreshIndicatorMode.armed && refreshTask == null) { |
|||
goToDone(); |
|||
goto case RefreshIndicatorMode.done; |
|||
} |
|||
|
|||
if (latestIndicatorBoxExtent > widget.refreshIndicatorExtent) { |
|||
return RefreshIndicatorMode.armed; |
|||
} else { |
|||
nextState = RefreshIndicatorMode.refresh; |
|||
} |
|||
|
|||
goto case RefreshIndicatorMode.refresh; |
|||
|
|||
case RefreshIndicatorMode.refresh: |
|||
if (refreshTask != null) { |
|||
return RefreshIndicatorMode.refresh; |
|||
} else { |
|||
goToDone(); |
|||
} |
|||
|
|||
goto case RefreshIndicatorMode.done; |
|||
|
|||
case RefreshIndicatorMode.done: |
|||
|
|||
if (latestIndicatorBoxExtent > |
|||
widget.refreshTriggerPullDistance * _inactiveResetOverscrollFraction) { |
|||
return RefreshIndicatorMode.done; |
|||
} else { |
|||
nextState = RefreshIndicatorMode.inactive; |
|||
} |
|||
break; |
|||
} |
|||
return nextState; |
|||
} |
|||
public override Widget build(BuildContext context) { |
|||
return new _CupertinoSliverRefresh( |
|||
refreshIndicatorLayoutExtent: widget.refreshIndicatorExtent, |
|||
hasLayoutExtent: hasSliverLayoutExtent, |
|||
|
|||
child: new LayoutBuilder( |
|||
builder: (BuildContext context1, BoxConstraints constraints) =>{ |
|||
latestIndicatorBoxExtent = constraints.maxHeight; |
|||
refreshState = transitionNextState(); |
|||
if (widget.builder != null && latestIndicatorBoxExtent > 0) { |
|||
return widget.builder( |
|||
context1, |
|||
refreshState, |
|||
latestIndicatorBoxExtent, |
|||
widget.refreshTriggerPullDistance, |
|||
widget.refreshIndicatorExtent |
|||
); |
|||
} |
|||
return new Container(); |
|||
} |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.widgets; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using UnityEngine; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using Rect = Unity.UIWidgets.ui.Rect; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
class CupertinoSegmentedControlsUtils { |
|||
public static readonly EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(horizontal: 16.0f); |
|||
public const float _kMinSegmentedControlHeight = 28.0f; |
|||
public static readonly TimeSpan _kFadeDuration = TimeSpan.FromMilliseconds(165); |
|||
} |
|||
|
|||
public class CupertinoSegmentedControl<T> : StatefulWidget { |
|||
public CupertinoSegmentedControl( |
|||
Key key = null, |
|||
Dictionary<T, Widget> children = null, |
|||
ValueChanged<T> onValueChanged = null, |
|||
T groupValue = default, |
|||
Color unselectedColor = null, |
|||
Color selectedColor = null, |
|||
Color borderColor = null, |
|||
Color pressedColor = null, |
|||
EdgeInsetsGeometry Padding = null |
|||
) :base(key: key) |
|||
{ |
|||
D.assert(children != null); |
|||
D.assert(children.Count >= 2); |
|||
D.assert(onValueChanged != null); |
|||
D.assert( |
|||
groupValue == null || children.Keys.Any((T child) => child.Equals(groupValue)), () => |
|||
"The groupValue must be either null or one of the keys in the children map." |
|||
); |
|||
} |
|||
public readonly Dictionary<T, Widget> children; |
|||
public readonly T groupValue; |
|||
public readonly ValueChanged<T> onValueChanged; |
|||
public readonly Color unselectedColor; |
|||
public readonly Color selectedColor; |
|||
public readonly Color borderColor; |
|||
public readonly Color pressedColor; |
|||
public readonly EdgeInsetsGeometry padding; |
|||
|
|||
public override State createState() { |
|||
return new _SegmentedControlState<T>(); |
|||
} |
|||
} |
|||
class _SegmentedControlState<T> : TickerProviderStateMixin<CupertinoSegmentedControl<T>> { |
|||
T _pressedKey; |
|||
|
|||
public readonly List<AnimationController> _selectionControllers = new List<AnimationController>(); |
|||
public readonly List<ColorTween> _childTweens = new List<ColorTween>(); |
|||
|
|||
ColorTween _forwardBackgroundColorTween; |
|||
ColorTween _reverseBackgroundColorTween; |
|||
ColorTween _textColorTween; |
|||
|
|||
Color _selectedColor; |
|||
Color _unselectedColor; |
|||
Color _borderColor; |
|||
Color _pressedColor; |
|||
|
|||
AnimationController createAnimationController() { |
|||
var controller = new AnimationController( |
|||
duration: CupertinoSegmentedControlsUtils._kFadeDuration, |
|||
vsync: this |
|||
); |
|||
controller.addListener(()=> { |
|||
setState(() =>{ |
|||
// State of background/text colors has changed
|
|||
}); |
|||
}); |
|||
return controller; |
|||
} |
|||
|
|||
bool _updateColors() { |
|||
D.assert(mounted, ()=>"This should only be called after didUpdateDependencies"); |
|||
bool changed = false; |
|||
Color selectedColor = widget.selectedColor ?? CupertinoTheme.of(context).primaryColor; |
|||
if (_selectedColor != selectedColor) { |
|||
changed = true; |
|||
_selectedColor = selectedColor; |
|||
} |
|||
Color unselectedColor = widget.unselectedColor ?? CupertinoTheme.of(context).primaryContrastingColor; |
|||
if (_unselectedColor != unselectedColor) { |
|||
changed = true; |
|||
_unselectedColor = unselectedColor; |
|||
} |
|||
Color borderColor = widget.borderColor ?? CupertinoTheme.of(context).primaryColor; |
|||
if (_borderColor != borderColor) { |
|||
changed = true; |
|||
_borderColor = borderColor; |
|||
} |
|||
Color pressedColor = widget.pressedColor ?? CupertinoTheme.of(context).primaryColor.withOpacity(0.2f); |
|||
if (_pressedColor != pressedColor) { |
|||
changed = true; |
|||
_pressedColor = pressedColor; |
|||
} |
|||
|
|||
_forwardBackgroundColorTween = new ColorTween( |
|||
begin: _pressedColor, |
|||
end: _selectedColor |
|||
); |
|||
_reverseBackgroundColorTween = new ColorTween( |
|||
begin: _unselectedColor, |
|||
end: _selectedColor |
|||
); |
|||
_textColorTween = new ColorTween( |
|||
begin: _selectedColor, |
|||
end: _unselectedColor |
|||
); |
|||
return changed; |
|||
} |
|||
|
|||
void _updateAnimationControllers() { |
|||
D.assert(mounted, ()=>"This should only be called after didUpdateDependencies"); |
|||
foreach ( AnimationController controller in _selectionControllers) { |
|||
controller.dispose(); |
|||
} |
|||
_selectionControllers.Clear(); |
|||
_childTweens.Clear(); |
|||
|
|||
foreach ( T key in widget.children.Keys) { |
|||
AnimationController animationController = createAnimationController(); |
|||
if (widget.groupValue.Equals(key)) { |
|||
_childTweens.Add(_reverseBackgroundColorTween); |
|||
animationController.setValue(1.0f); |
|||
} else { |
|||
_childTweens.Add(_forwardBackgroundColorTween); |
|||
} |
|||
_selectionControllers.Add(animationController); |
|||
} |
|||
} |
|||
|
|||
public override void didChangeDependencies() { |
|||
base.didChangeDependencies(); |
|||
|
|||
if (_updateColors()) { |
|||
_updateAnimationControllers(); |
|||
} |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|||
oldWidget = (CupertinoSegmentedControl<T>) oldWidget; |
|||
base.didUpdateWidget(oldWidget); |
|||
|
|||
if (_updateColors() || ((CupertinoSegmentedControl<T>) oldWidget).children.Count != widget.children.Count) { |
|||
_updateAnimationControllers(); |
|||
} |
|||
|
|||
if (!((CupertinoSegmentedControl<T>)oldWidget).groupValue.Equals(widget.groupValue)) { |
|||
int index = 0; |
|||
foreach ( T key in widget.children.Keys) { |
|||
if (widget.groupValue.Equals(key)) { |
|||
_childTweens[index] = _forwardBackgroundColorTween; |
|||
_selectionControllers[index].forward(); |
|||
} else { |
|||
_childTweens[index] = _reverseBackgroundColorTween; |
|||
_selectionControllers[index].reverse(); |
|||
} |
|||
index += 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override void dispose() { |
|||
foreach( AnimationController animationController in _selectionControllers) { |
|||
animationController.dispose(); |
|||
} |
|||
base.dispose(); |
|||
} |
|||
|
|||
|
|||
void _onTapDown(T currentKey) { |
|||
if (_pressedKey == null && !currentKey.Equals(widget.groupValue)) { |
|||
setState(()=> { |
|||
_pressedKey = currentKey; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
void _onTapCancel() { |
|||
setState(()=> { |
|||
_pressedKey = default(T); |
|||
}); |
|||
} |
|||
|
|||
void _onTap(T currentKey) { |
|||
if (!currentKey.Equals( _pressedKey)) |
|||
return; |
|||
if (!currentKey.Equals(widget.groupValue)) { |
|||
widget.onValueChanged(currentKey); |
|||
} |
|||
_pressedKey = default; |
|||
} |
|||
|
|||
Color getTextColor(int index, T currentKey) { |
|||
if (_selectionControllers[index].isAnimating) |
|||
return _textColorTween.evaluate(_selectionControllers[index]); |
|||
if (widget.groupValue.Equals(currentKey)) |
|||
return _unselectedColor; |
|||
return _selectedColor; |
|||
} |
|||
|
|||
Color getBackgroundColor(int index, T currentKey) { |
|||
if (_selectionControllers[index].isAnimating) |
|||
return _childTweens[index].evaluate(_selectionControllers[index]); |
|||
if (widget.groupValue.Equals(currentKey)) |
|||
return _selectedColor; |
|||
if (_pressedKey.Equals(currentKey)) |
|||
return _pressedColor; |
|||
return _unselectedColor; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
List<Widget> _gestureChildren = new List<Widget>(); |
|||
List<Color> _backgroundColors = new List<Color>(); |
|||
int index = 0; |
|||
int selectedIndex = 0; |
|||
int pressedIndex = 0; |
|||
foreach ( T currentKey in widget.children.Keys) { |
|||
selectedIndex = (widget.groupValue.Equals(currentKey)) ? index : selectedIndex; |
|||
pressedIndex = (_pressedKey.Equals(currentKey)) ? index : pressedIndex; |
|||
TextStyle textStyle = DefaultTextStyle.of(context).style.copyWith( |
|||
color: getTextColor(index, currentKey) |
|||
); |
|||
IconThemeData iconTheme = new IconThemeData( |
|||
color: getTextColor(index, currentKey) |
|||
); |
|||
|
|||
Widget child = new Center( |
|||
child: widget.children[currentKey] |
|||
); |
|||
|
|||
child = new GestureDetector( |
|||
onTapDown: (TapDownDetails _event)=> { |
|||
_onTapDown(currentKey); |
|||
}, |
|||
onTapCancel: _onTapCancel, |
|||
onTap: ()=> { |
|||
_onTap(currentKey); |
|||
}, |
|||
child: new IconTheme( |
|||
data: iconTheme, |
|||
child: new DefaultTextStyle( |
|||
style: textStyle, |
|||
child: child /*Semantics( |
|||
button: true, |
|||
inMutuallyExclusiveGroup: true, |
|||
selected: widget.groupValue.Equals(currentKey), |
|||
child: child |
|||
)*/ |
|||
) |
|||
) |
|||
); |
|||
|
|||
_backgroundColors.Add(getBackgroundColor(index, currentKey)); |
|||
_gestureChildren.Add(child); |
|||
index += 1; |
|||
} |
|||
|
|||
Widget box = new _SegmentedControlRenderWidget<T>( |
|||
children: _gestureChildren, |
|||
selectedIndex: selectedIndex, |
|||
pressedIndex: pressedIndex, |
|||
backgroundColors: _backgroundColors, |
|||
borderColor: _borderColor |
|||
); |
|||
|
|||
return new Padding( |
|||
padding: widget.padding ?? CupertinoSegmentedControlsUtils._kHorizontalItemPadding, |
|||
child: new UnconstrainedBox( |
|||
constrainedAxis: Axis.horizontal, |
|||
child: box |
|||
) |
|||
); |
|||
} |
|||
} |
|||
public class _SegmentedControlRenderWidget<T> : MultiChildRenderObjectWidget { |
|||
public _SegmentedControlRenderWidget( |
|||
Key key = null, |
|||
List<Widget> children = null, |
|||
int? selectedIndex = null, |
|||
int? pressedIndex = null, |
|||
List<Color> backgroundColors = null, |
|||
Color borderColor = null |
|||
) : base( |
|||
key: key, |
|||
children: children ?? new List<Widget>() |
|||
) { |
|||
this.selectedIndex = selectedIndex; |
|||
this.pressedIndex = pressedIndex; |
|||
this.backgroundColors = backgroundColors; |
|||
this.borderColor = borderColor; |
|||
} |
|||
|
|||
public readonly int? selectedIndex; |
|||
public readonly int? pressedIndex; |
|||
public readonly List<Color> backgroundColors; |
|||
public readonly Color borderColor; |
|||
public override RenderObject createRenderObject(BuildContext context) { |
|||
return new _RenderSegmentedControl<T>( |
|||
textDirection: Directionality.of(context), |
|||
selectedIndex: selectedIndex, |
|||
pressedIndex: pressedIndex, |
|||
backgroundColors: backgroundColors, |
|||
borderColor: borderColor |
|||
); |
|||
} |
|||
public override void updateRenderObject(BuildContext context,RenderObject renderObject) { |
|||
renderObject = (_RenderSegmentedControl<T>) renderObject; |
|||
((_RenderSegmentedControl<T>) renderObject).textDirection = Directionality.of(context); |
|||
((_RenderSegmentedControl<T>) renderObject).selectedIndex = selectedIndex; |
|||
((_RenderSegmentedControl<T>) renderObject).pressedIndex = pressedIndex; |
|||
((_RenderSegmentedControl<T>) renderObject).backgroundColors = backgroundColors; |
|||
((_RenderSegmentedControl<T>) renderObject).borderColor = borderColor; |
|||
|
|||
} |
|||
} |
|||
|
|||
class _SegmentedControlContainerBoxParentData : ContainerBoxParentData<RenderBox> { |
|||
public RRect surroundingRect; |
|||
} |
|||
|
|||
public delegate RenderBox _NextChild(RenderBox child); |
|||
|
|||
class _RenderSegmentedControl<T> : RenderBoxContainerDefaultsMixinContainerRenderObjectMixinRenderBox<RenderBox, |
|||
ContainerBoxParentData<RenderBox>> { |
|||
public _RenderSegmentedControl( |
|||
int? selectedIndex = null, |
|||
int? pressedIndex = null, |
|||
TextDirection? textDirection = null, |
|||
List<Color> backgroundColors = null, |
|||
Color borderColor = null |
|||
) { |
|||
D.assert(textDirection != null); |
|||
_textDirection = textDirection; |
|||
_selectedIndex = selectedIndex; |
|||
_pressedIndex = pressedIndex; |
|||
_backgroundColors = backgroundColors; |
|||
_borderColor = borderColor; |
|||
} |
|||
|
|||
public int? selectedIndex { |
|||
get {return _selectedIndex; } |
|||
set { |
|||
if (_selectedIndex == value) { |
|||
return; |
|||
} |
|||
_selectedIndex = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
int? _selectedIndex; |
|||
|
|||
public int? pressedIndex { |
|||
get { |
|||
return _pressedIndex; |
|||
} |
|||
set { |
|||
if (_pressedIndex == value) { |
|||
return; |
|||
} |
|||
_pressedIndex = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
int? _pressedIndex; |
|||
|
|||
public TextDirection? textDirection { |
|||
get { |
|||
return _textDirection; |
|||
} |
|||
set { |
|||
if (_textDirection == value) { |
|||
return; |
|||
} |
|||
_textDirection = value; |
|||
markNeedsLayout(); |
|||
} |
|||
} |
|||
TextDirection? _textDirection; |
|||
|
|||
|
|||
public List<Color> backgroundColors { |
|||
get { |
|||
return _backgroundColors; |
|||
} |
|||
set { |
|||
if (_backgroundColors == value) { |
|||
return; |
|||
} |
|||
_backgroundColors = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
List<Color> _backgroundColors; |
|||
|
|||
|
|||
public Color borderColor { |
|||
get { |
|||
return _borderColor; |
|||
|
|||
} |
|||
set { |
|||
if (_borderColor == value) { |
|||
return; |
|||
} |
|||
_borderColor = value; |
|||
markNeedsPaint(); |
|||
|
|||
} |
|||
|
|||
} |
|||
Color _borderColor; |
|||
|
|||
|
|||
protected internal override float computeMinIntrinsicWidth(float height) { |
|||
RenderBox child = firstChild; |
|||
float minWidth = 0.0f; |
|||
while (child != null) { |
|||
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData; |
|||
float childWidth = child.getMinIntrinsicWidth(height); |
|||
minWidth = Mathf.Max(minWidth, childWidth); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
return minWidth * childCount; |
|||
} |
|||
|
|||
protected internal override float computeMaxIntrinsicWidth(float height) { |
|||
RenderBox child = firstChild; |
|||
float maxWidth = 0.0f; |
|||
while (child != null) { |
|||
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData; |
|||
float childWidth = child.getMaxIntrinsicWidth(height); |
|||
maxWidth = Mathf.Max(maxWidth, childWidth); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
return maxWidth * childCount; |
|||
} |
|||
|
|||
protected internal override float computeMinIntrinsicHeight(float width) { |
|||
RenderBox child = firstChild; |
|||
float minHeight = 0.0f; |
|||
while (child != null) { |
|||
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData; |
|||
float childHeight = child.getMinIntrinsicHeight(width); |
|||
minHeight = Mathf.Max(minHeight, childHeight); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
return minHeight; |
|||
} |
|||
protected internal override float computeMaxIntrinsicHeight(float width) { |
|||
RenderBox child = firstChild; |
|||
float maxHeight = 0.0f; |
|||
while (child != null) { |
|||
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData; |
|||
float childHeight = child.getMaxIntrinsicHeight(width); |
|||
maxHeight = Mathf.Max(maxHeight, childHeight); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
return maxHeight; |
|||
} |
|||
|
|||
public override float? computeDistanceToActualBaseline(TextBaseline baseline) { |
|||
return defaultComputeDistanceToHighestActualBaseline(baseline); |
|||
} |
|||
|
|||
public override void setupParentData(RenderObject child) { |
|||
child = (RenderBox) child; |
|||
if (!(child.parentData is _SegmentedControlContainerBoxParentData)) { |
|||
child.parentData = new _SegmentedControlContainerBoxParentData(); |
|||
} |
|||
} |
|||
|
|||
void _layoutRects(_NextChild nextChild, RenderBox leftChild, RenderBox rightChild) { |
|||
RenderBox child = leftChild; |
|||
float start = 0.0f; |
|||
while (child != null) { |
|||
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData; |
|||
Offset childOffset = new Offset(start, 0.0f); |
|||
childParentData.offset = childOffset; |
|||
Rect childRect = Rect.fromLTWH(start, 0.0f, child.size.width, child.size.height); |
|||
RRect rChildRect = null; |
|||
if (child == leftChild) { |
|||
rChildRect = RRect.fromRectAndCorners(childRect, topLeft: Radius.circular(3.0f), |
|||
bottomLeft: Radius.circular(3.0f)); |
|||
} else if (child == rightChild) { |
|||
rChildRect = RRect.fromRectAndCorners(childRect, topRight: Radius.circular(3.0f), |
|||
bottomRight: Radius.circular(3.0f)); |
|||
} else { |
|||
rChildRect = RRect.fromRectAndCorners(childRect,topRight:Radius.zero); |
|||
} |
|||
childParentData.surroundingRect = rChildRect; |
|||
start += child.size.width; |
|||
child = nextChild(child); |
|||
} |
|||
} |
|||
protected override void performLayout() { |
|||
BoxConstraints constraints = this.constraints; |
|||
float maxHeight = CupertinoSegmentedControlsUtils._kMinSegmentedControlHeight; |
|||
|
|||
float childWidth = constraints.minWidth / childCount; |
|||
foreach ( RenderBox child in getChildrenAsList()) { |
|||
childWidth = Mathf.Max(childWidth, child.getMaxIntrinsicWidth(float.PositiveInfinity)); |
|||
} |
|||
childWidth = Mathf.Min(childWidth, constraints.maxWidth / childCount); |
|||
|
|||
RenderBox child1 = firstChild; |
|||
while (child1 != null) { |
|||
float boxHeight = child1.getMaxIntrinsicHeight(childWidth); |
|||
maxHeight = Mathf.Max(maxHeight, boxHeight); |
|||
child1 = childAfter(child1); |
|||
} |
|||
|
|||
constraints.constrainHeight(maxHeight); |
|||
|
|||
BoxConstraints childConstraints = BoxConstraints.tightFor( |
|||
width: childWidth, |
|||
height: maxHeight |
|||
); |
|||
|
|||
child1 = firstChild; |
|||
while (child1 != null) { |
|||
child1.layout(childConstraints, parentUsesSize: true); |
|||
child1 = childAfter(child1); |
|||
} |
|||
|
|||
switch (textDirection) { |
|||
case TextDirection.rtl: |
|||
_layoutRects( |
|||
childBefore, |
|||
lastChild, |
|||
firstChild |
|||
); |
|||
break; |
|||
case TextDirection.ltr: |
|||
_layoutRects( |
|||
childAfter, |
|||
firstChild, |
|||
lastChild |
|||
); |
|||
break; |
|||
} |
|||
|
|||
size = constraints.constrain(new Size(childWidth * childCount, maxHeight)); |
|||
} |
|||
public override void paint(PaintingContext context, Offset offset) { |
|||
RenderBox child = firstChild; |
|||
int index = 0; |
|||
while (child != null) { |
|||
_paintChild(context, offset, child, index); |
|||
child = childAfter(child); |
|||
index += 1; |
|||
} |
|||
} |
|||
|
|||
void _paintChild(PaintingContext context, Offset offset, RenderBox child, int childIndex) { |
|||
D.assert(child != null); |
|||
|
|||
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData; |
|||
|
|||
context.canvas.drawRRect( |
|||
childParentData.surroundingRect.shift(offset), |
|||
new Paint(){ |
|||
color = backgroundColors[childIndex], |
|||
style = PaintingStyle.fill} |
|||
); |
|||
context.canvas.drawRRect( |
|||
childParentData.surroundingRect.shift(offset), |
|||
new Paint(){ |
|||
color = borderColor, |
|||
strokeWidth = 1.0f, |
|||
style = PaintingStyle.stroke} |
|||
); |
|||
|
|||
context.paintChild(child, childParentData.offset + offset); |
|||
} |
|||
|
|||
protected override bool hitTestChildren(BoxHitTestResult result, Offset position = null ) { |
|||
D.assert(position != null); |
|||
RenderBox child = lastChild; |
|||
while (child != null) { |
|||
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData; |
|||
if (childParentData.surroundingRect.contains(position)) { |
|||
Offset center = (Offset.zero & child.size).center; |
|||
return result.addWithRawTransform( |
|||
transform: MatrixUtils.forceToPoint(center), |
|||
position: center, |
|||
hitTest: (BoxHitTestResult result1, Offset position1) =>{ |
|||
D.assert(position1 == center); |
|||
return child.hitTest(result1, position: center); |
|||
} |
|||
); |
|||
} |
|||
child = childParentData.previousSibling; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net.Sockets; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.cupertino; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.widgets; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.physics; |
|||
using Unity.UIWidgets.rendering; |
|||
using UnityEngine; |
|||
using Color = Unity.UIWidgets.ui.Color; |
|||
using Rect = Unity.UIWidgets.ui.Rect; |
|||
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|||
|
|||
namespace Unity.UIWidgets.cupertino { |
|||
public class CupertinoSlidingSegmentedControlsUtils { |
|||
public static readonly EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(vertical: 2, horizontal: 3); |
|||
public static readonly Radius _kThumbRadius = Radius.circular(6.93f); |
|||
|
|||
public static readonly EdgeInsets _kThumbInsets = EdgeInsets.symmetric(horizontal: 1); |
|||
|
|||
|
|||
public const float _kMinSegmentedControlHeight = 28.0f; |
|||
|
|||
public static readonly Color _kSeparatorColor = new Color(0x4D8E8E93); |
|||
|
|||
public static readonly CupertinoDynamicColor _kThumbColor = CupertinoDynamicColor.withBrightness( |
|||
color: new Color(0xFFFFFFFF), |
|||
darkColor: new Color(0xFF636366) |
|||
); |
|||
|
|||
public static readonly EdgeInsets _kSeparatorInset = EdgeInsets.symmetric(vertical: 6); |
|||
public const float _kSeparatorWidth = 1; |
|||
public static readonly Radius _kSeparatorRadius = Radius.circular(_kSeparatorWidth/2); |
|||
|
|||
|
|||
public const float _kMinThumbScale = 0.95f; |
|||
|
|||
public const float _kSegmentMinPadding = 9.25f; |
|||
|
|||
public const float _kTouchYDistanceThreshold = 50.0f * 50.0f; |
|||
|
|||
public const float _kCornerRadius = 8; |
|||
|
|||
public static readonly SpringSimulation _kThumbSpringAnimationSimulation = new SpringSimulation( |
|||
new SpringDescription(mass: 1, stiffness: 503.551f, damping: 44.8799f), |
|||
0, |
|||
1, |
|||
0 // Everytime a new spring animation starts the previous animation stops.
|
|||
); |
|||
|
|||
public static readonly TimeSpan _kSpringAnimationDuration= TimeSpan.FromMilliseconds(412); |
|||
|
|||
public static readonly TimeSpan _kOpacityAnimationDuration = TimeSpan.FromMilliseconds(470); |
|||
|
|||
public static readonly TimeSpan _kHighlightAnimationDuration = TimeSpan.FromMilliseconds(200); |
|||
} |
|||
|
|||
class _FontWeightTween : Tween<FontWeight> { |
|||
public _FontWeightTween(FontWeight begin = null, FontWeight end = null) : base(begin: begin, end: end) { |
|||
} |
|||
|
|||
public override FontWeight lerp(float t) => FontWeight.lerp(begin, end, t); |
|||
} |
|||
|
|||
public class CupertinoSlidingSegmentedControl<T> : StatefulWidget { |
|||
|
|||
public CupertinoSlidingSegmentedControl( |
|||
Key key = null, |
|||
Dictionary<T, Widget> children = null, |
|||
ValueChanged<T> onValueChanged = null, |
|||
T groupValue = default, |
|||
Color thumbColor = null, |
|||
EdgeInsetsGeometry padding = null, |
|||
Color backgroundColor = null |
|||
) :base(key: key) |
|||
{ |
|||
D.assert(children != null); |
|||
D.assert(children.Count >= 2); |
|||
D.assert(padding != null); |
|||
D.assert(onValueChanged != null); |
|||
D.assert( |
|||
groupValue == null || children.Keys.Contains(groupValue),()=> |
|||
"The groupValue must be either null or one of the keys in the children map." |
|||
); |
|||
this.children = children; |
|||
this.onValueChanged = onValueChanged; |
|||
this.groupValue = groupValue; |
|||
this.thumbColor = thumbColor ?? CupertinoSlidingSegmentedControlsUtils._kThumbColor; |
|||
this.padding = padding ??CupertinoSlidingSegmentedControlsUtils._kHorizontalItemPadding; |
|||
this.backgroundColor = backgroundColor ?? CupertinoColors.tertiarySystemFill; |
|||
} |
|||
public readonly Dictionary<T, Widget> children; |
|||
public readonly T groupValue; |
|||
public readonly ValueChanged<T> onValueChanged; |
|||
public readonly Color backgroundColor; |
|||
public readonly Color thumbColor; |
|||
public readonly EdgeInsetsGeometry padding; |
|||
|
|||
public override State createState() { |
|||
return new _SlidingSegmentedControlState<T>(); |
|||
} |
|||
} |
|||
public class _SlidingSegmentedControlState<T> : TickerProviderStateMixin<CupertinoSlidingSegmentedControl<T>> { |
|||
|
|||
public readonly Dictionary<T, AnimationController> _highlightControllers = new Dictionary<T, AnimationController>(); |
|||
public readonly Tween<FontWeight> _highlightTween = new _FontWeightTween(begin: FontWeight.normal, end: FontWeight.w500); |
|||
public readonly Dictionary<T, AnimationController> _pressControllers = new Dictionary<T, AnimationController>(); |
|||
public readonly Tween<float> _pressTween = new Tween<float>(begin: 1, end: 0.2f); |
|||
|
|||
List<T> keys; |
|||
|
|||
public AnimationController thumbController; |
|||
public AnimationController separatorOpacityController; |
|||
public AnimationController thumbScaleController; |
|||
|
|||
public readonly TapGestureRecognizer tap = new TapGestureRecognizer(); |
|||
public readonly HorizontalDragGestureRecognizer drag = new HorizontalDragGestureRecognizer(); |
|||
public readonly LongPressGestureRecognizer longPress = new LongPressGestureRecognizer(); |
|||
|
|||
AnimationController _createHighlightAnimationController( bool isCompleted = false ) { |
|||
return new AnimationController( |
|||
duration: CupertinoSlidingSegmentedControlsUtils._kHighlightAnimationDuration, |
|||
value: isCompleted ? 1 : 0, |
|||
vsync: this |
|||
); |
|||
} |
|||
AnimationController _createFadeoutAnimationController() { |
|||
return new AnimationController( |
|||
duration:CupertinoSlidingSegmentedControlsUtils._kOpacityAnimationDuration, |
|||
vsync: this |
|||
); |
|||
} |
|||
public override void initState() { |
|||
base.initState(); |
|||
|
|||
GestureArenaTeam team = new GestureArenaTeam(); |
|||
|
|||
longPress.team = team; |
|||
drag.team = team; |
|||
team.captain = drag; |
|||
|
|||
_highlighted = widget.groupValue; |
|||
|
|||
thumbController = new AnimationController( |
|||
duration: CupertinoSlidingSegmentedControlsUtils._kSpringAnimationDuration, |
|||
value: 0, |
|||
vsync: this |
|||
); |
|||
|
|||
thumbScaleController = new AnimationController( |
|||
duration: CupertinoSlidingSegmentedControlsUtils._kSpringAnimationDuration, |
|||
value: 1, |
|||
vsync: this |
|||
); |
|||
|
|||
separatorOpacityController = new AnimationController( |
|||
duration: CupertinoSlidingSegmentedControlsUtils._kSpringAnimationDuration, |
|||
value: 0, |
|||
vsync: this |
|||
); |
|||
|
|||
foreach (T currentKey in widget.children.Keys) { |
|||
_highlightControllers[currentKey] = _createHighlightAnimationController( |
|||
isCompleted: currentKey.Equals(widget.groupValue) // Highlight the current selection.
|
|||
); |
|||
_pressControllers[currentKey] = _createFadeoutAnimationController(); |
|||
} |
|||
} |
|||
|
|||
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|||
oldWidget = (CupertinoSlidingSegmentedControl<T>)oldWidget; |
|||
base.didUpdateWidget(oldWidget); |
|||
|
|||
// Update animation controllers.
|
|||
foreach( T oldKey in ((CupertinoSlidingSegmentedControl<T>)oldWidget).children.Keys) { |
|||
if (!widget.children.ContainsKey(oldKey)) { |
|||
_highlightControllers[oldKey].dispose(); |
|||
_pressControllers[oldKey].dispose(); |
|||
|
|||
_highlightControllers.Remove(oldKey); |
|||
_pressControllers.Remove(oldKey); |
|||
} |
|||
} |
|||
|
|||
foreach( T newKey in widget.children.Keys) { |
|||
if (!_highlightControllers.Keys.Contains(newKey)) { |
|||
_highlightControllers[newKey] = _createHighlightAnimationController(); |
|||
_pressControllers[newKey] = _createFadeoutAnimationController(); |
|||
} |
|||
} |
|||
|
|||
highlighted = widget.groupValue; |
|||
} |
|||
public override void dispose() { |
|||
foreach( AnimationController animationController in _highlightControllers.Values) { |
|||
animationController.dispose(); |
|||
} |
|||
|
|||
foreach( AnimationController animationController in _pressControllers.Values) { |
|||
animationController.dispose(); |
|||
} |
|||
|
|||
thumbScaleController.dispose(); |
|||
thumbController.dispose(); |
|||
separatorOpacityController.dispose(); |
|||
|
|||
drag.dispose(); |
|||
tap.dispose(); |
|||
longPress.dispose(); |
|||
|
|||
base.dispose(); |
|||
} |
|||
|
|||
void _animateHighlightController( T at = default, bool forward = false) { |
|||
if (at == null) |
|||
return; |
|||
AnimationController controller = _highlightControllers[at]; |
|||
D.assert(!forward || controller != null); |
|||
controller?.animateTo(forward ? 1 : 0, duration: CupertinoSlidingSegmentedControlsUtils._kHighlightAnimationDuration, curve: Curves.ease); |
|||
} |
|||
T _highlighted; |
|||
public T highlighted{ |
|||
set { |
|||
if (_highlighted.Equals(value)) |
|||
return; |
|||
_animateHighlightController(at: value, forward: true); |
|||
_animateHighlightController(at: _highlighted, forward: false); |
|||
_highlighted = value; |
|||
} |
|||
|
|||
} |
|||
T _pressed; |
|||
public T pressed{ |
|||
set { |
|||
if (_pressed.Equals(value)) |
|||
return; |
|||
|
|||
if (_pressed != null) { |
|||
_pressControllers[_pressed]?.animateTo(0, duration: CupertinoSlidingSegmentedControlsUtils._kOpacityAnimationDuration, curve: Curves.ease); |
|||
} |
|||
if (!value.Equals(_highlighted)&& value != null) { |
|||
_pressControllers[value].animateTo(1, duration: CupertinoSlidingSegmentedControlsUtils._kOpacityAnimationDuration, curve: Curves.ease); |
|||
} |
|||
_pressed = value; |
|||
} |
|||
} |
|||
|
|||
public void didChangeSelectedViaGesture() { |
|||
widget.onValueChanged(_highlighted); |
|||
} |
|||
|
|||
public T indexToKey(int index) { |
|||
return index == null ? default : keys[index]; |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
WidgetsD.debugCheckHasDirectionality(context); |
|||
|
|||
switch (Directionality.of(context)) { |
|||
case TextDirection.ltr: |
|||
keys = widget.children.Keys.ToList(); |
|||
break; |
|||
case TextDirection.rtl: |
|||
widget.children.Keys.ToList().Reverse(); |
|||
keys = widget.children.Keys.ToList(); |
|||
break; |
|||
} |
|||
List<Listenable> results = new List<Listenable>(); |
|||
results.AddRange(_highlightControllers.Values); |
|||
results.AddRange(_pressControllers.Values); |
|||
return new AnimatedBuilder( |
|||
animation: ListenableUtils.merge(results), |
|||
builder: (BuildContext context1, Widget child1) =>{ |
|||
List<Widget> children = new List<Widget>(); |
|||
foreach( T currentKey in keys){ |
|||
TextStyle textStyle = DefaultTextStyle.of(context1).style.copyWith( |
|||
fontWeight: _highlightTween.evaluate(_highlightControllers[currentKey]) |
|||
); |
|||
|
|||
Widget child = new DefaultTextStyle( |
|||
style: textStyle, |
|||
child: |
|||
new Opacity( |
|||
opacity: _pressTween.evaluate(_pressControllers[currentKey]), |
|||
child: new MetaData( |
|||
behavior: HitTestBehavior.opaque, |
|||
child: new Center(child: widget.children[currentKey]) |
|||
) |
|||
) |
|||
); |
|||
|
|||
children.Add(child); |
|||
} |
|||
|
|||
int selectedIndex = widget.groupValue.Equals(default) ? 0 : keys.IndexOf(widget.groupValue); |
|||
|
|||
Widget box = new _SlidingSegmentedControlRenderWidget<T>( |
|||
children: children, |
|||
selectedIndex: selectedIndex, |
|||
thumbColor: CupertinoDynamicColor.resolve(widget.thumbColor, context), |
|||
state: this |
|||
); |
|||
return new UnconstrainedBox( |
|||
constrainedAxis: Axis.horizontal, |
|||
child: new Container( |
|||
padding: widget.padding.resolve(Directionality.of(context)), |
|||
decoration: new BoxDecoration( |
|||
borderRadius: BorderRadius.all(Radius.circular(CupertinoSlidingSegmentedControlsUtils._kCornerRadius)), |
|||
color: CupertinoDynamicColor.resolve(widget.backgroundColor, context) |
|||
), |
|||
child: box |
|||
) |
|||
); |
|||
} |
|||
); |
|||
} |
|||
} |
|||
public class _SlidingSegmentedControlRenderWidget<T> : MultiChildRenderObjectWidget { |
|||
public _SlidingSegmentedControlRenderWidget( |
|||
Key key = null, |
|||
List<Widget> children = null, |
|||
int? selectedIndex = null, |
|||
Color thumbColor = null, |
|||
_SlidingSegmentedControlState<T> state = null |
|||
) : base(key: key, children: children ?? new List<Widget>()) { |
|||
this.selectedIndex = selectedIndex; |
|||
this.thumbColor = thumbColor; |
|||
this.state = state; |
|||
} |
|||
|
|||
public readonly int? selectedIndex; |
|||
public readonly Color thumbColor; |
|||
public readonly _SlidingSegmentedControlState<T> state; |
|||
public override RenderObject createRenderObject(BuildContext context) { |
|||
return new _RenderSlidingSegmentedControl<T>( |
|||
selectedIndex: selectedIndex, |
|||
thumbColor: CupertinoDynamicColor.resolve(thumbColor, context), |
|||
state: state |
|||
); |
|||
} |
|||
public override void updateRenderObject(BuildContext context,RenderObject renderObject) { |
|||
renderObject = (_RenderSlidingSegmentedControl<T>) renderObject; |
|||
((_RenderSlidingSegmentedControl<T>)renderObject).thumbColor = CupertinoDynamicColor.resolve(thumbColor, context); |
|||
((_RenderSlidingSegmentedControl<T>)renderObject).guardedSetHighlightedIndex(selectedIndex ?? 0); |
|||
} |
|||
} |
|||
public class _ChildAnimationManifest { |
|||
public _ChildAnimationManifest( |
|||
float opacity = 1f, |
|||
float separatorOpacity = 0f) { |
|||
D.assert(separatorOpacity != null); |
|||
D.assert(opacity != null); |
|||
separatorTween = new Tween<float>(begin: separatorOpacity, end: separatorOpacity); |
|||
opacityTween = new Tween<float>(begin: opacity, end: opacity); |
|||
} |
|||
float opacity; |
|||
Tween<float> opacityTween; |
|||
public float separatorOpacity; |
|||
public Tween<float> separatorTween; |
|||
} |
|||
class _SlidingSegmentedControlContainerBoxParentData : ContainerBoxParentData<RenderBox> { } |
|||
|
|||
public class _RenderSlidingSegmentedControl<T> : RenderBoxContainerDefaultsMixinContainerRenderObjectMixinRenderBox<RenderBox, ContainerBoxParentData<RenderBox>> { |
|||
public _RenderSlidingSegmentedControl( |
|||
int? selectedIndex = null, |
|||
Color thumbColor = null, |
|||
_SlidingSegmentedControlState<T> state = null |
|||
) { |
|||
highlightedIndex = selectedIndex; |
|||
_thumbColor = thumbColor; |
|||
D.assert(state != null); |
|||
state.drag.onDown = _onDown; |
|||
state.drag.onUpdate = _onUpdate; |
|||
state.drag.onEnd = _onEnd; |
|||
state.drag.onCancel = _onCancel; |
|||
state.tap.onTapUp = _onTapUp; |
|||
state.longPress.onLongPress = ()=> { }; |
|||
} |
|||
public readonly _SlidingSegmentedControlState<T> state; |
|||
|
|||
Dictionary<RenderBox, _ChildAnimationManifest> _childAnimations = new Dictionary<RenderBox, _ChildAnimationManifest>{}; |
|||
|
|||
|
|||
Rect currentThumbRect; |
|||
|
|||
Tween<Rect> _currentThumbTween; |
|||
|
|||
Tween<float> _thumbScaleTween = new Tween<float>(begin: CupertinoSlidingSegmentedControlsUtils._kMinThumbScale, end: 1); |
|||
float currentThumbScale = 1; |
|||
|
|||
|
|||
Offset _localDragOffset; |
|||
|
|||
bool _startedOnSelectedSegment; |
|||
|
|||
public override void insert(RenderBox child, RenderBox after = null) { |
|||
base.insert(child, after: after); |
|||
if (_childAnimations == null) |
|||
return; |
|||
|
|||
D.assert(_childAnimations[child] == null); |
|||
_childAnimations[child] = new _ChildAnimationManifest(separatorOpacity: 1); |
|||
} |
|||
|
|||
public override void remove(RenderBox child) { |
|||
base.remove(child); |
|||
_childAnimations?.Remove(child); |
|||
} |
|||
|
|||
public override void attach(object owner) { |
|||
owner = (PipelineOwner) owner; |
|||
base.attach(owner); |
|||
state.thumbController.addListener(markNeedsPaint); |
|||
state.thumbScaleController.addListener(markNeedsPaint); |
|||
state.separatorOpacityController.addListener(markNeedsPaint); |
|||
} |
|||
|
|||
public override void detach() { |
|||
state.thumbController.removeListener(markNeedsPaint); |
|||
state.thumbScaleController.removeListener(markNeedsPaint); |
|||
state.separatorOpacityController.removeListener(markNeedsPaint); |
|||
base.detach(); |
|||
} |
|||
|
|||
|
|||
bool _needsThumbAnimationUpdate = false; |
|||
|
|||
int? highlightedIndex { |
|||
get { |
|||
return _highlightedIndex; |
|||
} |
|||
set { |
|||
if (_highlightedIndex == value) { |
|||
return; |
|||
} |
|||
|
|||
_needsThumbAnimationUpdate = true; |
|||
_highlightedIndex = value; |
|||
|
|||
state.thumbController.animateWith(CupertinoSlidingSegmentedControlsUtils._kThumbSpringAnimationSimulation); |
|||
|
|||
state.separatorOpacityController.reset(); |
|||
state.separatorOpacityController.animateTo( |
|||
1, |
|||
duration: CupertinoSlidingSegmentedControlsUtils._kSpringAnimationDuration, |
|||
curve: Curves.ease |
|||
); |
|||
|
|||
state.highlighted = state.indexToKey(value??0); |
|||
markNeedsPaint(); |
|||
//markNeedsSemanticsUpdate();
|
|||
} |
|||
} |
|||
int? _highlightedIndex; |
|||
|
|||
|
|||
public void guardedSetHighlightedIndex(int value) { |
|||
// Ignore set highlightedIndex when the user is dragging the thumb around.
|
|||
if (_startedOnSelectedSegment == true) |
|||
return; |
|||
highlightedIndex = value; |
|||
} |
|||
|
|||
int? pressedIndex { |
|||
get { |
|||
return _pressedIndex; |
|||
} |
|||
set { |
|||
if (_pressedIndex == value) { |
|||
return; |
|||
} |
|||
|
|||
D.assert(value == null || (value >= 0 && value < childCount)); |
|||
|
|||
_pressedIndex = value; |
|||
state.pressed = state.indexToKey(value ?? 0); |
|||
} |
|||
} |
|||
int? _pressedIndex; |
|||
|
|||
|
|||
public Color thumbColor { |
|||
get { |
|||
return _thumbColor; |
|||
} |
|||
set { |
|||
if (_thumbColor == value) { |
|||
return; |
|||
} |
|||
_thumbColor = value; |
|||
markNeedsPaint(); |
|||
} |
|||
} |
|||
Color _thumbColor; |
|||
|
|||
float totalSeparatorWidth { |
|||
get { |
|||
return (CupertinoSlidingSegmentedControlsUtils._kSeparatorInset.horizontal + CupertinoSlidingSegmentedControlsUtils._kSeparatorWidth) * (childCount - 1); |
|||
} |
|||
} |
|||
|
|||
public override void handleEvent(PointerEvent _event, HitTestEntry entry) { |
|||
entry = (BoxHitTestEntry) entry; |
|||
|
|||
D.assert(debugHandleEvent(_event, entry)); |
|||
if (_event is PointerDownEvent) { |
|||
state.tap.addPointer((PointerDownEvent)_event); |
|||
state.longPress.addPointer((PointerDownEvent)_event); |
|||
state.drag.addPointer((PointerDownEvent)_event); |
|||
} |
|||
} |
|||
|
|||
int? indexFromLocation(Offset location) { |
|||
return childCount == 0 |
|||
? (int?) null |
|||
// This assumes all children have the same width.
|
|||
: (int)((location.dx / (size.width / childCount)) |
|||
.floor() |
|||
.clamp(0, childCount - 1) ); |
|||
} |
|||
|
|||
void _onTapUp(TapUpDetails details) { |
|||
highlightedIndex = indexFromLocation(details.localPosition); |
|||
state.didChangeSelectedViaGesture(); |
|||
} |
|||
|
|||
void _onDown(DragDownDetails details) { |
|||
D.assert(size.contains(details.localPosition)); |
|||
_localDragOffset = details.localPosition; |
|||
int? index = indexFromLocation(_localDragOffset); |
|||
_startedOnSelectedSegment = index == highlightedIndex; |
|||
pressedIndex = index; |
|||
|
|||
if (_startedOnSelectedSegment) { |
|||
_playThumbScaleAnimation(isExpanding: false); |
|||
} |
|||
} |
|||
|
|||
void _onUpdate(DragUpdateDetails details) { |
|||
_localDragOffset = details.localPosition; |
|||
int? newIndex = indexFromLocation(_localDragOffset); |
|||
|
|||
if (_startedOnSelectedSegment) { |
|||
highlightedIndex = newIndex; |
|||
pressedIndex = newIndex; |
|||
} else { |
|||
pressedIndex = _hasDraggedTooFar(details) ? 0 : newIndex; |
|||
} |
|||
} |
|||
|
|||
void _onEnd(DragEndDetails details) { |
|||
if (_startedOnSelectedSegment) { |
|||
_playThumbScaleAnimation(isExpanding: true); |
|||
state.didChangeSelectedViaGesture(); |
|||
} |
|||
|
|||
if (pressedIndex != null) { |
|||
highlightedIndex = pressedIndex; |
|||
state.didChangeSelectedViaGesture(); |
|||
} |
|||
pressedIndex = null; |
|||
_localDragOffset = null; |
|||
_startedOnSelectedSegment = false; |
|||
} |
|||
|
|||
void _onCancel() { |
|||
if (_startedOnSelectedSegment) { |
|||
_playThumbScaleAnimation(isExpanding: true); |
|||
} |
|||
|
|||
_localDragOffset = null; |
|||
pressedIndex = null; |
|||
_startedOnSelectedSegment = false; |
|||
} |
|||
|
|||
void _playThumbScaleAnimation(bool isExpanding = false) { |
|||
D.assert(isExpanding != null); |
|||
_thumbScaleTween = new Tween<float>(begin: currentThumbScale, end: isExpanding ? 1 : CupertinoSlidingSegmentedControlsUtils._kMinThumbScale); |
|||
state.thumbScaleController.animateWith(CupertinoSlidingSegmentedControlsUtils._kThumbSpringAnimationSimulation); |
|||
} |
|||
|
|||
bool _hasDraggedTooFar(DragUpdateDetails details) { |
|||
Offset offCenter = details.localPosition - new Offset(size.width/2, size.height/2); |
|||
return Mathf.Pow(Mathf.Max(0, offCenter.dx.abs() - size.width/2), 2) + Mathf.Pow(Mathf.Max(0, offCenter.dy.abs() - size.height/2), 2) > CupertinoSlidingSegmentedControlsUtils._kTouchYDistanceThreshold; |
|||
} |
|||
|
|||
protected internal override float computeMinIntrinsicWidth(float height) { |
|||
RenderBox child = firstChild; |
|||
float maxMinChildWidth = 0; |
|||
while (child != null) { |
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
child.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
float childWidth = child.getMinIntrinsicWidth(height); |
|||
maxMinChildWidth = Math.Max(maxMinChildWidth, childWidth); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
return (maxMinChildWidth + 2 * CupertinoSlidingSegmentedControlsUtils._kSegmentMinPadding) * childCount + totalSeparatorWidth; |
|||
} |
|||
|
|||
protected internal override float computeMaxIntrinsicWidth(float height) { |
|||
RenderBox child = firstChild; |
|||
float maxMaxChildWidth = 0; |
|||
while (child != null) { |
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
child.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
float childWidth = child.getMaxIntrinsicWidth(height); |
|||
maxMaxChildWidth = Mathf.Max(maxMaxChildWidth, childWidth); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
return (maxMaxChildWidth + 2 * CupertinoSlidingSegmentedControlsUtils._kSegmentMinPadding) * childCount + totalSeparatorWidth; |
|||
} |
|||
|
|||
protected internal override float computeMinIntrinsicHeight(float width) { |
|||
RenderBox child = firstChild; |
|||
float maxMinChildHeight = 0; |
|||
while (child != null) { |
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
child.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
float childHeight = child.getMinIntrinsicHeight(width); |
|||
maxMinChildHeight = Mathf.Max(maxMinChildHeight, childHeight); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
return maxMinChildHeight; |
|||
} |
|||
|
|||
protected internal override float computeMaxIntrinsicHeight(float width) { |
|||
RenderBox child = firstChild; |
|||
float maxMaxChildHeight = 0; |
|||
while (child != null) { |
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
child.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
float childHeight = child.getMaxIntrinsicHeight(width); |
|||
maxMaxChildHeight = Mathf.Max(maxMaxChildHeight, childHeight); |
|||
child = childParentData.nextSibling; |
|||
} |
|||
return maxMaxChildHeight; |
|||
} |
|||
|
|||
public override float? computeDistanceToActualBaseline(TextBaseline baseline) { |
|||
return defaultComputeDistanceToHighestActualBaseline(baseline); |
|||
} |
|||
|
|||
public override void setupParentData(RenderObject child) { |
|||
child = (RenderBox) child; |
|||
if (!(child.parentData is _SlidingSegmentedControlContainerBoxParentData)) { |
|||
child.parentData = new _SlidingSegmentedControlContainerBoxParentData(); |
|||
} |
|||
} |
|||
|
|||
protected override void performLayout() { |
|||
BoxConstraints constraints = this.constraints; |
|||
float childWidth = (constraints.minWidth - totalSeparatorWidth) / childCount; |
|||
float maxHeight = CupertinoSlidingSegmentedControlsUtils._kMinSegmentedControlHeight; |
|||
|
|||
foreach( RenderBox child1 in getChildrenAsList()) { |
|||
childWidth = Mathf.Max(childWidth, child1.getMaxIntrinsicWidth(float.PositiveInfinity) + 2 * CupertinoSlidingSegmentedControlsUtils._kSegmentMinPadding); |
|||
} |
|||
|
|||
childWidth = Mathf.Min( |
|||
childWidth, |
|||
(constraints.maxWidth - totalSeparatorWidth) / childCount |
|||
); |
|||
|
|||
RenderBox child = firstChild; |
|||
while (child != null) { |
|||
float boxHeight = child.getMaxIntrinsicHeight(childWidth); |
|||
maxHeight = Mathf.Max(maxHeight, boxHeight); |
|||
child = childAfter(child); |
|||
} |
|||
|
|||
constraints.constrainHeight(maxHeight); |
|||
|
|||
BoxConstraints childConstraints = BoxConstraints.tightFor( |
|||
width: childWidth, |
|||
height: maxHeight |
|||
); |
|||
|
|||
// Layout children.
|
|||
child = firstChild; |
|||
while (child != null) { |
|||
child.layout(childConstraints, parentUsesSize: true); |
|||
child = childAfter(child); |
|||
} |
|||
|
|||
float start = 0; |
|||
child = firstChild; |
|||
|
|||
while (child != null) { |
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
child.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
Offset childOffset = new Offset(start, 0); |
|||
childParentData.offset = childOffset; |
|||
start += child.size.width + CupertinoSlidingSegmentedControlsUtils._kSeparatorWidth + CupertinoSlidingSegmentedControlsUtils._kSeparatorInset.horizontal; |
|||
child = childAfter(child); |
|||
} |
|||
|
|||
size = constraints.constrain(new Size(childWidth * childCount + totalSeparatorWidth, maxHeight)); |
|||
} |
|||
|
|||
public override void paint(PaintingContext context, Offset offset) { |
|||
List<RenderBox> children = getChildrenAsList(); |
|||
|
|||
// Paint thumb if highlightedIndex is not null.
|
|||
if (highlightedIndex != null) { |
|||
if (_childAnimations == null) { |
|||
_childAnimations = new Dictionary<RenderBox, _ChildAnimationManifest>();//<RenderBox, _ChildAnimationManifest> { };
|
|||
for (int i = 0; i < childCount - 1; i += 1) { |
|||
bool shouldFadeOut = i == highlightedIndex || i == highlightedIndex - 1; |
|||
RenderBox child = children[i]; |
|||
_childAnimations[child] = new _ChildAnimationManifest(separatorOpacity: shouldFadeOut ? 0 : 1); |
|||
} |
|||
} |
|||
|
|||
RenderBox selectedChild = children[highlightedIndex ?? 0]; |
|||
|
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
selectedChild.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
Rect unscaledThumbTargetRect = CupertinoSlidingSegmentedControlsUtils._kThumbInsets.inflateRect(childParentData.offset & selectedChild.size); |
|||
|
|||
// Update related Tweens before animation update phase.
|
|||
if (_needsThumbAnimationUpdate) { |
|||
// Needs to ensure _currentThumbRect is valid.
|
|||
_currentThumbTween = new RectTween(begin: currentThumbRect ?? unscaledThumbTargetRect, end: unscaledThumbTargetRect); |
|||
|
|||
for (int i = 0; i < childCount - 1; i += 1) { |
|||
|
|||
bool shouldFadeOut = i == highlightedIndex || i == highlightedIndex - 1; |
|||
RenderBox child = children[i]; |
|||
_ChildAnimationManifest manifest = _childAnimations[child]; |
|||
D.assert(manifest != null); |
|||
manifest.separatorTween = new Tween<float>( |
|||
begin: manifest.separatorOpacity, |
|||
end: shouldFadeOut ? 0 : 1 |
|||
); |
|||
} |
|||
|
|||
_needsThumbAnimationUpdate = false; |
|||
} else if (_currentThumbTween != null && unscaledThumbTargetRect != _currentThumbTween.begin) { |
|||
_currentThumbTween = new RectTween(begin: _currentThumbTween.begin, end: unscaledThumbTargetRect); |
|||
} |
|||
|
|||
for (int index = 0; index < childCount - 1; index += 1) { |
|||
_paintSeparator(context, offset, children[index]); |
|||
} |
|||
|
|||
currentThumbRect = _currentThumbTween?.evaluate(state.thumbController) |
|||
?? unscaledThumbTargetRect; |
|||
|
|||
currentThumbScale = _thumbScaleTween.evaluate(state.thumbScaleController); |
|||
|
|||
Rect thumbRect = Rect.fromCenter( |
|||
center: currentThumbRect.center, |
|||
width: currentThumbRect.width * currentThumbScale, |
|||
height: currentThumbRect.height * currentThumbScale |
|||
); |
|||
|
|||
_paintThumb(context, offset, thumbRect); |
|||
} else { |
|||
// Reset all animations when there"s no thumb.
|
|||
currentThumbRect = null; |
|||
_childAnimations = null; |
|||
|
|||
for (int index = 0; index < childCount - 1; index += 1) { |
|||
_paintSeparator(context, offset, children[index]); |
|||
} |
|||
} |
|||
|
|||
for (int index = 0; index < children.Count; index++) { |
|||
_paintChild(context, offset, children[index], index); |
|||
} |
|||
} |
|||
|
|||
// Paint the separator to the right of the given child.
|
|||
void _paintSeparator(PaintingContext context, Offset offset, RenderBox child) { |
|||
D.assert(child != null); |
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
child.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
|
|||
Paint paint = new Paint(); |
|||
|
|||
_ChildAnimationManifest manifest = _childAnimations == null ? null : _childAnimations[child]; |
|||
float opacity = manifest?.separatorTween?.evaluate(state.separatorOpacityController) ?? 1; |
|||
if(manifest != null) |
|||
manifest.separatorOpacity = opacity; |
|||
paint.color = CupertinoSlidingSegmentedControlsUtils._kSeparatorColor.withOpacity(CupertinoSlidingSegmentedControlsUtils._kSeparatorColor.opacity * opacity); |
|||
|
|||
Rect childRect = (childParentData.offset + offset) & child.size; |
|||
Rect separatorRect = CupertinoSlidingSegmentedControlsUtils._kSeparatorInset.deflateRect( |
|||
childRect.topRight & new Size(CupertinoSlidingSegmentedControlsUtils._kSeparatorInset.horizontal + CupertinoSlidingSegmentedControlsUtils._kSeparatorWidth, |
|||
child.size.height) |
|||
); |
|||
|
|||
context.canvas.drawRRect( |
|||
RRect.fromRectAndRadius(separatorRect, CupertinoSlidingSegmentedControlsUtils._kSeparatorRadius), |
|||
paint |
|||
); |
|||
} |
|||
|
|||
void _paintChild(PaintingContext context, Offset offset, RenderBox child, int childIndex) { |
|||
D.assert(child != null); |
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
child.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
context.paintChild(child, childParentData.offset + offset); |
|||
} |
|||
|
|||
void _paintThumb(PaintingContext context, Offset offset, Rect thumbRect) { |
|||
List<BoxShadow> thumbShadow = new List<BoxShadow> { |
|||
new BoxShadow( |
|||
color: new Color(0x1F000000), |
|||
offset: new Offset(0, 3), |
|||
blurRadius: 8 |
|||
), |
|||
new BoxShadow( |
|||
color: new Color(0x0A000000), |
|||
offset: new Offset(0, 3), |
|||
blurRadius: 1 |
|||
) |
|||
}; |
|||
|
|||
RRect thumbRRect = RRect.fromRectAndRadius(thumbRect.shift(offset), CupertinoSlidingSegmentedControlsUtils._kThumbRadius); |
|||
|
|||
foreach( BoxShadow shadow in thumbShadow) { |
|||
context.canvas.drawRRect(thumbRRect.shift(shadow.offset), shadow.toPaint()); |
|||
} |
|||
|
|||
context.canvas.drawRRect( |
|||
thumbRRect.inflate(0.5f), |
|||
new Paint(){color = new Color(0x0A000000)} |
|||
); |
|||
|
|||
context.canvas.drawRRect( |
|||
thumbRRect, |
|||
new Paint(){color = thumbColor} |
|||
|
|||
); |
|||
} |
|||
|
|||
protected override bool hitTestChildren(BoxHitTestResult result, Offset position ) { |
|||
D.assert(position != null); |
|||
RenderBox child = lastChild; |
|||
while (child != null) { |
|||
_SlidingSegmentedControlContainerBoxParentData childParentData = |
|||
child.parentData as _SlidingSegmentedControlContainerBoxParentData; |
|||
if ((childParentData.offset & child.size).contains(position)) { |
|||
Offset center = (Offset.zero & child.size).center; |
|||
return result.addWithRawTransform( |
|||
transform: MatrixUtils.forceToPoint(center), |
|||
position: center, |
|||
hitTest: (BoxHitTestResult result1, Offset position1)=> { |
|||
D.assert(position == center); |
|||
return child.hitTest(result1, position: center); |
|||
} |
|||
); |
|||
} |
|||
child = childParentData.previousSibling; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
撰写
预览
正在加载...
取消
保存
Reference in new issue