Files
magpie/internal/gui/glide_darwin.go
T

137 lines
4.8 KiB
Go

//go:build !nogui
package gui
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Cocoa -framework QuartzCore
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
// The panel hangs from the menu bar, so its top edge stays where it is and
// the bottom moves; the system animates the frame on its own display clock.
static void glidePanel(void *w, int height, int ms, double x1, double y1, double x2, double y2) {
NSWindow *win = (NSWindow *)w;
dispatch_async(dispatch_get_main_queue(), ^{
NSRect f = win.frame;
NSRect to = NSMakeRect(f.origin.x, NSMaxY(f) - height, f.size.width, height);
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *ctx) {
ctx.duration = ms / 1000.0;
ctx.timingFunction = [CAMediaTimingFunction functionWithControlPoints:x1 :y1 :x2 :y2];
ctx.allowsImplicitAnimation = YES;
[[win animator] setFrame:to display:YES];
} completionHandler:nil];
});
}
// The page is drawn a frame or two after the panel grows, and until then
// WebKit fills the new edge with its own dark background. So the webview
// draws no background of its own (a key WebKit has long honoured, though not
// a public one) and the page's colour is painted in a view under it, which the
// system sizes along with the panel: the new edge shows that colour until the
// page catches up. Says 0 when the webview can't be made to draw none, and
// the page keeps painting its own.
static int tintPanel(void *w, int r, int g, int b, int a, int ms) {
NSWindow *win = (NSWindow *)w;
if (![win respondsToSelector:@selector(webView)]) return 0;
__block int ok = 0;
dispatch_sync(dispatch_get_main_queue(), ^{
NSView *web = [win performSelector:@selector(webView)];
NSView *host = web.superview;
if (host == nil) return;
@try {
[web setValue:@NO forKey:@"drawsBackground"];
} @catch (NSException *e) {
return;
}
NSView *tint = nil;
for (NSView *v in host.subviews) {
if ([v.identifier isEqualToString:@"magpie-tint"]) tint = v;
}
CGColorRef colour = CGColorCreateSRGB(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
if (tint == nil) {
tint = [[NSView alloc] initWithFrame:host.bounds];
tint.identifier = @"magpie-tint";
tint.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
// a layer of its own, which AppKit leaves alone
tint.layer = [CALayer layer];
tint.wantsLayer = YES;
tint.layer.cornerRadius = 12;
tint.layer.masksToBounds = YES;
tint.layer.backgroundColor = colour;
[host addSubview:tint positioned:NSWindowBelow relativeTo:web];
[tint release];
} else {
// a change of theme fades as the page's own colours do
[CATransaction begin];
CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
fade.fromValue = (id)tint.layer.backgroundColor;
fade.toValue = (__bridge id)colour;
fade.duration = ms / 1000.0;
fade.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
tint.layer.backgroundColor = colour;
if (ms > 0) [tint.layer addAnimation:fade forKey:@"tint"];
[CATransaction commit];
}
CGColorRelease(colour);
ok = 1;
});
return ok;
}
// A Dock icon is the Regular activation policy, none is Accessory. Leaving
// the Dock deactivates the app, so with front set it is brought back to the
// front after, with the window the Settings page is in; a window just closed
// leaves it where it is.
static void setDock(int on, int front) {
dispatch_async(dispatch_get_main_queue(), ^{
NSApplicationActivationPolicy p = on ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory;
if ([NSApp activationPolicy] == p) return;
[NSApp setActivationPolicy:p];
if (front) [NSApp activateIgnoringOtherApps:YES];
});
}
*/
import "C"
import "github.com/wailsapp/wails/v3/pkg/application"
// glidePanel moves the shown panel to height, its top edge held under the
// menu bar. It says false when it can't, for the caller to size it plainly.
func (h *host) glidePanel(height int, g Glide) bool {
w := h.panel.NativeWindow()
if w == nil {
return false
}
c := g.Curve
C.glidePanel(w, C.int(height), C.int(g.MS), C.double(c[0]), C.double(c[1]), C.double(c[2]), C.double(c[3]))
return true
}
// TintPanel paints the panel's tint under the page, fading to it over ms.
func (h *host) TintPanel(c [4]uint8, ms int) bool {
w := h.panel.NativeWindow()
if w == nil {
return false
}
return C.tintPanel(w, C.int(c[0]), C.int(c[1]), C.int(c[2]), C.int(c[3]), C.int(ms)) == 1
}
func dockPolicy(on bool) application.ActivationPolicy {
if on {
return application.ActivationPolicyRegular
}
return application.ActivationPolicyAccessory
}
// setDock shows magpie in the Dock or takes it out, at once, bringing it to
// the front after when front.
func setDock(on, front bool) { C.setDock(C.int(boolInt(on)), C.int(boolInt(front))) }
func boolInt(b bool) int {
if b {
return 1
}
return 0
}