Skip to content

Conversation

@baiyuanneko
Copy link
Contributor

@baiyuanneko baiyuanneko commented Nov 2, 2025

This PR introduces a feature allowing users to manually select a light or dark version for the Rune tray icon. This option is useful when the automatic detection is incorrect.

Summary by Sourcery

Allow users to override automatic tray icon theming by selecting a fixed light or dark icon mode or keeping automatic system-based behavior

New Features:

  • Introduce a tray icon color mode preference allowing users to select auto, light or dark icons manually
  • Add UI controls in both laboratory and theme settings screens to let users pick and immediately apply their tray icon color mode

Enhancements:

  • Extend TrayManager to asynchronously fetch and apply the user’s tray icon color mode preference with special GNOME handling
  • Convert TrayManager.getTrayIcon to async and update calls to await it, ensuring immediate icon refresh after preference changes
  • Add kTrayIconColorModeKey in configurations to store the tray icon color mode

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 2, 2025

Reviewer's Guide

This PR adds a new user preference for tray icon color mode—auto, light, or dark—by defining a settings key, updating TrayManager to load and honor that preference (with a GNOME special case), and introducing two new settings UI components to allow users to configure and immediately apply their choice.

Sequence diagram for updating tray icon after user changes color mode

sequenceDiagram
    actor User
    participant SettingsUI
    participant SettingsManager
    participant TrayManager
    participant SystemTray
    User->>SettingsUI: Selects tray icon color mode
    SettingsUI->>SettingsManager: Save new color mode
    SettingsUI->>TrayManager: updateTrayIcon()
    TrayManager->>SettingsManager: get tray icon color mode
    TrayManager->>SystemTray: setImage(iconPath)
Loading

Entity relationship diagram for tray icon color mode preference key

erDiagram
    SETTINGS_MANAGER {
      STRING kTrayIconColorModeKey
    }
    USER ||--o{ SETTINGS_MANAGER : sets
    SETTINGS_MANAGER ||--o{ TRAY_MANAGER : reads
Loading

Class diagram for new and updated tray icon color mode settings UI components

classDiagram
    class TrayIconColorModeSettings {
      +String? trayIconColorMode
      +SettingsManager _settingsManager
      +_loadSettings()
      +build()
    }
    class TrayIconColorModeSettingsState {
      +String? trayIconColorMode
      +SettingsManager _settingsManager
      +_loadSettings()
      +build()
    }
    TrayIconColorModeSettings --|> TrayIconColorModeSettingsState

    class TrayIconColorModeSetting {
      +String trayIconColorMode
      +_loadTrayIconColorMode()
      +_updateTrayIconColorMode(String)
      +build()
    }
    class TrayIconColorModeSettingState {
      +String trayIconColorMode
      +_loadTrayIconColorMode()
      +_updateTrayIconColorMode(String)
      +build()
    }
    TrayIconColorModeSetting --|> TrayIconColorModeSettingState
Loading

Class diagram for updated TrayManager logic

classDiagram
    class TrayManager {
      +Future<TrayIcon> getTrayIcon()
      +Future<void> updateTrayIcon()
    }
    class TrayIcon {
      +String path
      +bool isInstalled
    }
    TrayManager --> TrayIcon
Loading

File-Level Changes

Change Details Files
Async and preference-aware tray icon selection logic
  • Made TrayManager.getTrayIcon return Future and await user preference
  • Loaded trayIconColorMode from SettingsManager, defaulting to 'auto'
  • Added manual vs auto brightness logic with GNOME desktop special case
  • Updated updateTrayIcon and main to await the async getTrayIcon call
lib/utils/tray_manager.dart
lib/main.dart
Define and use new configuration key for tray icon mode
  • Added kTrayIconColorModeKey constant with documentation
  • Imported configurations and SettingsManager where needed
lib/constants/configurations.dart
lib/utils/tray_manager.dart
New settings UI for tray icon color mode
  • Created TrayIconColorModeSettings widget and added it to settings_laboratory
  • Created TrayIconColorModeSetting widget in settings_theme
  • Implemented combo box items, persistence via SettingsManager, and immediate tray icon update
lib/screens/settings_laboratory/widgets/settings/tray_icon_color_mode_settings.dart
lib/screens/settings_laboratory/settings_laboratory.dart
lib/screens/settings_theme/widgets/tray_icon_color_mode_setting.dart

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • Consider using an enum or static constants for the tray icon modes ("auto", "light", "dark") instead of raw strings to avoid typos and centralize mode definitions.
  • Manual color selection is only applied to the Windows .ico assets—macOS still always returns the same SVG; if you want macOS users to override the icon color, add or switch to light/dark SVG variants in getTrayIcon.
  • getTrayIcon currently only handles macOS and Windows; add a fallback return or explicit handling for other platforms (e.g. Linux) to ensure it never returns null.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider using an enum or static constants for the tray icon modes ("auto", "light", "dark") instead of raw strings to avoid typos and centralize mode definitions.
- Manual color selection is only applied to the Windows .ico assets—macOS still always returns the same SVG; if you want macOS users to override the icon color, add or switch to light/dark SVG variants in getTrayIcon.
- getTrayIcon currently only handles macOS and Windows; add a fallback return or explicit handling for other platforms (e.g. Linux) to ensure it never returns null.

## Individual Comments

### Comment 1
<location> `lib/utils/tray_manager.dart:38-40` </location>
<code_context>
-            ? Brightness.dark.name
-            : Brightness.light.name;
+    // Get user preference for tray icon color mode
+    final trayIconColorMode =
+        await $settingsManager.getValue<String>(kTrayIconColorModeKey) ??
+            "auto";
+
+    String brightness;
</code_context>

<issue_to_address>
**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.
</issue_to_address>

### Comment 2
<location> `lib/screens/settings_theme/widgets/tray_icon_color_mode_setting.dart:26-32` </location>
<code_context>
+  Future<void> _loadTrayIconColorMode() async {
+    final storedMode =
+        await $settingsManager.getValue<String>(kTrayIconColorModeKey);
+    setState(() {
+      trayIconColorMode = storedMode ?? "auto";
+    });
+  }
</code_context>

<issue_to_address>
**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.

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

### Comment 3
<location> `lib/screens/settings_theme/widgets/tray_icon_color_mode_setting.dart:40-41` </location>
<code_context>
+    await $settingsManager.setValue(kTrayIconColorModeKey, newMode);
+
+    // Update the tray icon immediately
+    $tray.updateTrayIcon();
+  }
+
</code_context>

<issue_to_address>
**suggestion:** Consider awaiting updateTrayIcon for consistency.

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

```suggestion
    // Update the tray icon immediately
    await $tray.updateTrayIcon();
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +38 to +40
final trayIconColorMode =
await $settingsManager.getValue<String>(kTrayIconColorModeKey) ??
"auto";
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.

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

Comment on lines +40 to +41
// Update the tray icon immediately
$tray.updateTrayIcon();
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();

@dosubot dosubot bot added C - l10n Localization related issue C - UI UI related issue R - enhancement New feature or request labels Nov 2, 2025
@Losses
Copy link
Owner

Losses commented Nov 3, 2025

Sorry, I didn't quite understand the context of this PR. When would there be a situation where the system color information is incorrect?

@baiyuanneko
Copy link
Contributor Author

Thanks for asking and reading my PR!

The current logic is if system theme is light, use dark tray icon. And, use light tray icon if system theme is dark.

However there is a situation that system theme is light, but user still needs a light tray icon. For example, GNOME always has a dark topbar, no matter user theme is light or dark. If one user uses light system theme, then Rune will render a dark tray icon, displaying on the dark topbar, which will be hard to see. So, I think having a option will be good in these circumstances.

@Losses
Copy link
Owner

Losses commented Nov 6, 2025

Thanks for asking and reading my PR!

The current logic is if system theme is light, use dark tray icon. And, use light tray icon if system theme is dark.

However there is a situation that system theme is light, but user still needs a light tray icon. For example, GNOME always has a dark topbar, no matter user theme is light or dark. If one user uses light system theme, then Rune will render a dark tray icon, displaying on the dark topbar, which will be hard to see. So, I think having a option will be good in these circumstances.

Thank you for the reply, I see the whole picture now. My main concern is that I don't want the system to be cluttered with these trivial settings, turning it into a monster like Foobar that has no simplicity whatsoever. But the situation you mentioned is indeed a problem. I think perhaps we could add it to a hidden menu (for power users only) instead of in the settings interface? We already have a hidden setting menu in the system

@baiyuanneko
Copy link
Contributor Author

Thank you for the reply, I see the whole picture now. My main concern is that I don't want the system to be cluttered with these trivial settings, turning it into a monster like Foobar that has no simplicity whatsoever. But the situation you mentioned is indeed a problem. I think perhaps we could add it to a hidden menu (for power users only) instead of in the settings interface? We already have a hidden setting menu in the system

Thank you again, and I've understand your concern. It's true that simplicity is important. I can move the setting into hidden setting menu later.

However, I think as a normal user, it is maybe hard to realize that there is a hidden setting menu?

Btw, I have another thought now. Maybe we can add a detection that when GNOME environment is detected, by default to offer a light tray icon. (AS GNOME always has a dark topbar.) This keeps simplicity and provides out-of-the-box fresh ready-to-go experience for GNOME users. What do you think about this idea?

@Losses
Copy link
Owner

Losses commented Nov 9, 2025

Thank you for the reply, I see the whole picture now. My main concern is that I don't want the system to be cluttered with these trivial settings, turning it into a monster like Foobar that has no simplicity whatsoever. But the situation you mentioned is indeed a problem. I think perhaps we could add it to a hidden menu (for power users only) instead of in the settings interface? We already have a hidden setting menu in the system

Thank you again, and I've understand your concern. It's true that simplicity is important. I can move the setting into hidden setting menu later.

However, I think as a normal user, it is maybe hard to realize that there is a hidden setting menu?

Btw, I have another thought now. Maybe we can add a detection that when GNOME environment is detected, by default to offer a light tray icon. (AS GNOME always has a dark topbar.) This keeps simplicity and provides out-of-the-box fresh ready-to-go experience for GNOME users. What do you think about this idea?

That's a good idea!

@baiyuanneko
Copy link
Contributor Author

Ok, I will move the color preference into hidden setting menu, and attempt to implement the GNOME detection feature!

@baiyuanneko baiyuanneko marked this pull request as draft November 15, 2025 11:06
@baiyuanneko baiyuanneko marked this pull request as ready for review November 15, 2025 11:28
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • Consider replacing the raw string values ("auto", "light", "dark") with a typed enum to avoid magic strings and make the code more maintainable.
  • The tray icon color mode settings UI is duplicated in two places—extract a shared widget or component to reduce code duplication.
  • You might move the system‐theme and GNOME detection logic into its own helper method or service to simplify TrayManager.getTrayIcon and improve readability.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider replacing the raw string values ("auto", "light", "dark") with a typed enum to avoid magic strings and make the code more maintainable.
- The tray icon color mode settings UI is duplicated in two places—extract a shared widget or component to reduce code duplication.
- You might move the system‐theme and GNOME detection logic into its own helper method or service to simplify TrayManager.getTrayIcon and improve readability.

## Individual Comments

### Comment 1
<location> `lib/utils/tray_manager.dart:38-40` </location>
<code_context>
-            ? Brightness.dark.name
-            : Brightness.light.name;
+    // Get user preference for tray icon color mode
+    final trayIconColorMode =
+        await $settingsManager.getValue<String>(kTrayIconColorModeKey) ??
+            "auto";
+
+    String brightness;
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Consider validating trayIconColorMode value before using it.

Without validation, an invalid trayIconColorMode value could cause the tray icon to display incorrectly. Implementing a check or default fallback will help prevent user-facing issues.
</issue_to_address>

### Comment 2
<location> `lib/utils/tray_manager.dart:61-62` </location>
<code_context>
+      brightness = trayIconColorMode;
+    }

     if (Platform.isWindows) {
       return TrayIcon('assets/tray_icon_$brightness.ico', false);
</code_context>

<issue_to_address>
**suggestion:** Consider normalizing brightness value for Windows tray icon filename.

Unexpected brightness values could result in missing icon files and tray icon load failures. Validating brightness before using it in the filename would improve robustness.

```suggestion
    if (Platform.isWindows) {
      // Normalize brightness value to avoid missing icon files
      final allowedBrightness = ['light', 'dark'];
      final normalizedBrightness = allowedBrightness.contains(brightness) ? brightness : 'light';
      return TrayIcon('assets/tray_icon_$normalizedBrightness.ico', false);
```
</issue_to_address>

### Comment 3
<location> `lib/screens/settings_laboratory/widgets/settings/tray_icon_color_mode_settings.dart:62-69` </location>
<code_context>
+        onChanged: (value) async {
+          if (value == null) return;
+          setState(() => trayIconColorMode = value);
+          await _settingsManager.setValue<String>(kTrayIconColorModeKey, value);
+
+          // Update the tray icon immediately
+          $tray.updateTrayIcon();
+        },
+      ),
</code_context>

<issue_to_address>
**suggestion:** Consider awaiting $tray.updateTrayIcon for consistency.

Not awaiting this async method may cause race conditions or UI issues if the tray icon update isn't finished before subsequent actions. Awaiting ensures the update completes first.

```suggestion
        onChanged: (value) async {
          if (value == null) return;
          setState(() => trayIconColorMode = value);
          await _settingsManager.setValue<String>(kTrayIconColorModeKey, value);

          // Update the tray icon immediately
          await $tray.updateTrayIcon();
        },
```
</issue_to_address>

### Comment 4
<location> `lib/screens/settings_theme/widgets/tray_icon_color_mode_setting.dart:41` </location>
<code_context>
+          await _settingsManager.setValue<String>(kTrayIconColorModeKey, value);
+
+          // Update the tray icon immediately
+          $tray.updateTrayIcon();
+        },
+      ),
</code_context>

<issue_to_address>
**suggestion:** Consider awaiting $tray.updateTrayIcon for consistency.

Not awaiting updateTrayIcon may cause race conditions or inconsistent UI updates. Awaiting ensures the tray icon reflects the latest state before proceeding.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@baiyuanneko
Copy link
Contributor Author

Now moved the color schema settings into the hidden setting menu, and default to light tray icon if current DE is GNOME.

@Losses
Copy link
Owner

Losses commented Nov 16, 2025

LGTM, Thanks!

@Losses Losses merged commit d9334e3 into Losses:master Nov 16, 2025
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C - l10n Localization related issue C - UI UI related issue R - enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants