#381 fixed inconsistent background height for multi-script subtitles
This commit is contained in:
parent
22149ffca2
commit
15c225fa89
5 changed files with 107 additions and 13 deletions
|
@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file.
|
|||
- rendering of panoramas with inconsistent metadata
|
||||
- failing scan of items copied to SD card on older devices
|
||||
- unreplaceable covers set before v1.7.1
|
||||
- inconsistent background height for multi-script subtitles
|
||||
|
||||
## <a id="v1.7.1"></a>[v1.7.1] - 2022-10-09
|
||||
|
||||
|
|
76
lib/widgets/common/basic/text_background_painter.dart
Normal file
76
lib/widgets/common/basic/text_background_painter.dart
Normal file
|
@ -0,0 +1,76 @@
|
|||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
// as of Flutter v3.3.7, text style background does not have consistent height
|
||||
// when rendering multi-script text, so we paint the background behind via a stack instead
|
||||
class TextBackgroundPainter extends StatelessWidget {
|
||||
final List<TextSpan> spans;
|
||||
final TextStyle style;
|
||||
final TextAlign textAlign;
|
||||
final Widget child;
|
||||
|
||||
const TextBackgroundPainter({
|
||||
super.key,
|
||||
required this.spans,
|
||||
required this.style,
|
||||
required this.textAlign,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backgroundColor = style.backgroundColor;
|
||||
if (backgroundColor == null || backgroundColor.alpha == 0) {
|
||||
return child;
|
||||
}
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final paragraph = RenderParagraph(
|
||||
TextSpan(
|
||||
children: spans,
|
||||
style: style,
|
||||
),
|
||||
textAlign: textAlign,
|
||||
textDirection: Directionality.of(context),
|
||||
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
||||
)..layout(constraints, parentUsesSize: true);
|
||||
|
||||
final textLength = spans.map((v) => v.text?.length ?? 0).sum;
|
||||
final allBoxes = paragraph.getBoxesForSelection(
|
||||
TextSelection(baseOffset: 0, extentOffset: textLength),
|
||||
boxHeightStyle: ui.BoxHeightStyle.max,
|
||||
);
|
||||
|
||||
// merge boxes to avoid artifacts at box edges, from anti-aliasing and rounding hacks
|
||||
final lineRects = groupBy<TextBox, double>(allBoxes, (v) => v.top).entries.map((kv) {
|
||||
final top = kv.key;
|
||||
final lineBoxes = kv.value;
|
||||
return Rect.fromLTRB(
|
||||
lineBoxes.map((v) => v.left).min,
|
||||
top,
|
||||
lineBoxes.map((v) => v.right).max,
|
||||
lineBoxes.first.bottom,
|
||||
);
|
||||
});
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
...lineRects.map((rect) {
|
||||
return Positioned.fromRect(
|
||||
rect: rect,
|
||||
child: ColoredBox(
|
||||
color: backgroundColor,
|
||||
),
|
||||
);
|
||||
}),
|
||||
child,
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/widgets/common/basic/outlined_text.dart';
|
||||
import 'package:aves/widgets/common/basic/text_background_painter.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/fx/borders.dart';
|
||||
import 'package:aves/widgets/viewer/visual/subtitle/subtitle.dart';
|
||||
|
@ -12,8 +13,13 @@ class SubtitleSample extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textSpans = [
|
||||
TextSpan(text: context.l10n.settingsSubtitleThemeSample),
|
||||
];
|
||||
|
||||
return Consumer<Settings>(
|
||||
builder: (context, settings, child) {
|
||||
final textAlign = settings.subtitleTextAlignment;
|
||||
final outlineColor = Colors.black.withOpacity(settings.subtitleTextColor.opacity);
|
||||
final shadows = [
|
||||
Shadow(
|
||||
|
@ -34,7 +40,7 @@ class SubtitleSample extends StatelessWidget {
|
|||
),
|
||||
height: 128,
|
||||
child: AnimatedAlign(
|
||||
alignment: _getAlignment(settings.subtitleTextAlignment),
|
||||
alignment: _getAlignment(textAlign),
|
||||
curve: Curves.easeInOutCubic,
|
||||
duration: const Duration(milliseconds: 400),
|
||||
child: Padding(
|
||||
|
@ -42,20 +48,24 @@ class SubtitleSample extends StatelessWidget {
|
|||
child: AnimatedDefaultTextStyle(
|
||||
style: TextStyle(
|
||||
color: settings.subtitleTextColor,
|
||||
backgroundColor: settings.subtitleBackgroundColor,
|
||||
fontSize: settings.subtitleFontSize,
|
||||
shadows: settings.subtitleShowOutline ? shadows : null,
|
||||
),
|
||||
textAlign: settings.subtitleTextAlignment,
|
||||
textAlign: textAlign,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: OutlinedText(
|
||||
textSpans: [
|
||||
TextSpan(
|
||||
text: context.l10n.settingsSubtitleThemeSample,
|
||||
child: Builder(
|
||||
builder: (context) => TextBackgroundPainter(
|
||||
spans: textSpans,
|
||||
style: DefaultTextStyle.of(context).style.copyWith(
|
||||
backgroundColor: settings.subtitleBackgroundColor,
|
||||
),
|
||||
textAlign: textAlign,
|
||||
child: OutlinedText(
|
||||
textSpans: textSpans,
|
||||
outlineWidth: settings.subtitleShowOutline ? 1 : 0,
|
||||
outlineColor: outlineColor,
|
||||
),
|
||||
],
|
||||
outlineWidth: settings.subtitleShowOutline ? 1 : 0,
|
||||
outlineColor: outlineColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -389,7 +389,7 @@ class AssParser {
|
|||
);
|
||||
}
|
||||
|
||||
static String _replaceChars(String text) => text.replaceAll(r'\h', noBreakSpace).replaceAll(r'\N', '\n');
|
||||
static String _replaceChars(String text) => text.replaceAll(r'\h', noBreakSpace).replaceAll(r'\N', '\n').trim();
|
||||
|
||||
static int? _parseAlpha(String param) {
|
||||
final match = alphaPattern.firstMatch(param);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/widgets/common/basic/outlined_text.dart';
|
||||
import 'package:aves/widgets/common/basic/text_background_painter.dart';
|
||||
import 'package:aves/widgets/viewer/video/controller.dart';
|
||||
import 'package:aves/widgets/viewer/visual/state.dart';
|
||||
import 'package:aves/widgets/viewer/visual/subtitle/ass_parser.dart';
|
||||
|
@ -42,7 +43,6 @@ class VideoSubtitles extends StatelessWidget {
|
|||
];
|
||||
final baseStyle = TextStyle(
|
||||
color: settings.subtitleTextColor,
|
||||
backgroundColor: settings.subtitleBackgroundColor,
|
||||
fontSize: settings.subtitleFontSize,
|
||||
shadows: settings.subtitleShowOutline ? baseShadows : null,
|
||||
);
|
||||
|
@ -243,7 +243,14 @@ class VideoSubtitles extends StatelessWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Align(
|
||||
alignment: Alignment(alignX, alignY),
|
||||
child: child,
|
||||
child: TextBackgroundPainter(
|
||||
spans: spans,
|
||||
style: DefaultTextStyle.of(context).style.merge(spans.first.style!.copyWith(
|
||||
backgroundColor: settings.subtitleBackgroundColor,
|
||||
)),
|
||||
textAlign: textAlign,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue