164 lines
5.8 KiB
Go
164 lines
5.8 KiB
Go
//go:build darwin
|
|
|
|
package main
|
|
|
|
/*
|
|
#cgo CFLAGS: -x objective-c -fobjc-arc -fblocks
|
|
#cgo LDFLAGS: -framework AppKit -framework QuartzCore
|
|
#include <stdlib.h>
|
|
#include <dispatch/dispatch.h>
|
|
#import <AppKit/AppKit.h>
|
|
#import <QuartzCore/QuartzCore.h>
|
|
|
|
static NSPanel *operationPanel = nil;
|
|
static NSTextField *operationTitleLabel = nil;
|
|
static NSTextField *operationDetailLabel = nil;
|
|
static NSTextField *operationIconLabel = nil;
|
|
static NSProgressIndicator *operationSpinner = nil;
|
|
|
|
static NSString *statusString(const char *value) {
|
|
if (value == NULL) {
|
|
return @"";
|
|
}
|
|
NSString *text = [NSString stringWithUTF8String:value];
|
|
return text ?: @"";
|
|
}
|
|
|
|
static void ensureOperationPanel(void) {
|
|
if (operationPanel != nil) {
|
|
return;
|
|
}
|
|
|
|
NSRect frame = NSMakeRect(0, 0, 360, 118);
|
|
operationPanel = [[NSPanel alloc]
|
|
initWithContentRect:frame
|
|
styleMask:NSWindowStyleMaskBorderless
|
|
backing:NSBackingStoreBuffered
|
|
defer:NO];
|
|
[operationPanel setOpaque:NO];
|
|
[operationPanel setBackgroundColor:[NSColor clearColor]];
|
|
[operationPanel setLevel:NSFloatingWindowLevel];
|
|
[operationPanel setHidesOnDeactivate:NO];
|
|
[operationPanel setCollectionBehavior:
|
|
NSWindowCollectionBehaviorCanJoinAllSpaces |
|
|
NSWindowCollectionBehaviorFullScreenAuxiliary |
|
|
NSWindowCollectionBehaviorStationary];
|
|
|
|
NSView *root = [[NSView alloc] initWithFrame:frame];
|
|
[root setWantsLayer:YES];
|
|
[[root layer] setCornerRadius:14];
|
|
[[root layer] setBackgroundColor:[[NSColor colorWithCalibratedWhite:0.08 alpha:0.92] CGColor]];
|
|
[[root layer] setShadowColor:[[NSColor blackColor] CGColor]];
|
|
[[root layer] setShadowOpacity:0.28];
|
|
[[root layer] setShadowRadius:18];
|
|
[[root layer] setShadowOffset:CGSizeMake(0, -8)];
|
|
|
|
operationSpinner = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(24, 42, 34, 34)];
|
|
[operationSpinner setStyle:NSProgressIndicatorStyleSpinning];
|
|
[operationSpinner setIndeterminate:YES];
|
|
|
|
operationIconLabel = [NSTextField labelWithString:@"OK"];
|
|
[operationIconLabel setFrame:NSMakeRect(21, 43, 42, 30)];
|
|
[operationIconLabel setAlignment:NSTextAlignmentCenter];
|
|
[operationIconLabel setFont:[NSFont systemFontOfSize:17 weight:NSFontWeightSemibold]];
|
|
[operationIconLabel setTextColor:[NSColor colorWithCalibratedRed:74.0/255.0 green:222.0/255.0 blue:128.0/255.0 alpha:1.0]];
|
|
[operationIconLabel setHidden:YES];
|
|
|
|
operationTitleLabel = [NSTextField labelWithString:@""];
|
|
[operationTitleLabel setFrame:NSMakeRect(76, 61, 260, 24)];
|
|
[operationTitleLabel setFont:[NSFont systemFontOfSize:15 weight:NSFontWeightSemibold]];
|
|
[operationTitleLabel setTextColor:[NSColor whiteColor]];
|
|
|
|
operationDetailLabel = [NSTextField labelWithString:@""];
|
|
[operationDetailLabel setFrame:NSMakeRect(76, 34, 260, 22)];
|
|
[operationDetailLabel setFont:[NSFont systemFontOfSize:12 weight:NSFontWeightRegular]];
|
|
[operationDetailLabel setTextColor:[NSColor colorWithCalibratedWhite:0.82 alpha:1.0]];
|
|
[operationDetailLabel setLineBreakMode:NSLineBreakByTruncatingTail];
|
|
|
|
[root addSubview:operationSpinner];
|
|
[root addSubview:operationIconLabel];
|
|
[root addSubview:operationTitleLabel];
|
|
[root addSubview:operationDetailLabel];
|
|
[operationPanel setContentView:root];
|
|
}
|
|
|
|
static void snipShowOperationStatus(const char *titleC, const char *detailC, int state) {
|
|
NSString *title = statusString(titleC);
|
|
NSString *detail = statusString(detailC);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
ensureOperationPanel();
|
|
[operationTitleLabel setStringValue:title];
|
|
[operationDetailLabel setStringValue:detail];
|
|
|
|
if (state == 0) {
|
|
[operationIconLabel setHidden:YES];
|
|
[operationSpinner setHidden:NO];
|
|
[operationSpinner startAnimation:nil];
|
|
} else {
|
|
[operationSpinner stopAnimation:nil];
|
|
[operationSpinner setHidden:YES];
|
|
[operationIconLabel setHidden:NO];
|
|
if (state == 2) {
|
|
[operationIconLabel setStringValue:@"!"];
|
|
[operationIconLabel setTextColor:[NSColor colorWithCalibratedRed:248.0/255.0 green:113.0/255.0 blue:113.0/255.0 alpha:1.0]];
|
|
} else {
|
|
[operationIconLabel setStringValue:@"OK"];
|
|
[operationIconLabel setTextColor:[NSColor colorWithCalibratedRed:74.0/255.0 green:222.0/255.0 blue:128.0/255.0 alpha:1.0]];
|
|
}
|
|
}
|
|
|
|
NSScreen *screen = [NSScreen mainScreen];
|
|
if (screen != nil) {
|
|
NSRect visible = [screen visibleFrame];
|
|
NSRect panelFrame = [operationPanel frame];
|
|
panelFrame.origin.x = NSMidX(visible) - panelFrame.size.width / 2;
|
|
panelFrame.origin.y = NSMaxY(visible) - panelFrame.size.height - 48;
|
|
[operationPanel setFrame:panelFrame display:YES];
|
|
}
|
|
[operationPanel orderFrontRegardless];
|
|
});
|
|
}
|
|
|
|
static void snipHideOperationStatus(void) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (operationPanel != nil) {
|
|
[operationSpinner stopAnimation:nil];
|
|
[operationPanel orderOut:nil];
|
|
}
|
|
});
|
|
}
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
operationStatusRunning = iota
|
|
operationStatusSuccess
|
|
operationStatusError
|
|
)
|
|
|
|
var operationStatusSeq atomic.Uint64
|
|
|
|
func showOperationStatus(title, detail string, state int) {
|
|
operationStatusSeq.Add(1)
|
|
ctitle := C.CString(title)
|
|
cdetail := C.CString(detail)
|
|
defer C.free(unsafe.Pointer(ctitle))
|
|
defer C.free(unsafe.Pointer(cdetail))
|
|
C.snipShowOperationStatus(ctitle, cdetail, C.int(state))
|
|
}
|
|
|
|
func hideOperationStatusAfter(delay time.Duration) {
|
|
seq := operationStatusSeq.Load()
|
|
time.AfterFunc(delay, func() {
|
|
if operationStatusSeq.Load() == seq {
|
|
C.snipHideOperationStatus()
|
|
}
|
|
})
|
|
}
|