fixed text / strut styles

This commit is contained in:
Thibault Deckers 2023-12-20 14:21:06 +01:00
parent 2d5de662ff
commit 81977ea4d8
18 changed files with 59 additions and 59 deletions

View file

@ -3,10 +3,6 @@ import 'dart:ui';
import 'package:flutter/painting.dart';
class AStyles {
// as of Flutter v2.8.0, overflowing `Text` miscalculates height and some text (e.g. 'Å') is clipped
// so we give it a `strutStyle` with a slightly larger height
static const overflowStrut = StrutStyle(height: 1.3);
static const knownTitleText = TextStyle(
fontSize: 20,
fontWeight: FontWeight.w300,

View file

@ -112,7 +112,6 @@ class _RandomTextSpanHighlighterState extends State<_RandomTextSpanHighlighter>
])
],
),
strutStyle: const StrutStyle(height: 1.5, forceStrutHeight: true),
);
}
}

View file

@ -4,7 +4,6 @@ import 'package:aves/model/settings/enums/coordinate_format.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/format.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/utils/collection_utils.dart';
import 'package:aves/utils/file_utils.dart';
@ -60,7 +59,6 @@ class EntryListDetails extends StatelessWidget {
children: spans,
),
style: style,
strutStyle: AStyles.overflowStrut,
softWrap: false,
overflow: TextOverflow.fade,
);

View file

@ -1,5 +1,4 @@
import 'package:aves/theme/format.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
@ -29,11 +28,16 @@ class EntryListDetailsTheme extends StatelessWidget {
final textScaler = mq.textScaler;
final textTheme = Theme.of(context).textTheme;
final titleStyle = textTheme.bodyMedium!;
final captionStyle = textTheme.bodySmall!;
// specify `height` for accurate paragraph height measurement
final defaultTextHeight = DefaultTextStyle.of(context).style.height;
final titleStyle = textTheme.bodyMedium!.copyWith(height: defaultTextHeight);
final captionStyle = textTheme.bodySmall!.copyWith(height: defaultTextHeight);
final titleLineHeightParagraph = RenderParagraph(
TextSpan(text: 'Fake Title', style: titleStyle),
TextSpan(
text: 'Fake Title',
style: titleStyle,
),
textDirection: TextDirection.ltr,
textScaler: textScaler,
)..layout(const BoxConstraints(), parentUsesSize: true);
@ -41,10 +45,12 @@ class EntryListDetailsTheme extends StatelessWidget {
titleLineHeightParagraph.dispose();
final captionLineHeightParagraph = RenderParagraph(
TextSpan(text: formatDateTime(DateTime.now(), locale, use24hour), style: captionStyle),
TextSpan(
text: formatDateTime(DateTime.now(), locale, use24hour),
style: captionStyle,
),
textDirection: TextDirection.ltr,
textScaler: textScaler,
strutStyle: AStyles.overflowStrut,
)..layout(const BoxConstraints(), parentUsesSize: true);
final captionLineHeight = captionLineHeightParagraph.getMaxIntrinsicHeight(double.infinity);

View file

@ -29,6 +29,14 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
late final Animation<double> _animation;
final List<_TextDiff> _diffs = [];
TextStyle get _textStyle {
final style = widget.textStyle ?? const TextStyle();
// specify `height` for accurate paragraph height measurement
return style.copyWith(height: style.height ?? DefaultTextStyle.of(context).style.height);
}
StrutStyle? get _strutStyle => widget.strutStyle;
@override
void initState() {
super.initState();
@ -101,24 +109,25 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
child: Text(
text,
key: Key(text),
style: _textStyle,
),
),
),
);
}).toList(),
),
strutStyle: widget.strutStyle,
strutStyle: _strutStyle,
);
},
);
}
Size textSize(String text) {
Size _textSize(String text) {
final paragraph = RenderParagraph(
TextSpan(text: text, style: widget.textStyle),
TextSpan(text: text, style: _textStyle),
textDirection: Directionality.of(context),
textScaler: MediaQuery.textScalerOf(context),
strutStyle: widget.strutStyle,
strutStyle: _strutStyle,
)..layout(const BoxConstraints(), parentUsesSize: true);
final width = paragraph.getMaxIntrinsicWidth(double.infinity);
final height = paragraph.getMaxIntrinsicHeight(double.infinity);
@ -140,7 +149,7 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
..clear()
..addAll(d.map((diff) {
final text = diff.text;
final size = textSize(text);
final size = _textSize(text);
return switch (diff.operation) {
Operation.delete => (text, null, size, Size.zero),
Operation.insert => (null, text, Size.zero, size),

View file

@ -68,7 +68,7 @@ class SectionHeader<T> extends StatelessWidget {
sectionKey: sectionKey,
browsingBuilder: leading != null
? (context) => Container(
padding: const EdgeInsetsDirectional.only(end: 8, bottom: 4),
padding: const EdgeInsetsDirectional.only(end: 8),
width: leadingSize.width,
height: leadingSize.height,
child: leading,
@ -80,7 +80,7 @@ class SectionHeader<T> extends StatelessWidget {
),
TextSpan(
text: title,
style: AStyles.unknownTitleText,
style: _headerTextStyle(context),
),
if (trailing != null)
WidgetSpan(
@ -152,7 +152,7 @@ class SectionHeader<T> extends StatelessWidget {
if (hasTrailing) TextSpan(text: '\u200A' * 17),
TextSpan(
text: title,
style: AStyles.unknownTitleText,
style: _headerTextStyle(context),
),
],
),
@ -163,6 +163,12 @@ class SectionHeader<T> extends StatelessWidget {
paragraph.dispose();
return height;
}
static TextStyle _headerTextStyle(BuildContext context) {
// specify `height` for accurate paragraph height measurement
final defaultTextHeight = DefaultTextStyle.of(context).style.height;
return AStyles.unknownTitleText.copyWith(height: defaultTextHeight);
}
}
class _SectionSelectableLeading<T> extends StatelessWidget {

View file

@ -39,7 +39,7 @@ class CaptionedButton extends StatefulWidget {
var height = width;
if (showCaption) {
final paragraph = RenderParagraph(
TextSpan(text: text, style: CaptionedButtonText.textStyle(context)),
TextSpan(text: text, style: CaptionedButtonText._textStyle(context)),
textDirection: TextDirection.ltr,
textScaler: MediaQuery.textScalerOf(context),
maxLines: CaptionedButtonText.maxLines,
@ -112,7 +112,7 @@ class _CaptionedButtonState extends State<CaptionedButton> {
child: ValueListenableBuilder<bool>(
valueListenable: _focusedNotifier,
builder: (context, focused, child) {
final style = CaptionedButtonText.textStyle(context);
final style = CaptionedButtonText._textStyle(context);
return AnimatedDefaultTextStyle(
style: focused
? style.copyWith(
@ -176,5 +176,9 @@ class CaptionedButtonText extends StatelessWidget {
);
}
static TextStyle textStyle(BuildContext context) => Theme.of(context).textTheme.bodySmall!;
static TextStyle _textStyle(BuildContext context) {
// specify `height` for accurate paragraph height measurement
final defaultTextHeight = DefaultTextStyle.of(context).style.height;
return Theme.of(context).textTheme.bodySmall!.copyWith(height: defaultTextHeight);
}
}

View file

@ -8,7 +8,6 @@ import 'package:aves/services/common/services.dart';
import 'package:aves/services/geocoding_service.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/utils/debouncer.dart';
import 'package:aves/widgets/common/basic/scaffold.dart';
@ -280,7 +279,6 @@ class _AddressRowState extends State<_AddressRow> {
builder: (context, addressLine, child) {
return Text(
addressLine ?? AText.valueNotAvailable,
strutStyle: AStyles.overflowStrut,
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,
@ -331,7 +329,6 @@ class _CoordinateRow extends StatelessWidget {
Expanded(
child: Text(
location != null ? settings.coordinateFormat.format(context, location!) : AText.valueNotAvailable,
strutStyle: AStyles.overflowStrut,
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,

View file

@ -4,7 +4,6 @@ import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/theme/format.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/file_utils.dart';
@ -107,7 +106,6 @@ class FilterListDetails<T extends CollectionFilter> extends StatelessWidget {
child: Text(
dateText,
style: detailsTheme.captionStyle,
strutStyle: AStyles.overflowStrut,
softWrap: false,
overflow: TextOverflow.fade,
),
@ -158,7 +156,6 @@ class FilterListDetails<T extends CollectionFilter> extends StatelessWidget {
Text(
'${l10n.itemCount(source.count(filter))}${formatFileSize(locale, source.size(filter))}',
style: detailsTheme.captionStyle,
strutStyle: AStyles.overflowStrut,
softWrap: false,
overflow: TextOverflow.fade,
),

View file

@ -1,7 +1,6 @@
import 'dart:math';
import 'package:aves/theme/format.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:flutter/material.dart';
@ -34,8 +33,10 @@ class FilterListDetailsTheme extends StatelessWidget {
final textTheme = Theme.of(context).textTheme;
final titleStyleBase = textTheme.bodyMedium!;
final titleStyle = titleStyleBase.copyWith(fontSize: textScaler.scale(titleStyleBase.fontSize!));
final captionStyle = textTheme.bodySmall!;
// specify `height` for accurate paragraph height measurement
final defaultTextHeight = DefaultTextStyle.of(context).style.height;
final titleStyle = titleStyleBase.copyWith(fontSize: textScaler.scale(titleStyleBase.fontSize!), height: defaultTextHeight);
final captionStyle = textTheme.bodySmall!.copyWith(height: defaultTextHeight);
final titleIconSize = textScaler.scale(AvesFilterChip.iconSize);
final titleLineHeightParagraph = RenderParagraph(
@ -50,7 +51,6 @@ class FilterListDetailsTheme extends StatelessWidget {
TextSpan(text: formatDateTime(DateTime.now(), locale, use24hour), style: captionStyle),
textDirection: TextDirection.ltr,
textScaler: textScaler,
strutStyle: AStyles.overflowStrut,
)..layout(const BoxConstraints(), parentUsesSize: true);
final captionLineHeight = captionLineHeightParagraph.getMaxIntrinsicHeight(double.infinity);
captionLineHeightParagraph.dispose();

View file

@ -5,7 +5,6 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/services/geocoding_service.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:flutter/material.dart';
@ -78,7 +77,6 @@ class _MapAddressRowState extends State<MapAddressRow> {
TextSpan(text: location),
],
),
strutStyle: AStyles.overflowStrut,
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,

View file

@ -1,7 +1,6 @@
import 'package:aves/model/entry/entry.dart';
import 'package:aves/theme/format.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/map/info_row.dart';
@ -35,7 +34,6 @@ class MapDateRow extends StatelessWidget {
TextSpan(text: dateText),
],
),
strutStyle: AStyles.overflowStrut,
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,

View file

@ -2,7 +2,6 @@ import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/theme/format.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/viewer/multipage/controller.dart';
@ -37,8 +36,8 @@ class OverlayDateRow extends StatelessWidget {
children: [
DecoratedIcon(AIcons.date, size: ViewerDetailOverlayContent.iconSize, shadows: ViewerDetailOverlayContent.shadows(context)),
const SizedBox(width: ViewerDetailOverlayContent.iconPadding),
Expanded(flex: 3, child: Text(dateText, strutStyle: AStyles.overflowStrut)),
Expanded(flex: 2, child: Text(resolutionText, strutStyle: AStyles.overflowStrut)),
Expanded(flex: 3, child: Text(dateText)),
Expanded(flex: 2, child: Text(resolutionText)),
],
);
}

View file

@ -1,5 +1,4 @@
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/widgets/viewer/overlay/details/details.dart';
import 'package:decorated_icon/decorated_icon.dart';
import 'package:flutter/material.dart';
@ -31,7 +30,6 @@ class OverlayDescriptionRow extends StatelessWidget {
TextSpan(text: description),
],
),
strutStyle: AStyles.overflowStrut,
);
}
}

View file

@ -3,7 +3,6 @@ import 'package:aves/model/entry/extensions/location.dart';
import 'package:aves/model/settings/enums/coordinate_format.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/widgets/viewer/overlay/details/details.dart';
import 'package:decorated_icon/decorated_icon.dart';
@ -33,7 +32,7 @@ class OverlayLocationRow extends AnimatedWidget {
children: [
DecoratedIcon(AIcons.location, size: ViewerDetailOverlayContent.iconSize, shadows: ViewerDetailOverlayContent.shadows(context)),
const SizedBox(width: ViewerDetailOverlayContent.iconPadding),
Expanded(child: Text(location ?? AText.valueNotAvailable, strutStyle: AStyles.overflowStrut)),
Expanded(child: Text(location ?? AText.valueNotAvailable)),
],
);
}

View file

@ -1,6 +1,5 @@
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/multipage.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/widgets/viewer/multipage/controller.dart';
import 'package:flutter/foundation.dart';
@ -25,12 +24,12 @@ class OverlayPositionTitleRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
Text toText({String? pagePosition}) => Text(
[
if (collectionPosition != null) collectionPosition,
if (pagePosition != null) pagePosition,
if (title != null) '${Unicode.FSI}$title${Unicode.PDI}',
].join(AText.separator),
strutStyle: AStyles.overflowStrut);
[
if (collectionPosition != null) collectionPosition,
if (pagePosition != null) pagePosition,
if (title != null) '${Unicode.FSI}$title${Unicode.PDI}',
].join(AText.separator),
);
if (multiPageController == null) return toText();

View file

@ -1,6 +1,5 @@
import 'package:aves/model/entry/entry.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/viewer/overlay/details/details.dart';
@ -57,7 +56,6 @@ class OverlayRatingTagsRow extends AnimatedWidget {
]
],
),
strutStyle: AStyles.overflowStrut,
);
}
}

View file

@ -1,6 +1,5 @@
import 'package:aves/model/metadata/overlay.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/viewer/overlay/details/details.dart';
@ -33,10 +32,10 @@ class OverlayShootingRow extends StatelessWidget {
children: [
DecoratedIcon(AIcons.shooting, size: ViewerDetailOverlayContent.iconSize, shadows: ViewerDetailOverlayContent.shadows(context)),
const SizedBox(width: ViewerDetailOverlayContent.iconPadding),
Expanded(child: Text(apertureText, strutStyle: AStyles.overflowStrut)),
Expanded(child: Text(details.exposureTime ?? AText.valueNotAvailable, strutStyle: AStyles.overflowStrut)),
Expanded(child: Text(focalLengthText, strutStyle: AStyles.overflowStrut)),
Expanded(child: Text(isoText, strutStyle: AStyles.overflowStrut)),
Expanded(child: Text(apertureText)),
Expanded(child: Text(details.exposureTime ?? AText.valueNotAvailable)),
Expanded(child: Text(focalLengthText)),
Expanded(child: Text(isoText)),
],
);
}