Skip to content

Commit

Permalink
feat: follow accent-color gsettings key for 24.10 (#922)
Browse files Browse the repository at this point in the history
* feat: follow accent-color gsettings key for 24.10

* fix: make theme sync work

* fix: use switch expression
  • Loading branch information
Feichtmeier authored Sep 18, 2024
1 parent 2465f72 commit c88fe55
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 33 deletions.
24 changes: 0 additions & 24 deletions lib/src/settings.dart

This file was deleted.

2 changes: 2 additions & 0 deletions lib/src/theme_widgets/gtk_constants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const kSchemaInterface = 'org.gnome.desktop.interface';
const kAccentColorKey = 'accent-color';
49 changes: 40 additions & 9 deletions lib/src/theme_widgets/inherited_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:platform_linux/platform.dart';
import 'package:yaru/src/settings.dart';
import 'package:yaru/src/theme_widgets/yaru_settings.dart';

import '../../theme.dart';

Expand Down Expand Up @@ -142,21 +142,29 @@ class YaruTheme extends StatefulWidget {
class _YaruThemeState extends State<YaruTheme> {
YaruVariant? _variant;
YaruSettings? _settings;
StreamSubscription<String?>? _subscription;
StreamSubscription<String?>? _themeNameSubscription;
StreamSubscription<String?>? _accentColorSubScription;

@override
void initState() {
super.initState();
if (widget.data.variant == null && canDetectVariant()) {
_settings = widget._settings ?? YaruSettings();
_variant = resolveVariant(_settings?.getThemeName());
_subscription = _settings!.themeNameChanged.listen(updateVariant);
_settings?.init();
_variant = resolveAccentColorVariant(_settings?.getAccentColor()) ??
resolveGtkThemeVariant(_settings?.getThemeName());
_accentColorSubScription ??=
_settings!.accentColorChanged.listen(updateVariant);
_themeNameSubscription ??=
_settings!.themeNameChanged.listen(updateVariant);
}
}

@override
void dispose() {
_subscription?.cancel();
_themeNameSubscription?.cancel();
_accentColorSubScription?.cancel();
_settings?.dispose();
super.dispose();
}

Expand All @@ -168,7 +176,7 @@ class _YaruThemeState extends State<YaruTheme> {

// This very simple but manual solution is the safest approach for now
// New theme mappings can be added here easily, after adding them in variant.dart
YaruVariant? resolveVariant(String? name) =>
YaruVariant? resolveGtkThemeVariant(String? name) =>
switch (name?.replaceAll('-dark', '')) {
'Adwaita' => YaruVariant.adwaitaBlue,
'Adwaita-green' || 'Yaru-green' => YaruVariant.adwaitaGreen,
Expand All @@ -193,10 +201,33 @@ class _YaruThemeState extends State<YaruTheme> {
_ => _defaultFallBackVariant(widget._platform),
};

// This is the gnome accent-color feature for Ubuntu 24.10+
// it is null on older systems.
// Previous similar Yaru versions replace their gnome counterpart.
// At some point we probably want to check which distribution of gnome is run and use the
// upstream colors instead.
YaruVariant? resolveAccentColorVariant(String? name) => switch (name) {
'blue' => YaruVariant.blue,
'teal' || 'Yaru-teal' => YaruVariant.adwaitaTeal,
'green' || 'Yaru-green' => YaruVariant.adwaitaGreen,
'yellow' || 'Yaru-yellow' => YaruVariant.adwaitaYellow,
'orange' => YaruVariant.orange,
'red' => YaruVariant.red,
'pink' || 'Yaru-pink' => YaruVariant.magenta,
'purple' => YaruVariant.purple,
'slate' || 'Yaru-slate' => YaruVariant.adwaitaSlate,
'brown' => YaruVariant.wartyBrown,
_ => null,
};

void updateVariant([String? value]) {
assert(canDetectVariant());
final name = value ?? _settings?.getThemeName();
setState(() => _variant = resolveVariant(name));
final gtkThemeName = value ?? _settings?.getThemeName();
final accentColor = value ?? _settings?.getAccentColor();
setState(
() => _variant = resolveAccentColorVariant(accentColor) ??
resolveGtkThemeVariant(gtkThemeName),
);
}

ThemeMode resolveMode() {
Expand Down Expand Up @@ -231,7 +262,7 @@ class _YaruThemeState extends State<YaruTheme> {

@override
Widget build(BuildContext context) {
if (_settings != null && _subscription == null) {
if (_settings != null && _themeNameSubscription == null) {
return const SizedBox.shrink(); // #231
}
final data = resolveData();
Expand Down
93 changes: 93 additions & 0 deletions lib/src/theme_widgets/settings_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import 'package:dbus/dbus.dart';
import 'package:flutter/foundation.dart';
import 'package:gsettings/gsettings.dart';

class GSettingsService {
final _settings = <String, GnomeSettings?>{};

GnomeSettings? lookup(String schemaId, {String? path}) {
try {
return _settings[schemaId] ??= GnomeSettings(schemaId, path: path);
} on GSettingsSchemaNotInstalledException catch (_) {
return null;
}
}

void dispose() {
for (final settings in _settings.values) {
settings?.dispose();
}
}
}

class GnomeSettings {
GnomeSettings(String schemaId, {String? path})
: _settings = GSettings(schemaId, path: path) {
_settings.keysChanged.listen((keys) {
for (final key in keys) {
_updateValue(key);
}
});
}

final GSettings _settings;
final _values = <String, dynamic>{};
final _listeners = <VoidCallback>{};

void addListener(VoidCallback listener) => _listeners.add(listener);
void removeListener(VoidCallback listener) => _listeners.remove(listener);
void notifyListeners() {
for (final listener in _listeners) {
listener();
}
}

void dispose() => _settings.close();

bool? boolValue(String key) => getValue<bool>(key);
int? intValue(String key) => getValue<int>(key);
double? doubleValue(String key) => getValue<double>(key);
String? stringValue(String key) => getValue<String>(key);
Iterable<String>? stringArrayValue(String key) =>
getValue<Iterable>(key)?.cast<String>();

T? getValue<T>(String key) => _values[key] ?? _updateValue(key);

T? _updateValue<T>(String key) {
T? value;
try {
_settings.get(key).then((v) {
value = v.toNative() as T?;
if (_values[key] != value) {
_values[key] = value;
notifyListeners();
}
});
} on GSettingsUnknownKeyException catch (_) {}
return value;
}

Future<void> setValue<T>(String key, T value) async {
if (_values[key] == value) return;
_values[key] = value;

return switch (T) {
const (bool) => _settings.set(key, DBusBoolean(value as bool)),
const (int) => _settings.set(key, DBusInt32(value as int)),
const (double) => _settings.set(key, DBusDouble(value as double)),
const (String) => _settings.set(key, DBusString(value as String)),
const (List<String>) =>
_settings.set(key, DBusArray.string(value as List<String>)),
_ => throw UnsupportedError('Unsupported type: $T'),
};
}

Future<void> setUint32Value(String key, int value) async {
if (_values[key] == value) return;
_values[key] = value;
await _settings.set(key, DBusUint32(value));
}

Future<void> resetValue(String key) =>
_settings.setAll(<String, DBusValue?>{key: null});
}
59 changes: 59 additions & 0 deletions lib/src/theme_widgets/yaru_settings.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:gtk/gtk.dart';
import 'package:yaru/src/theme_widgets/gtk_constants.dart';
import 'package:yaru/src/theme_widgets/settings_service.dart';

abstract class YaruSettings {
factory YaruSettings() = YaruGtkSettings;
const YaruSettings._();

String? getThemeName();
String? getAccentColor();
Stream<String?> get themeNameChanged;
Stream<String?> get accentColorChanged;
void init();
Future<void> dispose();
}

class YaruGtkSettings extends YaruSettings {
YaruGtkSettings([
@visibleForTesting GtkSettings? settings,
@visibleForTesting GSettingsService? settingsService,
]) : _gtkSettings = settings ?? GtkSettings(),
_gSettingsService = settingsService ?? GSettingsService(),
super._();

final GtkSettings _gtkSettings;
final GSettingsService _gSettingsService;
GnomeSettings? _gSettings;

@override
String? getThemeName() => _gtkSettings.getProperty(kGtkThemeName) as String?;

@override
Stream<String?> get themeNameChanged =>
_gtkSettings.notifyProperty(kGtkThemeName).cast<String?>();

final _accentColorController = StreamController<String?>.broadcast();
@override
Stream<String?> get accentColorChanged => _accentColorController.stream;

@override
void init() {
_gSettings ??= _gSettingsService.lookup(kSchemaInterface);
_gSettings?.addListener(
() => _accentColorController.add(getAccentColor()),
);
}

@override
Future<void> dispose() async {
await _accentColorController.close();
_gSettingsService.dispose();
}

@override
String? getAccentColor() => _gSettings?.stringValue(kAccentColorKey);
}
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ dependencies:
animated_vector: ^0.2.0
animated_vector_annotations: ^0.2.0
collection: ^1.17.0
dbus: ^0.7.10
flutter:
sdk: flutter
gsettings: ^0.2.8
gtk: ^2.1.0
platform: ^3.1.5
platform_linux: ^0.1.2
Expand Down

0 comments on commit c88fe55

Please sign in to comment.