33#include < glib.h>
44#include < gtk/gtk.h>
55#include < gdk-pixbuf/gdk-pixbuf.h>
6+ #include < libayatana-appindicator/app-indicator.h>
67#include " ../../menu.h"
78#include " ../../tray_icon.h"
89#include " ../../tray_icon_event.h"
@@ -12,41 +13,42 @@ namespace nativeapi {
1213// Private implementation class
1314class TrayIcon ::Impl {
1415 public:
15- Impl (GtkStatusIcon* tray ) : gtk_status_icon_(tray ), title_(" " ), tooltip_(" " ), context_menu_(nullptr ) {}
16+ Impl (AppIndicator* indicator ) : app_indicator_(indicator ), title_(" " ), tooltip_(" " ), context_menu_(nullptr ), visible_( false ) {}
1617
17- GtkStatusIcon* gtk_status_icon_ ;
18+ AppIndicator* app_indicator_ ;
1819 std::shared_ptr<Menu> context_menu_; // Store menu shared_ptr to keep it alive
19- std::string title_; // GTK StatusIcon doesn't have title, so we store it
20+ std::string title_;
2021 std::string tooltip_;
22+ bool visible_;
2123};
2224
2325TrayIcon::TrayIcon () : pimpl_(std::make_unique<Impl>(nullptr )) {
2426 id = -1 ;
2527}
2628
27- TrayIcon::TrayIcon (void * tray) : pimpl_(std::make_unique<Impl>((GtkStatusIcon *)tray)) {
29+ TrayIcon::TrayIcon (void * tray) : pimpl_(std::make_unique<Impl>((AppIndicator *)tray)) {
2830 id = -1 ; // Will be set by TrayManager when created
29- // Make the status icon visible
30- if (pimpl_->gtk_status_icon_ ) {
31- gtk_status_icon_set_visible ( pimpl_->gtk_status_icon_ , TRUE ) ;
31+ // Make the indicator visible by default
32+ if (pimpl_->app_indicator_ ) {
33+ pimpl_->visible_ = true ;
3234 }
3335}
3436
3537TrayIcon::~TrayIcon () {
36- if (pimpl_->gtk_status_icon_ ) {
37- g_object_unref (pimpl_->gtk_status_icon_ );
38+ if (pimpl_->app_indicator_ ) {
39+ g_object_unref (pimpl_->app_indicator_ );
3840 }
39-
4041}
4142
4243void TrayIcon::SetIcon (std::string icon) {
43- if (!pimpl_->gtk_status_icon_ ) {
44+ if (!pimpl_->app_indicator_ ) {
4445 return ;
4546 }
4647
4748 // Check if the icon is a base64 string
4849 if (icon.find (" data:image" ) != std::string::npos) {
49- // Extract the base64 part
50+ // For base64 images, we need to save them to a temporary file
51+ // since AppIndicator expects file paths or stock icon names
5052 size_t pos = icon.find (" base64," );
5153 if (pos != std::string::npos) {
5254 std::string base64Icon = icon.substr (pos + 7 );
@@ -56,45 +58,40 @@ void TrayIcon::SetIcon(std::string icon) {
5658 guchar* decoded_data = g_base64_decode (base64Icon.c_str (), &decoded_len);
5759
5860 if (decoded_data) {
59- // Create pixbuf from decoded data
60- GInputStream* stream = g_memory_input_stream_new_from_data (
61- decoded_data, decoded_len, g_free);
61+ // Create a temporary file path
62+ const char * temp_dir = g_get_tmp_dir ();
63+ std::string temp_path = std::string (temp_dir) + " /nativeapi_tray_icon_" + std::to_string (id) + " .png" ;
64+
65+ // Write to file
6266 GError* error = nullptr ;
63- GdkPixbuf* pixbuf = gdk_pixbuf_new_from_stream (stream, nullptr , &error);
64-
65- if (pixbuf && !error) {
66- // Scale pixbuf to appropriate size (24x24 is common for tray icons)
67- GdkPixbuf* scaled_pixbuf = gdk_pixbuf_scale_simple (
68- pixbuf, 24 , 24 , GDK_INTERP_BILINEAR);
69-
70- gtk_status_icon_set_from_pixbuf (pimpl_->gtk_status_icon_ , scaled_pixbuf);
71-
72- g_object_unref (scaled_pixbuf);
73- g_object_unref (pixbuf);
67+ if (g_file_set_contents (temp_path.c_str (), (const gchar*)decoded_data, decoded_len, &error)) {
68+ app_indicator_set_icon_full (pimpl_->app_indicator_ , temp_path.c_str (), " Tray Icon" );
7469 } else if (error) {
75- std::cerr << " Error loading icon from base64 : " << error->message << std::endl;
70+ std::cerr << " Error saving icon to temp file : " << error->message << std::endl;
7671 g_error_free (error);
7772 }
7873
79- g_object_unref (stream );
74+ g_free (decoded_data );
8075 }
8176 }
8277 } else {
8378 // Use the icon as a file path or stock icon name
8479 if (g_file_test (icon.c_str (), G_FILE_TEST_EXISTS)) {
8580 // It's a file path
86- gtk_status_icon_set_from_file (pimpl_->gtk_status_icon_ , icon.c_str ());
81+ app_indicator_set_icon_full (pimpl_->app_indicator_ , icon.c_str (), " Tray Icon " );
8782 } else {
8883 // Try as a stock icon name
89- gtk_status_icon_set_from_icon_name (pimpl_->gtk_status_icon_ , icon.c_str ());
84+ app_indicator_set_icon_full (pimpl_->app_indicator_ , icon.c_str (), " Tray Icon " );
9085 }
9186 }
9287}
9388
9489void TrayIcon::SetTitle (std::string title) {
9590 pimpl_->title_ = title;
96- // GTK StatusIcon doesn't support title directly, so we just store it
97- // Some desktop environments might show this in tooltips or context
91+ // AppIndicator uses the title as the accessible name and in some desktop environments
92+ if (pimpl_->app_indicator_ ) {
93+ app_indicator_set_title (pimpl_->app_indicator_ , title.c_str ());
94+ }
9895}
9996
10097std::string TrayIcon::GetTitle () {
@@ -103,9 +100,9 @@ std::string TrayIcon::GetTitle() {
103100
104101void TrayIcon::SetTooltip (std::string tooltip) {
105102 pimpl_->tooltip_ = tooltip;
106- if (pimpl_-> gtk_status_icon_ ) {
107- gtk_status_icon_set_tooltip_text (pimpl_-> gtk_status_icon_ , tooltip. c_str ());
108- }
103+ // AppIndicator doesn't have direct tooltip support like GtkStatusIcon
104+ // The tooltip functionality is typically handled through the title
105+ // or through custom menu items. We'll store it for potential future use.
109106}
110107
111108std::string TrayIcon::GetTooltip () {
@@ -116,8 +113,11 @@ void TrayIcon::SetContextMenu(std::shared_ptr<Menu> menu) {
116113 // Store the menu shared_ptr to keep it alive
117114 pimpl_->context_menu_ = menu;
118115
119- // Note: Full GTK integration would need to connect popup-menu signal
120- // and show the GTK menu from the Menu object's GetNativeObject()
116+ // AppIndicator requires a menu to be set
117+ if (pimpl_->app_indicator_ && menu && menu->GetNativeObject ()) {
118+ GtkMenu* gtk_menu = static_cast <GtkMenu*>(menu->GetNativeObject ());
119+ app_indicator_set_menu (pimpl_->app_indicator_ , gtk_menu);
120+ }
121121}
122122
123123std::shared_ptr<Menu> TrayIcon::GetContextMenu () {
@@ -127,41 +127,37 @@ std::shared_ptr<Menu> TrayIcon::GetContextMenu() {
127127Rectangle TrayIcon::GetBounds () {
128128 Rectangle bounds = {0 , 0 , 0 , 0 };
129129
130- if (pimpl_->gtk_status_icon_ ) {
131- GdkScreen* screen;
132- GdkRectangle area;
133- GtkOrientation orientation;
134-
135- if (gtk_status_icon_get_geometry (pimpl_->gtk_status_icon_ , &screen, &area, &orientation)) {
136- bounds.x = area.x ;
137- bounds.y = area.y ;
138- bounds.width = area.width ;
139- bounds.height = area.height ;
140- }
141- }
130+ // AppIndicator doesn't provide geometry information like GtkStatusIcon did
131+ // This is a limitation of the AppIndicator API as it's handled by the
132+ // system tray implementation. We return empty bounds.
133+ // In most modern desktop environments, this information isn't available
134+ // to applications for security reasons.
142135
143136 return bounds;
144137}
145138
146139bool TrayIcon::Show () {
147- if (pimpl_->gtk_status_icon_ ) {
148- gtk_status_icon_set_visible (pimpl_->gtk_status_icon_ , TRUE );
140+ if (pimpl_->app_indicator_ ) {
141+ app_indicator_set_status (pimpl_->app_indicator_ , APP_INDICATOR_STATUS_ACTIVE);
142+ pimpl_->visible_ = true ;
149143 return true ;
150144 }
151145 return false ;
152146}
153147
154148bool TrayIcon::Hide () {
155- if (pimpl_->gtk_status_icon_ ) {
156- gtk_status_icon_set_visible (pimpl_->gtk_status_icon_ , FALSE );
149+ if (pimpl_->app_indicator_ ) {
150+ app_indicator_set_status (pimpl_->app_indicator_ , APP_INDICATOR_STATUS_PASSIVE);
151+ pimpl_->visible_ = false ;
157152 return true ;
158153 }
159154 return false ;
160155}
161156
162157bool TrayIcon::IsVisible () {
163- if (pimpl_->gtk_status_icon_ ) {
164- return gtk_status_icon_get_visible (pimpl_->gtk_status_icon_ ) == TRUE ;
158+ if (pimpl_->app_indicator_ ) {
159+ AppIndicatorStatus status = app_indicator_get_status (pimpl_->app_indicator_ );
160+ return status == APP_INDICATOR_STATUS_ACTIVE;
165161 }
166162 return false ;
167163}
@@ -171,19 +167,20 @@ bool TrayIcon::ShowContextMenu(double x, double y) {
171167 return false ;
172168 }
173169
174- // Note: GTK implementation would need to show the menu at the specified coordinates
175- // This is a simplified implementation
176- return false ;
170+ // AppIndicator shows context menu automatically on right-click
171+ // We don't need to manually show it at specific coordinates
172+ // The menu is managed by the indicator framework
173+ return true ;
177174}
178175
179176bool TrayIcon::ShowContextMenu () {
180177 if (!pimpl_->context_menu_ || !pimpl_->context_menu_ ->GetNativeObject ()) {
181178 return false ;
182179 }
183180
184- // Note: GTK implementation would need to show the menu at cursor position
185- // This is a simplified implementation
186- return false ;
181+ // AppIndicator shows context menu automatically on right-click
182+ // We don't need to manually show it as it's managed by the indicator framework
183+ return true ;
187184}
188185
189186// Internal method to handle click events
0 commit comments