Skip to content

Add macOS menu bar status item (NSStatusItem) with system tray icon#712

Merged
lidaobing merged 3 commits into
mainfrom
bf_mac_indicator
Jan 29, 2026
Merged

Add macOS menu bar status item (NSStatusItem) with system tray icon#712
lidaobing merged 3 commits into
mainfrom
bf_mac_indicator

Conversation

@lidaobing

@lidaobing lidaobing commented Jan 29, 2026

Copy link
Copy Markdown
Member

Summary by Sourcery

Add native macOS menu bar status item implementation and integrate Objective-C++ support into the build.

New Features:

  • Introduce a macOS-specific app indicator implementation using NSStatusItem with menu actions wired to existing GActionGroup commands.

Enhancements:

  • Enable Objective-C++ compilation and Cocoa framework integration on macOS in the Meson build, including GLib version requirement configuration and consistent assertion flags for C++ and Objective-C++ sources.

@sourcery-ai

sourcery-ai Bot commented Jan 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds macOS-specific support for the application tray indicator using a new Objective‑C++ implementation, updates the Meson build to enable ObjC++ on macOS, and wires the new status item implementation into the iptux target as a Cocoa-based alternative to libappindicator.

Sequence diagram for macOS status item menu activation

sequenceDiagram
  actor User
  participant NSStatusItem_button as NSStatusItemButton
  participant IptuxStatusItemHelper as StatusItemHelper
  participant GActionGroup as ActionGroup
  participant IptuxApp as IptuxApplication

  User ->> NSStatusItem_button: Click menu item
  NSStatusItem_button ->> IptuxStatusItemHelper: send openMainWindow
  activate IptuxStatusItemHelper
  IptuxStatusItemHelper ->> ActionGroup: g_action_group_activate_action("open_main_window", NULL)
  activate ActionGroup
  ActionGroup ->> IptuxApp: Dispatch action open_main_window
  deactivate ActionGroup
  deactivate IptuxStatusItemHelper

  User ->> NSStatusItem_button: Click Preferences
  NSStatusItem_button ->> IptuxStatusItemHelper: send openPreferences
  IptuxStatusItemHelper ->> ActionGroup: g_action_group_activate_action("preferences", NULL)
  ActionGroup ->> IptuxApp: Dispatch action preferences

  User ->> NSStatusItem_button: Click Quit
  NSStatusItem_button ->> IptuxStatusItemHelper: send quit
  IptuxStatusItemHelper ->> ActionGroup: g_action_group_activate_action("quit", NULL)
  ActionGroup ->> IptuxApp: Dispatch action quit
Loading

Class diagram for the new macOS tray indicator implementation

classDiagram
  class IptuxStatusItemHelper {
    - GActionGroup* actionGroup_
    - iptux::IptuxAppIndicator* owner_
    + IptuxStatusItemHelper initWithActionGroup_actionGroup_owner_owner(GActionGroup* actionGroup, iptux::IptuxAppIndicator* owner)
    + void openMainWindow(id sender)
    + void openPreferences(id sender)
    + void quit(id sender)
    + void statusItemClicked(id sender)
  }

  class IptuxAppIndicatorPrivate {
    + IptuxAppIndicatorPrivate()
    + ~IptuxAppIndicatorPrivate()
    + NSStatusItem* statusItem
    + IptuxStatusItemHelper* helper
    + NSImage* normalIcon
    + NSImage* attentionIcon
  }

  class IptuxAppIndicator {
    - std::shared_ptr~IptuxAppIndicatorPrivate~ priv
    + IptuxAppIndicator(GActionGroup* action_group)
    + void SetUnreadCount(int count)
  }

  class NSStatusItem {
  }

  class NSImage {
  }

  class NSMenu {
  }

  class GActionGroup {
    + void g_action_group_activate_action(GActionGroup* action_group, const char* action_name, GVariant* parameter)
  }

  IptuxAppIndicator o-- IptuxAppIndicatorPrivate : owns
  IptuxAppIndicatorPrivate *-- NSStatusItem : uses
  IptuxAppIndicatorPrivate *-- IptuxStatusItemHelper : uses
  IptuxAppIndicatorPrivate *-- NSImage : uses
  IptuxAppIndicator ..> NSMenu : uses
  IptuxStatusItemHelper ..> GActionGroup : triggers_actions
  IptuxStatusItemHelper ..> IptuxAppIndicator : calls_owner_signals
Loading

Flow diagram for platform-specific app indicator implementation selection

flowchart TD
  A[Build on host_machine] --> B{host_machine.system == darwin}

  B -->|true| C[Enable ObjC++ language<br>add_languages objcpp]
  C --> D[Add GLib min version define<br>-DGLIB_VERSION_MIN_REQUIRED]
  D --> E{get_option werror}
  E -->|true| F[Add -U_LIBCPP_ENABLE_ASSERTIONS for cpp]
  F --> G[Add -U_LIBCPP_ENABLE_ASSERTIONS for objcpp]
  E -->|false| H[No extra warning flags]

  B -->|false| I[Skip ObjC++ setup]

  subgraph iptux_target_sources
    J[Base C++ sources]
    K{host_machine.system == darwin}
    K -->|true| L[Add AppIndicatorMac.mm]
    L --> M[Add dependency appleframeworks Cocoa]
    K -->|false| N{appindicator_dep.found}
    N -->|true| O[Add AppIndicator.cpp<br>Add dependency appindicator_dep]
    N -->|false| P[Build without tray indicator]
  end
Loading

File-Level Changes

Change Details Files
Enable Objective-C++ compilation and macOS-specific flags in the Meson build.
  • Always add Objective-C++ as a project language when building on macOS.
  • Define GLIB_VERSION_MIN_REQUIRED for Objective-C++ compilation on macOS to 2.70.
  • Extend the existing werror-based workaround for libc++ assertions to also apply to Objective-C++ sources on macOS while keeping the C++ behavior.
meson.build
Wire up a macOS-specific AppIndicator implementation into the iptux target.
  • On macOS, include a new Objective-C++ source file AppIndicatorMac.mm instead of the existing AppIndicator.cpp implementation.
  • On macOS, add a dependency on the Cocoa framework via Meson’s appleframeworks dependency.
  • Fall back to the existing libappindicator-based implementation on non-macOS platforms when the dependency is found, preserving the previous behavior elsewhere.
src/iptux/meson.build
Implement a Cocoa-based status bar item for macOS that integrates with the existing IptuxAppIndicator interface.
  • Introduce an Objective-C helper class IptuxStatusItemHelper that bridges NSStatusItem callbacks to GLib GActionGroup actions and signals on IptuxAppIndicator.
  • Add helper functions to convert GdkPixbuf icons into NSImage objects and load them from the current GTK icon theme, including a resize suitable for the macOS menu bar.
  • Provide a macOS-specific implementation of IptuxAppIndicator that creates and manages an NSStatusItem, builds a localized NSMenu for main window, preferences, and quit actions, switches icons based on unread count, and performs proper Cocoa object lifetime management in its private data class.
src/iptux/AppIndicatorMac.mm

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

@lidaobing lidaobing requested a review from Copilot January 29, 2026 05:24

@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 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/iptux/AppIndicatorMac.mm:144-145` </location>
<code_context>
+  priv->attentionIcon = loadIcon("iptux-attention", 64);
+
+  // Create status item
+  priv->statusItem =
+      [[[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength] retain];
+
+  if (priv->normalIcon) {
</code_context>

<issue_to_address>
**issue (bug_risk):** Potential over-retain of NSStatusItem leads to a leak.

`-[NSStatusBar statusItemWithLength:]` already returns a retained `NSStatusItem`, so the extra `retain` here over-retains it. The destructor only does one `[statusItem release]`, leaving the status item leaked.

Remove the explicit `retain`:

```objc
priv->statusItem = [[NSStatusBar systemStatusBar]
    statusItemWithLength:NSVariableStatusItemLength];
```

The existing release in the private dtor will then correctly balance the retain.
</issue_to_address>

### Comment 2
<location> `src/iptux/AppIndicatorMac.mm:90-96` </location>
<code_context>
+  return [image autorelease];
+}
+
+static NSImage* loadIcon(const char* iconName, int size) {
+  auto theme = gtk_icon_theme_get_default();
+  GError* error = nullptr;
+  auto pixbuf = gtk_icon_theme_load_icon(theme, iconName, size,
+                                         GtkIconLookupFlags(0), &error);
+  if (!pixbuf) {
+    g_warning("Couldn't load icon %s: %s", iconName, error->message);
+    g_error_free(error);
+    return nil;
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Error handling in loadIcon assumes error is always set when pixbuf is null.

`gtk_icon_theme_load_icon` is documented to set `error` on failure, but for robustness we shouldn't assume it’s always non-null before using `error->message`. A defensive pattern would be:

```cpp
if (!pixbuf) {
  const char *msg = (error && error->message) ? error->message : "unknown error";
  g_warning("Couldn't load icon %s: %s", iconName, msg);
  if (error) g_error_free(error);
  return nil;
}
```

```suggestion
  auto pixbuf = gtk_icon_theme_load_icon(theme, iconName, size,
                                         GtkIconLookupFlags(0), &error);
  if (!pixbuf) {
    const char* msg = (error && error->message) ? error->message : "unknown error";
    g_warning("Couldn't load icon %s: %s", iconName, msg);
    if (error) {
      g_error_free(error);
    }
    return nil;
  }
```
</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/iptux/AppIndicatorMac.mm
Comment thread src/iptux/AppIndicatorMac.mm
@github-actions

github-actions Bot commented Jan 29, 2026

Copy link
Copy Markdown

Test Results

69 tests  ±0   69 ✅ ±0   3s ⏱️ -1s
32 suites ±0    0 💤 ±0 
 1 files   ±0    0 ❌ ±0 

Results for commit 63ea050. ± Comparison against base commit 8dbae1a.

♻️ This comment has been updated with latest results.

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.

Pull request overview

Adds a native macOS menu bar status item implementation and updates the Meson build to support Objective-C++ and Cocoa framework linking on macOS.

Changes:

  • Add AppIndicatorMac.mm implementing a macOS NSStatusItem-based app indicator with menu actions mapped to existing GActionGroup commands.
  • Update Meson build files to compile Objective-C++ on macOS and link against the Cocoa framework.
  • Extend macOS build flags to apply the GLib version requirement and libc++ assertion behavior consistently across C++ and Objective-C++.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
meson.build Enables Objective-C++ on macOS and aligns build flags for objcpp sources.
src/iptux/meson.build Adds macOS-specific app indicator source and links Cocoa framework when building on darwin.
src/iptux/AppIndicatorMac.mm Implements the macOS menu bar status item and menu actions for app control.

Comment on lines +50 to +53
}

- (void)statusItemClicked:(id)sender {
(void)sender;

Copilot AI Jan 29, 2026

Copy link

Choose a reason for hiding this comment

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

statusItemClicked: is currently dead code: it isn’t wired to the NSStatusItem button (no target/action assignment), so this callback (and the owner_ field it depends on) will never be invoked. Either hook this up via the status item button’s target/action if you want click-to-open behavior, or remove the unused method/field to avoid confusion.

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +146

// Create status item
priv->statusItem =

Copilot AI Jan 29, 2026

Copy link

Choose a reason for hiding this comment

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

This Objective-C++ file doesn’t appear to follow the project’s clang-format style (e.g., very long lines here). Please run clang-format on AppIndicatorMac.mm so it matches the existing formatting seen in src/iptux/AppIndicator.cpp:36-56 and the rest of the codebase.

Copilot uses AI. Check for mistakes.
@codecov

codecov Bot commented Jan 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 51.99%. Comparing base (8dbae1a) to head (63ea050).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #712   +/-   ##
=======================================
  Coverage   51.99%   51.99%           
=======================================
  Files          64       64           
  Lines        8601     8601           
=======================================
  Hits         4472     4472           
  Misses       4129     4129           

☔ View full report in Codecov by Sentry.
📢 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.

@lidaobing lidaobing changed the title 1 Add macOS menu bar status item (NSStatusItem) with system tray icon Jan 29, 2026
@lidaobing lidaobing merged commit be163bb into main Jan 29, 2026
19 of 20 checks passed
@lidaobing lidaobing deleted the bf_mac_indicator branch January 29, 2026 05:36
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