diff --git a/lib/constants/configurations.dart b/lib/constants/configurations.dart index c8983a632..63413e9b5 100644 --- a/lib/constants/configurations.dart +++ b/lib/constants/configurations.dart @@ -149,3 +149,9 @@ const kDeduplicateThresholdKey = 'deduplicate_threshold'; /// controls similar to the Windows implementation. For compatibility reasons, /// this defaults to false (disabled) when the setting is missing. const kLinuxCustomWindowControlsKey = 'linux_custom_window_controls'; + +/// This key is used to store the user's preference for the tray icon color mode. +/// It determines whether the tray icon should automatically adapt to the system +/// theme or use a fixed color mode. Options include "auto" (follows system theme), +/// "light" (always use light icon), and "dark" (always use dark icon). +const kTrayIconColorModeKey = 'tray_icon_color_mode'; diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index b139cf006..b1ff745ac 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -198,6 +198,16 @@ "@light": { "description": "A color settings entry that enables light mode when selected." }, + "trayIconColorMode": "Tray Icon Color Mode", + "@trayIconColorMode": { + "description": "A settings entry that allows users to control the color of the system tray icon, choosing between automatic, light, and dark modes." + }, + "trayIconColorModeSubtitle": "Control how the tray icon adapts to your system theme.", + "@trayIconColorModeSubtitle": {}, + "automaticTrayIconMode": "Automatic", + "@automaticTrayIconMode": { + "description": "An option for tray icon color mode that automatically switches between light and dark based on the system theme." + }, "themeColor": "Theme Color", "@themeColor": { "description": "A settings entry that allows users to choose their preferred theme color." diff --git a/lib/l10n/intl_zh_Hans.arb b/lib/l10n/intl_zh_Hans.arb index 8ab754c5e..a0be92734 100644 --- a/lib/l10n/intl_zh_Hans.arb +++ b/lib/l10n/intl_zh_Hans.arb @@ -198,6 +198,16 @@ "@light": { "description": "一个色彩设置条目,选中后启用浅色模式。" }, + "trayIconColorMode": "托盘图标颜色模式", + "@trayIconColorMode": { + "description": "一个设置条目,允许用户控制系统托盘图标的颜色,可选择自动、亮色和暗色模式。" + }, + "trayIconColorModeSubtitle": "控制托盘图标如何适应您的系统主题。", + "@trayIconColorModeSubtitle": {}, + "automaticTrayIconMode": "自动", + "@automaticTrayIconMode": { + "description": "托盘图标颜色模式的一个选项,根据系统主题自动在亮色和暗色之间切换。" + }, "themeColor": "主题颜色", "@themeColor": { "description": "一个设置条目,允许用户选择自己喜欢的主题色。" diff --git a/lib/main.dart b/lib/main.dart index 73ff82647..c770a795f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -182,7 +182,7 @@ void main(List arguments) async { ); if (isDesktop) { - final icon = TrayManager.getTrayIcon(); + final icon = await TrayManager.getTrayIcon(); await systemTray.initSystemTray( title: Platform.isMacOS ? null : 'Rune', iconPath: icon.path, diff --git a/lib/screens/settings_laboratory/settings_laboratory.dart b/lib/screens/settings_laboratory/settings_laboratory.dart index eac137259..2b247c27c 100644 --- a/lib/screens/settings_laboratory/settings_laboratory.dart +++ b/lib/screens/settings_laboratory/settings_laboratory.dart @@ -14,6 +14,7 @@ import 'widgets/settings/branding_animation_settings.dart'; import 'widgets/settings/cover_wall_richness_settings.dart'; import 'widgets/settings/library_cover_wallpaper_settings.dart'; import 'widgets/settings/mild_spectrum_settings.dart'; +import 'widgets/settings/tray_icon_color_mode_settings.dart'; class SettingsLaboratory extends StatelessWidget { const SettingsLaboratory({super.key}); @@ -98,6 +99,7 @@ class _ResponsiveSettingsGrid extends StatelessWidget { CafeModeSettings(), ForceZuneSettings(), MildSpectrumSettings(), + TrayIconColorModeSettings(), ], ), ); diff --git a/lib/screens/settings_laboratory/widgets/settings/tray_icon_color_mode_settings.dart b/lib/screens/settings_laboratory/widgets/settings/tray_icon_color_mode_settings.dart new file mode 100644 index 000000000..e48bf029e --- /dev/null +++ b/lib/screens/settings_laboratory/widgets/settings/tray_icon_color_mode_settings.dart @@ -0,0 +1,73 @@ +import 'package:fluent_ui/fluent_ui.dart'; + +import '../../../../utils/tray_manager.dart'; +import '../../../../utils/settings_manager.dart'; +import '../../../../constants/configurations.dart'; + +import '../../utils/settings_combo_box_item.dart'; + +import '../settings_card.dart'; +import '../settings_combo_box.dart'; + +class TrayIconColorModeSettings extends StatefulWidget { + const TrayIconColorModeSettings({super.key}); + + @override + TrayIconColorModeSettingsState createState() => + TrayIconColorModeSettingsState(); +} + +class TrayIconColorModeSettingsState extends State { + String? trayIconColorMode = 'auto'; + final SettingsManager _settingsManager = SettingsManager(); + + @override + void initState() { + super.initState(); + _loadSettings(); + } + + Future _loadSettings() async { + final storedMode = + await _settingsManager.getValue(kTrayIconColorModeKey); + if (storedMode != null) { + setState(() => trayIconColorMode = storedMode); + } + } + + @override + Widget build(BuildContext context) { + final items = [ + SettingsComboBoxItem( + value: 'auto', + text: 'Automatic', + ), + SettingsComboBoxItem( + value: 'light', + text: 'Light', + ), + SettingsComboBoxItem( + value: 'dark', + text: 'Dark', + ), + ]; + + return SettingsCard( + title: "Tray Icon Color Mode", + description: + "Set the tray icon color mode preference. Automatic mode will adapt to your system theme.", + content: SettingsComboBox( + value: trayIconColorMode, + items: items, + onChanged: (value) async { + if (value == null) return; + setState(() => trayIconColorMode = value); + await _settingsManager.setValue(kTrayIconColorModeKey, value); + + // Update the tray icon immediately + $tray.updateTrayIcon(); + }, + ), + ); + } +} diff --git a/lib/screens/settings_theme/widgets/tray_icon_color_mode_setting.dart b/lib/screens/settings_theme/widgets/tray_icon_color_mode_setting.dart new file mode 100644 index 000000000..feac4b316 --- /dev/null +++ b/lib/screens/settings_theme/widgets/tray_icon_color_mode_setting.dart @@ -0,0 +1,65 @@ +import 'package:fluent_ui/fluent_ui.dart'; + +import '../../../utils/l10n.dart'; +import '../../../utils/tray_manager.dart'; +import '../../../widgets/settings/settings_box_combo_box.dart'; +import '../../../constants/configurations.dart'; +import '../../../constants/settings_manager.dart'; + +class TrayIconColorModeSetting extends StatefulWidget { + const TrayIconColorModeSetting({super.key}); + + @override + TrayIconColorModeSettingState createState() => + TrayIconColorModeSettingState(); +} + +class TrayIconColorModeSettingState extends State { + String trayIconColorMode = "auto"; + + @override + void initState() { + super.initState(); + _loadTrayIconColorMode(); + } + + Future _loadTrayIconColorMode() async { + final storedMode = + await $settingsManager.getValue(kTrayIconColorModeKey); + setState(() { + trayIconColorMode = storedMode ?? "auto"; + }); + } + + Future _updateTrayIconColorMode(String newMode) async { + setState(() { + trayIconColorMode = newMode; + }); + await $settingsManager.setValue(kTrayIconColorModeKey, newMode); + + // Update the tray icon immediately + $tray.updateTrayIcon(); + } + + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return SettingsBoxComboBox( + title: s.trayIconColorMode, + subtitle: s.trayIconColorModeSubtitle, + value: trayIconColorMode, + items: [ + SettingsBoxComboBoxItem( + value: "auto", title: s.automaticTrayIconMode), + SettingsBoxComboBoxItem(value: "light", title: s.light), + SettingsBoxComboBoxItem(value: "dark", title: s.dark), + ], + onChanged: (newValue) { + if (newValue != null) { + _updateTrayIconColorMode(newValue); + } + }, + ); + } +} diff --git a/lib/utils/tray_manager.dart b/lib/utils/tray_manager.dart index 68edb6dc3..55520b014 100644 --- a/lib/utils/tray_manager.dart +++ b/lib/utils/tray_manager.dart @@ -9,6 +9,9 @@ import 'package:bitsdojo_window/bitsdojo_window.dart'; import '../providers/status.dart'; import '../providers/router_path.dart'; +import '../constants/configurations.dart'; +import '../constants/settings_manager.dart'; + import 'api/play_next.dart'; import 'api/play_pause.dart'; import 'api/play_play.dart'; @@ -26,16 +29,34 @@ class TrayIcon { } class TrayManager { - static TrayIcon getTrayIcon() { + static Future getTrayIcon() async { if (Platform.isMacOS) { return TrayIcon('assets/mac-tray.svg', false); } - final brightness = - SchedulerBinding.instance.platformDispatcher.platformBrightness == - Brightness.light - ? Brightness.dark.name - : Brightness.light.name; + // Get user preference for tray icon color mode + final trayIconColorMode = + await $settingsManager.getValue(kTrayIconColorModeKey) ?? + "auto"; + + String brightness; + if (trayIconColorMode == "auto") { + // Automatic mode: follow system theme + // Special case: GNOME always uses light icon + if (Platform.isLinux && + Platform.environment['XDG_CURRENT_DESKTOP'] == 'GNOME') { + brightness = Brightness.light.name; + } else { + brightness = + SchedulerBinding.instance.platformDispatcher.platformBrightness == + Brightness.light + ? Brightness.dark.name + : Brightness.light.name; + } + } else { + // Manual mode: use user's selection directly + brightness = trayIconColorMode; + } if (Platform.isWindows) { return TrayIcon('assets/tray_icon_$brightness.ico', false); @@ -113,7 +134,7 @@ class TrayManager { } Future updateTrayIcon() async { - final icon = getTrayIcon(); + final icon = await TrayManager.getTrayIcon(); await systemTray.setImage(icon.path, isTemplate: true, isInstalled: icon.isInstalled); }