forked from joejulian/gio
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1802761c93 | |||
| 0558bb3f1c | |||
| 78ce5e3ad5 | |||
| 1be34eec6f | |||
| 44ede4eceb | |||
| 993ec907be | |||
| 35785e9c96 |
+12
@@ -120,6 +120,18 @@ func DataDir() (string, error) {
|
||||
return dataDir()
|
||||
}
|
||||
|
||||
// Main must be called last from the program main function.
|
||||
// On most platforms Main blocks forever, for Android and
|
||||
// iOS it returns immediately to give control of the main
|
||||
// thread back to the system.
|
||||
//
|
||||
// Calling Main is necessary because some operating systems
|
||||
// require control of the main thread of the program for
|
||||
// running windows.
|
||||
func Main() {
|
||||
osMain()
|
||||
}
|
||||
|
||||
func (FrameEvent) ImplementsEvent() {}
|
||||
|
||||
func init() {
|
||||
|
||||
+21
-5
@@ -33,12 +33,28 @@ For example:
|
||||
A program must keep receiving events from the event channel until
|
||||
[DestroyEvent] is received.
|
||||
|
||||
# Main Thread
|
||||
# Main
|
||||
|
||||
Some GUI platform need access to the main thread of the program. To avoid a
|
||||
deadlock on such platforms, at least one Window must have its Event method
|
||||
called by the main goroutine. It doesn't have to be any particular Window;
|
||||
even a destroyed Window suffices.
|
||||
The Main function must be called from a program's main function, to hand over
|
||||
control of the main thread to operating systems that need it.
|
||||
|
||||
Because Main is also blocking on some platforms, the event loop of a Window must run in a goroutine.
|
||||
|
||||
For example, to display a blank but otherwise functional window:
|
||||
|
||||
package main
|
||||
|
||||
import "gioui.org/app"
|
||||
|
||||
func main() {
|
||||
go func() {
|
||||
w := app.NewWindow()
|
||||
for {
|
||||
w.Event()
|
||||
}
|
||||
}()
|
||||
app.Main()
|
||||
}
|
||||
|
||||
# Permissions
|
||||
|
||||
|
||||
@@ -1317,6 +1317,9 @@ func findClass(env *C.JNIEnv, name string) C.jclass {
|
||||
return C.jni_FindClass(env, cn)
|
||||
}
|
||||
|
||||
func osMain() {
|
||||
}
|
||||
|
||||
func newWindow(window *callbacks, options []Option) {
|
||||
mainWindow.in <- windowAndConfig{window, options}
|
||||
<-mainWindow.windows
|
||||
|
||||
+10
-7
@@ -15,8 +15,8 @@ __attribute__ ((visibility ("hidden"))) void gio_hideCursor();
|
||||
__attribute__ ((visibility ("hidden"))) void gio_showCursor();
|
||||
__attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID);
|
||||
|
||||
static int isMainThread() {
|
||||
return [NSThread isMainThread] ? 1 : 0;
|
||||
static bool isMainThread() {
|
||||
return [NSThread isMainThread];
|
||||
}
|
||||
|
||||
static NSUInteger nsstringLength(CFTypeRef cstr) {
|
||||
@@ -77,7 +77,7 @@ var mainFuncs = make(chan func(), 1)
|
||||
|
||||
// runOnMain runs the function on the main thread.
|
||||
func runOnMain(f func()) {
|
||||
if isMainThread() {
|
||||
if C.isMainThread() {
|
||||
f()
|
||||
return
|
||||
}
|
||||
@@ -87,10 +87,6 @@ func runOnMain(f func()) {
|
||||
}()
|
||||
}
|
||||
|
||||
func isMainThread() bool {
|
||||
return C.isMainThread() != 0
|
||||
}
|
||||
|
||||
//export gio_dispatchMainFuncs
|
||||
func gio_dispatchMainFuncs() {
|
||||
for {
|
||||
@@ -263,3 +259,10 @@ func windowSetCursor(from, to pointer.Cursor) pointer.Cursor {
|
||||
C.gio_setCursor(C.NSUInteger(macosCursorID[to]))
|
||||
return to
|
||||
}
|
||||
|
||||
func (w *window) wakeup() {
|
||||
runOnMain(func() {
|
||||
w.loop.Wakeup()
|
||||
w.loop.FlushEvents()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "_cgo_export.h"
|
||||
|
||||
void gio_wakeupMainThread(void) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
gio_dispatchMainFuncs();
|
||||
});
|
||||
}
|
||||
+9
-7
@@ -21,6 +21,7 @@ struct drawParams {
|
||||
};
|
||||
|
||||
static void writeClipboard(unichar *chars, NSUInteger length) {
|
||||
#if !TARGET_OS_TV
|
||||
@autoreleasepool {
|
||||
NSString *s = [NSString string];
|
||||
if (length > 0) {
|
||||
@@ -29,13 +30,18 @@ static void writeClipboard(unichar *chars, NSUInteger length) {
|
||||
UIPasteboard *p = UIPasteboard.generalPasteboard;
|
||||
p.string = s;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static CFTypeRef readClipboard(void) {
|
||||
#if !TARGET_OS_TV
|
||||
@autoreleasepool {
|
||||
UIPasteboard *p = UIPasteboard.generalPasteboard;
|
||||
return (__bridge_retained CFTypeRef)p.string;
|
||||
}
|
||||
#else
|
||||
return nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void showTextInput(CFTypeRef viewRef) {
|
||||
@@ -388,17 +394,13 @@ func newWindow(win *callbacks, options []Option) {
|
||||
<-mainWindow.windows
|
||||
}
|
||||
|
||||
func osMain() {
|
||||
}
|
||||
|
||||
//export gio_runMain
|
||||
func gio_runMain() {
|
||||
runMain()
|
||||
}
|
||||
|
||||
func (w *window) wakeup() {
|
||||
runOnMain(func() {
|
||||
w.loop.Wakeup()
|
||||
w.loop.FlushEvents()
|
||||
})
|
||||
}
|
||||
|
||||
func (UIKitViewEvent) implementsViewEvent() {}
|
||||
func (UIKitViewEvent) ImplementsEvent() {}
|
||||
|
||||
+5
-7
@@ -26,12 +26,13 @@ CGFloat _keyboardHeight;
|
||||
self.view.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
|
||||
UIView *drawView = [[GioView alloc] initWithFrame:zeroFrame];
|
||||
[self.view addSubview: drawView];
|
||||
#ifndef TARGET_OS_TV
|
||||
#if !TARGET_OS_TV
|
||||
drawView.multipleTouchEnabled = YES;
|
||||
#endif
|
||||
drawView.preservesSuperviewLayoutMargins = YES;
|
||||
drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
|
||||
onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self);
|
||||
#if !TARGET_OS_TV
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(keyboardWillChange:)
|
||||
name:UIKeyboardWillShowNotification
|
||||
@@ -44,6 +45,7 @@ CGFloat _keyboardHeight;
|
||||
selector:@selector(keyboardWillHide:)
|
||||
name:UIKeyboardWillHideNotification
|
||||
object:nil];
|
||||
#endif
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(applicationDidEnterBackground:)
|
||||
name: UIApplicationDidEnterBackgroundNotification
|
||||
@@ -89,6 +91,7 @@ CGFloat _keyboardHeight;
|
||||
[super didReceiveMemoryWarning];
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (void)keyboardWillChange:(NSNotification *)note {
|
||||
NSDictionary *userInfo = note.userInfo;
|
||||
CGRect f = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||||
@@ -100,6 +103,7 @@ CGFloat _keyboardHeight;
|
||||
_keyboardHeight = 0.0;
|
||||
[self.view setNeedsLayout];
|
||||
}
|
||||
#endif
|
||||
@end
|
||||
|
||||
static void handleTouches(int last, GioView *view, NSSet<UITouch *> *touches, UIEvent *event) {
|
||||
@@ -276,9 +280,3 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
|
||||
GioView *v = (__bridge GioView *)viewRef;
|
||||
v.handle = handle;
|
||||
}
|
||||
|
||||
void gio_wakeupMainThread(void) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
gio_dispatchMainFuncs();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -741,6 +741,10 @@ func (w *window) navigationColor(c color.NRGBA) {
|
||||
theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B}))
|
||||
}
|
||||
|
||||
func osMain() {
|
||||
select {}
|
||||
}
|
||||
|
||||
func translateKey(k string) (key.Name, bool) {
|
||||
var n key.Name
|
||||
|
||||
|
||||
+32
-74
@@ -39,7 +39,7 @@ import (
|
||||
#define MOUSE_DOWN 3
|
||||
#define MOUSE_SCROLL 4
|
||||
|
||||
__attribute__ ((visibility ("hidden"))) void gio_initApp(void);
|
||||
__attribute__ ((visibility ("hidden"))) void gio_main(void);
|
||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(void);
|
||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
|
||||
__attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle);
|
||||
@@ -195,6 +195,14 @@ static void setScreenFrame(CFTypeRef windowRef, CGFloat x, CGFloat y, CGFloat w,
|
||||
}
|
||||
}
|
||||
|
||||
static void resetLayerFrame(CFTypeRef viewRef) {
|
||||
@autoreleasepool {
|
||||
NSView* view = (__bridge NSView *)viewRef;
|
||||
NSRect r = view.frame;
|
||||
view.layer.frame = r;
|
||||
}
|
||||
}
|
||||
|
||||
static void hideWindow(CFTypeRef windowRef) {
|
||||
@autoreleasepool {
|
||||
NSWindow* window = (__bridge NSWindow *)windowRef;
|
||||
@@ -289,16 +297,6 @@ static void invalidateCharacterCoordinates(CFTypeRef viewRef) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dispatchEvent(void) {
|
||||
@autoreleasepool {
|
||||
NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
||||
untilDate:[NSDate distantFuture]
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -333,16 +331,13 @@ type window struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
// launched is closed after gio_initApp returns.
|
||||
// launched is closed when applicationDidFinishLaunching is called.
|
||||
var launched = make(chan struct{})
|
||||
|
||||
// nextTopLeft is the offset to use for the next window's call to
|
||||
// cascadeTopLeftFromPoint.
|
||||
var nextTopLeft C.NSPoint
|
||||
|
||||
// mainThreadWindow is the window currently in control of the main thread.
|
||||
var mainThreadWindow *window
|
||||
|
||||
func windowFor(h C.uintptr_t) *window {
|
||||
return cgo.Handle(h).Value().(*window)
|
||||
}
|
||||
@@ -469,6 +464,9 @@ func (w *window) Configure(options []Option) {
|
||||
C.setWindowStandardButtonHidden(window, C.NSWindowCloseButton, barTrans)
|
||||
C.setWindowStandardButtonHidden(window, C.NSWindowMiniaturizeButton, barTrans)
|
||||
C.setWindowStandardButtonHidden(window, C.NSWindowZoomButton, barTrans)
|
||||
// When toggling the titlebar, the layer doesn't update its frame
|
||||
// until the next resize. Force it.
|
||||
C.resetLayerFrame(w.view)
|
||||
}
|
||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||
}
|
||||
@@ -835,51 +833,23 @@ func (w *window) draw() {
|
||||
|
||||
func (w *window) ProcessEvent(e event.Event) {
|
||||
w.w.ProcessEvent(e)
|
||||
// The main thread window deliver events in Event.
|
||||
if w != mainThreadWindow {
|
||||
w.loop.FlushEvents()
|
||||
}
|
||||
w.loop.FlushEvents()
|
||||
}
|
||||
|
||||
func (w *window) Event() event.Event {
|
||||
if !isMainThread() {
|
||||
return w.loop.Event()
|
||||
}
|
||||
mainThreadWindow = w
|
||||
defer func() { mainThreadWindow = nil }()
|
||||
for {
|
||||
if evt, ok := w.loop.win.nextEvent(); ok {
|
||||
return evt
|
||||
}
|
||||
C.dispatchEvent()
|
||||
gio_dispatchMainFuncs()
|
||||
}
|
||||
return w.loop.Event()
|
||||
}
|
||||
|
||||
func (w *window) Invalidate() {
|
||||
if isMainThread() {
|
||||
mainThreadWindow = w
|
||||
defer func() { mainThreadWindow = nil }()
|
||||
}
|
||||
w.loop.Invalidate()
|
||||
}
|
||||
|
||||
func (w *window) Run(f func()) {
|
||||
if isMainThread() {
|
||||
mainThreadWindow = w
|
||||
defer func() { mainThreadWindow = nil }()
|
||||
}
|
||||
w.loop.Run(f)
|
||||
}
|
||||
|
||||
func (w *window) Frame(frame *op.Ops) {
|
||||
if !isMainThread() {
|
||||
w.loop.Frame(frame)
|
||||
return
|
||||
}
|
||||
mainThreadWindow = w
|
||||
defer func() { mainThreadWindow = nil }()
|
||||
w.loop.win.ProcessFrame(frame, nil)
|
||||
w.loop.Frame(frame)
|
||||
}
|
||||
|
||||
func configFor(scale float32) unit.Metric {
|
||||
@@ -939,27 +909,20 @@ func gio_onWindowed(h C.uintptr_t) {
|
||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||
}
|
||||
|
||||
//export gio_onFinishLaunching
|
||||
func gio_onFinishLaunching() {
|
||||
close(launched)
|
||||
}
|
||||
|
||||
func newWindow(win *callbacks, options []Option) {
|
||||
w := &window{
|
||||
redraw: make(chan struct{}, 1),
|
||||
w: win,
|
||||
}
|
||||
w.loop = newEventLoop(w.w, w.wakeup)
|
||||
if isMainThread() {
|
||||
mainThreadWindow = w
|
||||
defer func() { mainThreadWindow = nil }()
|
||||
select {
|
||||
case <-launched:
|
||||
default:
|
||||
// If we're the main thread, initialize the GUI.
|
||||
C.gio_initApp()
|
||||
close(launched)
|
||||
}
|
||||
} else {
|
||||
<-launched
|
||||
}
|
||||
res := make(chan struct{}, 1)
|
||||
<-launched
|
||||
res := make(chan struct{})
|
||||
runOnMain(func() {
|
||||
w := &window{
|
||||
redraw: make(chan struct{}, 1),
|
||||
w: win,
|
||||
}
|
||||
w.loop = newEventLoop(w.w, w.wakeup)
|
||||
win.SetDriver(w)
|
||||
res <- struct{}{}
|
||||
if err := w.init(); err != nil {
|
||||
@@ -1012,6 +975,10 @@ func (w *window) init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func osMain() {
|
||||
C.gio_main()
|
||||
}
|
||||
|
||||
func convertKey(k rune) (key.Name, bool) {
|
||||
var n key.Name
|
||||
switch k {
|
||||
@@ -1096,14 +1063,5 @@ func convertMods(mods C.NSUInteger) key.Modifiers {
|
||||
return kmods
|
||||
}
|
||||
|
||||
func (w *window) wakeup() {
|
||||
runOnMain(func() {
|
||||
w.loop.Wakeup()
|
||||
if w != mainThreadWindow {
|
||||
w.loop.FlushEvents()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (AppKitViewEvent) implementsViewEvent() {}
|
||||
func (AppKitViewEvent) ImplementsEvent() {}
|
||||
|
||||
+12
-241
@@ -69,212 +69,6 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
||||
gio_onMouse(view.handle, (__bridge CFTypeRef)event, typ, event.buttonNumber, p.x, height - p.y, dx, dy, [event timestamp], [event modifierFlags]);
|
||||
}
|
||||
|
||||
@interface GioApplication: NSApplication
|
||||
@end
|
||||
|
||||
// Variables for tracking resizes.
|
||||
static struct {
|
||||
NSPoint dir;
|
||||
NSEvent *lastMouseDown;
|
||||
NSPoint off;
|
||||
} resizeState = {};
|
||||
|
||||
static NSBitmapImageRep *nsImageBitmap(NSImage *img) {
|
||||
NSArray<NSImageRep *> *reps = img.representations;
|
||||
if ([reps count] == 0) {
|
||||
return nil;
|
||||
}
|
||||
NSImageRep *rep = reps[0];
|
||||
if (![rep isKindOfClass:[NSBitmapImageRep class]]) {
|
||||
return nil;
|
||||
}
|
||||
return (NSBitmapImageRep *)rep;
|
||||
}
|
||||
|
||||
static NSCursor *lookupPrivateNSCursor(SEL name) {
|
||||
if (![NSCursor respondsToSelector:name]) {
|
||||
return nil;
|
||||
}
|
||||
id obj = [NSCursor performSelector:name];
|
||||
if (![obj isKindOfClass:[NSCursor class]]) {
|
||||
return nil;
|
||||
}
|
||||
return (NSCursor *)obj;
|
||||
}
|
||||
|
||||
static BOOL isEqualNSCursor(NSCursor *c1, SEL name2) {
|
||||
NSCursor *c2 = lookupPrivateNSCursor(name2);
|
||||
if (c2 == nil || !NSEqualPoints(c1.hotSpot, c2.hotSpot)) {
|
||||
return NO;
|
||||
}
|
||||
NSImage *img1 = c1.image;
|
||||
NSImage *img2 = c2.image;
|
||||
if (!NSEqualSizes(img1.size, img2.size)) {
|
||||
return NO;
|
||||
}
|
||||
NSBitmapImageRep *bit1 = nsImageBitmap(img1);
|
||||
NSBitmapImageRep *bit2 = nsImageBitmap(img2);
|
||||
if (bit1 == nil || bit2 == nil) {
|
||||
return NO;
|
||||
}
|
||||
NSInteger n1 = bit1.numberOfPlanes*bit1.bytesPerPlane;
|
||||
NSInteger n2 = bit1.numberOfPlanes*bit1.bytesPerPlane;
|
||||
if (n1 != n2) {
|
||||
return NO;
|
||||
}
|
||||
if (memcmp(bit1.bitmapData, bit2.bitmapData, n1) != 0) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@implementation GioApplication
|
||||
- (NSEvent *)nextEventMatchingMask:(NSEventMask)mask
|
||||
untilDate:(NSDate *)expiration
|
||||
inMode:(NSRunLoopMode)mode
|
||||
dequeue:(BOOL)deqFlag {
|
||||
if ([mode isEqualToString:NSEventTrackingRunLoopMode]) {
|
||||
NSEvent *l = resizeState.lastMouseDown;
|
||||
if (l != nil) {
|
||||
//lastMouseDown = nil;
|
||||
NSCursor *cur = [NSCursor currentSystemCursor];
|
||||
NSPoint dir = {};
|
||||
NSPoint off = {};
|
||||
NSSize wsz = [l window].frame.size;
|
||||
NSPoint center = NSMakePoint(wsz.width/2, wsz.height/2);
|
||||
NSPoint p = [l locationInWindow];
|
||||
if (p.x >= center.x) {
|
||||
dir.x = 1;
|
||||
off.x = p.x - wsz.width;
|
||||
} else {
|
||||
dir.x = -1;
|
||||
off.x = p.x;
|
||||
}
|
||||
if (p.y >= center.y) {
|
||||
dir.y = 1;
|
||||
off.y = p.y - wsz.height;
|
||||
} else {
|
||||
dir.y = -1;
|
||||
off.y = p.y;
|
||||
}
|
||||
// The button down coordinate distinguish the four quadrants. Use the
|
||||
// cursor image to determine the precise direction.
|
||||
SEL nw = @selector(_windowResizeNorthWestCursor);
|
||||
SEL n = @selector(_windowResizeNorthCursor);
|
||||
SEL ne = @selector(_windowResizeNorthEastCursor);
|
||||
SEL e = @selector(_windowResizeEastCursor);
|
||||
SEL se = @selector(_windowResizeSouthEastCursor);
|
||||
SEL s = @selector(_windowResizeSouthCursor);
|
||||
SEL sw = @selector(_windowResizeSouthWestCursor);
|
||||
SEL w = @selector(_windowResizeWestCursor);
|
||||
SEL ns = @selector(_windowResizeNorthSouthCursor);
|
||||
SEL ew = @selector(_windowResizeEastWestCursor);
|
||||
SEL nwse = @selector(_windowResizeNorthWestSouthEastCursor);
|
||||
SEL nesw = @selector(_windowResizeNorthEastSouthWestCursor);
|
||||
BOOL match = YES;
|
||||
if (dir.x != 0 && (isEqualNSCursor(cur, ew) || isEqualNSCursor(cur, w) || isEqualNSCursor(cur, e))) {
|
||||
dir.y = 0;
|
||||
}
|
||||
if (dir.y != 0 && (isEqualNSCursor(cur, ns) || isEqualNSCursor(cur, s) || isEqualNSCursor(cur, n))) {
|
||||
dir.x = 0;
|
||||
}
|
||||
// If none of the cursors matched, we may deduce that the resize
|
||||
// direction is one of the corners. However, to ensure that at least
|
||||
// one cursor matches, check the corner cursors.
|
||||
if (dir.x == 1 && dir.y == 1) {
|
||||
if (!isEqualNSCursor(cur, nesw) && !isEqualNSCursor(cur, sw)) {
|
||||
dir = NSZeroPoint;
|
||||
}
|
||||
} else if (dir.x == 1 && dir.y == -1) {
|
||||
if (!isEqualNSCursor(cur, nwse) && !isEqualNSCursor(cur, nw)) {
|
||||
dir = NSZeroPoint;
|
||||
}
|
||||
} else if (dir.x == -1 && dir.y == 1) {
|
||||
if (!isEqualNSCursor(cur, nwse) && !isEqualNSCursor(cur, se)) {
|
||||
dir = NSZeroPoint;
|
||||
}
|
||||
} else if (dir.x == -1 && dir.y == -1) {
|
||||
if (!isEqualNSCursor(cur, nesw) && !isEqualNSCursor(cur, ne)) {
|
||||
dir = NSZeroPoint;
|
||||
}
|
||||
}
|
||||
if (!NSEqualPoints(dir, NSZeroPoint)) {
|
||||
NSEvent *cancel = [NSEvent mouseEventWithType:NSEventTypeLeftMouseUp
|
||||
location:l.locationInWindow
|
||||
modifierFlags:l.modifierFlags
|
||||
timestamp:l.timestamp
|
||||
windowNumber:l.windowNumber
|
||||
context:l.context
|
||||
eventNumber:l.eventNumber
|
||||
clickCount:l.clickCount
|
||||
pressure:l.pressure];
|
||||
resizeState.off = off;
|
||||
resizeState.dir = dir;
|
||||
return cancel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:deqFlag];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface GioWindow: NSWindow
|
||||
@end
|
||||
|
||||
@implementation GioWindow
|
||||
- (void)sendEvent:(NSEvent *)evt {
|
||||
if (evt.type == NSEventTypeLeftMouseDown) {
|
||||
resizeState.lastMouseDown = evt;
|
||||
}
|
||||
NSPoint dir = resizeState.dir;
|
||||
if (NSEqualPoints(dir, NSZeroPoint)) {
|
||||
[super sendEvent:evt];
|
||||
return;
|
||||
}
|
||||
switch (evt.type) {
|
||||
default:
|
||||
return;
|
||||
case NSEventTypeLeftMouseUp:
|
||||
resizeState.dir = NSZeroPoint;
|
||||
resizeState.lastMouseDown = nil;
|
||||
return;
|
||||
case NSEventTypeLeftMouseDragged:
|
||||
// Ok to proceed.
|
||||
break;
|
||||
}
|
||||
NSPoint loc = evt.locationInWindow;
|
||||
NSPoint off = resizeState.off;
|
||||
loc.x -= off.x;
|
||||
loc.y -= off.y;
|
||||
NSRect frame = [self frame];
|
||||
NSSize min = [self minSize];
|
||||
NSSize max = [self maxSize];
|
||||
CGFloat width = frame.size.width;
|
||||
if (dir.x > 0) {
|
||||
width = loc.x;
|
||||
} else if (dir.x < 0) {
|
||||
width -= loc.x;
|
||||
}
|
||||
width = MIN(max.width, MAX(min.width, width));
|
||||
if (dir.x < 0) {
|
||||
frame.origin.x += frame.size.width - width;
|
||||
}
|
||||
frame.size.width = width;
|
||||
CGFloat height = frame.size.height;
|
||||
if (dir.y > 0) {
|
||||
height = loc.y;
|
||||
} else if (dir.y < 0) {
|
||||
height -= loc.y;
|
||||
}
|
||||
height = MIN(max.height, MAX(min.height, height));
|
||||
if (dir.y < 0) {
|
||||
frame.origin.y += frame.size.height - height;
|
||||
}
|
||||
frame.size.height = height;
|
||||
[self setFrame:frame display:YES animate:NO];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GioView
|
||||
- (void)setFrameSize:(NSSize)newSize {
|
||||
[super setFrameSize:newSize];
|
||||
@@ -348,6 +142,7 @@ static BOOL isEqualNSCursor(NSCursor *c1, SEL name2) {
|
||||
// Don't pass commands up the responder chain.
|
||||
// They will end up in a beep.
|
||||
}
|
||||
|
||||
- (BOOL)hasMarkedText {
|
||||
int res = gio_hasMarkedText(self.handle);
|
||||
return res ? YES : NO;
|
||||
@@ -460,11 +255,14 @@ void gio_showCursor() {
|
||||
// some cursors are not public, this tries to use a private cursor
|
||||
// and uses fallback when the use of private cursor fails.
|
||||
static void trySetPrivateCursor(SEL cursorName, NSCursor* fallback) {
|
||||
NSCursor *cur = lookupPrivateNSCursor(cursorName);
|
||||
if (cur == nil) {
|
||||
cur = fallback;
|
||||
if ([NSCursor respondsToSelector:cursorName]) {
|
||||
id object = [NSCursor performSelector:cursorName];
|
||||
if ([object isKindOfClass:[NSCursor class]]) {
|
||||
[(NSCursor*)object set];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[cur set];
|
||||
[fallback set];
|
||||
}
|
||||
|
||||
void gio_setCursor(NSUInteger curID) {
|
||||
@@ -563,7 +361,7 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGF
|
||||
NSMiniaturizableWindowMask |
|
||||
NSClosableWindowMask;
|
||||
|
||||
GioWindow* window = [[GioWindow alloc] initWithContentRect:rect
|
||||
NSWindow* window = [[NSWindow alloc] initWithContentRect:rect
|
||||
styleMask:styleMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
@@ -612,24 +410,13 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
// Force the [NSApp run] call to return.
|
||||
[NSApp stop:nil];
|
||||
NSEvent *dummy = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
|
||||
location:NSZeroPoint
|
||||
modifierFlags:0
|
||||
timestamp:0
|
||||
windowNumber:0
|
||||
context:nil
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
[NSApp postEvent:dummy atStart:YES];
|
||||
gio_onFinishLaunching();
|
||||
}
|
||||
@end
|
||||
|
||||
void gio_initApp() {
|
||||
void gio_main() {
|
||||
@autoreleasepool {
|
||||
[GioApplication sharedApplication];
|
||||
[NSApplication sharedApplication];
|
||||
GioAppDelegate *del = [[GioAppDelegate alloc] init];
|
||||
[NSApp setDelegate:del];
|
||||
|
||||
@@ -651,22 +438,6 @@ void gio_initApp() {
|
||||
|
||||
globalWindowDel = [[GioWindowDelegate alloc] init];
|
||||
|
||||
// Runs until stopped by applicationDidFinishLaunching.
|
||||
[NSApp run];
|
||||
}
|
||||
}
|
||||
|
||||
void gio_wakeupMainThread(void) {
|
||||
@autoreleasepool {
|
||||
NSEvent *dummy = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
|
||||
location:NSZeroPoint
|
||||
modifierFlags:0
|
||||
timestamp:0
|
||||
windowNumber:0
|
||||
context:nil
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
[NSApp postEvent:dummy atStart:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ type WaylandViewEvent struct {
|
||||
func (WaylandViewEvent) implementsViewEvent() {}
|
||||
func (WaylandViewEvent) ImplementsEvent() {}
|
||||
|
||||
func osMain() {
|
||||
select {}
|
||||
}
|
||||
|
||||
type windowDriver func(*callbacks, []Option) error
|
||||
|
||||
// Instead of creating files with build tags for each combination of wayland +/- x11
|
||||
|
||||
@@ -85,6 +85,10 @@ var resources struct {
|
||||
cursor syscall.Handle
|
||||
}
|
||||
|
||||
func osMain() {
|
||||
select {}
|
||||
}
|
||||
|
||||
func newWindow(win *callbacks, options []Option) {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
|
||||
+1
-1
@@ -25,6 +25,6 @@ func runMain() {
|
||||
// Indirect call, since the linker does not know the address of main when
|
||||
// laying down this package.
|
||||
fn := mainMain
|
||||
go fn()
|
||||
fn()
|
||||
})
|
||||
}
|
||||
|
||||
+1
-11
@@ -302,10 +302,7 @@ func (w *Window) Option(opts ...Option) {
|
||||
}
|
||||
|
||||
// Run f in the same thread as the native window event loop, and wait for f to
|
||||
// return or the window to close. Run is guaranteed not to deadlock if it is
|
||||
// invoked during the handling of a [ViewEvent], [FrameEvent],
|
||||
// [StageEvent]; call Run in a separate goroutine to avoid deadlock in all
|
||||
// other cases.
|
||||
// return or the window to close.
|
||||
//
|
||||
// Note that most programs should not call Run; configuring a Window with
|
||||
// [CustomRenderer] is a notable exception.
|
||||
@@ -684,13 +681,6 @@ func (w *Window) processEvent(e event.Event) bool {
|
||||
|
||||
// Event blocks until an event is received from the window, such as
|
||||
// [FrameEvent], or until [Invalidate] is called.
|
||||
//
|
||||
// Note: if more than one Window is active, at least one must have
|
||||
// its Event called from the main goroutine that runs the main
|
||||
// function. This is necessary because some operating system GUI
|
||||
// implementations require control of the main thread.
|
||||
// For this reason, it is allowed to call Event even after a
|
||||
// DestroyEvent has been received.
|
||||
func (w *Window) Event() event.Event {
|
||||
w.init()
|
||||
return w.basic.Event()
|
||||
|
||||
@@ -6,11 +6,11 @@ require (
|
||||
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2
|
||||
gioui.org/shader v1.0.8
|
||||
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372
|
||||
github.com/go-text/typesetting v0.1.1
|
||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95
|
||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91
|
||||
golang.org/x/image v0.5.0
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
|
||||
golang.org/x/sys v0.5.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.7.0
|
||||
require golang.org/x/text v0.9.0
|
||||
|
||||
@@ -5,9 +5,9 @@ gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJG
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
|
||||
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
||||
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo=
|
||||
github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
|
||||
github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo=
|
||||
github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@@ -28,15 +28,16 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@ package text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"log"
|
||||
@@ -276,7 +277,8 @@ func newShaperImpl(systemFonts bool, collection []FontFace) *shaperImpl {
|
||||
// It returns whether the face is now available for use. FontFaces are prioritized
|
||||
// in the order in which they are loaded, with the first face being the default.
|
||||
func (s *shaperImpl) Load(f FontFace) {
|
||||
s.fontMap.AddFace(f.Face.Face(), opentype.FontToDescription(f.Font))
|
||||
desc := opentype.FontToDescription(f.Font)
|
||||
s.fontMap.AddFace(f.Face.Face(), fontscan.Location{File: fmt.Sprint(desc)}, desc)
|
||||
s.addFace(f.Face.Face(), f.Font)
|
||||
}
|
||||
|
||||
|
||||
@@ -284,9 +284,9 @@ func TestIndexPositionBidi(t *testing.T) {
|
||||
name: "bidi rtl",
|
||||
glyphs: bidiRTLText,
|
||||
expectedXs: []fixed.Int26_6{
|
||||
2665, 3291, 3861, 4431, 4716, 5286, 5856, 6109, 6621, 7133, 2665, 2380, 1577, 985, 687, 266, // Positions on line 0.
|
||||
2646, 3272, 3842, 4412, 4697, 5267, 5837, 6090, 6602, 7114, 2646, 2380, 1577, 985, 687, 266, // Positions on line 0.
|
||||
|
||||
7886, 7118, 6350, 5582, 4814, 4529, 4231, 3933, 3667, 2300, 2585, 3155, 3667, 2300, 2015, 1709, 1117, 266, // Positions on line 1.
|
||||
7867, 7099, 6331, 5563, 4795, 4510, 4212, 3914, 3648, 2281, 2566, 3136, 3648, 2281, 2015, 1709, 1117, 266, // Positions on line 1.
|
||||
|
||||
8794, 8026, 7258, 6490, 5722, 5437, 4922, 4540, 4134, 3868, 0, 290, 860, 1430, 1715, 1989, 2559, 3071, 3583, // Positions on line 2.
|
||||
|
||||
@@ -402,7 +402,7 @@ func TestIndexPositionLines(t *testing.T) {
|
||||
xOff: fixed.Int26_6(0),
|
||||
yOff: 22,
|
||||
glyphs: 15,
|
||||
width: fixed.Int26_6(7133),
|
||||
width: fixed.Int26_6(7114),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
@@ -410,7 +410,7 @@ func TestIndexPositionLines(t *testing.T) {
|
||||
xOff: fixed.Int26_6(0),
|
||||
yOff: 41,
|
||||
glyphs: 15,
|
||||
width: fixed.Int26_6(7886),
|
||||
width: fixed.Int26_6(7867),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
@@ -477,18 +477,18 @@ func TestIndexPositionLines(t *testing.T) {
|
||||
glyphs: bidiRTLTextOpp,
|
||||
expectedLines: []lineInfo{
|
||||
{
|
||||
xOff: fixed.Int26_6(3107),
|
||||
xOff: fixed.Int26_6(3126),
|
||||
yOff: 22,
|
||||
glyphs: 15,
|
||||
width: fixed.Int26_6(7133),
|
||||
width: fixed.Int26_6(7114),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
{
|
||||
xOff: fixed.Int26_6(2354),
|
||||
xOff: fixed.Int26_6(2373),
|
||||
yOff: 41,
|
||||
glyphs: 15,
|
||||
width: fixed.Int26_6(7886),
|
||||
width: fixed.Int26_6(7867),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user