diff --git a/lib/core/theme/theme_controller.dart b/lib/core/theme/theme_controller.dart index 5f11fcac..711e019c 100644 --- a/lib/core/theme/theme_controller.dart +++ b/lib/core/theme/theme_controller.dart @@ -471,8 +471,13 @@ class ThemeController extends ChangeNotifier with WidgetsBindingObserver { Future setThemeMode(ThemeMode mode) async { _themeMode = mode; + final effectiveBrightness = _effectiveBrightness(); + if (_registryTheme != null && + _registryTheme!.brightness != effectiveBrightness) { + await _clearRegistrySelection(); + } if (_registryTheme == null && _preset != QueryaThemePreset.imported) { - _preset = mode == ThemeMode.light + _preset = effectiveBrightness == Brightness.light ? QueryaThemePreset.queryaLight : QueryaThemePreset.queryaDark; await AppSettings.instance.setThemePreset(_preset); diff --git a/lib/features/workspace/generic_sql_workspace.dart b/lib/features/workspace/generic_sql_workspace.dart index aab422a1..a28a4d87 100644 --- a/lib/features/workspace/generic_sql_workspace.dart +++ b/lib/features/workspace/generic_sql_workspace.dart @@ -37,6 +37,8 @@ import 'package:querya_desktop/features/workspace/sql_query_tab_session.dart'; import 'package:querya_desktop/features/workspace/sql_result_grid_schema.dart'; import 'package:querya_desktop/features/workspace/table_view_staging.dart'; import 'package:querya_desktop/features/workspace/sql_workspace_toolbar.dart'; +import 'package:querya_desktop/core/theme/querya_theme.dart'; +import 'package:querya_desktop/core/theme/querya_theme_scope.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; /// Reusable base SQL workspace supporting multi-tab editing, statement execution, @@ -169,6 +171,18 @@ class GenericSqlWorkspaceState extends material.State { } } + QueryaTheme? _lastTheme; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final theme = QueryaThemeScope.maybeOf(context); + if (_lastTheme != null && _lastTheme != theme) { + invalidateAllPanes(); + } + _lastTheme = theme; + } + SqlExecutionDelegate? _catalogDelegate; SqlExecutionDelegate get _diagramDelegate { @@ -1269,9 +1283,7 @@ class GenericSqlWorkspaceState extends material.State { top: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - material.Container( - padding: const material.EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: SqlEditorChrome.sqlToolbarDecoration(context), + _SqlWorkspaceToolbarContainer( child: SqlWorkspaceToolbar( session: session, delegate: widget.delegate, @@ -1356,3 +1368,18 @@ class GenericSqlWorkspaceState extends material.State { ); } } + +class _SqlWorkspaceToolbarContainer extends StatelessWidget { + const _SqlWorkspaceToolbarContainer({required this.child}); + + final material.Widget child; + + @override + material.Widget build(material.BuildContext context) { + return material.Container( + padding: const material.EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: SqlEditorChrome.sqlToolbarDecoration(context), + child: child, + ); + } +} diff --git a/lib/features/workspace/sql_editor_chrome.dart b/lib/features/workspace/sql_editor_chrome.dart index 4ca45a36..b3a35420 100644 --- a/lib/features/workspace/sql_editor_chrome.dart +++ b/lib/features/workspace/sql_editor_chrome.dart @@ -26,7 +26,7 @@ class SqlEditorChrome extends StatelessWidget { ) { final workbench = context.workbench; return material.BoxDecoration( - color: workbench.surface.withValues(alpha: 0.85), + color: workbench.surface, border: material.Border( bottom: material.BorderSide( color: workbench.borderSubtle.withValues(alpha: 0.35), diff --git a/test/core/theme/theme_controller_test.dart b/test/core/theme/theme_controller_test.dart index 44bf7d39..a67a36ed 100644 --- a/test/core/theme/theme_controller_test.dart +++ b/test/core/theme/theme_controller_test.dart @@ -354,6 +354,27 @@ void main() { expect(await AppSettings.instance.getSelectedThemeId(), isNull); }); + test( + 'setThemeMode clears conflicting registry selection and aligns preset (#1577)', + () async { + final c = ThemeController.instance; + await _copyFixture( + 'querya_custom_dark.json', + File(p.join(themesDir.path, 'querya_custom_dark.json')), + ); + await c.load(); + await c.setThemeById('fixture-custom-dark'); + expect(c.selectedThemeId, 'fixture-custom-dark'); + expect(c.activeTheme.brightness, Brightness.dark); + + await c.setThemeMode(ThemeMode.light); + + expect(c.selectedThemeId, isNull); + expect(c.activeTheme, QueryaTheme.lightDefault); + expect(c.preset, QueryaThemePreset.queryaLight); + expect(await AppSettings.instance.getSelectedThemeId(), isNull); + }); + test('setThemeById applies built-in asset theme from registry', () async { final c = ThemeController.instance; await c.load(); diff --git a/test/features/workspace/generic_sql_workspace_sessions_test.dart b/test/features/workspace/generic_sql_workspace_sessions_test.dart index d832d6e8..812e635f 100644 --- a/test/features/workspace/generic_sql_workspace_sessions_test.dart +++ b/test/features/workspace/generic_sql_workspace_sessions_test.dart @@ -6,7 +6,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:querya_desktop/core/actions/sql_editor_command_bridge.dart'; import 'package:querya_desktop/core/database/table_mutation_engine.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; +import 'package:querya_desktop/core/theme/querya_theme.dart'; import 'package:querya_desktop/features/workspace/generic_sql_workspace.dart'; +import 'package:querya_desktop/features/workspace/sql_workspace_toolbar.dart'; import '../../support/fake_sql_execution_delegate.dart'; import '../../support/local_db_test_support.dart'; @@ -254,4 +256,58 @@ void main() { () => Future.delayed(const Duration(milliseconds: 100)), ); }); + + testWidgets( + 'updates toolbar background when theme switches between dark and light (#1577)', + timeout: _timeout, (tester) async { + final delegate = FakeSqlExecutionDelegate(); + await tester.binding.setSurfaceSize(const material.Size(1200, 800)); + addTearDown(() => tester.binding.setSurfaceSize(null)); + + material.Widget buildWithTheme(QueryaTheme theme) { + return queryaThemeTestShell( + data: theme, + child: material.SizedBox.expand( + child: GenericSqlWorkspace( + connectionRow: connection, + delegate: delegate, + dialect: SqlDialect.sqlite, + ), + ), + ); + } + + // 1. Initial dark theme mount + await tester.pumpWidget(buildWithTheme(QueryaTheme.darkDefault)); + await tester.pumpAndSettle(); + + final toolbarFinder = find.byType(SqlWorkspaceToolbar); + expect(toolbarFinder, findsOneWidget); + + material.BoxDecoration getToolbarDecoration() { + final container = tester.widget( + find + .ancestor( + of: toolbarFinder, + matching: find.byType(material.Container), + ) + .first, + ); + return container.decoration! as material.BoxDecoration; + } + + expect( + getToolbarDecoration().color, + QueryaTheme.darkDefault.workbench.surface, + ); + + // 2. Switch to light theme + await tester.pumpWidget(buildWithTheme(QueryaTheme.lightDefault)); + await tester.pumpAndSettle(); + + expect( + getToolbarDecoration().color, + QueryaTheme.lightDefault.workbench.surface, + ); + }); } diff --git a/test/features/workspace/sql_editor_chrome_test.dart b/test/features/workspace/sql_editor_chrome_test.dart index 316a4b12..5d9132be 100644 --- a/test/features/workspace/sql_editor_chrome_test.dart +++ b/test/features/workspace/sql_editor_chrome_test.dart @@ -139,4 +139,52 @@ void main() { expect(outerGlow, const Color(0xFF00FFEE).withValues(alpha: 0.1)); }); }); + + group('SqlEditorChrome.sqlToolbarDecoration', () { + testWidgets('uses solid surface and subtle border in dark theme', + (tester) async { + late material.BoxDecoration deco; + await tester.pumpWidget( + queryaThemeTestShell( + data: QueryaTheme.darkDefault, + child: material.Builder( + builder: (context) { + deco = SqlEditorChrome.sqlToolbarDecoration(context); + return const material.SizedBox.shrink(); + }, + ), + ), + ); + expect(deco.color, QueryaTheme.darkDefault.workbench.surface); + final border = deco.border as Border?; + expect( + border?.bottom.color, + QueryaTheme.darkDefault.workbench.borderSubtle + .withValues(alpha: 0.35), + ); + }); + + testWidgets('uses solid surface and subtle border in light theme', + (tester) async { + late material.BoxDecoration deco; + await tester.pumpWidget( + queryaThemeTestShell( + data: QueryaTheme.lightDefault, + child: material.Builder( + builder: (context) { + deco = SqlEditorChrome.sqlToolbarDecoration(context); + return const material.SizedBox.shrink(); + }, + ), + ), + ); + expect(deco.color, QueryaTheme.lightDefault.workbench.surface); + final border = deco.border as Border?; + expect( + border?.bottom.color, + QueryaTheme.lightDefault.workbench.borderSubtle + .withValues(alpha: 0.35), + ); + }); + }); }