您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
944 行
33 KiB
944 行
33 KiB
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using uiwidgets;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.gestures;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.rendering;
|
|
using Unity.UIWidgets.service;
|
|
using Unity.UIWidgets.ui;
|
|
using Unity.UIWidgets.widgets;
|
|
using UnityEngine;
|
|
using Color = Unity.UIWidgets.ui.Color;
|
|
using TextStyle = Unity.UIWidgets.painting.TextStyle;
|
|
|
|
namespace Unity.UIWidgets.material {
|
|
public enum ListTileStyle {
|
|
list,
|
|
drawer
|
|
}
|
|
|
|
public class ListTileTheme : InheritedTheme {
|
|
public ListTileTheme(
|
|
Key key = null,
|
|
bool dense = false,
|
|
ListTileStyle style = ListTileStyle.list,
|
|
Color selectedColor = null,
|
|
Color iconColor = null,
|
|
Color textColor = null,
|
|
EdgeInsetsGeometry contentPadding = null,
|
|
Widget child = null) : base(key: key, child: child) {
|
|
this.dense = dense;
|
|
this.style = style;
|
|
this.selectedColor = selectedColor;
|
|
this.iconColor = iconColor;
|
|
this.textColor = textColor;
|
|
this.contentPadding = contentPadding;
|
|
}
|
|
|
|
public static Widget merge(
|
|
Key key = null,
|
|
bool? dense = null,
|
|
ListTileStyle? style = null,
|
|
Color selectedColor = null,
|
|
Color iconColor = null,
|
|
Color textColor = null,
|
|
EdgeInsetsGeometry contentPadding = null,
|
|
Widget child = null) {
|
|
D.assert(child != null);
|
|
return new Builder(
|
|
builder: (BuildContext context) => {
|
|
ListTileTheme parent = of(context);
|
|
return new ListTileTheme(
|
|
key: key,
|
|
dense: dense ?? parent.dense,
|
|
style: style ?? parent.style,
|
|
selectedColor: selectedColor ?? parent.selectedColor,
|
|
iconColor: iconColor ?? parent.iconColor,
|
|
textColor: textColor ?? parent.textColor,
|
|
contentPadding: contentPadding ?? parent.contentPadding,
|
|
child: child);
|
|
}
|
|
);
|
|
}
|
|
|
|
public readonly bool dense;
|
|
|
|
public readonly ListTileStyle style;
|
|
|
|
public readonly Color selectedColor;
|
|
|
|
public readonly Color iconColor;
|
|
|
|
public readonly Color textColor;
|
|
|
|
public readonly EdgeInsetsGeometry contentPadding;
|
|
|
|
public static ListTileTheme of(BuildContext context) {
|
|
ListTileTheme result = context.dependOnInheritedWidgetOfExactType<ListTileTheme>();
|
|
return result ?? new ListTileTheme();
|
|
}
|
|
|
|
public override Widget wrap(BuildContext context, Widget child) {
|
|
ListTileTheme ancestorTheme = context.findAncestorWidgetOfExactType<ListTileTheme>();
|
|
return ReferenceEquals(this, ancestorTheme)
|
|
? child
|
|
: new ListTileTheme(
|
|
dense: dense,
|
|
style: style,
|
|
selectedColor: selectedColor,
|
|
iconColor: iconColor,
|
|
textColor: textColor,
|
|
contentPadding: contentPadding,
|
|
child: child
|
|
);
|
|
}
|
|
|
|
public override bool updateShouldNotify(InheritedWidget oldWidget) {
|
|
ListTileTheme _oldWidget = (ListTileTheme) oldWidget;
|
|
return dense != _oldWidget.dense ||
|
|
style != _oldWidget.style ||
|
|
selectedColor != _oldWidget.selectedColor ||
|
|
iconColor != _oldWidget.iconColor ||
|
|
textColor != _oldWidget.textColor ||
|
|
contentPadding != _oldWidget.contentPadding;
|
|
}
|
|
}
|
|
|
|
public enum ListTileControlAffinity {
|
|
leading,
|
|
trailing,
|
|
platform
|
|
}
|
|
|
|
public class ListTile : StatelessWidget {
|
|
public ListTile(
|
|
Key key = null,
|
|
Widget leading = null,
|
|
Widget title = null,
|
|
Widget subtitle = null,
|
|
Widget trailing = null,
|
|
bool isThreeLine = false,
|
|
bool? dense = null,
|
|
EdgeInsetsGeometry contentPadding = null,
|
|
bool enabled = true,
|
|
GestureTapCallback onTap = null,
|
|
GestureLongPressCallback onLongPress = null,
|
|
bool selected = false
|
|
) : base(key: key) {
|
|
D.assert(!isThreeLine || subtitle != null);
|
|
this.leading = leading;
|
|
this.title = title;
|
|
this.subtitle = subtitle;
|
|
this.trailing = trailing;
|
|
this.isThreeLine = isThreeLine;
|
|
this.dense = dense;
|
|
this.contentPadding = contentPadding;
|
|
this.enabled = enabled;
|
|
this.onTap = onTap;
|
|
this.onLongPress = onLongPress;
|
|
this.selected = selected;
|
|
}
|
|
|
|
public readonly Widget leading;
|
|
|
|
public readonly Widget title;
|
|
|
|
public readonly Widget subtitle;
|
|
|
|
public readonly Widget trailing;
|
|
|
|
public readonly bool isThreeLine;
|
|
|
|
public readonly bool? dense;
|
|
|
|
public readonly EdgeInsetsGeometry contentPadding;
|
|
|
|
public readonly bool enabled;
|
|
|
|
public readonly GestureTapCallback onTap;
|
|
|
|
public readonly GestureLongPressCallback onLongPress;
|
|
|
|
public readonly bool selected;
|
|
|
|
public static IEnumerable<Widget> divideTiles(BuildContext context = null, IEnumerable<Widget> tiles = null,
|
|
Color color = null) {
|
|
D.assert(tiles != null);
|
|
D.assert(color != null || context != null);
|
|
|
|
IEnumerator<Widget> enumerator = tiles.GetEnumerator();
|
|
List<Widget> result = new List<Widget> { };
|
|
|
|
Decoration decoration = new BoxDecoration(
|
|
border: new Border(
|
|
bottom: Divider.createBorderSide(context, color: color)
|
|
)
|
|
);
|
|
|
|
Widget tile = enumerator.Current;
|
|
while (enumerator.MoveNext()) {
|
|
result.Add(new DecoratedBox(
|
|
position: DecorationPosition.foreground,
|
|
decoration: decoration,
|
|
child: tile
|
|
));
|
|
tile = enumerator.Current;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Color _iconColor(ThemeData theme, ListTileTheme tileTheme) {
|
|
if (!enabled) {
|
|
return theme.disabledColor;
|
|
}
|
|
|
|
if (selected && tileTheme?.selectedColor != null) {
|
|
return tileTheme.selectedColor;
|
|
}
|
|
|
|
if (!selected && tileTheme?.iconColor != null) {
|
|
return tileTheme.iconColor;
|
|
}
|
|
|
|
switch (theme.brightness) {
|
|
case Brightness.light:
|
|
return selected ? theme.primaryColor : Colors.black45;
|
|
case Brightness.dark:
|
|
return selected ? theme.accentColor : null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
Color _textColor(ThemeData theme, ListTileTheme tileTheme, Color defaultColor) {
|
|
if (!enabled) {
|
|
return theme.disabledColor;
|
|
}
|
|
|
|
if (selected && tileTheme?.selectedColor != null) {
|
|
return tileTheme.selectedColor;
|
|
}
|
|
|
|
if (!selected && tileTheme?.textColor != null) {
|
|
return tileTheme.textColor;
|
|
}
|
|
|
|
if (selected) {
|
|
switch (theme.brightness) {
|
|
case Brightness.light:
|
|
return theme.primaryColor;
|
|
case Brightness.dark:
|
|
return theme.accentColor;
|
|
}
|
|
}
|
|
|
|
return defaultColor;
|
|
}
|
|
|
|
bool _isDenseLayout(ListTileTheme tileTheme) {
|
|
return dense ?? tileTheme?.dense ?? false;
|
|
}
|
|
|
|
TextStyle _titleTextStyle(ThemeData theme, ListTileTheme tileTheme) {
|
|
TextStyle style = null;
|
|
if (tileTheme != null) {
|
|
switch (tileTheme.style) {
|
|
case ListTileStyle.drawer:
|
|
style = theme.textTheme.bodyText1;
|
|
break;
|
|
case ListTileStyle.list:
|
|
style = theme.textTheme.subtitle1;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
style = theme.textTheme.subtitle1;
|
|
}
|
|
|
|
Color color = _textColor(theme, tileTheme, style.color);
|
|
return _isDenseLayout(tileTheme)
|
|
? style.copyWith(fontSize: 13.0f, color: color)
|
|
: style.copyWith(color: color);
|
|
}
|
|
|
|
TextStyle _subtitleTextStyle(ThemeData theme, ListTileTheme tileTheme) {
|
|
TextStyle style = theme.textTheme.bodyText2;
|
|
Color color = _textColor(theme, tileTheme, theme.textTheme.caption.color);
|
|
return _isDenseLayout(tileTheme)
|
|
? style.copyWith(color: color, fontSize: 12.0f)
|
|
: style.copyWith(color: color);
|
|
}
|
|
|
|
public override Widget build(BuildContext context) {
|
|
D.assert(material_.debugCheckHasMaterial(context));
|
|
ThemeData theme = Theme.of(context);
|
|
ListTileTheme tileTheme = ListTileTheme.of(context);
|
|
|
|
IconThemeData iconThemeData = null;
|
|
if (leading != null || trailing != null) {
|
|
iconThemeData = new IconThemeData(color: _iconColor(theme, tileTheme));
|
|
}
|
|
|
|
Widget leadingIcon = null;
|
|
if (leading != null) {
|
|
leadingIcon = IconTheme.merge(
|
|
data: iconThemeData,
|
|
child: leading);
|
|
}
|
|
|
|
TextStyle titleStyle = _titleTextStyle(theme, tileTheme);
|
|
Widget titleText = new AnimatedDefaultTextStyle(
|
|
style: titleStyle,
|
|
duration: material_.kThemeChangeDuration,
|
|
child: title ?? new SizedBox()
|
|
);
|
|
|
|
Widget subtitleText = null;
|
|
TextStyle subtitleStyle = null;
|
|
if (subtitle != null) {
|
|
subtitleStyle = _subtitleTextStyle(theme, tileTheme);
|
|
subtitleText = new AnimatedDefaultTextStyle(
|
|
style: subtitleStyle,
|
|
duration: material_.kThemeChangeDuration,
|
|
child: subtitle);
|
|
}
|
|
|
|
Widget trailingIcon = null;
|
|
if (trailing != null) {
|
|
trailingIcon = IconTheme.merge(
|
|
data: iconThemeData,
|
|
child: trailing);
|
|
}
|
|
|
|
EdgeInsets _defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0f);
|
|
TextDirection textDirection = Directionality.of(context);
|
|
EdgeInsets resolvedContentPadding =
|
|
contentPadding?.resolve(textDirection) ?? tileTheme?.contentPadding?.resolve(textDirection) ?? _defaultContentPadding;
|
|
|
|
return new InkWell(
|
|
onTap: enabled ? onTap : null,
|
|
onLongPress: enabled ? onLongPress : null,
|
|
canRequestFocus: enabled,
|
|
child: new SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
mininum: resolvedContentPadding,
|
|
child: new _ListTile(
|
|
leading: leadingIcon,
|
|
title: titleText,
|
|
subtitle: subtitleText,
|
|
trailing: trailingIcon,
|
|
isDense: _isDenseLayout(tileTheme),
|
|
isThreeLine: isThreeLine,
|
|
titleBaselineType: titleStyle.textBaseline,
|
|
subtitleBaselineType: subtitleStyle?.textBaseline
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
public enum _ListTileSlot {
|
|
leading,
|
|
title,
|
|
subtitle,
|
|
trailing
|
|
}
|
|
|
|
public class _ListTile : RenderObjectWidget {
|
|
public _ListTile(
|
|
Key key = null,
|
|
Widget leading = null,
|
|
Widget title = null,
|
|
Widget subtitle = null,
|
|
Widget trailing = null,
|
|
bool? isThreeLine = null,
|
|
bool? isDense = null,
|
|
TextBaseline? titleBaselineType = null,
|
|
TextBaseline? subtitleBaselineType = null) : base(key: key) {
|
|
D.assert(isThreeLine != null);
|
|
D.assert(isDense != null);
|
|
D.assert(titleBaselineType != null);
|
|
this.leading = leading;
|
|
this.title = title;
|
|
this.subtitle = subtitle;
|
|
this.trailing = trailing;
|
|
this.isThreeLine = isThreeLine ?? false;
|
|
this.isDense = isDense ?? false;
|
|
this.titleBaselineType = titleBaselineType ?? TextBaseline.alphabetic;
|
|
this.subtitleBaselineType = subtitleBaselineType;
|
|
}
|
|
|
|
public readonly Widget leading;
|
|
|
|
public readonly Widget title;
|
|
|
|
public readonly Widget subtitle;
|
|
|
|
public readonly Widget trailing;
|
|
|
|
public readonly bool isThreeLine;
|
|
|
|
public readonly bool isDense;
|
|
|
|
public readonly TextBaseline titleBaselineType;
|
|
|
|
public readonly TextBaseline? subtitleBaselineType;
|
|
|
|
public override Element createElement() {
|
|
return new _ListTileElement(this);
|
|
}
|
|
|
|
public override RenderObject createRenderObject(BuildContext context) {
|
|
return new _RenderListTile(
|
|
isThreeLine: isThreeLine,
|
|
isDense: isDense,
|
|
titleBaselineType: titleBaselineType,
|
|
subtitleBaselineType: subtitleBaselineType
|
|
);
|
|
}
|
|
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
|
|
_RenderListTile _renderObject = (_RenderListTile) renderObject;
|
|
_renderObject.isThreeLine = isThreeLine;
|
|
_renderObject.isDense = isDense;
|
|
_renderObject.titleBaselineType = titleBaselineType;
|
|
_renderObject.subtitleBaselineType = subtitleBaselineType;
|
|
}
|
|
}
|
|
|
|
|
|
public class _ListTileElement : RenderObjectElement {
|
|
public _ListTileElement(RenderObjectWidget widget) : base(widget) {
|
|
}
|
|
|
|
readonly Dictionary<_ListTileSlot, Element> slotToChild = new Dictionary<_ListTileSlot, Element>();
|
|
readonly Dictionary<Element, _ListTileSlot> childToSlot = new Dictionary<Element, _ListTileSlot>();
|
|
|
|
public new _ListTile widget {
|
|
get { return (_ListTile) base.widget; }
|
|
}
|
|
|
|
public new _RenderListTile renderObject {
|
|
get { return (_RenderListTile) base.renderObject; }
|
|
}
|
|
|
|
public override void visitChildren(ElementVisitor visitor) {
|
|
foreach (var element in slotToChild.Values) {
|
|
visitor(element);
|
|
}
|
|
}
|
|
|
|
internal override void forgetChild(Element child) {
|
|
D.assert(slotToChild.Values.Contains(child));
|
|
D.assert(childToSlot.Keys.Contains(child));
|
|
_ListTileSlot slot = childToSlot[child];
|
|
childToSlot.Remove(child);
|
|
slotToChild.Remove(slot);
|
|
base.forgetChild(child);
|
|
}
|
|
|
|
void _mountChild(Widget widget, _ListTileSlot slot) {
|
|
Element oldChild = slotToChild.getOrDefault(slot);
|
|
Element newChild = updateChild(oldChild, widget, slot);
|
|
if (oldChild != null) {
|
|
slotToChild.Remove(slot);
|
|
childToSlot.Remove(oldChild);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
slotToChild[slot] = newChild;
|
|
childToSlot[newChild] = slot;
|
|
}
|
|
}
|
|
|
|
public override void mount(Element parent, object newSlot) {
|
|
base.mount(parent, newSlot);
|
|
_mountChild(widget.leading, _ListTileSlot.leading);
|
|
_mountChild(widget.title, _ListTileSlot.title);
|
|
_mountChild(widget.subtitle, _ListTileSlot.subtitle);
|
|
_mountChild(widget.trailing, _ListTileSlot.trailing);
|
|
}
|
|
|
|
void _updateChild(Widget widget, _ListTileSlot slot) {
|
|
Element oldChild = slotToChild.getOrDefault(slot);
|
|
Element newChild = updateChild(oldChild, widget, slot);
|
|
if (oldChild != null) {
|
|
childToSlot.Remove(oldChild);
|
|
slotToChild.Remove(slot);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
slotToChild[slot] = newChild;
|
|
childToSlot[newChild] = slot;
|
|
}
|
|
}
|
|
|
|
public override void update(Widget newWidget) {
|
|
base.update(newWidget);
|
|
D.assert(widget == newWidget);
|
|
_updateChild(widget.leading, _ListTileSlot.leading);
|
|
_updateChild(widget.title, _ListTileSlot.title);
|
|
_updateChild(widget.subtitle, _ListTileSlot.subtitle);
|
|
_updateChild(widget.trailing, _ListTileSlot.trailing);
|
|
}
|
|
|
|
void _updateRenderObject(RenderBox child, _ListTileSlot slot) {
|
|
switch (slot) {
|
|
case _ListTileSlot.leading:
|
|
renderObject.leading = (RenderBox) child;
|
|
break;
|
|
case _ListTileSlot.title:
|
|
renderObject.title = (RenderBox) child;
|
|
break;
|
|
case _ListTileSlot.subtitle:
|
|
renderObject.subtitle = (RenderBox) child;
|
|
break;
|
|
case _ListTileSlot.trailing:
|
|
renderObject.trailing = (RenderBox) child;
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected override void insertChildRenderObject(RenderObject child, object slotValue) {
|
|
D.assert(child is RenderBox);
|
|
D.assert(slotValue is _ListTileSlot);
|
|
_ListTileSlot slot = (_ListTileSlot) slotValue;
|
|
_updateRenderObject((RenderBox) child, slot);
|
|
D.assert(renderObject.childToSlot.Keys.Contains(child));
|
|
D.assert(renderObject.slotToChild.Keys.Contains(slot));
|
|
}
|
|
|
|
protected override void removeChildRenderObject(RenderObject child) {
|
|
D.assert(child is RenderBox);
|
|
D.assert(renderObject.childToSlot.Keys.Contains(child));
|
|
_ListTileSlot slot = renderObject.childToSlot[(RenderBox) child];
|
|
_updateRenderObject(null, slot);
|
|
D.assert(!renderObject.childToSlot.Keys.Contains(child));
|
|
D.assert(!renderObject.slotToChild.Keys.Contains(slot));
|
|
}
|
|
|
|
protected override void moveChildRenderObject(RenderObject child, object slotValue) {
|
|
D.assert(false, () => "not reachable");
|
|
}
|
|
}
|
|
|
|
|
|
public class _RenderListTile : RenderBox {
|
|
public _RenderListTile(
|
|
bool? isDense = null,
|
|
bool? isThreeLine = null,
|
|
TextBaseline? titleBaselineType = null,
|
|
TextBaseline? subtitleBaselineType = null) {
|
|
D.assert(isDense != null);
|
|
D.assert(isThreeLine != null);
|
|
D.assert(titleBaselineType != null);
|
|
_isDense = isDense ?? false;
|
|
_isThreeLine = isThreeLine ?? false;
|
|
_titleBaselineType = titleBaselineType ?? TextBaseline.alphabetic;
|
|
_subtitleBaselineType = subtitleBaselineType;
|
|
}
|
|
|
|
const float _minLeadingWidth = 40.0f;
|
|
|
|
const float _horizontalTitleGap = 16.0f;
|
|
|
|
const float _minVerticalPadding = 4.0f;
|
|
|
|
public readonly Dictionary<_ListTileSlot, RenderBox> slotToChild = new Dictionary<_ListTileSlot, RenderBox>();
|
|
public readonly Dictionary<RenderBox, _ListTileSlot> childToSlot = new Dictionary<RenderBox, _ListTileSlot>();
|
|
|
|
RenderBox _updateChild(RenderBox oldChild, RenderBox newChild, _ListTileSlot slot) {
|
|
if (oldChild != null) {
|
|
dropChild(oldChild);
|
|
childToSlot.Remove(oldChild);
|
|
slotToChild.Remove(slot);
|
|
}
|
|
|
|
if (newChild != null) {
|
|
childToSlot[newChild] = slot;
|
|
slotToChild[slot] = newChild;
|
|
adoptChild(newChild);
|
|
}
|
|
|
|
return newChild;
|
|
}
|
|
|
|
RenderBox _leading;
|
|
|
|
public RenderBox leading {
|
|
get { return _leading; }
|
|
set { _leading = _updateChild(_leading, value, _ListTileSlot.leading); }
|
|
}
|
|
|
|
RenderBox _title;
|
|
|
|
public RenderBox title {
|
|
get { return _title; }
|
|
set { _title = _updateChild(_title, value, _ListTileSlot.title); }
|
|
}
|
|
|
|
RenderBox _subtitle;
|
|
|
|
public RenderBox subtitle {
|
|
get { return _subtitle; }
|
|
set { _subtitle = _updateChild(_subtitle, value, _ListTileSlot.subtitle); }
|
|
}
|
|
|
|
RenderBox _trailing;
|
|
|
|
public RenderBox trailing {
|
|
get { return _trailing; }
|
|
set { _trailing = _updateChild(_trailing, value, _ListTileSlot.trailing); }
|
|
}
|
|
|
|
List<RenderObject> _children {
|
|
get {
|
|
List<RenderObject> ret = new List<RenderObject>();
|
|
if (leading != null) {
|
|
ret.Add(leading);
|
|
}
|
|
|
|
if (title != null) {
|
|
ret.Add(title);
|
|
}
|
|
|
|
if (subtitle != null) {
|
|
ret.Add(subtitle);
|
|
}
|
|
|
|
if (trailing != null) {
|
|
ret.Add(trailing);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
public bool isDense {
|
|
get { return _isDense; }
|
|
set {
|
|
if (_isDense == value) {
|
|
return;
|
|
}
|
|
|
|
_isDense = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
bool _isDense;
|
|
|
|
public bool isThreeLine {
|
|
get { return _isThreeLine; }
|
|
set {
|
|
if (_isThreeLine == value) {
|
|
return;
|
|
}
|
|
|
|
_isThreeLine = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
bool _isThreeLine;
|
|
|
|
public TextBaseline titleBaselineType {
|
|
get { return _titleBaselineType; }
|
|
set {
|
|
if (_titleBaselineType == value) {
|
|
return;
|
|
}
|
|
|
|
_titleBaselineType = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
TextBaseline _titleBaselineType;
|
|
|
|
public TextBaseline? subtitleBaselineType {
|
|
get { return _subtitleBaselineType; }
|
|
set {
|
|
if (_subtitleBaselineType == value) {
|
|
return;
|
|
}
|
|
|
|
_subtitleBaselineType = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
TextBaseline? _subtitleBaselineType;
|
|
|
|
public override void attach(object owner) {
|
|
base.attach(owner);
|
|
foreach (RenderBox child in _children) {
|
|
child.attach(owner);
|
|
}
|
|
}
|
|
|
|
public override void detach() {
|
|
base.detach();
|
|
foreach (RenderBox child in _children) {
|
|
child.detach();
|
|
}
|
|
}
|
|
|
|
public override void redepthChildren() {
|
|
foreach (var child in _children) {
|
|
redepthChild(child);
|
|
}
|
|
}
|
|
|
|
public override void visitChildren(RenderObjectVisitor visitor) {
|
|
foreach (var child in _children) {
|
|
visitor(child);
|
|
}
|
|
}
|
|
|
|
public override List<DiagnosticsNode> debugDescribeChildren() {
|
|
List<DiagnosticsNode> value = new List<DiagnosticsNode>();
|
|
|
|
void add(RenderBox child, string name) {
|
|
if (child != null) {
|
|
value.Add(child.toDiagnosticsNode(name: name));
|
|
}
|
|
}
|
|
|
|
add(leading, "leading");
|
|
add(title, "title");
|
|
add(subtitle, "subtitle");
|
|
add(trailing, "trailing");
|
|
return value;
|
|
}
|
|
|
|
protected override bool sizedByParent {
|
|
get { return false; }
|
|
}
|
|
|
|
static float _minWidth(RenderBox box, float height) {
|
|
return box == null ? 0.0f : box.getMinIntrinsicWidth(height);
|
|
}
|
|
|
|
static float _maxWidth(RenderBox box, float height) {
|
|
return box == null ? 0.0f : box.getMaxIntrinsicWidth(height);
|
|
}
|
|
|
|
protected internal override float computeMinIntrinsicWidth(float height) {
|
|
float leadingWidth = leading != null
|
|
? Mathf.Max(leading.getMinIntrinsicWidth(height), _minLeadingWidth) + _horizontalTitleGap
|
|
: 0.0f;
|
|
return leadingWidth + Mathf.Max(_minWidth(title, height), _minWidth(subtitle, height)) +
|
|
_maxWidth(trailing, height);
|
|
}
|
|
|
|
protected internal override float computeMaxIntrinsicWidth(float height) {
|
|
float leadingWidth = leading != null
|
|
? Mathf.Max(leading.getMaxIntrinsicWidth(height), _minLeadingWidth) + _horizontalTitleGap
|
|
: 0.0f;
|
|
return leadingWidth + Mathf.Max(_maxWidth(title, height), _maxWidth(subtitle, height)) +
|
|
_maxWidth(trailing, height);
|
|
}
|
|
|
|
float _defaultTileHeight {
|
|
get {
|
|
bool hasSubtitle = subtitle != null;
|
|
bool isTwoLine = !isThreeLine && hasSubtitle;
|
|
bool isOneLine = !isThreeLine && !hasSubtitle;
|
|
|
|
if (isOneLine) {
|
|
return isDense ? 48.0f : 56.0f;
|
|
}
|
|
|
|
if (isTwoLine) {
|
|
return isDense ? 64.0f : 72.0f;
|
|
}
|
|
|
|
return isDense ? 76.0f : 88.0f;
|
|
}
|
|
}
|
|
|
|
protected internal override float computeMinIntrinsicHeight(float width) {
|
|
return Mathf.Max(
|
|
_defaultTileHeight,
|
|
title.getMinIntrinsicHeight(width) + subtitle?.getMinIntrinsicHeight(width) ?? 0.0f);
|
|
}
|
|
|
|
protected internal override float computeMaxIntrinsicHeight(float width) {
|
|
return computeMinIntrinsicHeight(width);
|
|
}
|
|
|
|
public override float? computeDistanceToActualBaseline(TextBaseline baseline) {
|
|
D.assert(title != null);
|
|
BoxParentData parentData = (BoxParentData) title.parentData;
|
|
return parentData.offset.dy + title.getDistanceToActualBaseline(baseline);
|
|
}
|
|
|
|
static float _boxBaseline(RenderBox box, TextBaseline baseline) {
|
|
return box.getDistanceToBaseline(baseline) ?? 0.0f;
|
|
}
|
|
|
|
static Size _layoutBox(RenderBox box, BoxConstraints constraints) {
|
|
if (box == null) {
|
|
return Size.zero;
|
|
}
|
|
|
|
box.layout(constraints, parentUsesSize: true);
|
|
return box.size;
|
|
}
|
|
|
|
static void _positionBox(RenderBox box, Offset offset) {
|
|
BoxParentData parentData = (BoxParentData) box.parentData;
|
|
parentData.offset = offset;
|
|
}
|
|
|
|
protected override void performLayout() {
|
|
BoxConstraints constraints = this.constraints;
|
|
bool hasLeading = leading != null;
|
|
bool hasSubtitle = subtitle != null;
|
|
bool hasTrailing = trailing != null;
|
|
bool isTwoLine = !isThreeLine && hasSubtitle;
|
|
bool isOneLine = !isThreeLine && !hasSubtitle;
|
|
BoxConstraints maxIconHeightConstrains = new BoxConstraints(
|
|
maxHeight: isDense ? 48.0f : 56.0f
|
|
);
|
|
BoxConstraints looseConstraints = constraints.loosen();
|
|
BoxConstraints iconConstraints = looseConstraints.enforce(maxIconHeightConstrains);
|
|
|
|
float tileWidth = looseConstraints.maxWidth;
|
|
Size leadingSize = _layoutBox(leading, iconConstraints);
|
|
Size trailingSize = _layoutBox(trailing, iconConstraints);
|
|
D.assert(
|
|
tileWidth != leadingSize.width,
|
|
() => "Leading widget consumes entire width. Please use a sized widget."
|
|
);
|
|
D.assert(
|
|
tileWidth != trailingSize.width,
|
|
() => "Trailing widget consumes entire width. Please use a sized widget."
|
|
);
|
|
|
|
float titleStart = hasLeading
|
|
? Mathf.Max(_minLeadingWidth, leadingSize.width) + _horizontalTitleGap
|
|
: 0.0f;
|
|
BoxConstraints textConstraints = looseConstraints.tighten(
|
|
width: tileWidth - titleStart - (hasTrailing ? trailingSize.width + _horizontalTitleGap : 0.0f));
|
|
Size titleSize = _layoutBox(title, textConstraints);
|
|
Size subtitleSize = _layoutBox(subtitle, textConstraints);
|
|
|
|
float titleBaseline = 0.0f;
|
|
float subtitleBaseline = 0.0f;
|
|
if (isTwoLine) {
|
|
titleBaseline = isDense ? 28.0f : 32.0f;
|
|
subtitleBaseline = isDense ? 48.0f : 52.0f;
|
|
}
|
|
else if (isThreeLine) {
|
|
titleBaseline = isDense ? 22.0f : 28.0f;
|
|
subtitleBaseline = isDense ? 42.0f : 48.0f;
|
|
}
|
|
else {
|
|
D.assert(isOneLine);
|
|
}
|
|
|
|
float defaultTileHeight = _defaultTileHeight;
|
|
|
|
float tileHeight = 0.0f;
|
|
float titleY = 0.0f;
|
|
float subtitleY = 0.0f;
|
|
if (!hasSubtitle) {
|
|
tileHeight = Mathf.Max(defaultTileHeight, titleSize.height + 2.0f * _minVerticalPadding);
|
|
titleY = (tileHeight - titleSize.height) / 2.0f;
|
|
}
|
|
else {
|
|
D.assert(subtitleBaselineType != null);
|
|
titleY = titleBaseline - _boxBaseline(title, titleBaselineType);
|
|
subtitleY = subtitleBaseline -
|
|
_boxBaseline(subtitle, subtitleBaselineType ?? TextBaseline.alphabetic);
|
|
tileHeight = defaultTileHeight;
|
|
|
|
float titleOverlap = titleY + titleSize.height - subtitleY;
|
|
if (titleOverlap > 0.0f) {
|
|
titleY -= titleOverlap / 2.0f;
|
|
subtitleY += titleOverlap / 2.0f;
|
|
}
|
|
|
|
if (titleY < _minVerticalPadding ||
|
|
(subtitleY + subtitleSize.height + _minVerticalPadding) > tileHeight) {
|
|
tileHeight = titleSize.height + subtitleSize.height + 2.0f * _minVerticalPadding;
|
|
titleY = _minVerticalPadding;
|
|
subtitleY = titleSize.height + _minVerticalPadding;
|
|
}
|
|
}
|
|
|
|
float leadingY;
|
|
float trailingY;
|
|
|
|
if (tileHeight > 72.0f) {
|
|
leadingY = 16.0f;
|
|
trailingY = 16.0f;
|
|
}
|
|
else {
|
|
leadingY = Mathf.Min((tileHeight - leadingSize.height) / 2.0f, 16.0f);
|
|
trailingY = (tileHeight - trailingSize.height) / 2.0f;
|
|
}
|
|
|
|
if (hasLeading) {
|
|
_positionBox(leading, new Offset(0.0f, leadingY));
|
|
}
|
|
|
|
_positionBox(title, new Offset(titleStart, titleY));
|
|
if (hasSubtitle) {
|
|
_positionBox(subtitle, new Offset(titleStart, subtitleY));
|
|
}
|
|
|
|
if (hasTrailing) {
|
|
_positionBox(trailing, new Offset(tileWidth - trailingSize.width, trailingY));
|
|
}
|
|
|
|
size = constraints.constrain(new Size(tileWidth, tileHeight));
|
|
D.assert(size.width == constraints.constrainWidth(tileWidth));
|
|
D.assert(size.height == constraints.constrainHeight(tileHeight));
|
|
}
|
|
|
|
public override void paint(PaintingContext context, Offset offset) {
|
|
void doPaint(RenderBox child) {
|
|
if (child != null) {
|
|
BoxParentData parentData = (BoxParentData) child.parentData;
|
|
context.paintChild(child, parentData.offset + offset);
|
|
}
|
|
}
|
|
|
|
doPaint(leading);
|
|
doPaint(title);
|
|
doPaint(subtitle);
|
|
doPaint(trailing);
|
|
}
|
|
|
|
protected override bool hitTestSelf(Offset position) {
|
|
return true;
|
|
}
|
|
|
|
protected override bool hitTestChildren(BoxHitTestResult result, Offset position) {
|
|
D.assert(position != null);
|
|
foreach (RenderBox child in _children) {
|
|
BoxParentData parentData = child.parentData as BoxParentData;
|
|
bool isHit = result.addWithPaintOffset(
|
|
offset: parentData.offset,
|
|
position: position,
|
|
hitTest: (BoxHitTestResult resultIn, Offset transformed) => {
|
|
D.assert(transformed == position - parentData.offset);
|
|
return child.hitTest(resultIn, position: transformed);
|
|
}
|
|
);
|
|
if (isHit) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|