rtl prep
This commit is contained in:
parent
2a4c07a657
commit
d44b001bb7
33 changed files with 268 additions and 177 deletions
|
@ -3,10 +3,11 @@
|
||||||
"welcomeMessage": "Willkommen bei Aves",
|
"welcomeMessage": "Willkommen bei Aves",
|
||||||
"welcomeOptional": "Optional",
|
"welcomeOptional": "Optional",
|
||||||
"welcomeTermsToggle": "Ich stimme den Bedingungen und Konditionen zu",
|
"welcomeTermsToggle": "Ich stimme den Bedingungen und Konditionen zu",
|
||||||
"itemCount": " {count, plural, =1{1 Element} other{{count} Elemente}}",
|
"itemCount": "{count, plural, =1{1 Element} other{{count} Elemente}}",
|
||||||
|
|
||||||
"timeSeconds": " {seconds, plural, =1{1 Sekunde} other{{seconds} Sekunde}}",
|
"timeSeconds": "{seconds, plural, =1{1 Sekunde} other{{seconds} Sekunde}}",
|
||||||
"timeMinutes": " {minutes, plural, =1{1 Minute} other{{minutes} Minuten}}",
|
"timeMinutes": "{minutes, plural, =1{1 Minute} other{{minutes} Minuten}}",
|
||||||
|
"focalLength": "{length} mm",
|
||||||
|
|
||||||
"applyButtonLabel": "ANWENDEN",
|
"applyButtonLabel": "ANWENDEN",
|
||||||
"deleteButtonLabel": "LÖSCHEN",
|
"deleteButtonLabel": "LÖSCHEN",
|
||||||
|
@ -93,7 +94,7 @@
|
||||||
|
|
||||||
"coordinateFormatDms": "GMS",
|
"coordinateFormatDms": "GMS",
|
||||||
"coordinateFormatDecimal": "Dezimalgrad",
|
"coordinateFormatDecimal": "Dezimalgrad",
|
||||||
"coordinateDms": " {coordinate} {direction}",
|
"coordinateDms": "{coordinate} {direction}",
|
||||||
"coordinateDmsNorth": "N",
|
"coordinateDmsNorth": "N",
|
||||||
"coordinateDmsSouth": "s",
|
"coordinateDmsSouth": "s",
|
||||||
"coordinateDmsEast": "O",
|
"coordinateDmsEast": "O",
|
||||||
|
@ -144,7 +145,7 @@
|
||||||
"missingSystemFilePickerDialogMessage": "Der System-Dateiauswahldialog fehlt oder ist deaktiviert. Bitte aktivieren Sie ihn und versuchen Sie es erneut.",
|
"missingSystemFilePickerDialogMessage": "Der System-Dateiauswahldialog fehlt oder ist deaktiviert. Bitte aktivieren Sie ihn und versuchen Sie es erneut.",
|
||||||
|
|
||||||
"unsupportedTypeDialogTitle": "Nicht unterstützte Typen",
|
"unsupportedTypeDialogTitle": "Nicht unterstützte Typen",
|
||||||
"unsupportedTypeDialogMessage": " {count, plural, =1{Dieser Vorgang wird für Elemente des folgenden Typs nicht unterstützt: {types}.} other{Dieser Vorgang wird für Elemente der folgenden Typen nicht unterstützt: {types}.}}",
|
"unsupportedTypeDialogMessage": "{count, plural, =1{Dieser Vorgang wird für Elemente des folgenden Typs nicht unterstützt: {types}.} other{Dieser Vorgang wird für Elemente der folgenden Typen nicht unterstützt: {types}.}}",
|
||||||
|
|
||||||
"nameConflictDialogSingleSourceMessage": "Einige Dateien im Zielordner haben den gleichen Namen.",
|
"nameConflictDialogSingleSourceMessage": "Einige Dateien im Zielordner haben den gleichen Namen.",
|
||||||
"nameConflictDialogMultipleSourceMessage": "Einige Dateien haben denselben Namen.",
|
"nameConflictDialogMultipleSourceMessage": "Einige Dateien haben denselben Namen.",
|
||||||
|
@ -155,7 +156,7 @@
|
||||||
"noMatchingAppDialogTitle": "Keine passende App",
|
"noMatchingAppDialogTitle": "Keine passende App",
|
||||||
"noMatchingAppDialogMessage": "Es gibt keine Anwendungen, die dies bewältigen können.",
|
"noMatchingAppDialogMessage": "Es gibt keine Anwendungen, die dies bewältigen können.",
|
||||||
|
|
||||||
"deleteEntriesConfirmationDialogMessage": " {count, plural, =1{Sind Sie sicher, dass Sie dieses Element löschen möchten?} other{Sind Sie sicher, dass Sie diese {count} Elemente löschen möchten?}}",
|
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Sind Sie sicher, dass Sie dieses Element löschen möchten?} other{Sind Sie sicher, dass Sie diese {count} Elemente löschen möchten?}}",
|
||||||
|
|
||||||
"videoResumeDialogMessage": "Möchten Sie bei {time} weiter abspielen?",
|
"videoResumeDialogMessage": "Möchten Sie bei {time} weiter abspielen?",
|
||||||
"videoStartOverButtonLabel": "NEU BEGINNEN",
|
"videoStartOverButtonLabel": "NEU BEGINNEN",
|
||||||
|
@ -175,8 +176,8 @@
|
||||||
"renameAlbumDialogLabel": "Neuer Name",
|
"renameAlbumDialogLabel": "Neuer Name",
|
||||||
"renameAlbumDialogLabelAlreadyExistsHelper": "Verzeichnis existiert bereits",
|
"renameAlbumDialogLabelAlreadyExistsHelper": "Verzeichnis existiert bereits",
|
||||||
|
|
||||||
"deleteSingleAlbumConfirmationDialogMessage": " {count, plural, =1{Sind Sie sicher, dass Sie dieses Album und ihren Inhalt löschen möchten?} other{Sind Sie sicher, dass Sie dieses Album und deren {count} Elemente löschen möchten?}}",
|
"deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Sind Sie sicher, dass Sie dieses Album und ihren Inhalt löschen möchten?} other{Sind Sie sicher, dass Sie dieses Album und deren {count} Elemente löschen möchten?}}",
|
||||||
"deleteMultiAlbumConfirmationDialogMessage": " {count, plural, =1{Sind Sie sicher, dass Sie diese Alben und ihren Inhalt löschen möchten?} other{Sind Sie sicher, dass Sie diese Alben und deren {count} Elemente löschen möchten?}}",
|
"deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Sind Sie sicher, dass Sie diese Alben und ihren Inhalt löschen möchten?} other{Sind Sie sicher, dass Sie diese Alben und deren {count} Elemente löschen möchten?}}",
|
||||||
|
|
||||||
"exportEntryDialogFormat": "Format:",
|
"exportEntryDialogFormat": "Format:",
|
||||||
|
|
||||||
|
@ -256,7 +257,7 @@
|
||||||
|
|
||||||
"collectionPageTitle": "Sammlung",
|
"collectionPageTitle": "Sammlung",
|
||||||
"collectionPickPageTitle": "Wähle",
|
"collectionPickPageTitle": "Wähle",
|
||||||
"collectionSelectionPageTitle": " {count, plural, =0{Elemente auswählen} =1{1 Element} other{{count} Elemente}}",
|
"collectionSelectionPageTitle": "{count, plural, =0{Elemente auswählen} =1{1 Element} other{{count} Elemente}}",
|
||||||
|
|
||||||
"collectionActionShowTitleSearch": "Titelfilter anzeigen",
|
"collectionActionShowTitleSearch": "Titelfilter anzeigen",
|
||||||
"collectionActionHideTitleSearch": "Titelfilter ausblenden",
|
"collectionActionHideTitleSearch": "Titelfilter ausblenden",
|
||||||
|
@ -282,14 +283,14 @@
|
||||||
"dateToday": "Heute",
|
"dateToday": "Heute",
|
||||||
"dateYesterday": "Gestern",
|
"dateYesterday": "Gestern",
|
||||||
"dateThisMonth": "Diesen Monat",
|
"dateThisMonth": "Diesen Monat",
|
||||||
"collectionDeleteFailureFeedback": " {count, plural, =1{1 Element konnte nicht gelöscht werden} other{{count} Elemente konnten nicht gelöscht werden}}",
|
"collectionDeleteFailureFeedback": "{count, plural, =1{1 Element konnte nicht gelöscht werden} other{{count} Elemente konnten nicht gelöscht werden}}",
|
||||||
"collectionCopyFailureFeedback": " {count, plural, =1{1 Element konnte nicht kopiert werden} other{{count} Element konnten nicht kopiert werden}}",
|
"collectionCopyFailureFeedback": "{count, plural, =1{1 Element konnte nicht kopiert werden} other{{count} Element konnten nicht kopiert werden}}",
|
||||||
"collectionMoveFailureFeedback": " {count, plural, =1{1 Element konnte nicht verschoben werden} other{{count} Elemente konnten nicht verschoben werden}}",
|
"collectionMoveFailureFeedback": "{count, plural, =1{1 Element konnte nicht verschoben werden} other{{count} Elemente konnten nicht verschoben werden}}",
|
||||||
"collectionEditFailureFeedback": " {count, plural, =1{1 Element konnte nicht bearbeitet werden} other{{count} 1 Elemente konnten nicht bearbeitet werden}}",
|
"collectionEditFailureFeedback": "{count, plural, =1{1 Element konnte nicht bearbeitet werden} other{{count} 1 Elemente konnten nicht bearbeitet werden}}",
|
||||||
"collectionExportFailureFeedback": " {count, plural, =1{1 Seite konnte nicht exportiert werden} other{{count} Seiten konnten nicht exportiert werden}}",
|
"collectionExportFailureFeedback": "{count, plural, =1{1 Seite konnte nicht exportiert werden} other{{count} Seiten konnten nicht exportiert werden}}",
|
||||||
"collectionCopySuccessFeedback": " {count, plural, =1{1 Element kopier} other{ {count} Elemente kopiert}}",
|
"collectionCopySuccessFeedback": "{count, plural, =1{1 Element kopier} other{ {count} Elemente kopiert}}",
|
||||||
"collectionMoveSuccessFeedback": " {count, plural, =1{1 Element verschoben} other{{count} Elemente verschoben}}",
|
"collectionMoveSuccessFeedback": "{count, plural, =1{1 Element verschoben} other{{count} Elemente verschoben}}",
|
||||||
"collectionEditSuccessFeedback": " {count, plural, =1{1 Element bearbeitet} other{ {count} Elemente bearbeitet}}",
|
"collectionEditSuccessFeedback": "{count, plural, =1{1 Element bearbeitet} other{ {count} Elemente bearbeitet}}",
|
||||||
|
|
||||||
"collectionEmptyFavourites": "Keine Favoriten",
|
"collectionEmptyFavourites": "Keine Favoriten",
|
||||||
"collectionEmptyVideos": "Keine Videos",
|
"collectionEmptyVideos": "Keine Videos",
|
||||||
|
@ -471,7 +472,7 @@
|
||||||
"settingsUnitSystemTitle": "Einheiten",
|
"settingsUnitSystemTitle": "Einheiten",
|
||||||
|
|
||||||
"statsPageTitle": "Statistiken",
|
"statsPageTitle": "Statistiken",
|
||||||
"statsWithGps": " {count, plural, =1{1 Element mit Standort} other{{count} Elemente mit Standort}}",
|
"statsWithGps": "{count, plural, =1{1 Element mit Standort} other{{count} Elemente mit Standort}}",
|
||||||
"statsTopCountries": "Top-Länder",
|
"statsTopCountries": "Top-Länder",
|
||||||
"statsTopPlaces": "Top-Plätze",
|
"statsTopPlaces": "Top-Plätze",
|
||||||
"statsTopTags": "Top-Tags",
|
"statsTopTags": "Top-Tags",
|
||||||
|
|
|
@ -22,6 +22,15 @@
|
||||||
"minutes": {}
|
"minutes": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"focalLength": "{length} mm",
|
||||||
|
"@focalLength": {
|
||||||
|
"placeholders": {
|
||||||
|
"length": {
|
||||||
|
"type": "String",
|
||||||
|
"example": "5.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"applyButtonLabel": "APPLY",
|
"applyButtonLabel": "APPLY",
|
||||||
"deleteButtonLabel": "DELETE",
|
"deleteButtonLabel": "DELETE",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
"timeSeconds": "{seconds, plural, =1{1 segundo} other{{seconds} segundos}}",
|
"timeSeconds": "{seconds, plural, =1{1 segundo} other{{seconds} segundos}}",
|
||||||
"timeMinutes": "{minutes, plural, =1{1 minuto} other{{minutes} minutos}}",
|
"timeMinutes": "{minutes, plural, =1{1 minuto} other{{minutes} minutos}}",
|
||||||
|
"focalLength": "{length} mm",
|
||||||
|
|
||||||
"applyButtonLabel": "APLICAR",
|
"applyButtonLabel": "APLICAR",
|
||||||
"deleteButtonLabel": "BORRAR",
|
"deleteButtonLabel": "BORRAR",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
"timeSeconds": "{seconds, plural, =1{1 seconde} other{{seconds} secondes}}",
|
"timeSeconds": "{seconds, plural, =1{1 seconde} other{{seconds} secondes}}",
|
||||||
"timeMinutes": "{minutes, plural, =1{1 minute} other{{minutes} minutes}}",
|
"timeMinutes": "{minutes, plural, =1{1 minute} other{{minutes} minutes}}",
|
||||||
|
"focalLength": "{length} mm",
|
||||||
|
|
||||||
"applyButtonLabel": "ENREGISTRER",
|
"applyButtonLabel": "ENREGISTRER",
|
||||||
"deleteButtonLabel": "SUPPRIMER",
|
"deleteButtonLabel": "SUPPRIMER",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
"timeSeconds": "{seconds, plural, other{{seconds}초}}",
|
"timeSeconds": "{seconds, plural, other{{seconds}초}}",
|
||||||
"timeMinutes": "{minutes, plural, other{{minutes}분}}",
|
"timeMinutes": "{minutes, plural, other{{minutes}분}}",
|
||||||
|
"focalLength": "{length} mm",
|
||||||
|
|
||||||
"applyButtonLabel": "확인",
|
"applyButtonLabel": "확인",
|
||||||
"deleteButtonLabel": "삭제",
|
"deleteButtonLabel": "삭제",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
"timeSeconds": "{seconds, plural, =1{1 segundo} other{{seconds} segundos}}",
|
"timeSeconds": "{seconds, plural, =1{1 segundo} other{{seconds} segundos}}",
|
||||||
"timeMinutes": "{minutes, plural, =1{1 minuto} other{{minutes} minutos}}",
|
"timeMinutes": "{minutes, plural, =1{1 minuto} other{{minutes} minutos}}",
|
||||||
|
"focalLength": "{length} mm",
|
||||||
|
|
||||||
"applyButtonLabel": "APLIQUE",
|
"applyButtonLabel": "APLIQUE",
|
||||||
"deleteButtonLabel": "EXCLUIR",
|
"deleteButtonLabel": "EXCLUIR",
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
"timeSeconds": "{seconds, plural, =1{1 секунда} few{{seconds} секунды} other{{seconds} секунд}}",
|
"timeSeconds": "{seconds, plural, =1{1 секунда} few{{seconds} секунды} other{{seconds} секунд}}",
|
||||||
"timeMinutes": "{minutes, plural, =1{1 минута} few{{minutes} минуты} other{{minutes} минут}}",
|
"timeMinutes": "{minutes, plural, =1{1 минута} few{{minutes} минуты} other{{minutes} минут}}",
|
||||||
|
"focalLength": "{length} mm",
|
||||||
|
|
||||||
"applyButtonLabel": "ПРИМЕНИТЬ",
|
"applyButtonLabel": "ПРИМЕНИТЬ",
|
||||||
"deleteButtonLabel": "УДАЛИТЬ",
|
"deleteButtonLabel": "УДАЛИТЬ",
|
||||||
|
|
|
@ -17,10 +17,13 @@ class PolicyPage extends StatefulWidget {
|
||||||
class _PolicyPageState extends State<PolicyPage> {
|
class _PolicyPageState extends State<PolicyPage> {
|
||||||
late Future<String> _termsLoader;
|
late Future<String> _termsLoader;
|
||||||
|
|
||||||
|
static const termsPath = 'assets/terms.md';
|
||||||
|
static const termsDirection = TextDirection.ltr;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_termsLoader = rootBundle.loadString('assets/terms.md');
|
_termsLoader = rootBundle.loadString(termsPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -38,7 +41,10 @@ class _PolicyPageState extends State<PolicyPage> {
|
||||||
final terms = snapshot.data!;
|
final terms = snapshot.data!;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
child: MarkdownContainer(data: terms),
|
child: MarkdownContainer(
|
||||||
|
data: terms,
|
||||||
|
textDirection: termsDirection,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -192,6 +192,7 @@ class _CollectionSectionedContentState extends State<_CollectionSectionedContent
|
||||||
|
|
||||||
final isMainMode = context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value == AppMode.main);
|
final isMainMode = context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value == AppMode.main);
|
||||||
final selector = GridSelectionGestureDetector(
|
final selector = GridSelectionGestureDetector(
|
||||||
|
scrollableKey: scrollableKey,
|
||||||
selectable: isMainMode,
|
selectable: isMainMode,
|
||||||
items: collection.sortedEntries,
|
items: collection.sortedEntries,
|
||||||
scrollController: scrollController,
|
scrollController: scrollController,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -9,6 +10,8 @@ import 'package:flutter/material.dart';
|
||||||
- allow any `Widget` as label content
|
- allow any `Widget` as label content
|
||||||
- moved out constraints responsibility
|
- moved out constraints responsibility
|
||||||
- various extent & thumb positioning fixes
|
- various extent & thumb positioning fixes
|
||||||
|
- null safety
|
||||||
|
- directionality aware
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// Build the Scroll Thumb and label using the current configuration
|
/// Build the Scroll Thumb and label using the current configuration
|
||||||
|
@ -350,7 +353,7 @@ class SlideFadeTransition extends StatelessWidget {
|
||||||
builder: (context, child) => animation.value == 0.0 ? Container() : child!,
|
builder: (context, child) => animation.value == 0.0 ? Container() : child!,
|
||||||
child: SlideTransition(
|
child: SlideTransition(
|
||||||
position: Tween(
|
position: Tween(
|
||||||
begin: Offset((Directionality.of(context) == TextDirection.ltr ? 1 : -1) * .3, 0),
|
begin: Offset((context.isRtl ? -1 : 1) * .3, 0),
|
||||||
end: Offset.zero,
|
end: Offset.zero,
|
||||||
).animate(animation),
|
).animate(animation),
|
||||||
child: FadeTransition(
|
child: FadeTransition(
|
||||||
|
|
|
@ -33,6 +33,8 @@ class SideGestureAreaProtector extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned.fill(
|
return Positioned.fill(
|
||||||
child: Row(
|
child: Row(
|
||||||
|
// `systemGestureInsets` are not directional
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: context.select<MediaQueryData, double>((mq) => mq.systemGestureInsets.left),
|
width: context.select<MediaQueryData, double>((mq) => mq.systemGestureInsets.left),
|
||||||
|
|
|
@ -4,10 +4,12 @@ import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class MarkdownContainer extends StatelessWidget {
|
class MarkdownContainer extends StatelessWidget {
|
||||||
final String data;
|
final String data;
|
||||||
|
final TextDirection? textDirection;
|
||||||
|
|
||||||
const MarkdownContainer({
|
const MarkdownContainer({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.data,
|
required this.data,
|
||||||
|
this.textDirection,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
static const double maxWidth = 460;
|
static const double maxWidth = 460;
|
||||||
|
@ -34,6 +36,8 @@ class MarkdownContainer extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
|
child: Directionality(
|
||||||
|
textDirection: textDirection ?? Directionality.of(context),
|
||||||
child: Markdown(
|
child: Markdown(
|
||||||
data: data,
|
data: data,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
|
@ -47,6 +51,7 @@ class MarkdownContainer extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,6 @@ extension ExtraContext on BuildContext {
|
||||||
String? get currentRouteName => ModalRoute.of(this)?.settings.name;
|
String? get currentRouteName => ModalRoute.of(this)?.settings.name;
|
||||||
|
|
||||||
AppLocalizations get l10n => AppLocalizations.of(this)!;
|
AppLocalizations get l10n => AppLocalizations.of(this)!;
|
||||||
|
|
||||||
|
bool get isRtl => Directionality.of(this) == TextDirection.rtl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,15 @@ class SectionHeader<T> extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final spans = [
|
return Container(
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
padding: padding,
|
||||||
|
constraints: const BoxConstraints(minHeight: leadingDimension),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: selectable ? () => _toggleSectionSelection(context) : null,
|
||||||
|
child: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: widgetSpanAlignment,
|
alignment: widgetSpanAlignment,
|
||||||
child: _SectionSelectableLeading<T>(
|
child: _SectionSelectableLeading<T>(
|
||||||
|
@ -59,22 +67,8 @@ class SectionHeader<T> extends StatelessWidget {
|
||||||
child: trailing,
|
child: trailing,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
],
|
||||||
|
|
||||||
return Container(
|
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
padding: padding,
|
|
||||||
constraints: const BoxConstraints(minHeight: leadingDimension),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: selectable ? () => _toggleSectionSelection(context) : null,
|
|
||||||
child: Text.rich(
|
|
||||||
// bidi with optional surrounding widget spans is tricky
|
|
||||||
// so we use LTR direction for the rich text itself and reverse the spans if necessary
|
|
||||||
// TODO TLAD [rtl] revisit this solution as it is failing for multiline headers
|
|
||||||
TextSpan(
|
|
||||||
children: Directionality.of(context) == TextDirection.ltr ? spans : spans.reversed.toList(),
|
|
||||||
),
|
),
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/highlight.dart';
|
||||||
import 'package:aves/model/source/enums.dart';
|
import 'package:aves/model/source/enums.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/widgets/common/behaviour/eager_scale_gesture_recognizer.dart';
|
import 'package:aves/widgets/common/behaviour/eager_scale_gesture_recognizer.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/grid/theme.dart';
|
import 'package:aves/widgets/common/grid/theme.dart';
|
||||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||||
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
||||||
|
@ -304,7 +305,7 @@ class _ScaleOverlayState extends State<_ScaleOverlay> {
|
||||||
gradientCenter = center;
|
gradientCenter = center;
|
||||||
break;
|
break;
|
||||||
case TileLayout.list:
|
case TileLayout.list:
|
||||||
gradientCenter = Offset(Directionality.of(context) == TextDirection.rtl ? gridWidth : 0, center.dy);
|
gradientCenter = Offset(context.isRtl ? gridWidth : 0, center.dy);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,7 @@ class SectionedListLayout<T> {
|
||||||
required this.sectionLayouts,
|
required this.sectionLayouts,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// return tile rectangle in layout space, i.e. x=0 is start
|
||||||
Rect? getTileRect(T item) {
|
Rect? getTileRect(T item) {
|
||||||
final MapEntry<SectionKey?, List<T>>? section = sections.entries.firstWhereOrNull((kv) => kv.value.contains(item));
|
final MapEntry<SectionKey?, List<T>>? section = sections.entries.firstWhereOrNull((kv) => kv.value.contains(item));
|
||||||
if (section == null) return null;
|
if (section == null) return null;
|
||||||
|
@ -211,6 +212,7 @@ class SectionedListLayout<T> {
|
||||||
|
|
||||||
SectionLayout? getSectionAt(double offsetY) => sectionLayouts.firstWhereOrNull((sl) => offsetY < sl.maxOffset);
|
SectionLayout? getSectionAt(double offsetY) => sectionLayouts.firstWhereOrNull((sl) => offsetY < sl.maxOffset);
|
||||||
|
|
||||||
|
// `position` in layout space, i.e. x=0 is start
|
||||||
T? getItemAt(Offset position) {
|
T? getItemAt(Offset position) {
|
||||||
var dy = position.dy;
|
var dy = position.dy;
|
||||||
final sectionLayout = getSectionAt(dy);
|
final sectionLayout = getSectionAt(dy);
|
||||||
|
|
|
@ -3,12 +3,14 @@ import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/selection.dart';
|
import 'package:aves/model/selection.dart';
|
||||||
import 'package:aves/utils/math_utils.dart';
|
import 'package:aves/utils/math_utils.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/extensions/media_query.dart';
|
import 'package:aves/widgets/common/extensions/media_query.dart';
|
||||||
import 'package:aves/widgets/common/grid/section_layout.dart';
|
import 'package:aves/widgets/common/grid/section_layout.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class GridSelectionGestureDetector<T> extends StatefulWidget {
|
class GridSelectionGestureDetector<T> extends StatefulWidget {
|
||||||
|
final GlobalKey scrollableKey;
|
||||||
final bool selectable;
|
final bool selectable;
|
||||||
final List<T> items;
|
final List<T> items;
|
||||||
final ScrollController scrollController;
|
final ScrollController scrollController;
|
||||||
|
@ -17,6 +19,7 @@ class GridSelectionGestureDetector<T> extends StatefulWidget {
|
||||||
|
|
||||||
const GridSelectionGestureDetector({
|
const GridSelectionGestureDetector({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
required this.scrollableKey,
|
||||||
this.selectable = true,
|
this.selectable = true,
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.scrollController,
|
required this.scrollController,
|
||||||
|
@ -42,6 +45,13 @@ class _GridSelectionGestureDetectorState<T> extends State<GridSelectionGestureDe
|
||||||
|
|
||||||
double get appBarHeight => widget.appBarHeightNotifier.value;
|
double get appBarHeight => widget.appBarHeightNotifier.value;
|
||||||
|
|
||||||
|
double get scrollableWidth {
|
||||||
|
final scrollableContext = widget.scrollableKey.currentContext!;
|
||||||
|
final scrollableBox = scrollableContext.findRenderObject() as RenderBox;
|
||||||
|
// not the same as `MediaQuery.size.width`, because of screen insets/padding
|
||||||
|
return scrollableBox.size.width;
|
||||||
|
}
|
||||||
|
|
||||||
static const double scrollEdgeRatio = .15;
|
static const double scrollEdgeRatio = .15;
|
||||||
static const double scrollMaxPixelPerSecond = 600.0;
|
static const double scrollMaxPixelPerSecond = 600.0;
|
||||||
static const Duration scrollUpdateInterval = Duration(milliseconds: 100);
|
static const Duration scrollUpdateInterval = Duration(milliseconds: 100);
|
||||||
|
@ -147,7 +157,7 @@ class _GridSelectionGestureDetectorState<T> extends State<GridSelectionGestureDe
|
||||||
// so we use custom layout computation instead to find the item.
|
// so we use custom layout computation instead to find the item.
|
||||||
final offset = Offset(0, scrollController.offset - appBarHeight) + localPosition;
|
final offset = Offset(0, scrollController.offset - appBarHeight) + localPosition;
|
||||||
final sectionedListLayout = context.read<SectionedListLayout<T>>();
|
final sectionedListLayout = context.read<SectionedListLayout<T>>();
|
||||||
return sectionedListLayout.getItemAt(offset);
|
return sectionedListLayout.getItemAt(context.isRtl ? Offset(scrollableWidth - offset.dx, offset.dy) : offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggleSelectionToIndex(int toIndex) {
|
void _toggleSelectionToIndex(int toIndex) {
|
||||||
|
|
|
@ -283,9 +283,13 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (banner != null)
|
if (banner != null)
|
||||||
LayoutBuilder(builder: (context, constraints) {
|
LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
|
child: Align(
|
||||||
|
// align to corner the scaled down banner in RTL
|
||||||
|
alignment: AlignmentDirectional.topStart,
|
||||||
child: Transform(
|
child: Transform(
|
||||||
transform: Matrix4.identity().scaled((constraints.maxHeight / 90 - .4).clamp(.45, 1.0)),
|
transform: Matrix4.identity().scaled((constraints.maxHeight / 90 - .4).clamp(.45, 1.0)),
|
||||||
child: Banner(
|
child: Banner(
|
||||||
|
@ -295,8 +299,10 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
|
||||||
child: const SizedBox(),
|
child: const SizedBox(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/thumbnail/image.dart';
|
import 'package:aves/widgets/common/thumbnail/image.dart';
|
||||||
import 'package:custom_rounded_rectangle_border/custom_rounded_rectangle_border.dart';
|
import 'package:custom_rounded_rectangle_border/custom_rounded_rectangle_border.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -82,7 +83,20 @@ class ImageMarker extends StatelessWidget {
|
||||||
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 2),
|
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 2),
|
||||||
decoration: ShapeDecoration(
|
decoration: ShapeDecoration(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
shape: const CustomRoundedRectangleBorder(
|
shape: context.isRtl
|
||||||
|
? const CustomRoundedRectangleBorder(
|
||||||
|
leftSide: borderSide,
|
||||||
|
rightSide: borderSide,
|
||||||
|
topSide: borderSide,
|
||||||
|
bottomSide: borderSide,
|
||||||
|
topRightCornerSide: borderSide,
|
||||||
|
bottomLeftCornerSide: borderSide,
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topRight: innerRadius,
|
||||||
|
bottomLeft: innerRadius,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const CustomRoundedRectangleBorder(
|
||||||
leftSide: borderSide,
|
leftSide: borderSide,
|
||||||
rightSide: borderSide,
|
rightSide: borderSide,
|
||||||
topSide: borderSide,
|
topSide: borderSide,
|
||||||
|
|
|
@ -46,6 +46,8 @@ class _AppDebugPageState extends State<AppDebugPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MediaQueryDataProvider(
|
return MediaQueryDataProvider(
|
||||||
|
child: Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Debug'),
|
title: const Text('Debug'),
|
||||||
|
@ -88,6 +90,7 @@ class _AppDebugPageState extends State<AppDebugPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
|
||||||
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
|
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 8),
|
padding: const EdgeInsetsDirectional.only(start: 16, end: 8),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Text(formatDateTime(_setDateTime, locale, use24hour))),
|
Expanded(child: Text(formatDateTime(_setDateTime, locale, use24hour))),
|
||||||
|
@ -177,6 +177,8 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
|
||||||
const textStyle = TextStyle(fontSize: 34);
|
const textStyle = TextStyle(fontSize: 34);
|
||||||
return Center(
|
return Center(
|
||||||
child: Table(
|
child: Table(
|
||||||
|
// even when ambient direction is RTL, time is displayed in LTR
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
children: [
|
children: [
|
||||||
TableRow(
|
TableRow(
|
||||||
children: [
|
children: [
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@ -40,12 +41,17 @@ class _RenameEntryDialogState extends State<RenameEntryDialog> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isRtl = context.isRtl;
|
||||||
|
final extensionSuffixText = '${Constants.fsi}${entry.extension}${Constants.pdi}';
|
||||||
return AvesDialog(
|
return AvesDialog(
|
||||||
content: TextField(
|
content: TextField(
|
||||||
controller: _nameController,
|
controller: _nameController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: context.l10n.renameEntryDialogLabel,
|
labelText: context.l10n.renameEntryDialogLabel,
|
||||||
suffixText: entry.extension,
|
// decoration prefix and suffix follow directionality
|
||||||
|
// but the file extension should always be on the right
|
||||||
|
prefixText: isRtl ? extensionSuffixText : null,
|
||||||
|
suffixText: isRtl ? null : extensionSuffixText,
|
||||||
),
|
),
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
onChanged: (_) => _validate(),
|
onChanged: (_) => _validate(),
|
||||||
|
|
|
@ -131,9 +131,8 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
|
||||||
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Material(
|
Material(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.vertical(
|
||||||
topLeft: AvesDialog.cornerRadius,
|
top: AvesDialog.cornerRadius,
|
||||||
topRight: AvesDialog.cornerRadius,
|
|
||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
|
|
|
@ -168,7 +168,7 @@ class CoveredFilterChip<T extends CollectionFilter> extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
if (pinned)
|
if (pinned)
|
||||||
AnimatedPadding(
|
AnimatedPadding(
|
||||||
padding: EdgeInsets.only(right: padding),
|
padding: EdgeInsetsDirectional.only(end: padding),
|
||||||
duration: Durations.chipDecorationAnimation,
|
duration: Durations.chipDecorationAnimation,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
AIcons.pin,
|
AIcons.pin,
|
||||||
|
@ -178,7 +178,7 @@ class CoveredFilterChip<T extends CollectionFilter> extends StatelessWidget {
|
||||||
),
|
),
|
||||||
if (filter is AlbumFilter && androidFileUtils.isOnRemovableStorage(filter.album))
|
if (filter is AlbumFilter && androidFileUtils.isOnRemovableStorage(filter.album))
|
||||||
AnimatedPadding(
|
AnimatedPadding(
|
||||||
padding: EdgeInsets.only(right: padding),
|
padding: EdgeInsetsDirectional.only(end: padding),
|
||||||
duration: Durations.chipDecorationAnimation,
|
duration: Durations.chipDecorationAnimation,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
AIcons.removableStorage,
|
AIcons.removableStorage,
|
||||||
|
|
|
@ -384,6 +384,7 @@ class _FilterSectionedContentState<T extends CollectionFilter> extends State<_Fi
|
||||||
|
|
||||||
final isMainMode = context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value == AppMode.main);
|
final isMainMode = context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value == AppMode.main);
|
||||||
final selector = GridSelectionGestureDetector<FilterGridItem<T>>(
|
final selector = GridSelectionGestureDetector<FilterGridItem<T>>(
|
||||||
|
scrollableKey: scrollableKey,
|
||||||
selectable: isMainMode && widget.selectable,
|
selectable: isMainMode && widget.selectable,
|
||||||
items: visibleSections.values.expand((v) => v).toList(),
|
items: visibleSections.values.expand((v) => v).toList(),
|
||||||
scrollController: scrollController,
|
scrollController: scrollController,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/model/source/collection_lens.dart';
|
||||||
import 'package:aves/model/source/collection_source.dart';
|
import 'package:aves/model/source/collection_source.dart';
|
||||||
import 'package:aves/model/source/enums.dart';
|
import 'package:aves/model/source/enums.dart';
|
||||||
import 'package:aves/widgets/collection/collection_page.dart';
|
import 'package:aves/widgets/collection/collection_page.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/grid/scaling.dart';
|
import 'package:aves/widgets/common/grid/scaling.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||||
import 'package:aves/widgets/filter_grids/common/covered_filter_chip.dart';
|
import 'package:aves/widgets/filter_grids/common/covered_filter_chip.dart';
|
||||||
|
@ -178,8 +179,11 @@ class FilterTile<T extends CollectionFilter> extends StatelessWidget {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (onTap != null) {
|
if (onTap != null) {
|
||||||
|
// larger than the chip corner radius, so ink effects will be effectively clipped from the leading chip corners
|
||||||
|
const radius = Radius.circular(123);
|
||||||
child = InkWell(
|
child = InkWell(
|
||||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(123), bottomLeft: Radius.circular(123)),
|
// as of Flutter v2.8.1, `InkWell` does not use `BorderRadiusGeometry`
|
||||||
|
borderRadius: context.isRtl ? const BorderRadius.only(topRight: radius, bottomRight: radius) : const BorderRadius.only(topLeft: radius, bottomLeft: radius),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|
|
@ -51,7 +51,7 @@ class FilterListDetails<T extends CollectionFilter> extends StatelessWidget {
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: PlaceholderAlignment.middle,
|
alignment: PlaceholderAlignment.middle,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: FilterListDetailsTheme.titleIconPadding),
|
padding: const EdgeInsetsDirectional.only(end: FilterListDetailsTheme.titleIconPadding),
|
||||||
child: IconTheme(
|
child: IconTheme(
|
||||||
data: IconThemeData(color: detailsTheme.titleStyle.color),
|
data: IconThemeData(color: detailsTheme.titleStyle.color),
|
||||||
child: leading,
|
child: leading,
|
||||||
|
@ -127,7 +127,7 @@ class FilterListDetails<T extends CollectionFilter> extends StatelessWidget {
|
||||||
children: leadingIcons
|
children: leadingIcons
|
||||||
.mapIndexed((i, child) => i > 0
|
.mapIndexed((i, child) => i > 0
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.only(left: 8),
|
padding: const EdgeInsetsDirectional.only(start: 8),
|
||||||
child: child,
|
child: child,
|
||||||
)
|
)
|
||||||
: child)
|
: child)
|
||||||
|
|
|
@ -174,14 +174,14 @@ class _QuickActionEditorBodyState<T extends Object> extends State<QuickActionEdi
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: FractionallySizedBox(
|
child: FractionallySizedBox(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
widthFactor: .5,
|
widthFactor: .5,
|
||||||
child: header,
|
child: header,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: FractionallySizedBox(
|
child: FractionallySizedBox(
|
||||||
alignment: Alignment.centerRight,
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
widthFactor: .5,
|
widthFactor: .5,
|
||||||
child: footer,
|
child: footer,
|
||||||
),
|
),
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/utils/android_file_utils.dart';
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/widgets/common/basic/menu.dart';
|
import 'package:aves/widgets/common/basic/menu.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/buttons.dart';
|
import 'package:aves/widgets/common/identity/buttons.dart';
|
||||||
|
@ -179,7 +180,7 @@ class _FilePickerState extends State<FilePicker> {
|
||||||
Widget _buildContentLine(BuildContext context, FileSystemEntity content) {
|
Widget _buildContentLine(BuildContext context, FileSystemEntity content) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Icon(AIcons.folder),
|
leading: const Icon(AIcons.folder),
|
||||||
title: Text(pContext.split(content.path).last),
|
title: Text('${Constants.fsi}${pContext.split(content.path).last}${Constants.pdi}'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_goTo(content.path);
|
_goTo(content.path);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/utils/color_utils.dart';
|
import 'package:aves/utils/color_utils.dart';
|
||||||
import 'package:aves/utils/constants.dart';
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart' as intl;
|
import 'package:intl/intl.dart' as intl;
|
||||||
|
@ -40,7 +41,7 @@ class FilterTable<T extends Comparable> extends StatelessWidget {
|
||||||
|
|
||||||
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
||||||
final lineHeight = 16 * textScaleFactor;
|
final lineHeight = 16 * textScaleFactor;
|
||||||
final isRTL = Directionality.of(context) == TextDirection.rtl;
|
final isRtl = context.isRtl;
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsetsDirectional.only(start: AvesFilterChip.outlineWidth / 2 + 6, end: 8),
|
padding: const EdgeInsetsDirectional.only(start: AvesFilterChip.outlineWidth / 2 + 6, end: 8),
|
||||||
|
@ -74,7 +75,7 @@ class FilterTable<T extends Comparable> extends StatelessWidget {
|
||||||
backgroundColor: Colors.white24,
|
backgroundColor: Colors.white24,
|
||||||
progressColor: stringToColor(label),
|
progressColor: stringToColor(label),
|
||||||
animation: true,
|
animation: true,
|
||||||
isRTL: isRTL,
|
isRTL: isRtl,
|
||||||
padding: EdgeInsets.symmetric(horizontal: lineHeight),
|
padding: EdgeInsets.symmetric(horizontal: lineHeight),
|
||||||
center: Text(
|
center: Text(
|
||||||
intl.NumberFormat.percentPattern().format(percent),
|
intl.NumberFormat.percentPattern().format(percent),
|
||||||
|
|
|
@ -109,7 +109,7 @@ class StatsPage extends StatelessWidget {
|
||||||
backgroundColor: Colors.white24,
|
backgroundColor: Colors.white24,
|
||||||
progressColor: Theme.of(context).colorScheme.secondary,
|
progressColor: Theme.of(context).colorScheme.secondary,
|
||||||
animation: animate,
|
animation: animate,
|
||||||
isRTL: Directionality.of(context) == TextDirection.rtl,
|
isRTL: context.isRtl,
|
||||||
leading: const Icon(AIcons.location),
|
leading: const Icon(AIcons.location),
|
||||||
padding: EdgeInsets.symmetric(horizontal: lineHeight),
|
padding: EdgeInsets.symmetric(horizontal: lineHeight),
|
||||||
center: Text(
|
center: Text(
|
||||||
|
@ -221,24 +221,26 @@ class StatsPage extends StatelessWidget {
|
||||||
children: seriesData
|
children: seriesData
|
||||||
.map((d) => GestureDetector(
|
.map((d) => GestureDetector(
|
||||||
onTap: () => _onFilterSelection(context, MimeFilter(d.mimeType)),
|
onTap: () => _onFilterSelection(context, MimeFilter(d.mimeType)),
|
||||||
child: Text.rich(
|
child: Row(
|
||||||
TextSpan(
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
WidgetSpan(
|
Icon(AIcons.disc, color: d.color),
|
||||||
alignment: PlaceholderAlignment.middle,
|
const SizedBox(width: 8),
|
||||||
child: Padding(
|
Flexible(
|
||||||
padding: const EdgeInsetsDirectional.only(end: 8),
|
child: Text(
|
||||||
child: Icon(AIcons.disc, color: d.color),
|
d.displayText,
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: '${d.displayText} '),
|
|
||||||
TextSpan(text: '${d.entryCount}', style: const TextStyle(color: Colors.white70)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'${d.entryCount}',
|
||||||
|
style: const TextStyle(color: Colors.white70),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -358,7 +358,7 @@ class _PositionTitleRow extends StatelessWidget {
|
||||||
[
|
[
|
||||||
if (collectionPosition != null) collectionPosition,
|
if (collectionPosition != null) collectionPosition,
|
||||||
if (pagePosition != null) pagePosition,
|
if (pagePosition != null) pagePosition,
|
||||||
if (title != null) title,
|
if (title != null) '${Constants.fsi}$title${Constants.pdi}',
|
||||||
].join(separator),
|
].join(separator),
|
||||||
strutStyle: Constants.overflowStrutStyle);
|
strutStyle: Constants.overflowStrutStyle);
|
||||||
|
|
||||||
|
@ -430,7 +430,7 @@ class _ShootingRow extends StatelessWidget {
|
||||||
final apertureText = aperture != null ? 'ƒ/${NumberFormat('0.0', locale).format(aperture)}' : Constants.overlayUnknown;
|
final apertureText = aperture != null ? 'ƒ/${NumberFormat('0.0', locale).format(aperture)}' : Constants.overlayUnknown;
|
||||||
|
|
||||||
final focalLength = details.focalLength;
|
final focalLength = details.focalLength;
|
||||||
final focalLengthText = focalLength != null ? '${NumberFormat('0.#', locale).format(focalLength)} mm' : Constants.overlayUnknown;
|
final focalLengthText = focalLength != null ? context.l10n.focalLength(NumberFormat('0.#', locale).format(focalLength)) : Constants.overlayUnknown;
|
||||||
|
|
||||||
final iso = details.iso;
|
final iso = details.iso;
|
||||||
final isoText = iso != null ? 'ISO$iso' : Constants.overlayUnknown;
|
final isoText = iso != null ? 'ISO$iso' : Constants.overlayUnknown;
|
||||||
|
|
|
@ -25,11 +25,14 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
bool _hasAcceptedTerms = false;
|
bool _hasAcceptedTerms = false;
|
||||||
late Future<String> _termsLoader;
|
late Future<String> _termsLoader;
|
||||||
|
|
||||||
|
static const termsPath = 'assets/terms.md';
|
||||||
|
static const termsDirection = TextDirection.ltr;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
settings.setContextualDefaults();
|
settings.setContextualDefaults();
|
||||||
_termsLoader = rootBundle.loadString('assets/terms.md');
|
_termsLoader = rootBundle.loadString(termsPath);
|
||||||
WidgetsBinding.instance!.addPostFrameCallback((_) => _initWelcomeSettings());
|
WidgetsBinding.instance!.addPostFrameCallback((_) => _initWelcomeSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +71,12 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
children: [
|
children: [
|
||||||
..._buildHeader(context, isPortrait: isPortrait),
|
..._buildHeader(context, isPortrait: isPortrait),
|
||||||
if (isPortrait) ...[
|
if (isPortrait) ...[
|
||||||
Flexible(child: MarkdownContainer(data: terms)),
|
Flexible(
|
||||||
|
child: MarkdownContainer(
|
||||||
|
data: terms,
|
||||||
|
textDirection: termsDirection,
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
..._buildControls(context),
|
..._buildControls(context),
|
||||||
] else
|
] else
|
||||||
|
@ -78,14 +86,17 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
padding: const EdgeInsets.only(bottom: 8),
|
||||||
child: MarkdownContainer(data: terms),
|
child: MarkdownContainer(
|
||||||
)),
|
data: terms,
|
||||||
|
textDirection: termsDirection,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
// shrinkWrap: true,
|
|
||||||
children: _buildControls(context),
|
children: _buildControls(context),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue