Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions lib/constants/configurations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
10 changes: 10 additions & 0 deletions lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
10 changes: 10 additions & 0 deletions lib/l10n/intl_zh_Hans.arb
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@
"@light": {
"description": "一个色彩设置条目,选中后启用浅色模式。"
},
"trayIconColorMode": "托盘图标颜色模式",
"@trayIconColorMode": {
"description": "一个设置条目,允许用户控制系统托盘图标的颜色,可选择自动、亮色和暗色模式。"
},
"trayIconColorModeSubtitle": "控制托盘图标如何适应您的系统主题。",
"@trayIconColorModeSubtitle": {},
"automaticTrayIconMode": "自动",
"@automaticTrayIconMode": {
"description": "托盘图标颜色模式的一个选项,根据系统主题自动在亮色和暗色之间切换。"
},
"themeColor": "主题颜色",
"@themeColor": {
"description": "一个设置条目,允许用户选择自己喜欢的主题色。"
Expand Down
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ void main(List<String> arguments) async {
);

if (isDesktop) {
final icon = TrayManager.getTrayIcon();
final icon = await TrayManager.getTrayIcon();
await systemTray.initSystemTray(
title: Platform.isMacOS ? null : 'Rune',
iconPath: icon.path,
Expand Down
2 changes: 2 additions & 0 deletions lib/screens/settings_theme/settings_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'widgets/dynamic_colors_setting.dart';
import 'widgets/branding_animation_setting.dart';
import 'widgets/remember_window_size_setting_state.dart';
import 'widgets/linux_custom_window_controls_setting.dart';
import 'widgets/tray_icon_color_mode_setting.dart';

class SettingsTheme extends StatefulWidget {
const SettingsTheme({super.key});
Expand All @@ -32,6 +33,7 @@ class _SettingsThemeState extends State<SettingsTheme> {
ColorModeSetting(),
ThemeColorSetting(),
DynamicColorsSetting(),
TrayIconColorModeSetting(),
BrandingAnimationSetting(),
WindowSizeSetting(),
RememberWindowSizeSetting(),
Expand Down
Original file line number Diff line number Diff line change
@@ -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<TrayIconColorModeSetting> {
String trayIconColorMode = "auto";

@override
void initState() {
super.initState();
_loadTrayIconColorMode();
}

Future<void> _loadTrayIconColorMode() async {
final storedMode =
await $settingsManager.getValue<String>(kTrayIconColorModeKey);
setState(() {
trayIconColorMode = storedMode ?? "auto";
});
}
Comment on lines +26 to +32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consider handling asynchronous setState more robustly.

Check if the widget is mounted before calling setState in _loadTrayIconColorMode to prevent errors if the widget is disposed during the async operation.

Suggested change
Future<void> _loadTrayIconColorMode() async {
final storedMode =
await $settingsManager.getValue<String>(kTrayIconColorModeKey);
setState(() {
trayIconColorMode = storedMode ?? "auto";
});
}
Future<void> _loadTrayIconColorMode() async {
final storedMode =
await $settingsManager.getValue<String>(kTrayIconColorModeKey);
if (mounted) {
setState(() {
trayIconColorMode = storedMode ?? "auto";
});
}
}


Future<void> _updateTrayIconColorMode(String newMode) async {
setState(() {
trayIconColorMode = newMode;
});
await $settingsManager.setValue(kTrayIconColorModeKey, newMode);

// Update the tray icon immediately
$tray.updateTrayIcon();
Comment on lines +40 to +41
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider awaiting updateTrayIcon for consistency.

Awaiting updateTrayIcon will ensure the tray icon is updated before proceeding and allow proper error handling.

Suggested change
// Update the tray icon immediately
$tray.updateTrayIcon();
// Update the tray icon immediately
await $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);
}
},
);
}
}
29 changes: 22 additions & 7 deletions lib/utils/tray_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -26,16 +29,28 @@ class TrayIcon {
}

class TrayManager {
static TrayIcon getTrayIcon() {
static Future<TrayIcon> 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<String>(kTrayIconColorModeKey) ??
"auto";
Comment on lines +38 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Defaulting to "auto" is reasonable, but consider validating the value.

Validate trayIconColorMode against the allowed values before using it to prevent issues from invalid or corrupted settings.


String brightness;
if (trayIconColorMode == "auto") {
// Automatic mode: follow system theme
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);
Expand Down Expand Up @@ -113,7 +128,7 @@ class TrayManager {
}

Future<void> updateTrayIcon() async {
final icon = getTrayIcon();
final icon = await TrayManager.getTrayIcon();
await systemTray.setImage(icon.path, isTemplate: true, isInstalled: icon.isInstalled);
}

Expand Down
Loading