mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
Compare commits
5 Commits
main
...
remove-app-main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3879921b80 | |||
| b1f84da679 | |||
| 43024fcca2 | |||
| 9fe8b684e2 | |||
| 2a18a0c135 |
-12
@@ -120,18 +120,6 @@ func DataDir() (string, error) {
|
|||||||
return dataDir()
|
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 (FrameEvent) ImplementsEvent() {}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+5
-21
@@ -33,28 +33,12 @@ For example:
|
|||||||
A program must keep receiving events from the event channel until
|
A program must keep receiving events from the event channel until
|
||||||
[DestroyEvent] is received.
|
[DestroyEvent] is received.
|
||||||
|
|
||||||
# Main
|
# Main Thread
|
||||||
|
|
||||||
The Main function must be called from a program's main function, to hand over
|
Some GUI platform need access to the main thread of the program. To avoid a
|
||||||
control of the main thread to operating systems that need it.
|
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;
|
||||||
Because Main is also blocking on some platforms, the event loop of a Window must run in a goroutine.
|
even a destroyed Window suffices.
|
||||||
|
|
||||||
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
|
# Permissions
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -60,8 +60,9 @@ static void presentDrawable(CFTypeRef queueRef, CFTypeRef drawableRef) {
|
|||||||
id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef;
|
id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef;
|
||||||
id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef;
|
id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef;
|
||||||
id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer];
|
id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer];
|
||||||
[cmdBuffer presentDrawable:drawable];
|
|
||||||
[cmdBuffer commit];
|
[cmdBuffer commit];
|
||||||
|
[cmdBuffer waitUntilScheduled];
|
||||||
|
[drawable present];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -21,7 +21,10 @@ Class gio_layerClass(void) {
|
|||||||
static CFTypeRef getMetalLayer(CFTypeRef viewRef) {
|
static CFTypeRef getMetalLayer(CFTypeRef viewRef) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
UIView *view = (__bridge UIView *)viewRef;
|
UIView *view = (__bridge UIView *)viewRef;
|
||||||
return CFBridgingRetain(view.layer);
|
CAMetalLayer *l = (CAMetalLayer *)view.layer;
|
||||||
|
l.needsDisplayOnBoundsChange = YES;
|
||||||
|
l.presentsWithTransaction = YES;
|
||||||
|
return CFBridgingRetain(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -14,7 +14,11 @@ package app
|
|||||||
|
|
||||||
CALayer *gio_layerFactory(void) {
|
CALayer *gio_layerFactory(void) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
return [CAMetalLayer layer];
|
CAMetalLayer *l = [CAMetalLayer layer];
|
||||||
|
l.autoresizingMask = kCALayerHeightSizable|kCALayerWidthSizable;
|
||||||
|
l.needsDisplayOnBoundsChange = YES;
|
||||||
|
l.presentsWithTransaction = YES;
|
||||||
|
return l;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ type Config struct {
|
|||||||
CustomRenderer bool
|
CustomRenderer bool
|
||||||
// Decorated reports whether window decorations are provided automatically.
|
// Decorated reports whether window decorations are provided automatically.
|
||||||
Decorated bool
|
Decorated bool
|
||||||
|
// Focused reports whether has the keyboard focus.
|
||||||
|
Focused bool
|
||||||
// decoHeight is the height of the fallback decoration for platforms such
|
// decoHeight is the height of the fallback decoration for platforms such
|
||||||
// as Wayland that may need fallback client-side decorations.
|
// as Wayland that may need fallback client-side decorations.
|
||||||
decoHeight unit.Dp
|
decoHeight unit.Dp
|
||||||
|
|||||||
+2
-4
@@ -593,7 +593,8 @@ func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong)
|
|||||||
//export Java_org_gioui_GioView_onFocusChange
|
//export Java_org_gioui_GioView_onFocusChange
|
||||||
func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
|
func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
|
||||||
w := cgo.Handle(view).Value().(*window)
|
w := cgo.Handle(view).Value().(*window)
|
||||||
w.processEvent(key.FocusEvent{Focus: focus == C.JNI_TRUE})
|
w.config.Focused = focus == C.JNI_TRUE
|
||||||
|
w.processEvent(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export Java_org_gioui_GioView_onWindowInsets
|
//export Java_org_gioui_GioView_onWindowInsets
|
||||||
@@ -1316,9 +1317,6 @@ func findClass(env *C.JNIEnv, name string) C.jclass {
|
|||||||
return C.jni_FindClass(env, cn)
|
return C.jni_FindClass(env, cn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osMain() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWindow(window *callbacks, options []Option) {
|
func newWindow(window *callbacks, options []Option) {
|
||||||
mainWindow.in <- windowAndConfig{window, options}
|
mainWindow.in <- windowAndConfig{window, options}
|
||||||
<-mainWindow.windows
|
<-mainWindow.windows
|
||||||
|
|||||||
+7
-10
@@ -15,8 +15,8 @@ __attribute__ ((visibility ("hidden"))) void gio_hideCursor();
|
|||||||
__attribute__ ((visibility ("hidden"))) void gio_showCursor();
|
__attribute__ ((visibility ("hidden"))) void gio_showCursor();
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID);
|
__attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID);
|
||||||
|
|
||||||
static bool isMainThread() {
|
static int isMainThread() {
|
||||||
return [NSThread isMainThread];
|
return [NSThread isMainThread] ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSUInteger nsstringLength(CFTypeRef cstr) {
|
static NSUInteger nsstringLength(CFTypeRef cstr) {
|
||||||
@@ -77,7 +77,7 @@ var mainFuncs = make(chan func(), 1)
|
|||||||
|
|
||||||
// runOnMain runs the function on the main thread.
|
// runOnMain runs the function on the main thread.
|
||||||
func runOnMain(f func()) {
|
func runOnMain(f func()) {
|
||||||
if C.isMainThread() {
|
if isMainThread() {
|
||||||
f()
|
f()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -87,6 +87,10 @@ func runOnMain(f func()) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isMainThread() bool {
|
||||||
|
return C.isMainThread() != 0
|
||||||
|
}
|
||||||
|
|
||||||
//export gio_dispatchMainFuncs
|
//export gio_dispatchMainFuncs
|
||||||
func gio_dispatchMainFuncs() {
|
func gio_dispatchMainFuncs() {
|
||||||
for {
|
for {
|
||||||
@@ -259,10 +263,3 @@ func windowSetCursor(from, to pointer.Cursor) pointer.Cursor {
|
|||||||
C.gio_setCursor(C.NSUInteger(macosCursorID[to]))
|
C.gio_setCursor(C.NSUInteger(macosCursorID[to]))
|
||||||
return to
|
return to
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) wakeup() {
|
|
||||||
runOnMain(func() {
|
|
||||||
w.loop.Wakeup()
|
|
||||||
w.loop.FlushEvents()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
// 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
-4
@@ -211,7 +211,8 @@ func onDestroy(h C.uintptr_t) {
|
|||||||
//export onFocus
|
//export onFocus
|
||||||
func onFocus(h C.uintptr_t, focus int) {
|
func onFocus(h C.uintptr_t, focus int) {
|
||||||
w := viewFor(h)
|
w := viewFor(h)
|
||||||
w.ProcessEvent(key.FocusEvent{Focus: focus != 0})
|
w.config.Focused = focus != 0
|
||||||
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export onLowMemory
|
//export onLowMemory
|
||||||
@@ -387,13 +388,17 @@ func newWindow(win *callbacks, options []Option) {
|
|||||||
<-mainWindow.windows
|
<-mainWindow.windows
|
||||||
}
|
}
|
||||||
|
|
||||||
func osMain() {
|
|
||||||
}
|
|
||||||
|
|
||||||
//export gio_runMain
|
//export gio_runMain
|
||||||
func gio_runMain() {
|
func gio_runMain() {
|
||||||
runMain()
|
runMain()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *window) wakeup() {
|
||||||
|
runOnMain(func() {
|
||||||
|
w.loop.Wakeup()
|
||||||
|
w.loop.FlushEvents()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (UIKitViewEvent) implementsViewEvent() {}
|
func (UIKitViewEvent) implementsViewEvent() {}
|
||||||
func (UIKitViewEvent) ImplementsEvent() {}
|
func (UIKitViewEvent) ImplementsEvent() {}
|
||||||
|
|||||||
@@ -276,3 +276,9 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
|
|||||||
GioView *v = (__bridge GioView *)viewRef;
|
GioView *v = (__bridge GioView *)viewRef;
|
||||||
v.handle = handle;
|
v.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gio_wakeupMainThread(void) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
gio_dispatchMainFuncs();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
+4
-6
@@ -258,11 +258,13 @@ func (w *window) addEventListeners() {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
w.addEventListener(w.tarea, "focus", func(this js.Value, args []js.Value) interface{} {
|
w.addEventListener(w.tarea, "focus", func(this js.Value, args []js.Value) interface{} {
|
||||||
w.processEvent(key.FocusEvent{Focus: true})
|
w.config.Focused = true
|
||||||
|
w.processEvent(ConfigEvent{Config: w.config})
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} {
|
w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} {
|
||||||
w.processEvent(key.FocusEvent{Focus: false})
|
w.config.Focused = false
|
||||||
|
w.processEvent(ConfigEvent{Config: w.config})
|
||||||
w.blur()
|
w.blur()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -739,10 +741,6 @@ func (w *window) navigationColor(c color.NRGBA) {
|
|||||||
theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B}))
|
theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func osMain() {
|
|
||||||
select {}
|
|
||||||
}
|
|
||||||
|
|
||||||
func translateKey(k string) (key.Name, bool) {
|
func translateKey(k string) (key.Name, bool) {
|
||||||
var n key.Name
|
var n key.Name
|
||||||
|
|
||||||
|
|||||||
+76
-22
@@ -39,7 +39,7 @@ import (
|
|||||||
#define MOUSE_DOWN 3
|
#define MOUSE_DOWN 3
|
||||||
#define MOUSE_SCROLL 4
|
#define MOUSE_SCROLL 4
|
||||||
|
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_main(void);
|
__attribute__ ((visibility ("hidden"))) void gio_initApp(void);
|
||||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(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"))) 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);
|
__attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle);
|
||||||
@@ -289,6 +289,16 @@ 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"
|
import "C"
|
||||||
|
|
||||||
@@ -323,13 +333,16 @@ type window struct {
|
|||||||
config Config
|
config Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// launched is closed when applicationDidFinishLaunching is called.
|
// launched is closed after gio_initApp returns.
|
||||||
var launched = make(chan struct{})
|
var launched = make(chan struct{})
|
||||||
|
|
||||||
// nextTopLeft is the offset to use for the next window's call to
|
// nextTopLeft is the offset to use for the next window's call to
|
||||||
// cascadeTopLeftFromPoint.
|
// cascadeTopLeftFromPoint.
|
||||||
var nextTopLeft C.NSPoint
|
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 {
|
func windowFor(h C.uintptr_t) *window {
|
||||||
return cgo.Handle(h).Value().(*window)
|
return cgo.Handle(h).Value().(*window)
|
||||||
}
|
}
|
||||||
@@ -612,8 +625,9 @@ func gio_onDraw(h C.uintptr_t) {
|
|||||||
//export gio_onFocus
|
//export gio_onFocus
|
||||||
func gio_onFocus(h C.uintptr_t, focus C.int) {
|
func gio_onFocus(h C.uintptr_t, focus C.int) {
|
||||||
w := windowFor(h)
|
w := windowFor(h)
|
||||||
w.ProcessEvent(key.FocusEvent{Focus: focus == 1})
|
|
||||||
w.SetCursor(w.cursor)
|
w.SetCursor(w.cursor)
|
||||||
|
w.config.Focused = focus == 1
|
||||||
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onChangeScreen
|
//export gio_onChangeScreen
|
||||||
@@ -821,23 +835,51 @@ func (w *window) draw() {
|
|||||||
|
|
||||||
func (w *window) ProcessEvent(e event.Event) {
|
func (w *window) ProcessEvent(e event.Event) {
|
||||||
w.w.ProcessEvent(e)
|
w.w.ProcessEvent(e)
|
||||||
w.loop.FlushEvents()
|
// The main thread window deliver events in Event.
|
||||||
|
if w != mainThreadWindow {
|
||||||
|
w.loop.FlushEvents()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Event() event.Event {
|
func (w *window) Event() event.Event {
|
||||||
return w.loop.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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Invalidate() {
|
func (w *window) Invalidate() {
|
||||||
|
if isMainThread() {
|
||||||
|
mainThreadWindow = w
|
||||||
|
defer func() { mainThreadWindow = nil }()
|
||||||
|
}
|
||||||
w.loop.Invalidate()
|
w.loop.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Run(f func()) {
|
func (w *window) Run(f func()) {
|
||||||
|
if isMainThread() {
|
||||||
|
mainThreadWindow = w
|
||||||
|
defer func() { mainThreadWindow = nil }()
|
||||||
|
}
|
||||||
w.loop.Run(f)
|
w.loop.Run(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Frame(frame *op.Ops) {
|
func (w *window) Frame(frame *op.Ops) {
|
||||||
w.loop.Frame(frame)
|
if !isMainThread() {
|
||||||
|
w.loop.Frame(frame)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mainThreadWindow = w
|
||||||
|
defer func() { mainThreadWindow = nil }()
|
||||||
|
w.loop.win.ProcessFrame(frame, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configFor(scale float32) unit.Metric {
|
func configFor(scale float32) unit.Metric {
|
||||||
@@ -897,20 +939,27 @@ func gio_onWindowed(h C.uintptr_t) {
|
|||||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onFinishLaunching
|
|
||||||
func gio_onFinishLaunching() {
|
|
||||||
close(launched)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWindow(win *callbacks, options []Option) {
|
func newWindow(win *callbacks, options []Option) {
|
||||||
<-launched
|
w := &window{
|
||||||
res := make(chan struct{})
|
redraw: make(chan struct{}, 1),
|
||||||
runOnMain(func() {
|
w: win,
|
||||||
w := &window{
|
}
|
||||||
redraw: make(chan struct{}, 1),
|
w.loop = newEventLoop(w.w, w.wakeup)
|
||||||
w: win,
|
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)
|
||||||
}
|
}
|
||||||
w.loop = newEventLoop(w.w, w.wakeup)
|
} else {
|
||||||
|
<-launched
|
||||||
|
}
|
||||||
|
res := make(chan struct{}, 1)
|
||||||
|
runOnMain(func() {
|
||||||
win.SetDriver(w)
|
win.SetDriver(w)
|
||||||
res <- struct{}{}
|
res <- struct{}{}
|
||||||
if err := w.init(); err != nil {
|
if err := w.init(); err != nil {
|
||||||
@@ -963,10 +1012,6 @@ func (w *window) init() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osMain() {
|
|
||||||
C.gio_main()
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertKey(k rune) (key.Name, bool) {
|
func convertKey(k rune) (key.Name, bool) {
|
||||||
var n key.Name
|
var n key.Name
|
||||||
switch k {
|
switch k {
|
||||||
@@ -1051,5 +1096,14 @@ func convertMods(mods C.NSUInteger) key.Modifiers {
|
|||||||
return kmods
|
return kmods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *window) wakeup() {
|
||||||
|
runOnMain(func() {
|
||||||
|
w.loop.Wakeup()
|
||||||
|
if w != mainThreadWindow {
|
||||||
|
w.loop.FlushEvents()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (AppKitViewEvent) implementsViewEvent() {}
|
func (AppKitViewEvent) implementsViewEvent() {}
|
||||||
func (AppKitViewEvent) ImplementsEvent() {}
|
func (AppKitViewEvent) ImplementsEvent() {}
|
||||||
|
|||||||
+241
-12
@@ -69,6 +69,212 @@ 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]);
|
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
|
@implementation GioView
|
||||||
- (void)setFrameSize:(NSSize)newSize {
|
- (void)setFrameSize:(NSSize)newSize {
|
||||||
[super setFrameSize:newSize];
|
[super setFrameSize:newSize];
|
||||||
@@ -142,7 +348,6 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
|||||||
// Don't pass commands up the responder chain.
|
// Don't pass commands up the responder chain.
|
||||||
// They will end up in a beep.
|
// They will end up in a beep.
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)hasMarkedText {
|
- (BOOL)hasMarkedText {
|
||||||
int res = gio_hasMarkedText(self.handle);
|
int res = gio_hasMarkedText(self.handle);
|
||||||
return res ? YES : NO;
|
return res ? YES : NO;
|
||||||
@@ -255,14 +460,11 @@ void gio_showCursor() {
|
|||||||
// some cursors are not public, this tries to use a private cursor
|
// some cursors are not public, this tries to use a private cursor
|
||||||
// and uses fallback when the use of private cursor fails.
|
// and uses fallback when the use of private cursor fails.
|
||||||
static void trySetPrivateCursor(SEL cursorName, NSCursor* fallback) {
|
static void trySetPrivateCursor(SEL cursorName, NSCursor* fallback) {
|
||||||
if ([NSCursor respondsToSelector:cursorName]) {
|
NSCursor *cur = lookupPrivateNSCursor(cursorName);
|
||||||
id object = [NSCursor performSelector:cursorName];
|
if (cur == nil) {
|
||||||
if ([object isKindOfClass:[NSCursor class]]) {
|
cur = fallback;
|
||||||
[(NSCursor*)object set];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
[fallback set];
|
[cur set];
|
||||||
}
|
}
|
||||||
|
|
||||||
void gio_setCursor(NSUInteger curID) {
|
void gio_setCursor(NSUInteger curID) {
|
||||||
@@ -361,7 +563,7 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGF
|
|||||||
NSMiniaturizableWindowMask |
|
NSMiniaturizableWindowMask |
|
||||||
NSClosableWindowMask;
|
NSClosableWindowMask;
|
||||||
|
|
||||||
NSWindow* window = [[NSWindow alloc] initWithContentRect:rect
|
GioWindow* window = [[GioWindow alloc] initWithContentRect:rect
|
||||||
styleMask:styleMask
|
styleMask:styleMask
|
||||||
backing:NSBackingStoreBuffered
|
backing:NSBackingStoreBuffered
|
||||||
defer:NO];
|
defer:NO];
|
||||||
@@ -410,13 +612,24 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
|
|||||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||||
[NSApp activateIgnoringOtherApps:YES];
|
[NSApp activateIgnoringOtherApps:YES];
|
||||||
gio_onFinishLaunching();
|
// 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];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
void gio_main() {
|
void gio_initApp() {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
[NSApplication sharedApplication];
|
[GioApplication sharedApplication];
|
||||||
GioAppDelegate *del = [[GioAppDelegate alloc] init];
|
GioAppDelegate *del = [[GioAppDelegate alloc] init];
|
||||||
[NSApp setDelegate:del];
|
[NSApp setDelegate:del];
|
||||||
|
|
||||||
@@ -438,6 +651,22 @@ void gio_main() {
|
|||||||
|
|
||||||
globalWindowDel = [[GioWindowDelegate alloc] init];
|
globalWindowDel = [[GioWindowDelegate alloc] init];
|
||||||
|
|
||||||
|
// Runs until stopped by applicationDidFinishLaunching.
|
||||||
[NSApp run];
|
[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,10 +33,6 @@ type WaylandViewEvent struct {
|
|||||||
func (WaylandViewEvent) implementsViewEvent() {}
|
func (WaylandViewEvent) implementsViewEvent() {}
|
||||||
func (WaylandViewEvent) ImplementsEvent() {}
|
func (WaylandViewEvent) ImplementsEvent() {}
|
||||||
|
|
||||||
func osMain() {
|
|
||||||
select {}
|
|
||||||
}
|
|
||||||
|
|
||||||
type windowDriver func(*callbacks, []Option) error
|
type windowDriver func(*callbacks, []Option) error
|
||||||
|
|
||||||
// Instead of creating files with build tags for each combination of wayland +/- x11
|
// Instead of creating files with build tags for each combination of wayland +/- x11
|
||||||
|
|||||||
+4
-2
@@ -1222,7 +1222,8 @@ func gio_onKeyboardEnter(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, se
|
|||||||
w := callbackLoad(unsafe.Pointer(surf)).(*window)
|
w := callbackLoad(unsafe.Pointer(surf)).(*window)
|
||||||
s.keyboardFocus = w
|
s.keyboardFocus = w
|
||||||
s.disp.repeat.Stop(0)
|
s.disp.repeat.Stop(0)
|
||||||
w.ProcessEvent(key.FocusEvent{Focus: true})
|
w.config.Focused = true
|
||||||
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onKeyboardLeave
|
//export gio_onKeyboardLeave
|
||||||
@@ -1231,7 +1232,8 @@ func gio_onKeyboardLeave(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, se
|
|||||||
s.serial = serial
|
s.serial = serial
|
||||||
s.disp.repeat.Stop(0)
|
s.disp.repeat.Stop(0)
|
||||||
w := s.keyboardFocus
|
w := s.keyboardFocus
|
||||||
w.ProcessEvent(key.FocusEvent{Focus: false})
|
w.config.Focused = false
|
||||||
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onKeyboardKey
|
//export gio_onKeyboardKey
|
||||||
|
|||||||
+5
-10
@@ -50,7 +50,6 @@ type window struct {
|
|||||||
placement *windows.WindowPlacement
|
placement *windows.WindowPlacement
|
||||||
|
|
||||||
animating bool
|
animating bool
|
||||||
focused bool
|
|
||||||
|
|
||||||
borderSize image.Point
|
borderSize image.Point
|
||||||
config Config
|
config Config
|
||||||
@@ -86,10 +85,6 @@ var resources struct {
|
|||||||
cursor syscall.Handle
|
cursor syscall.Handle
|
||||||
}
|
}
|
||||||
|
|
||||||
func osMain() {
|
|
||||||
select {}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWindow(win *callbacks, options []Option) {
|
func newWindow(win *callbacks, options []Option) {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
@@ -269,11 +264,11 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
|||||||
Kind: pointer.Cancel,
|
Kind: pointer.Cancel,
|
||||||
})
|
})
|
||||||
case windows.WM_SETFOCUS:
|
case windows.WM_SETFOCUS:
|
||||||
w.focused = true
|
w.config.Focused = true
|
||||||
w.ProcessEvent(key.FocusEvent{Focus: true})
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
case windows.WM_KILLFOCUS:
|
case windows.WM_KILLFOCUS:
|
||||||
w.focused = false
|
w.config.Focused = false
|
||||||
w.ProcessEvent(key.FocusEvent{Focus: false})
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
case windows.WM_NCHITTEST:
|
case windows.WM_NCHITTEST:
|
||||||
if w.config.Decorated {
|
if w.config.Decorated {
|
||||||
// Let the system handle it.
|
// Let the system handle it.
|
||||||
@@ -496,7 +491,7 @@ func (w *window) hitTest(x, y int) uintptr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) pointerButton(btn pointer.Buttons, press bool, lParam uintptr, kmods key.Modifiers) {
|
func (w *window) pointerButton(btn pointer.Buttons, press bool, lParam uintptr, kmods key.Modifiers) {
|
||||||
if !w.focused {
|
if !w.config.Focused {
|
||||||
windows.SetFocus(w.hwnd)
|
windows.SetFocus(w.hwnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -657,9 +657,11 @@ func (h *x11EventHandler) handleEvents() bool {
|
|||||||
// redraw only on the last expose event
|
// redraw only on the last expose event
|
||||||
redraw = (*C.XExposeEvent)(unsafe.Pointer(xev)).count == 0
|
redraw = (*C.XExposeEvent)(unsafe.Pointer(xev)).count == 0
|
||||||
case C.FocusIn:
|
case C.FocusIn:
|
||||||
w.ProcessEvent(key.FocusEvent{Focus: true})
|
w.config.Focused = true
|
||||||
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
case C.FocusOut:
|
case C.FocusOut:
|
||||||
w.ProcessEvent(key.FocusEvent{Focus: false})
|
w.config.Focused = false
|
||||||
|
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||||
case C.ConfigureNotify: // window configuration change
|
case C.ConfigureNotify: // window configuration change
|
||||||
cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev))
|
cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev))
|
||||||
if sz := image.Pt(int(cevt.width), int(cevt.height)); sz != w.config.Size {
|
if sz := image.Pt(int(cevt.width), int(cevt.height)); sz != w.config.Size {
|
||||||
|
|||||||
+1
-1
@@ -25,6 +25,6 @@ func runMain() {
|
|||||||
// Indirect call, since the linker does not know the address of main when
|
// Indirect call, since the linker does not know the address of main when
|
||||||
// laying down this package.
|
// laying down this package.
|
||||||
fn := mainMain
|
fn := mainMain
|
||||||
fn()
|
go fn()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -630,9 +630,19 @@ func (w *Window) processEvent(e event.Event) bool {
|
|||||||
}
|
}
|
||||||
w.coalesced.view = &e2
|
w.coalesced.view = &e2
|
||||||
case ConfigEvent:
|
case ConfigEvent:
|
||||||
|
wasFocused := w.decorations.Config.Focused
|
||||||
w.decorations.Config = e2.Config
|
w.decorations.Config = e2.Config
|
||||||
e2.Config = w.effectiveConfig()
|
e2.Config = w.effectiveConfig()
|
||||||
w.coalesced.cfg = &e2
|
w.coalesced.cfg = &e2
|
||||||
|
if f := w.decorations.Config.Focused; f != wasFocused {
|
||||||
|
w.queue.Queue(key.FocusEvent{Focus: f})
|
||||||
|
}
|
||||||
|
t, handled := w.queue.WakeupTime()
|
||||||
|
if handled {
|
||||||
|
w.setNextFrame(t)
|
||||||
|
w.updateAnimation()
|
||||||
|
}
|
||||||
|
return handled
|
||||||
case event.Event:
|
case event.Event:
|
||||||
focusDir := key.FocusDirection(-1)
|
focusDir := key.FocusDirection(-1)
|
||||||
if e, ok := e2.(key.Event); ok && e.State == key.Press {
|
if e, ok := e2.(key.Event); ok && e.State == key.Press {
|
||||||
@@ -674,6 +684,13 @@ func (w *Window) processEvent(e event.Event) bool {
|
|||||||
|
|
||||||
// Event blocks until an event is received from the window, such as
|
// Event blocks until an event is received from the window, such as
|
||||||
// [FrameEvent], or until [Invalidate] is called.
|
// [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 {
|
func (w *Window) Event() event.Event {
|
||||||
w.init()
|
w.init()
|
||||||
return w.basic.Event()
|
return w.basic.Event()
|
||||||
|
|||||||
Reference in New Issue
Block a user