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/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()
|
||||||
|
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