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