1+ #include < iostream>
2+ #include < string>
3+ #include < glib.h>
4+ #include < gtk/gtk.h>
5+ #include < gdk-pixbuf/gdk-pixbuf.h>
6+ #include " ../../menu.h"
7+ #include " ../../tray.h"
8+
9+ namespace nativeapi {
10+
11+ // Private implementation class
12+ class Tray ::Impl {
13+ public:
14+ Impl (GtkStatusIcon* tray) : gtk_status_icon_(tray), title_(" " ), tooltip_(" " ) {}
15+
16+ GtkStatusIcon* gtk_status_icon_;
17+ Menu context_menu_; // Store menu object to keep it alive
18+ std::string title_; // GTK StatusIcon doesn't have title, so we store it
19+ std::string tooltip_;
20+ };
21+
22+ Tray::Tray () : pimpl_(new Impl(nullptr )) {
23+ id = -1 ;
24+ }
25+
26+ Tray::Tray (void * tray) : pimpl_(new Impl((GtkStatusIcon*)tray)) {
27+ id = -1 ; // Will be set by TrayManager when created
28+ // Make the status icon visible
29+ if (pimpl_->gtk_status_icon_ ) {
30+ gtk_status_icon_set_visible (pimpl_->gtk_status_icon_ , TRUE );
31+ }
32+ }
33+
34+ Tray::~Tray () {
35+ if (pimpl_->gtk_status_icon_ ) {
36+ g_object_unref (pimpl_->gtk_status_icon_ );
37+ }
38+ delete pimpl_;
39+ }
40+
41+ void Tray::SetIcon (std::string icon) {
42+ if (!pimpl_->gtk_status_icon_ ) {
43+ return ;
44+ }
45+
46+ // Check if the icon is a base64 string
47+ if (icon.find (" data:image" ) != std::string::npos) {
48+ // Extract the base64 part
49+ size_t pos = icon.find (" base64," );
50+ if (pos != std::string::npos) {
51+ std::string base64Icon = icon.substr (pos + 7 );
52+
53+ // Decode base64 data
54+ gsize decoded_len;
55+ guchar* decoded_data = g_base64_decode (base64Icon.c_str (), &decoded_len);
56+
57+ if (decoded_data) {
58+ // Create pixbuf from decoded data
59+ GInputStream* stream = g_memory_input_stream_new_from_data (
60+ decoded_data, decoded_len, g_free);
61+ GError* error = nullptr ;
62+ GdkPixbuf* pixbuf = gdk_pixbuf_new_from_stream (stream, nullptr , &error);
63+
64+ if (pixbuf && !error) {
65+ // Scale pixbuf to appropriate size (24x24 is common for tray icons)
66+ GdkPixbuf* scaled_pixbuf = gdk_pixbuf_scale_simple (
67+ pixbuf, 24 , 24 , GDK_INTERP_BILINEAR);
68+
69+ gtk_status_icon_set_from_pixbuf (pimpl_->gtk_status_icon_ , scaled_pixbuf);
70+
71+ g_object_unref (scaled_pixbuf);
72+ g_object_unref (pixbuf);
73+ } else if (error) {
74+ std::cerr << " Error loading icon from base64: " << error->message << std::endl;
75+ g_error_free (error);
76+ }
77+
78+ g_object_unref (stream);
79+ }
80+ }
81+ } else {
82+ // Use the icon as a file path or stock icon name
83+ if (g_file_test (icon.c_str (), G_FILE_TEST_EXISTS)) {
84+ // It's a file path
85+ gtk_status_icon_set_from_file (pimpl_->gtk_status_icon_ , icon.c_str ());
86+ } else {
87+ // Try as a stock icon name
88+ gtk_status_icon_set_from_icon_name (pimpl_->gtk_status_icon_ , icon.c_str ());
89+ }
90+ }
91+ }
92+
93+ void Tray::SetTitle (std::string title) {
94+ pimpl_->title_ = title;
95+ // GTK StatusIcon doesn't support title directly, so we just store it
96+ // Some desktop environments might show this in tooltips or context
97+ }
98+
99+ std::string Tray::GetTitle () {
100+ return pimpl_->title_ ;
101+ }
102+
103+ void Tray::SetTooltip (std::string tooltip) {
104+ pimpl_->tooltip_ = tooltip;
105+ if (pimpl_->gtk_status_icon_ ) {
106+ gtk_status_icon_set_tooltip_text (pimpl_->gtk_status_icon_ , tooltip.c_str ());
107+ }
108+ }
109+
110+ std::string Tray::GetTooltip () {
111+ return pimpl_->tooltip_ ;
112+ }
113+
114+ void Tray::SetContextMenu (Menu menu) {
115+ // Store the menu object to keep it alive
116+ pimpl_->context_menu_ = menu;
117+
118+ // Note: Full GTK integration would need to connect popup-menu signal
119+ // and show the GTK menu from the Menu object's GetNativeMenu()
120+ }
121+
122+ Menu Tray::GetContextMenu () {
123+ return pimpl_->context_menu_ ;
124+ }
125+
126+ Rectangle Tray::GetBounds () {
127+ Rectangle bounds = {0 , 0 , 0 , 0 };
128+
129+ if (pimpl_->gtk_status_icon_ ) {
130+ GdkScreen* screen;
131+ GdkRectangle area;
132+ GtkOrientation orientation;
133+
134+ if (gtk_status_icon_get_geometry (pimpl_->gtk_status_icon_ , &screen, &area, &orientation)) {
135+ bounds.x = area.x ;
136+ bounds.y = area.y ;
137+ bounds.width = area.width ;
138+ bounds.height = area.height ;
139+ }
140+ }
141+
142+ return bounds;
143+ }
144+
145+ } // namespace nativeapi
0 commit comments