From a6512f4ee14fa4f3d74b4618e947054fdb2f87b3 Mon Sep 17 00:00:00 2001 From: y-ploni <7353755@gmail.com> Date: Tue, 3 Mar 2026 10:34:25 +0200 Subject: [PATCH 1/9] =?UTF-8?q?=D7=AA=D7=99=D7=A7=D7=95=D7=9F=20=D7=A7?= =?UTF-8?q?=D7=A8=D7=99=D7=90=D7=AA=20=D7=94SQL=20=D7=9C=D7=90=D7=A0=D7=93?= =?UTF-8?q?=D7=A8=D7=95=D7=90=D7=99=D7=93=20-=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/migration/dao/repository/seforim_repository.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/migration/dao/repository/seforim_repository.dart b/lib/migration/dao/repository/seforim_repository.dart index 9e969fbe7..2a02d469f 100644 --- a/lib/migration/dao/repository/seforim_repository.dart +++ b/lib/migration/dao/repository/seforim_repository.dart @@ -1831,6 +1831,14 @@ class SeforimRepository { /// @param sql The SQL query to execute Future executeRawQuery(String sql) async { final db = await _database.database; + final normalizedSql = sql.trim().toUpperCase(); + final isPragma = normalizedSql.startsWith('PRAGMA '); + + if (isPragma && (Platform.isAndroid || Platform.isIOS)) { + await db.rawQuery(sql); + return; + } + await db.execute(sql); } @@ -2344,9 +2352,9 @@ class SeforimRepository { Future _executeRawQuery(String sql) async { final db = await _database.database; final normalizedSql = sql.trim().toUpperCase(); - final isJournalModePragma = normalizedSql.startsWith('PRAGMA JOURNAL_MODE'); + final isPragma = normalizedSql.startsWith('PRAGMA '); - if (isJournalModePragma && (Platform.isAndroid || Platform.isIOS)) { + if (isPragma && (Platform.isAndroid || Platform.isIOS)) { await db.rawQuery(sql); return; } From 2f1ffbd5b8a676aae7e6afcec7bd8dff8156d4a0 Mon Sep 17 00:00:00 2001 From: y-ploni <7353755@gmail.com> Date: Tue, 3 Mar 2026 10:42:22 +0200 Subject: [PATCH 2/9] =?UTF-8?q?=D7=AA=D7=99=D7=A7=D7=95=D7=9F=20=D7=9E?= =?UTF-8?q?=D7=A1=D7=9A=20=D7=A1=D7=A4=D7=A8=D7=99=D7=99=D7=94=20=D7=91?= =?UTF-8?q?=D7=9E=D7=A1=D7=9B=D7=99=D7=9D=20=D7=A6=D7=A8=D7=99=D7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/navigation/custom_title_bar.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/navigation/custom_title_bar.dart b/lib/navigation/custom_title_bar.dart index 1d2c09792..b0f55b8c3 100644 --- a/lib/navigation/custom_title_bar.dart +++ b/lib/navigation/custom_title_bar.dart @@ -42,7 +42,7 @@ class CustomTitleBar extends StatefulWidget { } const double _kAppBarControlsWidth = 125.0; -const double _kAppBarControlsWidthRightAligned = 105.0; +const double _kAppBarControlsWidthRightAligned = 125.0; const int _kActionButtonsCount = 1; // settings בלבד const double _kActionButtonWidth = 56.0; const double _kWindowCaptionButtonsWidth = 138.0; @@ -50,8 +50,9 @@ const double _kWindowCaptionButtonWidth = 46.0; /// סגנון משותף לכפתורי האייקון בשורת הכותרת final ButtonStyle _kIconButtonStyle = IconButton.styleFrom( - minimumSize: const Size(32, 32), + minimumSize: const Size(28, 28), padding: EdgeInsets.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), @@ -217,7 +218,7 @@ class _CustomTitleBarState extends State ? _kAppBarControlsWidthRightAligned : _kAppBarControlsWidth, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ IconButton( icon: const Icon(FluentIcons.history_24_regular, size: 18), From b6e60ebf94f2e5bc2131fe8d24ee90790e611ea2 Mon Sep 17 00:00:00 2001 From: y-ploni <7353755@gmail.com> Date: Tue, 3 Mar 2026 10:53:47 +0200 Subject: [PATCH 3/9] =?UTF-8?q?=D7=A9=D7=99=D7=A0=D7=95=D7=99=20=D7=94?= =?UTF-8?q?=D7=92=D7=93=D7=A8=D7=95=D7=AA=20=D7=91=D7=A8=D7=99=D7=A8=D7=AA?= =?UTF-8?q?=20=D7=94=D7=9E=D7=97=D7=93=D7=9C=20=D7=91=D7=9E=D7=A1=D7=9A=20?= =?UTF-8?q?=D7=A1=D7=A4=D7=A8=D7=99=D7=99=D7=94=20=D7=9C=D7=9E=D7=A1=D7=9B?= =?UTF-8?q?=D7=99=D7=9D=20=D7=A6=D7=A8=D7=99=D7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit תצוגת רשימה, ללא תצוגה מקדימה --- lib/settings/engine/settings_repository.dart | 4 ++-- lib/settings/engine/settings_state.dart | 2 +- test/unit/settings/settings_bloc_test.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/settings/engine/settings_repository.dart b/lib/settings/engine/settings_repository.dart index 13e73b848..e380bce89 100644 --- a/lib/settings/engine/settings_repository.dart +++ b/lib/settings/engine/settings_repository.dart @@ -209,7 +209,7 @@ class SettingsRepository { ), 'libraryShowPreview': _settings.getValue( keyLibraryShowPreview, - defaultValue: true, + defaultValue: false, ), 'shortcuts': await getShortcuts(), 'enablePerBookSettings': _settings.getValue( @@ -652,7 +652,7 @@ class SettingsRepository { await _settings.setValue(keyCopyHeaderFormat, 'same_line_after_brackets'); await _settings.setValue(keyIsFullscreen, false); await _settings.setValue(keyLibraryViewMode, 'grid'); - await _settings.setValue(keyLibraryShowPreview, true); + await _settings.setValue(keyLibraryShowPreview, false); await _settings.setValue(keyEnablePerBookSettings, true); await _settings.setValue(keyAlignTabsToRight, false); await _settings.setValue(keyPersonalNotesCollapsedByDefault, true); diff --git a/lib/settings/engine/settings_state.dart b/lib/settings/engine/settings_state.dart index 85ed45eff..fcb1615fd 100644 --- a/lib/settings/engine/settings_state.dart +++ b/lib/settings/engine/settings_state.dart @@ -110,7 +110,7 @@ class SettingsState extends Equatable { copyHeaderFormat: 'same_line_after_brackets', isFullscreen: false, libraryViewMode: 'grid', - libraryShowPreview: true, + libraryShowPreview: false, shortcuts: {}, enablePerBookSettings: true, isOfflineMode: false, diff --git a/test/unit/settings/settings_bloc_test.dart b/test/unit/settings/settings_bloc_test.dart index 9063cd4ca..befc35e6f 100644 --- a/test/unit/settings/settings_bloc_test.dart +++ b/test/unit/settings/settings_bloc_test.dart @@ -57,7 +57,7 @@ void main() { 'copyHeaderFormat': 'same_line_after_brackets', 'isFullscreen': false, 'libraryViewMode': 'grid', - 'libraryShowPreview': true, + 'libraryShowPreview': false, 'enablePerBookSettings': true, 'shortcuts': {}, 'isOfflineMode': false, From 1a660c8b483c40bc240c4a1f8d2ae672a3892b15 Mon Sep 17 00:00:00 2001 From: y-ploni <7353755@gmail.com> Date: Tue, 3 Mar 2026 21:18:26 +0200 Subject: [PATCH 4/9] =?UTF-8?q?=D7=AA=D7=99=D7=A7=D7=95=D7=9F=20=D7=9E?= =?UTF-8?q?=D7=A1=D7=9A=20=D7=94=D7=92=D7=93=D7=A8=D7=95=D7=AA/=D7=90?= =?UTF-8?q?=D7=95=D7=A6=D7=A8=D7=99=D7=90/=D7=AA=D7=95=D7=A8=D7=9E=D7=99?= =?UTF-8?q?=D7=9D,=20=D7=91=D7=9E=D7=A1=D7=9B=D7=99=D7=9D=20=D7=A6=D7=A8?= =?UTF-8?q?=D7=99=D7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/settings/tabs/system_settings_tab.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/settings/tabs/system_settings_tab.dart b/lib/settings/tabs/system_settings_tab.dart index d396eb19c..66a832306 100644 --- a/lib/settings/tabs/system_settings_tab.dart +++ b/lib/settings/tabs/system_settings_tab.dart @@ -846,12 +846,15 @@ class _MemorialCardsGrid extends StatelessWidget { Widget build(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { final spacing = 12.0; - final itemWidth = (constraints.maxWidth - spacing * 2) / 3; - const itemHeight = 150.0; + final crossAxisCount = constraints.maxWidth < 560 ? 2 : 3; + final itemWidth = + (constraints.maxWidth - spacing * (crossAxisCount - 1)) / + crossAxisCount; + final itemHeight = crossAxisCount == 2 ? 158.0 : 150.0; final aspectRatio = itemWidth / itemHeight; return GridView.count( - crossAxisCount: 3, + crossAxisCount: crossAxisCount, crossAxisSpacing: spacing, mainAxisSpacing: spacing, childAspectRatio: aspectRatio, From e09c864004f7f76b61c13e4ecfb7af985f1b9580 Mon Sep 17 00:00:00 2001 From: y-ploni <7353755@gmail.com> Date: Wed, 4 Mar 2026 14:42:28 +0200 Subject: [PATCH 5/9] =?UTF-8?q?=D7=9E=D7=A1=D7=9A=20=D7=9E=D7=9C=D7=90=20?= =?UTF-8?q?=D7=91=D7=90=D7=A0=D7=93=D7=A8=D7=95=D7=90=D7=99=D7=93,=20?= =?UTF-8?q?=D7=A6=D7=9E=D7=A6=D7=95=D7=9D=20=D7=9E=D7=A8=D7=95=D7=95=D7=97?= =?UTF-8?q?=D7=99=D7=9D=20=D7=91=D7=99=D7=9F=20=D7=94=D7=9C=D7=97=D7=A6?= =?UTF-8?q?=D7=A0=D7=99=D7=9D=20=D7=91=D7=9E=D7=A1=D7=9A=20=D7=A2=D7=99?= =?UTF-8?q?=D7=95=D7=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/navigation/custom_title_bar.dart | 151 +++++++++++++++++++++------ lib/utils/fullscreen_helper.dart | 33 ++++-- 2 files changed, 144 insertions(+), 40 deletions(-) diff --git a/lib/navigation/custom_title_bar.dart b/lib/navigation/custom_title_bar.dart index b0f55b8c3..570e42444 100644 --- a/lib/navigation/custom_title_bar.dart +++ b/lib/navigation/custom_title_bar.dart @@ -43,7 +43,6 @@ class CustomTitleBar extends StatefulWidget { const double _kAppBarControlsWidth = 125.0; const double _kAppBarControlsWidthRightAligned = 125.0; -const int _kActionButtonsCount = 1; // settings בלבד const double _kActionButtonWidth = 56.0; const double _kWindowCaptionButtonsWidth = 138.0; const double _kWindowCaptionButtonWidth = 46.0; @@ -218,7 +217,7 @@ class _CustomTitleBarState extends State ? _kAppBarControlsWidthRightAligned : _kAppBarControlsWidth, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.start, children: [ IconButton( icon: const Icon(FluentIcons.history_24_regular, size: 18), @@ -226,12 +225,14 @@ class _CustomTitleBarState extends State onPressed: () => _showHistoryDialog(context), style: _kIconButtonStyle, ), + const SizedBox(width: 4), IconButton( icon: const Icon(FluentIcons.bookmark_24_regular, size: 18), tooltip: 'הצג סימניות (${bookmarksShortcut.toUpperCase()})', onPressed: () => _showBookmarksDialog(context), style: _kIconButtonStyle, ), + const SizedBox(width: 4), IconButton( icon: const Icon(FluentIcons.add_square_24_regular, size: 18), tooltip: 'החלף שולחן עבודה (${workspaceShortcut.toUpperCase()})', @@ -249,13 +250,13 @@ class _CustomTitleBarState extends State navState.currentScreen == Screen.search) { return _buildReadingTabs(context, settingsState); } else if (navState.currentScreen == Screen.library) { - return _buildLibraryTitle(context); + return _buildLibraryTitle(context, settingsState); } else { - return _buildStandardTitle(context, navState); + return _buildStandardTitle(context, navState, settingsState); } } - Widget _buildLibraryTitle(BuildContext context) { + Widget _buildLibraryTitle(BuildContext context, SettingsState settingsState) { return BlocBuilder( buildWhen: (previous, current) => previous.currentCategory != current.currentCategory, @@ -279,14 +280,22 @@ class _CustomTitleBarState extends State ), Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), - child: IconButton( - icon: const Icon(FluentIcons.settings_24_regular, size: 18), - tooltip: 'הגדרות ספרייה', - onPressed: () => showLibrarySettingsDialog(context), - style: _kIconButtonStyle.copyWith( - foregroundColor: WidgetStatePropertyAll( - Theme.of(context).colorScheme.onSurfaceVariant), - ), + child: Row( + mainAxisSize: MainAxisSize.min, + textDirection: TextDirection.ltr, + children: [ + if (!kIsWeb && Platform.isAndroid) + _buildAndroidFullscreenButton(context, settingsState), + IconButton( + icon: const Icon(FluentIcons.settings_24_regular, size: 18), + tooltip: 'הגדרות ספרייה', + onPressed: () => showLibrarySettingsDialog(context), + style: _kIconButtonStyle.copyWith( + foregroundColor: WidgetStatePropertyAll( + Theme.of(context).colorScheme.onSurfaceVariant), + ), + ), + ], ), ), ], @@ -295,7 +304,8 @@ class _CustomTitleBarState extends State ); } - Widget _buildStandardTitle(BuildContext context, NavigationState navState) { + Widget _buildStandardTitle(BuildContext context, NavigationState navState, + SettingsState settingsState) { String title = 'אוצריא'; switch (navState.currentScreen) { case Screen.find: @@ -311,17 +321,54 @@ class _CustomTitleBarState extends State break; } - return DragToMoveArea( - child: Center( - child: Text( - title, - style: Theme.of(context).textTheme.titleMedium, + return Row( + children: [ + Expanded( + child: DragToMoveArea( + child: Center( + child: Text( + title, + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + ), + if (!kIsWeb && Platform.isAndroid) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: _buildAndroidFullscreenButton(context, settingsState), + ), + ], + ); + } + + Widget _buildAndroidFullscreenButton( + BuildContext context, SettingsState settingsState) { + return IconButton( + icon: Icon( + settingsState.isFullscreen + ? FluentIcons.full_screen_minimize_24_regular + : FluentIcons.full_screen_maximize_24_regular, + size: 18, + ), + tooltip: settingsState.isFullscreen ? 'צא ממסך מלא' : 'מסך מלא', + onPressed: () async { + final newFullscreenState = !settingsState.isFullscreen; + await FullscreenHelper.toggleFullscreen( + context, + newFullscreenState, + ); + }, + style: _kIconButtonStyle.copyWith( + foregroundColor: WidgetStatePropertyAll( + Theme.of(context).colorScheme.onSurfaceVariant, ), ), ); } Widget _buildReadingTabs(BuildContext context, SettingsState settingsState) { + final showAndroidFullscreenButton = !kIsWeb && Platform.isAndroid; return BlocBuilder( builder: (context, state) { if (!state.hasOpenTabs) { @@ -342,13 +389,20 @@ class _CustomTitleBarState extends State double rightSpacerWidth = 0; if (!settingsState.alignTabsToRight) { + final showAndroidFullscreenButton = !kIsWeb && Platform.isAndroid; bool showWindowControls = !kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS); + final int readingActionButtonsCount = + 1 + (showAndroidFullscreenButton ? 1 : 0); double windowControlsWidth = showWindowControls ? _kWindowCaptionButtonsWidth + _kWindowCaptionButtonWidth : 0.0; - double actionButtonsWidth = _kAppBarControlsWidth; - double extraButtonsWidth = _kActionButtonsCount * _kActionButtonWidth; + double actionButtonsWidth = (settingsState.alignTabsToRight + ? _kAppBarControlsWidthRightAligned + : _kAppBarControlsWidth) + + (showAndroidFullscreenButton ? _kActionButtonWidth : 0); + double extraButtonsWidth = + readingActionButtonsCount * _kActionButtonWidth; double totalLeft = actionButtonsWidth; double totalRight = extraButtonsWidth + windowControlsWidth; @@ -358,6 +412,12 @@ class _CustomTitleBarState extends State } else { rightSpacerWidth = totalLeft - totalRight; } + + // באנדרואיד אנחנו רוצים את כפתורי ההגדרות/מסך מלא צמודים לשמאל + // בלי spacer נוסף בצד שמאל. + if (showAndroidFullscreenButton) { + rightSpacerWidth = 0; + } } return Row( @@ -418,17 +478,48 @@ class _CustomTitleBarState extends State ), ), - // כפתורים נוספים (הגדרות) + // כפתורים נוספים (הגדרות/מסך מלא באנדרואיד) Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), - child: IconButton( - icon: const Icon(FluentIcons.settings_24_regular, size: 18), - tooltip: 'הגדרות תצוגת הספרים', - onPressed: () => showReadingSettingsDialog(context), - style: _kIconButtonStyle.copyWith( - foregroundColor: WidgetStatePropertyAll( - Theme.of(context).colorScheme.onSurfaceVariant), - ), + child: Row( + mainAxisSize: MainAxisSize.min, + textDirection: TextDirection.ltr, + children: [ + if (showAndroidFullscreenButton) + IconButton( + icon: Icon( + settingsState.isFullscreen + ? FluentIcons.full_screen_minimize_24_regular + : FluentIcons.full_screen_maximize_24_regular, + size: 18, + ), + tooltip: settingsState.isFullscreen + ? 'צא ממסך מלא' + : 'מסך מלא', + onPressed: () async { + final newFullscreenState = !settingsState.isFullscreen; + await FullscreenHelper.toggleFullscreen( + context, + newFullscreenState, + ); + }, + style: _kIconButtonStyle.copyWith( + foregroundColor: WidgetStatePropertyAll( + Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + IconButton( + icon: const Icon(FluentIcons.settings_24_regular, size: 18), + tooltip: 'הגדרות תצוגת הספרים', + onPressed: () => showReadingSettingsDialog(context), + style: _kIconButtonStyle.copyWith( + foregroundColor: WidgetStatePropertyAll( + Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + ], ), ), if (rightSpacerWidth > 0) SizedBox(width: rightSpacerWidth), diff --git a/lib/utils/fullscreen_helper.dart b/lib/utils/fullscreen_helper.dart index d285aaae2..6fe6cdd35 100644 --- a/lib/utils/fullscreen_helper.dart +++ b/lib/utils/fullscreen_helper.dart @@ -1,4 +1,7 @@ +import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:window_manager/window_manager.dart'; import 'package:otzaria/settings/engine/settings_bloc.dart'; @@ -11,21 +14,31 @@ class FullscreenHelper { BuildContext context, bool isFullscreen, ) async { - // עדכון ה-state ב-Bloc final settingsBloc = context.read(); if (settingsBloc.state.isFullscreen != isFullscreen) { settingsBloc.add(UpdateIsFullscreen(isFullscreen)); } - // פעולות על מנהל החלונות - // חשוב: להסתיר את ה-title bar לפני המעבר למסך מלא כדי למנוע הבהוב - if (isFullscreen) { - await windowManager.setTitleBarStyle(TitleBarStyle.hidden); - await windowManager.setFullScreen(true); - } else { - await windowManager.setFullScreen(false); - // אנחנו משתמשים ב-CustomTitleBar ולכן תמיד רוצים להסתיר את הכותרת המקורית - await windowManager.setTitleBarStyle(TitleBarStyle.hidden); + if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) { + if (isFullscreen) { + await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + } else { + await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } + return; + } + + if (!kIsWeb && + (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) { + // חשוב: להסתיר את ה-title bar לפני המעבר למסך מלא כדי למנוע הבהוב + if (isFullscreen) { + await windowManager.setTitleBarStyle(TitleBarStyle.hidden); + await windowManager.setFullScreen(true); + } else { + await windowManager.setFullScreen(false); + // אנחנו משתמשים ב-CustomTitleBar ולכן תמיד רוצים להסתיר את הכותרת המקורית + await windowManager.setTitleBarStyle(TitleBarStyle.hidden); + } } } } From 959befc0d9a2c133c8321f9e198b1c4d10004aef Mon Sep 17 00:00:00 2001 From: y-ploni <7353755@gmail.com> Date: Wed, 4 Mar 2026 15:30:35 +0200 Subject: [PATCH 6/9] =?UTF-8?q?Revert=20"=D7=AA=D7=99=D7=A7=D7=95=D7=9F=20?= =?UTF-8?q?=D7=9E=D7=A1=D7=9A=20=D7=94=D7=92=D7=93=D7=A8=D7=95=D7=AA/?= =?UTF-8?q?=D7=90=D7=95=D7=A6=D7=A8=D7=99=D7=90/=D7=AA=D7=95=D7=A8=D7=9E?= =?UTF-8?q?=D7=99=D7=9D,=20=D7=91=D7=9E=D7=A1=D7=9B=D7=99=D7=9D=20=D7=A6?= =?UTF-8?q?=D7=A8=D7=99=D7=9D"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit c59e3e4af34e225f55d301092c10e4cb9c619f3c. --- lib/settings/tabs/system_settings_tab.dart | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/settings/tabs/system_settings_tab.dart b/lib/settings/tabs/system_settings_tab.dart index 66a832306..d396eb19c 100644 --- a/lib/settings/tabs/system_settings_tab.dart +++ b/lib/settings/tabs/system_settings_tab.dart @@ -846,15 +846,12 @@ class _MemorialCardsGrid extends StatelessWidget { Widget build(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { final spacing = 12.0; - final crossAxisCount = constraints.maxWidth < 560 ? 2 : 3; - final itemWidth = - (constraints.maxWidth - spacing * (crossAxisCount - 1)) / - crossAxisCount; - final itemHeight = crossAxisCount == 2 ? 158.0 : 150.0; + final itemWidth = (constraints.maxWidth - spacing * 2) / 3; + const itemHeight = 150.0; final aspectRatio = itemWidth / itemHeight; return GridView.count( - crossAxisCount: crossAxisCount, + crossAxisCount: 3, crossAxisSpacing: spacing, mainAxisSpacing: spacing, childAspectRatio: aspectRatio, From b907b554776209eedcc6a5689df61aa02d54f4eb Mon Sep 17 00:00:00 2001 From: gaon gadol Date: Thu, 5 Mar 2026 17:26:31 +0200 Subject: [PATCH 7/9] =?UTF-8?q?=D7=92=D7=9C=D7=99=D7=9C=D7=94=20=D7=91?= =?UTF-8?q?=D7=A6=D7=95=D7=A8=D7=AA=20=D7=94=D7=93=D7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - לא סופי --- .../view/page_shape/page_shape_screen.dart | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/lib/text_book/view/page_shape/page_shape_screen.dart b/lib/text_book/view/page_shape/page_shape_screen.dart index 5b2ca223f..b68eb977a 100644 --- a/lib/text_book/view/page_shape/page_shape_screen.dart +++ b/lib/text_book/view/page_shape/page_shape_screen.dart @@ -589,6 +589,8 @@ class _CommentaryPaneState extends State<_CommentaryPane> { ItemPositionsListener.create(); List _relevantLinks = []; int? _lastSyncedIndex; // האינדקס האחרון שסונכרן + int? _lastObservedSelectedIndex; // הבחירה האחרונה שנצפתה + bool _preferSelectedIndexNextSync = false; // סנכרון חד-פעמי אחרי בחירה ידנית StreamSubscription? _blocSubscription; Set _highlightedIndices = {}; // אינדקסים להדגשה bool _highlightEnabled = false; @@ -652,6 +654,7 @@ class _CommentaryPaneState extends State<_CommentaryPane> { // סנכרון ראשוני עם ה-state הנוכחי final currentState = context.read().state; if (currentState is TextBookLoaded && mounted) { + _lastObservedSelectedIndex = currentState.selectedIndex; // נדחה מעט כדי לוודא שה-ScrollController מוכן WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { @@ -662,12 +665,31 @@ class _CommentaryPaneState extends State<_CommentaryPane> { _blocSubscription = context.read().stream.listen((state) { if (state is TextBookLoaded && mounted) { + // עדכון קישורים רלוונטיים כשנטענים קישורים חדשים ב-Bloc + _refreshRelevantLinks(state); + if (state.selectedIndex != _lastObservedSelectedIndex) { + _preferSelectedIndexNextSync = state.selectedIndex != null; + _lastObservedSelectedIndex = state.selectedIndex; + } _syncWithMainText(state); _updateHighlights(state); } }); } + /// עדכון קישורים רלוונטיים מה-state (נקרא בכל שינוי state) + void _refreshRelevantLinks(TextBookLoaded state) { + if (state.links.length == _relevantLinks.length && + _relevantLinks.isNotEmpty) { + return; // הקישורים לא השתנו + } + _relevantLinks = state.links.where((link) { + final linkTitle = utils.getTitleFromPath(link.path2); + return linkTitle == widget.commentatorName && + LinkTypes.isCommentaryOrTargum(link.connectionType); + }).toList(); + } + void _updateHighlights(TextBookLoaded state) { if (!_highlightEnabled || state.selectedIndex == null) { if (_highlightedIndices.isNotEmpty) { @@ -724,12 +746,7 @@ class _CommentaryPaneState extends State<_CommentaryPane> { if (!mounted) return; if (state is TextBookLoaded) { - // סינון קישורים לפי שם המפרש ולפי סוג הקישור (COMMENTARY/TARGUM) - _relevantLinks = state.links.where((link) { - final linkTitle = utils.getTitleFromPath(link.path2); - return linkTitle == widget.commentatorName && - LinkTypes.isCommentaryOrTargum(link.connectionType); - }).toList(); + _refreshRelevantLinks(state); } // מציאת הספר המלא של המפרש עם categoryId @@ -840,6 +857,7 @@ class _CommentaryPaneState extends State<_CommentaryPane> { _content = lines; _isLoading = false; _lastSyncedIndex = null; // איפוס לסנכרון ראשוני + _preferSelectedIndexNextSync = false; }); // סנכרון ראשוני - נדחה מעט כדי לוודא שה-ScrollController מוכן @@ -890,12 +908,20 @@ class _CommentaryPaneState extends State<_CommentaryPane> { return; } - // קביעת האינדקס הנוכחי בטקסט הראשי - // נעדיף את visibleIndices כי זה המיקום האמיתי בגלילה + // קביעת האינדקס הנוכחי בטקסט הראשי: + // בחירה ידנית חדשה (selectedIndex) מקבלת קדימות חד-פעמית, + // וביתר הזמן נצמדים ל-visibleIndices כדי לשמור על גלילה חלקה. + final useSelectedIndex = + _preferSelectedIndexNextSync && state.selectedIndex != null; + int currentMainIndex; - if (state.visibleIndices.isNotEmpty) { + if (useSelectedIndex) { + currentMainIndex = state.selectedIndex!; + _preferSelectedIndexNextSync = false; + } else if (state.visibleIndices.isNotEmpty) { currentMainIndex = state.visibleIndices.first; } else if (state.selectedIndex != null) { + // fallback למקרה שאין visibleIndices currentMainIndex = state.selectedIndex!; } else { return; // אין מידע על מיקום נוכחי @@ -963,15 +989,6 @@ class _CommentaryPaneState extends State<_CommentaryPane> { return TextBookStateBuilder( loadingWidget: const SizedBox(), builder: (context, state) { - // ניסיון סנכרון נוסף כשה-widget נבנה (במקרה שהסנכרון הראשוני נכשל) - if (_lastSyncedIndex == null && _scrollController.isAttached) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { - _syncWithMainText(state); - } - }); - } - return BlocBuilder( builder: (context, settingsState) { // מפרשים תחתונים משתמשים בגופן מההגדרות, עליונים בגופן הרגיל From 1aa5c91aa0114be3492671960fcd8293de95f3bb Mon Sep 17 00:00:00 2001 From: Y-Ploni <7353755@gmail.com> Date: Thu, 5 Mar 2026 17:49:06 +0200 Subject: [PATCH 8/9] =?UTF-8?q?=D7=A8=D7=99=D7=A4=D7=A7=D7=98=D7=95=D7=A8?= =?UTF-8?q?=20=D7=A0=D7=99=D7=94=D7=95=D7=9C=20=D7=92=D7=9C=D7=99=D7=9C?= =?UTF-8?q?=D7=94=20=D7=91=D7=91=D7=9C=D7=95=D7=A7=20=D7=9C=D7=A6=D7=95?= =?UTF-8?q?=D7=A8=D7=AA=20=D7=94=D7=93=D7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/text_book/bloc/text_book_bloc.dart | 76 +++++++++++++++--- lib/text_book/bloc/text_book_event.dart | 15 ++++ lib/text_book/bloc/text_book_state.dart | 13 ++++ .../view/page_shape/page_shape_screen.dart | 30 ++----- .../view/page_shape/simple_text_viewer.dart | 78 ++++++------------- 5 files changed, 125 insertions(+), 87 deletions(-) diff --git a/lib/text_book/bloc/text_book_bloc.dart b/lib/text_book/bloc/text_book_bloc.dart index 80d522d6e..cacb717a5 100644 --- a/lib/text_book/bloc/text_book_bloc.dart +++ b/lib/text_book/bloc/text_book_bloc.dart @@ -49,6 +49,7 @@ class TextBookBloc extends Bloc { on(_onToggleNikud); on(_onUpdateVisibleIndecies); on(_onUpdateSelectedIndex); + on(_onSelectAndScrollToIndex); on(_onHighlightLine); on(_onClearHighlightedLine); on(_onTogglePinLeftPane); @@ -249,6 +250,9 @@ class TextBookBloc extends Bloc { selectedTextEnd: state is TextBookLoaded ? (state as TextBookLoaded).selectedTextEnd : null, + pageShapeAnchorIndex: + visibleIndices.isNotEmpty ? visibleIndices.first : null, + pageShapePreferSelectedNextSync: false, )); // ── שלב 5: טעינות ברקע - לא חוסמות את ה-UI ── @@ -447,11 +451,21 @@ class TextBookBloc extends Bloc { selectedIndex: index, ); + final shouldUseSelected = + currentState.pageShapePreferSelectedNextSync && index != null; + final pageShapeAnchorIndex = shouldUseSelected + ? index + : (event.visibleIndecies.isNotEmpty + ? event.visibleIndecies.first + : index); + emit(currentState.copyWith( visibleIndices: event.visibleIndecies, currentTitle: newTitle, selectedIndex: index, visibleLinks: visibleLinks, + pageShapeAnchorIndex: pageShapeAnchorIndex, + pageShapePreferSelectedNextSync: false, )); } } @@ -469,20 +483,62 @@ class TextBookBloc extends Bloc { UpdateSelectedIndex event, Emitter emit, ) { - if (state is TextBookLoaded) { - final currentState = state as TextBookLoaded; - final visibleLinks = _getVisibleLinks( - links: currentState.links, - visibleIndices: currentState.visibleIndices, - selectedIndex: event.index, + if (state is! TextBookLoaded) return; + final currentState = state as TextBookLoaded; + _emitSelectedIndex( + currentState, + emit, + event.index, + preferForPageShape: true, + ); + } + + void _onSelectAndScrollToIndex( + SelectAndScrollToIndex event, + Emitter emit, + ) { + if (state is! TextBookLoaded) return; + final currentState = state as TextBookLoaded; + if (currentState.content.isEmpty) return; + + final boundedIndex = event.index.clamp(0, currentState.content.length - 1); + + _emitSelectedIndex( + currentState, + emit, + boundedIndex, + preferForPageShape: true, + ); + + if (scrollController.isAttached) { + scrollController.scrollTo( + index: boundedIndex, + duration: Duration(milliseconds: event.durationMs), + alignment: event.alignment, ); - emit(currentState.copyWith( - selectedIndex: event.index, - visibleLinks: visibleLinks, - )); } } + void _emitSelectedIndex( + TextBookLoaded currentState, + Emitter emit, + int? index, { + required bool preferForPageShape, + }) { + final visibleLinks = _getVisibleLinks( + links: currentState.links, + visibleIndices: currentState.visibleIndices, + selectedIndex: index, + ); + + emit(currentState.copyWith( + selectedIndex: index, + visibleLinks: visibleLinks, + pageShapeAnchorIndex: index ?? currentState.pageShapeAnchorIndex, + pageShapePreferSelectedNextSync: preferForPageShape && index != null, + )); + } + void _onHighlightLine( HighlightLine event, Emitter emit, diff --git a/lib/text_book/bloc/text_book_event.dart b/lib/text_book/bloc/text_book_event.dart index 7303861a9..3cc26012a 100644 --- a/lib/text_book/bloc/text_book_event.dart +++ b/lib/text_book/bloc/text_book_event.dart @@ -117,6 +117,21 @@ class UpdateSelectedIndex extends TextBookEvent { List get props => [index]; } +class SelectAndScrollToIndex extends TextBookEvent { + final int index; + final int durationMs; + final double? alignment; + + const SelectAndScrollToIndex( + this.index, { + this.durationMs = 300, + this.alignment, + }); + + @override + List get props => [index, durationMs, alignment]; +} + class HighlightLine extends TextBookEvent { final int lineIndex; diff --git a/lib/text_book/bloc/text_book_state.dart b/lib/text_book/bloc/text_book_state.dart index d4419b38d..0f396f94a 100644 --- a/lib/text_book/bloc/text_book_state.dart +++ b/lib/text_book/bloc/text_book_state.dart @@ -101,6 +101,8 @@ class TextBookLoaded extends TextBookState { final int? selectedTextStart; final int? selectedTextEnd; final int? highlightedLine; + final int? pageShapeAnchorIndex; + final bool pageShapePreferSelectedNextSync; // Editor state final bool isEditorOpen; @@ -144,6 +146,8 @@ class TextBookLoaded extends TextBookState { this.selectedTextStart, this.selectedTextEnd, this.highlightedLine, + this.pageShapeAnchorIndex, + this.pageShapePreferSelectedNextSync = false, this.isEditorOpen = false, this.editorIndex, this.editorSectionId, @@ -183,6 +187,8 @@ class TextBookLoaded extends TextBookState { selectedTextStart: null, selectedTextEnd: null, highlightedLine: null, + pageShapeAnchorIndex: index, + pageShapePreferSelectedNextSync: false, isEditorOpen: false, editorIndex: null, editorSectionId: null, @@ -222,6 +228,8 @@ class TextBookLoaded extends TextBookState { int? selectedTextStart, int? selectedTextEnd, int? highlightedLine, + int? pageShapeAnchorIndex, + bool? pageShapePreferSelectedNextSync, bool clearHighlight = false, bool? isEditorOpen, int? editorIndex, @@ -262,6 +270,9 @@ class TextBookLoaded extends TextBookState { selectedTextEnd: selectedTextEnd ?? this.selectedTextEnd, highlightedLine: clearHighlight ? null : (highlightedLine ?? this.highlightedLine), + pageShapeAnchorIndex: pageShapeAnchorIndex ?? this.pageShapeAnchorIndex, + pageShapePreferSelectedNextSync: pageShapePreferSelectedNextSync ?? + this.pageShapePreferSelectedNextSync, isEditorOpen: isEditorOpen ?? this.isEditorOpen, editorIndex: editorIndex ?? this.editorIndex, editorSectionId: editorSectionId ?? this.editorSectionId, @@ -296,6 +307,8 @@ class TextBookLoaded extends TextBookState { selectedTextStart, selectedTextEnd, highlightedLine, + pageShapeAnchorIndex, + pageShapePreferSelectedNextSync, isEditorOpen, editorIndex, editorSectionId, diff --git a/lib/text_book/view/page_shape/page_shape_screen.dart b/lib/text_book/view/page_shape/page_shape_screen.dart index b68eb977a..8637fc207 100644 --- a/lib/text_book/view/page_shape/page_shape_screen.dart +++ b/lib/text_book/view/page_shape/page_shape_screen.dart @@ -589,8 +589,6 @@ class _CommentaryPaneState extends State<_CommentaryPane> { ItemPositionsListener.create(); List _relevantLinks = []; int? _lastSyncedIndex; // האינדקס האחרון שסונכרן - int? _lastObservedSelectedIndex; // הבחירה האחרונה שנצפתה - bool _preferSelectedIndexNextSync = false; // סנכרון חד-פעמי אחרי בחירה ידנית StreamSubscription? _blocSubscription; Set _highlightedIndices = {}; // אינדקסים להדגשה bool _highlightEnabled = false; @@ -654,7 +652,6 @@ class _CommentaryPaneState extends State<_CommentaryPane> { // סנכרון ראשוני עם ה-state הנוכחי final currentState = context.read().state; if (currentState is TextBookLoaded && mounted) { - _lastObservedSelectedIndex = currentState.selectedIndex; // נדחה מעט כדי לוודא שה-ScrollController מוכן WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { @@ -667,10 +664,6 @@ class _CommentaryPaneState extends State<_CommentaryPane> { if (state is TextBookLoaded && mounted) { // עדכון קישורים רלוונטיים כשנטענים קישורים חדשים ב-Bloc _refreshRelevantLinks(state); - if (state.selectedIndex != _lastObservedSelectedIndex) { - _preferSelectedIndexNextSync = state.selectedIndex != null; - _lastObservedSelectedIndex = state.selectedIndex; - } _syncWithMainText(state); _updateHighlights(state); } @@ -857,7 +850,6 @@ class _CommentaryPaneState extends State<_CommentaryPane> { _content = lines; _isLoading = false; _lastSyncedIndex = null; // איפוס לסנכרון ראשוני - _preferSelectedIndexNextSync = false; }); // סנכרון ראשוני - נדחה מעט כדי לוודא שה-ScrollController מוכן @@ -908,22 +900,12 @@ class _CommentaryPaneState extends State<_CommentaryPane> { return; } - // קביעת האינדקס הנוכחי בטקסט הראשי: - // בחירה ידנית חדשה (selectedIndex) מקבלת קדימות חד-פעמית, - // וביתר הזמן נצמדים ל-visibleIndices כדי לשמור על גלילה חלקה. - final useSelectedIndex = - _preferSelectedIndexNextSync && state.selectedIndex != null; - - int currentMainIndex; - if (useSelectedIndex) { - currentMainIndex = state.selectedIndex!; - _preferSelectedIndexNextSync = false; - } else if (state.visibleIndices.isNotEmpty) { - currentMainIndex = state.visibleIndices.first; - } else if (state.selectedIndex != null) { - // fallback למקרה שאין visibleIndices - currentMainIndex = state.selectedIndex!; - } else { + final currentMainIndex = state.pageShapeAnchorIndex ?? + (state.visibleIndices.isNotEmpty + ? state.visibleIndices.first + : state.selectedIndex); + + if (currentMainIndex == null) { return; // אין מידע על מיקום נוכחי } diff --git a/lib/text_book/view/page_shape/simple_text_viewer.dart b/lib/text_book/view/page_shape/simple_text_viewer.dart index af7211432..950cc5ff7 100644 --- a/lib/text_book/view/page_shape/simple_text_viewer.dart +++ b/lib/text_book/view/page_shape/simple_text_viewer.dart @@ -107,14 +107,26 @@ class _SimpleTextViewerState extends State { void _scrollToCurrentPosition() { final bloc = context.read(); final state = bloc.state; - if (state is TextBookLoaded && _scrollController.isAttached) { - final targetIndex = state.selectedIndex ?? - (state.visibleIndices.isNotEmpty ? state.visibleIndices.first : null); + if (state is! TextBookLoaded) return; - if (targetIndex != null && targetIndex < widget.content.length) { - _scrollController.jumpTo(index: targetIndex); - } - } + final targetIndex = state.selectedIndex ?? + (state.visibleIndices.isNotEmpty ? state.visibleIndices.first : null); + if (targetIndex == null || targetIndex >= widget.content.length) return; + + bloc.add(SelectAndScrollToIndex( + targetIndex, + durationMs: 1, + )); + } + + void _selectAndScrollTo(int index, {int durationMs = 300, double? alignment}) { + context.read().add( + SelectAndScrollToIndex( + index, + durationMs: durationMs, + alignment: alignment, + ), + ); } /// טיפול באירועי מקלדת - חיצים לניווט @@ -130,14 +142,7 @@ class _SimpleTextViewerState extends State { final currentIndex = state.selectedIndex ?? 0; final nextIndex = (currentIndex + 1).clamp(0, widget.content.length - 1); if (nextIndex != currentIndex) { - context.read().add(UpdateSelectedIndex(nextIndex)); - if (_scrollController.isAttached) { - _scrollController.scrollTo( - index: nextIndex, - duration: const Duration(milliseconds: 200), - alignment: 0.5, - ); - } + _selectAndScrollTo(nextIndex, durationMs: 200, alignment: 0.5); } return true; } @@ -146,14 +151,7 @@ class _SimpleTextViewerState extends State { final currentIndex = state.selectedIndex ?? 0; final prevIndex = (currentIndex - 1).clamp(0, widget.content.length - 1); if (prevIndex != currentIndex) { - context.read().add(UpdateSelectedIndex(prevIndex)); - if (_scrollController.isAttached) { - _scrollController.scrollTo( - index: prevIndex, - duration: const Duration(milliseconds: 200), - alignment: 0.5, - ); - } + _selectAndScrollTo(prevIndex, durationMs: 200, alignment: 0.5); } return true; } @@ -162,14 +160,7 @@ class _SimpleTextViewerState extends State { if (event.logicalKey == LogicalKeyboardKey.pageDown) { final currentIndex = state.selectedIndex ?? 0; final nextIndex = (currentIndex + 10).clamp(0, widget.content.length - 1); - context.read().add(UpdateSelectedIndex(nextIndex)); - if (_scrollController.isAttached) { - _scrollController.scrollTo( - index: nextIndex, - duration: const Duration(milliseconds: 300), - alignment: 0.5, - ); - } + _selectAndScrollTo(nextIndex, alignment: 0.5); return true; } @@ -177,27 +168,14 @@ class _SimpleTextViewerState extends State { if (event.logicalKey == LogicalKeyboardKey.pageUp) { final currentIndex = state.selectedIndex ?? 0; final prevIndex = (currentIndex - 10).clamp(0, widget.content.length - 1); - context.read().add(UpdateSelectedIndex(prevIndex)); - if (_scrollController.isAttached) { - _scrollController.scrollTo( - index: prevIndex, - duration: const Duration(milliseconds: 300), - alignment: 0.5, - ); - } + _selectAndScrollTo(prevIndex, alignment: 0.5); return true; } // Home - תחילת הספר if (event.logicalKey == LogicalKeyboardKey.home && HardwareKeyboard.instance.isControlPressed) { - context.read().add(const UpdateSelectedIndex(0)); - if (_scrollController.isAttached) { - _scrollController.scrollTo( - index: 0, - duration: const Duration(milliseconds: 300), - ); - } + _selectAndScrollTo(0); return true; } @@ -205,13 +183,7 @@ class _SimpleTextViewerState extends State { if (event.logicalKey == LogicalKeyboardKey.end && HardwareKeyboard.instance.isControlPressed) { final lastIndex = widget.content.length - 1; - context.read().add(UpdateSelectedIndex(lastIndex)); - if (_scrollController.isAttached) { - _scrollController.scrollTo( - index: lastIndex, - duration: const Duration(milliseconds: 300), - ); - } + _selectAndScrollTo(lastIndex); return true; } From e235fc7dcfa1afc7e48be8f89e28e1601e61d9d3 Mon Sep 17 00:00:00 2001 From: gaon gadol Date: Mon, 9 Mar 2026 18:17:44 +0200 Subject: [PATCH 9/9] =?UTF-8?q?=D7=AA=D7=99=D7=A7=D7=95=D7=9F=20=D7=A9?= =?UTF-8?q?=D7=92=D7=99=D7=90=D7=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/text_book/bloc/text_book_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/text_book/bloc/text_book_bloc.dart b/lib/text_book/bloc/text_book_bloc.dart index cacb717a5..5f06ebc4e 100644 --- a/lib/text_book/bloc/text_book_bloc.dart +++ b/lib/text_book/bloc/text_book_bloc.dart @@ -514,7 +514,7 @@ class TextBookBloc extends Bloc { scrollController.scrollTo( index: boundedIndex, duration: Duration(milliseconds: event.durationMs), - alignment: event.alignment, + alignment: event.alignment ?? 0.0, ); } }