11package org .cryptomator .linux .tray ;
22
3- import org .apache .commons .lang3 .SystemUtils ;
43import org .cryptomator .integrations .common .CheckAvailability ;
4+ import org .cryptomator .integrations .common .OperatingSystem ;
5+ import org .cryptomator .integrations .common .Priority ;
56import org .cryptomator .integrations .tray .ActionItem ;
67import org .cryptomator .integrations .tray .SeparatorItem ;
78import org .cryptomator .integrations .tray .SubMenuItem ;
9+ import org .cryptomator .integrations .tray .TrayIconLoader ;
810import org .cryptomator .integrations .tray .TrayMenuController ;
911import org .cryptomator .integrations .tray .TrayMenuException ;
1012import org .cryptomator .integrations .tray .TrayMenuItem ;
1315import org .slf4j .LoggerFactory ;
1416
1517import java .io .File ;
18+ import java .lang .foreign .Arena ;
1619import java .lang .foreign .MemorySegment ;
17- import java .lang .foreign .SegmentScope ;
1820import java .net .URI ;
1921import java .net .URISyntaxException ;
2022import java .nio .file .Paths ;
2123import java .util .List ;
24+ import java .util .function .Consumer ;
2225
2326import static org .purejava .appindicator .app_indicator_h .*;
2427
25- @ CheckAvailability
28+ @ Priority (1000 )
29+ @ OperatingSystem (OperatingSystem .Value .LINUX )
2630public class AppindicatorTrayMenuController implements TrayMenuController {
2731
2832 private static final Logger LOG = LoggerFactory .getLogger (AppindicatorTrayMenuController .class );
2933
30- private final SegmentScope scope = SegmentScope . auto ();
34+ private static final Arena ARENA = Arena . openShared ();
3135 private MemorySegment indicator ;
3236 private MemorySegment menu = gtk_menu_new ();
3337
3438 @ CheckAvailability
3539 public static boolean isAvailable () {
36- return SystemUtils . IS_OS_LINUX && MemoryAllocator .isLoadedNativeLib ();
40+ return MemoryAllocator .isLoadedNativeLib ();
3741 }
3842
3943 @ Override
@@ -47,8 +51,13 @@ public void showTrayIcon(URI uri, Runnable runnable, String s) throws TrayMenuEx
4751 }
4852
4953 @ Override
50- public void updateTrayIcon (URI uri ) {
51- app_indicator_set_icon (indicator , MemoryAllocator .ALLOCATE_FOR (getAbsolutePath (getPathString (uri ))));
54+ public void updateTrayIcon (Consumer <TrayIconLoader > iconLoader ) {
55+ TrayIconLoader .FreedesktopIconName callback = this ::updateTrayIconCallback ;
56+ iconLoader .load (callback );
57+ }
58+
59+ private void updateTrayIconCallback (String s ) {
60+ app_indicator_set_icon (indicator , MemoryAllocator .ALLOCATE_FOR (s ));
5261 }
5362
5463 @ Override
@@ -66,30 +75,34 @@ public void onBeforeOpenMenu(Runnable runnable) {
6675
6776 private void addChildren (MemorySegment menu , List <TrayMenuItem > items ) {
6877 for (var item : items ) {
69- // TODO: use Pattern Matching for switch, once available
70- if (item instanceof ActionItem a ) {
71- var gtkMenuItem = gtk_menu_item_new ();
72- gtk_menu_item_set_label (gtkMenuItem , MemoryAllocator .ALLOCATE_FOR (a .title ()));
73- g_signal_connect_object (gtkMenuItem ,
74- MemoryAllocator .ALLOCATE_FOR ("activate" ),
75- MemoryAllocator .ALLOCATE_CALLBACK_FOR (new ActionItemCallback (a ), scope ),
76- menu ,
77- 0 );
78- gtk_menu_shell_append (menu , gtkMenuItem );
79- } else if (item instanceof SeparatorItem ) {
80- var gtkSeparator = gtk_menu_item_new ();
81- gtk_menu_shell_append (menu , gtkSeparator );
82- } else if (item instanceof SubMenuItem s ) {
83- var gtkMenuItem = gtk_menu_item_new ();
84- var gtkSubmenu = gtk_menu_new ();
85- gtk_menu_item_set_label (gtkMenuItem , MemoryAllocator .ALLOCATE_FOR (s .title ()));
86- addChildren (gtkSubmenu , s .items ());
87- gtk_menu_item_set_submenu (gtkMenuItem , gtkSubmenu );
88- gtk_menu_shell_append (menu , gtkMenuItem );
78+ switch (item ) {
79+ case ActionItem a -> {
80+ var gtkMenuItem = gtk_menu_item_new ();
81+ gtk_menu_item_set_label (gtkMenuItem , MemoryAllocator .ALLOCATE_FOR (a .title ()));
82+ g_signal_connect_object (gtkMenuItem ,
83+ MemoryAllocator .ALLOCATE_FOR ("activate" ),
84+ MemoryAllocator .ALLOCATE_CALLBACK_FOR (new ActionItemCallback (a ), ARENA .scope ()),
85+ menu ,
86+ 0 );
87+ gtk_menu_shell_append (menu , gtkMenuItem );
88+ }
89+ case SeparatorItem separatorItem -> {
90+ var gtkSeparator = gtk_menu_item_new ();
91+ gtk_menu_shell_append (menu , gtkSeparator );
92+ }
93+ case SubMenuItem s -> {
94+ var gtkMenuItem = gtk_menu_item_new ();
95+ var gtkSubmenu = gtk_menu_new ();
96+ gtk_menu_item_set_label (gtkMenuItem , MemoryAllocator .ALLOCATE_FOR (s .title ()));
97+ addChildren (gtkSubmenu , s .items ());
98+ gtk_menu_item_set_submenu (gtkMenuItem , gtkSubmenu );
99+ gtk_menu_shell_append (menu , gtkMenuItem );
100+ }
89101 }
90102 gtk_widget_show_all (menu );
91103 }
92104 }
105+
93106 private String getAbsolutePath (String iconName ) {
94107 var res = getClass ().getClassLoader ().getResource (iconName );
95108 if (null == res ) {
0 commit comments