diff --git a/CHANGELOG.md b/CHANGELOG.md
index e75d2d440..44223a455 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+### Changed
+
+- Viewer: keep controls in the lower right corner even with RTL locales
+
### Fixed
- crash when loading SVG defined with large dimensions
diff --git a/lib/widgets/viewer/overlay/bottom.dart b/lib/widgets/viewer/overlay/bottom.dart
index cb9339f2a..14dd28492 100644
--- a/lib/widgets/viewer/overlay/bottom.dart
+++ b/lib/widgets/viewer/overlay/bottom.dart
@@ -186,41 +186,45 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
builder: (context, child) {
final viewInsetsPadding = (widget.viewInsets ?? EdgeInsets.zero) + (widget.viewPadding ?? EdgeInsets.zero);
final selection = context.read?>();
- final viewerButtonRow = (selection?.isSelecting ?? false)
- ? SelectionButton(
- mainEntry: mainEntry,
- scale: _buttonScale,
- )
- : FocusableActionDetector(
- focusNode: _buttonRowFocusScopeNode,
- shortcuts: settings.useTvLayout
- ? const {
- SingleActivator(LogicalKeyboardKey.arrowUp): TvShowLessInfoIntent(),
- }
- : null,
- actions: {
- TvShowLessInfoIntent: CallbackAction(onInvoke: (intent) => TvShowLessInfoNotification().dispatch(context)),
- },
- child: SafeArea(
- top: false,
- bottom: false,
- minimum: EdgeInsets.only(
- left: viewInsetsPadding.left,
- right: viewInsetsPadding.right,
+ final viewerButtonRow = Directionality(
+ // always keep action buttons in the lower right corner, even with RTL locales
+ textDirection: TextDirection.ltr,
+ child: (selection?.isSelecting ?? false)
+ ? SelectionButton(
+ mainEntry: mainEntry,
+ scale: _buttonScale,
+ )
+ : FocusableActionDetector(
+ focusNode: _buttonRowFocusScopeNode,
+ shortcuts: settings.useTvLayout
+ ? const {
+ SingleActivator(LogicalKeyboardKey.arrowUp): TvShowLessInfoIntent(),
+ }
+ : null,
+ actions: {
+ TvShowLessInfoIntent: CallbackAction(onInvoke: (intent) => TvShowLessInfoNotification().dispatch(context)),
+ },
+ child: SafeArea(
+ top: false,
+ bottom: false,
+ minimum: EdgeInsets.only(
+ left: viewInsetsPadding.left,
+ right: viewInsetsPadding.right,
+ ),
+ child: isWallpaperMode
+ ? WallpaperButtons(
+ entry: pageEntry,
+ scale: _buttonScale,
+ )
+ : ViewerButtons(
+ mainEntry: mainEntry,
+ pageEntry: pageEntry,
+ collection: widget.collection,
+ scale: _buttonScale,
+ ),
),
- child: isWallpaperMode
- ? WallpaperButtons(
- entry: pageEntry,
- scale: _buttonScale,
- )
- : ViewerButtons(
- mainEntry: mainEntry,
- pageEntry: pageEntry,
- collection: widget.collection,
- scale: _buttonScale,
- ),
),
- );
+ );
final showMultiPageOverlay = mainEntry.isMultiPage && multiPageController != null;
final collapsedPageScroller = mainEntry.isMotionPhoto;
@@ -247,6 +251,8 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
(showMultiPageOverlay && collapsedPageScroller)
? Row(
crossAxisAlignment: CrossAxisAlignment.center,
+ // always keep action buttons in the lower right corner, even with RTL locales
+ textDirection: TextDirection.ltr,
children: [
SafeArea(
top: false,
diff --git a/lib/widgets/viewer/overlay/video/video.dart b/lib/widgets/viewer/overlay/video/video.dart
index 5272fa414..0ec8cdfb2 100644
--- a/lib/widgets/viewer/overlay/video/video.dart
+++ b/lib/widgets/viewer/overlay/video/video.dart
@@ -40,44 +40,48 @@ class _VideoControlOverlayState extends State with SingleTi
@override
Widget build(BuildContext context) {
- return StreamBuilder(
- stream: statusStream,
- builder: (context, snapshot) {
- // do not use stream snapshot because it is obsolete when switching between videos
- final status = controller?.status ?? VideoStatus.idle;
+ return Directionality(
+ // always keep action buttons in the lower right corner, even with RTL locales
+ textDirection: TextDirection.ltr,
+ child: StreamBuilder(
+ stream: statusStream,
+ builder: (context, snapshot) {
+ // do not use stream snapshot because it is obsolete when switching between videos
+ final status = controller?.status ?? VideoStatus.idle;
- if (status == VideoStatus.error) {
- const action = EntryAction.openVideo;
- return Align(
- alignment: AlignmentDirectional.centerEnd,
- child: OverlayButton(
- scale: scale,
- child: IconButton(
- icon: action.getIcon(),
- onPressed: entry.trashed ? null : () => widget.onActionSelected(action),
- tooltip: action.getText(context),
+ if (status == VideoStatus.error) {
+ const action = EntryAction.openVideo;
+ return Align(
+ alignment: AlignmentDirectional.centerEnd,
+ child: OverlayButton(
+ scale: scale,
+ child: IconButton(
+ icon: action.getIcon(),
+ onPressed: entry.trashed ? null : () => widget.onActionSelected(action),
+ tooltip: action.getText(context),
+ ),
),
- ),
- );
- }
+ );
+ }
- return Row(
- children: [
- Expanded(
- child: VideoProgressBar(
+ return Row(
+ children: [
+ Expanded(
+ child: VideoProgressBar(
+ controller: controller,
+ scale: scale,
+ ),
+ ),
+ VideoControlRow(
+ entry: entry,
controller: controller,
scale: scale,
+ onActionSelected: widget.onActionSelected,
),
- ),
- VideoControlRow(
- entry: entry,
- controller: controller,
- scale: scale,
- onActionSelected: widget.onActionSelected,
- ),
- ],
- );
- },
+ ],
+ );
+ },
+ ),
);
}
}
diff --git a/untranslated.json b/untranslated.json
index 68aae3568..5dad1ffde 100644
--- a/untranslated.json
+++ b/untranslated.json
@@ -6457,11 +6457,6 @@
"filePickerUseThisFolder"
],
- "pt": [
- "entryActionCast",
- "castDialogTitle"
- ],
-
"ro": [
"saveCopyButtonLabel",
"applyTooltip",