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 { ...@@ -103,11 +103,12 @@ class Tooltip extends StatefulWidget {
this.excludeFromSemantics, this.excludeFromSemantics,
this.decoration, this.decoration,
this.textStyle, this.textStyle,
this.textAlign,
this.waitDuration, this.waitDuration,
this.showDuration, this.showDuration,
this.child,
this.triggerMode, this.triggerMode,
this.enableFeedback, this.enableFeedback,
this.child,
}) : assert((message == null) != (richMessage == null), 'Either `message` or `richMessage` must be specified'), }) : assert((message == null) != (richMessage == null), 'Either `message` or `richMessage` must be specified'),
assert( assert(
richMessage == null || textStyle == null, richMessage == null || textStyle == null,
...@@ -197,6 +198,13 @@ class Tooltip extends StatefulWidget { ...@@ -197,6 +198,13 @@ class Tooltip extends StatefulWidget {
/// used with [Colors.black]. /// used with [Colors.black].
final TextStyle? textStyle; 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 /// The length of time that a pointer must hover over a tooltip's widget
/// before the tooltip will be shown. /// before the tooltip will be shown.
/// ///
...@@ -298,6 +306,7 @@ class Tooltip extends StatefulWidget { ...@@ -298,6 +306,7 @@ class Tooltip extends StatefulWidget {
properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null)); properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null));
properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null)); properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null));
properties.add(FlagProperty('enableFeedback', value: enableFeedback, ifTrue: 'true', showName: true)); 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 { ...@@ -317,12 +326,14 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
static const bool _defaultExcludeFromSemantics = false; static const bool _defaultExcludeFromSemantics = false;
static const TooltipTriggerMode _defaultTriggerMode = TooltipTriggerMode.longPress; static const TooltipTriggerMode _defaultTriggerMode = TooltipTriggerMode.longPress;
static const bool _defaultEnableFeedback = true; static const bool _defaultEnableFeedback = true;
static const TextAlign _defaultTextAlign = TextAlign.start;
late double _height; late double _height;
late EdgeInsetsGeometry _padding; late EdgeInsetsGeometry _padding;
late EdgeInsetsGeometry _margin; late EdgeInsetsGeometry _margin;
late Decoration _decoration; late Decoration _decoration;
late TextStyle _textStyle; late TextStyle _textStyle;
late TextAlign _textAlign;
late double _verticalOffset; late double _verticalOffset;
late bool _preferBelow; late bool _preferBelow;
late bool _excludeFromSemantics; late bool _excludeFromSemantics;
...@@ -570,6 +581,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin { ...@@ -570,6 +581,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
onExit: _mouseIsConnected ? (_) => _handleMouseExit() : null, onExit: _mouseIsConnected ? (_) => _handleMouseExit() : null,
decoration: _decoration, decoration: _decoration,
textStyle: _textStyle, textStyle: _textStyle,
textAlign: _textAlign,
animation: CurvedAnimation( animation: CurvedAnimation(
parent: _controller, parent: _controller,
curve: Curves.fastOutSlowIn, curve: Curves.fastOutSlowIn,
...@@ -691,6 +703,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin { ...@@ -691,6 +703,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
_excludeFromSemantics = widget.excludeFromSemantics ?? tooltipTheme.excludeFromSemantics ?? _defaultExcludeFromSemantics; _excludeFromSemantics = widget.excludeFromSemantics ?? tooltipTheme.excludeFromSemantics ?? _defaultExcludeFromSemantics;
_decoration = widget.decoration ?? tooltipTheme.decoration ?? defaultDecoration; _decoration = widget.decoration ?? tooltipTheme.decoration ?? defaultDecoration;
_textStyle = widget.textStyle ?? tooltipTheme.textStyle ?? defaultTextStyle; _textStyle = widget.textStyle ?? tooltipTheme.textStyle ?? defaultTextStyle;
_textAlign = widget.textAlign ?? tooltipTheme.textAlign ?? _defaultTextAlign;
_waitDuration = widget.waitDuration ?? tooltipTheme.waitDuration ?? _defaultWaitDuration; _waitDuration = widget.waitDuration ?? tooltipTheme.waitDuration ?? _defaultWaitDuration;
_showDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultShowDuration; _showDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultShowDuration;
_hoverShowDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultHoverShowDuration; _hoverShowDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultHoverShowDuration;
...@@ -786,6 +799,7 @@ class _TooltipOverlay extends StatelessWidget { ...@@ -786,6 +799,7 @@ class _TooltipOverlay extends StatelessWidget {
this.margin, this.margin,
this.decoration, this.decoration,
this.textStyle, this.textStyle,
this.textAlign,
required this.animation, required this.animation,
required this.target, required this.target,
required this.verticalOffset, required this.verticalOffset,
...@@ -800,6 +814,7 @@ class _TooltipOverlay extends StatelessWidget { ...@@ -800,6 +814,7 @@ class _TooltipOverlay extends StatelessWidget {
final EdgeInsetsGeometry? margin; final EdgeInsetsGeometry? margin;
final Decoration? decoration; final Decoration? decoration;
final TextStyle? textStyle; final TextStyle? textStyle;
final TextAlign? textAlign;
final Animation<double> animation; final Animation<double> animation;
final Offset target; final Offset target;
final double verticalOffset; final double verticalOffset;
...@@ -826,6 +841,7 @@ class _TooltipOverlay extends StatelessWidget { ...@@ -826,6 +841,7 @@ class _TooltipOverlay extends StatelessWidget {
child: Text.rich( child: Text.rich(
richMessage, richMessage,
style: textStyle, style: textStyle,
textAlign: textAlign,
), ),
), ),
), ),
......
...@@ -35,6 +35,7 @@ class TooltipThemeData with Diagnosticable { ...@@ -35,6 +35,7 @@ class TooltipThemeData with Diagnosticable {
this.excludeFromSemantics, this.excludeFromSemantics,
this.decoration, this.decoration,
this.textStyle, this.textStyle,
this.textAlign,
this.waitDuration, this.waitDuration,
this.showDuration, this.showDuration,
this.triggerMode, this.triggerMode,
...@@ -79,6 +80,9 @@ class TooltipThemeData with Diagnosticable { ...@@ -79,6 +80,9 @@ class TooltipThemeData with Diagnosticable {
/// The style to use for the message of [Tooltip]s. /// The style to use for the message of [Tooltip]s.
final TextStyle? textStyle; 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 /// The length of time that a pointer must hover over a tooltip's widget
/// before the tooltip will be shown. /// before the tooltip will be shown.
final Duration? waitDuration; final Duration? waitDuration;
...@@ -113,6 +117,7 @@ class TooltipThemeData with Diagnosticable { ...@@ -113,6 +117,7 @@ class TooltipThemeData with Diagnosticable {
bool? excludeFromSemantics, bool? excludeFromSemantics,
Decoration? decoration, Decoration? decoration,
TextStyle? textStyle, TextStyle? textStyle,
TextAlign? textAlign,
Duration? waitDuration, Duration? waitDuration,
Duration? showDuration, Duration? showDuration,
TooltipTriggerMode? triggerMode, TooltipTriggerMode? triggerMode,
...@@ -127,6 +132,7 @@ class TooltipThemeData with Diagnosticable { ...@@ -127,6 +132,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics: excludeFromSemantics ?? this.excludeFromSemantics, excludeFromSemantics: excludeFromSemantics ?? this.excludeFromSemantics,
decoration: decoration ?? this.decoration, decoration: decoration ?? this.decoration,
textStyle: textStyle ?? this.textStyle, textStyle: textStyle ?? this.textStyle,
textAlign: textAlign ?? this.textAlign,
waitDuration: waitDuration ?? this.waitDuration, waitDuration: waitDuration ?? this.waitDuration,
showDuration: showDuration ?? this.showDuration, showDuration: showDuration ?? this.showDuration,
triggerMode: triggerMode ?? this.triggerMode, triggerMode: triggerMode ?? this.triggerMode,
...@@ -152,6 +158,7 @@ class TooltipThemeData with Diagnosticable { ...@@ -152,6 +158,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics: t < 0.5 ? a?.excludeFromSemantics : b?.excludeFromSemantics, excludeFromSemantics: t < 0.5 ? a?.excludeFromSemantics : b?.excludeFromSemantics,
decoration: Decoration.lerp(a?.decoration, b?.decoration, t), decoration: Decoration.lerp(a?.decoration, b?.decoration, t),
textStyle: TextStyle.lerp(a?.textStyle, b?.textStyle, 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 { ...@@ -165,6 +172,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics, excludeFromSemantics,
decoration, decoration,
textStyle, textStyle,
textAlign,
waitDuration, waitDuration,
showDuration, showDuration,
triggerMode, triggerMode,
...@@ -186,6 +194,7 @@ class TooltipThemeData with Diagnosticable { ...@@ -186,6 +194,7 @@ class TooltipThemeData with Diagnosticable {
&& other.excludeFromSemantics == excludeFromSemantics && other.excludeFromSemantics == excludeFromSemantics
&& other.decoration == decoration && other.decoration == decoration
&& other.textStyle == textStyle && other.textStyle == textStyle
&& other.textAlign == textAlign
&& other.waitDuration == waitDuration && other.waitDuration == waitDuration
&& other.showDuration == showDuration && other.showDuration == showDuration
&& other.triggerMode == triggerMode && other.triggerMode == triggerMode
...@@ -203,6 +212,7 @@ class TooltipThemeData with Diagnosticable { ...@@ -203,6 +212,7 @@ class TooltipThemeData with Diagnosticable {
properties.add(FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true)); properties.add(FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true));
properties.add(DiagnosticsProperty<Decoration>('decoration', decoration, defaultValue: null)); properties.add(DiagnosticsProperty<Decoration>('decoration', decoration, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle>('textStyle', textStyle, 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>('wait duration', waitDuration, defaultValue: null));
properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null)); properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null));
properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null)); properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null));
......
...@@ -677,6 +677,41 @@ void main() { ...@@ -677,6 +677,41 @@ void main() {
expect(textStyle.decoration, TextDecoration.underline); 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 { testWidgets('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/40702. // Regression test for https://github.com/flutter/flutter/issues/40702.
Widget buildApp(String text, TextDirection textDirection) { Widget buildApp(String text, TextDirection textDirection) {
......
...@@ -46,6 +46,7 @@ void main() { ...@@ -46,6 +46,7 @@ void main() {
expect(theme.excludeFromSemantics, null); expect(theme.excludeFromSemantics, null);
expect(theme.decoration, null); expect(theme.decoration, null);
expect(theme.textStyle, null); expect(theme.textStyle, null);
expect(theme.textAlign, null);
expect(theme.waitDuration, null); expect(theme.waitDuration, null);
expect(theme.showDuration, null); expect(theme.showDuration, null);
expect(theme.triggerMode, null); expect(theme.triggerMode, null);
...@@ -78,6 +79,7 @@ void main() { ...@@ -78,6 +79,7 @@ void main() {
excludeFromSemantics: true, excludeFromSemantics: true,
decoration: BoxDecoration(color: Color(0xffffffff)), decoration: BoxDecoration(color: Color(0xffffffff)),
textStyle: TextStyle(decoration: TextDecoration.underline), textStyle: TextStyle(decoration: TextDecoration.underline),
textAlign: TextAlign.center,
waitDuration: wait, waitDuration: wait,
showDuration: show, showDuration: show,
triggerMode: triggerMode, triggerMode: triggerMode,
...@@ -97,6 +99,7 @@ void main() { ...@@ -97,6 +99,7 @@ void main() {
'semantics: excluded', 'semantics: excluded',
'decoration: BoxDecoration(color: Color(0xffffffff))', 'decoration: BoxDecoration(color: Color(0xffffffff))',
'textStyle: TextStyle(inherit: true, decoration: TextDecoration.underline)', 'textStyle: TextStyle(inherit: true, decoration: TextDecoration.underline)',
'textAlign: TextAlign.center',
'wait duration: $wait', 'wait duration: $wait',
'show duration: $show', 'show duration: $show',
'triggerMode: $triggerMode', 'triggerMode: $triggerMode',
...@@ -651,6 +654,45 @@ void main() { ...@@ -651,6 +654,45 @@ void main() {
expect(textStyle.decoration, TextDecoration.underline); 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 { testWidgets('Tooltip decoration - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey key = GlobalKey();
const Decoration customDecoration = ShapeDecoration( 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