mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f200f0e9a3 | |||
| 1ae2b9b8fe | |||
| fe4bf00c70 | |||
| 8107ec2206 | |||
| d2db4f6875 | |||
| 971f86ea7e | |||
| e1fbb189e5 | |||
| a206e5e847 | |||
| e025ed1344 | |||
| b576252963 | |||
| 1d0d5f0383 | |||
| 26cddb00b1 | |||
| 0cbbacc45a | |||
| 1d95c7c6b3 | |||
| 7337c06daf | |||
| 94355e5201 | |||
| ea456f42c7 | |||
| 8daff13af6 | |||
| aa158e0c9c | |||
| 520efdfa75 | |||
| d7a1ec7461 | |||
| d4c5e54375 | |||
| 44ac50506d | |||
| 38e4b1c6a4 | |||
| 5d886b4d7f | |||
| c7277581f8 | |||
| a5f7e7b2c7 | |||
| 0781e62b56 | |||
| 95f63c66b6 | |||
| 6efcb65c4b | |||
| f6e9f6861d | |||
| 6c6cc157c4 | |||
| 8cf449034c | |||
| 6722c7960a | |||
| 97044e53b5 | |||
| af6dda67a5 | |||
| 042ed4ab49 | |||
| 6aa027136e | |||
| b1db32ef72 | |||
| 9dceca6c95 | |||
| 7293fa8a41 | |||
| 82cbb7b8da | |||
| 6c19821a6c |
+1
-1
@@ -71,4 +71,4 @@ tasks:
|
||||
CC=$APPLE_TOOLCHAIN_ROOT/tools/clang-macos GOOS=darwin CGO_ENABLED=1 go build ./...
|
||||
- test_ios: |
|
||||
cd gio
|
||||
CC=$APPLE_TOOLCHAIN_ROOT/tools/clang-ios GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build -tags ios ./...
|
||||
CGO_CFLAGS=-Wno-deprecated-module-dot-map CC=$APPLE_TOOLCHAIN_ROOT/tools/clang-ios GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build -tags ios ./...
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: Unlicense OR MIT
|
||||
image: freebsd/13.x
|
||||
image: freebsd/latest
|
||||
packages:
|
||||
- libX11
|
||||
- libxkbcommon
|
||||
|
||||
@@ -259,6 +259,10 @@ public final class GioView extends SurfaceView implements Choreographer.FrameCal
|
||||
}
|
||||
|
||||
private void setHighRefreshRate() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
return;
|
||||
}
|
||||
|
||||
Context context = getContext();
|
||||
Display display = context.getDisplay();
|
||||
Display.Mode[] supportedModes = display.getSupportedModes();
|
||||
|
||||
@@ -274,6 +274,7 @@ const (
|
||||
WM_SETFOCUS = 0x0007
|
||||
WM_SHOWWINDOW = 0x0018
|
||||
WM_SIZE = 0x0005
|
||||
WM_STYLECHANGED = 0x007D
|
||||
WM_SYSKEYDOWN = 0x0104
|
||||
WM_SYSKEYUP = 0x0105
|
||||
WM_RBUTTONDOWN = 0x0204
|
||||
|
||||
+3
-2
@@ -4,14 +4,15 @@ package app
|
||||
|
||||
import (
|
||||
"log"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
syscall "golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type logger struct{}
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32")
|
||||
kernel32 = syscall.NewLazySystemDLL("kernel32")
|
||||
outputDebugStringW = kernel32.NewProc("OutputDebugStringW")
|
||||
debugView *logger
|
||||
)
|
||||
|
||||
@@ -195,8 +195,6 @@ type driver interface {
|
||||
Configure([]Option)
|
||||
// SetCursor updates the current cursor to name.
|
||||
SetCursor(cursor pointer.Cursor)
|
||||
// Wakeup wakes up the event loop and sends a WakeupEvent.
|
||||
// Wakeup()
|
||||
// Perform actions on the window.
|
||||
Perform(system.Action)
|
||||
// EditorStateChanged notifies the driver that the editor state changed.
|
||||
|
||||
+4
-4
@@ -575,10 +575,7 @@ func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view
|
||||
if !exist {
|
||||
return
|
||||
}
|
||||
if w.visible && w.animating {
|
||||
w.draw(env, false)
|
||||
callVoidMethod(env, w.view, gioView.postFrameCallback)
|
||||
}
|
||||
w.draw(env, false)
|
||||
}
|
||||
|
||||
//export Java_org_gioui_GioView_onBack
|
||||
@@ -882,6 +879,9 @@ func (w *window) draw(env *C.JNIEnv, sync bool) {
|
||||
},
|
||||
Sync: sync,
|
||||
})
|
||||
if w.animating {
|
||||
callVoidMethod(env, w.view, gioView.postFrameCallback)
|
||||
}
|
||||
a11yActive, err := callBooleanMethod(env, w.view, gioView.isA11yActive)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
+227
-138
@@ -109,6 +109,14 @@ static void makeKeyAndOrderFront(CFTypeRef windowRef) {
|
||||
}
|
||||
}
|
||||
|
||||
static void makeFirstResponder(CFTypeRef windowRef, CFTypeRef viewRef) {
|
||||
@autoreleasepool {
|
||||
NSWindow *window = (__bridge NSWindow *)windowRef;
|
||||
NSView *view = (__bridge NSView *)viewRef;
|
||||
[window makeFirstResponder:view];
|
||||
}
|
||||
}
|
||||
|
||||
static void toggleFullScreen(CFTypeRef windowRef) {
|
||||
@autoreleasepool {
|
||||
NSWindow *window = (__bridge NSWindow *)windowRef;
|
||||
@@ -238,6 +246,13 @@ static int isWindowZoomed(CFTypeRef windowRef) {
|
||||
}
|
||||
}
|
||||
|
||||
static int isWindowMiniaturized(CFTypeRef windowRef) {
|
||||
@autoreleasepool {
|
||||
NSWindow *window = (__bridge NSWindow *)windowRef;
|
||||
return window.miniaturized ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void zoomWindow(CFTypeRef windowRef) {
|
||||
@autoreleasepool {
|
||||
NSWindow *window = (__bridge NSWindow *)windowRef;
|
||||
@@ -297,6 +312,21 @@ static void invalidateCharacterCoordinates(CFTypeRef viewRef) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void interpretKeyEvents(CFTypeRef viewRef, CFTypeRef eventRef) {
|
||||
@autoreleasepool {
|
||||
NSView *view = (__bridge NSView *)viewRef;
|
||||
NSEvent *event = (__bridge NSEvent *)eventRef;
|
||||
[view interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||
}
|
||||
}
|
||||
|
||||
static int isMiniaturized(CFTypeRef windowRef) {
|
||||
@autoreleasepool {
|
||||
NSWindow *window = (__bridge NSWindow *)windowRef;
|
||||
return window.miniaturized ? 1 : 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -318,7 +348,6 @@ type window struct {
|
||||
view C.CFTypeRef
|
||||
w *callbacks
|
||||
anim bool
|
||||
visible bool
|
||||
displayLink *displayLink
|
||||
// redraw is a single entry channel for making sure only one
|
||||
// display link redraw request is in flight.
|
||||
@@ -326,9 +355,20 @@ type window struct {
|
||||
cursor pointer.Cursor
|
||||
pointerBtns pointer.Buttons
|
||||
loop *eventLoop
|
||||
lastMods C.NSUInteger
|
||||
|
||||
scale float32
|
||||
config Config
|
||||
|
||||
keysDown map[key.Name]struct{}
|
||||
// cmdKeys is for storing the current key event while
|
||||
// waiting for a doCommandBySelector.
|
||||
cmdKeys cmdKeys
|
||||
}
|
||||
|
||||
type cmdKeys struct {
|
||||
eventStr string
|
||||
eventMods key.Modifiers
|
||||
}
|
||||
|
||||
// launched is closed when applicationDidFinishLaunching is called.
|
||||
@@ -367,11 +407,23 @@ func (w *window) WriteClipboard(mime string, s []byte) {
|
||||
}
|
||||
|
||||
func (w *window) updateWindowMode() {
|
||||
w.scale = float32(C.getViewBackingScale(w.view))
|
||||
wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view))
|
||||
w.config.Size = image.Point{
|
||||
X: int(wf*w.scale + .5),
|
||||
Y: int(hf*w.scale + .5),
|
||||
}
|
||||
w.config.Mode = Windowed
|
||||
window := C.windowForView(w.view)
|
||||
if window == 0 {
|
||||
return
|
||||
}
|
||||
style := int(C.getWindowStyleMask(C.windowForView(w.view)))
|
||||
if style&C.NSWindowStyleMaskFullScreen != 0 {
|
||||
switch {
|
||||
case style&C.NSWindowStyleMaskFullScreen != 0:
|
||||
w.config.Mode = Fullscreen
|
||||
} else {
|
||||
w.config.Mode = Windowed
|
||||
case C.isWindowZoomed(window) != 0:
|
||||
w.config.Mode = Maximized
|
||||
}
|
||||
w.config.Decorated = style&C.NSWindowStyleMaskFullSizeContentView == 0
|
||||
}
|
||||
@@ -379,105 +431,82 @@ func (w *window) updateWindowMode() {
|
||||
func (w *window) Configure(options []Option) {
|
||||
screenScale := float32(C.getScreenBackingScale())
|
||||
cfg := configFor(screenScale)
|
||||
prev := w.config
|
||||
w.updateWindowMode()
|
||||
cnf := w.config
|
||||
cnf.apply(cfg, options)
|
||||
window := C.windowForView(w.view)
|
||||
|
||||
mask := C.getWindowStyleMask(window)
|
||||
fullscreen := mask&C.NSWindowStyleMaskFullScreen != 0
|
||||
switch cnf.Mode {
|
||||
case Fullscreen:
|
||||
switch prev.Mode {
|
||||
case Fullscreen:
|
||||
case Minimized:
|
||||
if C.isWindowMiniaturized(window) != 0 {
|
||||
C.unhideWindow(window)
|
||||
fallthrough
|
||||
default:
|
||||
w.config.Mode = Fullscreen
|
||||
}
|
||||
if !fullscreen {
|
||||
C.toggleFullScreen(window)
|
||||
}
|
||||
case Minimized:
|
||||
switch prev.Mode {
|
||||
case Minimized, Fullscreen:
|
||||
default:
|
||||
w.config.Mode = Minimized
|
||||
C.hideWindow(window)
|
||||
}
|
||||
C.hideWindow(window)
|
||||
case Maximized:
|
||||
switch prev.Mode {
|
||||
case Fullscreen:
|
||||
case Minimized:
|
||||
if C.isWindowMiniaturized(window) != 0 {
|
||||
C.unhideWindow(window)
|
||||
fallthrough
|
||||
default:
|
||||
w.config.Mode = Maximized
|
||||
w.setTitle(prev, cnf)
|
||||
if C.isWindowZoomed(window) == 0 {
|
||||
C.zoomWindow(window)
|
||||
}
|
||||
}
|
||||
if fullscreen {
|
||||
C.toggleFullScreen(window)
|
||||
}
|
||||
w.setTitle(cnf.Title)
|
||||
if C.isWindowZoomed(window) == 0 {
|
||||
C.zoomWindow(window)
|
||||
}
|
||||
case Windowed:
|
||||
switch prev.Mode {
|
||||
case Fullscreen:
|
||||
C.toggleFullScreen(window)
|
||||
case Minimized:
|
||||
if C.isWindowMiniaturized(window) != 0 {
|
||||
C.unhideWindow(window)
|
||||
case Maximized:
|
||||
if C.isWindowZoomed(window) != 0 {
|
||||
C.zoomWindow(window)
|
||||
}
|
||||
}
|
||||
w.config.Mode = Windowed
|
||||
w.setTitle(prev, cnf)
|
||||
if prev.Size != cnf.Size {
|
||||
w.config.Size = cnf.Size
|
||||
cnf.Size = cnf.Size.Div(int(screenScale))
|
||||
C.setSize(window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
|
||||
if fullscreen {
|
||||
C.toggleFullScreen(window)
|
||||
}
|
||||
if prev.MinSize != cnf.MinSize {
|
||||
w.config.MinSize = cnf.MinSize
|
||||
cnf.MinSize = cnf.MinSize.Div(int(screenScale))
|
||||
C.setMinSize(window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
|
||||
}
|
||||
if prev.MaxSize != cnf.MaxSize {
|
||||
w.config.MaxSize = cnf.MaxSize
|
||||
cnf.MaxSize = cnf.MaxSize.Div(int(screenScale))
|
||||
w.setTitle(cnf.Title)
|
||||
w.config.Size = cnf.Size
|
||||
cnf.Size = cnf.Size.Div(int(screenScale))
|
||||
C.setSize(window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
|
||||
w.config.MinSize = cnf.MinSize
|
||||
cnf.MinSize = cnf.MinSize.Div(int(screenScale))
|
||||
C.setMinSize(window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
|
||||
w.config.MaxSize = cnf.MaxSize
|
||||
cnf.MaxSize = cnf.MaxSize.Div(int(screenScale))
|
||||
if cnf.MaxSize != (image.Point{}) {
|
||||
C.setMaxSize(window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y))
|
||||
}
|
||||
}
|
||||
if cnf.Decorated != prev.Decorated {
|
||||
w.config.Decorated = cnf.Decorated
|
||||
mask := C.getWindowStyleMask(window)
|
||||
style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable)
|
||||
style = C.NSWindowStyleMaskFullSizeContentView
|
||||
mask &^= style
|
||||
barTrans := C.int(C.NO)
|
||||
titleVis := C.NSWindowTitleVisibility(C.NSWindowTitleVisible)
|
||||
if !cnf.Decorated {
|
||||
mask |= style
|
||||
barTrans = C.YES
|
||||
titleVis = C.NSWindowTitleHidden
|
||||
if C.isWindowZoomed(window) != 0 {
|
||||
C.zoomWindow(window)
|
||||
}
|
||||
C.setWindowTitlebarAppearsTransparent(window, barTrans)
|
||||
C.setWindowTitleVisibility(window, titleVis)
|
||||
C.setWindowStyleMask(window, mask)
|
||||
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})
|
||||
style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable)
|
||||
style = C.NSWindowStyleMaskFullSizeContentView
|
||||
mask &^= style
|
||||
barTrans := C.int(C.NO)
|
||||
titleVis := C.NSWindowTitleVisibility(C.NSWindowTitleVisible)
|
||||
if !cnf.Decorated {
|
||||
mask |= style
|
||||
barTrans = C.YES
|
||||
titleVis = C.NSWindowTitleHidden
|
||||
}
|
||||
C.setWindowTitlebarAppearsTransparent(window, barTrans)
|
||||
C.setWindowTitleVisibility(window, titleVis)
|
||||
C.setWindowStyleMask(window, mask)
|
||||
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)
|
||||
}
|
||||
|
||||
func (w *window) setTitle(prev, cnf Config) {
|
||||
if prev.Title != cnf.Title {
|
||||
w.config.Title = cnf.Title
|
||||
title := stringToNSString(cnf.Title)
|
||||
defer C.CFRelease(title)
|
||||
C.setTitle(C.windowForView(w.view), title)
|
||||
}
|
||||
func (w *window) setTitle(title string) {
|
||||
w.config.Title = title
|
||||
titleC := stringToNSString(title)
|
||||
defer C.CFRelease(titleC)
|
||||
C.setTitle(C.windowForView(w.view), titleC)
|
||||
}
|
||||
|
||||
func (w *window) Perform(acts system.Action) {
|
||||
@@ -520,7 +549,8 @@ func (w *window) SetInputHint(_ key.InputHint) {}
|
||||
|
||||
func (w *window) SetAnimating(anim bool) {
|
||||
w.anim = anim
|
||||
if w.anim && w.visible {
|
||||
window := C.windowForView(w.view)
|
||||
if w.anim && window != 0 && C.isMiniaturized(window) == 0 {
|
||||
w.displayLink.Start()
|
||||
} else {
|
||||
w.displayLink.Stop()
|
||||
@@ -538,23 +568,92 @@ func (w *window) runOnMain(f func()) {
|
||||
}
|
||||
|
||||
//export gio_onKeys
|
||||
func gio_onKeys(h C.uintptr_t, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
|
||||
func gio_onKeys(h C.uintptr_t, event C.CFTypeRef, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
|
||||
w := windowFor(h)
|
||||
if w.keysDown == nil {
|
||||
w.keysDown = make(map[key.Name]struct{})
|
||||
}
|
||||
str := nsstringToString(cstr)
|
||||
kmods := convertMods(mods)
|
||||
ks := key.Release
|
||||
if keyDown {
|
||||
ks = key.Press
|
||||
w.cmdKeys.eventStr = str
|
||||
w.cmdKeys.eventMods = kmods
|
||||
C.interpretKeyEvents(w.view, event)
|
||||
}
|
||||
w := windowFor(h)
|
||||
for _, k := range str {
|
||||
if n, ok := convertKey(k); ok {
|
||||
w.ProcessEvent(key.Event{
|
||||
ke := key.Event{
|
||||
Name: n,
|
||||
Modifiers: kmods,
|
||||
State: ks,
|
||||
}
|
||||
if keyDown {
|
||||
w.keysDown[ke.Name] = struct{}{}
|
||||
if _, isCmd := convertCommandKey(k); isCmd || kmods.Contain(key.ModCommand) {
|
||||
// doCommandBySelector already processed the event.
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if _, pressed := w.keysDown[n]; !pressed {
|
||||
continue
|
||||
}
|
||||
delete(w.keysDown, n)
|
||||
}
|
||||
w.ProcessEvent(ke)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//export gio_onCommandBySelector
|
||||
func gio_onCommandBySelector(h C.uintptr_t) C.bool {
|
||||
w := windowFor(h)
|
||||
ev := w.cmdKeys
|
||||
w.cmdKeys = cmdKeys{}
|
||||
handled := false
|
||||
for _, k := range ev.eventStr {
|
||||
n, ok := convertCommandKey(k)
|
||||
if !ok && ev.eventMods.Contain(key.ModCommand) {
|
||||
n, ok = convertKey(k)
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
ke := key.Event{
|
||||
Name: n,
|
||||
Modifiers: ev.eventMods,
|
||||
State: key.Press,
|
||||
}
|
||||
handled = w.processEvent(ke) || handled
|
||||
}
|
||||
return C.bool(handled)
|
||||
}
|
||||
|
||||
//export gio_onFlagsChanged
|
||||
func gio_onFlagsChanged(h C.uintptr_t, curMods C.NSUInteger) {
|
||||
w := windowFor(h)
|
||||
|
||||
mods := []C.NSUInteger{C.NSControlKeyMask, C.NSAlternateKeyMask, C.NSShiftKeyMask, C.NSCommandKeyMask}
|
||||
keys := []key.Name{key.NameCtrl, key.NameAlt, key.NameShift, key.NameCommand}
|
||||
|
||||
for i, mod := range mods {
|
||||
wasPressed := w.lastMods&mod != 0
|
||||
isPressed := curMods&mod != 0
|
||||
|
||||
if wasPressed != isPressed {
|
||||
st := key.Release
|
||||
if isPressed {
|
||||
st = key.Press
|
||||
}
|
||||
w.ProcessEvent(key.Event{
|
||||
Name: keys[i],
|
||||
State: st,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
w.lastMods = curMods
|
||||
}
|
||||
|
||||
//export gio_onText
|
||||
@@ -747,6 +846,15 @@ func gio_substringForProposedRange(h C.uintptr_t, crng C.NSRange, actual C.NSRan
|
||||
//export gio_insertText
|
||||
func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) {
|
||||
w := windowFor(h)
|
||||
str := nsstringToString(cstr)
|
||||
// macOS IME in some cases calls insertText for command keys such as backspace
|
||||
// instead of doCommandBySelector.
|
||||
for _, r := range str {
|
||||
if _, ok := convertCommandKey(r); ok {
|
||||
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
|
||||
return
|
||||
}
|
||||
}
|
||||
state := w.w.EditorState()
|
||||
rng := state.compose
|
||||
if rng.Start == -1 {
|
||||
@@ -758,7 +866,6 @@ func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) {
|
||||
End: state.RunesIndex(int(crng.location + crng.length)),
|
||||
}
|
||||
}
|
||||
str := nsstringToString(cstr)
|
||||
w.w.EditorReplace(rng, str)
|
||||
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
|
||||
start := rng.Start
|
||||
@@ -799,24 +906,19 @@ func gio_firstRectForCharacterRange(h C.uintptr_t, crng C.NSRange, actual C.NSRa
|
||||
}
|
||||
|
||||
func (w *window) draw() {
|
||||
cnf := w.config
|
||||
w.updateWindowMode()
|
||||
if w.config != cnf {
|
||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||
}
|
||||
select {
|
||||
case <-w.redraw:
|
||||
default:
|
||||
}
|
||||
w.visible = true
|
||||
if w.anim {
|
||||
w.SetAnimating(w.anim)
|
||||
}
|
||||
w.scale = float32(C.getViewBackingScale(w.view))
|
||||
wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view))
|
||||
sz := image.Point{
|
||||
X: int(wf*w.scale + .5),
|
||||
Y: int(hf*w.scale + .5),
|
||||
}
|
||||
if sz != w.config.Size {
|
||||
w.config.Size = sz
|
||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||
}
|
||||
sz := w.config.Size
|
||||
if sz.X == 0 || sz.Y == 0 {
|
||||
return
|
||||
}
|
||||
@@ -824,7 +926,7 @@ func (w *window) draw() {
|
||||
w.ProcessEvent(frameEvent{
|
||||
FrameEvent: FrameEvent{
|
||||
Now: time.Now(),
|
||||
Size: w.config.Size,
|
||||
Size: sz,
|
||||
Metric: cfg,
|
||||
},
|
||||
Sync: true,
|
||||
@@ -832,8 +934,13 @@ func (w *window) draw() {
|
||||
}
|
||||
|
||||
func (w *window) ProcessEvent(e event.Event) {
|
||||
w.w.ProcessEvent(e)
|
||||
w.processEvent(e)
|
||||
}
|
||||
|
||||
func (w *window) processEvent(e event.Event) bool {
|
||||
handled := w.w.ProcessEvent(e)
|
||||
w.loop.FlushEvents()
|
||||
return handled
|
||||
}
|
||||
|
||||
func (w *window) Event() event.Event {
|
||||
@@ -867,7 +974,6 @@ func gio_onAttached(h C.uintptr_t, attached C.int) {
|
||||
w.ProcessEvent(AppKitViewEvent{View: uintptr(w.view), Layer: uintptr(layer)})
|
||||
} else {
|
||||
w.ProcessEvent(AppKitViewEvent{})
|
||||
w.visible = false
|
||||
w.SetAnimating(w.anim)
|
||||
}
|
||||
}
|
||||
@@ -882,33 +988,6 @@ func gio_onDestroy(h C.uintptr_t) {
|
||||
w.view = 0
|
||||
}
|
||||
|
||||
//export gio_onHide
|
||||
func gio_onHide(h C.uintptr_t) {
|
||||
w := windowFor(h)
|
||||
w.visible = false
|
||||
w.SetAnimating(w.anim)
|
||||
}
|
||||
|
||||
//export gio_onShow
|
||||
func gio_onShow(h C.uintptr_t) {
|
||||
w := windowFor(h)
|
||||
w.draw()
|
||||
}
|
||||
|
||||
//export gio_onFullscreen
|
||||
func gio_onFullscreen(h C.uintptr_t) {
|
||||
w := windowFor(h)
|
||||
w.config.Mode = Fullscreen
|
||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||
}
|
||||
|
||||
//export gio_onWindowed
|
||||
func gio_onWindowed(h C.uintptr_t) {
|
||||
w := windowFor(h)
|
||||
w.config.Mode = Windowed
|
||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||
}
|
||||
|
||||
//export gio_onFinishLaunching
|
||||
func gio_onFinishLaunching() {
|
||||
close(launched)
|
||||
@@ -931,10 +1010,9 @@ func newWindow(win *callbacks, options []Option) {
|
||||
w.ProcessEvent(DestroyEvent{Err: err})
|
||||
return
|
||||
}
|
||||
window := C.gio_createWindow(w.view, 0, 0, 0, 0, 0, 0)
|
||||
window := C.gio_createWindow(w.view, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y), 0, 0, 0, 0)
|
||||
// Release our reference now that the NSWindow has it.
|
||||
C.CFRelease(w.view)
|
||||
w.updateWindowMode()
|
||||
w.Configure(options)
|
||||
if nextTopLeft.x == 0 && nextTopLeft.y == 0 {
|
||||
// cascadeTopLeftFromPoint treats (0, 0) as a no-op,
|
||||
@@ -942,6 +1020,7 @@ func newWindow(win *callbacks, options []Option) {
|
||||
nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft)
|
||||
}
|
||||
nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft)
|
||||
C.makeFirstResponder(window, w.view)
|
||||
// makeKeyAndOrderFront assumes ownership of our window reference.
|
||||
C.makeKeyAndOrderFront(window)
|
||||
})
|
||||
@@ -966,9 +1045,7 @@ func (w *window) init(customRenderer bool) error {
|
||||
return
|
||||
}
|
||||
w.runOnMain(func() {
|
||||
if w.visible {
|
||||
C.setNeedsDisplay(w.view)
|
||||
}
|
||||
C.setNeedsDisplay(w.view)
|
||||
})
|
||||
})
|
||||
w.displayLink = dl
|
||||
@@ -988,10 +1065,10 @@ func osMain() {
|
||||
C.gio_main()
|
||||
}
|
||||
|
||||
func convertKey(k rune) (key.Name, bool) {
|
||||
func convertCommandKey(k rune) (key.Name, bool) {
|
||||
var n key.Name
|
||||
switch k {
|
||||
case 0x1b:
|
||||
case '\x1b': // ASCII escape.
|
||||
n = key.NameEscape
|
||||
case C.NSLeftArrowFunctionKey:
|
||||
n = key.NameLeftArrow
|
||||
@@ -1001,22 +1078,36 @@ func convertKey(k rune) (key.Name, bool) {
|
||||
n = key.NameUpArrow
|
||||
case C.NSDownArrowFunctionKey:
|
||||
n = key.NameDownArrow
|
||||
case 0xd:
|
||||
case '\r':
|
||||
n = key.NameReturn
|
||||
case 0x3:
|
||||
case '\x03':
|
||||
n = key.NameEnter
|
||||
case C.NSHomeFunctionKey:
|
||||
n = key.NameHome
|
||||
case C.NSEndFunctionKey:
|
||||
n = key.NameEnd
|
||||
case 0x7f:
|
||||
case '\x7f', '\b':
|
||||
n = key.NameDeleteBackward
|
||||
case C.NSDeleteFunctionKey:
|
||||
n = key.NameDeleteForward
|
||||
case '\t', 0x19:
|
||||
n = key.NameTab
|
||||
case C.NSPageUpFunctionKey:
|
||||
n = key.NamePageUp
|
||||
case C.NSPageDownFunctionKey:
|
||||
n = key.NamePageDown
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
||||
func convertKey(k rune) (key.Name, bool) {
|
||||
if n, ok := convertCommandKey(k); ok {
|
||||
return n, true
|
||||
}
|
||||
var n key.Name
|
||||
switch k {
|
||||
case C.NSF1FunctionKey:
|
||||
n = key.NameF1
|
||||
case C.NSF2FunctionKey:
|
||||
@@ -1041,8 +1132,6 @@ func convertKey(k rune) (key.Name, bool) {
|
||||
n = key.NameF11
|
||||
case C.NSF12FunctionKey:
|
||||
n = key.NameF12
|
||||
case 0x09, 0x19:
|
||||
n = key.NameTab
|
||||
case 0x20:
|
||||
n = key.NameSpace
|
||||
default:
|
||||
|
||||
+16
-14
@@ -23,22 +23,22 @@ __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWi
|
||||
- (void)windowWillMiniaturize:(NSNotification *)notification {
|
||||
NSWindow *window = (NSWindow *)[notification object];
|
||||
GioView *view = (GioView *)window.contentView;
|
||||
gio_onHide(view.handle);
|
||||
gio_onDraw(view.handle);
|
||||
}
|
||||
- (void)windowDidDeminiaturize:(NSNotification *)notification {
|
||||
NSWindow *window = (NSWindow *)[notification object];
|
||||
GioView *view = (GioView *)window.contentView;
|
||||
gio_onShow(view.handle);
|
||||
gio_onDraw(view.handle);
|
||||
}
|
||||
- (void)windowWillEnterFullScreen:(NSNotification *)notification {
|
||||
NSWindow *window = (NSWindow *)[notification object];
|
||||
GioView *view = (GioView *)window.contentView;
|
||||
gio_onFullscreen(view.handle);
|
||||
gio_onDraw(view.handle);
|
||||
}
|
||||
- (void)windowWillExitFullScreen:(NSNotification *)notification {
|
||||
NSWindow *window = (NSWindow *)[notification object];
|
||||
GioView *view = (GioView *)window.contentView;
|
||||
gio_onWindowed(view.handle);
|
||||
gio_onDraw(view.handle);
|
||||
}
|
||||
- (void)windowDidChangeScreen:(NSNotification *)notification {
|
||||
NSWindow *window = (NSWindow *)[notification object];
|
||||
@@ -132,22 +132,25 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
||||
handleMouse(self, event, MOUSE_SCROLL, dx, dy);
|
||||
}
|
||||
- (void)keyDown:(NSEvent *)event {
|
||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||
NSString *keys = [event charactersIgnoringModifiers];
|
||||
gio_onKeys(self.handle, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
|
||||
gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
|
||||
}
|
||||
- (void)flagsChanged:(NSEvent *)event {
|
||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||
gio_onFlagsChanged(self.handle, [event modifierFlags]);
|
||||
}
|
||||
- (void)keyUp:(NSEvent *)event {
|
||||
NSString *keys = [event charactersIgnoringModifiers];
|
||||
gio_onKeys(self.handle, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false);
|
||||
gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false);
|
||||
}
|
||||
- (void)insertText:(id)string {
|
||||
gio_onText(self.handle, (__bridge CFTypeRef)string);
|
||||
}
|
||||
- (void)doCommandBySelector:(SEL)sel {
|
||||
// Don't pass commands up the responder chain.
|
||||
// They will end up in a beep.
|
||||
- (void)doCommandBySelector:(SEL)action {
|
||||
if (!gio_onCommandBySelector(self.handle)) {
|
||||
[super doCommandBySelector:action];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hasMarkedText {
|
||||
int res = gio_hasMarkedText(self.handle);
|
||||
return res ? YES : NO;
|
||||
@@ -202,10 +205,10 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
||||
return [[self window] convertRectToScreen:r];
|
||||
}
|
||||
- (void)applicationWillUnhide:(NSNotification *)notification {
|
||||
gio_onShow(self.handle);
|
||||
gio_onDraw(self.handle);
|
||||
}
|
||||
- (void)applicationDidHide:(NSNotification *)notification {
|
||||
gio_onHide(self.handle);
|
||||
gio_onDraw(self.handle);
|
||||
}
|
||||
- (void)dealloc {
|
||||
gio_onDestroy(self.handle);
|
||||
@@ -387,7 +390,6 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGF
|
||||
[window setAcceptsMouseMovedEvents:YES];
|
||||
NSView *view = (__bridge NSView *)viewRef;
|
||||
[window setContentView:view];
|
||||
[window makeFirstResponder:view];
|
||||
window.delegate = globalWindowDel;
|
||||
return (__bridge_retained CFTypeRef)window;
|
||||
}
|
||||
|
||||
+8
-6
@@ -1633,6 +1633,14 @@ func (w *window) flushScroll() {
|
||||
if total == (f32.Point{}) {
|
||||
return
|
||||
}
|
||||
if w.scroll.steps == (image.Point{}) {
|
||||
w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X)
|
||||
w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y)
|
||||
}
|
||||
// Zero scroll distance prior to calling ProcessEvent, otherwise we may recursively
|
||||
// re-process the scroll distance.
|
||||
w.scroll.dist = f32.Point{}
|
||||
w.scroll.steps = image.Point{}
|
||||
w.ProcessEvent(pointer.Event{
|
||||
Kind: pointer.Scroll,
|
||||
Source: pointer.Mouse,
|
||||
@@ -1642,12 +1650,6 @@ func (w *window) flushScroll() {
|
||||
Time: w.scroll.time,
|
||||
Modifiers: w.disp.xkb.Modifiers(),
|
||||
})
|
||||
if w.scroll.steps == (image.Point{}) {
|
||||
w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X)
|
||||
w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y)
|
||||
}
|
||||
w.scroll.dist = f32.Point{}
|
||||
w.scroll.steps = image.Point{}
|
||||
}
|
||||
|
||||
func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
|
||||
|
||||
+56
-47
@@ -46,14 +46,13 @@ type window struct {
|
||||
cursorIn bool
|
||||
cursor syscall.Handle
|
||||
|
||||
// placement saves the previous window position when in full screen mode.
|
||||
placement *windows.WindowPlacement
|
||||
|
||||
animating bool
|
||||
|
||||
borderSize image.Point
|
||||
config Config
|
||||
loop *eventLoop
|
||||
// frameDims stores the last seen window frame width and height.
|
||||
frameDims image.Point
|
||||
loop *eventLoop
|
||||
}
|
||||
|
||||
const _WM_WAKEUP = windows.WM_USER + iota
|
||||
@@ -108,8 +107,8 @@ func newWindow(win *callbacks, options []Option) {
|
||||
}
|
||||
winMap.Store(w.hwnd, w)
|
||||
defer winMap.Delete(w.hwnd)
|
||||
w.ProcessEvent(Win32ViewEvent{HWND: uintptr(w.hwnd)})
|
||||
w.Configure(options)
|
||||
w.ProcessEvent(Win32ViewEvent{HWND: uintptr(w.hwnd)})
|
||||
windows.SetForegroundWindow(w.hwnd)
|
||||
windows.SetFocus(w.hwnd)
|
||||
// Since the window class for the cursor is null,
|
||||
@@ -185,21 +184,39 @@ func (w *window) init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// update() handles changes done by the user, and updates the configuration.
|
||||
// update handles changes done by the user, and updates the configuration.
|
||||
// It reads the window style and size/position and updates w.config.
|
||||
// If anything has changed it emits a ConfigEvent to notify the application.
|
||||
func (w *window) update() {
|
||||
cr := windows.GetClientRect(w.hwnd)
|
||||
w.config.Size = image.Point{
|
||||
X: int(cr.Right - cr.Left),
|
||||
Y: int(cr.Bottom - cr.Top),
|
||||
p := windows.GetWindowPlacement(w.hwnd)
|
||||
if !p.IsMinimized() {
|
||||
r := windows.GetWindowRect(w.hwnd)
|
||||
cr := windows.GetClientRect(w.hwnd)
|
||||
w.config.Size = image.Point{
|
||||
X: int(cr.Right - cr.Left),
|
||||
Y: int(cr.Bottom - cr.Top),
|
||||
}
|
||||
w.frameDims = image.Point{
|
||||
X: int(r.Right - r.Left),
|
||||
Y: int(r.Bottom - r.Top),
|
||||
}.Sub(w.config.Size)
|
||||
}
|
||||
|
||||
w.borderSize = image.Pt(
|
||||
windows.GetSystemMetrics(windows.SM_CXSIZEFRAME),
|
||||
windows.GetSystemMetrics(windows.SM_CYSIZEFRAME),
|
||||
)
|
||||
style := windows.GetWindowLong(w.hwnd, windows.GWL_STYLE)
|
||||
switch {
|
||||
case p.IsMaximized() && style&windows.WS_OVERLAPPEDWINDOW != 0:
|
||||
w.config.Mode = Maximized
|
||||
case p.IsMaximized():
|
||||
w.config.Mode = Fullscreen
|
||||
default:
|
||||
w.config.Mode = Windowed
|
||||
}
|
||||
w.ProcessEvent(ConfigEvent{Config: w.config})
|
||||
w.draw(true)
|
||||
}
|
||||
|
||||
func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr {
|
||||
@@ -297,6 +314,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
||||
case windows.WM_DESTROY:
|
||||
w.ProcessEvent(Win32ViewEvent{})
|
||||
w.ProcessEvent(DestroyEvent{})
|
||||
w.w = nil
|
||||
if w.hdc != 0 {
|
||||
windows.ReleaseDC(w.hdc)
|
||||
w.hdc = 0
|
||||
@@ -304,6 +322,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
||||
// The system destroys the HWND for us.
|
||||
w.hwnd = 0
|
||||
windows.PostQuitMessage(0)
|
||||
return 0
|
||||
case windows.WM_NCCALCSIZE:
|
||||
if w.config.Decorated {
|
||||
// Let Windows handle decorations.
|
||||
@@ -328,37 +347,31 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
||||
return 0
|
||||
case windows.WM_PAINT:
|
||||
w.draw(true)
|
||||
case windows.WM_STYLECHANGED:
|
||||
w.update()
|
||||
case windows.WM_WINDOWPOSCHANGED:
|
||||
w.update()
|
||||
case windows.WM_SIZE:
|
||||
w.update()
|
||||
switch wParam {
|
||||
case windows.SIZE_MINIMIZED:
|
||||
w.config.Mode = Minimized
|
||||
case windows.SIZE_MAXIMIZED:
|
||||
w.config.Mode = Maximized
|
||||
case windows.SIZE_RESTORED:
|
||||
if w.config.Mode != Fullscreen {
|
||||
w.config.Mode = Windowed
|
||||
}
|
||||
}
|
||||
case windows.WM_GETMINMAXINFO:
|
||||
mm := (*windows.MinMaxInfo)(unsafe.Pointer(lParam))
|
||||
var bw, bh int32
|
||||
|
||||
var frameDims image.Point
|
||||
if w.config.Decorated {
|
||||
r := windows.GetWindowRect(w.hwnd)
|
||||
cr := windows.GetClientRect(w.hwnd)
|
||||
bw = r.Right - r.Left - (cr.Right - cr.Left)
|
||||
bh = r.Bottom - r.Top - (cr.Bottom - cr.Top)
|
||||
frameDims = w.frameDims
|
||||
}
|
||||
if p := w.config.MinSize; p.X > 0 || p.Y > 0 {
|
||||
p = p.Add(frameDims)
|
||||
mm.PtMinTrackSize = windows.Point{
|
||||
X: int32(p.X) + bw,
|
||||
Y: int32(p.Y) + bh,
|
||||
X: int32(p.X),
|
||||
Y: int32(p.Y),
|
||||
}
|
||||
}
|
||||
if p := w.config.MaxSize; p.X > 0 || p.Y > 0 {
|
||||
p = p.Add(frameDims)
|
||||
mm.PtMaxTrackSize = windows.Point{
|
||||
X: int32(p.X) + bw,
|
||||
Y: int32(p.Y) + bh,
|
||||
X: int32(p.X),
|
||||
Y: int32(p.Y),
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@@ -450,9 +463,6 @@ func getModifiers() key.Modifiers {
|
||||
// hitTest returns the non-client area hit by the point, needed to
|
||||
// process WM_NCHITTEST.
|
||||
func (w *window) hitTest(x, y int) uintptr {
|
||||
if w.config.Mode == Fullscreen {
|
||||
return windows.HTCLIENT
|
||||
}
|
||||
if w.config.Mode != Windowed {
|
||||
// Only windowed mode should allow resizing.
|
||||
return windows.HTCLIENT
|
||||
@@ -562,7 +572,8 @@ func (w *window) runLoop() {
|
||||
loop:
|
||||
for {
|
||||
anim := w.animating
|
||||
if anim && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) {
|
||||
p := windows.GetWindowPlacement(w.hwnd)
|
||||
if anim && !p.IsMinimized() && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) {
|
||||
w.draw(false)
|
||||
continue
|
||||
}
|
||||
@@ -685,8 +696,13 @@ func (w *window) readClipboard() error {
|
||||
func (w *window) Configure(options []Option) {
|
||||
dpi := windows.GetSystemDPI()
|
||||
metric := configForDPI(dpi)
|
||||
w.config.apply(metric, options)
|
||||
windows.SetWindowText(w.hwnd, w.config.Title)
|
||||
cnf := w.config
|
||||
cnf.apply(metric, options)
|
||||
w.config.Title = cnf.Title
|
||||
w.config.Decorated = cnf.Decorated
|
||||
w.config.MinSize = cnf.MinSize
|
||||
w.config.MaxSize = cnf.MaxSize
|
||||
windows.SetWindowText(w.hwnd, cnf.Title)
|
||||
|
||||
style := windows.GetWindowLong(w.hwnd, windows.GWL_STYLE)
|
||||
var showMode int32
|
||||
@@ -694,7 +710,7 @@ func (w *window) Configure(options []Option) {
|
||||
swpStyle := uintptr(windows.SWP_NOZORDER | windows.SWP_FRAMECHANGED)
|
||||
winStyle := uintptr(windows.WS_OVERLAPPEDWINDOW)
|
||||
style &^= winStyle
|
||||
switch w.config.Mode {
|
||||
switch cnf.Mode {
|
||||
case Minimized:
|
||||
style |= winStyle
|
||||
swpStyle |= windows.SWP_NOMOVE | windows.SWP_NOSIZE
|
||||
@@ -709,13 +725,13 @@ func (w *window) Configure(options []Option) {
|
||||
style |= winStyle
|
||||
showMode = windows.SW_SHOWNORMAL
|
||||
// Get target for client area size.
|
||||
width = int32(w.config.Size.X)
|
||||
height = int32(w.config.Size.Y)
|
||||
width = int32(cnf.Size.X)
|
||||
height = int32(cnf.Size.Y)
|
||||
// Get the current window size and position.
|
||||
wr := windows.GetWindowRect(w.hwnd)
|
||||
x = wr.Left
|
||||
y = wr.Top
|
||||
if w.config.Decorated {
|
||||
if cnf.Decorated {
|
||||
// Compute client size and position. Note that the client size is
|
||||
// equal to the window size when we are in control of decorations.
|
||||
r := windows.Rect{
|
||||
@@ -725,25 +741,18 @@ func (w *window) Configure(options []Option) {
|
||||
windows.AdjustWindowRectEx(&r, uint32(style), 0, dwExStyle)
|
||||
width = r.Right - r.Left
|
||||
height = r.Bottom - r.Top
|
||||
}
|
||||
if !w.config.Decorated {
|
||||
} else {
|
||||
// Enable drop shadows when we draw decorations.
|
||||
windows.DwmExtendFrameIntoClientArea(w.hwnd, windows.Margins{-1, -1, -1, -1})
|
||||
}
|
||||
|
||||
case Fullscreen:
|
||||
swpStyle |= windows.SWP_NOMOVE | windows.SWP_NOSIZE
|
||||
mi := windows.GetMonitorInfo(w.hwnd)
|
||||
x, y = mi.Monitor.Left, mi.Monitor.Top
|
||||
width = mi.Monitor.Right - mi.Monitor.Left
|
||||
height = mi.Monitor.Bottom - mi.Monitor.Top
|
||||
showMode = windows.SW_SHOWMAXIMIZED
|
||||
}
|
||||
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style)
|
||||
windows.SetWindowPos(w.hwnd, 0, x, y, width, height, swpStyle)
|
||||
windows.ShowWindow(w.hwnd, showMode)
|
||||
|
||||
w.update()
|
||||
}
|
||||
|
||||
func (w *window) WriteClipboard(mime string, s []byte) {
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ func (c *vkContext) refresh(surf vk.Surface, width, height int) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
minExt, maxExt := caps.MinExtent(), caps.MaxExtent()
|
||||
minExt, maxExt := vk.SurfaceCapabilitiesMinExtent(caps), vk.SurfaceCapabilitiesMaxExtent(caps)
|
||||
if width < minExt.X || maxExt.X < width || height < minExt.Y || maxExt.Y < height {
|
||||
return errOutOfDate
|
||||
}
|
||||
|
||||
+13
-4
@@ -89,6 +89,9 @@ type Window struct {
|
||||
}
|
||||
imeState editorState
|
||||
driver driver
|
||||
// gpuErr tracks the GPU error that is to be reported when
|
||||
// the window is closed.
|
||||
gpuErr error
|
||||
|
||||
// invMu protects mayInvalidate.
|
||||
invMu sync.Mutex
|
||||
@@ -227,7 +230,8 @@ func (w *Window) processFrame(frame *op.Ops, ack chan<- struct{}) {
|
||||
w.lastFrame.deco.Add(wrapper)
|
||||
if err := w.validateAndProcess(w.lastFrame.size, w.lastFrame.sync, wrapper, ack); err != nil {
|
||||
w.destroyGPU()
|
||||
w.driver.ProcessEvent(DestroyEvent{Err: err})
|
||||
w.gpuErr = err
|
||||
w.driver.Perform(system.ActionClose)
|
||||
return
|
||||
}
|
||||
w.updateState()
|
||||
@@ -390,6 +394,8 @@ func (c *callbacks) SetDriver(d driver) {
|
||||
if d == nil {
|
||||
panic("nil driver")
|
||||
}
|
||||
c.w.invMu.Lock()
|
||||
defer c.w.invMu.Unlock()
|
||||
c.w.driver = d
|
||||
}
|
||||
|
||||
@@ -637,11 +643,14 @@ func (w *Window) processEvent(e event.Event) bool {
|
||||
e2.Size = e2.Size.Sub(offset)
|
||||
w.coalesced.frame = &e2
|
||||
case DestroyEvent:
|
||||
if w.gpuErr != nil {
|
||||
e2.Err = w.gpuErr
|
||||
}
|
||||
w.destroyGPU()
|
||||
w.invMu.Lock()
|
||||
w.mayInvalidate = false
|
||||
w.invMu.Unlock()
|
||||
w.driver = nil
|
||||
w.invMu.Unlock()
|
||||
if q := w.timer.quit; q != nil {
|
||||
q <- struct{}{}
|
||||
<-q
|
||||
@@ -656,6 +665,7 @@ func (w *Window) processEvent(e event.Event) bool {
|
||||
}
|
||||
w.coalesced.view = &e2
|
||||
case ConfigEvent:
|
||||
w.decorations.Decorations.Maximized = e2.Config.Mode == Maximized
|
||||
wasFocused := w.decorations.Config.Focused
|
||||
w.decorations.Config = e2.Config
|
||||
e2.Config = w.effectiveConfig()
|
||||
@@ -718,7 +728,7 @@ func (w *Window) Event() event.Event {
|
||||
if w.driver == nil {
|
||||
e, ok := w.nextEvent()
|
||||
if !ok {
|
||||
panic("window initializion failed without a DestroyEvent")
|
||||
panic("window initialization failed without a DestroyEvent")
|
||||
}
|
||||
return e
|
||||
}
|
||||
@@ -801,7 +811,6 @@ func (w *Window) decorate(e FrameEvent, o *op.Ops) image.Point {
|
||||
default:
|
||||
panic(fmt.Errorf("unknown WindowMode %v", m))
|
||||
}
|
||||
deco.Perform(actions)
|
||||
gtx := layout.Context{
|
||||
Ops: o,
|
||||
Now: e.Now,
|
||||
|
||||
Generated
+15
-31
@@ -9,11 +9,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701721028,
|
||||
"narHash": "sha256-2z4YrdHPLoMZNWR1MPOjNZMqPg057i1eZXaYI6RTahQ=",
|
||||
"lastModified": 1733430059,
|
||||
"narHash": "sha256-o3O5tjrMMebRLuHQt7BbEw3jZgWRW5vnOptNXv8WdO4=",
|
||||
"owner": "tadfisher",
|
||||
"repo": "android-nixpkgs",
|
||||
"rev": "c923f9ec0f4dd0d7dc725dc5b73fbf03658e50dd",
|
||||
"rev": "d2f3c1ea99c0bea9d28a0e59daeb482f50d4cd35",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -27,15 +27,14 @@
|
||||
"nixpkgs": [
|
||||
"android",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701697687,
|
||||
"narHash": "sha256-dLLE5wQBVv+pIb4bWmKFSw2DvLVyuEk0F7ng6hpZPSU=",
|
||||
"lastModified": 1728330715,
|
||||
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "c3bd77911391eb1638af6ce773de86da57ee6df5",
|
||||
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -46,14 +45,14 @@
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -64,16 +63,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1701282334,
|
||||
"narHash": "sha256-MxCVrXY6v4QmfTwIysjjaX0XUhqBbxTWWB4HXtDYsdk=",
|
||||
"lastModified": 1733261153,
|
||||
"narHash": "sha256-eq51hyiaIwtWo19fPEeE0Zr2s83DYMKJoukNLgGGpek=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "057f9aecfb71c4437d2b27d3323df7f93c010b7e",
|
||||
"rev": "b681065d0919f7eb5309a93cea2cfa84dec9aa88",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "23.11",
|
||||
"ref": "nixos-24.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -98,21 +97,6 @@
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
description = "Gio build environment";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/23.11";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
|
||||
android.url = "github:tadfisher/android-nixpkgs";
|
||||
android.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
@@ -47,12 +47,6 @@
|
||||
xorg.libXfixes
|
||||
libGL
|
||||
pkg-config
|
||||
] else if stdenv.isDarwin then [
|
||||
darwin.apple_sdk_11_0.frameworks.Foundation
|
||||
darwin.apple_sdk_11_0.frameworks.Metal
|
||||
darwin.apple_sdk_11_0.frameworks.QuartzCore
|
||||
darwin.apple_sdk_11_0.frameworks.AppKit
|
||||
darwin.apple_sdk_11_0.MacOSX-SDK
|
||||
] else [ ]);
|
||||
} // (if stdenv.isLinux then {
|
||||
LD_LIBRARY_PATH = "${vulkan-loader}/lib";
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ type Font struct {
|
||||
// Face is an opaque handle to a typeface. The concrete implementation depends
|
||||
// upon the kind of font and shaper in use.
|
||||
type Face interface {
|
||||
Face() font.Face
|
||||
Face() *font.Face
|
||||
}
|
||||
|
||||
// Typeface identifies a list of font families to attempt to use for displaying
|
||||
|
||||
+41
-43
@@ -16,23 +16,21 @@ import (
|
||||
_ "image/png"
|
||||
|
||||
giofont "gioui.org/font"
|
||||
"github.com/go-text/typesetting/font"
|
||||
fontapi "github.com/go-text/typesetting/opentype/api/font"
|
||||
"github.com/go-text/typesetting/opentype/api/metadata"
|
||||
"github.com/go-text/typesetting/opentype/loader"
|
||||
fontapi "github.com/go-text/typesetting/font"
|
||||
"github.com/go-text/typesetting/font/opentype"
|
||||
)
|
||||
|
||||
// Face is a thread-safe representation of a loaded font. For efficiency, applications
|
||||
// should construct a face for any given font file once, reusing it across different
|
||||
// text shapers.
|
||||
type Face struct {
|
||||
face font.Font
|
||||
face *fontapi.Font
|
||||
font giofont.Font
|
||||
}
|
||||
|
||||
// Parse constructs a Face from source bytes.
|
||||
func Parse(src []byte) (Face, error) {
|
||||
ld, err := loader.NewLoader(bytes.NewReader(src))
|
||||
ld, err := opentype.NewLoader(bytes.NewReader(src))
|
||||
if err != nil {
|
||||
return Face{}, err
|
||||
}
|
||||
@@ -49,11 +47,11 @@ func Parse(src []byte) (Face, error) {
|
||||
// ParseCollection parse an Opentype font file, with support for collections.
|
||||
// Single font files are supported, returning a slice with length 1.
|
||||
// The returned fonts are automatically wrapped in a text.FontFace with
|
||||
// inferred font metadata.
|
||||
// inferred font font.
|
||||
// BUG(whereswaldon): the only Variant that can be detected automatically is
|
||||
// "Mono".
|
||||
func ParseCollection(src []byte) ([]giofont.FontFace, error) {
|
||||
lds, err := loader.NewLoaders(bytes.NewReader(src))
|
||||
lds, err := opentype.NewLoaders(bytes.NewReader(src))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -76,7 +74,7 @@ func ParseCollection(src []byte) ([]giofont.FontFace, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func DescriptionToFont(md metadata.Description) giofont.Font {
|
||||
func DescriptionToFont(md fontapi.Description) giofont.Font {
|
||||
return giofont.Font{
|
||||
Typeface: giofont.Typeface(md.Family),
|
||||
Style: gioStyle(md.Aspect.Style),
|
||||
@@ -84,30 +82,30 @@ func DescriptionToFont(md metadata.Description) giofont.Font {
|
||||
}
|
||||
}
|
||||
|
||||
func FontToDescription(font giofont.Font) metadata.Description {
|
||||
return metadata.Description{
|
||||
func FontToDescription(font giofont.Font) fontapi.Description {
|
||||
return fontapi.Description{
|
||||
Family: string(font.Typeface),
|
||||
Aspect: metadata.Aspect{
|
||||
Aspect: fontapi.Aspect{
|
||||
Style: mdStyle(font.Style),
|
||||
Weight: mdWeight(font.Weight),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// parseLoader parses the contents of the loader into a face and its metadata.
|
||||
func parseLoader(ld *loader.Loader) (font.Font, giofont.Font, error) {
|
||||
// parseLoader parses the contents of the loader into a face and its font.
|
||||
func parseLoader(ld *opentype.Loader) (*fontapi.Font, giofont.Font, error) {
|
||||
ft, err := fontapi.NewFont(ld)
|
||||
if err != nil {
|
||||
return nil, giofont.Font{}, err
|
||||
}
|
||||
data := DescriptionToFont(metadata.Metadata(ld))
|
||||
data := DescriptionToFont(ft.Describe())
|
||||
return ft, data, nil
|
||||
}
|
||||
|
||||
// Face returns a thread-unsafe wrapper for this Face suitable for use by a single shaper.
|
||||
// Face many be invoked any number of times and is safe so long as each return value is
|
||||
// only used by one goroutine.
|
||||
func (f Face) Face() font.Face {
|
||||
func (f Face) Face() *fontapi.Face {
|
||||
return &fontapi.Face{Font: f.face}
|
||||
}
|
||||
|
||||
@@ -119,74 +117,74 @@ func (f Face) Font() giofont.Font {
|
||||
return f.font
|
||||
}
|
||||
|
||||
func gioStyle(s metadata.Style) giofont.Style {
|
||||
func gioStyle(s fontapi.Style) giofont.Style {
|
||||
switch s {
|
||||
case metadata.StyleItalic:
|
||||
case fontapi.StyleItalic:
|
||||
return giofont.Italic
|
||||
case metadata.StyleNormal:
|
||||
case fontapi.StyleNormal:
|
||||
fallthrough
|
||||
default:
|
||||
return giofont.Regular
|
||||
}
|
||||
}
|
||||
|
||||
func mdStyle(g giofont.Style) metadata.Style {
|
||||
func mdStyle(g giofont.Style) fontapi.Style {
|
||||
switch g {
|
||||
case giofont.Italic:
|
||||
return metadata.StyleItalic
|
||||
return fontapi.StyleItalic
|
||||
case giofont.Regular:
|
||||
fallthrough
|
||||
default:
|
||||
return metadata.StyleNormal
|
||||
return fontapi.StyleNormal
|
||||
}
|
||||
}
|
||||
|
||||
func gioWeight(w metadata.Weight) giofont.Weight {
|
||||
func gioWeight(w fontapi.Weight) giofont.Weight {
|
||||
switch w {
|
||||
case metadata.WeightThin:
|
||||
case fontapi.WeightThin:
|
||||
return giofont.Thin
|
||||
case metadata.WeightExtraLight:
|
||||
case fontapi.WeightExtraLight:
|
||||
return giofont.ExtraLight
|
||||
case metadata.WeightLight:
|
||||
case fontapi.WeightLight:
|
||||
return giofont.Light
|
||||
case metadata.WeightNormal:
|
||||
case fontapi.WeightNormal:
|
||||
return giofont.Normal
|
||||
case metadata.WeightMedium:
|
||||
case fontapi.WeightMedium:
|
||||
return giofont.Medium
|
||||
case metadata.WeightSemibold:
|
||||
case fontapi.WeightSemibold:
|
||||
return giofont.SemiBold
|
||||
case metadata.WeightBold:
|
||||
case fontapi.WeightBold:
|
||||
return giofont.Bold
|
||||
case metadata.WeightExtraBold:
|
||||
case fontapi.WeightExtraBold:
|
||||
return giofont.ExtraBold
|
||||
case metadata.WeightBlack:
|
||||
case fontapi.WeightBlack:
|
||||
return giofont.Black
|
||||
default:
|
||||
return giofont.Normal
|
||||
}
|
||||
}
|
||||
|
||||
func mdWeight(g giofont.Weight) metadata.Weight {
|
||||
func mdWeight(g giofont.Weight) fontapi.Weight {
|
||||
switch g {
|
||||
case giofont.Thin:
|
||||
return metadata.WeightThin
|
||||
return fontapi.WeightThin
|
||||
case giofont.ExtraLight:
|
||||
return metadata.WeightExtraLight
|
||||
return fontapi.WeightExtraLight
|
||||
case giofont.Light:
|
||||
return metadata.WeightLight
|
||||
return fontapi.WeightLight
|
||||
case giofont.Normal:
|
||||
return metadata.WeightNormal
|
||||
return fontapi.WeightNormal
|
||||
case giofont.Medium:
|
||||
return metadata.WeightMedium
|
||||
return fontapi.WeightMedium
|
||||
case giofont.SemiBold:
|
||||
return metadata.WeightSemibold
|
||||
return fontapi.WeightSemibold
|
||||
case giofont.Bold:
|
||||
return metadata.WeightBold
|
||||
return fontapi.WeightBold
|
||||
case giofont.ExtraBold:
|
||||
return metadata.WeightExtraBold
|
||||
return fontapi.WeightExtraBold
|
||||
case giofont.Black:
|
||||
return metadata.WeightBlack
|
||||
return fontapi.WeightBlack
|
||||
default:
|
||||
return metadata.WeightNormal
|
||||
return fontapi.WeightNormal
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ go 1.21
|
||||
|
||||
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.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.5.0
|
||||
github.com/go-text/typesetting v0.2.1
|
||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37
|
||||
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37
|
||||
golang.org/x/image v0.18.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/text v0.16.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.9.0
|
||||
|
||||
@@ -1,45 +1,19 @@
|
||||
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
|
||||
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
|
||||
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=
|
||||
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.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/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||
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=
|
||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg=
|
||||
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFdfI8wCeyqgdXWN4+CkFVNPAT0=
|
||||
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
|
||||
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.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/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=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
||||
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
|
||||
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 h1:SOSg7+sueresE4IbmmGM60GmlIys+zNX63d6/J4CMtU=
|
||||
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
|
||||
-2193
File diff suppressed because it is too large
Load Diff
-129
@@ -1,129 +0,0 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gpu
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/cpu"
|
||||
)
|
||||
|
||||
// This file contains code specific to running compute shaders on the CPU.
|
||||
|
||||
// dispatcher dispatches CPU compute programs across multiple goroutines.
|
||||
type dispatcher struct {
|
||||
// done is notified when a worker completes its work slice.
|
||||
done chan struct{}
|
||||
// work receives work slice indices. It is closed when the dispatcher is released.
|
||||
work chan work
|
||||
// dispatch receives compute jobs, which is then split among workers.
|
||||
dispatch chan dispatch
|
||||
// sync receives notification when a Sync completes.
|
||||
sync chan struct{}
|
||||
}
|
||||
|
||||
type work struct {
|
||||
ctx *cpu.DispatchContext
|
||||
index int
|
||||
}
|
||||
|
||||
type dispatch struct {
|
||||
_type jobType
|
||||
program *cpu.ProgramInfo
|
||||
descSet unsafe.Pointer
|
||||
x, y, z int
|
||||
}
|
||||
|
||||
type jobType uint8
|
||||
|
||||
const (
|
||||
jobDispatch jobType = iota
|
||||
jobBarrier
|
||||
jobSync
|
||||
)
|
||||
|
||||
func newDispatcher(workers int) *dispatcher {
|
||||
d := &dispatcher{
|
||||
work: make(chan work, workers),
|
||||
done: make(chan struct{}, workers),
|
||||
// Leave some room to avoid blocking calls to Dispatch.
|
||||
dispatch: make(chan dispatch, 20),
|
||||
sync: make(chan struct{}),
|
||||
}
|
||||
for i := 0; i < workers; i++ {
|
||||
go d.worker()
|
||||
}
|
||||
go d.dispatcher()
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *dispatcher) dispatcher() {
|
||||
defer close(d.work)
|
||||
var free []*cpu.DispatchContext
|
||||
defer func() {
|
||||
for _, ctx := range free {
|
||||
ctx.Free()
|
||||
}
|
||||
}()
|
||||
var used []*cpu.DispatchContext
|
||||
for job := range d.dispatch {
|
||||
switch job._type {
|
||||
case jobDispatch:
|
||||
if len(free) == 0 {
|
||||
free = append(free, cpu.NewDispatchContext())
|
||||
}
|
||||
ctx := free[len(free)-1]
|
||||
free = free[:len(free)-1]
|
||||
used = append(used, ctx)
|
||||
ctx.Prepare(cap(d.work), job.program, job.descSet, job.x, job.y, job.z)
|
||||
for i := 0; i < cap(d.work); i++ {
|
||||
d.work <- work{
|
||||
ctx: ctx,
|
||||
index: i,
|
||||
}
|
||||
}
|
||||
case jobBarrier:
|
||||
// Wait for all outstanding dispatches to complete.
|
||||
for i := 0; i < len(used)*cap(d.work); i++ {
|
||||
<-d.done
|
||||
}
|
||||
free = append(free, used...)
|
||||
used = used[:0]
|
||||
case jobSync:
|
||||
d.sync <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dispatcher) worker() {
|
||||
thread := cpu.NewThreadContext()
|
||||
defer thread.Free()
|
||||
for w := range d.work {
|
||||
w.ctx.Dispatch(w.index, thread)
|
||||
d.done <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dispatcher) Barrier() {
|
||||
d.dispatch <- dispatch{_type: jobBarrier}
|
||||
}
|
||||
|
||||
func (d *dispatcher) Sync() {
|
||||
d.dispatch <- dispatch{_type: jobSync}
|
||||
<-d.sync
|
||||
}
|
||||
|
||||
func (d *dispatcher) Dispatch(program *cpu.ProgramInfo, descSet unsafe.Pointer, x, y, z int) {
|
||||
d.dispatch <- dispatch{
|
||||
_type: jobDispatch,
|
||||
program: program,
|
||||
descSet: descSet,
|
||||
x: x,
|
||||
y: y,
|
||||
z: z,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dispatcher) Stop() {
|
||||
close(d.dispatch)
|
||||
}
|
||||
+16
-5
@@ -9,11 +9,11 @@ package gpu
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"time"
|
||||
"unsafe"
|
||||
@@ -343,13 +343,12 @@ func New(api API) (GPU, error) {
|
||||
func NewWithDevice(d driver.Device) (GPU, error) {
|
||||
d.BeginFrame(nil, false, image.Point{})
|
||||
defer d.EndFrame()
|
||||
forceCompute := os.Getenv("GIORENDERER") == "forcecompute"
|
||||
feats := d.Caps().Features
|
||||
switch {
|
||||
case !forceCompute && feats.Has(driver.FeatureFloatRenderTargets) && feats.Has(driver.FeatureSRGB):
|
||||
case feats.Has(driver.FeatureFloatRenderTargets) && feats.Has(driver.FeatureSRGB):
|
||||
return newGPU(d)
|
||||
}
|
||||
return newCompute(d)
|
||||
return nil, errors.New("no available GPU driver")
|
||||
}
|
||||
|
||||
func newGPU(ctx driver.Device) (*gpu, error) {
|
||||
@@ -1484,7 +1483,7 @@ func (d *drawOps) buildVerts(pathData []byte, tr f32.Affine2D, outline bool, str
|
||||
// as needed and feeds them to the supplied splitter.
|
||||
func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) {
|
||||
for len(pathData) >= scene.CommandSize+4 {
|
||||
qs.contour = bo.Uint32(pathData)
|
||||
qs.contour = binary.LittleEndian.Uint32(pathData)
|
||||
cmd := ops.DecodeCommand(pathData[4:])
|
||||
switch cmd.Op() {
|
||||
case scene.OpLine:
|
||||
@@ -1579,3 +1578,15 @@ func isPureOffset(t f32.Affine2D) bool {
|
||||
a, b, _, d, e, _ := t.Elems()
|
||||
return a == 1 && b == 0 && d == 0 && e == 1
|
||||
}
|
||||
|
||||
func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.VertexShader, frag driver.FragmentShader, err error) {
|
||||
vert, err = ctx.NewVertexShader(vsrc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
frag, err = ctx.NewFragmentShader(fsrc)
|
||||
if err != nil {
|
||||
vert.Release()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -862,8 +862,8 @@ func (b *Backend) BindUniforms(buffer driver.Buffer) {
|
||||
buf := buffer.(*Buffer)
|
||||
cmdBuf := b.currentCmdBuf()
|
||||
for _, s := range b.pipe.pushRanges {
|
||||
off := s.Offset()
|
||||
vk.CmdPushConstants(cmdBuf, b.pipe.desc.layout, s.StageFlags(), off, buf.store[off:off+s.Size()])
|
||||
off := vk.PushConstantRangeOffset(s)
|
||||
vk.CmdPushConstants(cmdBuf, b.pipe.desc.layout, vk.PushConstantRangeStageFlags(s), off, buf.store[off:off+vk.PushConstantRangeSize(s)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+48
-26
@@ -9,8 +9,6 @@ import (
|
||||
"unsafe"
|
||||
|
||||
syscall "golang.org/x/sys/windows"
|
||||
|
||||
"gioui.org/internal/gl"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -24,23 +22,23 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
libEGL = syscall.NewLazyDLL("libEGL.dll")
|
||||
_eglChooseConfig = libEGL.NewProc("eglChooseConfig")
|
||||
_eglCreateContext = libEGL.NewProc("eglCreateContext")
|
||||
_eglCreateWindowSurface = libEGL.NewProc("eglCreateWindowSurface")
|
||||
_eglDestroyContext = libEGL.NewProc("eglDestroyContext")
|
||||
_eglDestroySurface = libEGL.NewProc("eglDestroySurface")
|
||||
_eglGetConfigAttrib = libEGL.NewProc("eglGetConfigAttrib")
|
||||
_eglGetDisplay = libEGL.NewProc("eglGetDisplay")
|
||||
_eglGetError = libEGL.NewProc("eglGetError")
|
||||
_eglInitialize = libEGL.NewProc("eglInitialize")
|
||||
_eglMakeCurrent = libEGL.NewProc("eglMakeCurrent")
|
||||
_eglReleaseThread = libEGL.NewProc("eglReleaseThread")
|
||||
_eglSwapInterval = libEGL.NewProc("eglSwapInterval")
|
||||
_eglSwapBuffers = libEGL.NewProc("eglSwapBuffers")
|
||||
_eglTerminate = libEGL.NewProc("eglTerminate")
|
||||
_eglQueryString = libEGL.NewProc("eglQueryString")
|
||||
_eglWaitClient = libEGL.NewProc("eglWaitClient")
|
||||
libEGL = syscall.DLL{}
|
||||
_eglChooseConfig *syscall.Proc
|
||||
_eglCreateContext *syscall.Proc
|
||||
_eglCreateWindowSurface *syscall.Proc
|
||||
_eglDestroyContext *syscall.Proc
|
||||
_eglDestroySurface *syscall.Proc
|
||||
_eglGetConfigAttrib *syscall.Proc
|
||||
_eglGetDisplay *syscall.Proc
|
||||
_eglGetError *syscall.Proc
|
||||
_eglInitialize *syscall.Proc
|
||||
_eglMakeCurrent *syscall.Proc
|
||||
_eglReleaseThread *syscall.Proc
|
||||
_eglSwapInterval *syscall.Proc
|
||||
_eglSwapBuffers *syscall.Proc
|
||||
_eglTerminate *syscall.Proc
|
||||
_eglQueryString *syscall.Proc
|
||||
_eglWaitClient *syscall.Proc
|
||||
)
|
||||
|
||||
var loadOnce sync.Once
|
||||
@@ -54,21 +52,45 @@ func loadEGL() error {
|
||||
}
|
||||
|
||||
func loadDLLs() error {
|
||||
if err := loadDLL(libEGL, "libEGL.dll"); err != nil {
|
||||
if err := loadDLL(&libEGL, "libEGL.dll"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := loadDLL(gl.LibGLESv2, "libGLESv2.dll"); err != nil {
|
||||
return err
|
||||
|
||||
procs := map[string]**syscall.Proc{
|
||||
"eglChooseConfig": &_eglChooseConfig,
|
||||
"eglCreateContext": &_eglCreateContext,
|
||||
"eglCreateWindowSurface": &_eglCreateWindowSurface,
|
||||
"eglDestroyContext": &_eglDestroyContext,
|
||||
"eglDestroySurface": &_eglDestroySurface,
|
||||
"eglGetConfigAttrib": &_eglGetConfigAttrib,
|
||||
"eglGetDisplay": &_eglGetDisplay,
|
||||
"eglGetError": &_eglGetError,
|
||||
"eglInitialize": &_eglInitialize,
|
||||
"eglMakeCurrent": &_eglMakeCurrent,
|
||||
"eglReleaseThread": &_eglReleaseThread,
|
||||
"eglSwapInterval": &_eglSwapInterval,
|
||||
"eglSwapBuffers": &_eglSwapBuffers,
|
||||
"eglTerminate": &_eglTerminate,
|
||||
"eglQueryString": &_eglQueryString,
|
||||
"eglWaitClient": &_eglWaitClient,
|
||||
}
|
||||
// d3dcompiler_47.dll is needed internally for shader compilation to function.
|
||||
return loadDLL(syscall.NewLazyDLL("d3dcompiler_47.dll"), "d3dcompiler_47.dll")
|
||||
for name, proc := range procs {
|
||||
p, err := libEGL.FindProc(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to locate %s in %s: %w", name, libEGL.Name, err)
|
||||
}
|
||||
*proc = p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadDLL(dll *syscall.LazyDLL, name string) error {
|
||||
err := dll.Load()
|
||||
func loadDLL(dll *syscall.DLL, name string) error {
|
||||
handle, err := syscall.LoadLibraryEx(name, 0, syscall.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("egl: failed to load %s: %v", name, err)
|
||||
}
|
||||
dll.Handle = handle
|
||||
dll.Name = name
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+207
-89
@@ -3,103 +3,217 @@
|
||||
package gl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func loadGLESv2Procs() error {
|
||||
dllName := "libGLESv2.dll"
|
||||
handle, err := windows.LoadLibraryEx(dllName, 0, windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gl: failed to load %s: %v", dllName, err)
|
||||
}
|
||||
gles := windows.DLL{Handle: handle, Name: dllName}
|
||||
// d3dcompiler_47.dll is needed internally for shader compilation to function.
|
||||
dllName = "d3dcompiler_47.dll"
|
||||
_, err = windows.LoadLibraryEx(dllName, 0, windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gl: failed to load %s: %v", dllName, err)
|
||||
}
|
||||
procs := map[string]**windows.Proc{
|
||||
"glActiveTexture": &_glActiveTexture,
|
||||
"glAttachShader": &_glAttachShader,
|
||||
"glBeginQuery": &_glBeginQuery,
|
||||
"glBindAttribLocation": &_glBindAttribLocation,
|
||||
"glBindBuffer": &_glBindBuffer,
|
||||
"glBindBufferBase": &_glBindBufferBase,
|
||||
"glBindFramebuffer": &_glBindFramebuffer,
|
||||
"glBindRenderbuffer": &_glBindRenderbuffer,
|
||||
"glBindTexture": &_glBindTexture,
|
||||
"glBindVertexArray": &_glBindVertexArray,
|
||||
"glBlendEquation": &_glBlendEquation,
|
||||
"glBlendFuncSeparate": &_glBlendFuncSeparate,
|
||||
"glBufferData": &_glBufferData,
|
||||
"glBufferSubData": &_glBufferSubData,
|
||||
"glCheckFramebufferStatus": &_glCheckFramebufferStatus,
|
||||
"glClear": &_glClear,
|
||||
"glClearColor": &_glClearColor,
|
||||
"glClearDepthf": &_glClearDepthf,
|
||||
"glDeleteQueries": &_glDeleteQueries,
|
||||
"glDeleteVertexArrays": &_glDeleteVertexArrays,
|
||||
"glCompileShader": &_glCompileShader,
|
||||
"glCopyTexSubImage2D": &_glCopyTexSubImage2D,
|
||||
"glGenerateMipmap": &_glGenerateMipmap,
|
||||
"glGenBuffers": &_glGenBuffers,
|
||||
"glGenFramebuffers": &_glGenFramebuffers,
|
||||
"glGenVertexArrays": &_glGenVertexArrays,
|
||||
"glGetUniformBlockIndex": &_glGetUniformBlockIndex,
|
||||
"glCreateProgram": &_glCreateProgram,
|
||||
"glGenRenderbuffers": &_glGenRenderbuffers,
|
||||
"glCreateShader": &_glCreateShader,
|
||||
"glGenTextures": &_glGenTextures,
|
||||
"glDeleteBuffers": &_glDeleteBuffers,
|
||||
"glDeleteFramebuffers": &_glDeleteFramebuffers,
|
||||
"glDeleteProgram": &_glDeleteProgram,
|
||||
"glDeleteShader": &_glDeleteShader,
|
||||
"glDeleteRenderbuffers": &_glDeleteRenderbuffers,
|
||||
"glDeleteTextures": &_glDeleteTextures,
|
||||
"glDepthFunc": &_glDepthFunc,
|
||||
"glDepthMask": &_glDepthMask,
|
||||
"glDisableVertexAttribArray": &_glDisableVertexAttribArray,
|
||||
"glDisable": &_glDisable,
|
||||
"glDrawArrays": &_glDrawArrays,
|
||||
"glDrawElements": &_glDrawElements,
|
||||
"glEnable": &_glEnable,
|
||||
"glEnableVertexAttribArray": &_glEnableVertexAttribArray,
|
||||
"glEndQuery": &_glEndQuery,
|
||||
"glFinish": &_glFinish,
|
||||
"glFlush": &_glFlush,
|
||||
"glFramebufferRenderbuffer": &_glFramebufferRenderbuffer,
|
||||
"glFramebufferTexture2D": &_glFramebufferTexture2D,
|
||||
"glGenQueries": &_glGenQueries,
|
||||
"glGetError": &_glGetError,
|
||||
"glGetRenderbufferParameteriv": &_glGetRenderbufferParameteriv,
|
||||
"glGetFloatv": &_glGetFloatv,
|
||||
"glGetFramebufferAttachmentParameteriv": &_glGetFramebufferAttachmentParameteriv,
|
||||
"glGetIntegerv": &_glGetIntegerv,
|
||||
"glGetIntegeri_v": &_glGetIntegeri_v,
|
||||
"glGetProgramiv": &_glGetProgramiv,
|
||||
"glGetProgramInfoLog": &_glGetProgramInfoLog,
|
||||
"glGetQueryObjectuiv": &_glGetQueryObjectuiv,
|
||||
"glGetShaderiv": &_glGetShaderiv,
|
||||
"glGetShaderInfoLog": &_glGetShaderInfoLog,
|
||||
"glGetString": &_glGetString,
|
||||
"glGetUniformLocation": &_glGetUniformLocation,
|
||||
"glGetVertexAttribiv": &_glGetVertexAttribiv,
|
||||
"glGetVertexAttribPointerv": &_glGetVertexAttribPointerv,
|
||||
"glInvalidateFramebuffer": &_glInvalidateFramebuffer,
|
||||
"glIsEnabled": &_glIsEnabled,
|
||||
"glLinkProgram": &_glLinkProgram,
|
||||
"glPixelStorei": &_glPixelStorei,
|
||||
"glReadPixels": &_glReadPixels,
|
||||
"glRenderbufferStorage": &_glRenderbufferStorage,
|
||||
"glScissor": &_glScissor,
|
||||
"glShaderSource": &_glShaderSource,
|
||||
"glTexImage2D": &_glTexImage2D,
|
||||
"glTexStorage2D": &_glTexStorage2D,
|
||||
"glTexSubImage2D": &_glTexSubImage2D,
|
||||
"glTexParameteri": &_glTexParameteri,
|
||||
"glUniformBlockBinding": &_glUniformBlockBinding,
|
||||
"glUniform1f": &_glUniform1f,
|
||||
"glUniform1i": &_glUniform1i,
|
||||
"glUniform2f": &_glUniform2f,
|
||||
"glUniform3f": &_glUniform3f,
|
||||
"glUniform4f": &_glUniform4f,
|
||||
"glUseProgram": &_glUseProgram,
|
||||
"glVertexAttribPointer": &_glVertexAttribPointer,
|
||||
"glViewport": &_glViewport,
|
||||
}
|
||||
for name, proc := range procs {
|
||||
p, err := gles.FindProc(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to locate %s in %s: %w", name, gles.Name, err)
|
||||
}
|
||||
*proc = p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
LibGLESv2 = windows.NewLazyDLL("libGLESv2.dll")
|
||||
_glActiveTexture = LibGLESv2.NewProc("glActiveTexture")
|
||||
_glAttachShader = LibGLESv2.NewProc("glAttachShader")
|
||||
_glBeginQuery = LibGLESv2.NewProc("glBeginQuery")
|
||||
_glBindAttribLocation = LibGLESv2.NewProc("glBindAttribLocation")
|
||||
_glBindBuffer = LibGLESv2.NewProc("glBindBuffer")
|
||||
_glBindBufferBase = LibGLESv2.NewProc("glBindBufferBase")
|
||||
_glBindFramebuffer = LibGLESv2.NewProc("glBindFramebuffer")
|
||||
_glBindRenderbuffer = LibGLESv2.NewProc("glBindRenderbuffer")
|
||||
_glBindTexture = LibGLESv2.NewProc("glBindTexture")
|
||||
_glBindVertexArray = LibGLESv2.NewProc("glBindVertexArray")
|
||||
_glBlendEquation = LibGLESv2.NewProc("glBlendEquation")
|
||||
_glBlendFuncSeparate = LibGLESv2.NewProc("glBlendFuncSeparate")
|
||||
_glBufferData = LibGLESv2.NewProc("glBufferData")
|
||||
_glBufferSubData = LibGLESv2.NewProc("glBufferSubData")
|
||||
_glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus")
|
||||
_glClear = LibGLESv2.NewProc("glClear")
|
||||
_glClearColor = LibGLESv2.NewProc("glClearColor")
|
||||
_glClearDepthf = LibGLESv2.NewProc("glClearDepthf")
|
||||
_glDeleteQueries = LibGLESv2.NewProc("glDeleteQueries")
|
||||
_glDeleteVertexArrays = LibGLESv2.NewProc("glDeleteVertexArrays")
|
||||
_glCompileShader = LibGLESv2.NewProc("glCompileShader")
|
||||
_glCopyTexSubImage2D = LibGLESv2.NewProc("glCopyTexSubImage2D")
|
||||
_glGenerateMipmap = LibGLESv2.NewProc("glGenerateMipmap")
|
||||
_glGenBuffers = LibGLESv2.NewProc("glGenBuffers")
|
||||
_glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers")
|
||||
_glGenVertexArrays = LibGLESv2.NewProc("glGenVertexArrays")
|
||||
_glGetUniformBlockIndex = LibGLESv2.NewProc("glGetUniformBlockIndex")
|
||||
_glCreateProgram = LibGLESv2.NewProc("glCreateProgram")
|
||||
_glGenRenderbuffers = LibGLESv2.NewProc("glGenRenderbuffers")
|
||||
_glCreateShader = LibGLESv2.NewProc("glCreateShader")
|
||||
_glGenTextures = LibGLESv2.NewProc("glGenTextures")
|
||||
_glDeleteBuffers = LibGLESv2.NewProc("glDeleteBuffers")
|
||||
_glDeleteFramebuffers = LibGLESv2.NewProc("glDeleteFramebuffers")
|
||||
_glDeleteProgram = LibGLESv2.NewProc("glDeleteProgram")
|
||||
_glDeleteShader = LibGLESv2.NewProc("glDeleteShader")
|
||||
_glDeleteRenderbuffers = LibGLESv2.NewProc("glDeleteRenderbuffers")
|
||||
_glDeleteTextures = LibGLESv2.NewProc("glDeleteTextures")
|
||||
_glDepthFunc = LibGLESv2.NewProc("glDepthFunc")
|
||||
_glDepthMask = LibGLESv2.NewProc("glDepthMask")
|
||||
_glDisableVertexAttribArray = LibGLESv2.NewProc("glDisableVertexAttribArray")
|
||||
_glDisable = LibGLESv2.NewProc("glDisable")
|
||||
_glDrawArrays = LibGLESv2.NewProc("glDrawArrays")
|
||||
_glDrawElements = LibGLESv2.NewProc("glDrawElements")
|
||||
_glEnable = LibGLESv2.NewProc("glEnable")
|
||||
_glEnableVertexAttribArray = LibGLESv2.NewProc("glEnableVertexAttribArray")
|
||||
_glEndQuery = LibGLESv2.NewProc("glEndQuery")
|
||||
_glFinish = LibGLESv2.NewProc("glFinish")
|
||||
_glFlush = LibGLESv2.NewProc("glFlush")
|
||||
_glFramebufferRenderbuffer = LibGLESv2.NewProc("glFramebufferRenderbuffer")
|
||||
_glFramebufferTexture2D = LibGLESv2.NewProc("glFramebufferTexture2D")
|
||||
_glGenQueries = LibGLESv2.NewProc("glGenQueries")
|
||||
_glGetError = LibGLESv2.NewProc("glGetError")
|
||||
_glGetRenderbufferParameteriv = LibGLESv2.NewProc("glGetRenderbufferParameteriv")
|
||||
_glGetFloatv = LibGLESv2.NewProc("glGetFloatv")
|
||||
_glGetFramebufferAttachmentParameteriv = LibGLESv2.NewProc("glGetFramebufferAttachmentParameteriv")
|
||||
_glGetIntegerv = LibGLESv2.NewProc("glGetIntegerv")
|
||||
_glGetIntegeri_v = LibGLESv2.NewProc("glGetIntegeri_v")
|
||||
_glGetProgramiv = LibGLESv2.NewProc("glGetProgramiv")
|
||||
_glGetProgramInfoLog = LibGLESv2.NewProc("glGetProgramInfoLog")
|
||||
_glGetQueryObjectuiv = LibGLESv2.NewProc("glGetQueryObjectuiv")
|
||||
_glGetShaderiv = LibGLESv2.NewProc("glGetShaderiv")
|
||||
_glGetShaderInfoLog = LibGLESv2.NewProc("glGetShaderInfoLog")
|
||||
_glGetString = LibGLESv2.NewProc("glGetString")
|
||||
_glGetUniformLocation = LibGLESv2.NewProc("glGetUniformLocation")
|
||||
_glGetVertexAttribiv = LibGLESv2.NewProc("glGetVertexAttribiv")
|
||||
_glGetVertexAttribPointerv = LibGLESv2.NewProc("glGetVertexAttribPointerv")
|
||||
_glInvalidateFramebuffer = LibGLESv2.NewProc("glInvalidateFramebuffer")
|
||||
_glIsEnabled = LibGLESv2.NewProc("glIsEnabled")
|
||||
_glLinkProgram = LibGLESv2.NewProc("glLinkProgram")
|
||||
_glPixelStorei = LibGLESv2.NewProc("glPixelStorei")
|
||||
_glReadPixels = LibGLESv2.NewProc("glReadPixels")
|
||||
_glRenderbufferStorage = LibGLESv2.NewProc("glRenderbufferStorage")
|
||||
_glScissor = LibGLESv2.NewProc("glScissor")
|
||||
_glShaderSource = LibGLESv2.NewProc("glShaderSource")
|
||||
_glTexImage2D = LibGLESv2.NewProc("glTexImage2D")
|
||||
_glTexStorage2D = LibGLESv2.NewProc("glTexStorage2D")
|
||||
_glTexSubImage2D = LibGLESv2.NewProc("glTexSubImage2D")
|
||||
_glTexParameteri = LibGLESv2.NewProc("glTexParameteri")
|
||||
_glUniformBlockBinding = LibGLESv2.NewProc("glUniformBlockBinding")
|
||||
_glUniform1f = LibGLESv2.NewProc("glUniform1f")
|
||||
_glUniform1i = LibGLESv2.NewProc("glUniform1i")
|
||||
_glUniform2f = LibGLESv2.NewProc("glUniform2f")
|
||||
_glUniform3f = LibGLESv2.NewProc("glUniform3f")
|
||||
_glUniform4f = LibGLESv2.NewProc("glUniform4f")
|
||||
_glUseProgram = LibGLESv2.NewProc("glUseProgram")
|
||||
_glVertexAttribPointer = LibGLESv2.NewProc("glVertexAttribPointer")
|
||||
_glViewport = LibGLESv2.NewProc("glViewport")
|
||||
glInitOnce sync.Once
|
||||
_glActiveTexture *windows.Proc
|
||||
_glAttachShader *windows.Proc
|
||||
_glBeginQuery *windows.Proc
|
||||
_glBindAttribLocation *windows.Proc
|
||||
_glBindBuffer *windows.Proc
|
||||
_glBindBufferBase *windows.Proc
|
||||
_glBindFramebuffer *windows.Proc
|
||||
_glBindRenderbuffer *windows.Proc
|
||||
_glBindTexture *windows.Proc
|
||||
_glBindVertexArray *windows.Proc
|
||||
_glBlendEquation *windows.Proc
|
||||
_glBlendFuncSeparate *windows.Proc
|
||||
_glBufferData *windows.Proc
|
||||
_glBufferSubData *windows.Proc
|
||||
_glCheckFramebufferStatus *windows.Proc
|
||||
_glClear *windows.Proc
|
||||
_glClearColor *windows.Proc
|
||||
_glClearDepthf *windows.Proc
|
||||
_glDeleteQueries *windows.Proc
|
||||
_glDeleteVertexArrays *windows.Proc
|
||||
_glCompileShader *windows.Proc
|
||||
_glCopyTexSubImage2D *windows.Proc
|
||||
_glGenerateMipmap *windows.Proc
|
||||
_glGenBuffers *windows.Proc
|
||||
_glGenFramebuffers *windows.Proc
|
||||
_glGenVertexArrays *windows.Proc
|
||||
_glGetUniformBlockIndex *windows.Proc
|
||||
_glCreateProgram *windows.Proc
|
||||
_glGenRenderbuffers *windows.Proc
|
||||
_glCreateShader *windows.Proc
|
||||
_glGenTextures *windows.Proc
|
||||
_glDeleteBuffers *windows.Proc
|
||||
_glDeleteFramebuffers *windows.Proc
|
||||
_glDeleteProgram *windows.Proc
|
||||
_glDeleteShader *windows.Proc
|
||||
_glDeleteRenderbuffers *windows.Proc
|
||||
_glDeleteTextures *windows.Proc
|
||||
_glDepthFunc *windows.Proc
|
||||
_glDepthMask *windows.Proc
|
||||
_glDisableVertexAttribArray *windows.Proc
|
||||
_glDisable *windows.Proc
|
||||
_glDrawArrays *windows.Proc
|
||||
_glDrawElements *windows.Proc
|
||||
_glEnable *windows.Proc
|
||||
_glEnableVertexAttribArray *windows.Proc
|
||||
_glEndQuery *windows.Proc
|
||||
_glFinish *windows.Proc
|
||||
_glFlush *windows.Proc
|
||||
_glFramebufferRenderbuffer *windows.Proc
|
||||
_glFramebufferTexture2D *windows.Proc
|
||||
_glGenQueries *windows.Proc
|
||||
_glGetError *windows.Proc
|
||||
_glGetRenderbufferParameteriv *windows.Proc
|
||||
_glGetFloatv *windows.Proc
|
||||
_glGetFramebufferAttachmentParameteriv *windows.Proc
|
||||
_glGetIntegerv *windows.Proc
|
||||
_glGetIntegeri_v *windows.Proc
|
||||
_glGetProgramiv *windows.Proc
|
||||
_glGetProgramInfoLog *windows.Proc
|
||||
_glGetQueryObjectuiv *windows.Proc
|
||||
_glGetShaderiv *windows.Proc
|
||||
_glGetShaderInfoLog *windows.Proc
|
||||
_glGetString *windows.Proc
|
||||
_glGetUniformLocation *windows.Proc
|
||||
_glGetVertexAttribiv *windows.Proc
|
||||
_glGetVertexAttribPointerv *windows.Proc
|
||||
_glInvalidateFramebuffer *windows.Proc
|
||||
_glIsEnabled *windows.Proc
|
||||
_glLinkProgram *windows.Proc
|
||||
_glPixelStorei *windows.Proc
|
||||
_glReadPixels *windows.Proc
|
||||
_glRenderbufferStorage *windows.Proc
|
||||
_glScissor *windows.Proc
|
||||
_glShaderSource *windows.Proc
|
||||
_glTexImage2D *windows.Proc
|
||||
_glTexStorage2D *windows.Proc
|
||||
_glTexSubImage2D *windows.Proc
|
||||
_glTexParameteri *windows.Proc
|
||||
_glUniformBlockBinding *windows.Proc
|
||||
_glUniform1f *windows.Proc
|
||||
_glUniform1i *windows.Proc
|
||||
_glUniform2f *windows.Proc
|
||||
_glUniform3f *windows.Proc
|
||||
_glUniform4f *windows.Proc
|
||||
_glUseProgram *windows.Proc
|
||||
_glVertexAttribPointer *windows.Proc
|
||||
_glViewport *windows.Proc
|
||||
)
|
||||
|
||||
type Functions struct {
|
||||
@@ -115,7 +229,11 @@ func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
|
||||
if ctx != nil {
|
||||
panic("non-nil context")
|
||||
}
|
||||
return new(Functions), nil
|
||||
var err error
|
||||
glInitOnce.Do(func() {
|
||||
err = loadGLESv2Procs()
|
||||
})
|
||||
return new(Functions), err
|
||||
}
|
||||
|
||||
func (c *Functions) ActiveTexture(t Enum) {
|
||||
|
||||
@@ -1893,27 +1893,27 @@ func BuildWriteDescriptorSetBuffer(set DescriptorSet, binding int, typ Descripto
|
||||
}
|
||||
}
|
||||
|
||||
func (r PushConstantRange) StageFlags() ShaderStageFlags {
|
||||
func PushConstantRangeStageFlags(r PushConstantRange) ShaderStageFlags {
|
||||
return r.stageFlags
|
||||
}
|
||||
|
||||
func (r PushConstantRange) Offset() int {
|
||||
func PushConstantRangeOffset(r PushConstantRange) int {
|
||||
return int(r.offset)
|
||||
}
|
||||
|
||||
func (r PushConstantRange) Size() int {
|
||||
func PushConstantRangeSize(r PushConstantRange) int {
|
||||
return int(r.size)
|
||||
}
|
||||
|
||||
func (p QueueFamilyProperties) Flags() QueueFlags {
|
||||
func QueueFamilyPropertiesFlags(p QueueFamilyProperties) QueueFlags {
|
||||
return p.queueFlags
|
||||
}
|
||||
|
||||
func (c SurfaceCapabilities) MinExtent() image.Point {
|
||||
func SurfaceCapabilitiesMinExtent(c SurfaceCapabilities) image.Point {
|
||||
return image.Pt(int(c.minImageExtent.width), int(c.minImageExtent.height))
|
||||
}
|
||||
|
||||
func (c SurfaceCapabilities) MaxExtent() image.Point {
|
||||
func SurfaceCapabilitiesMaxExtent(c SurfaceCapabilities) image.Point {
|
||||
return image.Pt(int(c.maxImageExtent.width), int(c.maxImageExtent.height))
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -60,7 +60,7 @@ type Router struct {
|
||||
}
|
||||
|
||||
// Source implements the interface between a Router and user interface widgets.
|
||||
// The value Source is disabled.
|
||||
// The zero-value Source is disabled.
|
||||
type Source struct {
|
||||
r *Router
|
||||
}
|
||||
@@ -171,22 +171,22 @@ func (q *Router) Source() Source {
|
||||
|
||||
// Execute a command.
|
||||
func (s Source) Execute(c Command) {
|
||||
if !s.Enabled() {
|
||||
if !s.enabled() {
|
||||
return
|
||||
}
|
||||
s.r.execute(c)
|
||||
}
|
||||
|
||||
// Enabled reports whether the source is enabled. Only enabled
|
||||
// enabled reports whether the source is enabled. Only enabled
|
||||
// Sources deliver events and respond to commands.
|
||||
func (s Source) Enabled() bool {
|
||||
func (s Source) enabled() bool {
|
||||
return s.r != nil
|
||||
}
|
||||
|
||||
// Focused reports whether tag is focused, according to the most recent
|
||||
// [key.FocusEvent] delivered.
|
||||
func (s Source) Focused(tag event.Tag) bool {
|
||||
if !s.Enabled() {
|
||||
if !s.enabled() {
|
||||
return false
|
||||
}
|
||||
return s.r.state().keyState.focus == tag
|
||||
@@ -194,7 +194,7 @@ func (s Source) Focused(tag event.Tag) bool {
|
||||
|
||||
// Event returns the next event that matches at least one of filters.
|
||||
func (s Source) Event(filters ...event.Filter) (event.Event, bool) {
|
||||
if !s.Enabled() {
|
||||
if !s.enabled() {
|
||||
return nil, false
|
||||
}
|
||||
return s.r.Event(filters...)
|
||||
|
||||
+17
-3
@@ -5,6 +5,7 @@ package layout
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/input"
|
||||
"gioui.org/io/system"
|
||||
"gioui.org/op"
|
||||
@@ -28,6 +29,7 @@ type Context struct {
|
||||
// Interested users must look up and populate these values manually.
|
||||
Locale system.Locale
|
||||
|
||||
disabled bool
|
||||
input.Source
|
||||
*op.Ops
|
||||
}
|
||||
@@ -42,9 +44,21 @@ func (c Context) Sp(v unit.Sp) int {
|
||||
return c.Metric.Sp(v)
|
||||
}
|
||||
|
||||
// Disabled returns a copy of this context with a disabled Source,
|
||||
// blocking widgets from changing its state and receiving events.
|
||||
func (c Context) Event(filters ...event.Filter) (event.Event, bool) {
|
||||
if c.disabled {
|
||||
return nil, false
|
||||
}
|
||||
return c.Source.Event(filters...)
|
||||
}
|
||||
|
||||
// Enabled reports whether this context is enabled. Disabled contexts
|
||||
// don't report events.
|
||||
func (c Context) Enabled() bool {
|
||||
return !c.disabled
|
||||
}
|
||||
|
||||
// Disabled returns a copy of this context that don't deliver any events.
|
||||
func (c Context) Disabled() Context {
|
||||
c.Source = input.Source{}
|
||||
c.disabled = true
|
||||
return c
|
||||
}
|
||||
|
||||
+39
-88
@@ -12,10 +12,9 @@ import (
|
||||
|
||||
"github.com/go-text/typesetting/di"
|
||||
"github.com/go-text/typesetting/font"
|
||||
gotextot "github.com/go-text/typesetting/font/opentype"
|
||||
"github.com/go-text/typesetting/fontscan"
|
||||
"github.com/go-text/typesetting/language"
|
||||
"github.com/go-text/typesetting/opentype/api"
|
||||
"github.com/go-text/typesetting/opentype/api/metadata"
|
||||
"github.com/go-text/typesetting/shaping"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/image/math/fixed"
|
||||
@@ -201,7 +200,7 @@ type runLayout struct {
|
||||
// Direction is the layout direction of the glyphs.
|
||||
Direction system.TextDirection
|
||||
// face is the font face that the ID of each Glyph in the Layout refers to.
|
||||
face font.Face
|
||||
face *font.Face
|
||||
// truncator indicates that this run is a text truncator standing in for remaining
|
||||
// text.
|
||||
truncator bool
|
||||
@@ -211,8 +210,8 @@ type runLayout struct {
|
||||
type shaperImpl struct {
|
||||
// Fields for tracking fonts/faces.
|
||||
fontMap *fontscan.FontMap
|
||||
faces []font.Face
|
||||
faceToIndex map[font.Font]int
|
||||
faces []*font.Face
|
||||
faceToIndex map[*font.Font]int
|
||||
faceMeta []giofont.Font
|
||||
defaultFaces []string
|
||||
logger interface {
|
||||
@@ -254,7 +253,7 @@ func newShaperImpl(systemFonts bool, collection []FontFace) *shaperImpl {
|
||||
var shaper shaperImpl
|
||||
shaper.logger = newDebugLogger()
|
||||
shaper.fontMap = fontscan.NewFontMap(shaper.logger)
|
||||
shaper.faceToIndex = make(map[font.Font]int)
|
||||
shaper.faceToIndex = make(map[*font.Font]int)
|
||||
if systemFonts {
|
||||
str, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
@@ -282,7 +281,7 @@ func (s *shaperImpl) Load(f FontFace) {
|
||||
s.addFace(f.Face.Face(), f.Font)
|
||||
}
|
||||
|
||||
func (s *shaperImpl) addFace(f font.Face, md giofont.Font) {
|
||||
func (s *shaperImpl) addFace(f *font.Face, md giofont.Font) {
|
||||
if _, ok := s.faceToIndex[f.Font]; ok {
|
||||
return
|
||||
}
|
||||
@@ -377,11 +376,11 @@ func (s *shaperImpl) splitBidi(input shaping.Input) []shaping.Input {
|
||||
// ResolveFace allows shaperImpl to implement shaping.FontMap, wrapping its fontMap
|
||||
// field and ensuring that any faces loaded as part of the search are registered with
|
||||
// ids so that they can be referred to by a GlyphID.
|
||||
func (s *shaperImpl) ResolveFace(r rune) font.Face {
|
||||
func (s *shaperImpl) ResolveFace(r rune) *font.Face {
|
||||
face := s.fontMap.ResolveFace(r)
|
||||
if face != nil {
|
||||
family, aspect := s.fontMap.FontMetadata(face.Font)
|
||||
md := opentype.DescriptionToFont(metadata.Description{
|
||||
md := opentype.DescriptionToFont(font.Description{
|
||||
Family: family,
|
||||
Aspect: aspect,
|
||||
})
|
||||
@@ -491,9 +490,11 @@ func wrapPolicyToGoText(p WrapPolicy) shaping.LineBreakPolicy {
|
||||
// shapeAndWrapText invokes the text shaper and returns wrapped lines in the shaper's native format.
|
||||
func (s *shaperImpl) shapeAndWrapText(params Parameters, txt []rune) (_ []shaping.Line, truncated int) {
|
||||
wc := shaping.WrapConfig{
|
||||
TruncateAfterLines: params.MaxLines,
|
||||
TextContinues: params.forceTruncate,
|
||||
BreakPolicy: wrapPolicyToGoText(params.WrapPolicy),
|
||||
Direction: mapDirection(params.Locale.Direction),
|
||||
TruncateAfterLines: params.MaxLines,
|
||||
TextContinues: params.forceTruncate,
|
||||
BreakPolicy: wrapPolicyToGoText(params.WrapPolicy),
|
||||
DisableTrailingWhitespaceTrim: params.DisableSpaceTrim,
|
||||
}
|
||||
families := s.defaultFaces
|
||||
if params.Font.Typeface != "" {
|
||||
@@ -663,7 +664,7 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec {
|
||||
scaleFactor := fixedToFloat(ppem) / float32(face.Upem())
|
||||
glyphData := face.GlyphData(gid)
|
||||
switch glyphData := glyphData.(type) {
|
||||
case api.GlyphOutline:
|
||||
case font.GlyphOutline:
|
||||
outline := glyphData
|
||||
// Move to glyph position.
|
||||
pos := f32.Point{
|
||||
@@ -678,9 +679,9 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec {
|
||||
for _, fseg := range outline.Segments {
|
||||
nargs := 1
|
||||
switch fseg.Op {
|
||||
case api.SegmentOpQuadTo:
|
||||
case gotextot.SegmentOpQuadTo:
|
||||
nargs = 2
|
||||
case api.SegmentOpCubeTo:
|
||||
case gotextot.SegmentOpCubeTo:
|
||||
nargs = 3
|
||||
}
|
||||
var args [3]f32.Point
|
||||
@@ -695,13 +696,13 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec {
|
||||
}
|
||||
}
|
||||
switch fseg.Op {
|
||||
case api.SegmentOpMoveTo:
|
||||
case gotextot.SegmentOpMoveTo:
|
||||
builder.Move(args[0])
|
||||
case api.SegmentOpLineTo:
|
||||
case gotextot.SegmentOpLineTo:
|
||||
builder.Line(args[0])
|
||||
case api.SegmentOpQuadTo:
|
||||
case gotextot.SegmentOpQuadTo:
|
||||
builder.Quad(args[0], args[1])
|
||||
case api.SegmentOpCubeTo:
|
||||
case gotextot.SegmentOpCubeTo:
|
||||
builder.Cube(args[0], args[1], args[2])
|
||||
default:
|
||||
panic("unsupported segment op")
|
||||
@@ -742,16 +743,16 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp {
|
||||
}
|
||||
glyphData := face.GlyphData(gid)
|
||||
switch glyphData := glyphData.(type) {
|
||||
case api.GlyphBitmap:
|
||||
case font.GlyphBitmap:
|
||||
var imgOp paint.ImageOp
|
||||
var imgSize image.Point
|
||||
bitmapData, ok := s.bitmapGlyphCache.Get(g.ID)
|
||||
if !ok {
|
||||
var img image.Image
|
||||
switch glyphData.Format {
|
||||
case api.PNG, api.JPG, api.TIFF:
|
||||
case font.PNG, font.JPG, font.TIFF:
|
||||
img, _, _ = image.Decode(bytes.NewReader(glyphData.Data))
|
||||
case api.BlackAndWhite:
|
||||
case font.BlackAndWhite:
|
||||
// This is a complex family of uncompressed bitmaps that don't seem to be
|
||||
// very common in practice. We can try adding support later if needed.
|
||||
fallthrough
|
||||
@@ -807,7 +808,7 @@ type langConfig struct {
|
||||
}
|
||||
|
||||
// toInput converts its parameters into a shaping.Input.
|
||||
func toInput(face font.Face, ppem fixed.Int26_6, lc langConfig, runes []rune) shaping.Input {
|
||||
func toInput(face *font.Face, ppem fixed.Int26_6, lc langConfig, runes []rune) shaping.Input {
|
||||
var input shaping.Input
|
||||
input.Direction = lc.Direction
|
||||
input.Text = runes
|
||||
@@ -867,13 +868,14 @@ func toGioGlyphs(in []shaping.Glyph, ppem fixed.Int26_6, faceIdx int) []glyph {
|
||||
}
|
||||
|
||||
// toLine converts the output into a Line with the provided dominant text direction.
|
||||
func toLine(faceToIndex map[font.Font]int, o shaping.Line, dir system.TextDirection) line {
|
||||
func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirection) line {
|
||||
if len(o) < 1 {
|
||||
return line{}
|
||||
}
|
||||
line := line{
|
||||
runs: make([]runLayout, len(o)),
|
||||
direction: dir,
|
||||
runs: make([]runLayout, len(o)),
|
||||
direction: dir,
|
||||
visualOrder: make([]int, len(o)),
|
||||
}
|
||||
maxSize := fixed.Int26_6(0)
|
||||
for i := range o {
|
||||
@@ -881,7 +883,7 @@ func toLine(faceToIndex map[font.Font]int, o shaping.Line, dir system.TextDirect
|
||||
if run.Size > maxSize {
|
||||
maxSize = run.Size
|
||||
}
|
||||
var font font.Font
|
||||
var font *font.Font
|
||||
if run.Face != nil {
|
||||
font = run.Face.Font
|
||||
}
|
||||
@@ -891,11 +893,13 @@ func toLine(faceToIndex map[font.Font]int, o shaping.Line, dir system.TextDirect
|
||||
Count: run.Runes.Count,
|
||||
Offset: line.runeCount,
|
||||
},
|
||||
Direction: unmapDirection(run.Direction),
|
||||
face: run.Face,
|
||||
Advance: run.Advance,
|
||||
PPEM: run.Size,
|
||||
Direction: unmapDirection(run.Direction),
|
||||
face: run.Face,
|
||||
Advance: run.Advance,
|
||||
PPEM: run.Size,
|
||||
VisualPosition: int(run.VisualIndex),
|
||||
}
|
||||
line.visualOrder[run.VisualIndex] = i
|
||||
line.runeCount += run.Runes.Count
|
||||
line.width += run.Advance
|
||||
if line.ascent < run.LineBounds.Ascent {
|
||||
@@ -906,64 +910,11 @@ func toLine(faceToIndex map[font.Font]int, o shaping.Line, dir system.TextDirect
|
||||
}
|
||||
}
|
||||
line.lineHeight = maxSize
|
||||
computeVisualOrder(&line)
|
||||
return line
|
||||
}
|
||||
|
||||
// computeVisualOrder will populate the Line's VisualOrder field and the
|
||||
// VisualPosition field of each element in Runs.
|
||||
func computeVisualOrder(l *line) {
|
||||
l.visualOrder = make([]int, len(l.runs))
|
||||
const none = -1
|
||||
bidiRangeStart := none
|
||||
|
||||
// visPos returns the visual position for an individual logically-indexed
|
||||
// run in this line, taking only the line's overall text direction into
|
||||
// account.
|
||||
visPos := func(logicalIndex int) int {
|
||||
if l.direction.Progression() == system.TowardOrigin {
|
||||
return len(l.runs) - 1 - logicalIndex
|
||||
}
|
||||
return logicalIndex
|
||||
}
|
||||
|
||||
// resolveBidi populated the line's VisualOrder fields for the elements in the
|
||||
// half-open range [bidiRangeStart:bidiRangeEnd) indicating that those elements
|
||||
// should be displayed in reverse-visual order.
|
||||
resolveBidi := func(bidiRangeStart, bidiRangeEnd int) {
|
||||
firstVisual := bidiRangeEnd - 1
|
||||
// Just found the end of a bidi range.
|
||||
for startIdx := bidiRangeStart; startIdx < bidiRangeEnd; startIdx++ {
|
||||
pos := visPos(firstVisual)
|
||||
l.runs[startIdx].VisualPosition = pos
|
||||
l.visualOrder[pos] = startIdx
|
||||
firstVisual--
|
||||
}
|
||||
bidiRangeStart = none
|
||||
}
|
||||
for runIdx, run := range l.runs {
|
||||
if run.Direction.Progression() != l.direction.Progression() {
|
||||
if bidiRangeStart == none {
|
||||
bidiRangeStart = runIdx
|
||||
}
|
||||
continue
|
||||
} else if bidiRangeStart != none {
|
||||
// Just found the end of a bidi range.
|
||||
resolveBidi(bidiRangeStart, runIdx)
|
||||
bidiRangeStart = none
|
||||
}
|
||||
pos := visPos(runIdx)
|
||||
l.runs[runIdx].VisualPosition = pos
|
||||
l.visualOrder[pos] = runIdx
|
||||
}
|
||||
if bidiRangeStart != none {
|
||||
// We ended iteration within a bidi segment, resolve it.
|
||||
resolveBidi(bidiRangeStart, len(l.runs))
|
||||
}
|
||||
// Iterate and resolve the X of each run.
|
||||
x := fixed.Int26_6(0)
|
||||
for _, runIdx := range l.visualOrder {
|
||||
l.runs[runIdx].X = x
|
||||
x += l.runs[runIdx].Advance
|
||||
for _, runIdx := range line.visualOrder {
|
||||
line.runs[runIdx].X = x
|
||||
x += line.runs[runIdx].Advance
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package text
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -450,120 +449,6 @@ func TestToLine(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeVisualOrder(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
input line
|
||||
expectedVisualOrder []int
|
||||
}
|
||||
for _, tc := range []testcase{
|
||||
{
|
||||
name: "ltr",
|
||||
input: line{
|
||||
direction: system.LTR,
|
||||
runs: []runLayout{
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.LTR},
|
||||
},
|
||||
},
|
||||
expectedVisualOrder: []int{0, 1, 2},
|
||||
},
|
||||
{
|
||||
name: "rtl",
|
||||
input: line{
|
||||
direction: system.RTL,
|
||||
runs: []runLayout{
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.RTL},
|
||||
},
|
||||
},
|
||||
expectedVisualOrder: []int{2, 1, 0},
|
||||
},
|
||||
{
|
||||
name: "bidi-ltr",
|
||||
input: line{
|
||||
direction: system.LTR,
|
||||
runs: []runLayout{
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.LTR},
|
||||
},
|
||||
},
|
||||
expectedVisualOrder: []int{0, 3, 2, 1, 4},
|
||||
},
|
||||
{
|
||||
name: "bidi-ltr-complex",
|
||||
input: line{
|
||||
direction: system.LTR,
|
||||
runs: []runLayout{
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.RTL},
|
||||
},
|
||||
},
|
||||
expectedVisualOrder: []int{1, 0, 2, 4, 3, 5, 7, 6, 8, 10, 9},
|
||||
},
|
||||
{
|
||||
name: "bidi-rtl",
|
||||
input: line{
|
||||
direction: system.RTL,
|
||||
runs: []runLayout{
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.RTL},
|
||||
},
|
||||
},
|
||||
expectedVisualOrder: []int{4, 1, 2, 3, 0},
|
||||
},
|
||||
{
|
||||
name: "bidi-rtl-complex",
|
||||
input: line{
|
||||
direction: system.RTL,
|
||||
runs: []runLayout{
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.RTL},
|
||||
{Direction: system.LTR},
|
||||
{Direction: system.LTR},
|
||||
},
|
||||
},
|
||||
expectedVisualOrder: []int{9, 10, 8, 6, 7, 5, 3, 4, 2, 0, 1},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
computeVisualOrder(&tc.input)
|
||||
if !reflect.DeepEqual(tc.input.visualOrder, tc.expectedVisualOrder) {
|
||||
t.Errorf("expected visual order %v, got %v", tc.expectedVisualOrder, tc.input.visualOrder)
|
||||
}
|
||||
for i, visualIndex := range tc.input.visualOrder {
|
||||
if pos := tc.input.runs[visualIndex].VisualPosition; pos != i {
|
||||
t.Errorf("line.VisualOrder[%d]=%d, but line.Runs[%d].VisualPosition=%d", i, visualIndex, visualIndex, pos)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzLayout(f *testing.F) {
|
||||
ltrFace, _ := opentype.Parse(goregular.TTF)
|
||||
rtlFace, _ := opentype.Parse(nsareg.TTF)
|
||||
|
||||
+14
-1
@@ -76,6 +76,11 @@ type Parameters struct {
|
||||
// text with a MaxLines. It is unexported because this behavior only makes sense for the
|
||||
// shaper to control when it iterates paragraphs of text.
|
||||
forceTruncate bool
|
||||
|
||||
// DisableSpaceTrim prevents the width of the final whitespace glyph on a line from being zeroed.
|
||||
// This is desirable for text editors (so that the whitespace can be selected), but is undesirable
|
||||
// for ordinary display text.
|
||||
DisableSpaceTrim bool
|
||||
}
|
||||
|
||||
type FontFace = giofont.FontFace
|
||||
@@ -199,7 +204,15 @@ func (f Flags) String() string {
|
||||
|
||||
type GlyphID uint64
|
||||
|
||||
// Shaper converts strings of text into glyphs that can be displayed.
|
||||
// Shaper converts strings of text into glyphs that can be displayed. The same
|
||||
// Shaper should not be used in different goroutines.
|
||||
//
|
||||
// The Shaper controls text layout and has a cache, implemented as a map, and
|
||||
// so laying out text in two different goroutines can easily result in
|
||||
// concurrent access to said map, resulting in a panic.
|
||||
//
|
||||
// Practically speaking, this means you should use different Shapers for
|
||||
// different top-level windows.
|
||||
type Shaper struct {
|
||||
config struct {
|
||||
disableSystemFonts bool
|
||||
|
||||
+6
-23
@@ -11,8 +11,11 @@ import (
|
||||
|
||||
// Decorations handles the states of window decorations.
|
||||
type Decorations struct {
|
||||
// Maximized controls the look and behaviour of the maximize
|
||||
// button. It is the user's responsibility to set Maximized
|
||||
// according to the window state reported through [app.ConfigEvent].
|
||||
Maximized bool
|
||||
clicks map[int]*Clickable
|
||||
maximized bool
|
||||
}
|
||||
|
||||
// LayoutMove lays out the widget that makes a window movable.
|
||||
@@ -40,17 +43,6 @@ func (d *Decorations) Clickable(action system.Action) *Clickable {
|
||||
return click
|
||||
}
|
||||
|
||||
// Perform updates the decorations as if the specified actions were
|
||||
// performed by the user.
|
||||
func (d *Decorations) Perform(actions system.Action) {
|
||||
if actions&system.ActionMaximize != 0 {
|
||||
d.maximized = true
|
||||
}
|
||||
if actions&(system.ActionUnmaximize|system.ActionMinimize|system.ActionFullscreen) != 0 {
|
||||
d.maximized = false
|
||||
}
|
||||
}
|
||||
|
||||
// Update the state and return the set of actions activated by the user.
|
||||
func (d *Decorations) Update(gtx layout.Context) system.Action {
|
||||
var actions system.Action
|
||||
@@ -60,21 +52,12 @@ func (d *Decorations) Update(gtx layout.Context) system.Action {
|
||||
}
|
||||
action := system.Action(1 << idx)
|
||||
switch {
|
||||
case action == system.ActionMaximize && d.maximized:
|
||||
case action == system.ActionMaximize && d.Maximized:
|
||||
action = system.ActionUnmaximize
|
||||
case action == system.ActionUnmaximize && !d.maximized:
|
||||
case action == system.ActionUnmaximize && !d.Maximized:
|
||||
action = system.ActionMaximize
|
||||
}
|
||||
switch action {
|
||||
case system.ActionMaximize, system.ActionUnmaximize:
|
||||
d.maximized = !d.maximized
|
||||
}
|
||||
actions |= action
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
// Maximized returns whether the window is maximized.
|
||||
func (d *Decorations) Maximized() bool {
|
||||
return d.maximized
|
||||
}
|
||||
|
||||
@@ -610,6 +610,7 @@ func (e *Editor) initBuffer() {
|
||||
e.text.SingleLine = e.SingleLine
|
||||
e.text.Mask = e.Mask
|
||||
e.text.WrapPolicy = e.WrapPolicy
|
||||
e.text.DisableSpaceTrim = true
|
||||
}
|
||||
|
||||
// Update the state of the editor in response to input events. Update consumes editor
|
||||
|
||||
+65
-47
@@ -2,12 +2,18 @@ package widget
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
|
||||
"gioui.org/font"
|
||||
"gioui.org/font/opentype"
|
||||
"gioui.org/gpu/headless"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/text"
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
"golang.org/x/image/math/fixed"
|
||||
@@ -16,11 +22,11 @@ import (
|
||||
// makePosTestText returns two bidi samples of shaped text at the given
|
||||
// font size and wrapped to the given line width. The runeLimit, if nonzero,
|
||||
// truncates the sample text to ensure shorter output for expensive tests.
|
||||
func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (source string, bidiLTR, bidiRTL []text.Glyph) {
|
||||
func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (shaper *text.Shaper, source string, bidiLTR, bidiRTL []text.Glyph) {
|
||||
ltrFace, _ := opentype.Parse(goregular.TTF)
|
||||
rtlFace, _ := opentype.Parse(nsareg.TTF)
|
||||
|
||||
shaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection([]font.FontFace{
|
||||
shaper = text.NewShaper(text.NoSystemFonts(), text.WithCollection([]font.FontFace{
|
||||
{
|
||||
Font: font.Font{Typeface: "LTR"},
|
||||
Face: ltrFace,
|
||||
@@ -58,7 +64,7 @@ func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (source string
|
||||
for g, ok := shaper.NextGlyph(); ok; g, ok = shaper.NextGlyph() {
|
||||
bidiRTL = append(bidiRTL, g)
|
||||
}
|
||||
return bidiSource, bidiLTR, bidiRTL
|
||||
return shaper, bidiSource, bidiLTR, bidiRTL
|
||||
}
|
||||
|
||||
// makeAccountingTestText shapes text designed to stress rune accounting
|
||||
@@ -260,7 +266,7 @@ func TestIndexPositionWhitespace(t *testing.T) {
|
||||
func TestIndexPositionBidi(t *testing.T) {
|
||||
fontSize := 16
|
||||
lineWidth := fontSize * 10
|
||||
_, bidiLTRText, bidiRTLText := makePosTestText(fontSize, lineWidth, false)
|
||||
shaper, _, bidiLTRText, bidiRTLText := makePosTestText(fontSize, lineWidth, false)
|
||||
type testcase struct {
|
||||
name string
|
||||
glyphs []text.Glyph
|
||||
@@ -284,13 +290,12 @@ func TestIndexPositionBidi(t *testing.T) {
|
||||
name: "bidi rtl",
|
||||
glyphs: bidiRTLText,
|
||||
expectedXs: []fixed.Int26_6{
|
||||
2646, 3272, 3842, 4412, 4697, 5267, 5837, 6090, 6602, 7114, 2646, 2380, 1577, 985, 687, 266, // Positions on line 0.
|
||||
|
||||
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.
|
||||
|
||||
324, 894, 1464, 2034, 324, 0, // Positions on line 3.
|
||||
// Line 0
|
||||
5718, 6344, 6914, 7484, 7769, 8339, 8909, 9162, 9674, 10186, 5718, 5452, 4649, 4057, 3759, 3338, 3072, 2304, 1536, 768, 0,
|
||||
// Line 1
|
||||
9170, 8872, 8574, 8308, 6941, 7226, 7796, 8308, 6941, 6675, 6369, 5777, 4926, 4660, 3892, 3124, 2356, 1588, 1303, 788, 406, 0,
|
||||
// Line 2
|
||||
324, 614, 1184, 1754, 2039, 2313, 2883, 3395, 3907, 4192, 4762, 5332, 5902, 324, 0,
|
||||
},
|
||||
},
|
||||
} {
|
||||
@@ -337,6 +342,35 @@ func TestIndexPositionBidi(t *testing.T) {
|
||||
printPositions(t, gi.positions)
|
||||
if t.Failed() {
|
||||
printGlyphs(t, tc.glyphs)
|
||||
width := lineWidth
|
||||
height := 100
|
||||
cap := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
w, _ := headless.NewWindow(width, height)
|
||||
defer w.Release()
|
||||
ops := new(op.Ops)
|
||||
gtx := layout.Context{
|
||||
Constraints: layout.Constraints{Max: image.Pt(width, height)},
|
||||
Ops: ops,
|
||||
}
|
||||
it := textIterator{viewport: image.Rectangle{Max: image.Point{X: width, Y: height}}}
|
||||
for _, g := range tc.glyphs {
|
||||
it.processGlyph(g, true)
|
||||
}
|
||||
var glyphs [32]text.Glyph
|
||||
line := glyphs[:0]
|
||||
for _, g := range gi.glyphs {
|
||||
var ok bool
|
||||
if line, ok = it.paintGlyph(gtx, shaper, g, line); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
w.Frame(ops)
|
||||
w.Screenshot(cap)
|
||||
b := new(bytes.Buffer)
|
||||
_ = png.Encode(b, cap)
|
||||
screenshotName := tc.name + ".png"
|
||||
_ = os.WriteFile(screenshotName, b.Bytes(), 0o644)
|
||||
t.Logf("wrote %q", screenshotName)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -345,8 +379,8 @@ func TestIndexPositionBidi(t *testing.T) {
|
||||
func TestIndexPositionLines(t *testing.T) {
|
||||
fontSize := 16
|
||||
lineWidth := fontSize * 10
|
||||
source1, bidiLTRText, bidiRTLText := makePosTestText(fontSize, lineWidth, false)
|
||||
source2, bidiLTRTextOpp, bidiRTLTextOpp := makePosTestText(fontSize, lineWidth, true)
|
||||
_, source1, bidiLTRText, bidiRTLText := makePosTestText(fontSize, lineWidth, false)
|
||||
_, source2, bidiLTRTextOpp, bidiRTLTextOpp := makePosTestText(fontSize, lineWidth, true)
|
||||
type testcase struct {
|
||||
name string
|
||||
source string
|
||||
@@ -379,7 +413,7 @@ func TestIndexPositionLines(t *testing.T) {
|
||||
xOff: fixed.Int26_6(0),
|
||||
yOff: 60,
|
||||
glyphs: 18,
|
||||
width: fixed.Int26_6(8813),
|
||||
width: fixed.Int26_6(8528),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
@@ -401,32 +435,24 @@ func TestIndexPositionLines(t *testing.T) {
|
||||
{
|
||||
xOff: fixed.Int26_6(0),
|
||||
yOff: 22,
|
||||
glyphs: 15,
|
||||
width: fixed.Int26_6(7114),
|
||||
glyphs: 20,
|
||||
width: fixed.Int26_6(10186),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
{
|
||||
xOff: fixed.Int26_6(0),
|
||||
yOff: 41,
|
||||
glyphs: 15,
|
||||
width: fixed.Int26_6(7867),
|
||||
glyphs: 19,
|
||||
width: fixed.Int26_6(9170),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
{
|
||||
xOff: fixed.Int26_6(0),
|
||||
yOff: 60,
|
||||
glyphs: 18,
|
||||
width: fixed.Int26_6(8794),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
{
|
||||
xOff: fixed.Int26_6(0),
|
||||
yOff: 79,
|
||||
glyphs: 4,
|
||||
width: fixed.Int26_6(2034),
|
||||
glyphs: 13,
|
||||
width: fixed.Int26_6(5902),
|
||||
ascent: fixed.Int26_6(968),
|
||||
descent: fixed.Int26_6(216),
|
||||
},
|
||||
@@ -454,10 +480,10 @@ func TestIndexPositionLines(t *testing.T) {
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
{
|
||||
xOff: fixed.Int26_6(1427),
|
||||
xOff: fixed.Int26_6(1712),
|
||||
yOff: 60,
|
||||
glyphs: 18,
|
||||
width: fixed.Int26_6(8813),
|
||||
width: fixed.Int26_6(8528),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
@@ -477,34 +503,26 @@ func TestIndexPositionLines(t *testing.T) {
|
||||
glyphs: bidiRTLTextOpp,
|
||||
expectedLines: []lineInfo{
|
||||
{
|
||||
xOff: fixed.Int26_6(3126),
|
||||
xOff: fixed.Int26_6(54),
|
||||
yOff: 22,
|
||||
glyphs: 15,
|
||||
width: fixed.Int26_6(7114),
|
||||
glyphs: 20,
|
||||
width: fixed.Int26_6(10186),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
{
|
||||
xOff: fixed.Int26_6(2373),
|
||||
xOff: fixed.Int26_6(1070),
|
||||
yOff: 41,
|
||||
glyphs: 15,
|
||||
width: fixed.Int26_6(7867),
|
||||
glyphs: 19,
|
||||
width: fixed.Int26_6(9170),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
{
|
||||
xOff: fixed.Int26_6(1446),
|
||||
xOff: fixed.Int26_6(4338),
|
||||
yOff: 60,
|
||||
glyphs: 18,
|
||||
width: fixed.Int26_6(8794),
|
||||
ascent: fixed.Int26_6(1407),
|
||||
descent: fixed.Int26_6(756),
|
||||
},
|
||||
{
|
||||
xOff: fixed.Int26_6(8206),
|
||||
yOff: 79,
|
||||
glyphs: 4,
|
||||
width: fixed.Int26_6(2034),
|
||||
glyphs: 13,
|
||||
width: fixed.Int26_6(5902),
|
||||
ascent: fixed.Int26_6(968),
|
||||
descent: fixed.Int26_6(216),
|
||||
},
|
||||
|
||||
@@ -75,7 +75,7 @@ func (d DecorationsStyle) layoutDecorations(gtx layout.Context) layout.Dimension
|
||||
case system.ActionMinimize:
|
||||
w = minimizeWindow
|
||||
case system.ActionMaximize:
|
||||
if d.Decorations.Maximized() {
|
||||
if d.Decorations.Maximized {
|
||||
w = maximizedWindow
|
||||
} else {
|
||||
w = maximizeWindow
|
||||
|
||||
@@ -32,6 +32,9 @@ type Palette struct {
|
||||
ContrastFg color.NRGBA
|
||||
}
|
||||
|
||||
// Theme holds the general theme of an app or window. Different top-level
|
||||
// windows should have different instances of Theme (with different Shapers;
|
||||
// see the godoc for [text.Shaper]), though their other fields can be equal.
|
||||
type Theme struct {
|
||||
Shaper *text.Shaper
|
||||
Palette
|
||||
|
||||
@@ -61,6 +61,9 @@ type textView struct {
|
||||
Truncator string
|
||||
// WrapPolicy configures how displayed text will be broken into lines.
|
||||
WrapPolicy text.WrapPolicy
|
||||
// DisableSpaceTrim configures whether trailing whitespace on a line will have its
|
||||
// width zeroed. Set to true for editors, but false for non-editable text.
|
||||
DisableSpaceTrim bool
|
||||
// Mask replaces the visual display of each rune in the contents with the given rune.
|
||||
// Newline characters are not masked. When non-zero, the unmasked contents
|
||||
// are accessed by Len, Text, and SetText.
|
||||
@@ -285,6 +288,10 @@ func (e *textView) Layout(gtx layout.Context, lt *text.Shaper, font font.Font, s
|
||||
e.params.LineHeightScale = e.LineHeightScale
|
||||
e.invalidate()
|
||||
}
|
||||
if e.DisableSpaceTrim != e.params.DisableSpaceTrim {
|
||||
e.params.DisableSpaceTrim = e.DisableSpaceTrim
|
||||
e.invalidate()
|
||||
}
|
||||
|
||||
e.makeValid()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user