diff --git a/lib/features/extensions/extension_driver_recovery_banner.dart b/lib/features/extensions/extension_driver_recovery_banner.dart index f9947bf9..ace33501 100644 --- a/lib/features/extensions/extension_driver_recovery_banner.dart +++ b/lib/features/extensions/extension_driver_recovery_banner.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart' as material; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Banner displayed when an extension driver process crashes, deadlocks, /// or disconnects unexpectedly, allowing the user to restart the driver process @@ -61,18 +62,12 @@ class ExtensionDriverRecoveryBanner extends material.StatelessWidget { ), ), const Gap(12), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: isRestarting ? 'Restarting...' : 'Restart Driver', + icon: material.Icons.restart_alt_rounded, + loading: isRestarting, onPressed: isRestarting ? null : onRestart, - leading: isRestarting - ? const QueryaSpinner( - size: QueryaSpinnerSize.sm, - ) - : const material.Icon( - material.Icons.restart_alt_rounded, - size: 16, - ), - child: Text(isRestarting ? 'Restarting...' : 'Restart Driver'), + size: QueryaControlSize.sm, ), ], ), diff --git a/lib/features/extensions/extension_table_toolbar.dart b/lib/features/extensions/extension_table_toolbar.dart index 92b038f5..e95f7762 100644 --- a/lib/features/extensions/extension_table_toolbar.dart +++ b/lib/features/extensions/extension_table_toolbar.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart' as material; import 'package:querya_desktop/shared/services/data_export_service.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Standard toolbar for [ExtensionTableView] with title, pagination chip, DDL inspection, custom filter toggle, export/save actions, and navigation. class ExtensionTableToolbar extends material.StatelessWidget { @@ -71,14 +72,11 @@ class ExtensionTableToolbar extends material.StatelessWidget { if (onNavigateHome != null) ...[ material.Tooltip( message: 'Return to driver overview', - child: OutlineButton( - size: ButtonSize.small, + child: QueryaButton.secondary( + label: 'Driver', + icon: material.Icons.dns_outlined, onPressed: onNavigateHome, - leading: const material.Icon( - material.Icons.dns_outlined, - size: 14, - ), - child: const Text('Driver'), + size: QueryaControlSize.sm, ), ), const Gap(10), @@ -133,26 +131,20 @@ class ExtensionTableToolbar extends material.StatelessWidget { editAction!, const Gap(4), ], - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'DDL', + icon: material.Icons.code_rounded, onPressed: onOpenDdl, - leading: const material.Icon( - material.Icons.code_rounded, - size: 16, - ), - child: const Text('DDL'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, - onPressed: onToggleFilter, - leading: material.Icon( - isFiltered + QueryaButton.secondary( + label: isFiltered ? 'Filter (active)' : 'Filter', + icon: isFiltered ? material.Icons.filter_alt_rounded : material.Icons.filter_alt_outlined, - size: 15, - ), - child: Text(isFiltered ? 'Filter (active)' : 'Filter'), + onPressed: onToggleFilter, + size: QueryaControlSize.sm, ), if (onCopyFormat != null) ...[ const Gap(4), @@ -174,59 +166,43 @@ class ExtensionTableToolbar extends material.StatelessWidget { ], if (loading && onCancelQuery != null) ...[ const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.ghost( + label: 'Cancel', + icon: material.Icons.cancel_outlined, onPressed: onCancelQuery, - leading: const material.Icon( - material.Icons.cancel_outlined, - size: 15, - ), - child: const Text('Cancel'), + size: QueryaControlSize.sm, ), ], const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Prev', + icon: material.Icons.chevron_left_rounded, onPressed: canGoPrevious ? onGoPrevious : null, - leading: const material.Icon( - material.Icons.chevron_left_rounded, - size: 16, - ), - child: const Text('Prev'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Next', + icon: material.Icons.chevron_right_rounded, onPressed: canGoNext ? onGoNext : null, - leading: const material.Icon( - material.Icons.chevron_right_rounded, - size: 16, - ), - child: const Text('Next'), + size: QueryaControlSize.sm, ), const Gap(8), if (onRestartDriver != null) ...[ - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: isRestarting ? 'Restarting...' : 'Restart Driver', + icon: material.Icons.restart_alt_rounded, + loading: isRestarting, onPressed: loading || isRestarting ? null : onRestartDriver, - leading: isRestarting - ? const QueryaSpinner(size: QueryaSpinnerSize.sm) - : const material.Icon( - material.Icons.restart_alt_rounded, - size: 15, - ), - child: Text(isRestarting ? 'Restarting...' : 'Restart Driver'), + size: QueryaControlSize.sm, ), const Gap(4), ], - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Refresh', + icon: material.Icons.refresh_rounded, onPressed: loading ? null : onRefresh, - leading: const material.Icon( - material.Icons.refresh_rounded, - size: 14, - ), - child: const Text('Refresh'), + size: QueryaControlSize.sm, ), ], ), diff --git a/lib/features/extensions/extension_table_view.dart b/lib/features/extensions/extension_table_view.dart index 5b80dde8..91125a24 100644 --- a/lib/features/extensions/extension_table_view.dart +++ b/lib/features/extensions/extension_table_view.dart @@ -12,6 +12,7 @@ import 'package:querya_desktop/features/extensions/extension_table_toolbar.dart' import 'package:querya_desktop/features/workspace/workspace.dart'; import 'package:querya_desktop/shared/services/data_export_service.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; const _defaultPageSize = 200; @@ -419,9 +420,10 @@ class _ExtensionTableViewState extends material.State { const Gap(16), material.Align( alignment: material.Alignment.centerRight, - child: OutlineButton( + child: QueryaButton.ghost( + label: 'Close', onPressed: () => material.Navigator.of(ctx).pop(), - child: const Text('Close'), + size: QueryaControlSize.md, ), ), ], @@ -511,23 +513,23 @@ class _ExtensionTableViewState extends material.State { ), ), const Gap(8), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Apply', onPressed: () { final st = _genericKey.currentState; if (st != null) _applyFilter(st); }, - child: const Text('Apply'), + size: QueryaControlSize.sm, ), if (_filterController.text.isNotEmpty) ...[ const Gap(6), - GhostButton( - size: ButtonSize.small, + QueryaButton.ghost( + label: 'Clear', onPressed: () { final st = _genericKey.currentState; if (st != null) _clearFilter(st); }, - child: const Text('Clear'), + size: QueryaControlSize.sm, ), ], ], @@ -608,9 +610,10 @@ Future _showSaveFileErrorDialog(material.BuildContext context) { const Gap(20), material.Align( alignment: material.Alignment.centerRight, - child: PrimaryButton( + child: QueryaButton.primary( + label: 'OK', onPressed: () => material.Navigator.of(ctx).pop(), - child: const Text('OK'), + size: QueryaControlSize.md, ), ), ], diff --git a/lib/features/extensions/presentation/widgets/extension_card.dart b/lib/features/extensions/presentation/widgets/extension_card.dart index 0419ce34..5c508119 100644 --- a/lib/features/extensions/presentation/widgets/extension_card.dart +++ b/lib/features/extensions/presentation/widgets/extension_card.dart @@ -3,6 +3,7 @@ import 'package:querya_desktop/core/extensions/extension_support.dart'; import 'package:querya_desktop/core/extensions/models/extension_manifest.dart'; import 'package:querya_desktop/core/extensions/models/extension_type.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; class ExtensionCard extends material.StatelessWidget { const ExtensionCard({ @@ -104,9 +105,10 @@ class ExtensionCard extends material.StatelessWidget { if (isInstalled && hasUpdate && !isInstalling) material.Padding( padding: const material.EdgeInsets.only(bottom: 8.0), - child: PrimaryButton( + child: QueryaButton.primary( + label: 'Update', onPressed: onUpdate, - child: const Text('Update'), + size: QueryaControlSize.md, ), ), if (isInstalling) @@ -132,22 +134,25 @@ class ExtensionCard extends material.StatelessWidget { ], ) else if (isInstalled) - SecondaryButton( + QueryaButton.secondary( + label: 'Uninstall', onPressed: onUninstall, - child: const Text('Uninstall'), + size: QueryaControlSize.md, ) else if (isPreview) const material.Tooltip( message: ExtensionSupport.databaseDriverPreviewNotice, - child: OutlineButton( + child: QueryaButton.secondary( + label: 'Preview', onPressed: null, - child: Text('Preview'), + size: QueryaControlSize.md, ), ) else - PrimaryButton( + QueryaButton.primary( + label: 'Install', onPressed: onInstall, - child: const Text('Install'), + size: QueryaControlSize.md, ), ], ), diff --git a/lib/features/main_screen/workspace_empty_hero.dart b/lib/features/main_screen/workspace_empty_hero.dart index 53c15470..9aa3bab1 100644 --- a/lib/features/main_screen/workspace_empty_hero.dart +++ b/lib/features/main_screen/workspace_empty_hero.dart @@ -8,6 +8,7 @@ import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/core/ui/querya_icons.dart'; import 'package:querya_desktop/features/connections/driver_icon.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Task-oriented empty workspace shown before a connection is selected. class WorkspaceEmptyHero extends StatefulWidget { @@ -149,44 +150,36 @@ class _WorkspaceEmptyHeroState extends State { spacing: 10, runSpacing: 10, children: [ - PrimaryButton( + QueryaButton.primary( key: const Key('empty_new_connection'), + label: 'New connection', + icon: material.Icons.add_link_rounded, onPressed: widget.onNewConnection, - leading: const material.Icon( - material.Icons.add_link_rounded, - size: 18, - ), - child: const Text('New connection'), + size: QueryaControlSize.md, ), if (widget.onLaunchDemo != null) - OutlineButton( + QueryaButton.secondary( key: const Key('empty_try_demo_playground'), + label: 'Demo Playground', + icon: material.Icons.play_circle_outline_rounded, onPressed: widget.onLaunchDemo, - leading: const material.Icon( - material.Icons.play_circle_outline_rounded, - size: 18, - ), - child: const Text('Demo Playground'), + size: QueryaControlSize.md, ), if (widget.onNewConnectionFromUrl != null) - OutlineButton( + QueryaButton.secondary( key: const Key('empty_new_from_url'), + label: 'New from URL', + icon: material.Icons.link_rounded, onPressed: widget.onNewConnectionFromUrl, - leading: const material.Icon( - material.Icons.link_rounded, - size: 18, - ), - child: const Text('New from URL'), + size: QueryaControlSize.md, ), if (widget.onOpenSqlite != null) - OutlineButton( + QueryaButton.secondary( key: const Key('empty_open_sqlite'), + label: 'Open SQLite file', + icon: material.Icons.folder_open_rounded, onPressed: widget.onOpenSqlite, - leading: const material.Icon( - material.Icons.folder_open_rounded, - size: 18, - ), - child: const Text('Open SQLite file'), + size: QueryaControlSize.md, ), ], ), diff --git a/lib/features/mysql/mysql_table_view.dart b/lib/features/mysql/mysql_table_view.dart index 740a2662..be64b60d 100644 --- a/lib/features/mysql/mysql_table_view.dart +++ b/lib/features/mysql/mysql_table_view.dart @@ -14,6 +14,7 @@ import 'package:querya_desktop/features/mysql/mysql_sql_editor_dialog.dart'; import 'package:querya_desktop/features/mysql/mysql_table_utils.dart'; import 'package:querya_desktop/features/workspace/workspace.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; const _defaultLimit = 200; @@ -357,14 +358,11 @@ class _MysqlTableViewState extends material.State { if (widget.onNavigateHome != null) ...[ material.Tooltip( message: 'Return to server overview', - child: OutlineButton( - size: ButtonSize.small, + child: QueryaButton.secondary( + label: 'Server', + icon: material.Icons.dns_outlined, onPressed: () => unawaited(state.navigateHome()), - leading: const material.Icon( - material.Icons.dns_outlined, - size: 14, - ), - child: const Text('Server'), + size: QueryaControlSize.sm, ), ), const Gap(10), @@ -427,62 +425,47 @@ class _MysqlTableViewState extends material.State { state.buildEditModeButton(), const Gap(4), ], - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'SQL', + icon: material.Icons.code_rounded, onPressed: () => _openSqlEditor(ctx, state), - leading: const material.Icon( - material.Icons.code_rounded, - size: 15, - ), - child: const Text('SQL'), + size: QueryaControlSize.sm, ), if (state.customSqlActive) ...[ const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Browse', + icon: material.Icons.table_chart_rounded, onPressed: () => unawaited(state.exitCustomMode()), - leading: const material.Icon( - material.Icons.table_chart_rounded, - size: 15, - ), - child: const Text('Browse'), + size: QueryaControlSize.sm, ), ], const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Prev', + icon: material.Icons.chevron_left_rounded, onPressed: (!state.canGoPrevious || state.isLoading) ? null : state.goToPreviousPage, - leading: const material.Icon( - material.Icons.chevron_left_rounded, - size: 16, - ), - child: const Text('Prev'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Next', + icon: material.Icons.chevron_right_rounded, onPressed: (!state.canGoNext || state.isLoading) ? null : state.goToNextPage, - leading: const material.Icon( - material.Icons.chevron_right_rounded, - size: 16, - ), - child: const Text('Next'), + size: QueryaControlSize.sm, ), const Gap(8), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Refresh', + icon: material.Icons.refresh_rounded, onPressed: state.isLoading ? null : () => unawaited(state.refresh()), - leading: const material.Icon( - material.Icons.refresh_rounded, - size: 14, - ), - child: const Text('Refresh'), + size: QueryaControlSize.sm, ), ], ), diff --git a/lib/features/postgresql/postgres_table_toolbar.dart b/lib/features/postgresql/postgres_table_toolbar.dart index 1b495143..f9cf90fb 100644 --- a/lib/features/postgresql/postgres_table_toolbar.dart +++ b/lib/features/postgresql/postgres_table_toolbar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart' as material; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Toolbar for [PostgresTableView]: title, pagination chip, actions (scrollable when narrow). class PostgresTableToolbar extends material.StatelessWidget { @@ -62,14 +63,11 @@ class PostgresTableToolbar extends material.StatelessWidget { if (onNavigateHome != null) ...[ material.Tooltip( message: 'Return to server overview', - child: OutlineButton( - size: ButtonSize.small, + child: QueryaButton.secondary( + label: 'Server', + icon: material.Icons.dns_outlined, onPressed: onNavigateHome, - leading: const material.Icon( - material.Icons.dns_outlined, - size: 14, - ), - child: const Text('Server'), + size: QueryaControlSize.sm, ), ), const Gap(10), @@ -124,79 +122,57 @@ class PostgresTableToolbar extends material.StatelessWidget { editAction!, const Gap(4), ], - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'SQL', + icon: material.Icons.code_rounded, onPressed: onOpenSql, - leading: const material.Icon( - material.Icons.code_rounded, - size: 16, - ), - child: const Text('SQL'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Privileges', + icon: material.Icons.admin_panel_settings_rounded, onPressed: onOpenPrivileges, - leading: const material.Icon( - material.Icons.admin_panel_settings_rounded, - size: 15, - ), - child: const Text('Privileges'), + size: QueryaControlSize.sm, ), if (isMaterializedView && !customSqlActive) ...[ const Gap(4), - OutlineButton( - size: ButtonSize.small, - onPressed: - loading ? null : onRefreshMaterializedView, - leading: const material.Icon( - material.Icons.sync_rounded, - size: 15, - ), - child: const Text('Refresh MV'), + QueryaButton.secondary( + label: 'Refresh MV', + icon: material.Icons.sync_rounded, + onPressed: loading ? null : onRefreshMaterializedView, + size: QueryaControlSize.sm, ), ], if (customSqlActive) ...[ const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Table', + icon: material.Icons.table_chart_rounded, onPressed: loading ? null : onExitCustomMode, - leading: const material.Icon( - material.Icons.table_chart_rounded, - size: 16, - ), - child: const Text('Table'), + size: QueryaControlSize.sm, ), ], const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Prev', + icon: material.Icons.chevron_left_rounded, onPressed: canGoPrevious ? onGoPrevious : null, - leading: const material.Icon( - material.Icons.chevron_left_rounded, - size: 16, - ), - child: const Text('Prev'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Next', + icon: material.Icons.chevron_right_rounded, onPressed: canGoNext ? onGoNext : null, - leading: const material.Icon( - material.Icons.chevron_right_rounded, - size: 16, - ), - child: const Text('Next'), + size: QueryaControlSize.sm, ), const Gap(8), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Refresh', + icon: material.Icons.refresh_rounded, onPressed: loading ? null : onRefresh, - leading: const material.Icon( - material.Icons.refresh_rounded, - size: 14, - ), - child: const Text('Refresh'), + size: QueryaControlSize.sm, ), ], ), diff --git a/lib/features/settings/preferences_about_section.dart b/lib/features/settings/preferences_about_section.dart index c4f73e86..f9dc5024 100644 --- a/lib/features/settings/preferences_about_section.dart +++ b/lib/features/settings/preferences_about_section.dart @@ -6,6 +6,7 @@ import 'package:querya_desktop/core/theme/theme_paths.dart'; import 'package:querya_desktop/core/ui/querya_icons.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; import 'package:url_launcher/url_launcher_string.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; class PreferencesAboutSection extends StatelessWidget { const PreferencesAboutSection({super.key}); @@ -131,47 +132,32 @@ class PreferencesAboutSection extends StatelessWidget { spacing: 8, runSpacing: 8, children: [ - OutlineButton( + QueryaButton.secondary( + label: 'Open App Data Folder', + icon: material.Icons.folder_outlined, onPressed: () async { final dir = await AppDataRoot.applicationSupportDirectory(); await openDirectoryInFileManager(dir.path); }, - child: const material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - material.Icon(material.Icons.folder_outlined, size: 14), - material.SizedBox(width: 6), - material.Text('Open App Data Folder'), - ], - ), + size: QueryaControlSize.md, ), - OutlineButton( + QueryaButton.secondary( + label: 'Open Themes Folder', + icon: material.Icons.palette_outlined, onPressed: () async { final dir = await ThemePaths.ensureUserThemesDirectory(); await openDirectoryInFileManager(dir.path); }, - child: const material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - material.Icon(material.Icons.palette_outlined, size: 14), - material.SizedBox(width: 6), - material.Text('Open Themes Folder'), - ], - ), + size: QueryaControlSize.md, ), - OutlineButton( + QueryaButton.secondary( + label: 'Open Extensions Folder', + icon: material.Icons.extension_outlined, onPressed: () async { final dir = await ExtensionPaths.ensureExtensionsDirectory(); await openDirectoryInFileManager(dir.path); }, - child: const material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - material.Icon(material.Icons.extension_outlined, size: 14), - material.SizedBox(width: 6), - material.Text('Open Extensions Folder'), - ], - ), + size: QueryaControlSize.md, ), ], ), @@ -184,33 +170,23 @@ class PreferencesAboutSection extends StatelessWidget { spacing: 8, runSpacing: 8, children: [ - OutlineButton( + QueryaButton.secondary( + label: 'GitHub Repository', + icon: material.Icons.code_rounded, onPressed: () => launchUrlString( githubRepoUrl, mode: LaunchMode.externalApplication, ), - child: const material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - material.Icon(material.Icons.code_rounded, size: 14), - material.SizedBox(width: 6), - material.Text('GitHub Repository'), - ], - ), + size: QueryaControlSize.md, ), - OutlineButton( + QueryaButton.secondary( + label: 'Report an Issue', + icon: material.Icons.bug_report_outlined, onPressed: () => launchUrlString( '$githubRepoUrl/issues', mode: LaunchMode.externalApplication, ), - child: const material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - material.Icon(material.Icons.bug_report_outlined, size: 14), - material.SizedBox(width: 6), - material.Text('Report an Issue'), - ], - ), + size: QueryaControlSize.md, ), ], ), diff --git a/lib/features/settings/preferences_appearance_section.dart b/lib/features/settings/preferences_appearance_section.dart index 8c6aed62..f4794773 100644 --- a/lib/features/settings/preferences_appearance_section.dart +++ b/lib/features/settings/preferences_appearance_section.dart @@ -16,6 +16,7 @@ import 'package:querya_desktop/features/settings/theme_picker_button.dart'; import 'package:querya_desktop/features/settings/theme_preview_card.dart'; import 'package:querya_desktop/features/settings/theme_remote_install_dialog.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Appearance / theme controls for [PreferencesDialog]. class PreferencesAppearanceSection extends material.StatefulWidget { @@ -293,94 +294,45 @@ class _PreferencesAppearanceSectionState spacing: 8, runSpacing: 8, children: [ - OutlineButton( + QueryaButton.secondary( + label: _importing ? 'Importing…' : 'Import theme…', + icon: material.Icons.file_open_outlined, onPressed: (_importing || _installingFromUrl) ? null : () => unawaited(_pickAndImportTheme()), - child: material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - const material.Icon( - material.Icons.file_open_outlined, - size: 14, - ), - const material.SizedBox(width: 6), - material.Text( - _importing ? 'Importing…' : 'Import theme…', - ), - ], - ), + size: QueryaControlSize.md, ), - OutlineButton( + QueryaButton.secondary( + label: _installingFromUrl ? 'Installing…' : 'Install from URL…', + icon: material.Icons.download_rounded, onPressed: (_importing || _installingFromUrl || refreshingThemes) ? null : () => unawaited(_installThemeFromUrl()), - child: material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - const material.Icon( - material.Icons.download_rounded, - size: 14, - ), - const material.SizedBox(width: 6), - material.Text( - _installingFromUrl ? 'Installing…' : 'Install from URL…', - ), - ], - ), + size: QueryaControlSize.md, ), - OutlineButton( + QueryaButton.secondary( + label: refreshingThemes ? 'Refreshing…' : 'Refresh themes', + icon: material.Icons.refresh_rounded, onPressed: (_importing || _installingFromUrl || refreshingThemes) ? null : () => unawaited(_refreshThemes()), - child: material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - const material.Icon( - material.Icons.refresh_rounded, - size: 14, - ), - const material.SizedBox(width: 6), - material.Text( - refreshingThemes ? 'Refreshing…' : 'Refresh themes', - ), - ], - ), + size: QueryaControlSize.md, ), - OutlineButton( - onPressed: - (_importing || _installingFromUrl || _openingThemesFolder) - ? null - : () => unawaited(_openThemesFolder()), - child: material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - const material.Icon( - material.Icons.folder_open_outlined, - size: 14, - ), - const material.SizedBox(width: 6), - material.Text( - _openingThemesFolder + QueryaButton.secondary( + label: _openingThemesFolder ? 'Opening…' : 'Open themes folder', - ), - ], - ), + icon: material.Icons.folder_open_outlined, + onPressed: (_importing || _installingFromUrl || _openingThemesFolder) + ? null + : () => unawaited(_openThemesFolder()), + size: QueryaControlSize.md, ), - OutlineButton( + QueryaButton.secondary( + label: 'Reset appearance', + icon: material.Icons.restart_alt_rounded, onPressed: () => unawaited(_resetAppearance()), - child: const material.Row( - mainAxisSize: material.MainAxisSize.min, - children: [ - material.Icon( - material.Icons.restart_alt_rounded, - size: 14, - ), - material.SizedBox(width: 6), - Text('Reset appearance'), - ], - ), + size: QueryaControlSize.md, ), ], ), diff --git a/lib/features/settings/preferences_extensions_section.dart b/lib/features/settings/preferences_extensions_section.dart index e303d2c1..90950e5a 100644 --- a/lib/features/settings/preferences_extensions_section.dart +++ b/lib/features/settings/preferences_extensions_section.dart @@ -8,6 +8,7 @@ import 'package:querya_desktop/core/market/marketplace_repository.dart'; import 'package:querya_desktop/core/platform/open_directory.dart'; import 'package:querya_desktop/features/settings/preferences_controls.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Preferences section for sideloading extensions from local archives (#316). class PreferencesExtensionsSection extends material.StatefulWidget { @@ -109,15 +110,15 @@ class PreferencesExtensionsSectionState spacing: 8, runSpacing: 8, children: [ - OutlineButton( + QueryaButton.secondary( + label: _installing ? 'Installing…' : 'Install from file…', onPressed: busy ? null : () => unawaited(_installFromFile()), - child: Text(_installing ? 'Installing…' : 'Install from file…'), + size: QueryaControlSize.md, ), - OutlineButton( + QueryaButton.secondary( + label: _openingFolder ? 'Opening…' : 'Open extensions folder', onPressed: busy ? null : () => unawaited(_openExtensionsFolder()), - child: Text( - _openingFolder ? 'Opening…' : 'Open extensions folder', - ), + size: QueryaControlSize.md, ), ], ), diff --git a/lib/features/sqlite/sqlite_table_view.dart b/lib/features/sqlite/sqlite_table_view.dart index a103fa1b..debbae35 100644 --- a/lib/features/sqlite/sqlite_table_view.dart +++ b/lib/features/sqlite/sqlite_table_view.dart @@ -12,6 +12,7 @@ import 'package:querya_desktop/features/sqlite/sqlite_result_utils.dart'; import 'package:querya_desktop/features/sqlite/sqlite_table_utils.dart'; import 'package:querya_desktop/features/workspace/workspace.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; const _defaultLimit = 200; @@ -342,9 +343,10 @@ class _SqliteTableViewState extends material.State { const Gap(16), material.Align( alignment: material.Alignment.centerRight, - child: OutlineButton( + child: QueryaButton.ghost( + label: 'Close', onPressed: () => material.Navigator.of(ctx).pop(), - child: const Text('Close'), + size: QueryaControlSize.md, ), ), ], @@ -396,14 +398,11 @@ class _SqliteTableViewState extends material.State { if (widget.onNavigateHome != null) ...[ material.Tooltip( message: 'Return to overview', - child: OutlineButton( - size: ButtonSize.small, + child: QueryaButton.secondary( + label: 'Overview', + icon: material.Icons.dns_outlined, onPressed: () => unawaited(state.navigateHome()), - leading: const material.Icon( - material.Icons.dns_outlined, - size: 14, - ), - child: const Text('Overview'), + size: QueryaControlSize.sm, ), ), const Gap(10), @@ -466,48 +465,39 @@ class _SqliteTableViewState extends material.State { state.buildEditModeButton(), const Gap(4), ], - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'DDL', onPressed: state.isLoading ? null : () => unawaited(_showDdlDialog()), - child: const Text('DDL'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Refresh', + icon: material.Icons.refresh_rounded, onPressed: state.isLoading ? null : () => unawaited(state.refresh()), - leading: const material.Icon( - material.Icons.refresh_rounded, - size: 14, - ), - child: const Text('Refresh'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Prev', + icon: material.Icons.chevron_left_rounded, onPressed: (!state.canGoPrevious || state.isLoading) ? null : state.goToPreviousPage, - leading: const material.Icon( - material.Icons.chevron_left_rounded, - size: 16, - ), - child: const Text('Prev'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Next', + icon: material.Icons.chevron_right_rounded, onPressed: (!state.canGoNext || state.isLoading) ? null : state.goToNextPage, - leading: const material.Icon( - material.Icons.chevron_right_rounded, - size: 16, - ), - child: const Text('Next'), + size: QueryaControlSize.sm, ), ], ), diff --git a/lib/features/workspace/data_grid_staging_toolbar.dart b/lib/features/workspace/data_grid_staging_toolbar.dart index 31eb9b06..f9c0357d 100644 --- a/lib/features/workspace/data_grid_staging_toolbar.dart +++ b/lib/features/workspace/data_grid_staging_toolbar.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart' as material; import 'package:querya_desktop/features/workspace/data_grid_staging_buffer.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Toolbar for managing staged data changes (Add, Delete, Revert, Save). class DataGridStagingToolbar extends StatelessWidget { @@ -102,28 +103,20 @@ class DataGridStagingToolbar extends StatelessWidget { message: 'Discard all pending edits (Ctrl+Z / Cmd+Z)', waitDuration: const Duration(milliseconds: 400), child: compact - ? OutlineButton( - size: ButtonSize.small, - density: ButtonDensity.icon, - onPressed: isSaving + ? QueryaButton.icon( + icon: material.Icons.undo_rounded, + tooltip: 'Revert All', + size: QueryaControlSize.sm, + onPressed: isSaving ? null : () => stagingBuffer.revertAll(), + ) + : QueryaButton.secondary( + label: 'Revert All', + icon: material.Icons.undo_rounded, + onPressed: isSaving ? null : () => stagingBuffer.revertAll(), - child: const material.Icon( - material.Icons.undo_rounded, - size: 14, - ), - ) - : OutlineButton( - size: ButtonSize.small, - onPressed: isSaving - ? null - : () => stagingBuffer.revertAll(), - leading: const material.Icon( - material.Icons.undo_rounded, - size: 14, - ), - child: const Text('Revert All'), - ), + size: QueryaControlSize.sm, + ), ), ] else material.Row( @@ -144,23 +137,12 @@ class DataGridStagingToolbar extends StatelessWidget { ? 'Review and save pending changes (Ctrl+S / Cmd+S)' : 'No pending changes to save', waitDuration: const Duration(milliseconds: 400), - child: PrimaryButton( - size: ButtonSize.small, + child: QueryaButton.primary( + label: isSaving ? 'Saving…' : (compact ? 'Save' : 'Save Changes'), + icon: material.Icons.save_rounded, + loading: isSaving, onPressed: isDirty && !isSaving ? onApplyChanges : null, - leading: isSaving - ? QueryaSpinner( - size: QueryaSpinnerSize.sm, - color: cs.primaryForeground, - ) - : const material.Icon( - material.Icons.save_rounded, - size: 14, - ), - child: Text( - isSaving - ? 'Saving…' - : (compact ? 'Save' : 'Save Changes'), - ), + size: QueryaControlSize.sm, ), ), ], diff --git a/lib/features/workspace/generic_table_view.dart b/lib/features/workspace/generic_table_view.dart index d8dc92dc..87d9e420 100644 --- a/lib/features/workspace/generic_table_view.dart +++ b/lib/features/workspace/generic_table_view.dart @@ -16,6 +16,7 @@ import 'package:querya_desktop/features/workspace/results_tab.dart'; import 'package:querya_desktop/features/workspace/table_data_delegate.dart'; import 'package:querya_desktop/features/workspace/table_view_staging.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Reusable database table browser supporting: /// - Pagination (LIMIT/OFFSET) and row counts @@ -695,14 +696,11 @@ class GenericTableViewState extends material.State { if (widget.onNavigateHome != null) ...[ material.Tooltip( message: 'Return to overview', - child: OutlineButton( - size: ButtonSize.small, + child: QueryaButton.secondary( + label: 'Overview', + icon: material.Icons.dns_outlined, onPressed: () => unawaited(navigateHome()), - leading: const material.Icon( - material.Icons.dns_outlined, - size: 14, - ), - child: const Text('Overview'), + size: QueryaControlSize.sm, ), ), const Gap(10), @@ -767,45 +765,33 @@ class GenericTableViewState extends material.State { const Gap(4), ], if (_customSqlActive) ...[ - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Browse', + icon: material.Icons.table_chart_rounded, onPressed: _loading ? null : () => unawaited(exitCustomMode()), - leading: const material.Icon( - material.Icons.table_chart_rounded, - size: 15, - ), - child: const Text('Browse'), + size: QueryaControlSize.sm, ), const Gap(4), ], - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Prev', + icon: material.Icons.chevron_left_rounded, onPressed: (!canGoPrevious || _loading) ? null : goToPreviousPage, - leading: const material.Icon( - material.Icons.chevron_left_rounded, - size: 16, - ), - child: const Text('Prev'), + size: QueryaControlSize.sm, ), const Gap(4), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Next', + icon: material.Icons.chevron_right_rounded, onPressed: (!canGoNext || _loading) ? null : goToNextPage, - leading: const material.Icon( - material.Icons.chevron_right_rounded, - size: 16, - ), - child: const Text('Next'), + size: QueryaControlSize.sm, ), const Gap(8), - OutlineButton( - size: ButtonSize.small, + QueryaButton.secondary( + label: 'Refresh', + icon: material.Icons.refresh_rounded, onPressed: _loading ? null : () => unawaited(refresh()), - leading: const material.Icon( - material.Icons.refresh_rounded, - size: 14, - ), - child: const Text('Refresh'), + size: QueryaControlSize.sm, ), ], ), diff --git a/lib/features/workspace/grid_cell_popover_inspector.dart b/lib/features/workspace/grid_cell_popover_inspector.dart index e2734279..edcf34b7 100644 --- a/lib/features/workspace/grid_cell_popover_inspector.dart +++ b/lib/features/workspace/grid_cell_popover_inspector.dart @@ -6,6 +6,7 @@ import 'package:querya_desktop/core/editor/querya_code_editor.dart'; import 'package:querya_desktop/core/editor/querya_code_language.dart'; import 'package:querya_desktop/features/workspace/xml_html_formatter.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Opens a rich modal inspector for viewing and editing large text, JSON, XML, or BLOB values. /// @@ -295,36 +296,27 @@ class _GridCellInspectorDialogState ).semiBold(), ), if (_isJson()) ...[ - GhostButton( - density: ButtonDensity.compact, + QueryaButton.ghost( + label: 'Format JSON', + icon: material.Icons.format_align_left_rounded, onPressed: _formatJson, - leading: const material.Icon( - material.Icons.format_align_left_rounded, - size: 14, - ), - child: const Text('Format JSON'), + size: QueryaControlSize.sm, ), const Gap(6), - GhostButton( - density: ButtonDensity.compact, + QueryaButton.ghost( + label: 'Minify', + icon: material.Icons.compress_rounded, onPressed: _minifyJson, - leading: const material.Icon( - material.Icons.compress_rounded, - size: 14, - ), - child: const Text('Minify'), + size: QueryaControlSize.sm, ), const Gap(6), ], if (_isXml()) ...[ - GhostButton( - density: ButtonDensity.compact, + QueryaButton.ghost( + label: 'Format XML', + icon: material.Icons.code_rounded, onPressed: _formatXml, - leading: const material.Icon( - material.Icons.code_rounded, - size: 14, - ), - child: const Text('Format XML'), + size: QueryaControlSize.sm, ), const Gap(6), ], @@ -337,33 +329,27 @@ class _GridCellInspectorDialogState ), ), if (_isHex()) ...[ - GhostButton( - density: ButtonDensity.compact, + QueryaButton.ghost( + label: 'Format Hex', + icon: material.Icons.grid_view_rounded, onPressed: _formatHex, - leading: const material.Icon( - material.Icons.grid_view_rounded, - size: 14, - ), - child: const Text('Format Hex'), + size: QueryaControlSize.sm, ), const Gap(6), ], - GhostButton( - density: ButtonDensity.compact, - onPressed: () => setState(() => _wordWrap = !_wordWrap), - leading: material.Icon( - _wordWrap + QueryaButton.ghost( + label: _wordWrap ? 'Wrap' : 'No Wrap', + icon: _wordWrap ? material.Icons.wrap_text_rounded : material.Icons.notes_rounded, - size: 14, - ), - child: Text(_wordWrap ? 'Wrap' : 'No Wrap'), + onPressed: () => setState(() => _wordWrap = !_wordWrap), + size: QueryaControlSize.sm, ), const Gap(6), - GhostButton( - density: ButtonDensity.compact, + QueryaButton.ghost( + label: 'Set NULL', onPressed: _isNull ? null : _setNull, - child: const Text('Set NULL'), + size: QueryaControlSize.sm, ), ], ), @@ -389,11 +375,11 @@ class _GridCellInspectorDialogState children: [ const Text('Value is NULL').muted().semiBold(), const Gap(8), - GhostButton( - density: ButtonDensity.compact, + QueryaButton.ghost( + label: 'Enter text value', onPressed: () => setState(() => _isNull = false), - child: const Text('Enter text value'), + size: QueryaControlSize.sm, ), ], ), @@ -438,8 +424,9 @@ class _GridCellInspectorDialogState children: [ material.Row( children: [ - GhostButton( - density: ButtonDensity.compact, + QueryaButton.ghost( + label: 'Copy', + icon: material.Icons.copy_rounded, onPressed: _savingToDb ? null : () { @@ -449,11 +436,7 @@ class _GridCellInspectorDialogState _isNull ? 'NULL' : _controller.text), ); }, - leading: const material.Icon( - material.Icons.copy_rounded, - size: 14, - ), - child: const Text('Copy'), + size: QueryaControlSize.sm, ), const Gap(12), if (!_isNull) @@ -475,38 +458,31 @@ class _GridCellInspectorDialogState spacing: 8, runSpacing: 8, children: [ - OutlineButton( - density: ButtonDensity.compact, + QueryaButton.ghost( + label: 'Cancel', onPressed: _savingToDb ? null : () => material.Navigator.of(context).pop(null), - child: const Text('Cancel'), + size: QueryaControlSize.sm, ), if (widget.onSaveToDatabase != null) ...[ - PrimaryButton( - density: ButtonDensity.compact, + QueryaButton.primary( + label: _savingToDb ? 'Saving…' : 'Save to DB', + icon: material.Icons.save_rounded, + loading: _savingToDb, onPressed: _savingToDb ? null : _saveToDatabase, - leading: _savingToDb - ? QueryaSpinner( - size: QueryaSpinnerSize.sm, - color: cs.primaryForeground, - ) - : const material.Icon( - material.Icons.save_rounded, - size: 14, - ), - child: Text(_savingToDb ? 'Saving…' : 'Save to DB'), + size: QueryaControlSize.sm, ), - OutlineButton( - density: ButtonDensity.compact, + QueryaButton.secondary( + label: 'Apply', onPressed: _savingToDb ? null : _apply, - child: const Text('Apply'), + size: QueryaControlSize.sm, ), ] else - PrimaryButton( - density: ButtonDensity.compact, + QueryaButton.primary( + label: 'Apply', onPressed: _apply, - child: const Text('Apply'), + size: QueryaControlSize.sm, ), ], ), diff --git a/lib/features/workspace/table_view_staging.dart b/lib/features/workspace/table_view_staging.dart index 9c1b4566..a3b4068a 100644 --- a/lib/features/workspace/table_view_staging.dart +++ b/lib/features/workspace/table_view_staging.dart @@ -6,6 +6,7 @@ import 'package:querya_desktop/features/workspace/data_grid_staging_buffer.dart' import 'package:querya_desktop/features/workspace/dml_preview_dialog.dart'; import 'package:querya_desktop/features/workspace/save_error_description.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; +import 'package:querya_desktop/core/ui/querya_control_tokens.dart'; /// Outcome of [loadTableViewSchema] (success vs swallowed getTableSchema error). class TableViewSchemaLoad { @@ -151,13 +152,15 @@ Future showDiscardTableEditsDialog({ spacing: 8, runSpacing: 8, children: [ - OutlineButton( + QueryaButton.ghost( + label: 'Cancel', onPressed: () => material.Navigator.of(ctx).pop(false), - child: const Text('Cancel'), + size: QueryaControlSize.md, ), - DestructiveButton( + QueryaButton.destructive( + label: 'Discard', onPressed: () => material.Navigator.of(ctx).pop(true), - child: const Text('Discard'), + size: QueryaControlSize.md, ), ], ), @@ -221,11 +224,11 @@ class TableEditModeButton extends material.StatelessWidget { return material.Tooltip( message: 'Stop editing (Ctrl+E)', waitDuration: const Duration(milliseconds: 400), - child: PrimaryButton( - size: ButtonSize.small, + child: QueryaButton.primary( + label: 'Done', + icon: material.Icons.check_rounded, onPressed: busy ? null : onDone, - leading: const material.Icon(material.Icons.check_rounded, size: 15), - child: const Text('Done'), + size: QueryaControlSize.sm, ), ); } @@ -235,11 +238,11 @@ class TableEditModeButton extends material.StatelessWidget { ? 'Edit rows (Ctrl+E)' : (disabledReason ?? 'This table cannot be edited'), waitDuration: const Duration(milliseconds: 400), - child: OutlineButton( - size: ButtonSize.small, + child: QueryaButton.secondary( + label: 'Edit', + icon: material.Icons.edit_rounded, onPressed: enabled ? onEdit : null, - leading: const material.Icon(material.Icons.edit_rounded, size: 14), - child: const Text('Edit'), + size: QueryaControlSize.sm, ), ); } @@ -360,20 +363,19 @@ class _SaveFailedDialogState extends material.State<_SaveFailedDialog> { material.Row( mainAxisAlignment: material.MainAxisAlignment.end, children: [ - OutlineButton( - onPressed: _copy, - leading: material.Icon( - _copied + QueryaButton.secondary( + label: _copied ? 'Copied' : 'Copy details', + icon: _copied ? material.Icons.check_rounded : material.Icons.copy_rounded, - size: 14, - ), - child: Text(_copied ? 'Copied' : 'Copy details'), + onPressed: _copy, + size: QueryaControlSize.md, ), const Gap(8), - PrimaryButton( + QueryaButton.primary( + label: 'OK', onPressed: () => material.Navigator.of(context).pop(), - child: const Text('OK'), + size: QueryaControlSize.md, ), ], ), diff --git a/test/features/workspace/sql_workspace_readonly_save_test.dart b/test/features/workspace/sql_workspace_readonly_save_test.dart index e7e3b366..7563ebc4 100644 --- a/test/features/workspace/sql_workspace_readonly_save_test.dart +++ b/test/features/workspace/sql_workspace_readonly_save_test.dart @@ -6,7 +6,7 @@ import 'package:path_provider_platform_interface/path_provider_platform_interfac import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/features/sqlite/sqlite_sql_workspace.dart'; import 'package:querya_desktop/features/workspace/data_grid_staging_buffer.dart'; -import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:querya_desktop/shared/widgets/querya_button.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import '../../support/querya_theme_test_shell.dart'; @@ -119,10 +119,10 @@ void main() { dynamicState.debugRebuildActivePane(); await tester.pumpAndSettle(); - final saveButtonFinder = find.widgetWithText(PrimaryButton, 'Save Changes'); + final saveButtonFinder = find.widgetWithText(QueryaButton, 'Save Changes'); expect(saveButtonFinder, findsOneWidget); expect( - tester.widget(saveButtonFinder).onPressed, + tester.widget(saveButtonFinder).onPressed, isNotNull, reason: 'Save should be enabled while writable and dirty', ); @@ -133,10 +133,10 @@ void main() { await tester.pumpWidget(buildWorkspace(isReadOnly: true)); await tester.pumpAndSettle(); - final saveButtonAfter = find.widgetWithText(PrimaryButton, 'Save Changes'); + final saveButtonAfter = find.widgetWithText(QueryaButton, 'Save Changes'); expect(saveButtonAfter, findsOneWidget); expect( - tester.widget(saveButtonAfter).onPressed, + tester.widget(saveButtonAfter).onPressed, isNull, reason: 'Save must disable once the connection is read-only, even though '