Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions lib/core/theme/querya_color_scheme.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,76 @@
import 'package:shadcn_flutter/shadcn_flutter.dart' hide Gap, TextExtension;

import 'querya_theme.dart';
import 'querya_ui_colors.dart';

/// Shadcn [ColorScheme] presets — derived from [QueryaTheme] defaults.
abstract class QueryaColorScheme {
static ColorScheme get dark => QueryaTheme.darkDefault.colorScheme;
static ColorScheme get light => QueryaTheme.lightDefault.colorScheme;
}

final Expando<QueryaUiColors> _uiColorsCache = Expando();

/// The bridge between shadcn's scheme and [QueryaUiColors] while the app still
/// runs on shadcn's shell (#1730, epic #1465); it goes with #1735.
extension QueryaUiColorsFromShadcn on ColorScheme {
/// The same colours as [QueryaUiColors] (cached per scheme instance).
QueryaUiColors get uiColors => _uiColorsCache[this] ??= QueryaUiColors(
brightness: brightness,
background: background,
foreground: foreground,
card: card,
popover: popover,
popoverForeground: popoverForeground,
primary: primary,
primaryForeground: primaryForeground,
secondary: secondary,
secondaryForeground: secondaryForeground,
muted: muted,
mutedForeground: mutedForeground,
accent: accent,
destructive: destructive,
destructiveForeground: destructiveForeground,
border: border,
ring: ring,
chart1: chart1,
chart2: chart2,
chart3: chart3,
chart4: chart4,
chart5: chart5,
);
}

extension QueryaUiColorsToShadcn on QueryaUiColors {
/// A shadcn scheme for the widgets still on shadcn. The fields shadcn has
/// and the app does not read follow the ones next to them, as every Querya
/// theme sets them today: card and accent text are [foreground], the input
/// outline is [border].
ColorScheme toShadcn() => ColorScheme(
brightness: brightness,
background: background,
foreground: foreground,
card: card,
cardForeground: foreground,
popover: popover,
popoverForeground: popoverForeground,
primary: primary,
primaryForeground: primaryForeground,
secondary: secondary,
secondaryForeground: secondaryForeground,
muted: muted,
mutedForeground: mutedForeground,
accent: accent,
accentForeground: foreground,
destructive: destructive,
destructiveForeground: destructiveForeground,
border: border,
input: border,
ring: ring,
chart1: chart1,
chart2: chart2,
chart3: chart3,
chart4: chart4,
chart5: chart5,
);
}
9 changes: 5 additions & 4 deletions lib/core/theme/querya_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import 'package:shadcn_flutter/shadcn_flutter.dart' hide Gap, TextExtension;

import 'color_contrast.dart';
import 'parser/vscode_theme_manifest.dart';
import 'querya_color_scheme.dart';
import 'querya_colors.dart';
import 'querya_ui_colors.dart';
import 'querya_editor_theme.dart';
import 'querya_workbench_theme.dart';

/// The type of [QueryaTheme.colorScheme], nameable by widgets that must not
/// import the UI library behind it.
typedef QueryaThemeColorScheme = ColorScheme;

/// Full Querya theme: workbench chrome + editor tokens + shadcn [ColorScheme].
class QueryaTheme {
const QueryaTheme({
Expand All @@ -25,6 +23,9 @@ class QueryaTheme {
final Brightness brightness;
final ColorScheme colorScheme;

/// [colorScheme] as the app's own colour type (#1730).
QueryaUiColors get colors => colorScheme.uiColors;

/// VS Code `tokenColors` for syntax highlighting (imported themes).
final List<TokenColorRule> tokenColors;

Expand Down
23 changes: 18 additions & 5 deletions lib/core/theme/querya_theme_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import 'package:flutter/widgets.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart' as shadcn
hide TextExtension;

import 'querya_color_scheme.dart';
import 'querya_editor_theme.dart';
import 'querya_semantic_palette.dart';
import 'querya_theme.dart';
import 'querya_ui_colors.dart';
import 'querya_workbench_theme.dart';

/// Provides [QueryaTheme] (workbench + editor tokens) below [ShadcnApp].
Expand Down Expand Up @@ -54,15 +56,26 @@ extension QueryaThemeContext on BuildContext {
/// Shortcut for Material [material.Theme.of].
material.ThemeData get theme => material.Theme.of(this);

/// Shortcut for [shadcn.ColorScheme] from [shadcn.Theme.of].
shadcn.ColorScheme get colors => shadcn.Theme.of(this).colorScheme;
/// The colours widgets read (#1730): the ones the running shell uses, so a
/// caller that moves here from `Theme.of(context).colorScheme` sees the same
/// values, mid theme animation included. Without a shell theme (a bare app
/// in a test) the nearest [QueryaThemeScope]'s, else the default theme's.
QueryaUiColors get colors {
final shell =
dependOnInheritedWidgetOfExactType<shadcn.Theme>()?.data.colorScheme;
if (shell != null) return shell.uiColors;
return (QueryaThemeScope.maybeOf(this) ??
(_ambientBrightness == Brightness.light
? QueryaTheme.lightDefault
: QueryaTheme.darkDefault))
.colors;
}

QueryaTheme get queryaTheme => QueryaThemeScope.of(this);

/// The brightness the defaults below follow when there is no
/// [QueryaThemeScope]: the shadcn theme's, else Material's. [colors] asserts
/// without a shadcn theme, and these defaults exist for exactly that case
/// (a widget under a bare app, as in tests).
/// [QueryaThemeScope]: the shadcn theme's, else Material's (a widget under a
/// bare app, as in tests).
Brightness get _ambientBrightness =>
dependOnInheritedWidgetOfExactType<shadcn.Theme>()
?.data
Expand Down
131 changes: 131 additions & 0 deletions lib/core/theme/querya_ui_colors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import 'dart:ui';

import 'package:flutter/foundation.dart';

/// The colours the app's widgets read: surfaces, text, accent, borders and
/// the chart series (#1730, epic #1465).
///
/// Holds exactly the fields `lib/` uses of shadcn's `ColorScheme`, under the
/// same names, so a caller moves from `Theme.of(context).colorScheme` to
/// `context.colors` without other changes. While the app still runs on
/// shadcn's shell the values are read from the shadcn scheme and converted in
/// `querya_color_scheme.dart`, the one place that knows both; after #1735 this
/// is the source.
@immutable
class QueryaUiColors {
const QueryaUiColors({
required this.brightness,
required this.background,
required this.foreground,
required this.card,
required this.popover,
required this.popoverForeground,
required this.primary,
required this.primaryForeground,
required this.secondary,
required this.secondaryForeground,
required this.muted,
required this.mutedForeground,
required this.accent,
required this.destructive,
required this.destructiveForeground,
required this.border,
required this.ring,
required this.chart1,
required this.chart2,
required this.chart3,
required this.chart4,
required this.chart5,
});

final Brightness brightness;

/// Page background.
final Color background;

/// Main text and icons.
final Color foreground;

/// Raised surfaces: cards, panels.
final Color card;

/// Menus, dropdowns and other floating surfaces, and their text.
final Color popover;
final Color popoverForeground;

/// The accent: filled buttons, selection, links; and what sits on it.
final Color primary;
final Color primaryForeground;

/// Quiet filled surfaces (secondary buttons) and their text.
final Color secondary;
final Color secondaryForeground;

/// Subdued backgrounds (hover, chips) and secondary text.
final Color muted;
final Color mutedForeground;

/// Hover and highlight background.
final Color accent;

/// Errors and destructive actions, and what sits on them.
final Color destructive;
final Color destructiveForeground;

/// Hairlines and control outlines; the focus ring.
final Color border;
final Color ring;

/// Chart series.
final Color chart1;
final Color chart2;
final Color chart3;
final Color chart4;
final Color chart5;

static QueryaUiColors lerp(QueryaUiColors a, QueryaUiColors b, double t) {
Color l(Color x, Color y) => Color.lerp(x, y, t)!;
return QueryaUiColors(
brightness: t < 0.5 ? a.brightness : b.brightness,
background: l(a.background, b.background),
foreground: l(a.foreground, b.foreground),
card: l(a.card, b.card),
popover: l(a.popover, b.popover),
popoverForeground: l(a.popoverForeground, b.popoverForeground),
primary: l(a.primary, b.primary),
primaryForeground: l(a.primaryForeground, b.primaryForeground),
secondary: l(a.secondary, b.secondary),
secondaryForeground: l(a.secondaryForeground, b.secondaryForeground),
muted: l(a.muted, b.muted),
mutedForeground: l(a.mutedForeground, b.mutedForeground),
accent: l(a.accent, b.accent),
destructive: l(a.destructive, b.destructive),
destructiveForeground:
l(a.destructiveForeground, b.destructiveForeground),
border: l(a.border, b.border),
ring: l(a.ring, b.ring),
chart1: l(a.chart1, b.chart1),
chart2: l(a.chart2, b.chart2),
chart3: l(a.chart3, b.chart3),
chart4: l(a.chart4, b.chart4),
chart5: l(a.chart5, b.chart5),
);
}

List<Object> get _props => [
brightness, background, foreground, card, popover, popoverForeground,
primary, primaryForeground, secondary, secondaryForeground, muted,
mutedForeground, accent, destructive, destructiveForeground, border,
ring, chart1, chart2, chart3, chart4, chart5,
];

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is! QueryaUiColors) return false;
return listEquals(_props, other._props);
}

@override
int get hashCode => Object.hashAll(_props);
}
3 changes: 2 additions & 1 deletion lib/features/connections/driver_manager_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart' as material;
import 'package:querya_desktop/core/theme/querya_ui_colors.dart';
import 'package:querya_desktop/core/extensions/extension_driver_catalog.dart';
import 'package:querya_desktop/core/extensions/local_extension_registry.dart';
import 'package:querya_desktop/core/layout/window_layout.dart';
Expand Down Expand Up @@ -147,7 +148,7 @@ class _DriverRow extends material.StatelessWidget {
});

final _DriverInfo info;
final ColorScheme theme;
final QueryaUiColors theme;

@override
material.Widget build(material.BuildContext context) {
Expand Down
3 changes: 2 additions & 1 deletion lib/features/connections/new_connection_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:math' as math;

import 'package:flutter/material.dart' as material;
import 'package:querya_desktop/core/theme/querya_ui_colors.dart';
import 'package:querya_desktop/core/database/connection_type_choice.dart';
import 'package:querya_desktop/core/extensions/extension_driver_catalog.dart';
import 'package:querya_desktop/core/extensions/local_extension_registry.dart';
Expand Down Expand Up @@ -357,7 +358,7 @@ class _DbTypeCard extends material.StatelessWidget {
});

final ConnectionTypeChoice choice;
final ColorScheme theme;
final QueryaUiColors theme;
final bool selected;
final VoidCallback onTap;

Expand Down
16 changes: 8 additions & 8 deletions lib/shared/widgets/querya_dropdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import 'package:flutter/services.dart';
import 'package:querya_desktop/core/layout/ui_scale.dart';
import 'package:querya_desktop/core/motion/querya_motion.dart';
import 'package:querya_desktop/core/motion/querya_motion_context.dart';
import 'package:querya_desktop/core/theme/querya_theme.dart';
import 'package:querya_desktop/core/theme/querya_theme_scope.dart';
import 'package:querya_desktop/core/theme/querya_ui_colors.dart';
import 'package:querya_desktop/core/ui/querya_control_tokens.dart';
import 'package:querya_desktop/shared/widgets/querya_dropdown_tokens.dart';

Expand All @@ -30,7 +30,7 @@ class QueryaDropdownItem<T> {

/// Stable dropdown built on [material.MenuAnchor] (no Overlay portal).
///
/// Visual metrics: [QueryaDropdownTokens]. Colors from shadcn [QueryaThemeColorScheme].
/// Visual metrics: [QueryaDropdownTokens]. Colors from [QueryaUiColors].
///
/// **Exit motion:** item pick and trigger-toggle delay [MenuController.close]
/// so fade-slide can run. Outside-tap / focus-loss closes via [MenuAnchor]
Expand Down Expand Up @@ -153,7 +153,7 @@ class _QueryaDropdownState<T> extends material.State<QueryaDropdown<T>> {
return QueryaControlScope.maybeOf(context) ?? QueryaControlSize.lg;
}

List<material.Widget> _menuChildren(QueryaThemeColorScheme cs, QueryaControlSize size) {
List<material.Widget> _menuChildren(QueryaUiColors cs, QueryaControlSize size) {
if (_cachedMenuChildren != null &&
listEquals(_cachedMenuItems, widget.items) &&
_cachedMenuValue == widget.value &&
Expand All @@ -172,7 +172,7 @@ class _QueryaDropdownState<T> extends material.State<QueryaDropdown<T>> {
material.Widget _triggerLabelText({
required material.BuildContext context,
required String label,
required QueryaThemeColorScheme cs,
required QueryaUiColors cs,
required bool expand,
required QueryaControlSize size,
}) {
Expand Down Expand Up @@ -204,7 +204,7 @@ class _QueryaDropdownState<T> extends material.State<QueryaDropdown<T>> {

material.Widget _menuItem(
QueryaDropdownItem<T> item,
QueryaThemeColorScheme cs,
QueryaUiColors cs,
QueryaControlSize size,
) {
return _QueryaDropdownMenuItem<T>(
Expand All @@ -224,7 +224,7 @@ class _QueryaDropdownState<T> extends material.State<QueryaDropdown<T>> {
material.Widget _buildTrigger({
required material.BuildContext context,
required material.MenuController controller,
required QueryaThemeColorScheme cs,
required QueryaUiColors cs,
required String label,
required double? fieldWidth,
}) {
Expand Down Expand Up @@ -512,7 +512,7 @@ class _QueryaDropdownMenuItem<T> extends material.StatefulWidget {
final QueryaDropdownItem<T> item;
final bool selected;
final bool enabled;
final QueryaThemeColorScheme colorScheme;
final QueryaUiColors colorScheme;
final bool compact;
final QueryaControlSize size;
final material.VoidCallback onPick;
Expand All @@ -527,7 +527,7 @@ class _QueryaDropdownMenuItemState<T>
bool _hovered = false;
bool _focused = false;

material.Widget _leading(material.BuildContext context, QueryaThemeColorScheme cs) {
material.Widget _leading(material.BuildContext context, QueryaUiColors cs) {
final slot = context.scaled(QueryaDropdownTokens.selectedCheckSlotWidth);
final checkSize = context.scaled(QueryaDropdownTokens.selectedCheckSize);
if (widget.selected) {
Expand Down
Loading
Loading