Skip to content

Add StatusIcon for Windows - #735

Merged
lidaobing merged 2 commits into
mainfrom
bf_win_appindicator
Aug 1, 2026
Merged

Add StatusIcon for Windows#735
lidaobing merged 2 commits into
mainfrom
bf_win_appindicator

Conversation

@lidaobing

@lidaobing lidaobing commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary by Sourcery

Implement Windows system tray app indicator support and centralize status icon state logic with accompanying tests and configuration updates.

New Features:

  • Add Windows-specific app indicator implementation integrated with the system tray, including menu actions and icon handling.

Enhancements:

  • Refine status icon state handling into a dedicated AppIndicatorState utility with unit tests.
  • Extend Windows build configuration to link shell32 and include Windows app indicator sources.
  • Expose host OS flags in configuration data and adjust test configuration path quoting for better portability.

Tests:

  • Add unit tests for app indicator state resolution and blinking behavior.

Copilot AI review requested due to automatic review settings August 1, 2026 04:24
@sourcery-ai

sourcery-ai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a Windows-specific system tray app indicator implementation, wires it into the build for Windows (including tests), and introduces a small configuration and test harness tweak to support the new functionality.

Sequence diagram for Windows tray interactions with the new app indicator

sequenceDiagram
    actor User
    participant WindowsShell
    participant IptuxAppIndicatorPrivate
    participant IptuxAppIndicator
    participant GActionGroup

    User->>WindowsShell: Click tray icon
    WindowsShell->>IptuxAppIndicatorPrivate: WndProc(kTrayCallbackMessage)
    IptuxAppIndicatorPrivate->>IptuxAppIndicator: sigActivateMainWindow.emit()

    User->>WindowsShell: Right-click tray icon
    WindowsShell->>IptuxAppIndicatorPrivate: WndProc(kTrayCallbackMessage)
    IptuxAppIndicatorPrivate->>IptuxAppIndicatorPrivate: ShowMenu()
    IptuxAppIndicatorPrivate->>WindowsShell: TrackPopupMenu(menu)

    User->>WindowsShell: Select menu item
    WindowsShell->>IptuxAppIndicatorPrivate: WndProc(WM_COMMAND)
    IptuxAppIndicatorPrivate->>GActionGroup: ActivateAction("open_main_window"/"preferences"/"quit")
Loading

File-Level Changes

Change Details Files
Introduce Windows tray app indicator implementation and icon state management utilities.
  • Add AppIndicatorWindows implementation that uses Win32 Shell_NotifyIcon and a hidden message-only window to manage the tray icon, callbacks, and popup menu.
  • Implement UTF-8-to-wide string conversion, themed icon loading from GTK icon theme, and conversion from GdkPixbuf to HICON for tray icons.
  • Manage blinking behavior, unread-count-driven visual state, and lifecycle of Win32 resources (window class, window handle, menu, icons) via a private helper class.
src/iptux/AppIndicatorWindows.cpp
Extract app indicator icon state logic into a reusable header and add unit tests for it.
  • Define AppIndicatorIconState enum and helper functions ShouldBlink and ResolveIconState based on status mode, unread count, and blink toggle.
  • Add GoogleTest-based tests that validate blinking conditions and the resolved icon states across NORMAL, NONE, and BLINKING modes.
src/iptux/AppIndicatorState.h
src/iptux/AppIndicatorStateTest.cpp
Extend Meson build configuration for Windows app indicator support and test harness changes.
  • Add SYSTEM_WINDOWS configuration flag alongside existing platform flags in src/meson.build.
  • Change CURRENT_SOURCE_PATH test configuration value to a raw string-like literal suitable for C++ tests.
  • Update iptux Meson build to link shell32 on Windows, compile AppIndicatorWindows.cpp and AppIndicatorWindows-specific source, and register AppIndicatorStateTest.cpp in test_sources.
  • Adjust platform-specific app indicator source selection to include Windows implementation before macOS/appindicator fallbacks.
src/meson.build
src/iptux/meson.build

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The Windows-specific logic in src/iptux/meson.build is now split across two separate if host_machine.system() == 'windows' blocks; consider consolidating these into a single block to keep platform-specific configuration easier to follow and maintain.
  • In IptuxAppIndicatorPrivate's destructor you unconditionally call UnregisterClassW(kTrayWindowClass, GetModuleHandleW(nullptr)); if multiple instances are ever created this will tear down the window class globally—consider registering/unregistering the class once (e.g., via a static guard) or only unregistering if this instance actually registered it.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The Windows-specific logic in `src/iptux/meson.build` is now split across two separate `if host_machine.system() == 'windows'` blocks; consider consolidating these into a single block to keep platform-specific configuration easier to follow and maintain.
- In `IptuxAppIndicatorPrivate`'s destructor you unconditionally call `UnregisterClassW(kTrayWindowClass, GetModuleHandleW(nullptr))`; if multiple instances are ever created this will tear down the window class globally—consider registering/unregistering the class once (e.g., via a static guard) or only unregistering if this instance actually registered it.

## Individual Comments

### Comment 1
<location path="src/meson.build" line_range="61-62" />
<code_context>

 test_conf_data = configuration_data()
-test_conf_data.set_quoted('CURRENT_SOURCE_PATH', meson.current_source_dir())
+test_conf_data.set(
+  'CURRENT_SOURCE_PATH',
+  'R"(' + meson.current_source_dir() + ')"',
+)
</code_context>
<issue_to_address>
**issue:** Raw string construction for CURRENT_SOURCE_PATH can break if the path ever contains the sequence ")".

This builds `CURRENT_SOURCE_PATH` as `"R"(" + meson.current_source_dir() + ")""`, which relies on the path never containing the raw-string terminator `" )"`. If that sequence appears in the directory name, the generated C++ literal is ill-formed and compilation will fail. Use a custom raw-string delimiter (e.g. `R"iptux(... )iptux"`) or another safe encoding instead of hard-coding `R"(` and `)"` around the path.
</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 thread src/meson.build Outdated
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Test Results

72 tests  +3   72 ✅ +3   2s ⏱️ -1s
33 suites +1    0 💤 ±0 
 1 files   ±0    0 ❌ ±0 

Results for commit af2d103. ± Comparison against base commit d232d08.

♻️ This comment has been updated with latest results.

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.53%. Comparing base (d232d08) to head (af2d103).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #735      +/-   ##
==========================================
+ Coverage   52.37%   52.53%   +0.15%     
==========================================
  Files          70       71       +1     
  Lines        8994     9004      +10     
==========================================
+ Hits         4711     4730      +19     
+ Misses       4283     4274       -9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Not ready to approve

It introduces a confirmed conversion error path in Utf8ToWide() and the implemented “centralized” status-icon logic currently only applies to Windows, diverging from the PR’s stated scope.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Adds Windows system-tray (status icon) support and introduces a small utility for consistent status-icon state resolution, along with build/config updates to enable this on Windows.

Changes:

  • Add a Windows-specific IptuxAppIndicator implementation using Shell_NotifyIconW and a Win32 popup menu.
  • Introduce AppIndicatorState helpers (ShouldBlink, ResolveIconState) with a new unit test.
  • Update Meson/config to expose SYSTEM_WINDOWS, enable HAVE_STATUS_ICON on Windows, link shell32, and adjust CURRENT_SOURCE_PATH quoting for tests.
File summaries
File Description
src/meson.build Adds SYSTEM_WINDOWS define and adjusts CURRENT_SOURCE_PATH configuration for tests.
src/iptux/meson.build Links shell32 on Windows, compiles AppIndicatorWindows.cpp, and adds AppIndicatorStateTest.cpp.
src/iptux/AppIndicatorWindows.cpp New Win32 tray icon implementation (menu, icon loading, blink timer).
src/iptux/AppIndicatorStateTest.cpp Unit tests for blink/state resolution logic.
src/iptux/AppIndicatorState.h New helper API for determining icon state and blink behavior.
src/config.h.in Adds SYSTEM_WINDOWS and enables HAVE_STATUS_ICON when building for Windows.
Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 2
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +38 to +43
std::wstring result(static_cast<size_t>(len), L'\0');
MultiByteToWideChar(CP_UTF8, 0, text, -1, result.data(), len);
if (!result.empty() && result.back() == L'\0') {
result.pop_back();
}
return result;
Comment on lines +14 to +20
inline bool ShouldBlink(StatusIconMode mode, int unread_count) {
return mode == STATUS_ICON_MODE_BLINKING && unread_count > 0;
}

inline AppIndicatorIconState ResolveIconState(StatusIconMode mode,
int unread_count,
bool blink_state) {
Copilot AI review requested due to automatic review settings August 1, 2026 04:28
@sonarqubecloud

sonarqubecloud Bot commented Aug 1, 2026

Copy link
Copy Markdown

❌ The last analysis has failed.

See analysis details on SonarQube Cloud

@lidaobing lidaobing changed the title 1 Add StatusIcon for Windows Aug 1, 2026
@lidaobing
lidaobing merged commit f47c73f into main Aug 1, 2026
20 of 23 checks passed
@lidaobing
lidaobing deleted the bf_win_appindicator branch August 1, 2026 04:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Not ready to approve

There are confirmed build/runtime correctness issues (case-sensitive source filename mismatch in Meson and an invalid Win32 ReleaseDC call path) that should be fixed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (1)

src/iptux/AppIndicatorWindows.cpp:81

  • ReleaseDC is called unconditionally, but GetDC can return NULL; calling ReleaseDC with a NULL HDC is invalid. Guard the ReleaseDC call (or avoid acquiring an HDC at all) to prevent undefined behavior on failure paths.
  ReleaseDC(nullptr, hdc);
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread src/iptux/meson.build
dependencies += [ws2_32]
shell32 = cc.find_library('shell32')
dependencies += [ws2_32, shell32]
sources += ['UiHelperWindows.cpp']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants