rtl fixes
This commit is contained in:
parent
273ec45a28
commit
059e852ed1
10 changed files with 39 additions and 19 deletions
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:aves/app_flavor.dart';
|
import 'package:aves/app_flavor.dart';
|
||||||
import 'package:aves/flutter_version.dart';
|
import 'package:aves/flutter_version.dart';
|
||||||
|
@ -98,6 +99,7 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
|
||||||
// as of Flutter v3.0.0, `SelectableText` does not allow passing the `scrollController`
|
// as of Flutter v3.0.0, `SelectableText` does not allow passing the `scrollController`
|
||||||
child: SelectableText(
|
child: SelectableText(
|
||||||
info,
|
info,
|
||||||
|
textDirection: ui.TextDirection.ltr,
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -58,7 +58,6 @@ class AvesApp extends StatefulWidget {
|
||||||
// temporary exclude locales not ready yet for prime time
|
// temporary exclude locales not ready yet for prime time
|
||||||
// `ckb`: add `flutter_ckb_localization` and necessary app localization delegates when ready
|
// `ckb`: add `flutter_ckb_localization` and necessary app localization delegates when ready
|
||||||
static final _unsupportedLocales = {
|
static final _unsupportedLocales = {
|
||||||
'ar', // Arabic
|
|
||||||
'bn', // Bengali
|
'bn', // Bengali
|
||||||
'ckb', // Kurdish (Central)
|
'ckb', // Kurdish (Central)
|
||||||
'fa', // Persian
|
'fa', // Persian
|
||||||
|
|
|
@ -113,17 +113,22 @@ class RenderMosaicGridRow extends RenderBox with ContainerRenderObjectMixin<Rend
|
||||||
final thumbnailHeight = rowLayout.height - spacing;
|
final thumbnailHeight = rowLayout.height - spacing;
|
||||||
size = Size(constraints.maxWidth, rowLayout.height);
|
size = Size(constraints.maxWidth, rowLayout.height);
|
||||||
final flipMainAxis = textDirection == TextDirection.rtl;
|
final flipMainAxis = textDirection == TextDirection.rtl;
|
||||||
final sign = (flipMainAxis ? -1.0 : 1.0);
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var offset = Offset(flipMainAxis ? size.width - rowLayout.itemWidths[i] : 0, 0);
|
double offsetX = flipMainAxis ? size.width : 0;
|
||||||
while (child != null) {
|
while (child != null) {
|
||||||
final thumbnailWidth = rowLayout.itemWidths[i];
|
final thumbnailWidth = rowLayout.itemWidths[i];
|
||||||
final childConstraints = BoxConstraints.tight(Size(thumbnailWidth, thumbnailHeight));
|
final childConstraints = BoxConstraints.tight(Size(thumbnailWidth, thumbnailHeight));
|
||||||
child.layout(childConstraints, parentUsesSize: false);
|
child.layout(childConstraints, parentUsesSize: false);
|
||||||
final childParentData = child.parentData! as _GridRowParentData;
|
final childParentData = child.parentData! as _GridRowParentData;
|
||||||
childParentData.offset = offset;
|
if (flipMainAxis) {
|
||||||
final dx = sign * (thumbnailWidth + spacing);
|
offsetX -= thumbnailWidth;
|
||||||
offset += Offset(dx, 0);
|
}
|
||||||
|
childParentData.offset = Offset(offsetX, 0);
|
||||||
|
if (flipMainAxis) {
|
||||||
|
offsetX -= spacing;
|
||||||
|
} else {
|
||||||
|
offsetX += thumbnailWidth + spacing;
|
||||||
|
}
|
||||||
child = childParentData.nextSibling;
|
child = childParentData.nextSibling;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ class _AvesMultiSelectionDialogState<T> extends State<AvesMultiSelectionDialog<T
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
title: Align(
|
title: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
child: Text(title),
|
child: Text(title),
|
||||||
),
|
),
|
||||||
subtitle: subtitle != null
|
subtitle: subtitle != null
|
||||||
|
|
|
@ -15,8 +15,8 @@ class HomeWidgetPainter {
|
||||||
final double devicePixelRatio;
|
final double devicePixelRatio;
|
||||||
|
|
||||||
static const backgroundGradient = LinearGradient(
|
static const backgroundGradient = LinearGradient(
|
||||||
begin: Alignment.bottomLeft,
|
begin: AlignmentDirectional.bottomStart,
|
||||||
end: Alignment.topRight,
|
end: AlignmentDirectional.topEnd,
|
||||||
colors: AColors.boraBoraGradient,
|
colors: AColors.boraBoraGradient,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
class SupportedLocales {
|
class SupportedLocales {
|
||||||
static const languagesByLanguageCode = {
|
static const languagesByLanguageCode = {
|
||||||
|
'ar': 'العربية',
|
||||||
'be': 'Беларуская мова',
|
'be': 'Беларуская мова',
|
||||||
'cs': 'Čeština',
|
'cs': 'Čeština',
|
||||||
'de': 'Deutsch',
|
'de': 'Deutsch',
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:aves/widgets/settings/common/tiles.dart';
|
||||||
import 'package:aves/widgets/settings/settings_definition.dart';
|
import 'package:aves/widgets/settings/settings_definition.dart';
|
||||||
import 'package:aves/widgets/settings/thumbnails/collection_actions_editor_page.dart';
|
import 'package:aves/widgets/settings/thumbnails/collection_actions_editor_page.dart';
|
||||||
import 'package:aves/widgets/settings/thumbnails/overlay.dart';
|
import 'package:aves/widgets/settings/thumbnails/overlay.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -68,6 +69,6 @@ class SettingsTileBurstPatterns extends SettingsTile {
|
||||||
onSelection: (v) => settings.collectionBurstPatterns = v,
|
onSelection: (v) => settings.collectionBurstPatterns = v,
|
||||||
tileTitle: title(context),
|
tileTitle: title(context),
|
||||||
noneSubtitle: context.l10n.settingsCollectionBurstPatternsNone,
|
noneSubtitle: context.l10n.settingsCollectionBurstPatternsNone,
|
||||||
optionSubtitleBuilder: BurstPatterns.getExample,
|
optionSubtitleBuilder: (value) => '${Unicode.FSI}${BurstPatterns.getExample(value)}${Unicode.PDI}',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,8 @@ class SubtitleSample extends StatelessWidget {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
gradient: const LinearGradient(
|
||||||
begin: Alignment.bottomLeft,
|
begin: AlignmentDirectional.bottomStart,
|
||||||
end: Alignment.topRight,
|
end: AlignmentDirectional.topEnd,
|
||||||
colors: AColors.boraBoraGradient,
|
colors: AColors.boraBoraGradient,
|
||||||
),
|
),
|
||||||
border: AvesBorder.border(context),
|
border: AvesBorder.border(context),
|
||||||
|
|
|
@ -360,7 +360,7 @@ class _BasicInfoState extends State<_BasicInfo> {
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: PlaceholderAlignment.middle,
|
alignment: PlaceholderAlignment.middle,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsetsDirectional.only(end: 4),
|
padding: const EdgeInsetsDirectional.only(start: 2, end: 4),
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
// use constraints instead of sizing `Image`,
|
// use constraints instead of sizing `Image`,
|
||||||
// so that it can collapse when handling an empty image
|
// so that it can collapse when handling an empty image
|
||||||
|
|
|
@ -116,20 +116,32 @@ class _InfoRowGroupState extends State<InfoRowGroup> {
|
||||||
(kv) {
|
(kv) {
|
||||||
final key = kv.key;
|
final key = kv.key;
|
||||||
final value = kv.value;
|
final value = kv.value;
|
||||||
final spanBuilder = spanBuilders[key] ?? _buildTextValueSpans;
|
final customSpanBuilder = spanBuilders[key];
|
||||||
|
final spanBuilder = customSpanBuilder ?? _buildTextValueSpans;
|
||||||
final thisSpaceSize = max(0.0, (baseValueX - keySizes[key]!)) + InfoRowGroup.keyValuePadding;
|
final thisSpaceSize = max(0.0, (baseValueX - keySizes[key]!)) + InfoRowGroup.keyValuePadding;
|
||||||
final textScaleFactor = textScaler.scale(thisSpaceSize) / thisSpaceSize;
|
|
||||||
|
|
||||||
return [
|
InlineSpan paddingSpan;
|
||||||
TextSpan(text: _buildTextValue(key), style: _keyStyle),
|
if (customSpanBuilder != null) {
|
||||||
WidgetSpan(
|
// add padding using hair spaces instead of a straightforward `SizedBox` in a `WidgetSpan`,
|
||||||
|
// because ordering of multiple `WidgetSpan`s (e.g. with owner app icon) in Bidi context is tricky
|
||||||
|
final baseSpaceWidth = _getSpanWidth(TextSpan(text: '\u200A' * 100, style: _keyStyle), textScaler);
|
||||||
|
final spaceCount = (100 * thisSpaceSize / baseSpaceWidth).round();
|
||||||
|
paddingSpan = TextSpan(text: '\u200A' * spaceCount);
|
||||||
|
} else {
|
||||||
|
final textScaleFactor = textScaler.scale(thisSpaceSize) / thisSpaceSize;
|
||||||
|
paddingSpan = WidgetSpan(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: thisSpaceSize / textScaleFactor,
|
width: thisSpaceSize / textScaleFactor,
|
||||||
// as of Flutter v3.0.0, the underline decoration from the following `TextSpan`
|
// as of Flutter v3.0.0, the underline decoration from the following `TextSpan`
|
||||||
// is applied to the `WidgetSpan` too, so we add a dummy `Text` as a workaround
|
// is applied to the `WidgetSpan` too, so we add a dummy `Text` as a workaround
|
||||||
child: const Text(''),
|
child: const Text(''),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
TextSpan(text: _buildTextValue(key), style: _keyStyle),
|
||||||
|
paddingSpan,
|
||||||
...spanBuilder(context, key, value),
|
...spanBuilder(context, key, value),
|
||||||
if (key != lastKey) const TextSpan(text: '\n'),
|
if (key != lastKey) const TextSpan(text: '\n'),
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue