1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

examples: add macos_tray example app (#15101)

This commit is contained in:
Zhomart Mukhamejanov 2022-07-17 23:42:45 -07:00 committed by GitHub
parent 5462d4aebf
commit c1502b3c1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 206 additions and 0 deletions

View File

@ -198,6 +198,9 @@ pub fn new_test_session(_vargs string, will_compile bool) TestSession {
skip_files << 'examples/sokol/sounds/wav_player.v' skip_files << 'examples/sokol/sounds/wav_player.v'
skip_files << 'examples/sokol/sounds/simple_sin_tones.v' skip_files << 'examples/sokol/sounds/simple_sin_tones.v'
} }
$if !macos {
skip_files << 'examples/macos_tray/tray.v'
}
} }
vargs := _vargs.replace('-progress', '').replace('-progress', '') vargs := _vargs.replace('-progress', '').replace('-progress', '')
vexe := pref.vexe_path() vexe := pref.vexe_path()

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

131
examples/macos_tray/tray.m Normal file
View File

@ -0,0 +1,131 @@
// #include <Cocoa/Cocoa.h> // Uncomment for C/C++ intellisense
// See this tutorial to learn about NSApplicationDelegate:
// https://bumbershootsoft.wordpress.com/2018/11/22/unfiltered-cocoa-completing-the-application/
static NSString *nsstring(string s) {
return [[NSString alloc] initWithBytesNoCopy:s.str
length:s.len
encoding:NSUTF8StringEncoding
freeWhenDone:NO];
}
// Manages the app lifecycle.
@interface AppDelegate : NSObject <NSApplicationDelegate> {
NSAutoreleasePool *pool;
NSApplication *app;
NSStatusItem *statusItem; // our button
main__TrayParams trayParams; // TrayParams is defined in tray.v
}
@end
@implementation AppDelegate
- (AppDelegate *)initWithParams:(main__TrayParams)params {
if (self = [super init]) {
trayParams = params;
}
return self;
}
// Called when NSMenuItem is clicked.
- (void)onAction:(id)sender {
struct main__TrayMenuItem *item =
(struct main__TrayMenuItem *)[[sender representedObject] pointerValue];
if (item) {
trayParams.on_click(*item);
}
}
- (NSMenu *)buildMenu {
NSMenu *menu = [NSMenu new];
[menu autorelease];
[menu setAutoenablesItems:NO];
main__TrayMenuItem *params_items = trayParams.items.data;
for (int i = 0; i < trayParams.items.len; i++) {
NSString *title = nsstring(params_items[i].text);
NSMenuItem *item = [menu addItemWithTitle:title
action:@selector(onAction:)
keyEquivalent:@""];
NSValue *representedObject = [NSValue valueWithPointer:(params_items + i)];
[item setRepresentedObject:representedObject];
[item setTarget:self];
[item autorelease];
[item setEnabled:YES];
}
return menu;
}
- (void)initTrayMenuItem {
NSStatusBar *statusBar = [NSStatusBar systemStatusBar];
statusItem = [statusBar statusItemWithLength:NSSquareStatusItemLength];
[statusItem retain];
[statusItem setVisible:YES];
NSStatusBarButton *statusBarButton = [statusItem button];
// Height must be 22px.
NSImage *img = [NSImage imageNamed:@"icon.png"];
[statusBarButton setImage:img];
NSMenu *menu = [self buildMenu];
[statusItem setMenu:menu];
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
NSLog(@"applicationWillFinishLaunching called");
}
- (void)applicationWillTerminate:(NSNotification *)notif;
{ NSLog(@"applicationWillTerminate called"); }
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication *)sender {
NSLog(@"applicationShouldTerminate called");
return NSTerminateNow;
}
@end
// Initializes NSApplication and NSStatusItem, aka system tray menu item.
main__TrayInfo *tray_app_init(main__TrayParams params) {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] initWithParams:params];
// Hide icon from the doc.
[app setActivationPolicy:NSApplicationActivationPolicyProhibited];
[app setDelegate:appDelegate];
[appDelegate initTrayMenuItem];
main__TrayInfo *tray_info = malloc(sizeof(main__TrayInfo));
tray_info->app = app;
tray_info->app_delegate = appDelegate;
return tray_info;
}
// Blocks and runs the application.
void tray_app_run(main__TrayInfo *tray_info) {
NSApplication *app = (NSApplication *)(tray_info->app);
[app run];
}
// Processes a single NSEvent while blocking the thread
// until there is an event.
void tray_app_loop(main__TrayInfo *tray_info) {
NSDate *until = [NSDate distantFuture];
NSApplication *app = (NSApplication *)(tray_info->app);
NSEvent *event = [app nextEventMatchingMask:ULONG_MAX
untilDate:until
inMode:@"kCFRunLoopDefaultMode"
dequeue:YES];
if (event) {
[app sendEvent:event];
}
}
// Terminates the app.
void tray_app_exit(main__TrayInfo *tray_info) {
NSApplication *app = (NSApplication *)(tray_info->app);
[app terminate:app];
}

View File

@ -0,0 +1,72 @@
// Simple windows-less application that shows a icon button
// on Mac OS tray.
//
// Tested on Mac OS Monterey (12.3).
module main
#include <Cocoa/Cocoa.h>
#flag -framework Cocoa
#include "@VMODROOT/tray.m"
fn C.tray_app_init(TrayParams) &TrayInfo
fn C.tray_app_loop(&TrayInfo)
fn C.tray_app_run(&TrayInfo)
fn C.tray_app_exit(&TrayInfo)
struct TrayMenuItem {
id string [required] // Unique ID.
text string [required] // Text to display.
}
// Parameters to configure the tray button.
struct TrayParams {
items []TrayMenuItem [required]
on_click fn (item TrayMenuItem)
}
// Internal Cocoa application state.
struct TrayInfo {
app voidptr // pointer to NSApplication
app_delegate voidptr // pointer to AppDelegate
}
[heap]
struct MyApp {
mut:
tray_info &TrayInfo
}
fn (app &MyApp) on_menu_item_click(item TrayMenuItem) {
println('click $item.id')
if item.id == 'quit' {
C.tray_app_exit(app.tray_info)
}
}
fn main() {
mut my_app := &MyApp{
tray_info: 0
}
my_app.tray_info = C.tray_app_init(TrayParams{
items: [TrayMenuItem{
id: 'hello'
text: 'Hello'
}, TrayMenuItem{
id: 'quit'
text: 'Quit!'
}]
on_click: my_app.on_menu_item_click
})
//// Use this:
// for {
// C.tray_app_loop(my_app.tray_info)
// // println("loop")
// }
//// Or this:
C.tray_app_run(my_app.tray_info)
}

View File