Unverified Commit 190d7762 authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

Add Tooltip textAlign property (#103475)

parent aa39fa5a
......@@ -103,11 +103,12 @@ class Tooltip extends StatefulWidget {
this.excludeFromSemantics,
this.decoration,
this.textStyle,
this.textAlign,
this.waitDuration,
this.showDuration,
this.child,
this.triggerMode,
this.enableFeedback,
this.child,
}) : assert((message == null) != (richMessage == null), 'Either `message` or `richMessage` must be specified'),
assert(
richMessage == null || textStyle == null,
......@@ -197,6 +198,13 @@ class Tooltip extends StatefulWidget {
/// used with [Colors.black].
final TextStyle? textStyle;
/// How the message of the tooltip is aligned horizontally.
///
/// If this property is null, then [TooltipThemeData.textAlign] is used.
/// If [TooltipThemeData.textAlign] is also null, the default value is
/// [TextAlign.start].
final TextAlign? textAlign;
/// The length of time that a pointer must hover over a tooltip's widget
/// before the tooltip will be shown.
///
......@@ -298,6 +306,7 @@ class Tooltip extends StatefulWidget {
properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null));
properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null));
properties.add(FlagProperty('enableFeedback', value: enableFeedback, ifTrue: 'true', showName: true));
properties.add(DiagnosticsProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
}
}
......@@ -317,12 +326,14 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
static const bool _defaultExcludeFromSemantics = false;
static const TooltipTriggerMode _defaultTriggerMode = TooltipTriggerMode.longPress;
static const bool _defaultEnableFeedback = true;
static const TextAlign _defaultTextAlign = TextAlign.start;
late double _height;
late EdgeInsetsGeometry _padding;
late EdgeInsetsGeometry _margin;
late Decoration _decoration;
late TextStyle _textStyle;
late TextAlign _textAlign;
late double _verticalOffset;
late bool _preferBelow;
late bool _excludeFromSemantics;
......@@ -570,6 +581,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
onExit: _mouseIsConnected ? (_) => _handleMouseExit() : null,
decoration: _decoration,
textStyle: _textStyle,
textAlign: _textAlign,
animation: CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
......@@ -691,6 +703,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
_excludeFromSemantics = widget.excludeFromSemantics ?? tooltipTheme.excludeFromSemantics ?? _defaultExcludeFromSemantics;
_decoration = widget.decoration ?? tooltipTheme.decoration ?? defaultDecoration;
_textStyle = widget.textStyle ?? tooltipTheme.textStyle ?? defaultTextStyle;
_textAlign = widget.textAlign ?? tooltipTheme.textAlign ?? _defaultTextAlign;
_waitDuration = widget.waitDuration ?? tooltipTheme.waitDuration ?? _defaultWaitDuration;
_showDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultShowDuration;
_hoverShowDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultHoverShowDuration;
......@@ -786,6 +799,7 @@ class _TooltipOverlay extends StatelessWidget {
this.margin,
this.decoration,
this.textStyle,
this.textAlign,
required this.animation,
required this.target,
required this.verticalOffset,
......@@ -800,6 +814,7 @@ class _TooltipOverlay extends StatelessWidget {
final EdgeInsetsGeometry? margin;
final Decoration? decoration;
final TextStyle? textStyle;
final TextAlign? textAlign;
final Animation<double> animation;
final Offset target;
final double verticalOffset;
......@@ -826,6 +841,7 @@ class _TooltipOverlay extends StatelessWidget {
child: Text.rich(
richMessage,
style: textStyle,
textAlign: textAlign,
),
),
),
......
......@@ -35,6 +35,7 @@ class TooltipThemeData with Diagnosticable {
this.excludeFromSemantics,
this.decoration,
this.textStyle,
this.textAlign,
this.waitDuration,
this.showDuration,
this.triggerMode,
......@@ -79,6 +80,9 @@ class TooltipThemeData with Diagnosticable {
/// The style to use for the message of [Tooltip]s.
final TextStyle? textStyle;
/// The [TextAlign] to use for the message of [Tooltip]s.
final TextAlign? textAlign;
/// The length of time that a pointer must hover over a tooltip's widget
/// before the tooltip will be shown.
final Duration? waitDuration;
......@@ -113,6 +117,7 @@ class TooltipThemeData with Diagnosticable {
bool? excludeFromSemantics,
Decoration? decoration,
TextStyle? textStyle,
TextAlign? textAlign,
Duration? waitDuration,
Duration? showDuration,
TooltipTriggerMode? triggerMode,
......@@ -127,6 +132,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics: excludeFromSemantics ?? this.excludeFromSemantics,
decoration: decoration ?? this.decoration,
textStyle: textStyle ?? this.textStyle,
textAlign: textAlign ?? this.textAlign,
waitDuration: waitDuration ?? this.waitDuration,
showDuration: showDuration ?? this.showDuration,
triggerMode: triggerMode ?? this.triggerMode,
......@@ -152,6 +158,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics: t < 0.5 ? a?.excludeFromSemantics : b?.excludeFromSemantics,
decoration: Decoration.lerp(a?.decoration, b?.decoration, t),
textStyle: TextStyle.lerp(a?.textStyle, b?.textStyle, t),
textAlign: t < 0.5 ? a?.textAlign: b?.textAlign,
);
}
......@@ -165,6 +172,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics,
decoration,
textStyle,
textAlign,
waitDuration,
showDuration,
triggerMode,
......@@ -186,6 +194,7 @@ class TooltipThemeData with Diagnosticable {
&& other.excludeFromSemantics == excludeFromSemantics
&& other.decoration == decoration
&& other.textStyle == textStyle
&& other.textAlign == textAlign
&& other.waitDuration == waitDuration
&& other.showDuration == showDuration
&& other.triggerMode == triggerMode
......@@ -203,6 +212,7 @@ class TooltipThemeData with Diagnosticable {
properties.add(FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true));
properties.add(DiagnosticsProperty<Decoration>('decoration', decoration, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle>('textStyle', textStyle, defaultValue: null));
properties.add(DiagnosticsProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
properties.add(DiagnosticsProperty<Duration>('wait duration', waitDuration, defaultValue: null));
properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null));
properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null));
......
......@@ -677,6 +677,41 @@ void main() {
expect(textStyle.decoration, TextDecoration.underline);
});
testWidgets('Custom tooltip message textAlign', (WidgetTester tester) async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
MaterialApp(
home: Tooltip(
key: tooltipKey,
textAlign: textAlign,
message: tooltipText,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green[500],
),
),
),
);
tooltipKey.currentState?.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
}
// Default value should be TextAlign.start
await pumpTooltipWithTextAlign();
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.start);
await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.center);
await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.end);
});
testWidgets('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/40702.
Widget buildApp(String text, TextDirection textDirection) {
......
......@@ -46,6 +46,7 @@ void main() {
expect(theme.excludeFromSemantics, null);
expect(theme.decoration, null);
expect(theme.textStyle, null);
expect(theme.textAlign, null);
expect(theme.waitDuration, null);
expect(theme.showDuration, null);
expect(theme.triggerMode, null);
......@@ -78,6 +79,7 @@ void main() {
excludeFromSemantics: true,
decoration: BoxDecoration(color: Color(0xffffffff)),
textStyle: TextStyle(decoration: TextDecoration.underline),
textAlign: TextAlign.center,
waitDuration: wait,
showDuration: show,
triggerMode: triggerMode,
......@@ -97,6 +99,7 @@ void main() {
'semantics: excluded',
'decoration: BoxDecoration(color: Color(0xffffffff))',
'textStyle: TextStyle(inherit: true, decoration: TextDecoration.underline)',
'textAlign: TextAlign.center',
'wait duration: $wait',
'show duration: $show',
'triggerMode: $triggerMode',
......@@ -651,6 +654,45 @@ void main() {
expect(textStyle.decoration, TextDecoration.underline);
});
testWidgets('Tooltip message textAlign - TooltipTheme', (WidgetTester tester) async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
MaterialApp(
home: TooltipTheme(
data: TooltipThemeData(
textAlign: textAlign,
),
child: Tooltip(
key: tooltipKey,
message: tooltipText,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green[500],
),
),
),
),
);
tooltipKey.currentState?.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
}
// Default value should be TextAlign.start
await pumpTooltipWithTextAlign();
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.start);
await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.center);
await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.end);
});
testWidgets('Tooltip decoration - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
const Decoration customDecoration = ShapeDecoration(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment