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:
parent
5462d4aebf
commit
c1502b3c1f
@ -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/simple_sin_tones.v'
|
||||
}
|
||||
$if !macos {
|
||||
skip_files << 'examples/macos_tray/tray.v'
|
||||
}
|
||||
}
|
||||
vargs := _vargs.replace('-progress', '').replace('-progress', '')
|
||||
vexe := pref.vexe_path()
|
||||
|
BIN
examples/macos_tray/icon.png
Normal file
BIN
examples/macos_tray/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 690 B |
131
examples/macos_tray/tray.m
Normal file
131
examples/macos_tray/tray.m
Normal 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];
|
||||
}
|
72
examples/macos_tray/tray.v
Normal file
72
examples/macos_tray/tray.v
Normal 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)
|
||||
}
|
0
examples/macos_tray/v.mod
Normal file
0
examples/macos_tray/v.mod
Normal file
Loading…
Reference in New Issue
Block a user