|
|
|
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using Unity.UIWidgets.foundation; |
|
|
|
using Unity.UIWidgets.gestures; |
|
|
|
using Unity.UIWidgets.painting; |
|
|
|
|
|
|
using UnityEngine; |
|
|
|
using Color = Unity.UIWidgets.ui.Color; |
|
|
|
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|
|
|
|
|
|
|
namespace Unity.UIWidgets.material { |
|
|
|
|
|
|
VoidCallback onPressed = null, |
|
|
|
VoidCallback onLongPress = null, |
|
|
|
Color focusColor = null, |
|
|
|
Color hoverColor = null, |
|
|
|
float focusElevation = 4.0f, |
|
|
|
float hoverElevation = 4.0f, |
|
|
|
VisualDensity visualDensity = null, |
|
|
|
FocusNode focusNode = null, |
|
|
|
bool autofocus = false, |
|
|
|
Widget child = null) : base(key: key) { |
|
|
|
D.assert(elevation >= 0.0); |
|
|
|
D.assert(highlightElevation >= 0.0); |
|
|
|
D.assert(disabledElevation >= 0.0); |
|
|
|
Widget child = null, |
|
|
|
bool enableFeedback = true) : base(key: key) { |
|
|
|
D.assert(elevation >= 0.0f); |
|
|
|
D.assert(focusElevation >= 0.0f); |
|
|
|
D.assert(focusElevation >= 0.0f); |
|
|
|
D.assert(highlightElevation >= 0.0f); |
|
|
|
D.assert(disabledElevation >= 0.0f); |
|
|
|
visualDensity = visualDensity ?? new VisualDensity(); |
|
|
|
this.onLongPress = onLongPress; |
|
|
|
this.focusColor = focusColor; |
|
|
|
this.hoverColor = hoverColor; |
|
|
|
this.focusElevation = focusElevation; |
|
|
|
this.hoverElevation = hoverElevation; |
|
|
|
this.visualDensity = visualDensity; |
|
|
|
this.focusNode = focusNode; |
|
|
|
this.autofocus = autofocus; |
|
|
|
this.enableFeedback = enableFeedback; |
|
|
|
this.materialTapTargetSize = _materialTapTargetSize; |
|
|
|
this.child = child; |
|
|
|
} |
|
|
|
|
|
|
public readonly VoidCallback onLongPress; |
|
|
|
|
|
|
|
public readonly ValueChanged<bool> onHighlightChanged; |
|
|
|
|
|
|
|
public readonly TextStyle textStyle; |
|
|
|
|
|
|
public readonly Color focusColor; |
|
|
|
|
|
|
|
public readonly Color hoverColor; |
|
|
|
|
|
|
|
|
|
|
|
public readonly float hoverElevation; |
|
|
|
|
|
|
|
public readonly float focusElevation; |
|
|
|
|
|
|
|
public readonly float highlightElevation; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public readonly VisualDensity visualDensity; |
|
|
|
|
|
|
|
public readonly BoxConstraints constraints; |
|
|
|
|
|
|
|
public readonly ShapeBorder shape; |
|
|
|
|
|
|
public readonly Widget child; |
|
|
|
|
|
|
|
public bool enabled { |
|
|
|
get { return onPressed != null; } |
|
|
|
get { return onPressed != null || onLongPress != null; } |
|
|
|
public readonly FocusNode focusNode; |
|
|
|
|
|
|
|
public readonly bool autofocus; |
|
|
|
|
|
|
|
public readonly bool enableFeedback; |
|
|
|
|
|
|
|
public override State createState() { |
|
|
|
return new _RawMaterialButtonState(); |
|
|
|
} |
|
|
|
|
|
|
class _RawMaterialButtonState : State<RawMaterialButton> { |
|
|
|
bool _highlight = false; |
|
|
|
readonly HashSet<MaterialState> _states = new HashSet<MaterialState>(); |
|
|
|
|
|
|
|
bool _hovered { |
|
|
|
get { return _states.Contains(MaterialState.hovered); } |
|
|
|
} |
|
|
|
|
|
|
|
bool _focused { |
|
|
|
get { return _states.Contains(MaterialState.focused); } |
|
|
|
} |
|
|
|
|
|
|
|
bool _pressed { |
|
|
|
get { return _states.Contains(MaterialState.pressed); } |
|
|
|
} |
|
|
|
|
|
|
|
bool _disabled { |
|
|
|
get { return _states.Contains(MaterialState.disabled); } |
|
|
|
} |
|
|
|
|
|
|
|
void _updateState(MaterialState state, bool value) { |
|
|
|
if (value) { |
|
|
|
_states.Add(state); |
|
|
|
} |
|
|
|
else { |
|
|
|
_states.Remove(state); |
|
|
|
} |
|
|
|
} |
|
|
|
if (_highlight != value) { |
|
|
|
if (_pressed != value) { |
|
|
|
_highlight = value; |
|
|
|
_updateState(MaterialState.pressed, value); |
|
|
|
if (widget.onHighlightChanged != null) { |
|
|
|
widget.onHighlightChanged(value); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
void _handleHoveredChanged(bool value) { |
|
|
|
if (_hovered != value) { |
|
|
|
setState(() => { _updateState(MaterialState.hovered, value); }); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void _handleFocusedChanged(bool value) { |
|
|
|
if (_focused != value) { |
|
|
|
setState(() => { _updateState(MaterialState.focused, value); }); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override void initState() { |
|
|
|
base.initState(); |
|
|
|
_updateState(MaterialState.disabled, !widget.enabled); |
|
|
|
} |
|
|
|
|
|
|
|
if (_highlight && !widget.enabled) { |
|
|
|
_highlight = false; |
|
|
|
if (widget.onHighlightChanged != null) { |
|
|
|
widget.onHighlightChanged(false); |
|
|
|
_updateState(MaterialState.disabled, !widget.enabled); |
|
|
|
if (_disabled && _pressed) { |
|
|
|
_handleHighlightChanged(false); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
float _effectiveElevation { |
|
|
|
get { |
|
|
|
if (_disabled) { |
|
|
|
return widget.disabledElevation; |
|
|
|
} |
|
|
|
|
|
|
|
if (_pressed) { |
|
|
|
return widget.highlightElevation; |
|
|
|
|
|
|
|
if (_hovered) { |
|
|
|
return widget.hoverElevation; |
|
|
|
} |
|
|
|
|
|
|
|
if (_focused) { |
|
|
|
return widget.focusElevation; |
|
|
|
} |
|
|
|
|
|
|
|
return widget.elevation; |
|
|
|
float elevation = widget.enabled |
|
|
|
? (_highlight ? widget.highlightElevation : widget.elevation) |
|
|
|
: widget.disabledElevation; |
|
|
|
Color effectiveTextColor = MaterialStateProperty.resolveAs<Color>(widget.textStyle?.color, _states); |
|
|
|
ShapeBorder effectiveShape = MaterialStateProperty.resolveAs<ShapeBorder>(widget.shape, _states); |
|
|
|
Offset densityAdjustment = widget.visualDensity.baseSizeAdjustment; |
|
|
|
BoxConstraints effectiveConstraints = widget.visualDensity.effectiveConstraints(widget.constraints); |
|
|
|
EdgeInsets padding = widget.padding.add( |
|
|
|
EdgeInsets.only( |
|
|
|
left: densityAdjustment.dx, |
|
|
|
top: densityAdjustment.dy, |
|
|
|
right: densityAdjustment.dx, |
|
|
|
bottom: densityAdjustment.dy |
|
|
|
) |
|
|
|
).clamp(EdgeInsets.zero, EdgeInsets.infinity) as EdgeInsets; |
|
|
|
constraints: widget.constraints, |
|
|
|
constraints: effectiveConstraints, |
|
|
|
elevation: elevation, |
|
|
|
textStyle: widget.textStyle, |
|
|
|
shape: widget.shape, |
|
|
|
elevation: _effectiveElevation, |
|
|
|
textStyle: widget.textStyle?.copyWith(color: effectiveTextColor), |
|
|
|
shape: effectiveShape, |
|
|
|
focusNode: widget.focusNode, |
|
|
|
canRequestFocus: widget.enabled, |
|
|
|
onFocusChange: _handleFocusedChanged, |
|
|
|
autofocus: widget.autofocus, |
|
|
|
focusColor: widget.focusColor, |
|
|
|
hoverColor: widget.hoverColor, |
|
|
|
onHover: _handleHoveredChanged, |
|
|
|
onTap: widget.onPressed == null |
|
|
|
? (GestureTapCallback) null |
|
|
|
: () => { |
|
|
|
|
|
|
}, |
|
|
|
customBorder: widget.shape, |
|
|
|
onLongPress: widget.onLongPress, |
|
|
|
enableFeedback: widget.enableFeedback, |
|
|
|
customBorder: effectiveShape, |
|
|
|
data: new IconThemeData(color: widget.textStyle?.color), |
|
|
|
data: new IconThemeData(color: effectiveTextColor), |
|
|
|
padding: widget.padding, |
|
|
|
padding: padding, |
|
|
|
child: new Center( |
|
|
|
widthFactor: 1.0f, |
|
|
|
heightFactor: 1.0f, |
|
|
|
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
return result; |
|
|
|
Size minSize = null; |
|
|
|
switch (widget.materialTapTargetSize) { |
|
|
|
case MaterialTapTargetSize.padded: |
|
|
|
minSize = new Size( |
|
|
|
Constants.kMinInteractiveDimension + densityAdjustment.dx, |
|
|
|
Constants.kMinInteractiveDimension + densityAdjustment.dy |
|
|
|
); |
|
|
|
D.assert(minSize.width >= 0.0f); |
|
|
|
D.assert(minSize.height >= 0.0f); |
|
|
|
break; |
|
|
|
case MaterialTapTargetSize.shrinkWrap: |
|
|
|
minSize = Size.zero; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return new _InputPadding( |
|
|
|
minSize: minSize, |
|
|
|
child: result |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _InputPadding : SingleChildRenderObjectWidget { |
|
|
|
public _InputPadding( |
|
|
|
Key key = null, |
|
|
|
Widget child = null, |
|
|
|
Size minSize = null) : base(key: key, child: child) { |
|
|
|
this.minSize = minSize; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly Size minSize; |
|
|
|
|
|
|
|
public override RenderObject createRenderObject(BuildContext context) { |
|
|
|
return new _RenderInputPadding(minSize); |
|
|
|
} |
|
|
|
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject renderObject) { |
|
|
|
var _renderObject = (_RenderInputPadding) renderObject; |
|
|
|
_renderObject.minSize = minSize; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _RenderInputPadding : RenderShiftedBox { |
|
|
|
public _RenderInputPadding( |
|
|
|
Size minSize, |
|
|
|
RenderBox child = null |
|
|
|
) : base(child: child) { |
|
|
|
_minSize = minSize; |
|
|
|
} |
|
|
|
|
|
|
|
public Size minSize { |
|
|
|
get { return _minSize; } |
|
|
|
set { |
|
|
|
if (_minSize == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
_minSize = value; |
|
|
|
markNeedsLayout(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Size _minSize; |
|
|
|
|
|
|
|
protected internal override float computeMinIntrinsicWidth(float height) { |
|
|
|
if (child != null) { |
|
|
|
return Mathf.Max(child.getMinIntrinsicWidth(height), minSize.width); |
|
|
|
} |
|
|
|
|
|
|
|
return 0.0f; |
|
|
|
} |
|
|
|
|
|
|
|
protected internal override float computeMinIntrinsicHeight(float width) { |
|
|
|
if (child != null) { |
|
|
|
return Mathf.Max(child.getMinIntrinsicHeight(width), minSize.height); |
|
|
|
} |
|
|
|
|
|
|
|
return 0.0f; |
|
|
|
} |
|
|
|
|
|
|
|
protected internal override float computeMaxIntrinsicWidth(float height) { |
|
|
|
if (child != null) { |
|
|
|
return Mathf.Max(child.getMaxIntrinsicWidth(height), minSize.width); |
|
|
|
} |
|
|
|
|
|
|
|
return 0.0f; |
|
|
|
} |
|
|
|
|
|
|
|
protected internal override float computeMaxIntrinsicHeight(float width) { |
|
|
|
if (child != null) { |
|
|
|
return Mathf.Max(child.getMaxIntrinsicHeight(width), minSize.height); |
|
|
|
} |
|
|
|
|
|
|
|
return 0.0f; |
|
|
|
} |
|
|
|
|
|
|
|
protected override void performLayout() { |
|
|
|
BoxConstraints constraints = this.constraints; |
|
|
|
if (child != null) { |
|
|
|
child.layout(constraints, parentUsesSize: true); |
|
|
|
float height = Mathf.Max(child.size.width, minSize.width); |
|
|
|
float width = Mathf.Max(child.size.height, minSize.height); |
|
|
|
size = constraints.constrain(new Size(height, width)); |
|
|
|
BoxParentData childParentData = child.parentData as BoxParentData; |
|
|
|
childParentData.offset = Alignment.center.alongOffset(size - child.size as Offset); |
|
|
|
} |
|
|
|
else { |
|
|
|
size = Size.zero; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override bool hitTest(BoxHitTestResult result, Offset position = null) { |
|
|
|
if (base.hitTest(result, position: position)) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
Offset center = child.size.center(Offset.zero); |
|
|
|
return result.addWithRawTransform( |
|
|
|
transform: MatrixUtils.forceToPoint(center), |
|
|
|
position: center, |
|
|
|
hitTest: (BoxHitTestResult result, Offset position) => { |
|
|
|
D.assert(position == center); |
|
|
|
return child.hitTest(result, position: center); |
|
|
|
} |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
} |