From 4b63512a2f8450fa1f0ed6b91ec44142a3f8263a Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Tue, 2 Mar 2021 02:06:00 -0500 Subject: [PATCH 01/62] track draggable object position to avoid moving it to (0,0) when the drag event goes out of the widget --- internal/driver/gomobile/canvas.go | 24 ++++++++++++-------- internal/driver/gomobile/canvas_test.go | 30 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/internal/driver/gomobile/canvas.go b/internal/driver/gomobile/canvas.go index b62fe7e045..eafa6e231d 100644 --- a/internal/driver/gomobile/canvas.go +++ b/internal/driver/gomobile/canvas.go @@ -40,12 +40,13 @@ type mobileCanvas struct { onTypedKey func(event *fyne.KeyEvent) shortcut fyne.ShortcutHandler - inited bool - lastTapDown map[int]time.Time - lastTapDownPos map[int]fyne.Position - dragging fyne.Draggable - refreshQueue chan fyne.CanvasObject - minSizeCache map[fyne.CanvasObject]fyne.Size + inited bool + lastTapDown map[int]time.Time + lastTapDownPos map[int]fyne.Position + dragging fyne.Draggable + dragStart, dragOffset fyne.Position + refreshQueue chan fyne.CanvasObject + minSizeCache map[fyne.CanvasObject]fyne.Size touchTapCount int touchCancelFunc context.CancelFunc @@ -389,8 +390,9 @@ func (c *mobileCanvas) tapDown(pos fyne.Position, tapID int) { func (c *mobileCanvas) tapMove(pos fyne.Position, tapID int, dragCallback func(fyne.Draggable, *fyne.DragEvent)) { - deltaX := pos.X - c.lastTapDownPos[tapID].X - deltaY := pos.Y - c.lastTapDownPos[tapID].Y + previousPos := c.lastTapDownPos[tapID] + deltaX := pos.X - previousPos.X + deltaY := pos.Y - previousPos.Y if c.dragging == nil && (math.Abs(float64(deltaX)) < tapMoveThreshold && math.Abs(float64(deltaY)) < tapMoveThreshold) { return @@ -420,13 +422,17 @@ func (c *mobileCanvas) tapMove(pos fyne.Position, tapID int, if c.dragging == nil { if drag, ok := co.(fyne.Draggable); ok { c.dragging = drag + c.dragStart = co.Position() + c.dragOffset = previousPos.Subtract(pos) } else { return } } ev := new(fyne.DragEvent) - ev.Position = objPos + draggedObjDelta := c.dragStart.Subtract(c.dragging.(fyne.CanvasObject).Position()) + ev.AbsolutePosition = pos + ev.Position = pos.Subtract(c.dragOffset).Add(draggedObjDelta) ev.Dragged = fyne.Delta{DX: deltaX, DY: deltaY} dragCallback(c.dragging, ev) diff --git a/internal/driver/gomobile/canvas_test.go b/internal/driver/gomobile/canvas_test.go index 7929cfbe37..436302be55 100644 --- a/internal/driver/gomobile/canvas_test.go +++ b/internal/driver/gomobile/canvas_test.go @@ -178,6 +178,36 @@ func TestCanvas_Dragged(t *testing.T) { }) } +func TestCanvas_DraggingOutOfWidget(t *testing.T) { + c := NewCanvas().(*mobileCanvas) + slider := widget.NewSlider(0.0, 100.0) + c.SetContent(container.NewGridWithRows(2, slider, widget.NewLabel("Outside"))) + c.resize(fyne.NewSize(100, 200)) + + assert.Zero(t, slider.Value) + lastValue := slider.Value + + dragged := false + c.tapDown(fyne.NewPos(23, 13), 0) + c.tapMove(fyne.NewPos(30, 13), 0, func(wid fyne.Draggable, ev *fyne.DragEvent) { + assert.Equal(t, slider, wid) + wid.Dragged(ev) + dragged = true + }) + assert.True(t, dragged) + assert.Greater(t, slider.Value, lastValue) + lastValue = slider.Value + + dragged = false + c.tapMove(fyne.NewPos(40, 120), 0, func(wid fyne.Draggable, ev *fyne.DragEvent) { + assert.Equal(t, slider, wid) + wid.Dragged(ev) + dragged = true + }) + assert.True(t, dragged) + assert.Greater(t, slider.Value, lastValue) +} + func TestCanvas_Tappable(t *testing.T) { content := &touchableLabel{Label: widget.NewLabel("Hi\nHi\nHi")} content.ExtendBaseWidget(content) From fcca757e8546f317d795dd9a228d373d6b971f0e Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Mon, 8 Mar 2021 13:10:45 -0500 Subject: [PATCH 02/62] fix drag offset calculation --- internal/driver/gomobile/canvas.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/driver/gomobile/canvas.go b/internal/driver/gomobile/canvas.go index eafa6e231d..a91b948c7c 100644 --- a/internal/driver/gomobile/canvas.go +++ b/internal/driver/gomobile/canvas.go @@ -422,8 +422,8 @@ func (c *mobileCanvas) tapMove(pos fyne.Position, tapID int, if c.dragging == nil { if drag, ok := co.(fyne.Draggable); ok { c.dragging = drag + c.dragOffset = previousPos.Subtract(objPos) c.dragStart = co.Position() - c.dragOffset = previousPos.Subtract(pos) } else { return } @@ -431,7 +431,6 @@ func (c *mobileCanvas) tapMove(pos fyne.Position, tapID int, ev := new(fyne.DragEvent) draggedObjDelta := c.dragStart.Subtract(c.dragging.(fyne.CanvasObject).Position()) - ev.AbsolutePosition = pos ev.Position = pos.Subtract(c.dragOffset).Add(draggedObjDelta) ev.Dragged = fyne.Delta{DX: deltaX, DY: deltaY} From c02750a81eb2089d0103ed49b12619ba54a13926 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Mon, 8 Mar 2021 01:14:53 -0500 Subject: [PATCH 03/62] entry text position should not go beyond the total rows --- widget/entry.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/widget/entry.go b/widget/entry.go index 0f28e899a8..9b12fc63c9 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -876,14 +876,15 @@ func (e *Entry) registerShortcut() { func (e *Entry) rowColFromTextPos(pos int) (row int, col int) { provider := e.textProvider() canWrap := e.Wrapping == fyne.TextWrapBreak || e.Wrapping == fyne.TextWrapWord - for i := 0; i < provider.rows(); i++ { + totalRows := provider.rows() + for i := 0; i < totalRows; i++ { b := provider.rowBoundary(i) if b[0] <= pos { if b[1] < pos { row++ } col = pos - b[0] - if canWrap && b[0] == pos && col == 0 && pos != 0 { + if canWrap && b[0] == pos && col == 0 && pos != 0 && row < (totalRows-1) { row++ } } else { From 28d911ab5b0570860bd779e7f51a4975a5ab4d4a Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Mon, 8 Mar 2021 01:34:01 -0500 Subject: [PATCH 04/62] added test --- widget/entry_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/widget/entry_test.go b/widget/entry_test.go index 96cfc0b52e..d487a5ad00 100644 --- a/widget/entry_test.go +++ b/widget/entry_test.go @@ -331,6 +331,34 @@ func TestEntry_MultilineSelect(t *testing.T) { assert.Equal(t, "ng\nTe", e.SelectedText()) } +func TestEntry_MultilineWrapping_DeleteWithBackspace(t *testing.T) { + entry := widget.NewMultiLineEntry() + entry.Wrapping = fyne.TextWrapWord + entry.Resize(fyne.NewSize(64, 64)) + test.Type(entry, "line1") + test.Type(entry, "\nline2") + test.Type(entry, "\nline3") + + assert.Equal(t, 5, entry.CursorColumn) + assert.Equal(t, 2, entry.CursorRow) + + for i := 0; i < 4; i++ { + entry.TypedKey(&fyne.KeyEvent{Name: fyne.KeyBackspace}) + assert.Equal(t, 4-i, entry.CursorColumn) + assert.Equal(t, 2, entry.CursorRow) + } + + entry.TypedKey(&fyne.KeyEvent{Name: fyne.KeyBackspace}) + assert.Equal(t, 0, entry.CursorColumn) + assert.Equal(t, 2, entry.CursorRow) + + assert.NotPanics(t, func() { + entry.TypedKey(&fyne.KeyEvent{Name: fyne.KeyBackspace}) + }) + assert.Equal(t, 5, entry.CursorColumn) + assert.Equal(t, 1, entry.CursorRow) +} + func TestEntry_Notify(t *testing.T) { entry := widget.NewEntry() changed := false From 3c3b812f7bdf33012857f5fd175e798fa542239f Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Mon, 8 Mar 2021 16:11:23 -0500 Subject: [PATCH 05/62] use the existing rowColFromTextPos to set CursorRow and CursorColumn in entry.pasteFromClipboard --- widget/entry.go | 17 +++-------------- widget/entry_internal_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/widget/entry.go b/widget/entry.go index 9b12fc63c9..ea7c039011 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -821,21 +821,10 @@ func (e *Entry) pasteFromClipboard(clipboard fyne.Clipboard) { } provider := e.textProvider() runes := []rune(text) - provider.insertAt(e.cursorTextPos(), runes) + pos := e.cursorTextPos() + provider.insertAt(pos, runes) + e.CursorRow, e.CursorColumn = e.rowColFromTextPos(pos + len(runes)) - newlines := strings.Count(text, "\n") - if newlines == 0 { - e.CursorColumn += len(runes) - } else { - e.CursorRow += newlines - lastNewlineIndex := 0 - for i, r := range runes { - if r == '\n' { - lastNewlineIndex = i - } - } - e.CursorColumn = len(runes) - lastNewlineIndex - 1 - } e.updateText(provider.String()) e.Refresh() } diff --git a/widget/entry_internal_test.go b/widget/entry_internal_test.go index ec307c7346..d06d888378 100644 --- a/widget/entry_internal_test.go +++ b/widget/entry_internal_test.go @@ -255,6 +255,35 @@ func TestEntry_PasteFromClipboard(t *testing.T) { assert.Equal(t, entry.Text, testContent) } +func TestEntry_PasteFromClipboard_MultilineWrapping(t *testing.T) { + entry := NewMultiLineEntry() + entry.Wrapping = fyne.TextWrapWord + + w := test.NewApp().NewWindow("") + w.SetContent(entry) + w.Resize(fyne.NewSize(100, 64)) + + test.Type(entry, "T") + assert.Equal(t, 0, entry.CursorRow) + assert.Equal(t, 1, entry.CursorColumn) + + clipboard := fyne.CurrentApp().Driver().AllWindows()[0].Clipboard() + clipboard.SetContent("esting entry") + + entry.pasteFromClipboard(clipboard) + + assert.Equal(t, entry.Text, "Testing entry") + assert.Equal(t, 1, entry.CursorRow) + assert.Equal(t, 5, entry.CursorColumn) + + clipboard.SetContent(" paste\ncontent") + entry.pasteFromClipboard(clipboard) + + assert.Equal(t, "Testing entry paste\ncontent", entry.Text) + assert.Equal(t, 2, entry.CursorRow) + assert.Equal(t, 7, entry.CursorColumn) +} + func TestEntry_Tab(t *testing.T) { e := NewEntry() e.SetText("a\n\tb\nc") From 79b600b28b1337d4e6fdc3a50039da959387c51f Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 8 Mar 2021 12:54:28 +0000 Subject: [PATCH 06/62] When window init fails we should quit the app Fixes #1593 --- internal/driver/glfw/driver.go | 11 +++++++++++ internal/driver/glfw/window.go | 7 ++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/internal/driver/glfw/driver.go b/internal/driver/glfw/driver.go index 4964b5c022..7eef21173b 100644 --- a/internal/driver/glfw/driver.go +++ b/internal/driver/glfw/driver.go @@ -3,6 +3,7 @@ package glfw import ( + "os" "runtime" "strconv" "strings" @@ -113,6 +114,16 @@ func (d *gLDriver) windowList() []fyne.Window { return d.windows } +func (d *gLDriver) initFailed(msg string, err error) { + fyne.LogError(msg, err) + + if running() { + d.Quit() + } else { + os.Exit(1) + } +} + func goroutineID() int { b := make([]byte, 64) b = b[:runtime.Stack(b, false)] diff --git a/internal/driver/glfw/window.go b/internal/driver/glfw/window.go index 01e0ac4cca..ae5f84fe64 100644 --- a/internal/driver/glfw/window.go +++ b/internal/driver/glfw/window.go @@ -57,6 +57,7 @@ type window struct { cursor desktop.Cursor customCursor *glfw.Cursor canvas *glCanvas + driver *gLDriver title string icon fyne.Resource mainmenu *fyne.MainMenu @@ -446,7 +447,7 @@ func (w *window) Close() { func (w *window) ShowAndRun() { w.Show() - fyne.CurrentApp().Driver().Run() + w.driver.Run() } // Clipboard returns the system clipboard @@ -1265,7 +1266,7 @@ func (d *gLDriver) createWindow(title string, decorate bool) fyne.Window { runOnMain(func() { d.initGLFW() - ret = &window{title: title, decorate: decorate} + ret = &window{title: title, decorate: decorate, driver: d} // This channel will be closed when the window is closed. ret.eventQueue = make(chan func(), 1024) go ret.runEventQueue() @@ -1308,7 +1309,7 @@ func (w *window) create() { win, err := glfw.CreateWindow(pixWidth, pixHeight, w.title, nil, nil) if err != nil { - fyne.LogError("window creation error", err) + w.driver.initFailed("window creation error", err) return } From 7a29c1702420995f531b79df3113fe51f5aa747c Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 10 Mar 2021 12:21:25 +0000 Subject: [PATCH 07/62] Global settings not yet supported on mobile Don't show settings dialog until this is fixed. Fixes #2062 --- cmd/fyne_demo/main.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/fyne_demo/main.go b/cmd/fyne_demo/main.go index 7e444e3600..a6fa02316c 100644 --- a/cmd/fyne_demo/main.go +++ b/cmd/fyne_demo/main.go @@ -80,9 +80,13 @@ func main() { u, _ := url.Parse("https://github.com/sponsors/fyne-io") _ = a.OpenURL(u) })) + file := fyne.NewMenu("File", newItem) + if !fyne.CurrentDevice().IsMobile() { + file.Items = append(file.Items, fyne.NewMenuItemSeparator(), settingsItem) + } mainMenu := fyne.NewMainMenu( // a quit item will be appended to our first menu - fyne.NewMenu("File", newItem, fyne.NewMenuItemSeparator(), settingsItem), + file, fyne.NewMenu("Edit", cutItem, copyItem, pasteItem, fyne.NewMenuItemSeparator(), findItem), helpMenu, ) From 27cca58100ba8c148d21ac4e29f689293827ffff Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 12 Mar 2021 10:52:15 +0000 Subject: [PATCH 08/62] Avoid mobile crash, focus manager can now be nil --- internal/driver/gomobile/canvas.go | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/internal/driver/gomobile/canvas.go b/internal/driver/gomobile/canvas.go index a91b948c7c..1824df2525 100644 --- a/internal/driver/gomobile/canvas.go +++ b/internal/driver/gomobile/canvas.go @@ -83,7 +83,7 @@ func (c *mobileCanvas) Content() fyne.CanvasObject { func (c *mobileCanvas) Focus(obj fyne.Focusable) { focusMgr := c.focusManager() - if focusMgr.Focus(obj) { // fast path – probably >99.9% of all cases + if focusMgr != nil && focusMgr.Focus(obj) { // fast path – probably >99.9% of all cases c.handleKeyboard(obj) return } @@ -102,15 +102,27 @@ func (c *mobileCanvas) Focus(obj fyne.Focusable) { } func (c *mobileCanvas) FocusNext() { - c.focusManager().FocusNext() + mgr := c.focusManager() + if mgr == nil { + return + } + mgr.FocusNext() } func (c *mobileCanvas) FocusPrevious() { - c.focusManager().FocusPrevious() + mgr := c.focusManager() + if mgr == nil { + return + } + mgr.FocusPrevious() } func (c *mobileCanvas) Focused() fyne.Focusable { - return c.focusManager().Focused() + mgr := c.focusManager() + if mgr == nil { + return nil + } + return mgr.Focused() } func (c *mobileCanvas) InteractiveArea() (fyne.Position, fyne.Size) { @@ -177,7 +189,11 @@ func (c *mobileCanvas) Size() fyne.Size { } func (c *mobileCanvas) Unfocus() { - if c.focusManager().Focus(nil) { + mgr := c.focusManager() + if mgr == nil { + return + } + if mgr.Focus(nil) { hideVirtualKeyboard() } } From b7c60b1fd841b12d3ec275620d6d3fdcfd0adbce Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sat, 6 Mar 2021 15:56:57 -0500 Subject: [PATCH 09/62] allow copy content from a disabled widget.Entry using the keyboard --- internal/app/focus_manager.go | 5 ++++- internal/driver/glfw/window.go | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/app/focus_manager.go b/internal/app/focus_manager.go index 668aa12520..d18c640cce 100644 --- a/internal/app/focus_manager.go +++ b/internal/app/focus_manager.go @@ -5,6 +5,7 @@ import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/internal/driver" + "fyne.io/fyne/v2/widget" ) // FocusManager represents a standard manager of input focus for a canvas @@ -52,7 +53,9 @@ func (f *FocusManager) Focus(obj fyne.Focusable) bool { return true } if dis, ok := obj.(fyne.Disableable); ok && dis.Disabled() { - return true + if _, isEntry := obj.(*widget.Entry); !isEntry { + return true + } } } f.focus(obj) diff --git a/internal/driver/glfw/window.go b/internal/driver/glfw/window.go index ae5f84fe64..464c719b4e 100644 --- a/internal/driver/glfw/window.go +++ b/internal/driver/glfw/window.go @@ -1124,10 +1124,15 @@ func (w *window) keyPressed(_ *glfw.Window, key glfw.Key, scancode int, action g if shortcut != nil { if focused, ok := w.canvas.Focused().(fyne.Shortcutable); ok { - w.queueEvent(func() { focused.TypedShortcut(shortcut) }) + shouldRunShortcut := true + if ent, ok := focused.(*widget.Entry); ok && ent.Disabled() { + shouldRunShortcut = shortcut.ShortcutName() == "Copy" + } + if shouldRunShortcut { + w.queueEvent(func() { focused.TypedShortcut(shortcut) }) + } return } - w.queueEvent(func() { w.canvas.shortcut.TypedShortcut(shortcut) }) return } From 701c483d398c511d6c3c2cc1f95b777de5d84f9d Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sat, 6 Mar 2021 17:10:51 -0500 Subject: [PATCH 10/62] fix circular dependency, fix mobile tests --- internal/app/focus_manager.go | 6 ++++-- internal/driver/glfw/window.go | 6 +++++- internal/driver/gomobile/canvas_test.go | 28 ++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/internal/app/focus_manager.go b/internal/app/focus_manager.go index d18c640cce..d24f26680a 100644 --- a/internal/app/focus_manager.go +++ b/internal/app/focus_manager.go @@ -5,7 +5,6 @@ import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/internal/driver" - "fyne.io/fyne/v2/widget" ) // FocusManager represents a standard manager of input focus for a canvas @@ -53,7 +52,10 @@ func (f *FocusManager) Focus(obj fyne.Focusable) bool { return true } if dis, ok := obj.(fyne.Disableable); ok && dis.Disabled() { - if _, isEntry := obj.(*widget.Entry); !isEntry { + type selectableText interface { + SelectedText() string + } + if _, isSelectableText := obj.(selectableText); !isSelectableText || fyne.CurrentDevice().IsMobile() { return true } } diff --git a/internal/driver/glfw/window.go b/internal/driver/glfw/window.go index 464c719b4e..2e1c4418f3 100644 --- a/internal/driver/glfw/window.go +++ b/internal/driver/glfw/window.go @@ -1125,7 +1125,11 @@ func (w *window) keyPressed(_ *glfw.Window, key glfw.Key, scancode int, action g if shortcut != nil { if focused, ok := w.canvas.Focused().(fyne.Shortcutable); ok { shouldRunShortcut := true - if ent, ok := focused.(*widget.Entry); ok && ent.Disabled() { + type selectableText interface { + fyne.Disableable + SelectedText() string + } + if selectableTextWid, ok := focused.(selectableText); ok && selectableTextWid.Disabled() { shouldRunShortcut = shortcut.ShortcutName() == "Copy" } if shouldRunShortcut { diff --git a/internal/driver/gomobile/canvas_test.go b/internal/driver/gomobile/canvas_test.go index 436302be55..b49cff9a23 100644 --- a/internal/driver/gomobile/canvas_test.go +++ b/internal/driver/gomobile/canvas_test.go @@ -4,6 +4,7 @@ package gomobile import ( "image/color" + "os" "testing" "time" @@ -19,6 +20,14 @@ import ( "github.com/stretchr/testify/assert" ) +func TestMain(m *testing.M) { + currentApp := fyne.CurrentApp() + fyne.SetCurrentApp(newTestMobileApp()) + ret := m.Run() + fyne.SetCurrentApp(currentApp) + os.Exit(ret) +} + func TestCanvas_ChildMinSizeChangeAffectsAncestorsUpToRoot(t *testing.T) { c := NewCanvas().(*mobileCanvas) leftObj1 := canvas.NewRectangle(color.Black) @@ -170,12 +179,13 @@ func TestCanvas_Dragged(t *testing.T) { assert.True(t, dragged) assert.Equal(t, scroll, draggedObj) - // TODO find a way to get the test driver to report as mobile dragged = false c.tapMove(fyne.NewPos(32, 5), 0, func(wid fyne.Draggable, ev *fyne.DragEvent) { wid.Dragged(ev) dragged = true }) + assert.True(t, dragged) + assert.Equal(t, fyne.NewPos(0, 5), scroll.Offset) } func TestCanvas_DraggingOutOfWidget(t *testing.T) { @@ -389,3 +399,19 @@ func simulateTap(c *mobileCanvas) { }, func(wid fyne.Draggable) { }) } + +type mobileApp struct { + fyne.App + driver fyne.Driver +} + +func (a *mobileApp) Driver() fyne.Driver { + return a.driver +} + +func newTestMobileApp() fyne.App { + return &mobileApp{ + App: fyne.CurrentApp(), + driver: NewGoMobileDriver(), + } +} From f900c6914db9635ae82f56880eca387994f02101 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sat, 6 Mar 2021 17:42:08 -0500 Subject: [PATCH 11/62] added test --- internal/driver/glfw/window_test.go | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/internal/driver/glfw/window_test.go b/internal/driver/glfw/window_test.go index ed51481468..bcabd91ac9 100644 --- a/internal/driver/glfw/window_test.go +++ b/internal/driver/glfw/window_test.go @@ -992,6 +992,46 @@ func TestWindow_Clipboard(t *testing.T) { cb.SetContent(cliboardContent) } +func TestWindow_ClipboardCopy_DisabledEntry(t *testing.T) { + w := createWindow("Test").(*window) + e := widget.NewEntry() + e.SetText("Testing") + e.Disable() + w.SetContent(e) + repaintWindow(w) + + w.canvas.Focus(e) + e.DoubleTapped(nil) + assert.Equal(t, "Testing", e.SelectedText()) + + ctrlMod := glfw.ModControl + if runtime.GOOS == "darwin" { + ctrlMod = glfw.ModSuper + } + w.keyPressed(nil, glfw.KeyC, 0, glfw.Repeat, ctrlMod) + w.waitForEvents() + + assert.Equal(t, "Testing", w.Clipboard().Content()) + + e.SetText("Testing2") + e.DoubleTapped(nil) + assert.Equal(t, "Testing2", e.SelectedText()) + + // any other shortcut should be forbidden (Cut) + w.keyPressed(nil, glfw.KeyX, 0, glfw.Repeat, ctrlMod) + w.waitForEvents() + + assert.Equal(t, "Testing2", e.Text) + assert.Equal(t, "Testing", w.Clipboard().Content()) + + // any other shortcut should be forbidden (Paste) + w.keyPressed(nil, glfw.KeyV, 0, glfw.Repeat, ctrlMod) + w.waitForEvents() + + assert.Equal(t, "Testing2", e.Text) + assert.Equal(t, "Testing", w.Clipboard().Content()) +} + func TestWindow_CloseInterception(t *testing.T) { d := NewGLDriver() w := d.CreateWindow("test").(*window) From 2ca8eebba0de1197a7bd5df5f4d832f7fc73413d Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sat, 6 Mar 2021 20:03:27 -0500 Subject: [PATCH 12/62] hide text selection on a disabled widget.Entry when it loses focus --- widget/entry.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/widget/entry.go b/widget/entry.go index ea7c039011..8c6897f1da 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -283,9 +283,6 @@ func (e *Entry) ExtendBaseWidget(wid fyne.Widget) { // // Implements: fyne.Focusable func (e *Entry) FocusGained() { - if e.Disabled() { - return - } e.setFieldsAndRefresh(func() { e.focused = true }) @@ -1175,7 +1172,7 @@ func (r *entryRenderer) Refresh() { provider := r.entry.textProvider() text := r.entry.Text content := r.entry.content - focused := r.entry.focused + focusedAppearance := r.entry.focused && !r.entry.disabled size := r.entry.size wrapping := r.entry.Wrapping r.entry.propertyLock.RUnlock() @@ -1215,7 +1212,7 @@ func (r *entryRenderer) Refresh() { } r.box.FillColor = theme.InputBackgroundColor() - if focused { + if focusedAppearance { r.line.FillColor = theme.PrimaryColor() } else { if r.entry.Disabled() { @@ -1359,7 +1356,7 @@ func (r *entryContentRenderer) Refresh() { provider := r.content.entry.textProvider() placeholder := r.content.entry.placeholderProvider() content := r.content.entry.Text - focused := r.content.entry.focused + focusedAppearance := r.content.entry.focused && !r.content.entry.disabled selections := r.selection r.updateScrollDirections() r.content.entry.propertyLock.RUnlock() @@ -1374,7 +1371,7 @@ func (r *entryContentRenderer) Refresh() { placeholder.Hide() } - if focused { + if focusedAppearance { r.cursor.Show() r.content.entry.cursorAnim.start() } else { @@ -1384,7 +1381,7 @@ func (r *entryContentRenderer) Refresh() { r.moveCursor() for _, selection := range selections { - selection.(*canvas.Rectangle).Hidden = !r.content.entry.focused && !r.content.entry.disabled + selection.(*canvas.Rectangle).Hidden = !r.content.entry.focused selection.(*canvas.Rectangle).FillColor = theme.PrimaryColor() } From 04de92b08574349ace93b63ef26319dcaa447e40 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sat, 6 Mar 2021 21:09:41 -0500 Subject: [PATCH 13/62] do not show validation state if widget.Entry is disabled --- widget/entry.go | 2 +- widget/entry_validation.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/entry.go b/widget/entry.go index 8c6897f1da..bf1a89f41a 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -1236,7 +1236,7 @@ func (r *entryRenderer) Refresh() { } if r.entry.Validator != nil { - if !r.entry.focused && r.entry.Text != "" && r.entry.validationError != nil { + if !r.entry.focused && !r.entry.Disabled() && r.entry.Text != "" && r.entry.validationError != nil { r.line.FillColor = theme.ErrorColor() } r.ensureValidationSetup() diff --git a/widget/entry_validation.go b/widget/entry_validation.go index 5a380f6f84..5a162ea841 100644 --- a/widget/entry_validation.go +++ b/widget/entry_validation.go @@ -95,7 +95,7 @@ func (r *validationStatusRenderer) MinSize() fyne.Size { func (r *validationStatusRenderer) Refresh() { r.entry.propertyLock.RLock() defer r.entry.propertyLock.RUnlock() - if r.entry.Text == "" { + if r.entry.Text == "" || r.entry.disabled { r.icon.Hide() return } From c85185930852fa3d8e4c452c3a314c5c012d3ee9 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sat, 6 Mar 2021 22:53:10 -0500 Subject: [PATCH 14/62] do not render text selection if selectRow and selectColum are zero --- widget/entry.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/widget/entry.go b/widget/entry.go index bf1a89f41a..8482e53dea 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -254,6 +254,9 @@ func (e *Entry) Dragged(d *fyne.DragEvent) { pevt.Position = pevt.Position.Subtract(e.scroll.Offset) if !e.selecting { e.selectRow, e.selectColumn = e.getRowCol(&pevt) + if e.selectRow == 0 && e.selectColumn == 0 { + return + } e.selecting = true } e.updateMousePointer(&pevt, false) From 64c8d8664161cd2461b9a23783e0dcc02757a61f Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sat, 6 Mar 2021 23:55:46 -0500 Subject: [PATCH 15/62] revert last commit and fix text selection correctly by comparing CursorColumn and CursorRow with selectColumn and selectRow in DragEnd event --- widget/entry.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/widget/entry.go b/widget/entry.go index 8482e53dea..a7d38643e5 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -237,10 +237,19 @@ func (e *Entry) DoubleTapped(p *fyne.PointEvent) { }) } -// DragEnd is called at end of a drag event. It does nothing. +// DragEnd is called at end of a drag event. // // Implements: fyne.Draggable func (e *Entry) DragEnd() { + e.propertyLock.Lock() + if e.CursorColumn == e.selectColumn && e.CursorRow == e.selectRow { + e.selecting = false + } + shouldRefresh := !e.selecting + e.propertyLock.Unlock() + if shouldRefresh { + e.Refresh() + } } // Dragged is called when the pointer moves while a button is held down. @@ -254,9 +263,6 @@ func (e *Entry) Dragged(d *fyne.DragEvent) { pevt.Position = pevt.Position.Subtract(e.scroll.Offset) if !e.selecting { e.selectRow, e.selectColumn = e.getRowCol(&pevt) - if e.selectRow == 0 && e.selectColumn == 0 { - return - } e.selecting = true } e.updateMousePointer(&pevt, false) @@ -1293,7 +1299,7 @@ func (e *entryContent) CreateRenderer() fyne.WidgetRenderer { return r } -// DragEnd is called at end of a drag event. It does nothing. +// DragEnd is called at end of a drag event. // // Implements: fyne.Draggable func (e *entryContent) DragEnd() { From de3f86387120e26f97b8e0ffc89360e50ff03ab8 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sun, 7 Mar 2021 01:05:19 -0500 Subject: [PATCH 16/62] reset selectKeyDown variable when widget.Entry loses its focus, added early return in KeyDown and KeyUp methods if entry is disabled --- widget/entry.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/widget/entry.go b/widget/entry.go index a7d38643e5..296030e4ef 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -303,6 +303,7 @@ func (e *Entry) FocusGained() { func (e *Entry) FocusLost() { e.setFieldsAndRefresh(func() { e.focused = false + e.selectKeyDown = false }) } @@ -335,6 +336,9 @@ func (e *Entry) Keyboard() mobile.KeyboardType { // // Implements: desktop.Keyable func (e *Entry) KeyDown(key *fyne.KeyEvent) { + if e.Disabled() { + return + } // For keyboard cursor controlled selection we now need to store shift key state and selection "start" // Note: selection start is where the highlight started (if the user moves the selection up or left then // the selectRow/Column will not match SelectionStart) @@ -351,6 +355,9 @@ func (e *Entry) KeyDown(key *fyne.KeyEvent) { // // Implements: desktop.Keyable func (e *Entry) KeyUp(key *fyne.KeyEvent) { + if e.Disabled() { + return + } // Handle shift release for keyboard selection // Note: if shift is released then the user may repress it without moving to adjust their old selection if key.Name == desktop.KeyShiftLeft || key.Name == desktop.KeyShiftRight { From e04442ee03a9786df83ce618fd358b73bf4c85b9 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sun, 7 Mar 2021 01:52:44 -0500 Subject: [PATCH 17/62] added tests --- widget/entry_internal_test.go | 52 ++++++++++++++++++ widget/entry_test.go | 16 ++++++ widget/entry_validation_test.go | 12 ++++ .../testdata/entry/disabled_text_selected.png | Bin 0 -> 1697 bytes .../entry/disabled_text_unselected.png | Bin 0 -> 1399 bytes widget/testdata/entry/validation_disabled.png | Bin 0 -> 1631 bytes 6 files changed, 80 insertions(+) create mode 100644 widget/testdata/entry/disabled_text_selected.png create mode 100644 widget/testdata/entry/disabled_text_unselected.png create mode 100644 widget/testdata/entry/validation_disabled.png diff --git a/widget/entry_internal_test.go b/widget/entry_internal_test.go index d06d888378..13992c9521 100644 --- a/widget/entry_internal_test.go +++ b/widget/entry_internal_test.go @@ -93,6 +93,47 @@ func TestEntry_DragSelect(t *testing.T) { assert.Equal(t, "r the laz", entry.SelectedText()) } +func TestEntry_DragSelectEmpty(t *testing.T) { + entry := NewEntry() + entry.SetText("Testing") + + ev1 := getClickPosition("T", 0) + ev2 := getClickPosition("Testing", 0) + + // Test empty selection - drag from 'e' to 'e' (empty) + de := &fyne.DragEvent{PointEvent: *ev1, Dragged: fyne.NewDelta(1, 0)} + entry.Dragged(de) + de = &fyne.DragEvent{PointEvent: *ev1, Dragged: fyne.NewDelta(1, 0)} + entry.Dragged(de) + + entry.propertyLock.RLock() + assert.True(t, entry.selecting) + entry.propertyLock.RUnlock() + + entry.DragEnd() + assert.Equal(t, "", entry.SelectedText()) + entry.propertyLock.RLock() + assert.False(t, entry.selecting) + entry.propertyLock.RUnlock() + + // Test non-empty selection - drag from 'T' to 'g' (empty) + ev1 = getClickPosition("", 0) + de = &fyne.DragEvent{PointEvent: *ev1, Dragged: fyne.NewDelta(1, 0)} + entry.Dragged(de) + de = &fyne.DragEvent{PointEvent: *ev2, Dragged: fyne.NewDelta(1, 0)} + entry.Dragged(de) + + entry.propertyLock.RLock() + assert.True(t, entry.selecting) + entry.propertyLock.RUnlock() + + entry.DragEnd() + assert.Equal(t, "Testing", entry.SelectedText()) + entry.propertyLock.RLock() + assert.True(t, entry.selecting) + entry.propertyLock.RUnlock() +} + func TestEntry_DragSelectWithScroll(t *testing.T) { entry := NewEntry() entry.SetText("The quick brown fox jumped over and over the lazy dog.") @@ -319,6 +360,17 @@ func TestEntry_TabSelection(t *testing.T) { test.AssertImageMatches(t, "entry/tab-select.png", w.Canvas().Capture()) } +func TestEntry_ShiftSelection_ResetOnFocusLost(t *testing.T) { + e := NewEntry() + e.SetText("Hello") + + e.KeyDown(&fyne.KeyEvent{Name: desktop.KeyShiftLeft}) + assert.True(t, e.selectKeyDown) + + e.FocusLost() + assert.False(t, e.selectKeyDown) +} + func getClickPosition(str string, row int) *fyne.PointEvent { x := fyne.MeasureText(str, theme.TextSize(), fyne.TextStyle{}).Width + theme.Padding() diff --git a/widget/entry_test.go b/widget/entry_test.go index d487a5ad00..20b46952e4 100644 --- a/widget/entry_test.go +++ b/widget/entry_test.go @@ -195,6 +195,22 @@ func TestEntry_Disableable(t *testing.T) { test.AssertRendersToMarkup(t, "entry/disableable_enabled_custom_value.xml", c) } +func TestEntry_Disabled_TextSelection(t *testing.T) { + entry, window := setupImageTest(t, false) + defer teardownImageTest(window) + entry.SetText("Testing") + entry.Disable() + c := window.Canvas() + + assert.True(t, entry.Disabled()) + test.DoubleTap(entry) + + test.AssertImageMatches(t, "entry/disabled_text_selected.png", c.Capture()) + + entry.FocusLost() + test.AssertImageMatches(t, "entry/disabled_text_unselected.png", c.Capture()) +} + func TestEntry_EmptySelection(t *testing.T) { entry := widget.NewEntry() entry.SetText("text") diff --git a/widget/entry_validation_test.go b/widget/entry_validation_test.go index d190bdf12e..4e72353a9e 100644 --- a/widget/entry_validation_test.go +++ b/widget/entry_validation_test.go @@ -15,6 +15,18 @@ import ( var validator = validation.NewRegexp(`^\d{4}-\d{2}-\d{2}$`, "Input is not a valid date") +func TestEntry_DisabledHideValidation(t *testing.T) { + entry, window := setupImageTest(t, false) + defer teardownImageTest(window) + c := window.Canvas() + + entry.Validator = validator + entry.SetText("invalid text") + entry.Disable() + + test.AssertImageMatches(t, "entry/validation_disabled.png", c.Capture()) +} + func TestEntry_ValidatedEntry(t *testing.T) { entry, window := setupImageTest(t, false) defer teardownImageTest(window) diff --git a/widget/testdata/entry/disabled_text_selected.png b/widget/testdata/entry/disabled_text_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..9b07619a9d57e8638e8f356b3ab2bb5f33895bd8 GIT binary patch literal 1697 zcmeAS@N?(olHy`uVBq!ia0vp^(}4H{2NRHNeco@xz`&N_>EaktG3U+g^Z6l9<&J-R zUtH(6+<2*Gu4b;%AlT zO6IIvr>B^5?eRUvC{4AN<1HU%2*?Q^N_ML7;ppdlXsBod7M#=iJ})~vdbRD!jO*)S zqrW9va>3;61@e6Mxu;XsH1d%n&v2p7ne`|kH3;#`TF zi`~D2(z8SA_x-(D->SP++v?9mv*Y_#z50{>@xZhS)!Qp&PW$XwugbMZ<>6Ju*VhGq ze>j))@*8ic_dD_I%I`h@uU$TPhA(newtL}gw{OvFQq}H+t@o~%)L-#>@uuA```!0Q zoSw_+-IKZM;x<$BICZ9-)+?t3@k-8}Rrg8TIZR+>b?cM#OX~`Af4cAW+i7e+UwcK* z`mGahKi(#EdBVkYw%bjWjb;QNjmSNfb>3{}fmI*327R6*H!lI`s15rRUw@9fUaWHI z(USpfN z`hQ(NAakp$Epy*e9|7&_M*1Z!y)#p?e+Kx)eP~l()02NF;_rUlSNk6Axf}W6&D9Fu z*Wo{fpK-mKEWh{M+oOHgZgkAq^`x%qWcAnjgD;lUSSRfNGe05NjMHG!^j%q*HxqJJ z|G268{p@nC`mG1^+1^b5#{TuHTE`i8@ju0X-+lh=BxqTDl~?KOv4soNQjaaW(tf`> zwO&`?l)ds>*)5mnm26Q<_UL@`Kf0tmi1+!_+|Hm&_Blr{Uq0J=b<>H@vDL*f@pEmC zlwav{vz_BFx4ieI>=ypK&a9x5v0Fo*-CryE>s#c%K#NJ?KLhSrKcD_DbD4Lqc*<)x z%T3yE-hI})S8#jw;=XGdkM*;fxAOZ;jJfR+<-31*)W&JQdgkq2t+)2{zbJ(%_6xoG zmRWAHuK956mX_|KvixherI*+3d;5jQLOkEXou6gt!>tC@|1M^|v%h^tY|W`zN6u*e z@7$uexR^CGI@K)o`M)3M&M=ncUcD!?|I>Yi$20FmmuWU>7^Y>eo|xkm6Wylv>|1?F zmTla!2=H#Z&O`fb@$@QC$ie%0rtGNmS*eyJsf5@^N~vMyp@Zzbd`+jCgmqw znHQ(5XST%a+Oop)4Yo6akIjg-{CYQE)J%Bq`akNIwy5iuvBbxH>#LvZ)wgxh{_8Je zeG8Xd6`y{$=7r_74CQJw_v+=-dDm|J`0dz_C#_GurI(#sqbl||Alkz0>E8Q~qrEom zyLnw`?%VRy|B9~JZ~d^n=J{XVZLW235gkhcLNddv&)vBlVmHNObx-=6_T&FkG&cJA z^?QeQsPu$fyT1R?@Rn5y$;sK?U-~wA$sc4TF+AxrzAglxYc>9xwUYmN7c)2 ztK3PNXWnf7!h7@U)nltRcpI;tU9c5sXI@WaZ{*~nGiR%>eKlOY<;i-J1Dw`Ti)T%- zwMu>JymhIc{JqFE-rR-C&Lz=eyGlgC@Vf{FM7?i zUn%OMoFSM0e7n72)lR36Knnp#DT1|V85Kk;YiSuE``H;7{{MH2UY>E{oVqQr>S6G7 L^>bP0l+XkKlEaktG3U+g*M5)un2&!v zR}$5CizAF>qIg{Q^tW3*cvwUjmpV+9TjnpPmdWuymcL{5{tv(R+>U(Hxc)L9$E#nz zZYiaFTF&3FuZ#1cp`wji%kh>EGX&&>4<$QQ_;B=t1;2i)kB^Q0yIR>YWBc~)*8g?V zu`Aqjf5wGvZ|B>wsJ!2E@SFMb=g+?U!HgdB+TJ+iP)>L#^etGBa-KX8vCVNV>?Uud4c*`*HpNRHSuceogHr}Xe z5LgtTkrKJhRCDQ+b+4nQiMmd_cI%IjhvwNiewR&7Z!+MyY;tz{e3j1B$fHSt*|M$| zuk~@YHVGtcjmlgrd%W{;1jokPo{hH*AKpIobkqCak<(_T7hA~8TD~uCJ)ASNF|E|FqHVZCkJXytZV@*|f{0 zcTIekPVx8m|5~;8_S>|`Z?fuNv)|e;3|e(D!$)nh0*B7&Pqq6mW_+o#T{`7?<=?vd zEC!cPp9#GG-CTc3w9R>G9=5cqtx;>E)|S=R=hrDNnX)%d-&1I5$m*-Ne(!X+@xN!xHZaSx<*2v1rR%SOJe6auYWPYRKs^X_b8QTxs`KP}mP-N!R#4U-R zf6hJs{BNDT=cKHyS#B)?^LZ1ltxGG+ej20Y^3 zty3<2uU$IjS`>$2{AQikU%!^_j4_*iR_FAw#~UU8bi6ECHN|MX+nJ(eVXNQ%Evd8r z_-%X5{!g4{pC6r%`S{84_O@1m=;oil7yZ=!b^e`d-(z4J$}rh=_uV=6Kh66-ac;|x zJSS0=oBbhwVp+%bo#KivOM@)WxV|)3`;(`xu6{Jhu~H5rRumV1UfA}uVzxd zprPTZ4HF9HOQI0E#IiEsiab(G&e%j}fkudez*x84t?lpqw?CfWbIog-P17mfmH_H!RZ-&N}hKVOLnsU4n{wj#FA(tH24^Woy` zoW0+85L$Pue7%aOFjJI07Plrx1p*O-?p11IAv8E3yvi4bK+sfcx5@(xNA>^$>?vtHxv&*7;Ozn%J(iit({i%1rbun?ftOU*;AV}Oak+a3aAT^vpRY^x+ z(&jo&02|en#Q9Z_ch04cC|nodWc_dF(?pi}S~RN5WHL?oL;|I{;yb*6pN6z)bRX)= z<7m|S!a>@@90qIXly+L&T(kzQRt>P(3PqY(PZkxYC!@ORd!S3JtD)mP)&m3g?#;|R z*;oDS)Gy%@lR?`MUz8pTsq?;m6ijpQ{BdJ#MKw8@h@+F!lm-sFhZqr4d#6J%FmN^A zW6+QO-&NdEgKHFmR!cDmZ`M& zWYM!zeJRlT__$panN2JL9j~1g>0tST$A5#o%;O*FxR&W`d~-YL%hJ#-<%ep$ji)u! z!&*~VSQv@9yE4!j)XxodbwHFF>sf?zE+FwS;8_w0Cokem+FU)-ZCEUDw5(X@HnyVI z-(3>C$}<>^sSv%%eRiNjlyEzV^WfXIfG7fEy5EnQ!jqGj=l8pbFrBh4HcFje>^|<_yiz@u1;Gz1X{rmM%^|A$1%0@I3|=!& zzup7ou-UYwx5GkBd^tdZGg{S1kkdM^uPe)WZ-U%ymEiF_8z zAnLE2*yn6>wOB}A_{QAe=GW$+6GF#F_MPS{ka6KP9oid}nI&t>ZP0O{FyfVEyfFZ) zDU*9LgKuEMboub;h?rXNve5ZNQvUjTTZ;Ej!rav)t_PMmgT>Kn zRKZFO8TW!=pH9c8%gOU0j0oyn>Y9k#3~uO` z8dc$;f zy2Zl%&Y&TRIW=)|a=GN^yL*=@Xa!%hLone#VNhep9^$+B1` zi3mG!npa_+vipkyAERN|%LrahjX@gHdYF9R`^ b+5QTv^}#%Rid7l`vJHS3BqoX*dE(;lH0K~q literal 0 HcmV?d00001 From 7dfcf63ed409d169893c4eaefbb07325d82c06f4 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Sun, 7 Mar 2021 23:58:15 -0500 Subject: [PATCH 18/62] do not actually render any selection rectangle if cursorRow/cursorCol are equal to selectRow/selectCol --- widget/entry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/entry.go b/widget/entry.go index 296030e4ef..9b838a9d24 100644 --- a/widget/entry.go +++ b/widget/entry.go @@ -1421,7 +1421,7 @@ func (r *entryContentRenderer) buildSelection() { } r.content.entry.propertyLock.RUnlock() - if selectRow == -1 { + if selectRow == -1 || (cursorRow == selectRow && cursorCol == selectCol) { r.selection = r.selection[:0] return From 1050b9d78412e89cae6d3ef251579d2fabb7d3ba Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Wed, 17 Mar 2021 12:15:38 +0000 Subject: [PATCH 19/62] Actually try writing in the write test --- cmd/fyne_demo/tutorials/dialog.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/fyne_demo/tutorials/dialog.go b/cmd/fyne_demo/tutorials/dialog.go index 54fd28d121..a829d0148a 100644 --- a/cmd/fyne_demo/tutorials/dialog.go +++ b/cmd/fyne_demo/tutorials/dialog.go @@ -67,7 +67,7 @@ func dialogScreen(win fyne.Window) fyne.CanvasObject { return } - fileSaved(writer) + fileSaved(writer, win) }, win) }), widget.NewButton("Folder Open", func() { @@ -141,13 +141,18 @@ func imageOpened(f fyne.URIReadCloser) { showImage(f) } -func fileSaved(f fyne.URIWriteCloser) { +func fileSaved(f fyne.URIWriteCloser, w fyne.Window) { if f == nil { log.Println("Cancelled") return } - log.Println("Save to...", f.URI()) + defer f.Close() + _, err := f.Write([]byte("Written by Fyne demo\n")) + if err != nil { + dialog.ShowError(err, w) + } + log.Println("Saved to...", f.URI()) } func loadImage(f fyne.URIReadCloser) *canvas.Image { From d922b8d17e693df2535e7511ba2b079dc0558b91 Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Wed, 10 Mar 2021 14:11:11 -0600 Subject: [PATCH 20/62] android file save dialog --- cmd/fyne/internal/mobile/dex.go | 367 ++++++++++++----------- dialog/file_mobile.go | 7 +- internal/driver/gomobile/android.c | 32 ++ internal/driver/gomobile/file.go | 48 ++- internal/driver/gomobile/file_android.go | 46 +++ 5 files changed, 312 insertions(+), 188 deletions(-) diff --git a/cmd/fyne/internal/mobile/dex.go b/cmd/fyne/internal/mobile/dex.go index f26a9eac7f..6bca115d24 100644 --- a/cmd/fyne/internal/mobile/dex.go +++ b/cmd/fyne/internal/mobile/dex.go @@ -6,183 +6,192 @@ package mobile -var dexStr = `ZGV4CjAzNQAClnuWucRCmShMpICNQdPrq7aZEIeogCKMJAAAcAAAAHhWNBIAAAAAAAAAAL` + - `wjAAC3AAAAcAAAAC0AAABMAwAAOwAAAAAEAAASAAAAxAYAAGMAAABUBwAABgAAAGwKAABg` + - `GQAALAsAAAIVAAAEFQAACRUAABEVAAAlFQAAPBUAAEwVAABSFQAAaRUAAGwVAABxFQAAdx` + - `UAAHwVAACCFQAAhRUAAIkVAACOFQAAkhUAAJcVAACcFQAAuhUAANsVAAD2FQAAEBYAADMW` + - `AABYFgAAcRYAAIQWAACgFgAAtRYAAMsWAADkFgAAABcAABQXAABJFwAAaRcAAJUXAACqFw` + - `AA0RcAAOgXAAAFGAAANBgAAE8YAAB6GAAAnxgAAL8YAADPGAAA6RgAAAAZAAAfGQAAMxkA` + - `AEkZAABdGQAAcRkAAIgZAACtGQAA0hkAAPcZAAAeGgAAQxoAAGYaAAB8GgAAhxoAAJAaAA` + - `CqGgAArRoAALEaAAC2GgAAvRoAAMMaAADHGgAAzBoAANMaAADfGgAA5BoAAOcaAADrGgAA` + - `8BoAAAUbAAAJGwAAFRsAACEbAAAtGwAAORsAAEUbAABRGwAAXhsAAGsbAAB7GwAAhRsAAK` + - `AbAAC4GwAAyhsAAOAbAAAFHAAALxwAAFEcAAByHAAAixwAAJ4cAACsHAAAthwAAMUcAADV` + - `HAAA5RwAAPUcAAD4HAAAAB0AACMdAAA3HQAARR0AAEodAABbHQAAbB0AAHkdAACHHQAAkB` + - `0AAJ4dAACpHQAAtB0AAMcdAADUHQAA6R0AAPIdAAD9HQAADx4AACseAABFHgAAYB4AAHke` + - `AACEHgAAjh4AAJkeAACpHgAAxx4AANkeAADhHgAA7x4AAAgfAAAWHwAAJR8AADUfAABEHw` + - `AASh8AAFIfAABYHwAAZR8AAHkfAACiHwAArR8AALcfAAC9HwAAxx8AANkfAADjHwAA8x8A` + - `AAIgAAAMIAAAGiAAAB8gAAAuIAAAPSAAAEsgAABcIAAAZSAAAG4gAAB9IAAAiSAAAJcgAA` + - `ClIAAAtCAAALsgAADTIAAA4CAAAOggAADwIAAA+iAAAP8gAAAjIQAAMSEAAEMhAABKIQAA` + - `USEAAAgAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAAAB` + - `4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAAr` + - `AAAALAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOA` + - `AAADkAAAA6AAAAOwAAAEAAAABKAAAATQAAAAgAAAAAAAAAAAAAAAkAAAAAAAAA+BMAAAoA` + - `AAAAAAAAABQAAAsAAAAAAAAADBQAAAwAAAAAAAAAFBQAAA0AAAACAAAAAAAAAA0AAAAEAA` + - `AAAAAAAA4AAAAEAAAAIBQAABIAAAAEAAAAKBQAABAAAAAEAAAAMBQAABIAAAAEAAAAOBQA` + - `ABEAAAAFAAAAQBQAAA0AAAAGAAAAAAAAAA0AAAAIAAAAAAAAAA0AAAALAAAAAAAAAA4AAA` + - `AQAAAAIBQAAA0AAAASAAAAAAAAAA4AAAASAAAAIBQAAA0AAAAUAAAAAAAAAA0AAAAVAAAA` + - `AAAAABAAAAAXAAAASBQAABIAAAAXAAAAUBQAAA0AAAAbAAAAAAAAAA8AAAAcAAAA+BMAAB` + - `AAAAAfAAAAMBQAAA0AAAAhAAAAAAAAABAAAAAhAAAAMBQAABAAAAAhAAAASBQAABIAAAAh` + - `AAAAWBQAAA0AAAApAAAAAAAAAEAAAAAqAAAAAAAAAEEAAAAqAAAAIBQAAEIAAAAqAAAA+B` + - `MAAEMAAAAqAAAAYBQAAEQAAAAqAAAAbBQAAEUAAAAqAAAAeBQAAEYAAAAqAAAAgBQAAEUA` + - `AAAqAAAAiBQAAEUAAAAqAAAAkBQAAEUAAAAqAAAAmBQAAEUAAAAqAAAA8BMAAEUAAAAqAA` + - `AA6BMAAEgAAAAqAAAAoBQAAEkAAAAqAAAAuBQAAEUAAAAqAAAAwBQAAEUAAAAqAAAAyBQA` + - `AEcAAAAqAAAA0BQAAEUAAAAqAAAA4BMAAEUAAAAqAAAAMBQAAEUAAAAqAAAA3BQAAEUAAA` + - `AqAAAASBQAAEYAAAAqAAAA5BQAAEkAAAAqAAAAWBQAAEoAAAArAAAAAAAAAEwAAAArAAAA` + - `7BQAAEwAAAArAAAA9BQAAEsAAAArAAAAyBQAAEsAAAArAAAA/BQAABAAAAAsAAAAMBQAAA` + - `UACgCVAAAABwAAAI4AAAAHAAAAsAAAAAkAAAA+AAAAJAApAK0AAAAkAAAAswAAACUAKQCt` + - `AAAAJgApAK0AAAAnACgArgAAACgAKQCtAAAAKQAAAAMAAAApAAAABAAAACkAAAAFAAAAKQ` + - `AAADwAAAApAAAAPwAAACkAKQCGAAAAKQAXAJQAAAApACEAlwAAAAEAHgACAAAAAQAmAJkA` + - `AAAEADAAAgAAAAQACQBWAAAABAAHAFgAAAAEAAgAZQAAAAQABQByAAAABAANAHMAAAAEAA` + - `oAnAAAAAQACQCkAAAABgALAHAAAAAHAB4AAgAAAAcAAACHAAAABwAAALUAAAAIABkArwAA` + - `AAoAGgB7AAAADgADAGkAAAAOAAQAaQAAABAAAQBuAAAAEAAPAJAAAAASACkAWQAAABIAAA` + - `B1AAAAEgAQAHgAAAASABMAeQAAABIAAACCAAAAEgAOAIQAAAASACUAhQAAABQAEAB0AAAA` + - `FQAAAH0AAAAVAAAAfgAAABUAAAB/AAAAFQAAAIAAAAAWADYAiQAAABYANwCpAAAAFwAjAA` + - `IAAAAXACgAWgAAABcAHgBjAAAAFwA1AJ0AAAAXAB8AoAAAABcAHwChAAAAFwAsAKIAAAAX` + - `AC0AowAAABcAHwClAAAAGAAgAAIAAAAbABkAbwAAABwAAACPAAAAHAAXAKwAAAAcABkArw` + - `AAAB8AHgACAAAAIQA4AGQAAAAhADkAagAAACEAAACPAAAAIQA6AKoAAAAiADAAkQAAACQA` + - `MwACAAAAJAAeAJ4AAAAlADIAAgAAACUAHgCeAAAAJgAyAAIAAAAmACoAmgAAACcAMQACAA` + - `AAJwAnAFsAAAAnAC4AYgAAACcALgCbAAAAKAAyAAIAAAAoAB4AngAAACkAHgACAAAAKQAU` + - `AE8AAAApABUAUAAAACkAGwBRAAAAKQAcAFIAAAApAB0AUwAAACkANABUAAAAKQArAFcAAA` + - `ApAB4AZgAAACkAMABnAAAAKQAfAGgAAAApADAAbAAAACkAEQBtAAAAKQAWAHEAAAApAAYA` + - `dgAAACkADAB3AAAAKQACAHoAAAApABgAfAAAACkAGQCBAAAAKQASAIMAAAApAB4AiAAAAC` + - `kAIQCLAAAAKQAeAIwAAAApADAAjQAAACkAHgCQAAAAKQAiAJgAAAApACYAmQAAACkALwCf` + - `AAAAKQAeAKYAAAApADAApwAAACkAHwCoAAAAKQAkAKsAAAApAB4AsgAAACQAAAAAAAAAHw` + - `AAAOATAAAHAAAAkBMAAOAiAAAAAAAAJQAAAAAAAAAfAAAA4BMAAAcAAACgEwAA9CIAAAAA` + - `AAAmAAAAAAAAAB8AAADoEwAABwAAALATAAAFIwAAAAAAACcAAAAAAAAAHwAAAPATAAAHAA` + - `AAwBMAABYjAAAAAAAAKAAAAAAAAAAfAAAA4BMAAAcAAADQEwAALyMAAAAAAAApAAAAAQAA` + - `AAEAAAAAAAAABwAAAAAAAABAIwAA0yIAAAIAAACnIgAAriIAAAIAAAC3IgAAriIAAAIAAA` + - `C+IgAAriIAAAIAAADFIgAAriIAAAIAAADMIgAAriIAAAMAAwABAAAAVCEAAAgAAABbAQQA` + - `WQIFAHAQMAAAAA4ABgABAAMAAABbIQAAeAAAABUBAEASYhIEFACQAAgAUlMFACsDZQAAAB` + - `oCBgAaA7EAcSAQADIAVFIEAHEQQwACAAwCbiAmABIAVFEEAHEQQwABAAwBbiAnAAEAVFAE` + - `ABoBAABxIEYAEABUUAQAcRBDAAAADAAaAQAAbiApABAAVFAEAHEQQwAAAAwAbiAqAEAAVF` + - `AEAHEQQwAAAAwAbhAkAAAAVFAEAHEQQwAAAAwAbhAlAAAAVFAEABoBigBuIFMAEAAMAB8A` + - `FgBUUQQAcRBDAAEADAFuMCEAEAQOAAEhKKwUAJIACAABISinAAAAAQMAAAAAAAoAAABdAA` + - `AAXwAAAAIAAgABAAAAdiEAAAYAAABbAQYAcBAwAAAADgADAAEAAgAAAHwhAAAMAAAAVCAG` + - `AHEQQwAAAAwAEwEIAG4gKgAQAA4AAgACAAEAAACCIQAABgAAAFsBBwBwEDAAAAAOAAsACg` + - `ABAAAAiSEAAAYAAABUEAcAbhBiAAAADgACAAIAAQAAAJkhAAAGAAAAWwEIAHAQMAAAAA4A` + - `AgACAAAAAACgIQAAAQAAAA4AAAAFAAUAAAAAAKchAAABAAAADgAAAAgABQADAAAAsSEAAF` + - `AAAAByEC0ABAAKAFQxCABUEQkAcRBFAAEADAFuEDMAAQAKATcQLQBUMAgAVAAJAFQxCABU` + - `EQkAcRBFAAEADAFuEDMAAQAKAXIQLQAEAAoCcjAuABQCDAFyEC8AAQAMAXEgSAAQAFQwCA` + - `BUAAkAchAvAAQADAFxIEYAEAAOAHIQLQAEAAoAVDEIAFQRCQBxEEUAAQAMAW4QMwABAAoB` + - `NRDk/yjiAgACAAEAAADFIQAABgAAAFsBCQBwEDAAAAAOAAUAAQADAAAAzCEAAE4AAAAS41` + - `RACQAiARcAcQBHAAAADAJwICIAIQBxIEQAEABUQAkAcRBDAAAADAATAQgAbiAqABAAVEAJ` + - `AHEQQwAAAAwAFAGQAAgAbiAnABAAIgAYAHAwKwAwA1RBCQBxEEMAAQAMAW4gKAABAFRBCQ` + - `BUQgkAcRBDAAIADAJuMEkAIQBUQAkAcRBDAAAADAAiAScAcCA8AEEAbiAjABAADgACAAEA` + - `AQAAANshAAAKAAAAcBAAAAEAGgAAAFsQEQBpAQ8ADgACAAEAAAAAAOMhAAADAAAAVBAQAB` + - `EAAAACAAIAAAAAAOkhAAADAAAAWwEQABEBAAACAAEAAAAAAPAhAAADAAAAVBARABEAAAAC` + - `AAIAAAAAAPYhAAADAAAAWwERABEBAAABAAAAAAAAAP0hAAADAAAAYgAPABEAAAACAAIAAg` + - `AAAAIiAAAEAAAAcCBZABAADgAHAAMAAwABAAkiAAAZAAAAEvBxEBMABAAMAW4wEgBRBgoB` + - `OQEDAA8AARAo/g0BGgIGABoDawBxMBEAMgEo9Q0BKPMAAAEAAAAHAAEAAQIPFx0OAAABAA` + - `AAAQAAABoiAAAGAAAAYgAPAG4QSgAAAA4ABAABAAMAAQAgIgAAMwAAAG4QUQADAAwAbhBQ` + - `AAMADAFuEAYAAQAMARMCgABuMAoAEAIMAFQBAAA5AQoAGgAGABoBkwBxIBAAEAAOAFQAAA` + - `AaAVwAbiAPABAADABxEDUAAAAo9A0AGgEGABoCkgBxMBEAIQAo6wAAAAAAACkAAQABAR0q` + - `AgABAAIAAAAxIgAACQAAACIAKABwIEAAEABuIF0AAQAOAAAAAgABAAIAAAA6IgAABgAAAG` + - `IADwBuIEsAEAAOAAIAAQACAAAAQiIAAAYAAABiAA8AbiBMABAADgAEAAEAAwAAAEkiAAAk` + - `AAAAGgCKAG4gUwADAAwAHwAWABQBAgACAW4gTgATAAwBbhAWAAEADAFuEBkAAQAMARICbj` + - `AgABACIgAlAHAgOAAwAG4gXQADAA4ABgACAAMAAABSIgAAVwAAABITIgAEABoBXQBwIAIA` + - `EAAaAWEAbiAyAFEACgE4ARwAYAEDABMCFQA0IRYAIgAEABoBXgBwIAIAEABuIAQAMAAaAT` + - `0AcSAFABAADABuMGEABAMOABoBtgBuIDEAFQAKATgBHgBgAQMAEwITADQhGAAaAQEAbiAJ` + - `ABAAGgFgABoCTgBuIDQAJQAMAm4wCAAQAhoBXwBuIAMAEAAo024gCQBQABoBXwBuIAMAEA` + - `AoygAAAwACAAMAAABmIgAACQAAACIAJABwMDYAEAJuIF0AAQAOAAAAAgABAAEAAABvIgAA` + - `CQAAAG4QTwABAAwAbhAsAAAADAARAAAABQAEAAIAAAB0IgAAGQAAABIQMgIDAA4AEvAyAw` + - `gAGgAAAHAgTQABACj3bhAHAAQADABuEA4AAAAMAHAgTQABACjrAAAEAAIAAgAAAIYiAAAd` + - `AAAAcBBaAAIAbyABADIAcBBeAAIAFAACAAIBbiBOAAIADABuEBYAAAAMACIBJgBwIDoAIQ` + - `BuIBQAEAAOAAAABwABAAUAAQCSIgAAYAAAAG4QVQAGAAwAbhAbAAAADABuEBcAAAAMADkA` + - `AwAOAG4QHwAAAAoBbhAcAAAACgJuEB0AAAAKA24QHgAAAAoAcFBXABYyKOwNACIABwBwEA` + - `sAAABuEFUABgAMAW4QGwABAAwBbiAaAAEAFAECAAIBbiBOABYADAFuEBYAAQAMAVICAgBu` + - `EBUAAQAKA24QDAAAAAoEsUNSBAIAsUNSBAEAbhAYAAEACgFuEA0AAAAKBbFRUgABAJEAAQ` + - `BwUFcAJkMorwAAAAAiAAEAAQEeIywLAAAAAAAAAAAAAAAAAAA4CwAAAAAAAAAAAAAAAAAA` + - `RAsAAAAAAAAAAAAAAAAAAFALAAAAAAAAAAAAAAAAAABcCwAAAAAAAAAAAAAAAAAAAQAAAC` + - `AAAAABAAAAEQAAAAEAAAANAAAAAgAAAAAAAAADAAAAAAAAAAAAAAACAAAAIQAhAAMAAAAh` + - `ACEAIwAAAAEAAAAAAAAAAgAAAAQAHAABAAAAIQAAAAIAAAAhACwAAgAAAAIAAAABAAAAKQ` + - `AAAAIAAAApABcAAgAAACkAIQAEAAAAAAAAAAAAAAADAAAAAAAAAAQAAAABAAAAAwAAAAIA` + - `AAAEAAAAAQAAAAcAAAABAAAACgAAAAEAAAAMAAAACQAAABIAAAAAAAAAAAAAAAAAAAAAAA` + - `AAAgAAABIAEwABAAAAEwAAAAEAAAAcAAAABAAAABwAAAAAAAAAAQAAACgAAAACAAAAKQAA` + - `AAIAAAALAAAAAgAAABIAAAABAAAAHwAAAAMqLyoABjxpbml0PgASREVGQVVMVF9JTlBVVF` + - `9UWVBFABVERUZBVUxUX0tFWUJPQVJEX0NPREUADkZJTEVfT1BFTl9DT0RFAARGeW5lABVH` + - `b05hdGl2ZUFjdGl2aXR5LmphdmEAAUkAA0lJSQAESUlJSQADSUxMAARJTExMAAFMAAJMSQ` + - `ADTElJAAJMTAADTExJAANMTEwAHExhbmRyb2lkL2FwcC9OYXRpdmVBY3Rpdml0eTsAH0xh` + - `bmRyb2lkL2NvbnRlbnQvQ29tcG9uZW50TmFtZTsAGUxhbmRyb2lkL2NvbnRlbnQvQ29udG` + - `V4dDsAGExhbmRyb2lkL2NvbnRlbnQvSW50ZW50OwAhTGFuZHJvaWQvY29udGVudC9wbS9B` + - `Y3Rpdml0eUluZm87ACNMYW5kcm9pZC9jb250ZW50L3BtL1BhY2thZ2VNYW5hZ2VyOwAXTG` + - `FuZHJvaWQvZ3JhcGhpY3MvUmVjdDsAEUxhbmRyb2lkL25ldC9Vcmk7ABpMYW5kcm9pZC9v` + - `cy9CdWlsZCRWRVJTSU9OOwATTGFuZHJvaWQvb3MvQnVuZGxlOwAUTGFuZHJvaWQvb3MvSU` + - `JpbmRlcjsAF0xhbmRyb2lkL3RleHQvRWRpdGFibGU7ABpMYW5kcm9pZC90ZXh0L1RleHRX` + - `YXRjaGVyOwASTGFuZHJvaWQvdXRpbC9Mb2c7ADNMYW5kcm9pZC92aWV3L0tleUNoYXJhY3` + - `Rlck1hcCRVbmF2YWlsYWJsZUV4Y2VwdGlvbjsAHkxhbmRyb2lkL3ZpZXcvS2V5Q2hhcmFj` + - `dGVyTWFwOwAqTGFuZHJvaWQvdmlldy9WaWV3JE9uTGF5b3V0Q2hhbmdlTGlzdGVuZXI7AB` + - `NMYW5kcm9pZC92aWV3L1ZpZXc7ACVMYW5kcm9pZC92aWV3L1ZpZXdHcm91cCRMYXlvdXRQ` + - `YXJhbXM7ABVMYW5kcm9pZC92aWV3L1dpbmRvdzsAG0xhbmRyb2lkL3ZpZXcvV2luZG93SW` + - `5zZXRzOwAtTGFuZHJvaWQvdmlldy9pbnB1dG1ldGhvZC9JbnB1dE1ldGhvZE1hbmFnZXI7` + - `ABlMYW5kcm9pZC93aWRnZXQvRWRpdFRleHQ7AClMYW5kcm9pZC93aWRnZXQvRnJhbWVMYX` + - `lvdXQkTGF5b3V0UGFyYW1zOwAjTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ01ldGhv` + - `ZDsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAOTGphdmEvaW8vRmlsZTsAGE` + - `xqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOwAVTGphdmEvbGFuZy9FeGNlcHRpb247AB1MamF2` + - `YS9sYW5nL05vU3VjaE1ldGhvZEVycm9yOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS` + - `9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3Rl` + - `bTsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAjTG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQW` + - `N0aXZpdHkkMTsAI0xvcmcvZ29sYW5nL2FwcC9Hb05hdGl2ZUFjdGl2aXR5JDI7ACNMb3Jn` + - `L2dvbGFuZy9hcHAvR29OYXRpdmVBY3Rpdml0eSQzOwAlTG9yZy9nb2xhbmcvYXBwL0dvTm` + - `F0aXZlQWN0aXZpdHkkNCQxOwAjTG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkk` + - `NDsAIUxvcmcvZ29sYW5nL2FwcC9Hb05hdGl2ZUFjdGl2aXR5OwAUTlVNQkVSX0tFWUJPQV` + - `JEX0NPREUACU9wZW4gRmlsZQAHU0RLX0lOVAAYU0lOR0xFTElORV9LRVlCT0FSRF9DT0RF` + - `AAFWAAJWSQADVklJAAVWSUlJSQAEVklJTAACVkwAA1ZMSQAFVkxJSUkAClZMSUlJSUlJSU` + - `kAA1ZMTAABWgACWkwAA1pMSQATW0xqYXZhL2xhbmcvU3RyaW5nOwACXHwACmFjY2VzcyQw` + - `MDAACmFjY2VzcyQwMDIACmFjY2VzcyQxMDAACmFjY2VzcyQxMDIACmFjY2VzcyQyMDAACm` + - `FjY2VzcyQzMDAAC2FjY2Vzc0ZsYWdzAAthZGRDYXRlZ29yeQAOYWRkQ29udGVudFZpZXcA` + - `CGFkZEZsYWdzABlhZGRPbkxheW91dENoYW5nZUxpc3RlbmVyABZhZGRUZXh0Q2hhbmdlZE` + - `xpc3RlbmVyABBhZnRlclRleHRDaGFuZ2VkABRhbmRyb2lkLmFwcC5saWJfbmFtZQAjYW5k` + - `cm9pZC5pbnRlbnQuYWN0aW9uLk9QRU5fRE9DVU1FTlQAKGFuZHJvaWQuaW50ZW50LmFjdG` + - `lvbi5PUEVOX0RPQ1VNRU5UX1RSRUUAIGFuZHJvaWQuaW50ZW50LmNhdGVnb3J5Lk9QRU5B` + - `QkxFAB9hbmRyb2lkLmludGVudC5leHRyYS5NSU1FX1RZUEVTABdhcHBsaWNhdGlvbi94LW` + - `RpcmVjdG9yeQARYmVmb3JlVGV4dENoYW5nZWQADGJyaW5nVG9Gcm9udAAIY29udGFpbnMA` + - `DWNyZWF0ZUNob29zZXIADmRvSGlkZUtleWJvYXJkAA5kb1Nob3dGaWxlT3BlbgAOZG9TaG` + - `93S2V5Ym9hcmQAAWUABmVxdWFscwAhZXhjZXB0aW9uIHJlYWRpbmcgS2V5Q2hhcmFjdGVy` + - `TWFwABJmaWxlUGlja2VyUmV0dXJuZWQADGZpbmRWaWV3QnlJZAADZ2V0AA9nZXRBYnNvbH` + - `V0ZVBhdGgAD2dldEFjdGl2aXR5SW5mbwALZ2V0Q2FjaGVEaXIADGdldENvbXBvbmVudAAH` + - `Z2V0RGF0YQAMZ2V0RGVjb3JWaWV3AAlnZXRIZWlnaHQACWdldEludGVudAARZ2V0UGFja2` + - `FnZU1hbmFnZXIAC2dldFJvb3RWaWV3ABNnZXRSb290V2luZG93SW5zZXRzAAdnZXRSdW5l` + - `AAlnZXRTdHJpbmcAEGdldFN5c3RlbVNlcnZpY2UAGmdldFN5c3RlbVdpbmRvd0luc2V0Qm` + - `90dG9tABhnZXRTeXN0ZW1XaW5kb3dJbnNldExlZnQAGWdldFN5c3RlbVdpbmRvd0luc2V0` + - `UmlnaHQAF2dldFN5c3RlbVdpbmRvd0luc2V0VG9wAAlnZXRUbXBkaXIACGdldFdpZHRoAA` + - `lnZXRXaW5kb3cADmdldFdpbmRvd1Rva2VuABxnZXRXaW5kb3dWaXNpYmxlRGlzcGxheUZy` + - `YW1lABBnb05hdGl2ZUFjdGl2aXR5AAZoZWlnaHQADGhpZGVLZXlib2FyZAAXaGlkZVNvZn` + - `RJbnB1dEZyb21XaW5kb3cADGlucHV0X21ldGhvZAANaW5zZXRzQ2hhbmdlZAAOa2V5Ym9h` + - `cmREZWxldGUADWtleWJvYXJkVHlwZWQABGxlZnQABmxlbmd0aAAEbG9hZAALbG9hZExpYn` + - `JhcnkAEmxvYWRMaWJyYXJ5IGZhaWxlZAAnbG9hZExpYnJhcnk6IG5vIG1hbmlmZXN0IG1l` + - `dGFkYXRhIGZvdW5kAAltVGV4dEVkaXQACG1ldGFEYXRhAARuYW1lAAhvbGRTdGF0ZQAQb2` + - `5BY3Rpdml0eVJlc3VsdAAIb25DcmVhdGUADm9uTGF5b3V0Q2hhbmdlAA1vblRleHRDaGFu` + - `Z2VkAAhwdXRFeHRyYQAMcmVxdWVzdEZvY3VzAANydW4ADXJ1bk9uVWlUaHJlYWQADXNldE` + - `ltZU9wdGlvbnMADHNldElucHV0VHlwZQAPc2V0TGF5b3V0UGFyYW1zAAdzZXRUZXh0AAdz` + - `ZXRUeXBlAA1zZXRWaXNpYmlsaXR5AApzZXR1cEVudHJ5AAxzaG93RmlsZU9wZW4ADHNob3` + - `dLZXlib2FyZAANc2hvd1NvZnRJbnB1dAAFc3BsaXQAFnN0YXJ0QWN0aXZpdHlGb3JSZXN1` + - `bHQAC3N1YlNlcXVlbmNlAAZ0aGlzJDAABnRoaXMkMQAIdG9TdHJpbmcAA3RvcAAidW5rbm` + - `93biBrZXlib2FyZCB0eXBlLCB1c2UgZGVmYXVsdAAMdXBkYXRlTGF5b3V0ABB2YWwka2V5` + - `Ym9hcmRUeXBlAAV2YWx1ZQAFd2lkdGgAAXwATgIAAAcOAFEAB3cQAg5ZeZaXeLSWlpellg` + - `JtLCBLAnQdAHsBAAcOAH4ABw60AMQBAQAHDgDHAQkAAAAAAAAAAAAHDloA2QEBAAcOAOsB` + - `AQAHDgDnAQQAAAAABw4A3AEEAAAAAAcOARIPAR8TtAJ7HQDNAQEABw4A0AEABx3htMRblr` + - `UCFOAALgAHDjhOLQAaAQAHDgAaAgAABw4AGgEABw4AGgIAAAcOABoABw4AGgIAAAcOAJkB` + - `AwAAAAcdhzQCeywgHoMAcwAHDloAsAEABw5Lo0xLfwJ7HYdLHgDNAQAHDgIihgCEAQEABw` + - `5aAEoBAAcOWgB3AAcOh7SIjACIAQEABx144XhElgJ3HeFatGo8AE4BAAcOAiKGADMABw4A` + - `9AEDAAAABw4CDDsCeR08bEsAvwEBAAcOPDw9tIwAOAAHDsMCDiwCdh2HhUweWrW0/9AAAh` + - `kBtAEaTAIaAlUEAJYBHgIZAbQBGkoCGQG0ARpcAhkBtAEaQQIZAbQBGl4FRJAACAQABAEE` + - `AgQBAAIBAQSQIAGQIDaAgAToFjcBiBcAAQEBBpAgOICABIgZOQGkGQABAQEHkCA6gIAEzB` + - `k7AegZAAEBAwiQIDyAgASEGj0BoBoBAbQaAQHIGgABAQEJkCBAgIAE+BtBAZQcBgIRBwoa` + - `ARoBGgEaARoBChACAQJCgYAEwB0BiCDkHQGIIPwdAYgglB4BiCCsHgGIIMQeAYgg3B4Fgg` + - `IABQj0HgQIyB8BggIAAYICAAGCAgABAuQfBALoIAEIjCEBCKghSgDEIQEAnCIBANwjCACA` + - `JAcEpCQBAegkBgC0JREAAAAAAAAAAQAAAAAAAAABAAAAtwAAAHAAAAACAAAALQAAAEwDAA` + - `ADAAAAOwAAAAAEAAAEAAAAEgAAAMQGAAAFAAAAYwAAAFQHAAAGAAAABgAAAGwKAAADEAAA` + - `BQAAACwLAAABIAAAIAAAAGgLAAAGIAAABQAAAJATAAABEAAAIAAAAOATAAACIAAAtwAAAA` + - `IVAAADIAAAIAAAAFQhAAAEIAAABgAAAKciAAAFIAAAAQAAANMiAAAAIAAABgAAAOAiAAAA` + - `EAAAAQAAALwjAAA=` + +var dexStr = `ZGV4CjAzNQCp5hmlGxA9dMqSjxkmhaLh8XHxc3H/tQdsJgAAcAAAAHhWNBIAAAAAAAAAAJ` + + `wlAAC+AAAAcAAAAC4AAABoAwAAOwAAACAEAAATAAAA5AYAAGUAAAB8BwAABgAAAKQKAAAI` + + `GwAAZAsAACoWAAAsFgAAMRYAADYWAAA+FgAAUhYAAGkWAAB5FgAAiRYAAI8WAACmFgAAqR` + + `YAAK4WAAC0FgAAuRYAAL8WAADCFgAAxhYAAMsWAADPFgAA1BYAANkWAAD3FgAAGBcAADMX` + + `AABNFwAAcBcAAJUXAACuFwAAwRcAAN0XAADyFwAACBgAACEYAAA9GAAAURgAAIYYAACmGA` + + `AA0hgAAOcYAAAOGQAAJRkAAEIZAABxGQAAjBkAALcZAADcGQAA/BkAABsaAAArGgAARRoA` + + `AFwaAAB7GgAAjxoAAKUaAAC5GgAAzRoAAOQaAAAJGwAALhsAAFMbAAB6GwAAnxsAAMIbAA` + + `DYGwAA4xsAAOwbAAAGHAAAERwAABQcAAAYHAAAHRwAACQcAAAqHAAALhwAADMcAAA6HAAA` + + `RhwAAEscAABOHAAAUhwAAFccAABsHAAAcBwAAHwcAACIHAAAlBwAAKAcAACsHAAAuBwAAM` + + `UcAADSHAAA4hwAAOwcAAAHHQAAHx0AADEdAABHHQAAbh0AAJMdAAC9HQAA3x0AAAAeAAAZ` + + `HgAALB4AADoeAABEHgAAUx4AAGMeAABzHgAAgx4AAJMeAACWHgAAnh4AAMEeAADVHgAA4x` + + `4AAOgeAAD5HgAACh8AABcfAAAlHwAALh8AADwfAABHHwAAUh8AAGUfAAByHwAAhx8AAJAf` + + `AACbHwAArR8AAMkfAADjHwAA/h8AABcgAAAiIAAALCAAADcgAABHIAAAZSAAAHcgAAB/IA` + + `AAjSAAAKYgAAC0IAAAwyAAANMgAADiIAAA6CAAAPAgAAD2IAAAAyEAABchAABAIQAASyEA` + + `AFUhAABbIQAAZSEAAHchAACBIQAAkSEAAKAhAACqIQAAuCEAAL0hAADMIQAA2yEAAOkhAA` + + `D6IQAAAyIAAAwiAAAbIgAAJyIAADUiAABDIgAAUSIAAGAiAABnIgAAfyIAAIwiAACUIgAA` + + `nCIAAKYiAACrIgAAzyIAAN0iAADvIgAA9iIAAP0iAAAKAAAAFQAAABYAAAAXAAAAGAAAAB` + + `kAAAAaAAAAGwAAABwAAAAdAAAAHgAAAB8AAAAgAAAAIQAAACIAAAAjAAAAJAAAACUAAAAm` + + `AAAAJwAAACgAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAvAAAAMAAAADEAAAAyAAAAMw` + + `AAADQAAAA1AAAANgAAADcAAAA4AAAAOQAAADoAAAA7AAAAPAAAAD0AAAA+AAAARAAAAE4A` + + `AABRAAAACgAAAAAAAAAAAAAACwAAAAAAAAAgFQAADAAAAAAAAAAoFQAADQAAAAAAAAA0FQ` + + `AADgAAAAAAAAA8FQAADwAAAAIAAAAAAAAADwAAAAQAAAAAAAAAEAAAAAQAAABIFQAAFAAA` + + `AAQAAABQFQAAEgAAAAQAAABYFQAAFAAAAAQAAABgFQAAEwAAAAUAAABoFQAADwAAAAYAAA` + + `AAAAAADwAAAAgAAAAAAAAADwAAAAsAAAAAAAAAEAAAABAAAABIFQAADwAAABIAAAAAAAAA` + + `EAAAABIAAABIFQAADwAAABQAAAAAAAAADwAAABUAAAAAAAAAEgAAABcAAABwFQAAFAAAAB` + + `cAAAB4FQAADwAAABwAAAAAAAAAEQAAAB0AAAAgFQAAEgAAACAAAABYFQAADwAAACIAAAAA` + + `AAAAEgAAACIAAABYFQAAEgAAACIAAABwFQAAFAAAACIAAACAFQAADwAAACoAAAAAAAAARA` + + `AAACsAAAAAAAAARQAAACsAAABIFQAARgAAACsAAAAgFQAARwAAACsAAACIFQAASAAAACsA` + + `AACUFQAASQAAACsAAACgFQAASgAAACsAAACoFQAASQAAACsAAACwFQAASQAAACsAAAC4FQ` + + `AASQAAACsAAADAFQAASQAAACsAAAAYFQAASQAAACsAAAAQFQAATAAAACsAAADIFQAATQAA` + + `ACsAAADgFQAASQAAACsAAADoFQAASQAAACsAAADwFQAASwAAACsAAAD4FQAASQAAACsAAA` + + `AIFQAASQAAACsAAABYFQAASQAAACsAAAAEFgAASQAAACsAAABwFQAASgAAACsAAAAMFgAA` + + `TQAAACsAAACAFQAATgAAACwAAAAAAAAAUAAAACwAAAAUFgAAUAAAACwAAAAcFgAATwAAAC` + + `wAAADwFQAATwAAACwAAAAkFgAAEgAAAC0AAABYFQAABQAKAJsAAAAHAAAAlAAAAAcAAAC3` + + `AAAACQAAAEEAAAAlACoAtAAAACUAAAC6AAAAJgAqALQAAAAnACoAtAAAACgAKQC1AAAAKQ` + + `AqALQAAAAqAAAABAAAACoAAAAFAAAAKgAAAAYAAAAqAAAABwAAACoAAAA/AAAAKgAAAEIA` + + `AAAqACoAjAAAACoAFwCaAAAAKgAiAJ0AAAABAB4AAwAAAAEAJgCfAAAABAAwAAMAAAAEAA` + + `kAWgAAAAQABwBcAAAABAAIAGoAAAAEAAUAeAAAAAQADQB5AAAABAAKAKIAAAAEAAkAqgAA` + + `AAYACwB2AAAABwAeAAMAAAAHAAAAjQAAAAcAAAC8AAAACAAZALYAAAAKABoAgQAAAA4AAw` + + `BvAAAADgAEAG8AAAAQAAEAdAAAABAADwCWAAAAEgApAF0AAAASAAAAewAAABIAEAB+AAAA` + + `EgATAH8AAAASAAAAiAAAABIADgCKAAAAEgAlAIsAAAAUABAAegAAABUAAACDAAAAFQAAAI` + + `QAAAAVAAAAhQAAABUAAACGAAAAFgA2AI8AAAAWADcAsAAAABcAIwADAAAAFwAoAF4AAAAX` + + `AB4AaAAAABcANQCjAAAAFwAfAKYAAAAXAB8ApwAAABcALACoAAAAFwAtAKkAAAAXAB8Aqw` + + `AAABgAIAADAAAAHAAZAHUAAAAdAAAAlQAAAB0AFwCzAAAAHQAZALYAAAAgAB4AAwAAACIA` + + `OABpAAAAIgA5AHAAAAAiAAAAlQAAACIAOgCxAAAAIwAwAJcAAAAlADMAAwAAACUAHgCkAA` + + `AAJgAyAAMAAAAmAB4ApAAAACcAMgADAAAAJwAqAKAAAAAoADEAAwAAACgAJwBfAAAAKAAu` + + `AGcAAAAoAC4AoQAAACkAMgADAAAAKQAeAKQAAAAqAB4AAwAAACoAFABTAAAAKgAVAFQAAA` + + `AqABsAVQAAACoAHABWAAAAKgAdAFcAAAAqADQAWAAAACoAKwBbAAAAKgAeAGsAAAAqADAA` + + `bAAAACoAMABtAAAAKgAfAG4AAAAqADAAcgAAACoAEQBzAAAAKgAWAHcAAAAqAAYAfAAAAC` + + `oADAB9AAAAKgACAIAAAAAqABgAggAAACoAGQCHAAAAKgASAIkAAAAqAB4AjgAAACoAIQCR` + + `AAAAKgAeAJIAAAAqADAAkwAAACoAHgCWAAAAKgAiAJ4AAAAqACYAnwAAACoALwClAAAAKg` + + `AeAKwAAAAqADAArQAAACoAMACuAAAAKgAfAK8AAAAqACQAsgAAACoAHgC5AAAAJQAAAAAA` + + `AAAgAAAACBUAAAkAAACwFAAAsyQAAAAAAAAmAAAAAAAAACAAAAAIFQAACQAAAMgUAADHJA` + + `AAAAAAACcAAAAAAAAAIAAAABAVAAAJAAAA2BQAANgkAAAAAAAAKAAAAAAAAAAgAAAAGBUA` + + `AAkAAADoFAAA6SQAAAAAAAApAAAAAAAAACAAAAAIFQAACQAAAPgUAAACJQAAAAAAACoAAA` + + `ABAAAAAQAAAAAAAAAJAAAAAAAAABMlAACkJAAAAgAAAG8kAAB2JAAAAQAAAH8kAAACAAAA` + + `iCQAAHYkAAACAAAAjyQAAHYkAAACAAAAliQAAHYkAAACAAAAnSQAAHYkAAADAAMAAQAAAA` + + `AjAAAIAAAAWwEEAFkCBQBwEDAAAAAOAAYAAQADAAAAByMAAHgAAAAVAQBAEmISBBQAkAAI` + + `AFJTBQArA2UAAAAaAggAGgO4AHEgEAAyAFRSBABxEEMAAgAMAm4gJgASAFRRBABxEEMAAQ` + + `AMAW4gJwABAFRQBAAaAQAAcSBGABAAVFAEAHEQQwAAAAwAGgEAAG4gKQAQAFRQBABxEEMA` + + `AAAMAG4gKgBAAFRQBABxEEMAAAAMAG4QJAAAAFRQBABxEEMAAAAMAG4QJQAAAFRQBAAaAZ` + + `AAbiBUABAADAAfABYAVFEEAHEQQwABAAwBbjAhABAEDgABISisFACSAAgAASEopwAAAAED` + + `AAAAAAAKAAAAXQAAAF8AAAACAAIAAQAAACIjAAAGAAAAWwEGAHAQMAAAAA4AAwABAAIAAA` + + `AoIwAADAAAAFQgBgBxEEMAAAAMABMBCABuICoAEAAOAAIAAgABAAAALiMAAAYAAABbAQcA` + + `cBAwAAAADgALAAoAAQAAADUjAAAGAAAAVBAHAG4QZAAAAA4AAgACAAEAAABFIwAABgAAAF` + + `sBCABwEDAAAAAOAAIAAgAAAAAATCMAAAEAAAAOAAAABQAFAAAAAABTIwAAAQAAAA4AAAAI` + + `AAUAAwAAAF0jAABQAAAAchAtAAQACgBUMQgAVBEJAHEQRQABAAwBbhAzAAEACgE3EC0AVD` + + `AIAFQACQBUMQgAVBEJAHEQRQABAAwBbhAzAAEACgFyEC0ABAAKAnIwLgAUAgwBchAvAAEA` + + `DAFxIEgAEABUMAgAVAAJAHIQLwAEAAwBcSBGABAADgByEC0ABAAKAFQxCABUEQkAcRBFAA` + + `EADAFuEDMAAQAKATUQ5P8o4gIAAgABAAAAcSMAAAYAAABbAQkAcBAwAAAADgAFAAEAAwAA` + + `AHgjAABOAAAAEuNUQAkAIgEXAHEARwAAAAwCcCAiACEAcSBEABAAVEAJAHEQQwAAAAwAEw` + + `EIAG4gKgAQAFRACQBxEEMAAAAMABQBkAAIAG4gJwAQACIAGABwMCsAMANUQQkAcRBDAAEA` + + `DAFuICgAAQBUQQkAVEIJAHEQQwACAAwCbjBJACEAVEAJAHEQQwAAAAwAIgEoAHAgPABBAG` + + `4gIwAQAA4AAgABAAEAAACHIwAACgAAAHAQAAABABoAAABbEBIAaQEQAA4AAgABAAAAAACP` + + `IwAAAwAAAFQQEQARAAAAAgACAAAAAACVIwAAAwAAAFsBEQARAQAAAgABAAAAAACcIwAAAw` + + `AAAFQQEgARAAAAAgACAAAAAACiIwAAAwAAAFsBEgARAQAAAQAAAAAAAACpIwAAAwAAAGIA` + + `EAARAAAAAgACAAIAAACuIwAABAAAAHAgWgAQAA4ABwADAAMAAQC1IwAAGQAAABLwcRATAA` + + `QADAFuMBIAUQYKATkBAwAPAAEQKP4NARoCCAAaA3EAcTARADIBKPUNASjzAAABAAAABwAB` + + `AAECDxceDgAAAQAAAAEAAADGIwAABgAAAGIAEABuEEoAAAAOAAQAAQADAAEAzCMAADMAAA` + + `BuEFIAAwAMAG4QUQADAAwBbhAGAAEADAETAoAAbjAKABACDABUAQAAOQEKABoACAAaAZkA` + + `cSAQABAADgBUAAAAGgFgAG4gDwAQAAwAcRA1AAAAKPQNABoBCAAaApgAcTARACEAKOsAAA` + + `AAAAApAAEAAQEeKgIAAQACAAAA3SMAAAkAAAAiACkAcCBAABAAbiBeAAEADgAAAAIAAQAC` + + `AAAA5iMAAAYAAABiABAAbiBLABAADgACAAEAAgAAAO4jAAAGAAAAYgAQAG4gTAAQAA4AAg` + + `ABAAIAAAD2IwAABgAAAGIAEABuIE0AEAAOAAQAAQADAAAA/SMAACQAAAAaAJAAbiBUAAMA` + + `DAAfABYAFAECAAIBbiBPABMADAFuEBYAAQAMAW4QGQABAAwBEgJuMCAAEAIiACYAcCA4AD` + + `AAbiBeAAMADgAGAAIAAwAAAAYkAABXAAAAEhMiAAQAGgFiAHAgAgAQABoBZgBuIDIAUQAK` + + `ATgBHABgAQMAEwIVADQhFgAiAAQAGgFjAHAgAgAQAG4gBAAwABoBQABxIAUAEAAMAG4wYw` + + `AEAw4AGgG9AG4gMQAVAAoBOAEeAGABAwATAhMANCEYABoBAgBuIAkAEAAaAWUAGgJSAG4g` + + `NAAlAAwCbjAIABACGgFkAG4gAwAQACjTbiAJAFAAGgFkAG4gAwAQACjKAAAFAAIAAwAAAB` + + `okAABYAAAAIgAEABoBYQBwIAIAEAAaAWYAbiAyAEEACgE4AR4AYAEDABMCFQA0IRgAIgAE` + + `ABoBYwBwIAIAEAASEW4gBAAQABoBQwBxIAUAEAAMABIhbjBjAAMBDgAaAb0AbiAxABQACg` + + `E4AR4AYAEDABMCEwA0IRgAGgECAG4gCQAQABoBZQAaAlIAbiA0ACQADAJuMAgAEAIaAWQA` + + `biADABAAKNJuIAkAQAAaAWQAbiADABAAKMkDAAIAAwAAAC4kAAAJAAAAIgAlAHAwNgAQAm` + + `4gXgABAA4AAAACAAEAAQAAADckAAAJAAAAbhBQAAEADABuECwAAAAMABEAAAAFAAQAAgAA` + + `ADwkAAAcAAAAEhAyAgYAEiAyAgMADgAS8DIDCAAaAAAAcCBOAAEAKPduEAcABAAMAG4QDg` + + `AAAAwAcCBOAAEAKOsEAAIAAgAAAE4kAAAdAAAAcBBbAAIAbyABADIAcBBfAAIAFAACAAIB` + + `biBPAAIADABuEBYAAAAMACIBJwBwIDoAIQBuIBQAEAAOAAAABwABAAUAAQBaJAAAYAAAAG` + + `4QVgAGAAwAbhAbAAAADABuEBcAAAAMADkAAwAOAG4QHwAAAAoBbhAcAAAACgJuEB0AAAAK` + + `A24QHgAAAAoAcFBYABYyKOwNACIABwBwEAsAAABuEFYABgAMAW4QGwABAAwBbiAaAAEAFA` + + `ECAAIBbiBPABYADAFuEBYAAQAMAVICAgBuEBUAAQAKA24QDAAAAAoEsUNSBAIAsUNSBAEA` + + `bhAYAAEACgFuEA0AAAAKBbFRUgABAJEAAQBwUFgAJkMorwAAAAAiAAEAAQEfI2QLAAAAAA` + + `AAAQAAAAAAAAA2AAAAcAsAAHgLAAAAAAAAAAAAAAAAAACECwAAAAAAAAAAAAAAAAAAkAsA` + + `AAAAAAAAAAAAAAAAAJwLAAAAAAAAAAAAAAAAAAABAAAAIQAAAAEAAAARAAAAAQAAAA0AAA` + + `ACAAAAAAAAAAMAAAAAAAAAAAAAAAIAAAAiACIAAwAAACIAIgAkAAAAAQAAAAAAAAACAAAA` + + `BAAdAAEAAAAiAAAAAgAAACIALQACAAAAAgAAAAEAAAAqAAAAAgAAACoAFwACAAAAKgAiAA` + + `QAAAAAAAAAAAAAAAMAAAAAAAAABAAAAAEAAAADAAAAAgAAAAQAAAABAAAABwAAAAEAAAAK` + + `AAAAAQAAAAwAAAAJAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAACAAAAEgATAAEAAAATAAAAAQ` + + `AAAB0AAAAEAAAAHQAAAAAAAAABAAAAKQAAAAIAAAAqAAAAAgAAAAsAAAACAAAAEgAAAAEA` + + `AAAgAAAAAygpVgADKi8qAAY8aW5pdD4AEkRFRkFVTFRfSU5QVVRfVFlQRQAVREVGQVVMVF` + + `9LRVlCT0FSRF9DT0RFAA5GSUxFX09QRU5fQ09ERQAORklMRV9TQVZFX0NPREUABEZ5bmUA` + + `FUdvTmF0aXZlQWN0aXZpdHkuamF2YQABSQADSUlJAARJSUlJAANJTEwABElMTEwAAUwAAk` + + `xJAANMSUkAAkxMAANMTEkAA0xMTAAcTGFuZHJvaWQvYXBwL05hdGl2ZUFjdGl2aXR5OwAf` + + `TGFuZHJvaWQvY29udGVudC9Db21wb25lbnROYW1lOwAZTGFuZHJvaWQvY29udGVudC9Db2` + + `50ZXh0OwAYTGFuZHJvaWQvY29udGVudC9JbnRlbnQ7ACFMYW5kcm9pZC9jb250ZW50L3Bt` + + `L0FjdGl2aXR5SW5mbzsAI0xhbmRyb2lkL2NvbnRlbnQvcG0vUGFja2FnZU1hbmFnZXI7AB` + + `dMYW5kcm9pZC9ncmFwaGljcy9SZWN0OwARTGFuZHJvaWQvbmV0L1VyaTsAGkxhbmRyb2lk` + + `L29zL0J1aWxkJFZFUlNJT047ABNMYW5kcm9pZC9vcy9CdW5kbGU7ABRMYW5kcm9pZC9vcy` + + `9JQmluZGVyOwAXTGFuZHJvaWQvdGV4dC9FZGl0YWJsZTsAGkxhbmRyb2lkL3RleHQvVGV4` + + `dFdhdGNoZXI7ABJMYW5kcm9pZC91dGlsL0xvZzsAM0xhbmRyb2lkL3ZpZXcvS2V5Q2hhcm` + + `FjdGVyTWFwJFVuYXZhaWxhYmxlRXhjZXB0aW9uOwAeTGFuZHJvaWQvdmlldy9LZXlDaGFy` + + `YWN0ZXJNYXA7ACpMYW5kcm9pZC92aWV3L1ZpZXckT25MYXlvdXRDaGFuZ2VMaXN0ZW5lcj` + + `sAE0xhbmRyb2lkL3ZpZXcvVmlldzsAJUxhbmRyb2lkL3ZpZXcvVmlld0dyb3VwJExheW91` + + `dFBhcmFtczsAFUxhbmRyb2lkL3ZpZXcvV2luZG93OwAbTGFuZHJvaWQvdmlldy9XaW5kb3` + + `dJbnNldHM7AC1MYW5kcm9pZC92aWV3L2lucHV0bWV0aG9kL0lucHV0TWV0aG9kTWFuYWdl` + + `cjsAGUxhbmRyb2lkL3dpZGdldC9FZGl0VGV4dDsAKUxhbmRyb2lkL3dpZGdldC9GcmFtZU` + + `xheW91dCRMYXlvdXRQYXJhbXM7ACNMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nTWV0` + + `aG9kOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7AB1MZGFsdmlrL2Fubm90YX` + + `Rpb24vU2lnbmF0dXJlOwAOTGphdmEvaW8vRmlsZTsAGExqYXZhL2xhbmcvQ2hhclNlcXVl` + + `bmNlOwAVTGphdmEvbGFuZy9FeGNlcHRpb247AB1MamF2YS9sYW5nL05vU3VjaE1ldGhvZE` + + `Vycm9yOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGph` + + `dmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3` + + `dhYmxlOwAjTG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkMTsAI0xvcmcvZ29s` + + `YW5nL2FwcC9Hb05hdGl2ZUFjdGl2aXR5JDI7ACNMb3JnL2dvbGFuZy9hcHAvR29OYXRpdm` + + `VBY3Rpdml0eSQzOwAlTG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkNCQxOwAj` + + `TG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkNDsAIUxvcmcvZ29sYW5nL2FwcC` + + `9Hb05hdGl2ZUFjdGl2aXR5OwAUTlVNQkVSX0tFWUJPQVJEX0NPREUACU9wZW4gRmlsZQAH` + + `U0RLX0lOVAAYU0lOR0xFTElORV9LRVlCT0FSRF9DT0RFAAlTYXZlIEZpbGUAAVYAAlZJAA` + + `NWSUkABVZJSUlJAARWSUlMAAJWTAADVkxJAAVWTElJSQAKVkxJSUlJSUlJSQADVkxMAAFa` + + `AAJaTAADWkxJABNbTGphdmEvbGFuZy9TdHJpbmc7AAJcfAAKYWNjZXNzJDAwMAAKYWNjZX` + + `NzJDAwMgAKYWNjZXNzJDEwMAAKYWNjZXNzJDEwMgAKYWNjZXNzJDIwMAAKYWNjZXNzJDMw` + + `MAALYWNjZXNzRmxhZ3MAC2FkZENhdGVnb3J5AA5hZGRDb250ZW50VmlldwAIYWRkRmxhZ3` + + `MAGWFkZE9uTGF5b3V0Q2hhbmdlTGlzdGVuZXIAFmFkZFRleHRDaGFuZ2VkTGlzdGVuZXIA` + + `EGFmdGVyVGV4dENoYW5nZWQAFGFuZHJvaWQuYXBwLmxpYl9uYW1lACVhbmRyb2lkLmludG` + + `VudC5hY3Rpb24uQ1JFQVRFX0RPQ1VNRU5UACNhbmRyb2lkLmludGVudC5hY3Rpb24uT1BF` + + `Tl9ET0NVTUVOVAAoYW5kcm9pZC5pbnRlbnQuYWN0aW9uLk9QRU5fRE9DVU1FTlRfVFJFRQ` + + `AgYW5kcm9pZC5pbnRlbnQuY2F0ZWdvcnkuT1BFTkFCTEUAH2FuZHJvaWQuaW50ZW50LmV4` + + `dHJhLk1JTUVfVFlQRVMAF2FwcGxpY2F0aW9uL3gtZGlyZWN0b3J5ABFiZWZvcmVUZXh0Q2` + + `hhbmdlZAAMYnJpbmdUb0Zyb250AAhjb250YWlucwANY3JlYXRlQ2hvb3NlcgAOZG9IaWRl` + + `S2V5Ym9hcmQADmRvU2hvd0ZpbGVPcGVuAA5kb1Nob3dGaWxlU2F2ZQAOZG9TaG93S2V5Ym` + + `9hcmQAAWUABmVxdWFscwAhZXhjZXB0aW9uIHJlYWRpbmcgS2V5Q2hhcmFjdGVyTWFwABJm` + + `aWxlUGlja2VyUmV0dXJuZWQADGZpbmRWaWV3QnlJZAADZ2V0AA9nZXRBYnNvbHV0ZVBhdG` + + `gAD2dldEFjdGl2aXR5SW5mbwALZ2V0Q2FjaGVEaXIADGdldENvbXBvbmVudAAHZ2V0RGF0` + + `YQAMZ2V0RGVjb3JWaWV3AAlnZXRIZWlnaHQACWdldEludGVudAARZ2V0UGFja2FnZU1hbm` + + `FnZXIAC2dldFJvb3RWaWV3ABNnZXRSb290V2luZG93SW5zZXRzAAdnZXRSdW5lAAlnZXRT` + + `dHJpbmcAEGdldFN5c3RlbVNlcnZpY2UAGmdldFN5c3RlbVdpbmRvd0luc2V0Qm90dG9tAB` + + `hnZXRTeXN0ZW1XaW5kb3dJbnNldExlZnQAGWdldFN5c3RlbVdpbmRvd0luc2V0UmlnaHQA` + + `F2dldFN5c3RlbVdpbmRvd0luc2V0VG9wAAlnZXRUbXBkaXIACGdldFdpZHRoAAlnZXRXaW` + + `5kb3cADmdldFdpbmRvd1Rva2VuABxnZXRXaW5kb3dWaXNpYmxlRGlzcGxheUZyYW1lABBn` + + `b05hdGl2ZUFjdGl2aXR5AAZoZWlnaHQADGhpZGVLZXlib2FyZAAXaGlkZVNvZnRJbnB1dE` + + `Zyb21XaW5kb3cADGlucHV0X21ldGhvZAANaW5zZXRzQ2hhbmdlZAAOa2V5Ym9hcmREZWxl` + + `dGUADWtleWJvYXJkVHlwZWQABGxlZnQABmxlbmd0aAAEbG9hZAALbG9hZExpYnJhcnkAEm` + + `xvYWRMaWJyYXJ5IGZhaWxlZAAnbG9hZExpYnJhcnk6IG5vIG1hbmlmZXN0IG1ldGFkYXRh` + + `IGZvdW5kAAltVGV4dEVkaXQACG1ldGFEYXRhAARuYW1lAAhvbGRTdGF0ZQAQb25BY3Rpdm` + + `l0eVJlc3VsdAAIb25DcmVhdGUADm9uTGF5b3V0Q2hhbmdlAA1vblRleHRDaGFuZ2VkAAhw` + + `dXRFeHRyYQAMcmVxdWVzdEZvY3VzAANydW4ADXJ1bk9uVWlUaHJlYWQADXNldEltZU9wdG` + + `lvbnMADHNldElucHV0VHlwZQAPc2V0TGF5b3V0UGFyYW1zAAdzZXRUZXh0AAdzZXRUeXBl` + + `AA1zZXRWaXNpYmlsaXR5AApzZXR1cEVudHJ5AAxzaG93RmlsZU9wZW4ADHNob3dGaWxlU2` + + `F2ZQAMc2hvd0tleWJvYXJkAA1zaG93U29mdElucHV0AAVzcGxpdAAWc3RhcnRBY3Rpdml0` + + `eUZvclJlc3VsdAALc3ViU2VxdWVuY2UABnRoaXMkMAAGdGhpcyQxAAh0b1N0cmluZwADdG` + + `9wACJ1bmtub3duIGtleWJvYXJkIHR5cGUsIHVzZSBkZWZhdWx0AAx1cGRhdGVMYXlvdXQA` + + `EHZhbCRrZXlib2FyZFR5cGUABXZhbHVlAAV3aWR0aAABfABPAgAABw4AUgAHdxACDll5lp` + + `d4tJaWl6WWAm0sIEsCdB0AfAEABw4AfwAHDrQA2AEBAAcOANsBCQAAAAAAAAAAAAcOWgDt` + + `AQEABw4A/wEBAAcOAPsBBAAAAAAHDgDwAQQAAAAABw4BEg8BHxO0AnsdAOEBAQAHDgDkAQ` + + `AHHeG0xFuWtQIU4AAvAAcOOE4tABoBAAcOABoCAAAHDgAaAQAHDgAaAgAABw4AGgAHDgAa` + + `AgAABw4ArQEDAAAABx2HNAJ7LCAegwB0AAcOWgDEAQAHDkujTEt/Ansdh0seAOEBAAcOAi` + + `KGAIUBAQAHDloAmQEBAAcOWgBLAQAHDloAeAAHDoe0iIwAiQEBAAcdeOF4RJYCdx3hWrRq` + + `PACdAQEABw544XhTpQJ3HeFatGo8AE8BAAcOAiKGADQABw4AiAIDAAAABw4CDGgCeR08bE` + + `sA0wEBAAcOPDw9tIwAOQAHDsMCDiwCdh2HhUweWrW0/9AAAhkBuwEaTQIaAlkEAJwBHgIb` + + `AbsBHAEXAQIZAbsBGkoCGQG7ARpdAhkBuwEaQQIZAbsBGl8GRJAACAQABAEEAgQCBAEAAg` + + `EBBJAgAZAgNoCABKgXNwHIFwABAQEGkCA4gIAEyBk5AeQZAAEBAQeQIDqAgASMGjsBqBoA` + + `AQEDCJAgPICABMQaPQHgGgEB9BoBAYgbAAEBAQmQIECAgAS4HEEB1BwHAhIIChoBGgEaAR` + + `oBGgEaAQoRAgECQoGABIAeAYggpB4BiCC8HgGIINQeAYgg7B4BiCCEHwGIIJwfBoICAAUI` + + `tB8ECIggAYICAAGCAgABggIAAQKkIAQCqCEBCMwhAQjoIQEIhCJKAKAiAQD4IgEAuCQBAP` + + `glCACcJgcEwCYBAYgnBwDUJwAAABEAAAAAAAAAAQAAAAAAAAABAAAAvgAAAHAAAAACAAAA` + + `LgAAAGgDAAADAAAAOwAAACAEAAAEAAAAEwAAAOQGAAAFAAAAZQAAAHwHAAAGAAAABgAAAK` + + `QKAAADEAAABgAAAGQLAAABIAAAIgAAAKgLAAAGIAAABQAAALAUAAABEAAAIAAAAAgVAAAC` + + `IAAAvgAAACoWAAADIAAAIgAAAAAjAAAEIAAABwAAAG8kAAAFIAAAAQAAAKQkAAAAIAAABg` + + `AAALMkAAAAEAAAAQAAAJwlAAA=` + `` diff --git a/dialog/file_mobile.go b/dialog/file_mobile.go index 8e9350f620..d2fd3e36fe 100644 --- a/dialog/file_mobile.go +++ b/dialog/file_mobile.go @@ -36,12 +36,7 @@ func fileOpenOSOverride(f *FileDialog) bool { } func fileSaveOSOverride(f *FileDialog) bool { - ShowInformation("File Save", "File save not available on mobile", f.parent) - - callback := f.callback.(func(fyne.URIWriteCloser, error)) - if callback != nil { - callback(nil, nil) - } + gomobile.ShowFileSavePicker(f.callback.(func(fyne.URIWriteCloser, error))) return true } diff --git a/internal/driver/gomobile/android.c b/internal/driver/gomobile/android.c index 930a46dcf0..43501b05fc 100644 --- a/internal/driver/gomobile/android.c +++ b/internal/driver/gomobile/android.c @@ -144,6 +144,25 @@ void* openStream(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { return (*env)->NewGlobalRef(env, stream); } +void* saveStream(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { + JNIEnv *env = (JNIEnv*)jni_env; + jobject resolver = getContentResolver(jni_env, ctx); + + jclass resolverClass = (*env)->GetObjectClass(env, resolver); + jmethodID saveOutputStream = find_method(env, resolverClass, "openOutputStream", "(Landroid/net/Uri;)Ljava/io/OutputStream;"); + + jobject uri = parseURI(jni_env, ctx, uriCstr); + jobject stream = (jobject)(*env)->CallObjectMethod(env, resolver, saveOutputStream, uri); + jthrowable loadErr = (*env)->ExceptionOccurred(env); + + if (loadErr != NULL) { + (*env)->ExceptionClear(env); + return NULL; + } + + return (*env)->NewGlobalRef(env, stream); +} + char* readStream(uintptr_t jni_env, uintptr_t ctx, void* stream, int len, int* total) { JNIEnv *env = (JNIEnv*)jni_env; jclass streamClass = (*env)->GetObjectClass(env, stream); @@ -162,6 +181,19 @@ char* readStream(uintptr_t jni_env, uintptr_t ctx, void* stream, int len, int* t return bytes; } +void writeStream(uintptr_t jni_env, uintptr_t ctx, void* stream, char* buf, int len) { + JNIEnv *env = (JNIEnv*)jni_env; + jclass streamClass = (*env)->GetObjectClass(env, stream); + jmethodID write = find_method(env, streamClass, "write", "([BII)V"); + + jbyteArray data = (*env)->NewByteArray(env, len); + jboolean isCopy; + void *underlying = (*env)->GetPrimitiveArrayCritical(env, (jarray)data, &isCopy); + memcpy(underlying, buf, len); + + (*env)->CallVoidMethod(env, stream, write, data, 0, len); +} + void closeStream(uintptr_t jni_env, uintptr_t ctx, void* stream) { JNIEnv *env = (JNIEnv*)jni_env; jclass streamClass = (*env)->GetObjectClass(env, stream); diff --git a/internal/driver/gomobile/file.go b/internal/driver/gomobile/file.go index 51834c354c..91d92f03f6 100644 --- a/internal/driver/gomobile/file.go +++ b/internal/driver/gomobile/file.go @@ -43,14 +43,14 @@ func mobileFilter(filter storage.FileFilter) *app.FileFilter { return mobile } -type hasPicker interface { +type hasOpenPicker interface { ShowFileOpenPicker(func(string, func()), *app.FileFilter) } // ShowFileOpenPicker loads the native file open dialog and returns the chosen file path via the callback func. func ShowFileOpenPicker(callback func(fyne.URIReadCloser, error), filter storage.FileFilter) { drv := fyne.CurrentApp().Driver().(*mobileDriver) - if a, ok := drv.app.(hasPicker); ok { + if a, ok := drv.app.(hasOpenPicker); ok { a.ShowFileOpenPicker(func(uri string, closer func()) { if uri == "" { callback(nil, nil) @@ -69,7 +69,7 @@ func ShowFileOpenPicker(callback func(fyne.URIReadCloser, error), filter storage func ShowFolderOpenPicker(callback func(fyne.ListableURI, error)) { filter := storage.NewMimeTypeFileFilter([]string{"application/x-directory"}) drv := fyne.CurrentApp().Driver().(*mobileDriver) - if a, ok := drv.app.(hasPicker); ok { + if a, ok := drv.app.(hasOpenPicker); ok { a.ShowFileOpenPicker(func(uri string, _ func()) { if uri == "" { callback(nil, nil) @@ -80,3 +80,45 @@ func ShowFolderOpenPicker(callback func(fyne.ListableURI, error)) { }, mobileFilter(filter)) } } + +type fileSave struct { + io.WriteCloser + uri fyne.URI + done func() +} + +func (f *fileSave) URI() fyne.URI { + return f.uri +} + +func fileWriterForURI(u fyne.URI) (fyne.URIWriteCloser, error) { + file := &fileSave{uri: u} + write, err := nativeFileSave(file) + if write == nil { + return nil, err + } + file.WriteCloser = write + return file, err +} + +type hasSavePicker interface { + ShowFileSavePicker(func(string, func())) +} + +// ShowFileSavePicker loads the native file save dialog and returns the chosen file path via the callback func. +func ShowFileSavePicker(callback func(fyne.URIWriteCloser, error)) { + drv := fyne.CurrentApp().Driver().(*mobileDriver) + if a, ok := drv.app.(hasSavePicker); ok { + a.ShowFileSavePicker(func(uri string, closer func()) { + if uri == "" { + callback(nil, nil) + return + } + f, err := fileWriterForURI(storage.NewURI(uri)) + if f != nil { + f.(*fileSave).done = closer + } + callback(f, err) + }) + } +} diff --git a/internal/driver/gomobile/file_android.go b/internal/driver/gomobile/file_android.go index 5c69c8af45..a1e6f84c85 100644 --- a/internal/driver/gomobile/file_android.go +++ b/internal/driver/gomobile/file_android.go @@ -9,6 +9,8 @@ package gomobile void* openStream(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); char* readStream(uintptr_t jni_env, uintptr_t ctx, void* stream, int len, int* total); +void* saveStream(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); +void writeStream(uintptr_t jni_env, uintptr_t ctx, void* stream, char* data, int len); void closeStream(uintptr_t jni_env, uintptr_t ctx, void* stream); */ import "C" @@ -92,6 +94,50 @@ func nativeFileOpen(f *fileOpen) (io.ReadCloser, error) { return stream, nil } +func saveStream(uri string) unsafe.Pointer { + uriStr := C.CString(uri) + defer C.free(unsafe.Pointer(uriStr)) + + var stream unsafe.Pointer + app.RunOnJVM(func(_, env, ctx uintptr) error { + streamPtr := C.saveStream(C.uintptr_t(env), C.uintptr_t(ctx), uriStr) + if streamPtr == C.NULL { + return os.ErrNotExist + } + + stream = unsafe.Pointer(streamPtr) + return nil + }) + return stream +} + +func nativeFileSave(f *fileSave) (io.WriteCloser, error) { + if f.uri == nil || f.uri.String() == "" { + return nil, nil + } + + ret := saveStream(f.uri.String()) + if ret == nil { + return nil, errors.New("resource not found at URI") + } + + stream := &javaStream{} + stream.stream = ret + return stream, nil +} + +// Declare conformity to WriteCloser interface +var _ io.WriteCloser = (*javaStream)(nil) + +func (s *javaStream) Write(p []byte) (int, error) { + err := app.RunOnJVM(func(_, env, ctx uintptr) error { + C.writeStream(C.uintptr_t(env), C.uintptr_t(ctx), s.stream, (*C.char)(unsafe.Pointer(&p[0])), C.int(len(p))) + return nil + }) + + return len(p), err +} + func registerRepository(d *mobileDriver) { repo := &mobileFileRepo{driver: d} repository.Register("file", repo) From d094f811425ec30b5f2b2cd4328979bb56c42e49 Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Wed, 10 Mar 2021 14:29:24 -0600 Subject: [PATCH 21/62] add missing stubs for native file save --- internal/driver/gomobile/file_desktop.go | 5 +++++ internal/driver/gomobile/file_ios.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/internal/driver/gomobile/file_desktop.go b/internal/driver/gomobile/file_desktop.go index d8833575f2..ba84061029 100644 --- a/internal/driver/gomobile/file_desktop.go +++ b/internal/driver/gomobile/file_desktop.go @@ -14,6 +14,11 @@ func nativeFileOpen(*fileOpen) (io.ReadCloser, error) { return nil, nil } +func nativeFileSave(*fileSave) (io.WriteCloser, error) { + // no-op as we use the internal FileRepository + return nil, nil +} + func registerRepository(d *mobileDriver) { repository.Register("file", intRepo.NewFileRepository()) } diff --git a/internal/driver/gomobile/file_ios.go b/internal/driver/gomobile/file_ios.go index 21cc47697b..b08c0ca46d 100644 --- a/internal/driver/gomobile/file_ios.go +++ b/internal/driver/gomobile/file_ios.go @@ -13,6 +13,7 @@ const void* iosReadFromURL(void* url, int* len); */ import "C" import ( + "errors" "io" "unsafe" @@ -77,6 +78,10 @@ func nativeFileOpen(f *fileOpen) (io.ReadCloser, error) { return fileStruct, nil } +func nativeFileSave(f *fileSave) (io.WriteCloser, error) { + return nil, errors.New("Currently unsupported.") +} + func registerRepository(d *mobileDriver) { repo := &mobileFileRepo{driver: d} repository.Register("file", repo) From d44bac4256b64ca282baadc87920e839ec2de4d1 Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Thu, 11 Mar 2021 16:35:39 -0600 Subject: [PATCH 22/62] mobile: add mime filter for save --- cmd/fyne/internal/mobile/dex.go | 305 +++++++++++++++---------------- dialog/file_mobile.go | 2 +- internal/driver/gomobile/file.go | 6 +- 3 files changed, 156 insertions(+), 157 deletions(-) diff --git a/cmd/fyne/internal/mobile/dex.go b/cmd/fyne/internal/mobile/dex.go index 6bca115d24..c8a8df6669 100644 --- a/cmd/fyne/internal/mobile/dex.go +++ b/cmd/fyne/internal/mobile/dex.go @@ -6,40 +6,40 @@ package mobile -var dexStr = `ZGV4CjAzNQCp5hmlGxA9dMqSjxkmhaLh8XHxc3H/tQdsJgAAcAAAAHhWNBIAAAAAAAAAAJ` + - `wlAAC+AAAAcAAAAC4AAABoAwAAOwAAACAEAAATAAAA5AYAAGUAAAB8BwAABgAAAKQKAAAI` + - `GwAAZAsAACoWAAAsFgAAMRYAADYWAAA+FgAAUhYAAGkWAAB5FgAAiRYAAI8WAACmFgAAqR` + - `YAAK4WAAC0FgAAuRYAAL8WAADCFgAAxhYAAMsWAADPFgAA1BYAANkWAAD3FgAAGBcAADMX` + - `AABNFwAAcBcAAJUXAACuFwAAwRcAAN0XAADyFwAACBgAACEYAAA9GAAAURgAAIYYAACmGA` + - `AA0hgAAOcYAAAOGQAAJRkAAEIZAABxGQAAjBkAALcZAADcGQAA/BkAABsaAAArGgAARRoA` + - `AFwaAAB7GgAAjxoAAKUaAAC5GgAAzRoAAOQaAAAJGwAALhsAAFMbAAB6GwAAnxsAAMIbAA` + - `DYGwAA4xsAAOwbAAAGHAAAERwAABQcAAAYHAAAHRwAACQcAAAqHAAALhwAADMcAAA6HAAA` + - `RhwAAEscAABOHAAAUhwAAFccAABsHAAAcBwAAHwcAACIHAAAlBwAAKAcAACsHAAAuBwAAM` + - `UcAADSHAAA4hwAAOwcAAAHHQAAHx0AADEdAABHHQAAbh0AAJMdAAC9HQAA3x0AAAAeAAAZ` + - `HgAALB4AADoeAABEHgAAUx4AAGMeAABzHgAAgx4AAJMeAACWHgAAnh4AAMEeAADVHgAA4x` + - `4AAOgeAAD5HgAACh8AABcfAAAlHwAALh8AADwfAABHHwAAUh8AAGUfAAByHwAAhx8AAJAf` + - `AACbHwAArR8AAMkfAADjHwAA/h8AABcgAAAiIAAALCAAADcgAABHIAAAZSAAAHcgAAB/IA` + - `AAjSAAAKYgAAC0IAAAwyAAANMgAADiIAAA6CAAAPAgAAD2IAAAAyEAABchAABAIQAASyEA` + - `AFUhAABbIQAAZSEAAHchAACBIQAAkSEAAKAhAACqIQAAuCEAAL0hAADMIQAA2yEAAOkhAA` + - `D6IQAAAyIAAAwiAAAbIgAAJyIAADUiAABDIgAAUSIAAGAiAABnIgAAfyIAAIwiAACUIgAA` + - `nCIAAKYiAACrIgAAzyIAAN0iAADvIgAA9iIAAP0iAAAKAAAAFQAAABYAAAAXAAAAGAAAAB` + +var dexStr = `ZGV4CjAzNQDm5OAXFST/M4oWO8uK7rX3ZYH75wE14SUoJgAAcAAAAHhWNBIAAAAAAAAAAF` + + `glAAC+AAAAcAAAAC4AAABoAwAAOwAAACAEAAATAAAA5AYAAGUAAAB8BwAABgAAAKQKAADE` + + `GgAAZAsAAO4VAADwFQAA9RUAAPoVAAACFgAAFhYAAC0WAAA9FgAATRYAAFMWAABqFgAAbR` + + `YAAHIWAAB4FgAAfRYAAIMWAACGFgAAihYAAI8WAACTFgAAmBYAAJ0WAAC7FgAA3BYAAPcW` + + `AAARFwAANBcAAFkXAAByFwAAhRcAAKEXAAC2FwAAzBcAAOUXAAABGAAAFRgAAEoYAABqGA` + + `AAlhgAAKsYAADSGAAA6RgAAAYZAAA1GQAAUBkAAHsZAACgGQAAwBkAAN8ZAADvGQAACRoA` + + `ACAaAAA/GgAAUxoAAGkaAAB9GgAAkRoAAKgaAADNGgAA8hoAABcbAAA+GwAAYxsAAIYbAA` + + `CcGwAApxsAALAbAADKGwAA1RsAANgbAADcGwAA4RsAAOgbAADuGwAA8hsAAPcbAAD+GwAA` + + `ChwAAA8cAAASHAAAFhwAABscAAAwHAAANBwAAEAcAABMHAAAWBwAAGQcAABwHAAAfBwAAI` + + `kcAACWHAAAphwAALAcAADLHAAA4xwAAPUcAAALHQAAMh0AAFcdAACBHQAAox0AAMQdAADd` + + `HQAA8B0AAP4dAAAIHgAAFx4AACceAAA3HgAARx4AAFceAABaHgAAYh4AAIUeAACZHgAApx` + + `4AAKweAAC9HgAAzh4AANseAADpHgAA8h4AAAAfAAALHwAAFh8AACkfAAA2HwAASx8AAFQf` + + `AABfHwAAcR8AAI0fAACnHwAAwh8AANsfAADmHwAA8B8AAPsfAAALIAAAKSAAADsgAABDIA` + + `AAUSAAAGogAAB4IAAAhyAAAJcgAACmIAAArCAAALQgAAC6IAAAxyAAANsgAAAEIQAADyEA` + + `ABkhAAAfIQAAKSEAADshAABFIQAAVSEAAGQhAABuIQAAfCEAAIEhAACQIQAAnyEAAK0hAA` + + `C+IQAAxyEAANAhAADfIQAA6yEAAPkhAAAHIgAAFSIAACQiAAArIgAAQyIAAFAiAABYIgAA` + + `YCIAAGoiAABvIgAAkyIAAKEiAACzIgAAuiIAAMEiAAAKAAAAFQAAABYAAAAXAAAAGAAAAB` + `kAAAAaAAAAGwAAABwAAAAdAAAAHgAAAB8AAAAgAAAAIQAAACIAAAAjAAAAJAAAACUAAAAm` + `AAAAJwAAACgAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAvAAAAMAAAADEAAAAyAAAAMw` + `AAADQAAAA1AAAANgAAADcAAAA4AAAAOQAAADoAAAA7AAAAPAAAAD0AAAA+AAAARAAAAE4A` + - `AABRAAAACgAAAAAAAAAAAAAACwAAAAAAAAAgFQAADAAAAAAAAAAoFQAADQAAAAAAAAA0FQ` + - `AADgAAAAAAAAA8FQAADwAAAAIAAAAAAAAADwAAAAQAAAAAAAAAEAAAAAQAAABIFQAAFAAA` + - `AAQAAABQFQAAEgAAAAQAAABYFQAAFAAAAAQAAABgFQAAEwAAAAUAAABoFQAADwAAAAYAAA` + - `AAAAAADwAAAAgAAAAAAAAADwAAAAsAAAAAAAAAEAAAABAAAABIFQAADwAAABIAAAAAAAAA` + - `EAAAABIAAABIFQAADwAAABQAAAAAAAAADwAAABUAAAAAAAAAEgAAABcAAABwFQAAFAAAAB` + - `cAAAB4FQAADwAAABwAAAAAAAAAEQAAAB0AAAAgFQAAEgAAACAAAABYFQAADwAAACIAAAAA` + - `AAAAEgAAACIAAABYFQAAEgAAACIAAABwFQAAFAAAACIAAACAFQAADwAAACoAAAAAAAAARA` + - `AAACsAAAAAAAAARQAAACsAAABIFQAARgAAACsAAAAgFQAARwAAACsAAACIFQAASAAAACsA` + - `AACUFQAASQAAACsAAACgFQAASgAAACsAAACoFQAASQAAACsAAACwFQAASQAAACsAAAC4FQ` + - `AASQAAACsAAADAFQAASQAAACsAAAAYFQAASQAAACsAAAAQFQAATAAAACsAAADIFQAATQAA` + - `ACsAAADgFQAASQAAACsAAADoFQAASQAAACsAAADwFQAASwAAACsAAAD4FQAASQAAACsAAA` + - `AIFQAASQAAACsAAABYFQAASQAAACsAAAAEFgAASQAAACsAAABwFQAASgAAACsAAAAMFgAA` + - `TQAAACsAAACAFQAATgAAACwAAAAAAAAAUAAAACwAAAAUFgAAUAAAACwAAAAcFgAATwAAAC` + - `wAAADwFQAATwAAACwAAAAkFgAAEgAAAC0AAABYFQAABQAKAJsAAAAHAAAAlAAAAAcAAAC3` + + `AABRAAAACgAAAAAAAAAAAAAACwAAAAAAAADkFAAADAAAAAAAAADsFAAADQAAAAAAAAD4FA` + + `AADgAAAAAAAAAAFQAADwAAAAIAAAAAAAAADwAAAAQAAAAAAAAAEAAAAAQAAAAMFQAAFAAA` + + `AAQAAAAUFQAAEgAAAAQAAAAcFQAAFAAAAAQAAAAkFQAAEwAAAAUAAAAsFQAADwAAAAYAAA` + + `AAAAAADwAAAAgAAAAAAAAADwAAAAsAAAAAAAAAEAAAABAAAAAMFQAADwAAABIAAAAAAAAA` + + `EAAAABIAAAAMFQAADwAAABQAAAAAAAAADwAAABUAAAAAAAAAEgAAABcAAAA0FQAAFAAAAB` + + `cAAAA8FQAADwAAABwAAAAAAAAAEQAAAB0AAADkFAAAEgAAACAAAAAcFQAADwAAACIAAAAA` + + `AAAAEgAAACIAAAAcFQAAEgAAACIAAAA0FQAAFAAAACIAAABEFQAADwAAACoAAAAAAAAARA` + + `AAACsAAAAAAAAARQAAACsAAAAMFQAARgAAACsAAADkFAAARwAAACsAAABMFQAASAAAACsA` + + `AABYFQAASQAAACsAAABkFQAASgAAACsAAABsFQAASQAAACsAAAB0FQAASQAAACsAAAB8FQ` + + `AASQAAACsAAACEFQAASQAAACsAAADcFAAASQAAACsAAADUFAAATAAAACsAAACMFQAATQAA` + + `ACsAAACkFQAASQAAACsAAACsFQAASQAAACsAAAC0FQAASwAAACsAAAC8FQAASQAAACsAAA` + + `DMFAAASQAAACsAAAAcFQAASQAAACsAAADIFQAASQAAACsAAAA0FQAASgAAACsAAADQFQAA` + + `TQAAACsAAABEFQAATgAAACwAAAAAAAAAUAAAACwAAADYFQAAUAAAACwAAADgFQAATwAAAC` + + `wAAAC0FQAATwAAACwAAADoFQAAEgAAAC0AAAAcFQAABQAKAJsAAAAHAAAAlAAAAAcAAAC3` + `AAAACQAAAEEAAAAlACoAtAAAACUAAAC6AAAAJgAqALQAAAAnACoAtAAAACgAKQC1AAAAKQ` + `AqALQAAAAqAAAABAAAACoAAAAFAAAAKgAAAAYAAAAqAAAABwAAACoAAAA/AAAAKgAAAEIA` + `AAAqACoAjAAAACoAFwCaAAAAKgAiAJ0AAAABAB4AAwAAAAEAJgCfAAAABAAwAAMAAAAEAA` + @@ -58,140 +58,139 @@ var dexStr = `ZGV4CjAzNQCp5hmlGxA9dMqSjxkmhaLh8XHxc3H/tQdsJgAAcAAAAHhWNBIAAAAAAA `oADAB9AAAAKgACAIAAAAAqABgAggAAACoAGQCHAAAAKgASAIkAAAAqAB4AjgAAACoAIQCR` + `AAAAKgAeAJIAAAAqADAAkwAAACoAHgCWAAAAKgAiAJ4AAAAqACYAnwAAACoALwClAAAAKg` + `AeAKwAAAAqADAArQAAACoAMACuAAAAKgAfAK8AAAAqACQAsgAAACoAHgC5AAAAJQAAAAAA` + - `AAAgAAAACBUAAAkAAACwFAAAsyQAAAAAAAAmAAAAAAAAACAAAAAIFQAACQAAAMgUAADHJA` + - `AAAAAAACcAAAAAAAAAIAAAABAVAAAJAAAA2BQAANgkAAAAAAAAKAAAAAAAAAAgAAAAGBUA` + - `AAkAAADoFAAA6SQAAAAAAAApAAAAAAAAACAAAAAIFQAACQAAAPgUAAACJQAAAAAAACoAAA` + - `ABAAAAAQAAAAAAAAAJAAAAAAAAABMlAACkJAAAAgAAAG8kAAB2JAAAAQAAAH8kAAACAAAA` + - `iCQAAHYkAAACAAAAjyQAAHYkAAACAAAAliQAAHYkAAACAAAAnSQAAHYkAAADAAMAAQAAAA` + - `AjAAAIAAAAWwEEAFkCBQBwEDAAAAAOAAYAAQADAAAAByMAAHgAAAAVAQBAEmISBBQAkAAI` + + `AAAgAAAAzBQAAAkAAAB0FAAAcSQAAAAAAAAmAAAAAAAAACAAAADMFAAACQAAAIwUAACFJA` + + `AAAAAAACcAAAAAAAAAIAAAANQUAAAJAAAAnBQAAJYkAAAAAAAAKAAAAAAAAAAgAAAA3BQA` + + `AAkAAACsFAAApyQAAAAAAAApAAAAAAAAACAAAADMFAAACQAAALwUAADAJAAAAAAAACoAAA` + + `ABAAAAAQAAAAAAAAAJAAAAAAAAANEkAABiJAAAAgAAAC0kAAA0JAAAAQAAAD0kAAACAAAA` + + `RiQAADQkAAACAAAATSQAADQkAAACAAAAVCQAADQkAAACAAAAWyQAADQkAAADAAMAAQAAAM` + + `QiAAAIAAAAWwEEAFkCBQBwEDAAAAAOAAYAAQADAAAAyyIAAHgAAAAVAQBAEmISBBQAkAAI` + `AFJTBQArA2UAAAAaAggAGgO4AHEgEAAyAFRSBABxEEMAAgAMAm4gJgASAFRRBABxEEMAAQ` + `AMAW4gJwABAFRQBAAaAQAAcSBGABAAVFAEAHEQQwAAAAwAGgEAAG4gKQAQAFRQBABxEEMA` + `AAAMAG4gKgBAAFRQBABxEEMAAAAMAG4QJAAAAFRQBABxEEMAAAAMAG4QJQAAAFRQBAAaAZ` + `AAbiBUABAADAAfABYAVFEEAHEQQwABAAwBbjAhABAEDgABISisFACSAAgAASEopwAAAAED` + - `AAAAAAAKAAAAXQAAAF8AAAACAAIAAQAAACIjAAAGAAAAWwEGAHAQMAAAAA4AAwABAAIAAA` + - `AoIwAADAAAAFQgBgBxEEMAAAAMABMBCABuICoAEAAOAAIAAgABAAAALiMAAAYAAABbAQcA` + - `cBAwAAAADgALAAoAAQAAADUjAAAGAAAAVBAHAG4QZAAAAA4AAgACAAEAAABFIwAABgAAAF` + - `sBCABwEDAAAAAOAAIAAgAAAAAATCMAAAEAAAAOAAAABQAFAAAAAABTIwAAAQAAAA4AAAAI` + - `AAUAAwAAAF0jAABQAAAAchAtAAQACgBUMQgAVBEJAHEQRQABAAwBbhAzAAEACgE3EC0AVD` + + `AAAAAAAKAAAAXQAAAF8AAAACAAIAAQAAAOYiAAAGAAAAWwEGAHAQMAAAAA4AAwABAAIAAA` + + `DsIgAADAAAAFQgBgBxEEMAAAAMABMBCABuICoAEAAOAAIAAgABAAAA8iIAAAYAAABbAQcA` + + `cBAwAAAADgALAAoAAQAAAPkiAAAGAAAAVBAHAG4QZAAAAA4AAgACAAEAAAAJIwAABgAAAF` + + `sBCABwEDAAAAAOAAIAAgAAAAAAECMAAAEAAAAOAAAABQAFAAAAAAAXIwAAAQAAAA4AAAAI` + + `AAUAAwAAACEjAABQAAAAchAtAAQACgBUMQgAVBEJAHEQRQABAAwBbhAzAAEACgE3EC0AVD` + `AIAFQACQBUMQgAVBEJAHEQRQABAAwBbhAzAAEACgFyEC0ABAAKAnIwLgAUAgwBchAvAAEA` + `DAFxIEgAEABUMAgAVAAJAHIQLwAEAAwBcSBGABAADgByEC0ABAAKAFQxCABUEQkAcRBFAA` + - `EADAFuEDMAAQAKATUQ5P8o4gIAAgABAAAAcSMAAAYAAABbAQkAcBAwAAAADgAFAAEAAwAA` + - `AHgjAABOAAAAEuNUQAkAIgEXAHEARwAAAAwCcCAiACEAcSBEABAAVEAJAHEQQwAAAAwAEw` + + `EADAFuEDMAAQAKATUQ5P8o4gIAAgABAAAANSMAAAYAAABbAQkAcBAwAAAADgAFAAEAAwAA` + + `ADwjAABOAAAAEuNUQAkAIgEXAHEARwAAAAwCcCAiACEAcSBEABAAVEAJAHEQQwAAAAwAEw` + `EIAG4gKgAQAFRACQBxEEMAAAAMABQBkAAIAG4gJwAQACIAGABwMCsAMANUQQkAcRBDAAEA` + `DAFuICgAAQBUQQkAVEIJAHEQQwACAAwCbjBJACEAVEAJAHEQQwAAAAwAIgEoAHAgPABBAG` + - `4gIwAQAA4AAgABAAEAAACHIwAACgAAAHAQAAABABoAAABbEBIAaQEQAA4AAgABAAAAAACP` + - `IwAAAwAAAFQQEQARAAAAAgACAAAAAACVIwAAAwAAAFsBEQARAQAAAgABAAAAAACcIwAAAw` + - `AAAFQQEgARAAAAAgACAAAAAACiIwAAAwAAAFsBEgARAQAAAQAAAAAAAACpIwAAAwAAAGIA` + - `EAARAAAAAgACAAIAAACuIwAABAAAAHAgWgAQAA4ABwADAAMAAQC1IwAAGQAAABLwcRATAA` + + `4gIwAQAA4AAgABAAEAAABLIwAACgAAAHAQAAABABoAAABbEBIAaQEQAA4AAgABAAAAAABT` + + `IwAAAwAAAFQQEQARAAAAAgACAAAAAABZIwAAAwAAAFsBEQARAQAAAgABAAAAAABgIwAAAw` + + `AAAFQQEgARAAAAAgACAAAAAABmIwAAAwAAAFsBEgARAQAAAQAAAAAAAABtIwAAAwAAAGIA` + + `EAARAAAAAgACAAIAAAByIwAABAAAAHAgWgAQAA4ABwADAAMAAQB5IwAAGQAAABLwcRATAA` + `QADAFuMBIAUQYKATkBAwAPAAEQKP4NARoCCAAaA3EAcTARADIBKPUNASjzAAABAAAABwAB` + - `AAECDxceDgAAAQAAAAEAAADGIwAABgAAAGIAEABuEEoAAAAOAAQAAQADAAEAzCMAADMAAA` + + `AAECDxceDgAAAQAAAAEAAACKIwAABgAAAGIAEABuEEoAAAAOAAQAAQADAAEAkCMAADMAAA` + `BuEFIAAwAMAG4QUQADAAwBbhAGAAEADAETAoAAbjAKABACDABUAQAAOQEKABoACAAaAZkA` + `cSAQABAADgBUAAAAGgFgAG4gDwAQAAwAcRA1AAAAKPQNABoBCAAaApgAcTARACEAKOsAAA` + - `AAAAApAAEAAQEeKgIAAQACAAAA3SMAAAkAAAAiACkAcCBAABAAbiBeAAEADgAAAAIAAQAC` + - `AAAA5iMAAAYAAABiABAAbiBLABAADgACAAEAAgAAAO4jAAAGAAAAYgAQAG4gTAAQAA4AAg` + - `ABAAIAAAD2IwAABgAAAGIAEABuIE0AEAAOAAQAAQADAAAA/SMAACQAAAAaAJAAbiBUAAMA` + + `AAAAApAAEAAQEeKgIAAQACAAAAoSMAAAkAAAAiACkAcCBAABAAbiBeAAEADgAAAAIAAQAC` + + `AAAAqiMAAAYAAABiABAAbiBLABAADgACAAEAAgAAALIjAAAGAAAAYgAQAG4gTAAQAA4AAg` + + `ABAAIAAAC6IwAABgAAAGIAEABuIE0AEAAOAAQAAQADAAAAwSMAACQAAAAaAJAAbiBUAAMA` + `DAAfABYAFAECAAIBbiBPABMADAFuEBYAAQAMAW4QGQABAAwBEgJuMCAAEAIiACYAcCA4AD` + - `AAbiBeAAMADgAGAAIAAwAAAAYkAABXAAAAEhMiAAQAGgFiAHAgAgAQABoBZgBuIDIAUQAK` + + `AAbiBeAAMADgAGAAIAAwAAAMojAABXAAAAEhMiAAQAGgFiAHAgAgAQABoBZgBuIDIAUQAK` + `ATgBHABgAQMAEwIVADQhFgAiAAQAGgFjAHAgAgAQAG4gBAAwABoBQABxIAUAEAAMAG4wYw` + `AEAw4AGgG9AG4gMQAVAAoBOAEeAGABAwATAhMANCEYABoBAgBuIAkAEAAaAWUAGgJSAG4g` + - `NAAlAAwCbjAIABACGgFkAG4gAwAQACjTbiAJAFAAGgFkAG4gAwAQACjKAAAFAAIAAwAAAB` + - `okAABYAAAAIgAEABoBYQBwIAIAEAAaAWYAbiAyAEEACgE4AR4AYAEDABMCFQA0IRgAIgAE` + - `ABoBYwBwIAIAEAASEW4gBAAQABoBQwBxIAUAEAAMABIhbjBjAAMBDgAaAb0AbiAxABQACg` + - `E4AR4AYAEDABMCEwA0IRgAGgECAG4gCQAQABoBZQAaAlIAbiA0ACQADAJuMAgAEAIaAWQA` + - `biADABAAKNJuIAkAQAAaAWQAbiADABAAKMkDAAIAAwAAAC4kAAAJAAAAIgAlAHAwNgAQAm` + - `4gXgABAA4AAAACAAEAAQAAADckAAAJAAAAbhBQAAEADABuECwAAAAMABEAAAAFAAQAAgAA` + - `ADwkAAAcAAAAEhAyAgYAEiAyAgMADgAS8DIDCAAaAAAAcCBOAAEAKPduEAcABAAMAG4QDg` + - `AAAAwAcCBOAAEAKOsEAAIAAgAAAE4kAAAdAAAAcBBbAAIAbyABADIAcBBfAAIAFAACAAIB` + - `biBPAAIADABuEBYAAAAMACIBJwBwIDoAIQBuIBQAEAAOAAAABwABAAUAAQBaJAAAYAAAAG` + - `4QVgAGAAwAbhAbAAAADABuEBcAAAAMADkAAwAOAG4QHwAAAAoBbhAcAAAACgJuEB0AAAAK` + - `A24QHgAAAAoAcFBYABYyKOwNACIABwBwEAsAAABuEFYABgAMAW4QGwABAAwBbiAaAAEAFA` + - `ECAAIBbiBPABYADAFuEBYAAQAMAVICAgBuEBUAAQAKA24QDAAAAAoEsUNSBAIAsUNSBAEA` + - `bhAYAAEACgFuEA0AAAAKBbFRUgABAJEAAQBwUFgAJkMorwAAAAAiAAEAAQEfI2QLAAAAAA` + - `AAAQAAAAAAAAA2AAAAcAsAAHgLAAAAAAAAAAAAAAAAAACECwAAAAAAAAAAAAAAAAAAkAsA` + - `AAAAAAAAAAAAAAAAAJwLAAAAAAAAAAAAAAAAAAABAAAAIQAAAAEAAAARAAAAAQAAAA0AAA` + - `ACAAAAAAAAAAMAAAAAAAAAAAAAAAIAAAAiACIAAwAAACIAIgAkAAAAAQAAAAAAAAACAAAA` + - `BAAdAAEAAAAiAAAAAgAAACIALQACAAAAAgAAAAEAAAAqAAAAAgAAACoAFwACAAAAKgAiAA` + - `QAAAAAAAAAAAAAAAMAAAAAAAAABAAAAAEAAAADAAAAAgAAAAQAAAABAAAABwAAAAEAAAAK` + - `AAAAAQAAAAwAAAAJAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAACAAAAEgATAAEAAAATAAAAAQ` + - `AAAB0AAAAEAAAAHQAAAAAAAAABAAAAKQAAAAIAAAAqAAAAAgAAAAsAAAACAAAAEgAAAAEA` + - `AAAgAAAAAygpVgADKi8qAAY8aW5pdD4AEkRFRkFVTFRfSU5QVVRfVFlQRQAVREVGQVVMVF` + - `9LRVlCT0FSRF9DT0RFAA5GSUxFX09QRU5fQ09ERQAORklMRV9TQVZFX0NPREUABEZ5bmUA` + - `FUdvTmF0aXZlQWN0aXZpdHkuamF2YQABSQADSUlJAARJSUlJAANJTEwABElMTEwAAUwAAk` + - `xJAANMSUkAAkxMAANMTEkAA0xMTAAcTGFuZHJvaWQvYXBwL05hdGl2ZUFjdGl2aXR5OwAf` + - `TGFuZHJvaWQvY29udGVudC9Db21wb25lbnROYW1lOwAZTGFuZHJvaWQvY29udGVudC9Db2` + - `50ZXh0OwAYTGFuZHJvaWQvY29udGVudC9JbnRlbnQ7ACFMYW5kcm9pZC9jb250ZW50L3Bt` + - `L0FjdGl2aXR5SW5mbzsAI0xhbmRyb2lkL2NvbnRlbnQvcG0vUGFja2FnZU1hbmFnZXI7AB` + - `dMYW5kcm9pZC9ncmFwaGljcy9SZWN0OwARTGFuZHJvaWQvbmV0L1VyaTsAGkxhbmRyb2lk` + - `L29zL0J1aWxkJFZFUlNJT047ABNMYW5kcm9pZC9vcy9CdW5kbGU7ABRMYW5kcm9pZC9vcy` + - `9JQmluZGVyOwAXTGFuZHJvaWQvdGV4dC9FZGl0YWJsZTsAGkxhbmRyb2lkL3RleHQvVGV4` + - `dFdhdGNoZXI7ABJMYW5kcm9pZC91dGlsL0xvZzsAM0xhbmRyb2lkL3ZpZXcvS2V5Q2hhcm` + - `FjdGVyTWFwJFVuYXZhaWxhYmxlRXhjZXB0aW9uOwAeTGFuZHJvaWQvdmlldy9LZXlDaGFy` + - `YWN0ZXJNYXA7ACpMYW5kcm9pZC92aWV3L1ZpZXckT25MYXlvdXRDaGFuZ2VMaXN0ZW5lcj` + - `sAE0xhbmRyb2lkL3ZpZXcvVmlldzsAJUxhbmRyb2lkL3ZpZXcvVmlld0dyb3VwJExheW91` + - `dFBhcmFtczsAFUxhbmRyb2lkL3ZpZXcvV2luZG93OwAbTGFuZHJvaWQvdmlldy9XaW5kb3` + - `dJbnNldHM7AC1MYW5kcm9pZC92aWV3L2lucHV0bWV0aG9kL0lucHV0TWV0aG9kTWFuYWdl` + - `cjsAGUxhbmRyb2lkL3dpZGdldC9FZGl0VGV4dDsAKUxhbmRyb2lkL3dpZGdldC9GcmFtZU` + - `xheW91dCRMYXlvdXRQYXJhbXM7ACNMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nTWV0` + - `aG9kOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7AB1MZGFsdmlrL2Fubm90YX` + - `Rpb24vU2lnbmF0dXJlOwAOTGphdmEvaW8vRmlsZTsAGExqYXZhL2xhbmcvQ2hhclNlcXVl` + - `bmNlOwAVTGphdmEvbGFuZy9FeGNlcHRpb247AB1MamF2YS9sYW5nL05vU3VjaE1ldGhvZE` + - `Vycm9yOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGph` + - `dmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3` + - `dhYmxlOwAjTG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkMTsAI0xvcmcvZ29s` + - `YW5nL2FwcC9Hb05hdGl2ZUFjdGl2aXR5JDI7ACNMb3JnL2dvbGFuZy9hcHAvR29OYXRpdm` + - `VBY3Rpdml0eSQzOwAlTG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkNCQxOwAj` + - `TG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkNDsAIUxvcmcvZ29sYW5nL2FwcC` + - `9Hb05hdGl2ZUFjdGl2aXR5OwAUTlVNQkVSX0tFWUJPQVJEX0NPREUACU9wZW4gRmlsZQAH` + - `U0RLX0lOVAAYU0lOR0xFTElORV9LRVlCT0FSRF9DT0RFAAlTYXZlIEZpbGUAAVYAAlZJAA` + - `NWSUkABVZJSUlJAARWSUlMAAJWTAADVkxJAAVWTElJSQAKVkxJSUlJSUlJSQADVkxMAAFa` + - `AAJaTAADWkxJABNbTGphdmEvbGFuZy9TdHJpbmc7AAJcfAAKYWNjZXNzJDAwMAAKYWNjZX` + - `NzJDAwMgAKYWNjZXNzJDEwMAAKYWNjZXNzJDEwMgAKYWNjZXNzJDIwMAAKYWNjZXNzJDMw` + - `MAALYWNjZXNzRmxhZ3MAC2FkZENhdGVnb3J5AA5hZGRDb250ZW50VmlldwAIYWRkRmxhZ3` + - `MAGWFkZE9uTGF5b3V0Q2hhbmdlTGlzdGVuZXIAFmFkZFRleHRDaGFuZ2VkTGlzdGVuZXIA` + - `EGFmdGVyVGV4dENoYW5nZWQAFGFuZHJvaWQuYXBwLmxpYl9uYW1lACVhbmRyb2lkLmludG` + - `VudC5hY3Rpb24uQ1JFQVRFX0RPQ1VNRU5UACNhbmRyb2lkLmludGVudC5hY3Rpb24uT1BF` + - `Tl9ET0NVTUVOVAAoYW5kcm9pZC5pbnRlbnQuYWN0aW9uLk9QRU5fRE9DVU1FTlRfVFJFRQ` + - `AgYW5kcm9pZC5pbnRlbnQuY2F0ZWdvcnkuT1BFTkFCTEUAH2FuZHJvaWQuaW50ZW50LmV4` + - `dHJhLk1JTUVfVFlQRVMAF2FwcGxpY2F0aW9uL3gtZGlyZWN0b3J5ABFiZWZvcmVUZXh0Q2` + - `hhbmdlZAAMYnJpbmdUb0Zyb250AAhjb250YWlucwANY3JlYXRlQ2hvb3NlcgAOZG9IaWRl` + - `S2V5Ym9hcmQADmRvU2hvd0ZpbGVPcGVuAA5kb1Nob3dGaWxlU2F2ZQAOZG9TaG93S2V5Ym` + - `9hcmQAAWUABmVxdWFscwAhZXhjZXB0aW9uIHJlYWRpbmcgS2V5Q2hhcmFjdGVyTWFwABJm` + - `aWxlUGlja2VyUmV0dXJuZWQADGZpbmRWaWV3QnlJZAADZ2V0AA9nZXRBYnNvbHV0ZVBhdG` + - `gAD2dldEFjdGl2aXR5SW5mbwALZ2V0Q2FjaGVEaXIADGdldENvbXBvbmVudAAHZ2V0RGF0` + - `YQAMZ2V0RGVjb3JWaWV3AAlnZXRIZWlnaHQACWdldEludGVudAARZ2V0UGFja2FnZU1hbm` + - `FnZXIAC2dldFJvb3RWaWV3ABNnZXRSb290V2luZG93SW5zZXRzAAdnZXRSdW5lAAlnZXRT` + - `dHJpbmcAEGdldFN5c3RlbVNlcnZpY2UAGmdldFN5c3RlbVdpbmRvd0luc2V0Qm90dG9tAB` + - `hnZXRTeXN0ZW1XaW5kb3dJbnNldExlZnQAGWdldFN5c3RlbVdpbmRvd0luc2V0UmlnaHQA` + - `F2dldFN5c3RlbVdpbmRvd0luc2V0VG9wAAlnZXRUbXBkaXIACGdldFdpZHRoAAlnZXRXaW` + - `5kb3cADmdldFdpbmRvd1Rva2VuABxnZXRXaW5kb3dWaXNpYmxlRGlzcGxheUZyYW1lABBn` + - `b05hdGl2ZUFjdGl2aXR5AAZoZWlnaHQADGhpZGVLZXlib2FyZAAXaGlkZVNvZnRJbnB1dE` + - `Zyb21XaW5kb3cADGlucHV0X21ldGhvZAANaW5zZXRzQ2hhbmdlZAAOa2V5Ym9hcmREZWxl` + - `dGUADWtleWJvYXJkVHlwZWQABGxlZnQABmxlbmd0aAAEbG9hZAALbG9hZExpYnJhcnkAEm` + - `xvYWRMaWJyYXJ5IGZhaWxlZAAnbG9hZExpYnJhcnk6IG5vIG1hbmlmZXN0IG1ldGFkYXRh` + - `IGZvdW5kAAltVGV4dEVkaXQACG1ldGFEYXRhAARuYW1lAAhvbGRTdGF0ZQAQb25BY3Rpdm` + - `l0eVJlc3VsdAAIb25DcmVhdGUADm9uTGF5b3V0Q2hhbmdlAA1vblRleHRDaGFuZ2VkAAhw` + - `dXRFeHRyYQAMcmVxdWVzdEZvY3VzAANydW4ADXJ1bk9uVWlUaHJlYWQADXNldEltZU9wdG` + - `lvbnMADHNldElucHV0VHlwZQAPc2V0TGF5b3V0UGFyYW1zAAdzZXRUZXh0AAdzZXRUeXBl` + - `AA1zZXRWaXNpYmlsaXR5AApzZXR1cEVudHJ5AAxzaG93RmlsZU9wZW4ADHNob3dGaWxlU2` + - `F2ZQAMc2hvd0tleWJvYXJkAA1zaG93U29mdElucHV0AAVzcGxpdAAWc3RhcnRBY3Rpdml0` + - `eUZvclJlc3VsdAALc3ViU2VxdWVuY2UABnRoaXMkMAAGdGhpcyQxAAh0b1N0cmluZwADdG` + - `9wACJ1bmtub3duIGtleWJvYXJkIHR5cGUsIHVzZSBkZWZhdWx0AAx1cGRhdGVMYXlvdXQA` + - `EHZhbCRrZXlib2FyZFR5cGUABXZhbHVlAAV3aWR0aAABfABPAgAABw4AUgAHdxACDll5lp` + - `d4tJaWl6WWAm0sIEsCdB0AfAEABw4AfwAHDrQA2AEBAAcOANsBCQAAAAAAAAAAAAcOWgDt` + - `AQEABw4A/wEBAAcOAPsBBAAAAAAHDgDwAQQAAAAABw4BEg8BHxO0AnsdAOEBAQAHDgDkAQ` + - `AHHeG0xFuWtQIU4AAvAAcOOE4tABoBAAcOABoCAAAHDgAaAQAHDgAaAgAABw4AGgAHDgAa` + - `AgAABw4ArQEDAAAABx2HNAJ7LCAegwB0AAcOWgDEAQAHDkujTEt/Ansdh0seAOEBAAcOAi` + - `KGAIUBAQAHDloAmQEBAAcOWgBLAQAHDloAeAAHDoe0iIwAiQEBAAcdeOF4RJYCdx3hWrRq` + - `PACdAQEABw544XhTpQJ3HeFatGo8AE8BAAcOAiKGADQABw4AiAIDAAAABw4CDGgCeR08bE` + - `sA0wEBAAcOPDw9tIwAOQAHDsMCDiwCdh2HhUweWrW0/9AAAhkBuwEaTQIaAlkEAJwBHgIb` + - `AbsBHAEXAQIZAbsBGkoCGQG7ARpdAhkBuwEaQQIZAbsBGl8GRJAACAQABAEEAgQCBAEAAg` + - `EBBJAgAZAgNoCABKgXNwHIFwABAQEGkCA4gIAEyBk5AeQZAAEBAQeQIDqAgASMGjsBqBoA` + - `AQEDCJAgPICABMQaPQHgGgEB9BoBAYgbAAEBAQmQIECAgAS4HEEB1BwHAhIIChoBGgEaAR` + - `oBGgEaAQoRAgECQoGABIAeAYggpB4BiCC8HgGIINQeAYgg7B4BiCCEHwGIIJwfBoICAAUI` + - `tB8ECIggAYICAAGCAgABggIAAQKkIAQCqCEBCMwhAQjoIQEIhCJKAKAiAQD4IgEAuCQBAP` + - `glCACcJgcEwCYBAYgnBwDUJwAAABEAAAAAAAAAAQAAAAAAAAABAAAAvgAAAHAAAAACAAAA` + - `LgAAAGgDAAADAAAAOwAAACAEAAAEAAAAEwAAAOQGAAAFAAAAZQAAAHwHAAAGAAAABgAAAK` + - `QKAAADEAAABgAAAGQLAAABIAAAIgAAAKgLAAAGIAAABQAAALAUAAABEAAAIAAAAAgVAAAC` + - `IAAAvgAAACoWAAADIAAAIgAAAAAjAAAEIAAABwAAAG8kAAAFIAAAAQAAAKQkAAAAIAAABg` + - `AAALMkAAAAEAAAAQAAAJwlAAA=` + + `NAAlAAwCbjAIABACGgFkAG4gAwAQACjTbiAJAFAAGgFkAG4gAwAQACjKAAAFAAIAAwAAAN` + + `4jAAA5AAAAIgAEABoBYQBwIAIAEAAaAb0AbiAxABQACgE4ASgAYAEDABMCEwA0ISIAGgEC` + + `AG4gCQAQABoBZQAaAlIAbiA0ACQADAJuMAgAEAIaAWQAbiADABAAGgFDAHEgBQAQAAwAEi` + + `FuMGMAAwEOAG4gCQBAACjtAAADAAIAAwAAAOwjAAAJAAAAIgAlAHAwNgAQAm4gXgABAA4A` + + `AAACAAEAAQAAAPUjAAAJAAAAbhBQAAEADABuECwAAAAMABEAAAAFAAQAAgAAAPojAAAcAA` + + `AAEhAyAgYAEiAyAgMADgAS8DIDCAAaAAAAcCBOAAEAKPduEAcABAAMAG4QDgAAAAwAcCBO` + + `AAEAKOsEAAIAAgAAAAwkAAAdAAAAcBBbAAIAbyABADIAcBBfAAIAFAACAAIBbiBPAAIADA` + + `BuEBYAAAAMACIBJwBwIDoAIQBuIBQAEAAOAAAABwABAAUAAQAYJAAAYAAAAG4QVgAGAAwA` + + `bhAbAAAADABuEBcAAAAMADkAAwAOAG4QHwAAAAoBbhAcAAAACgJuEB0AAAAKA24QHgAAAA` + + `oAcFBYABYyKOwNACIABwBwEAsAAABuEFYABgAMAW4QGwABAAwBbiAaAAEAFAECAAIBbiBP` + + `ABYADAFuEBYAAQAMAVICAgBuEBUAAQAKA24QDAAAAAoEsUNSBAIAsUNSBAEAbhAYAAEACg` + + `FuEA0AAAAKBbFRUgABAJEAAQBwUFgAJkMorwAAAAAiAAEAAQEfI2QLAAAAAAAAAQAAAAAA` + + `AAA2AAAAcAsAAHgLAAAAAAAAAAAAAAAAAACECwAAAAAAAAAAAAAAAAAAkAsAAAAAAAAAAA` + + `AAAAAAAJwLAAAAAAAAAAAAAAAAAAABAAAAIQAAAAEAAAARAAAAAQAAAA0AAAACAAAAAAAA` + + `AAMAAAAAAAAAAAAAAAIAAAAiACIAAwAAACIAIgAkAAAAAQAAAAAAAAACAAAABAAdAAEAAA` + + `AiAAAAAgAAACIALQACAAAAAgAAAAEAAAAqAAAAAgAAACoAFwACAAAAKgAiAAQAAAAAAAAA` + + `AAAAAAMAAAAAAAAABAAAAAEAAAADAAAAAgAAAAQAAAABAAAABwAAAAEAAAAKAAAAAQAAAA` + + `wAAAAJAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAACAAAAEgATAAEAAAATAAAAAQAAAB0AAAAE` + + `AAAAHQAAAAAAAAABAAAAKQAAAAIAAAAqAAAAAgAAAAsAAAACAAAAEgAAAAEAAAAgAAAAAy` + + `gpVgADKi8qAAY8aW5pdD4AEkRFRkFVTFRfSU5QVVRfVFlQRQAVREVGQVVMVF9LRVlCT0FS` + + `RF9DT0RFAA5GSUxFX09QRU5fQ09ERQAORklMRV9TQVZFX0NPREUABEZ5bmUAFUdvTmF0aX` + + `ZlQWN0aXZpdHkuamF2YQABSQADSUlJAARJSUlJAANJTEwABElMTEwAAUwAAkxJAANMSUkA` + + `AkxMAANMTEkAA0xMTAAcTGFuZHJvaWQvYXBwL05hdGl2ZUFjdGl2aXR5OwAfTGFuZHJvaW` + + `QvY29udGVudC9Db21wb25lbnROYW1lOwAZTGFuZHJvaWQvY29udGVudC9Db250ZXh0OwAY` + + `TGFuZHJvaWQvY29udGVudC9JbnRlbnQ7ACFMYW5kcm9pZC9jb250ZW50L3BtL0FjdGl2aX` + + `R5SW5mbzsAI0xhbmRyb2lkL2NvbnRlbnQvcG0vUGFja2FnZU1hbmFnZXI7ABdMYW5kcm9p` + + `ZC9ncmFwaGljcy9SZWN0OwARTGFuZHJvaWQvbmV0L1VyaTsAGkxhbmRyb2lkL29zL0J1aW` + + `xkJFZFUlNJT047ABNMYW5kcm9pZC9vcy9CdW5kbGU7ABRMYW5kcm9pZC9vcy9JQmluZGVy` + + `OwAXTGFuZHJvaWQvdGV4dC9FZGl0YWJsZTsAGkxhbmRyb2lkL3RleHQvVGV4dFdhdGNoZX` + + `I7ABJMYW5kcm9pZC91dGlsL0xvZzsAM0xhbmRyb2lkL3ZpZXcvS2V5Q2hhcmFjdGVyTWFw` + + `JFVuYXZhaWxhYmxlRXhjZXB0aW9uOwAeTGFuZHJvaWQvdmlldy9LZXlDaGFyYWN0ZXJNYX` + + `A7ACpMYW5kcm9pZC92aWV3L1ZpZXckT25MYXlvdXRDaGFuZ2VMaXN0ZW5lcjsAE0xhbmRy` + + `b2lkL3ZpZXcvVmlldzsAJUxhbmRyb2lkL3ZpZXcvVmlld0dyb3VwJExheW91dFBhcmFtcz` + + `sAFUxhbmRyb2lkL3ZpZXcvV2luZG93OwAbTGFuZHJvaWQvdmlldy9XaW5kb3dJbnNldHM7` + + `AC1MYW5kcm9pZC92aWV3L2lucHV0bWV0aG9kL0lucHV0TWV0aG9kTWFuYWdlcjsAGUxhbm` + + `Ryb2lkL3dpZGdldC9FZGl0VGV4dDsAKUxhbmRyb2lkL3dpZGdldC9GcmFtZUxheW91dCRM` + + `YXlvdXRQYXJhbXM7ACNMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nTWV0aG9kOwAeTG` + + `RhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7AB1MZGFsdmlrL2Fubm90YXRpb24vU2ln` + + `bmF0dXJlOwAOTGphdmEvaW8vRmlsZTsAGExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOwAVTG` + + `phdmEvbGFuZy9FeGNlcHRpb247AB1MamF2YS9sYW5nL05vU3VjaE1ldGhvZEVycm9yOwAS` + + `TGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy` + + `9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAj` + + `TG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkMTsAI0xvcmcvZ29sYW5nL2FwcC` + + `9Hb05hdGl2ZUFjdGl2aXR5JDI7ACNMb3JnL2dvbGFuZy9hcHAvR29OYXRpdmVBY3Rpdml0` + + `eSQzOwAlTG9yZy9nb2xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkNCQxOwAjTG9yZy9nb2` + + `xhbmcvYXBwL0dvTmF0aXZlQWN0aXZpdHkkNDsAIUxvcmcvZ29sYW5nL2FwcC9Hb05hdGl2` + + `ZUFjdGl2aXR5OwAUTlVNQkVSX0tFWUJPQVJEX0NPREUACU9wZW4gRmlsZQAHU0RLX0lOVA` + + `AYU0lOR0xFTElORV9LRVlCT0FSRF9DT0RFAAlTYXZlIEZpbGUAAVYAAlZJAANWSUkABVZJ` + + `SUlJAARWSUlMAAJWTAADVkxJAAVWTElJSQAKVkxJSUlJSUlJSQADVkxMAAFaAAJaTAADWk` + + `xJABNbTGphdmEvbGFuZy9TdHJpbmc7AAJcfAAKYWNjZXNzJDAwMAAKYWNjZXNzJDAwMgAK` + + `YWNjZXNzJDEwMAAKYWNjZXNzJDEwMgAKYWNjZXNzJDIwMAAKYWNjZXNzJDMwMAALYWNjZX` + + `NzRmxhZ3MAC2FkZENhdGVnb3J5AA5hZGRDb250ZW50VmlldwAIYWRkRmxhZ3MAGWFkZE9u` + + `TGF5b3V0Q2hhbmdlTGlzdGVuZXIAFmFkZFRleHRDaGFuZ2VkTGlzdGVuZXIAEGFmdGVyVG` + + `V4dENoYW5nZWQAFGFuZHJvaWQuYXBwLmxpYl9uYW1lACVhbmRyb2lkLmludGVudC5hY3Rp` + + `b24uQ1JFQVRFX0RPQ1VNRU5UACNhbmRyb2lkLmludGVudC5hY3Rpb24uT1BFTl9ET0NVTU` + + `VOVAAoYW5kcm9pZC5pbnRlbnQuYWN0aW9uLk9QRU5fRE9DVU1FTlRfVFJFRQAgYW5kcm9p` + + `ZC5pbnRlbnQuY2F0ZWdvcnkuT1BFTkFCTEUAH2FuZHJvaWQuaW50ZW50LmV4dHJhLk1JTU` + + `VfVFlQRVMAF2FwcGxpY2F0aW9uL3gtZGlyZWN0b3J5ABFiZWZvcmVUZXh0Q2hhbmdlZAAM` + + `YnJpbmdUb0Zyb250AAhjb250YWlucwANY3JlYXRlQ2hvb3NlcgAOZG9IaWRlS2V5Ym9hcm` + + `QADmRvU2hvd0ZpbGVPcGVuAA5kb1Nob3dGaWxlU2F2ZQAOZG9TaG93S2V5Ym9hcmQAAWUA` + + `BmVxdWFscwAhZXhjZXB0aW9uIHJlYWRpbmcgS2V5Q2hhcmFjdGVyTWFwABJmaWxlUGlja2` + + `VyUmV0dXJuZWQADGZpbmRWaWV3QnlJZAADZ2V0AA9nZXRBYnNvbHV0ZVBhdGgAD2dldEFj` + + `dGl2aXR5SW5mbwALZ2V0Q2FjaGVEaXIADGdldENvbXBvbmVudAAHZ2V0RGF0YQAMZ2V0RG` + + `Vjb3JWaWV3AAlnZXRIZWlnaHQACWdldEludGVudAARZ2V0UGFja2FnZU1hbmFnZXIAC2dl` + + `dFJvb3RWaWV3ABNnZXRSb290V2luZG93SW5zZXRzAAdnZXRSdW5lAAlnZXRTdHJpbmcAEG` + + `dldFN5c3RlbVNlcnZpY2UAGmdldFN5c3RlbVdpbmRvd0luc2V0Qm90dG9tABhnZXRTeXN0` + + `ZW1XaW5kb3dJbnNldExlZnQAGWdldFN5c3RlbVdpbmRvd0luc2V0UmlnaHQAF2dldFN5c3` + + `RlbVdpbmRvd0luc2V0VG9wAAlnZXRUbXBkaXIACGdldFdpZHRoAAlnZXRXaW5kb3cADmdl` + + `dFdpbmRvd1Rva2VuABxnZXRXaW5kb3dWaXNpYmxlRGlzcGxheUZyYW1lABBnb05hdGl2ZU` + + `FjdGl2aXR5AAZoZWlnaHQADGhpZGVLZXlib2FyZAAXaGlkZVNvZnRJbnB1dEZyb21XaW5k` + + `b3cADGlucHV0X21ldGhvZAANaW5zZXRzQ2hhbmdlZAAOa2V5Ym9hcmREZWxldGUADWtleW` + + `JvYXJkVHlwZWQABGxlZnQABmxlbmd0aAAEbG9hZAALbG9hZExpYnJhcnkAEmxvYWRMaWJy` + + `YXJ5IGZhaWxlZAAnbG9hZExpYnJhcnk6IG5vIG1hbmlmZXN0IG1ldGFkYXRhIGZvdW5kAA` + + `ltVGV4dEVkaXQACG1ldGFEYXRhAARuYW1lAAhvbGRTdGF0ZQAQb25BY3Rpdml0eVJlc3Vs` + + `dAAIb25DcmVhdGUADm9uTGF5b3V0Q2hhbmdlAA1vblRleHRDaGFuZ2VkAAhwdXRFeHRyYQ` + + `AMcmVxdWVzdEZvY3VzAANydW4ADXJ1bk9uVWlUaHJlYWQADXNldEltZU9wdGlvbnMADHNl` + + `dElucHV0VHlwZQAPc2V0TGF5b3V0UGFyYW1zAAdzZXRUZXh0AAdzZXRUeXBlAA1zZXRWaX` + + `NpYmlsaXR5AApzZXR1cEVudHJ5AAxzaG93RmlsZU9wZW4ADHNob3dGaWxlU2F2ZQAMc2hv` + + `d0tleWJvYXJkAA1zaG93U29mdElucHV0AAVzcGxpdAAWc3RhcnRBY3Rpdml0eUZvclJlc3` + + `VsdAALc3ViU2VxdWVuY2UABnRoaXMkMAAGdGhpcyQxAAh0b1N0cmluZwADdG9wACJ1bmtu` + + `b3duIGtleWJvYXJkIHR5cGUsIHVzZSBkZWZhdWx0AAx1cGRhdGVMYXlvdXQAEHZhbCRrZX` + + `lib2FyZFR5cGUABXZhbHVlAAV3aWR0aAABfABPAgAABw4AUgAHdxACDll5lpd4tJaWl6WW` + + `Am0sIEsCdB0AfAEABw4AfwAHDrQA1AEBAAcOANcBCQAAAAAAAAAAAAcOWgDpAQEABw4A+w` + + `EBAAcOAPcBBAAAAAAHDgDsAQQAAAAABw4BEg8BHxO0AnsdAN0BAQAHDgDgAQAHHeG0xFuW` + + `tQIU4AAvAAcOOE4tABoBAAcOABoCAAAHDgAaAQAHDgAaAgAABw4AGgAHDgAaAgAABw4AqQ` + + `EDAAAABx2HNAJ7LCAegwB0AAcOWgDAAQAHDkujTEt/Ansdh0seAN0BAAcOAiKGAIUBAQAH` + + `DloAmQEBAAcOWgBLAQAHDloAeAAHDoe0iIwAiQEBAAcdeOF4RJYCdx3hWrRqPACdAQEABw` + + `544Vq3WqUZAE8BAAcOAiKGADQABw4AhAIDAAAABw4CDGgCeR08bEsAzwEBAAcOPDw9tIwA` + + `OQAHDsMCDiwCdh2HhUweWrW0/9AAAhkBuwEaTQIaAlkEAJwBHgIbAbsBHAEXAQIZAbsBGk` + + `oCGQG7ARpdAhkBuwEaQQIZAbsBGl8GRJAACAQABAEEAgQCBAEAAgEBBJAgAZAgNoCABKgX` + + `NwHIFwABAQEGkCA4gIAEyBk5AeQZAAEBAQeQIDqAgASMGjsBqBoAAQEDCJAgPICABMQaPQ` + + `HgGgEB9BoBAYgbAAEBAQmQIECAgAS4HEEB1BwHAhIIChoBGgEaARoBGgEaAQoRAgECQoGA` + + `BIAeAYggpB4BiCC8HgGIINQeAYgg7B4BiCCEHwGIIJwfBoICAAUItB8ECIggAYICAAGCAg` + + `ABggIAAQKkIAQCqCEBCMwhAQjoIQEIhCJKAKAiAQD4IgEAuCQBALwlCADgJQcEhCYBAcwm` + + `BwCYJwARAAAAAAAAAAEAAAAAAAAAAQAAAL4AAABwAAAAAgAAAC4AAABoAwAAAwAAADsAAA` + + `AgBAAABAAAABMAAADkBgAABQAAAGUAAAB8BwAABgAAAAYAAACkCgAAAxAAAAYAAABkCwAA` + + `ASAAACIAAACoCwAABiAAAAUAAAB0FAAAARAAACAAAADMFAAAAiAAAL4AAADuFQAAAyAAAC` + + `IAAADEIgAABCAAAAcAAAAtJAAABSAAAAEAAABiJAAAACAAAAYAAABxJAAAABAAAAEAAABY` + + `JQAA` + `` diff --git a/dialog/file_mobile.go b/dialog/file_mobile.go index d2fd3e36fe..6e31497cb2 100644 --- a/dialog/file_mobile.go +++ b/dialog/file_mobile.go @@ -36,7 +36,7 @@ func fileOpenOSOverride(f *FileDialog) bool { } func fileSaveOSOverride(f *FileDialog) bool { - gomobile.ShowFileSavePicker(f.callback.(func(fyne.URIWriteCloser, error))) + gomobile.ShowFileSavePicker(f.callback.(func(fyne.URIWriteCloser, error)), f.filter) return true } diff --git a/internal/driver/gomobile/file.go b/internal/driver/gomobile/file.go index 91d92f03f6..15e83087bd 100644 --- a/internal/driver/gomobile/file.go +++ b/internal/driver/gomobile/file.go @@ -102,11 +102,11 @@ func fileWriterForURI(u fyne.URI) (fyne.URIWriteCloser, error) { } type hasSavePicker interface { - ShowFileSavePicker(func(string, func())) + ShowFileSavePicker(func(string, func()), *app.FileFilter) } // ShowFileSavePicker loads the native file save dialog and returns the chosen file path via the callback func. -func ShowFileSavePicker(callback func(fyne.URIWriteCloser, error)) { +func ShowFileSavePicker(callback func(fyne.URIWriteCloser, error), filter storage.FileFilter) { drv := fyne.CurrentApp().Driver().(*mobileDriver) if a, ok := drv.app.(hasSavePicker); ok { a.ShowFileSavePicker(func(uri string, closer func()) { @@ -119,6 +119,6 @@ func ShowFileSavePicker(callback func(fyne.URIWriteCloser, error)) { f.(*fileSave).done = closer } callback(f, err) - }) + }, mobileFilter(filter)) } } From d802edbd48925ee69285b4d03be42c1c8ff4ed96 Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Fri, 12 Mar 2021 10:32:32 -0600 Subject: [PATCH 23/62] enable write for mobile repository --- internal/driver/gomobile/repository.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/driver/gomobile/repository.go b/internal/driver/gomobile/repository.go index 4b2d33f5a6..3555eb2414 100644 --- a/internal/driver/gomobile/repository.go +++ b/internal/driver/gomobile/repository.go @@ -11,9 +11,7 @@ import ( // declare conformance with repository types var _ repository.Repository = (*mobileFileRepo)(nil) var _ repository.ListableRepository = (*mobileFileRepo)(nil) - -// TODO add write support (not yet supported on mobile) -// var _ repository.WritableRepository = (*mobileFileRepo)(nil) +var _ repository.WritableRepository = (*mobileFileRepo)(nil) type mobileFileRepo struct { driver *mobileDriver @@ -34,6 +32,19 @@ func (m *mobileFileRepo) CanRead(u fyne.URI) (bool, error) { func (m *mobileFileRepo) Destroy(string) { } +func (m *mobileFileRepo) CanWrite(u fyne.URI) (bool, error) { + return true, nil // TODO check a file can be read +} + +func (m *mobileFileRepo) Writer(u fyne.URI) (fyne.URIWriteCloser, error) { + return fileWriterForURI(u) +} + +func (m *mobileFileRepo) Delete(u fyne.URI) error { + // TODO: implement this + return repository.ErrOperationNotSupported +} + func (m *mobileFileRepo) CanList(u fyne.URI) (bool, error) { return canListURI(u), nil } From 6af3cd2fb28831275a522e4507e999eaf4d46591 Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Fri, 12 Mar 2021 12:28:49 -0600 Subject: [PATCH 24/62] typo --- internal/driver/gomobile/repository.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/driver/gomobile/repository.go b/internal/driver/gomobile/repository.go index 3555eb2414..52e515975f 100644 --- a/internal/driver/gomobile/repository.go +++ b/internal/driver/gomobile/repository.go @@ -33,7 +33,7 @@ func (m *mobileFileRepo) Destroy(string) { } func (m *mobileFileRepo) CanWrite(u fyne.URI) (bool, error) { - return true, nil // TODO check a file can be read + return true, nil // TODO check a file can be written } func (m *mobileFileRepo) Writer(u fyne.URI) (fyne.URIWriteCloser, error) { From 466ddc9a8a8e309a1e75f88b70009989853ea754 Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Fri, 12 Mar 2021 13:46:43 -0600 Subject: [PATCH 25/62] update mobile vendor version dex.go was updated on earlier commit, so running gendex.go again does not show a change Conflicts: go.mod go.sum vendor/modules.txt --- go.mod | 2 +- go.sum | 6 ++-- .../fyne-io/mobile/app/GoNativeActivity.java | 18 ++++++++++- .../github.com/fyne-io/mobile/app/android.c | 14 ++++++++- .../github.com/fyne-io/mobile/app/android.go | 30 +++++++++++++++++-- vendor/github.com/fyne-io/mobile/app/app.go | 4 +++ .../fyne-io/mobile/app/darwin_desktop.go | 4 +++ .../fyne-io/mobile/app/darwin_ios.go | 3 ++ vendor/github.com/fyne-io/mobile/app/shiny.go | 4 +++ vendor/github.com/fyne-io/mobile/app/x11.go | 4 +++ vendor/modules.txt | 2 +- 11 files changed, 82 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index b7918fc3fb..155caf609d 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/akavel/rsrc v0.8.0 // indirect github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 github.com/fsnotify/fsnotify v1.4.9 - github.com/fyne-io/mobile v0.1.2 + github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 github.com/godbus/dbus/v5 v5.0.3 diff --git a/go.sum b/go.sum index 112f526ce0..7adfab78d2 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,10 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d h1:WfVxpuVm+5Gr3ipAoWrxV8lJFYkaBWoEwFRrWThWRSU= github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/fyne-io/mobile v0.1.2 h1:0HaXDtOOwyOTn3Umi0uKVCOgJtfX73c6unC4U8i5VZU= -github.com/fyne-io/mobile v0.1.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= +github.com/fyne-io/mobile v0.1.3-0.20210308120140-0925491569e9 h1:VNKrDC+nmsomQ8llShnBd8X8WFAr66eEwJxX5sap1oE= +github.com/fyne-io/mobile v0.1.3-0.20210308120140-0925491569e9/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= +github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc h1:A5hFL3tVUfFHhVpjmLGPs8SdVIcSkTRpFMm1Vsio5+k= +github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= diff --git a/vendor/github.com/fyne-io/mobile/app/GoNativeActivity.java b/vendor/github.com/fyne-io/mobile/app/GoNativeActivity.java index 71e3d7cc76..16df340d56 100644 --- a/vendor/github.com/fyne-io/mobile/app/GoNativeActivity.java +++ b/vendor/github.com/fyne-io/mobile/app/GoNativeActivity.java @@ -26,6 +26,7 @@ public class GoNativeActivity extends NativeActivity { private static GoNativeActivity goNativeActivity; private static final int FILE_OPEN_CODE = 1; + private static final int FILE_SAVE_CODE = 2; private static final int DEFAULT_INPUT_TYPE = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; // this is required to force samsung keyboards to not suggest @@ -148,6 +149,21 @@ void doShowFileOpen(String mimes) { startActivityForResult(Intent.createChooser(intent, "Open File"), FILE_OPEN_CODE); } + static void showFileSave(String mimes) { + goNativeActivity.doShowFileSave(mimes); + } + + void doShowFileSave(String mimes) { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + if (mimes.contains("|") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + intent.setType("*/*"); + intent.putExtra(Intent.EXTRA_MIME_TYPES, mimes.split("\\|")); + } else { + intent.setType(mimes); + } + intent.addCategory(Intent.CATEGORY_OPENABLE); + startActivityForResult(Intent.createChooser(intent, "Save File"), FILE_SAVE_CODE); + } static int getRune(int deviceId, int keyCode, int metaState) { try { int rune = KeyCharacterMap.load(deviceId).get(keyCode, metaState); @@ -241,7 +257,7 @@ public void afterTextChanged(Editable s) { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // unhandled request - if (requestCode != FILE_OPEN_CODE) { + if (requestCode != FILE_OPEN_CODE && requestCode != FILE_SAVE_CODE) { return; } diff --git a/vendor/github.com/fyne-io/mobile/app/android.c b/vendor/github.com/fyne-io/mobile/app/android.c index 2ed86d5854..0c1ed9e6e6 100644 --- a/vendor/github.com/fyne-io/mobile/app/android.c +++ b/vendor/github.com/fyne-io/mobile/app/android.c @@ -51,6 +51,7 @@ static jmethodID key_rune_method; static jmethodID show_keyboard_method; static jmethodID hide_keyboard_method; static jmethodID show_file_open_method; +static jmethodID show_file_save_method; jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; @@ -82,6 +83,7 @@ void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_ show_keyboard_method = find_static_method(env, current_class, "showKeyboard", "(I)V"); hide_keyboard_method = find_static_method(env, current_class, "hideKeyboard", "()V"); show_file_open_method = find_static_method(env, current_class, "showFileOpen", "(Ljava/lang/String;)V"); + show_file_save_method = find_static_method(env, current_class, "showFileSave", "(Ljava/lang/String;)V"); setCurrentContext(activity->vm, (*env)->NewGlobalRef(env, activity->clazz)); @@ -239,6 +241,16 @@ void showFileOpen(JNIEnv* env, char* mimes) { ); } +void showFileSave(JNIEnv* env, char* mimes) { + jstring mimesJString = (*env)->NewStringUTF(env, mimes); + (*env)->CallStaticVoidMethod( + env, + current_class, + show_file_save_method, + mimesJString + ); +} + void Java_org_golang_app_GoNativeActivity_filePickerReturned(JNIEnv *env, jclass clazz, jstring str) { const char* cstr = (*env)->GetStringUTFChars(env, str, JNI_FALSE); filePickerReturned((char*)cstr); @@ -255,4 +267,4 @@ void Java_org_golang_app_GoNativeActivity_keyboardTyped(JNIEnv *env, jclass claz void Java_org_golang_app_GoNativeActivity_keyboardDelete(JNIEnv *env, jclass clazz) { keyboardDelete(); -} \ No newline at end of file +} diff --git a/vendor/github.com/fyne-io/mobile/app/android.go b/vendor/github.com/fyne-io/mobile/app/android.go index 742dfff54b..37c7a45e7c 100644 --- a/vendor/github.com/fyne-io/mobile/app/android.go +++ b/vendor/github.com/fyne-io/mobile/app/android.go @@ -45,6 +45,7 @@ int32_t getKeyRune(JNIEnv* env, AInputEvent* e); void showKeyboard(JNIEnv* env, int keyboardType); void hideKeyboard(JNIEnv* env); void showFileOpen(JNIEnv* env, char* mimes); +void showFileSave(JNIEnv* env, char* mimes); void Java_org_golang_app_GoNativeActivity_filePickerReturned(JNIEnv *env, jclass clazz, jstring str); */ @@ -337,9 +338,7 @@ func insetsChanged(top, bottom, left, right int) { screenInsetTop, screenInsetBottom, screenInsetLeft, screenInsetRight = top, bottom, left, right } -func driverShowFileOpenPicker(callback func(string, func()), filter *FileFilter) { - fileCallback = callback - +func mimeStringFromFilter(filter *FileFilter) string { mimes := "*/*" if filter.MimeTypes != nil { mimes = strings.Join(filter.MimeTypes, "|") @@ -362,6 +361,13 @@ func driverShowFileOpenPicker(callback func(string, func()), filter *FileFilter) } mimes = strings.Join(mimeTypes, "|") } + return mimes +} + +func driverShowFileOpenPicker(callback func(string, func()), filter *FileFilter) { + fileCallback = callback + + mimes := mimeStringFromFilter(filter) mimeStr := C.CString(mimes) defer C.free(unsafe.Pointer(mimeStr)) @@ -377,6 +383,24 @@ func driverShowFileOpenPicker(callback func(string, func()), filter *FileFilter) } } +func driverShowFileSavePicker(callback func(string, func()), filter *FileFilter) { + fileCallback = callback + + mimes := mimeStringFromFilter(filter) + mimeStr := C.CString(mimes) + defer C.free(unsafe.Pointer(mimeStr)) + + save := func(vm, jniEnv, ctx uintptr) error { + env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer + C.showFileSave(env, mimeStr) + return nil + } + + if err := mobileinit.RunOnJVM(save); err != nil { + log.Fatalf("app: %v", err) + } +} + var mainUserFn func(App) var DisplayMetrics struct { diff --git a/vendor/github.com/fyne-io/mobile/app/app.go b/vendor/github.com/fyne-io/mobile/app/app.go index 44eccb6995..b1ca18b278 100644 --- a/vendor/github.com/fyne-io/mobile/app/app.go +++ b/vendor/github.com/fyne-io/mobile/app/app.go @@ -55,6 +55,7 @@ type App interface { ShowVirtualKeyboard(KeyboardType) HideVirtualKeyboard() ShowFileOpenPicker(func(string, func()), *FileFilter) + ShowFileSavePicker(func(string, func()), *FileFilter) } type FileFilter struct { @@ -149,6 +150,9 @@ func (a *app) HideVirtualKeyboard() { func (a *app) ShowFileOpenPicker(callback func(string, func()), filter *FileFilter) { driverShowFileOpenPicker(callback, filter) } +func (a *app) ShowFileSavePicker(callback func(string, func()), filter *FileFilter) { + driverShowFileSavePicker(callback, filter) +} type stopPumping struct{} diff --git a/vendor/github.com/fyne-io/mobile/app/darwin_desktop.go b/vendor/github.com/fyne-io/mobile/app/darwin_desktop.go index 54f8b2ad2c..0e1a240ef2 100644 --- a/vendor/github.com/fyne-io/mobile/app/darwin_desktop.go +++ b/vendor/github.com/fyne-io/mobile/app/darwin_desktop.go @@ -239,6 +239,10 @@ func driverHideVirtualKeyboard() { func driverShowFileOpenPicker(func(string, func()), *FileFilter) { } +// driverShowFileSavePicker does nothing on desktop +func driverShowFileSavePicker(func(string, func()), *FileFilter) { +} + // convRune marks the Carbon/Cocoa private-range unicode rune representing // a non-unicode key event to -1, used for Rune in the key package. // diff --git a/vendor/github.com/fyne-io/mobile/app/darwin_ios.go b/vendor/github.com/fyne-io/mobile/app/darwin_ios.go index eb65c66bc6..7c82b50d27 100644 --- a/vendor/github.com/fyne-io/mobile/app/darwin_ios.go +++ b/vendor/github.com/fyne-io/mobile/app/darwin_ios.go @@ -301,3 +301,6 @@ func driverShowFileOpenPicker(callback func(string, func()), filter *FileFilter) C.showFileOpenPicker(mimeStr, extStr) } + +func driverShowFileSavePicker(callback func(string, func()), filter *FileFilter) { +} diff --git a/vendor/github.com/fyne-io/mobile/app/shiny.go b/vendor/github.com/fyne-io/mobile/app/shiny.go index c7e1adc351..b7c1926a43 100644 --- a/vendor/github.com/fyne-io/mobile/app/shiny.go +++ b/vendor/github.com/fyne-io/mobile/app/shiny.go @@ -25,3 +25,7 @@ func driverHideVirtualKeyboard() { // driverShowFileOpenPicker does nothing on desktop func driverShowFileOpenPicker(func(string, func()), *FileFilter) { } + +// driverShowFileSavePicker does nothing on desktop +func driverShowFileSavePicker(func(string, func()), *FileFilter) { +} diff --git a/vendor/github.com/fyne-io/mobile/app/x11.go b/vendor/github.com/fyne-io/mobile/app/x11.go index e93dc4e663..edc3977af1 100644 --- a/vendor/github.com/fyne-io/mobile/app/x11.go +++ b/vendor/github.com/fyne-io/mobile/app/x11.go @@ -135,3 +135,7 @@ func driverHideVirtualKeyboard() { // driverShowFileOpenPicker does nothing on desktop func driverShowFileOpenPicker(func(string, func()), *FileFilter) { } + +// driverShowFileSavePicker does nothing on desktop +func driverShowFileSavePicker(func(string, func()), *FileFilter) { +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 958b2782f8..49f9dc5234 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,7 +10,7 @@ github.com/davecgh/go-spew/spew github.com/fredbi/uri # github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify -# github.com/fyne-io/mobile v0.1.2 +# github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc github.com/fyne-io/mobile/app github.com/fyne-io/mobile/app/internal/callfn github.com/fyne-io/mobile/event/key From 9aaf82a739f0eb829ce995308e86ac78f096986b Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Wed, 17 Mar 2021 11:34:13 -0500 Subject: [PATCH 26/62] fix sending gobytes to C --- internal/driver/gomobile/android.c | 5 +++-- internal/driver/gomobile/file_android.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/driver/gomobile/android.c b/internal/driver/gomobile/android.c index 43501b05fc..a953bf6ecc 100644 --- a/internal/driver/gomobile/android.c +++ b/internal/driver/gomobile/android.c @@ -188,10 +188,11 @@ void writeStream(uintptr_t jni_env, uintptr_t ctx, void* stream, char* buf, int jbyteArray data = (*env)->NewByteArray(env, len); jboolean isCopy; - void *underlying = (*env)->GetPrimitiveArrayCritical(env, (jarray)data, &isCopy); - memcpy(underlying, buf, len); + (*env)->SetByteArrayRegion(env, data, 0, len, buf); (*env)->CallVoidMethod(env, stream, write, data, 0, len); + + free(buf); } void closeStream(uintptr_t jni_env, uintptr_t ctx, void* stream) { diff --git a/internal/driver/gomobile/file_android.go b/internal/driver/gomobile/file_android.go index a1e6f84c85..d7203391e6 100644 --- a/internal/driver/gomobile/file_android.go +++ b/internal/driver/gomobile/file_android.go @@ -131,7 +131,7 @@ var _ io.WriteCloser = (*javaStream)(nil) func (s *javaStream) Write(p []byte) (int, error) { err := app.RunOnJVM(func(_, env, ctx uintptr) error { - C.writeStream(C.uintptr_t(env), C.uintptr_t(ctx), s.stream, (*C.char)(unsafe.Pointer(&p[0])), C.int(len(p))) + C.writeStream(C.uintptr_t(env), C.uintptr_t(ctx), s.stream, (*C.char)(C.CBytes(p)), C.int(len(p))) return nil }) From af9d7692349abdf0a21fb185219b2a972478f181 Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Wed, 17 Mar 2021 16:29:27 -0500 Subject: [PATCH 27/62] fix indenting, run go mod tidy Conflicts: go.sum --- go.sum | 8 -------- internal/driver/gomobile/android.c | 5 ++--- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/go.sum b/go.sum index 7adfab78d2..166547191f 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,6 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d h1:WfVxpuVm+5Gr3ipAoWrxV8lJFYkaBWoEwFRrWThWRSU= github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/fyne-io/mobile v0.1.3-0.20210308120140-0925491569e9 h1:VNKrDC+nmsomQ8llShnBd8X8WFAr66eEwJxX5sap1oE= -github.com/fyne-io/mobile v0.1.3-0.20210308120140-0925491569e9/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc h1:A5hFL3tVUfFHhVpjmLGPs8SdVIcSkTRpFMm1Vsio5+k= github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= @@ -46,7 +44,6 @@ github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQO github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM= github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= @@ -54,14 +51,12 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= @@ -74,18 +69,15 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8= golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190808195139-e713427fea3f h1:lSQQYboXWc71s9tnZRRBiMcc9Uc1BPWj3Bzvdk8UQ0Y= golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03 h1:XpToik3MpT5iW3iHgNwnh3a8QwugfomvxOlyDnaOils= golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/driver/gomobile/android.c b/internal/driver/gomobile/android.c index a953bf6ecc..22f5e16386 100644 --- a/internal/driver/gomobile/android.c +++ b/internal/driver/gomobile/android.c @@ -187,12 +187,11 @@ void writeStream(uintptr_t jni_env, uintptr_t ctx, void* stream, char* buf, int jmethodID write = find_method(env, streamClass, "write", "([BII)V"); jbyteArray data = (*env)->NewByteArray(env, len); - jboolean isCopy; - (*env)->SetByteArrayRegion(env, data, 0, len, buf); + (*env)->SetByteArrayRegion(env, data, 0, len, buf); (*env)->CallVoidMethod(env, stream, write, data, 0, len); - free(buf); + free(buf); } void closeStream(uintptr_t jni_env, uintptr_t ctx, void* stream) { From 7c5719832f76c8292e2ef3bc3f8ed46b8e3f7c21 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Thu, 18 Mar 2021 11:52:14 +0000 Subject: [PATCH 28/62] Update to internet on by default with Android to match iOS Our upcoming metadata file can turn this off if we need to. Fixes #1715 --- cmd/fyne/internal/mobile/manifest.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/fyne/internal/mobile/manifest.go b/cmd/fyne/internal/mobile/manifest.go index 96884fd1a7..29b6a2f5bc 100644 --- a/cmd/fyne/internal/mobile/manifest.go +++ b/cmd/fyne/internal/mobile/manifest.go @@ -78,4 +78,5 @@ var manifestTmpl = template.Must(template.New("manifest").Parse(` + `)) From 03a6e892f77e5231dfa6199596641184d8cbb18b Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Thu, 18 Mar 2021 20:03:42 +0000 Subject: [PATCH 29/62] First pass for iOS file save Working for 'On this Phone' file locations. Seems to have an issue with DropBox export. Fixes #2076 --- go.mod | 2 +- go.sum | 4 +- internal/driver/gomobile/file_ios.go | 39 ++++++++++++++++++- internal/driver/gomobile/file_ios.m | 11 ++++++ .../fyne-io/mobile/app/darwin_ios.go | 33 ++++++++++------ .../fyne-io/mobile/app/darwin_ios.m | 33 ++++++++++++++-- vendor/modules.txt | 2 +- 7 files changed, 104 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 155caf609d..b3f2d40bfd 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/akavel/rsrc v0.8.0 // indirect github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 github.com/fsnotify/fsnotify v1.4.9 - github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc + github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 github.com/godbus/dbus/v5 v5.0.3 diff --git a/go.sum b/go.sum index 166547191f..a0be01e87a 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d h1:WfVxpuVm+5Gr3ipAoWrxV8lJFYkaBWoEwFRrWThWRSU= github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc h1:A5hFL3tVUfFHhVpjmLGPs8SdVIcSkTRpFMm1Vsio5+k= -github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= +github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f h1:rguJ/t99j/6zRSFzsBKlsmmyl+vOvCeTJ+2uTBvuXFI= +github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= diff --git a/internal/driver/gomobile/file_ios.go b/internal/driver/gomobile/file_ios.go index b08c0ca46d..d399832157 100644 --- a/internal/driver/gomobile/file_ios.go +++ b/internal/driver/gomobile/file_ios.go @@ -10,10 +10,10 @@ package gomobile void* iosParseUrl(const char* url); const void* iosReadFromURL(void* url, int* len); +const int iosWriteToURL(void* url, const void* bytes, int len); */ import "C" import ( - "errors" "io" "unsafe" @@ -64,6 +64,31 @@ func (s *secureReadCloser) Close() error { return nil } +type secureWriteCloser struct { + url unsafe.Pointer + closer func() + + offset int +} + +// Declare conformity to WriteCloser interface +var _ io.WriteCloser = (*secureWriteCloser)(nil) + +func (s *secureWriteCloser) Write(p []byte) (int, error) { + count := int(C.iosWriteToURL(s.url, C.CBytes(p), C.int(len(p)))) + s.offset += count + + return count, nil +} + +func (s *secureWriteCloser) Close() error { + if s.closer != nil { + s.closer() + } + s.url = nil + return nil +} + func nativeFileOpen(f *fileOpen) (io.ReadCloser, error) { if f.uri == nil || f.uri.String() == "" { return nil, nil @@ -79,7 +104,17 @@ func nativeFileOpen(f *fileOpen) (io.ReadCloser, error) { } func nativeFileSave(f *fileSave) (io.WriteCloser, error) { - return nil, errors.New("Currently unsupported.") + if f.uri == nil || f.uri.String() == "" { + return nil, nil + } + + cStr := C.CString(f.uri.String()) + defer C.free(unsafe.Pointer(cStr)) + + url := C.iosParseUrl(cStr) + + fileStruct := &secureWriteCloser{url: url, closer: f.done} + return fileStruct, nil } func registerRepository(d *mobileDriver) { diff --git a/internal/driver/gomobile/file_ios.m b/internal/driver/gomobile/file_ios.m index 864e2093d4..613cf0a228 100644 --- a/internal/driver/gomobile/file_ios.m +++ b/internal/driver/gomobile/file_ios.m @@ -16,3 +16,14 @@ *len = data.length; return data.bytes; } + +const int iosWriteToURL(void* urlPtr, const void* bytes, int len) { + NSURL* url = (NSURL*)urlPtr; + NSData *data = [NSData dataWithBytes:bytes length:len]; + BOOL ok = [data writeToURL:url atomically:YES]; + + if (!ok) { + return 0; + } + return data.length; +} diff --git a/vendor/github.com/fyne-io/mobile/app/darwin_ios.go b/vendor/github.com/fyne-io/mobile/app/darwin_ios.go index 7c82b50d27..dc30a97438 100644 --- a/vendor/github.com/fyne-io/mobile/app/darwin_ios.go +++ b/vendor/github.com/fyne-io/mobile/app/darwin_ios.go @@ -28,6 +28,7 @@ void showKeyboard(int keyboardType); void hideKeyboard(); void showFileOpenPicker(char* mimes, char *exts); +void showFileSavePicker(char* mimes, char *exts); void closeFileResource(void* urlPtr); */ import "C" @@ -258,6 +259,19 @@ func (a *app) loop(ctx C.GLintptr) { } } +func cStringsForFilter(filter *FileFilter) (*C.char, *C.char) { + mimes := strings.Join(filter.MimeTypes, "|") + + // extensions must have the '.' removed for UTI lookups on iOS + extList := []string{} + for _, ext := range filter.Extensions { + extList = append(extList, ext[1:]) + } + exts := strings.Join(extList, "|") + + return C.CString(mimes), C.CString(exts) +} + // driverShowVirtualKeyboard requests the driver to show a virtual keyboard for text input func driverShowVirtualKeyboard(keyboard KeyboardType) { C.showKeyboard(C.int(int32(keyboard))) @@ -285,22 +299,19 @@ func filePickerReturned(str *C.char, urlPtr unsafe.Pointer) { func driverShowFileOpenPicker(callback func(string, func()), filter *FileFilter) { fileCallback = callback - mimes := strings.Join(filter.MimeTypes, "|") - - // extensions must have the '.' removed for UTI lookups on iOS - extList := []string{} - for _, ext := range filter.Extensions { - extList = append(extList, ext[1:]) - } - exts := strings.Join(extList, "|") - - mimeStr := C.CString(mimes) + mimeStr, extStr := cStringsForFilter(filter) defer C.free(unsafe.Pointer(mimeStr)) - extStr := C.CString(exts) defer C.free(unsafe.Pointer(extStr)) C.showFileOpenPicker(mimeStr, extStr) } func driverShowFileSavePicker(callback func(string, func()), filter *FileFilter) { + fileCallback = callback + + mimeStr, extStr := cStringsForFilter(filter) + defer C.free(unsafe.Pointer(mimeStr)) + defer C.free(unsafe.Pointer(extStr)) + + C.showFileSavePicker(mimeStr, extStr) } diff --git a/vendor/github.com/fyne-io/mobile/app/darwin_ios.m b/vendor/github.com/fyne-io/mobile/app/darwin_ios.m index 7e9441a4a5..0081dc8630 100644 --- a/vendor/github.com/fyne-io/mobile/app/darwin_ios.m +++ b/vendor/github.com/fyne-io/mobile/app/darwin_ios.m @@ -294,9 +294,7 @@ void hideKeyboard() { }); } -void showFileOpenPicker(char* mimes, char *exts) { - GoAppAppDelegate *appDelegate = (GoAppAppDelegate *)[[UIApplication sharedApplication] delegate]; - +NSMutableArray *docTypesForMimeExts(char *mimes, char *exts) { NSMutableArray *docTypes = [NSMutableArray array]; if (mimes != NULL && strlen(mimes) > 0) { NSString *mimeList = [NSString stringWithUTF8String:mimes]; @@ -325,6 +323,14 @@ void showFileOpenPicker(char* mimes, char *exts) { [docTypes addObject:@"public.data"]; } + return docTypes; +} + +void showFileOpenPicker(char* mimes, char *exts) { + GoAppAppDelegate *appDelegate = (GoAppAppDelegate *)[[UIApplication sharedApplication] delegate]; + + NSMutableArray *docTypes = docTypesForMimeExts(mimes, exts); + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:docTypes inMode:UIDocumentPickerModeOpen]; documentPicker.delegate = appDelegate; @@ -334,6 +340,27 @@ void showFileOpenPicker(char* mimes, char *exts) { }); } +void showFileSavePicker(char* mimes, char *exts) { + GoAppAppDelegate *appDelegate = (GoAppAppDelegate *)[[UIApplication sharedApplication] delegate]; + + NSMutableArray *docTypes = docTypesForMimeExts(mimes, exts); + + NSURL *temporaryDirectoryURL = [NSURL fileURLWithPath: NSTemporaryDirectory() isDirectory: YES]; + NSURL *temporaryFileURL = [temporaryDirectoryURL URLByAppendingPathComponent:@"filename"]; + + char* bytes = "\n"; + NSData *data = [NSData dataWithBytes:bytes length:1]; + BOOL ok = [data writeToURL:temporaryFileURL atomically:YES]; + + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] + initWithURL:temporaryFileURL inMode:UIDocumentPickerModeMoveToService]; + documentPicker.delegate = appDelegate; + + dispatch_async(dispatch_get_main_queue(), ^{ + [appDelegate.controller presentViewController:documentPicker animated:YES completion:nil]; + }); +} + void closeFileResource(void* urlPtr) { if (urlPtr == NULL) { return; diff --git a/vendor/modules.txt b/vendor/modules.txt index 49f9dc5234..79a1e924fd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,7 +10,7 @@ github.com/davecgh/go-spew/spew github.com/fredbi/uri # github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify -# github.com/fyne-io/mobile v0.1.3-0.20210312180903-f9a21000f5dc +# github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f github.com/fyne-io/mobile/app github.com/fyne-io/mobile/app/internal/callfn github.com/fyne-io/mobile/event/key From f4b970cddda76cd5d90c22448802e54577133b13 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 22 Mar 2021 11:53:46 +0000 Subject: [PATCH 30/62] Add missing child/parent support for file repo in mobile --- internal/driver/gomobile/repository.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/internal/driver/gomobile/repository.go b/internal/driver/gomobile/repository.go index 52e515975f..bd6487171e 100644 --- a/internal/driver/gomobile/repository.go +++ b/internal/driver/gomobile/repository.go @@ -10,6 +10,7 @@ import ( // declare conformance with repository types var _ repository.Repository = (*mobileFileRepo)(nil) +var _ repository.HierarchicalRepository = (*mobileFileRepo)(nil) var _ repository.ListableRepository = (*mobileFileRepo)(nil) var _ repository.WritableRepository = (*mobileFileRepo)(nil) @@ -17,6 +18,22 @@ type mobileFileRepo struct { driver *mobileDriver } +func (m *mobileFileRepo) Child(u fyne.URI, name string) (fyne.URI, error) { + if u == nil || u.Scheme() != "file" { + return nil, repository.ErrOperationNotSupported + } + + return repository.GenericChild(u, name) +} + +func (m *mobileFileRepo) Parent(u fyne.URI) (fyne.URI, error) { + if u == nil || u.Scheme() != "file" { + return nil, repository.ErrOperationNotSupported + } + + return repository.GenericParent(u) +} + func (m *mobileFileRepo) Exists(u fyne.URI) (bool, error) { return true, nil // TODO check a file exists } From 9867f663df61b52c4a16c2fdc759311ae31ed2e8 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 22 Mar 2021 11:59:02 +0000 Subject: [PATCH 31/62] repository tests and actually run them in CI --- internal/driver/gomobile/repository.go | 2 -- internal/driver/gomobile/repository_test.go | 40 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 internal/driver/gomobile/repository_test.go diff --git a/internal/driver/gomobile/repository.go b/internal/driver/gomobile/repository.go index bd6487171e..f6477f2583 100644 --- a/internal/driver/gomobile/repository.go +++ b/internal/driver/gomobile/repository.go @@ -1,5 +1,3 @@ -// +build ios android - package gomobile import ( diff --git a/internal/driver/gomobile/repository_test.go b/internal/driver/gomobile/repository_test.go new file mode 100644 index 0000000000..99a7d7c129 --- /dev/null +++ b/internal/driver/gomobile/repository_test.go @@ -0,0 +1,40 @@ +package gomobile + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "fyne.io/fyne/v2/storage" + "fyne.io/fyne/v2/storage/repository" +) + +func TestFileRepositoryChild(t *testing.T) { + f := &mobileFileRepo{} + repository.Register("file", f) + + p, _ := storage.Child(storage.NewFileURI("/foo/bar"), "baz") + assert.Equal(t, "file:///foo/bar/baz", p.String()) + + p, _ = storage.Child(storage.NewFileURI("/foo/bar/"), "baz") + assert.Equal(t, "file:///foo/bar/baz", p.String()) + + p, err := storage.Child(storage.NewURI("content://thing"), "new") + assert.NotNil(t, err) + assert.Nil(t, p) +} + +func TestFileRepositoryParent(t *testing.T) { + f := &mobileFileRepo{} + repository.Register("file", f) + + p, _ := storage.Parent(storage.NewFileURI("/foo/bar")) + assert.Equal(t, "file:///foo", p.String()) + + p, _ = storage.Parent(storage.NewFileURI("/foo/bar/")) + assert.Equal(t, "file:///foo", p.String()) + + p, err := storage.Parent(storage.NewURI("content://thing")) + assert.NotNil(t, err) + assert.Nil(t, p) +} From 40210cd0316df8e43ccd04281b1f575536da7dba Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Wed, 17 Mar 2021 18:45:07 -0500 Subject: [PATCH 32/62] get filename of content:// uri --- internal/driver/gomobile/android.c | 33 ++++++++++++++++++++++ internal/driver/gomobile/file.go | 11 +++++++- internal/driver/gomobile/file_android.go | 35 ++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/internal/driver/gomobile/android.c b/internal/driver/gomobile/android.c index 22f5e16386..4246d50d95 100644 --- a/internal/driver/gomobile/android.c +++ b/internal/driver/gomobile/android.c @@ -275,6 +275,39 @@ bool canListURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { return false; } +char* contentURIGetFileName(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { + JNIEnv *env = (JNIEnv*)jni_env; + jobject resolver = getContentResolver(jni_env, ctx); + jstring uriStr = (*env)->NewStringUTF(env, uriCstr); + jobject uri = parseURI(jni_env, ctx, uriCstr); + jthrowable loadErr = (*env)->ExceptionOccurred(env); + + if (loadErr != NULL) { + (*env)->ExceptionClear(env); + return ""; + } + + jclass stringClass = find_class(env, "java/lang/String"); + jobjectArray project = (*env)->NewObjectArray(env, 1, stringClass, (*env)->NewStringUTF(env, "_display_name")); + + jclass resolverClass = (*env)->GetObjectClass(env, resolver); + jmethodID query = find_method(env, resolverClass, "query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;"); + + jobject cursor = (jobject)(*env)->CallObjectMethod(env, resolver, query, uri, project, NULL, NULL, NULL); + jclass cursorClass = (*env)->GetObjectClass(env, cursor); + + jmethodID first = find_method(env, cursorClass, "moveToFirst", "()Z"); + jmethodID get = find_method(env, cursorClass, "getString", "(I)Ljava/lang/String;"); + + if (((jboolean)(*env)->CallBooleanMethod(env, cursor, first)) == JNI_TRUE) { + jstring name = (jstring)(*env)->CallObjectMethod(env, cursor, get, 0); + char *fname = getString(jni_env, ctx, name); + return fname; + } + + return NULL; +} + char* listContentURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { JNIEnv *env = (JNIEnv*)jni_env; jobject resolver = getContentResolver(jni_env, ctx); diff --git a/internal/driver/gomobile/file.go b/internal/driver/gomobile/file.go index 15e83087bd..15e0aa24b5 100644 --- a/internal/driver/gomobile/file.go +++ b/internal/driver/gomobile/file.go @@ -9,6 +9,12 @@ import ( "fyne.io/fyne/v2/storage" ) +// Override Name on android for content:// +type mobileURI struct { + systemURI string + fyne.URI +} + type fileOpen struct { io.ReadCloser uri fyne.URI @@ -56,7 +62,10 @@ func ShowFileOpenPicker(callback func(fyne.URIReadCloser, error), filter storage callback(nil, nil) return } - f, err := fileReaderForURI(storage.NewURI(uri)) + f, err := fileReaderForURI(&mobileURI{ + URI: storage.NewURI(uri), + systemURI: uri, + }) if f != nil { f.(*fileOpen).done = closer } diff --git a/internal/driver/gomobile/file_android.go b/internal/driver/gomobile/file_android.go index d7203391e6..867f7dd580 100644 --- a/internal/driver/gomobile/file_android.go +++ b/internal/driver/gomobile/file_android.go @@ -12,18 +12,34 @@ char* readStream(uintptr_t jni_env, uintptr_t ctx, void* stream, int len, int* t void* saveStream(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); void writeStream(uintptr_t jni_env, uintptr_t ctx, void* stream, char* data, int len); void closeStream(uintptr_t jni_env, uintptr_t ctx, void* stream); +char* contentURIGetFileName(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); */ import "C" import ( "errors" "io" "os" + "path/filepath" "unsafe" "fyne.io/fyne/v2/storage/repository" "github.com/fyne-io/mobile/app" ) +func (m mobileURI) Name() string { + if m.Scheme() == "content" { + result := contentURIGetFileName(m.systemURI) + if result != "" { + return result + } + } + return m.URI.Name() +} + +func (m mobileURI) Extension() string { + return filepath.Ext(m.Name()) +} + type javaStream struct { stream unsafe.Pointer // java.io.InputStream } @@ -79,6 +95,25 @@ func openStream(uri string) unsafe.Pointer { return stream } +func contentURIGetFileName(uri string) string { + uriStr := C.CString(uri) + defer C.free(unsafe.Pointer(uriStr)) + + var filename string + app.RunOnJVM(func(_, env, ctx uintptr) error { + fnamePtr := C.contentURIGetFileName(C.uintptr_t(env), C.uintptr_t(ctx), uriStr) + vPtr := unsafe.Pointer(fnamePtr) + if vPtr == C.NULL { + return nil + } + filename = C.GoString(fnamePtr) + C.free(vPtr) + + return nil + }) + return filename +} + func nativeFileOpen(f *fileOpen) (io.ReadCloser, error) { if f.uri == nil || f.uri.String() == "" { return nil, nil From 10bd01d3b6933c202ee5e02ac0474322e3ef95f6 Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Thu, 18 Mar 2021 09:32:40 -0500 Subject: [PATCH 33/62] move mobile uri to separate file --- internal/driver/gomobile/file.go | 6 --- internal/driver/gomobile/file_android.go | 35 ---------------- internal/driver/gomobile/uri.go | 11 +++++ internal/driver/gomobile/uri_android.go | 51 ++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 41 deletions(-) create mode 100644 internal/driver/gomobile/uri.go create mode 100644 internal/driver/gomobile/uri_android.go diff --git a/internal/driver/gomobile/file.go b/internal/driver/gomobile/file.go index 15e0aa24b5..c4cd8e3d6a 100644 --- a/internal/driver/gomobile/file.go +++ b/internal/driver/gomobile/file.go @@ -9,12 +9,6 @@ import ( "fyne.io/fyne/v2/storage" ) -// Override Name on android for content:// -type mobileURI struct { - systemURI string - fyne.URI -} - type fileOpen struct { io.ReadCloser uri fyne.URI diff --git a/internal/driver/gomobile/file_android.go b/internal/driver/gomobile/file_android.go index 867f7dd580..d7203391e6 100644 --- a/internal/driver/gomobile/file_android.go +++ b/internal/driver/gomobile/file_android.go @@ -12,34 +12,18 @@ char* readStream(uintptr_t jni_env, uintptr_t ctx, void* stream, int len, int* t void* saveStream(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); void writeStream(uintptr_t jni_env, uintptr_t ctx, void* stream, char* data, int len); void closeStream(uintptr_t jni_env, uintptr_t ctx, void* stream); -char* contentURIGetFileName(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); */ import "C" import ( "errors" "io" "os" - "path/filepath" "unsafe" "fyne.io/fyne/v2/storage/repository" "github.com/fyne-io/mobile/app" ) -func (m mobileURI) Name() string { - if m.Scheme() == "content" { - result := contentURIGetFileName(m.systemURI) - if result != "" { - return result - } - } - return m.URI.Name() -} - -func (m mobileURI) Extension() string { - return filepath.Ext(m.Name()) -} - type javaStream struct { stream unsafe.Pointer // java.io.InputStream } @@ -95,25 +79,6 @@ func openStream(uri string) unsafe.Pointer { return stream } -func contentURIGetFileName(uri string) string { - uriStr := C.CString(uri) - defer C.free(unsafe.Pointer(uriStr)) - - var filename string - app.RunOnJVM(func(_, env, ctx uintptr) error { - fnamePtr := C.contentURIGetFileName(C.uintptr_t(env), C.uintptr_t(ctx), uriStr) - vPtr := unsafe.Pointer(fnamePtr) - if vPtr == C.NULL { - return nil - } - filename = C.GoString(fnamePtr) - C.free(vPtr) - - return nil - }) - return filename -} - func nativeFileOpen(f *fileOpen) (io.ReadCloser, error) { if f.uri == nil || f.uri.String() == "" { return nil, nil diff --git a/internal/driver/gomobile/uri.go b/internal/driver/gomobile/uri.go new file mode 100644 index 0000000000..803b9b3496 --- /dev/null +++ b/internal/driver/gomobile/uri.go @@ -0,0 +1,11 @@ +package gomobile + +import ( + "fyne.io/fyne/v2" +) + +// Override Name on android for content:// +type mobileURI struct { + systemURI string + fyne.URI +} diff --git a/internal/driver/gomobile/uri_android.go b/internal/driver/gomobile/uri_android.go new file mode 100644 index 0000000000..99aaaa0556 --- /dev/null +++ b/internal/driver/gomobile/uri_android.go @@ -0,0 +1,51 @@ +// +build android + +package gomobile + +/* +#cgo LDFLAGS: -landroid -llog + +#include + +char* contentURIGetFileName(uintptr_t jni_env, uintptr_t ctx, char* uriCstr); +*/ +import "C" +import ( + "path/filepath" + "unsafe" + + "github.com/fyne-io/mobile/app" +) + +func (m *mobileURI) Name() string { + if m.Scheme() == "content" { + result := contentURIGetFileName(m.systemURI) + if result != "" { + return result + } + } + return m.URI.Name() +} + +func (m *mobileURI) Extension() string { + return filepath.Ext(m.Name()) +} + +func contentURIGetFileName(uri string) string { + uriStr := C.CString(uri) + defer C.free(unsafe.Pointer(uriStr)) + + var filename string + app.RunOnJVM(func(_, env, ctx uintptr) error { + fnamePtr := C.contentURIGetFileName(C.uintptr_t(env), C.uintptr_t(ctx), uriStr) + vPtr := unsafe.Pointer(fnamePtr) + if vPtr == C.NULL { + return nil + } + filename = C.GoString(fnamePtr) + C.free(vPtr) + + return nil + }) + return filename +} From 0265ce92d83cc722c8050ffab6e4b6e07e33c74e Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Thu, 18 Mar 2021 10:10:06 -0500 Subject: [PATCH 34/62] custom uri handling android-only --- internal/driver/gomobile/file.go | 5 +---- internal/driver/gomobile/uri.go | 9 +++++---- internal/driver/gomobile/uri_android.go | 24 ++++++++++++++++++------ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/internal/driver/gomobile/file.go b/internal/driver/gomobile/file.go index c4cd8e3d6a..ea12251caa 100644 --- a/internal/driver/gomobile/file.go +++ b/internal/driver/gomobile/file.go @@ -56,10 +56,7 @@ func ShowFileOpenPicker(callback func(fyne.URIReadCloser, error), filter storage callback(nil, nil) return } - f, err := fileReaderForURI(&mobileURI{ - URI: storage.NewURI(uri), - systemURI: uri, - }) + f, err := fileReaderForURI(nativeURI(uri)) if f != nil { f.(*fileOpen).done = closer } diff --git a/internal/driver/gomobile/uri.go b/internal/driver/gomobile/uri.go index 803b9b3496..dadfc50006 100644 --- a/internal/driver/gomobile/uri.go +++ b/internal/driver/gomobile/uri.go @@ -1,11 +1,12 @@ +// +build !android + package gomobile import ( "fyne.io/fyne/v2" + "fyne.io/fyne/v2/storage" ) -// Override Name on android for content:// -type mobileURI struct { - systemURI string - fyne.URI +func nativeURI(uri string) fyne.URI { + return storage.NewURI(uri) } diff --git a/internal/driver/gomobile/uri_android.go b/internal/driver/gomobile/uri_android.go index 99aaaa0556..b1138747f4 100644 --- a/internal/driver/gomobile/uri_android.go +++ b/internal/driver/gomobile/uri_android.go @@ -14,21 +14,29 @@ import ( "path/filepath" "unsafe" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/storage" "github.com/fyne-io/mobile/app" ) -func (m *mobileURI) Name() string { - if m.Scheme() == "content" { - result := contentURIGetFileName(m.systemURI) +type androidURI struct { + systemURI string + fyne.URI +} + +// Override Name on android for content:// +func (a *androidURI) Name() string { + if a.Scheme() == "content" { + result := contentURIGetFileName(a.systemURI) if result != "" { return result } } - return m.URI.Name() + return a.URI.Name() } -func (m *mobileURI) Extension() string { - return filepath.Ext(m.Name()) +func (a *androidURI) Extension() string { + return filepath.Ext(a.Name()) } func contentURIGetFileName(uri string) string { @@ -49,3 +57,7 @@ func contentURIGetFileName(uri string) string { }) return filename } + +func nativeURI(uri string) fyne.URI { + return &androidURI{URI: storage.NewURI(uri), systemURI: uri} +} From bd4849fd4350297bada2649db5afd75c67ab9abe Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Thu, 18 Mar 2021 15:41:15 -0500 Subject: [PATCH 35/62] remove unused variable --- internal/driver/gomobile/android.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/driver/gomobile/android.c b/internal/driver/gomobile/android.c index 4246d50d95..06d66d6768 100644 --- a/internal/driver/gomobile/android.c +++ b/internal/driver/gomobile/android.c @@ -215,7 +215,6 @@ bool hasPrefix(char* string, char* prefix) { bool canListContentURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { JNIEnv *env = (JNIEnv*)jni_env; jobject resolver = getContentResolver(jni_env, ctx); - jstring uriStr = (*env)->NewStringUTF(env, uriCstr); jobject uri = parseURI(jni_env, ctx, uriCstr); jthrowable loadErr = (*env)->ExceptionOccurred(env); @@ -278,7 +277,6 @@ bool canListURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { char* contentURIGetFileName(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { JNIEnv *env = (JNIEnv*)jni_env; jobject resolver = getContentResolver(jni_env, ctx); - jstring uriStr = (*env)->NewStringUTF(env, uriCstr); jobject uri = parseURI(jni_env, ctx, uriCstr); jthrowable loadErr = (*env)->ExceptionOccurred(env); @@ -311,7 +309,6 @@ char* contentURIGetFileName(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { char* listContentURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { JNIEnv *env = (JNIEnv*)jni_env; jobject resolver = getContentResolver(jni_env, ctx); - jstring uriStr = (*env)->NewStringUTF(env, uriCstr); jobject uri = parseURI(jni_env, ctx, uriCstr); jthrowable loadErr = (*env)->ExceptionOccurred(env); From 2a976fb1e6423cdc5fa43f3da7e32eb294f54bc8 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 22 Mar 2021 12:13:33 +0000 Subject: [PATCH 36/62] Fix file order --- internal/driver/gomobile/repository.go | 61 +++++++++++++------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/internal/driver/gomobile/repository.go b/internal/driver/gomobile/repository.go index f6477f2583..316bd9c4db 100644 --- a/internal/driver/gomobile/repository.go +++ b/internal/driver/gomobile/repository.go @@ -16,59 +16,60 @@ type mobileFileRepo struct { driver *mobileDriver } -func (m *mobileFileRepo) Child(u fyne.URI, name string) (fyne.URI, error) { - if u == nil || u.Scheme() != "file" { - return nil, repository.ErrOperationNotSupported - } +func (m *mobileFileRepo) CanList(u fyne.URI) (bool, error) { + return canListURI(u), nil +} - return repository.GenericChild(u, name) +func (m *mobileFileRepo) CanRead(u fyne.URI) (bool, error) { + return true, nil // TODO check a file can be read } -func (m *mobileFileRepo) Parent(u fyne.URI) (fyne.URI, error) { + +func (m *mobileFileRepo) CanWrite(u fyne.URI) (bool, error) { + return true, nil // TODO check a file can be written +} + +func (m *mobileFileRepo) Child(u fyne.URI, name string) (fyne.URI, error) { if u == nil || u.Scheme() != "file" { return nil, repository.ErrOperationNotSupported } - return repository.GenericParent(u) -} - -func (m *mobileFileRepo) Exists(u fyne.URI) (bool, error) { - return true, nil // TODO check a file exists + return repository.GenericChild(u, name) } -func (m *mobileFileRepo) Reader(u fyne.URI) (fyne.URIReadCloser, error) { - return fileReaderForURI(u) +func (m *mobileFileRepo) CreateListable(u fyne.URI) error { + // TODO: implement this + return repository.ErrOperationNotSupported } -func (m *mobileFileRepo) CanRead(u fyne.URI) (bool, error) { - return true, nil // TODO check a file can be read +func (m *mobileFileRepo) Delete(u fyne.URI) error { + // TODO: implement this + return repository.ErrOperationNotSupported } func (m *mobileFileRepo) Destroy(string) { } -func (m *mobileFileRepo) CanWrite(u fyne.URI) (bool, error) { - return true, nil // TODO check a file can be written +func (m *mobileFileRepo) Exists(u fyne.URI) (bool, error) { + return true, nil // TODO check a file exists } -func (m *mobileFileRepo) Writer(u fyne.URI) (fyne.URIWriteCloser, error) { - return fileWriterForURI(u) +func (m *mobileFileRepo) List(u fyne.URI) ([]fyne.URI, error) { + return listURI(u) } -func (m *mobileFileRepo) Delete(u fyne.URI) error { - // TODO: implement this - return repository.ErrOperationNotSupported -} +func (m *mobileFileRepo) Parent(u fyne.URI) (fyne.URI, error) { + if u == nil || u.Scheme() != "file" { + return nil, repository.ErrOperationNotSupported + } -func (m *mobileFileRepo) CanList(u fyne.URI) (bool, error) { - return canListURI(u), nil + return repository.GenericParent(u) } -func (m *mobileFileRepo) List(u fyne.URI) ([]fyne.URI, error) { - return listURI(u) +func (m *mobileFileRepo) Reader(u fyne.URI) (fyne.URIReadCloser, error) { + return fileReaderForURI(u) } -func (m *mobileFileRepo) CreateListable(u fyne.URI) error { - // TODO: implement this - return repository.ErrOperationNotSupported +func (m *mobileFileRepo) Writer(u fyne.URI) (fyne.URIWriteCloser, error) { + return fileWriterForURI(u) } From 5f7c7775fa005569762afc880d9b0a2f9aa14f50 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 22 Mar 2021 12:23:55 +0000 Subject: [PATCH 37/62] Fix format error --- internal/driver/gomobile/repository.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/driver/gomobile/repository.go b/internal/driver/gomobile/repository.go index 316bd9c4db..5727022489 100644 --- a/internal/driver/gomobile/repository.go +++ b/internal/driver/gomobile/repository.go @@ -24,7 +24,6 @@ func (m *mobileFileRepo) CanRead(u fyne.URI) (bool, error) { return true, nil // TODO check a file can be read } - func (m *mobileFileRepo) CanWrite(u fyne.URI) (bool, error) { return true, nil // TODO check a file can be written } From 72d7ba55abbf1734b8d588017bbedde41006c497 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 22 Mar 2021 12:53:00 +0000 Subject: [PATCH 38/62] Rmove unused field --- internal/driver/gomobile/file_android.go | 2 +- internal/driver/gomobile/file_ios.go | 2 +- internal/driver/gomobile/repository.go | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/driver/gomobile/file_android.go b/internal/driver/gomobile/file_android.go index d7203391e6..4becae0f26 100644 --- a/internal/driver/gomobile/file_android.go +++ b/internal/driver/gomobile/file_android.go @@ -139,7 +139,7 @@ func (s *javaStream) Write(p []byte) (int, error) { } func registerRepository(d *mobileDriver) { - repo := &mobileFileRepo{driver: d} + repo := &mobileFileRepo{} repository.Register("file", repo) repository.Register("content", repo) } diff --git a/internal/driver/gomobile/file_ios.go b/internal/driver/gomobile/file_ios.go index d399832157..528c7a5a79 100644 --- a/internal/driver/gomobile/file_ios.go +++ b/internal/driver/gomobile/file_ios.go @@ -118,6 +118,6 @@ func nativeFileSave(f *fileSave) (io.WriteCloser, error) { } func registerRepository(d *mobileDriver) { - repo := &mobileFileRepo{driver: d} + repo := &mobileFileRepo{} repository.Register("file", repo) } diff --git a/internal/driver/gomobile/repository.go b/internal/driver/gomobile/repository.go index 5727022489..29fb537d76 100644 --- a/internal/driver/gomobile/repository.go +++ b/internal/driver/gomobile/repository.go @@ -13,7 +13,6 @@ var _ repository.ListableRepository = (*mobileFileRepo)(nil) var _ repository.WritableRepository = (*mobileFileRepo)(nil) type mobileFileRepo struct { - driver *mobileDriver } func (m *mobileFileRepo) CanList(u fyne.URI) (bool, error) { From 873bdf8aae92af9f0abbacee4908fe1f99ec008f Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 22 Mar 2021 22:02:32 +0000 Subject: [PATCH 39/62] Android storage root was incorrect --- app/app_mobile_and.go | 3 ++- app/preferences_android.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/app_mobile_and.go b/app/app_mobile_and.go index e68aa82a23..29d9657e38 100644 --- a/app/app_mobile_and.go +++ b/app/app_mobile_and.go @@ -16,6 +16,7 @@ import ( "log" "net/url" "os" + "path/filepath" "unsafe" mobileApp "github.com/fyne-io/mobile/app" @@ -54,5 +55,5 @@ func rootConfigDir() string { return "/data/data" // probably won't work, but we can't make a better guess } - return filesDir + return filepath.Join(filesDir, "fyne") } diff --git a/app/preferences_android.go b/app/preferences_android.go index 7118f5f698..663abd28bb 100644 --- a/app/preferences_android.go +++ b/app/preferences_android.go @@ -7,12 +7,12 @@ import "path/filepath" // storagePath returns the location of the settings storage func (p *preferences) storagePath() string { // we have no global storage, use app global instead - rootConfigDir looks up in app_mobile_and.go - return filepath.Join(rootConfigDir(), "storage", "preferences.json") + return filepath.Join(p.app.storageRoot(), "preferences.json") } // storageRoot returns the location of the app storage func (a *fyneApp) storageRoot() string { - return filepath.Join(rootConfigDir(), a.uniqueID) + return rootConfigDir() // we are in a sandbox, so no app ID added to this path } func (p *preferences) watch() { From 807d4e7c0db610e215fc0734b3c59621c3247be6 Mon Sep 17 00:00:00 2001 From: Christophe Meessen Date: Fri, 26 Feb 2021 10:44:58 +0100 Subject: [PATCH 40/62] correct error in fyne.io example --- fyne.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fyne.go b/fyne.go index 36cb43972a..899421e465 100644 --- a/fyne.go +++ b/fyne.go @@ -9,13 +9,14 @@ // // import "fyne.io/fyne/v2/app" // import "fyne.io/fyne/v2/widget" +// import "fyne.io/fyne/v2/container" // // func main() { // a := app.New() // w := a.NewWindow("Hello") // // hello := widget.NewLabel("Hello Fyne!") -// w.SetContent(widget.NewVBox( +// w.SetContent(container.NewVBox( // hello, // widget.NewButton("Hi!", func() { // hello.SetText("Welcome :)") From 9c30251e308018607f43fde2fb5788372985effd Mon Sep 17 00:00:00 2001 From: Christophe Meessen Date: Fri, 26 Feb 2021 12:45:56 +0100 Subject: [PATCH 41/62] set import in alphabetic order --- fyne.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fyne.go b/fyne.go index 899421e465..0e37b34941 100644 --- a/fyne.go +++ b/fyne.go @@ -8,8 +8,8 @@ // package main // // import "fyne.io/fyne/v2/app" -// import "fyne.io/fyne/v2/widget" // import "fyne.io/fyne/v2/container" +// import "fyne.io/fyne/v2/widget" // // func main() { // a := app.New() From ce0f137e57fa27c4d7282fcf7ac2df73c0969e66 Mon Sep 17 00:00:00 2001 From: Pablo Fuentes Date: Mon, 1 Mar 2021 12:13:21 -0500 Subject: [PATCH 42/62] Upgrade GLFW from upstream May fix #73 --- go.mod | 4 +- go.sum | 4 +- .../go-gl/glfw/v3.3/glfw/GLFW_C_REVISION.txt | 2 +- .../github.com/go-gl/glfw/v3.3/glfw/build.go | 8 +- .../go-gl/glfw/v3.3/glfw/glfw/deps/linmath.h | 4 +- .../glfw/v3.3/glfw/glfw/include/GLFW/glfw3.h | 55 ++- .../glfw/v3.3/glfw/glfw/src/cocoa_init.m | 12 +- .../glfw/v3.3/glfw/glfw/src/cocoa_monitor.m | 99 +++-- .../glfw/v3.3/glfw/glfw/src/cocoa_platform.h | 3 +- .../glfw/v3.3/glfw/glfw/src/cocoa_window.m | 35 +- .../glfw/v3.3/glfw/glfw/src/egl_context.c | 16 +- .../go-gl/glfw/v3.3/glfw/glfw/src/internal.h | 36 +- .../glfw/v3.3/glfw/glfw/src/nsgl_context.m | 2 +- ...idle-inhibit-unstable-v1-client-protocol.c | 10 +- ...idle-inhibit-unstable-v1-client-protocol.h | 2 +- ...-constraints-unstable-v1-client-protocol.c | 28 +- ...-constraints-unstable-v1-client-protocol.h | 2 +- ...tive-pointer-unstable-v1-client-protocol.c | 12 +- ...tive-pointer-unstable-v1-client-protocol.h | 2 +- .../src/wayland-viewporter-client-protocol.c | 2 +- .../src/wayland-viewporter-client-protocol.h | 2 +- ...g-decoration-unstable-v1-client-protocol.c | 2 +- ...g-decoration-unstable-v1-client-protocol.h | 2 +- .../src/wayland-xdg-shell-client-protocol.c | 2 +- .../src/wayland-xdg-shell-client-protocol.h | 2 +- .../glfw/v3.3/glfw/glfw/src/wgl_context.h | 4 - .../glfw/v3.3/glfw/glfw/src/win32_init.c | 2 + .../glfw/v3.3/glfw/glfw/src/win32_joystick.c | 10 +- .../glfw/v3.3/glfw/glfw/src/win32_monitor.c | 2 + .../glfw/v3.3/glfw/glfw/src/win32_platform.h | 13 +- .../glfw/v3.3/glfw/glfw/src/win32_window.c | 131 ++++--- .../go-gl/glfw/v3.3/glfw/glfw/src/window.c | 1 + .../go-gl/glfw/v3.3/glfw/glfw/src/wl_init.c | 4 +- .../glfw/v3.3/glfw/glfw/src/wl_monitor.c | 8 + .../glfw/v3.3/glfw/glfw/src/wl_platform.h | 2 +- .../go-gl/glfw/v3.3/glfw/glfw/src/wl_window.c | 31 +- .../go-gl/glfw/v3.3/glfw/glfw/src/x11_init.c | 351 ++++++++++++------ .../glfw/v3.3/glfw/glfw/src/x11_monitor.c | 2 +- .../glfw/v3.3/glfw/glfw/src/x11_platform.h | 5 +- .../glfw/v3.3/glfw/glfw/src/x11_window.c | 50 ++- .../go-gl/glfw/v3.3/glfw/glfw_tree_rebuild.go | 10 + .../github.com/go-gl/glfw/v3.3/glfw/util.go | 19 +- vendor/modules.txt | 2 +- 43 files changed, 631 insertions(+), 364 deletions(-) create mode 100644 vendor/github.com/go-gl/glfw/v3.3/glfw/glfw_tree_rebuild.go diff --git a/go.mod b/go.mod index b3f2d40bfd..7648888d0f 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/fsnotify/fsnotify v1.4.9 github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 - github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48 github.com/godbus/dbus/v5 v5.0.3 github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526 @@ -29,5 +29,3 @@ require ( gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v2 v2.2.8 // indirect ) - -replace github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 => github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d diff --git a/go.sum b/go.sum index a0be01e87a..74eb47698a 100644 --- a/go.sum +++ b/go.sum @@ -10,12 +10,12 @@ github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8 github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d h1:WfVxpuVm+5Gr3ipAoWrxV8lJFYkaBWoEwFRrWThWRSU= -github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f h1:rguJ/t99j/6zRSFzsBKlsmmyl+vOvCeTJ+2uTBvuXFI= github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48 h1:QrUfZrT8n72FUuiABt4tbu8PwDnOPAbnj3Mql1UhdRI= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/GLFW_C_REVISION.txt b/vendor/github.com/go-gl/glfw/v3.3/glfw/GLFW_C_REVISION.txt index 6a8efe19df..2630b14ca1 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/GLFW_C_REVISION.txt +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/GLFW_C_REVISION.txt @@ -1 +1 @@ -0a49ef0a00baa3ab520ddc452f0e3b1e099c5589 +746cdea490a70dd6747272a171b19e5ec1fb9900 diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/build.go b/vendor/github.com/go-gl/glfw/v3.3/glfw/build.go index acf4d926e3..b60c631dbd 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/build.go +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/build.go @@ -7,8 +7,10 @@ package glfw #cgo windows CFLAGS: -D_GLFW_WIN32 -Iglfw/deps/mingw // Linker Options: -#cgo windows LDFLAGS: -lopengl32 -lgdi32 +#cgo windows LDFLAGS: -lgdi32 +#cgo !gles2,windows LDFLAGS: -lopengl32 +#cgo gles2,windows LDFLAGS: -lGLESv2 // Darwin Build Tags // ---------------- @@ -16,8 +18,10 @@ package glfw #cgo darwin CFLAGS: -D_GLFW_COCOA -Wno-deprecated-declarations // Linker Options: -#cgo darwin LDFLAGS: -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo +#cgo darwin LDFLAGS: -framework Cocoa -framework IOKit -framework CoreVideo +#cgo !gles2,darwin LDFLAGS: -framework OpenGL +#cgo gles2,darwin LDFLAGS: -lGLESv2 // Linux Build Tags // ---------------- diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/deps/linmath.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/deps/linmath.h index 9c2e2a0abf..0ab7a414b4 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/deps/linmath.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/deps/linmath.h @@ -237,9 +237,9 @@ static inline void mat4x4_rotate_Y(mat4x4 Q, mat4x4 M, float angle) float s = sinf(angle); float c = cosf(angle); mat4x4 R = { - { c, 0.f, s, 0.f}, + { c, 0.f, -s, 0.f}, { 0.f, 1.f, 0.f, 0.f}, - { -s, 0.f, c, 0.f}, + { s, 0.f, c, 0.f}, { 0.f, 0.f, 0.f, 1.f} }; mat4x4_mul(Q, M, R); diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/include/GLFW/glfw3.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/include/GLFW/glfw3.h index 66dff64905..35bbf0755f 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/include/GLFW/glfw3.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/include/GLFW/glfw3.h @@ -52,7 +52,7 @@ extern "C" { * This is the reference documentation for OpenGL and OpenGL ES context related * functions. For more task-oriented information, see the @ref context_guide. */ -/*! @defgroup vulkan Vulkan reference +/*! @defgroup vulkan Vulkan support reference * @brief Functions and types related to Vulkan. * * This is the reference documentation for Vulkan related functions and types. @@ -193,7 +193,38 @@ extern "C" { #endif /*__APPLE__*/ -#elif !defined(GLFW_INCLUDE_NONE) +#elif defined(GLFW_INCLUDE_GLU) + + #if defined(__APPLE__) + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #else /*__APPLE__*/ + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) && \ + !defined(__gl_h_) && \ + !defined(__gles1_gl_h_) && \ + !defined(__gles2_gl2_h_) && \ + !defined(__gles2_gl3_h_) && \ + !defined(__gles2_gl31_h_) && \ + !defined(__gles2_gl32_h_) && \ + !defined(__gl_glcorearb_h_) && \ + !defined(__gl2_h_) /*legacy*/ && \ + !defined(__gl3_h_) /*legacy*/ && \ + !defined(__gl31_h_) /*legacy*/ && \ + !defined(__gl32_h_) /*legacy*/ && \ + !defined(__glcorearb_h_) /*legacy*/ && \ + !defined(__GL_H__) /*non-standard*/ && \ + !defined(__gltypes_h_) /*non-standard*/ && \ + !defined(__glee_h_) /*non-standard*/ #if defined(__APPLE__) @@ -201,9 +232,6 @@ extern "C" { #define GL_GLEXT_LEGACY #endif #include - #if defined(GLFW_INCLUDE_GLU) - #include - #endif #else /*__APPLE__*/ @@ -211,9 +239,6 @@ extern "C" { #if defined(GLFW_INCLUDE_GLEXT) #include #endif - #if defined(GLFW_INCLUDE_GLU) - #include - #endif #endif /*__APPLE__*/ @@ -270,7 +295,7 @@ extern "C" { * API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 2 +#define GLFW_VERSION_REVISION 3 /*! @} */ /*! @brief One. @@ -949,9 +974,9 @@ extern "C" { * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). */ #define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 -/*! @brief OpenGL debug context hint and attribute. +/*! @brief Debug mode context hint and attribute. * - * OpenGL debug context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and + * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). */ #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 @@ -1328,7 +1353,7 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * @endcode * * @param[in] window The window that was maximized or restored. - * @param[in] iconified `GLFW_TRUE` if the window was maximized, or + * @param[in] maximized `GLFW_TRUE` if the window was maximized, or * `GLFW_FALSE` if it was restored. * * @sa @ref window_maximize @@ -1753,6 +1778,10 @@ typedef struct GLFWgamepadstate * bundle, if present. This can be disabled with the @ref * GLFW_COCOA_CHDIR_RESOURCES init hint. * + * @remark @x11 This function will set the `LC_CTYPE` category of the + * application locale according to the current environment if that category is + * still "C". This is because the "C" locale breaks Unicode text input. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init @@ -1776,6 +1805,8 @@ GLFWAPI int glfwInit(void); * call this function, as it is called by @ref glfwInit before it returns * failure. * + * This function has no effect if GLFW is not initialized. + * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * * @remark This function may be called before @ref glfwInit. diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_init.m b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_init.m index 4b8e74b624..209639e270 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_init.m +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_init.m @@ -439,13 +439,6 @@ - (void)applicationWillFinishLaunching:(NSNotification *)notification } else createMenuBar(); - - // Fix for issue #1648: menubar not clickable on macOS Catalina until - // it lost and regained focus - dispatch_async(dispatch_get_main_queue(), ^{ - // In case we are unbundled, make us a proper UI application - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - }); } } @@ -453,6 +446,11 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification { _glfw.ns.finishedLaunching = GLFW_TRUE; _glfwPlatformPostEmptyEvent(); + + // In case we are unbundled, make us a proper UI application + if (_glfw.hints.init.ns.menubar) + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [NSApp stop:nil]; } diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_monitor.m b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_monitor.m index 8ef94a3b0f..55638cf0d9 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_monitor.m +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_monitor.m @@ -39,8 +39,21 @@ // Get the name of the specified display, or NULL // -static char* getDisplayName(CGDirectDisplayID displayID) +static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen) { + // IOKit doesn't work on Apple Silicon anymore + // Luckily, 10.15 introduced -[NSScreen localizedName]. + // Use it if available, and fall back to IOKit otherwise. + if (screen) + { + if ([screen respondsToSelector:@selector(localizedName)]) + { + NSString* name = [screen valueForKey:@"localizedName"]; + if (name) + return _glfw_strdup([name UTF8String]); + } + } + io_iterator_t it; io_service_t service; CFDictionaryRef info; @@ -209,31 +222,6 @@ static void endFadeReservation(CGDisplayFadeReservationToken token) } } -// Finds and caches the NSScreen corresponding to the specified monitor -// -static GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor) -{ - if (monitor->ns.screen) - return GLFW_TRUE; - - for (NSScreen* screen in [NSScreen screens]) - { - NSNumber* displayID = [screen deviceDescription][@"NSScreenNumber"]; - - // HACK: Compare unit numbers instead of display IDs to work around - // display replacement on machines with automatic graphics - // switching - if (monitor->ns.unitNumber == CGDisplayUnitNumber([displayID unsignedIntValue])) - { - monitor->ns.screen = screen; - return GLFW_TRUE; - } - } - - _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find a screen for monitor"); - return GLFW_FALSE; -} - // Returns the display refresh rate queried from the I/O registry // static double getFallbackRefreshRate(CGDirectDisplayID displayID) @@ -277,14 +265,20 @@ static double getFallbackRefreshRate(CGDirectDisplayID displayID) CFSTR("IOFBCurrentPixelCount"), kCFAllocatorDefault, kNilOptions); - if (!clockRef || !countRef) - break; uint32_t clock = 0, count = 0; - CFNumberGetValue(clockRef, kCFNumberIntType, &clock); - CFNumberGetValue(countRef, kCFNumberIntType, &count); - CFRelease(clockRef); - CFRelease(countRef); + + if (clockRef) + { + CFNumberGetValue(clockRef, kCFNumberIntType, &clock); + CFRelease(clockRef); + } + + if (countRef) + { + CFNumberGetValue(countRef, kCFNumberIntType, &count); + CFRelease(countRef); + } if (clock > 0 && count > 0) refreshRate = clock / (double) count; @@ -328,27 +322,46 @@ void _glfwPollMonitorsNS(void) if (CGDisplayIsAsleep(displays[i])) continue; + const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]); + NSScreen* screen = nil; + + for (screen in [NSScreen screens]) + { + NSNumber* screenNumber = [screen deviceDescription][@"NSScreenNumber"]; + + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + if (CGDisplayUnitNumber([screenNumber unsignedIntValue]) == unitNumber) + break; + } + // HACK: Compare unit numbers instead of display IDs to work around // display replacement on machines with automatic graphics // switching - const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]); - for (uint32_t j = 0; j < disconnectedCount; j++) + uint32_t j; + for (j = 0; j < disconnectedCount; j++) { if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber) { + disconnected[j]->ns.screen = screen; disconnected[j] = NULL; break; } } + if (j < disconnectedCount) + continue; + const CGSize size = CGDisplayScreenSize(displays[i]); - char* name = getDisplayName(displays[i]); + char* name = getMonitorName(displays[i], screen); if (!name) name = _glfw_strdup("Unknown"); _GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height); monitor->ns.displayID = displays[i]; monitor->ns.unitNumber = unitNumber; + monitor->ns.screen = screen; free(name); @@ -457,8 +470,11 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, { @autoreleasepool { - if (!refreshMonitorScreen(monitor)) - return; + if (!monitor->ns.screen) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Cannot query content scale without screen"); + } const NSRect points = [monitor->ns.screen frame]; const NSRect pixels = [monitor->ns.screen convertRectToBacking:points]; @@ -477,8 +493,11 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, { @autoreleasepool { - if (!refreshMonitorScreen(monitor)) - return; + if (!monitor->ns.screen) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Cannot query workarea without screen"); + } const NSRect frameRect = [monitor->ns.screen visibleFrame]; @@ -521,7 +540,7 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, } // Skip duplicate modes - if (i < *count) + if (j < *count) continue; (*count)++; diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_platform.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_platform.h index 15c2169619..05c23b70f9 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_platform.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_platform.h @@ -92,7 +92,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMeta #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) -#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.view) +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.layer) #define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns @@ -121,6 +121,7 @@ typedef struct _GLFWwindowNS id layer; GLFWbool maximized; + GLFWbool occluded; GLFWbool retina; // Cached window properties to filter out duplicate events diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_window.m b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_window.m index 129e975e55..9fa72a6b7e 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_window.m +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/cocoa_window.m @@ -322,6 +322,14 @@ - (void)windowDidResignKey:(NSNotification *)notification _glfwInputWindowFocus(window, GLFW_FALSE); } +- (void)windowDidChangeOcclusionState:(NSNotification* )notification +{ + if ([window->ns.object occlusionState] & NSWindowOcclusionStateVisible) + window->ns.occluded = GLFW_FALSE; + else + window->ns.occluded = GLFW_TRUE; +} + @end @@ -723,14 +731,24 @@ - (void)insertText:(id)string replacementRange:(NSRange)replacementRange else characters = (NSString*) string; - const NSUInteger length = [characters length]; - for (NSUInteger i = 0; i < length; i++) + NSRange range = NSMakeRange(0, [characters length]); + while (range.length) { - const unichar codepoint = [characters characterAtIndex:i]; - if ((codepoint & 0xff00) == 0xf700) - continue; + uint32_t codepoint = 0; + + if ([characters getBytes:&codepoint + maxLength:sizeof(codepoint) + usedLength:NULL + encoding:NSUTF32StringEncoding + options:0 + range:range + remainingRange:&range]) + { + if (codepoint >= 0xf700 && codepoint <= 0xf7ff) + continue; - _glfwInputChar(window, codepoint, mods, plain); + _glfwInputChar(window, codepoint, mods, plain); + } } } @@ -901,6 +919,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, } else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { + // EGL implementation on macOS use CALayer* EGLNativeWindowType so we + // need to get the layer for EGL window surface creation. + [window->ns.view setWantsLayer:YES]; + window->ns.layer = [window->ns.view layer]; + if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/egl_context.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/egl_context.c index e458bfb87e..6288fb7ce4 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/egl_context.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/egl_context.c @@ -588,18 +588,16 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, } // Set up attributes for surface creation - { - int index = 0; - - if (fbconfig->sRGB) - { - if (_glfw.egl.KHR_gl_colorspace) - setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); - } + index = 0; - setAttrib(EGL_NONE, EGL_NONE); + if (fbconfig->sRGB) + { + if (_glfw.egl.KHR_gl_colorspace) + setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); } + setAttrib(EGL_NONE, EGL_NONE); + window->context.egl.surface = eglCreateWindowSurface(_glfw.egl.display, config, diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/internal.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/internal.h index 92b9497abb..91631c0620 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/internal.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/internal.h @@ -342,9 +342,9 @@ struct _GLFWcontext int robustness; int release; - PFNGLGETSTRINGIPROC GetStringi; + PFNGLGETSTRINGIPROC GetStringi; PFNGLGETINTEGERVPROC GetIntegerv; - PFNGLGETSTRINGPROC GetString; + PFNGLGETSTRINGPROC GetString; _GLFWmakecontextcurrentfun makeCurrent; _GLFWswapbuffersfun swapBuffers; @@ -396,23 +396,23 @@ struct _GLFWwindow _GLFWcontext context; struct { - GLFWwindowposfun pos; - GLFWwindowsizefun size; - GLFWwindowclosefun close; - GLFWwindowrefreshfun refresh; - GLFWwindowfocusfun focus; - GLFWwindowiconifyfun iconify; - GLFWwindowmaximizefun maximize; - GLFWframebuffersizefun fbsize; + GLFWwindowposfun pos; + GLFWwindowsizefun size; + GLFWwindowclosefun close; + GLFWwindowrefreshfun refresh; + GLFWwindowfocusfun focus; + GLFWwindowiconifyfun iconify; + GLFWwindowmaximizefun maximize; + GLFWframebuffersizefun fbsize; GLFWwindowcontentscalefun scale; - GLFWmousebuttonfun mouseButton; - GLFWcursorposfun cursorPos; - GLFWcursorenterfun cursorEnter; - GLFWscrollfun scroll; - GLFWkeyfun key; - GLFWcharfun character; - GLFWcharmodsfun charmods; - GLFWdropfun drop; + GLFWmousebuttonfun mouseButton; + GLFWcursorposfun cursorPos; + GLFWcursorenterfun cursorEnter; + GLFWscrollfun scroll; + GLFWkeyfun key; + GLFWcharfun character; + GLFWcharmodsfun charmods; + GLFWdropfun drop; } callbacks; // This is defined in the window API's platform.h diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/nsgl_context.m b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/nsgl_context.m index 3bcfafcc7a..10286849ba 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/nsgl_context.m +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/nsgl_context.m @@ -51,7 +51,7 @@ static void swapBuffersNSGL(_GLFWwindow* window) // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to // windows with a non-visible occlusion state - if (!([window->ns.object occlusionState] & NSWindowOcclusionStateVisible)) + if (window->ns.occluded) { int interval = 0; [window->context.nsgl.object getValues:&interval diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c index 8ac3c12699..6a70a81d35 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ /* * Copyright © 2015 Samsung Electronics Co., Ltd @@ -40,14 +40,14 @@ extern const struct wl_interface wl_surface_interface; extern const struct wl_interface zwp_idle_inhibitor_v1_interface; -static const struct wl_interface *idle_inhibit_unstable_v1_wl_ii_types[] = { +static const struct wl_interface *idle_inhibit_unstable_v1_types[] = { &zwp_idle_inhibitor_v1_interface, &wl_surface_interface, }; static const struct wl_message zwp_idle_inhibit_manager_v1_requests[] = { - { "destroy", "", idle_inhibit_unstable_v1_wl_ii_types + 0 }, - { "create_inhibitor", "no", idle_inhibit_unstable_v1_wl_ii_types + 0 }, + { "destroy", "", idle_inhibit_unstable_v1_types + 0 }, + { "create_inhibitor", "no", idle_inhibit_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zwp_idle_inhibit_manager_v1_interface = { @@ -57,7 +57,7 @@ WL_PRIVATE const struct wl_interface zwp_idle_inhibit_manager_v1_interface = { }; static const struct wl_message zwp_idle_inhibitor_v1_requests[] = { - { "destroy", "", idle_inhibit_unstable_v1_wl_ii_types + 0 }, + { "destroy", "", idle_inhibit_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zwp_idle_inhibitor_v1_interface = { diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.h index 65ece1f4e7..7aa028e1b4 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ #ifndef IDLE_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H #define IDLE_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c index 3779b8c6ff..f97c52eb56 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ /* * Copyright © 2014 Jonas Ådahl @@ -44,7 +44,7 @@ extern const struct wl_interface wl_surface_interface; extern const struct wl_interface zwp_confined_pointer_v1_interface; extern const struct wl_interface zwp_locked_pointer_v1_interface; -static const struct wl_interface *pointer_constraints_unstable_v1_wl_pc_types[] = { +static const struct wl_interface *pointer_constraints_unstable_v1_types[] = { NULL, NULL, &zwp_locked_pointer_v1_interface, @@ -62,9 +62,9 @@ static const struct wl_interface *pointer_constraints_unstable_v1_wl_pc_types[] }; static const struct wl_message zwp_pointer_constraints_v1_requests[] = { - { "destroy", "", pointer_constraints_unstable_v1_wl_pc_types + 0 }, - { "lock_pointer", "noo?ou", pointer_constraints_unstable_v1_wl_pc_types + 2 }, - { "confine_pointer", "noo?ou", pointer_constraints_unstable_v1_wl_pc_types + 7 }, + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "lock_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 2 }, + { "confine_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 7 }, }; WL_PRIVATE const struct wl_interface zwp_pointer_constraints_v1_interface = { @@ -74,14 +74,14 @@ WL_PRIVATE const struct wl_interface zwp_pointer_constraints_v1_interface = { }; static const struct wl_message zwp_locked_pointer_v1_requests[] = { - { "destroy", "", pointer_constraints_unstable_v1_wl_pc_types + 0 }, - { "set_cursor_position_hint", "ff", pointer_constraints_unstable_v1_wl_pc_types + 0 }, - { "set_region", "?o", pointer_constraints_unstable_v1_wl_pc_types + 12 }, + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "set_cursor_position_hint", "ff", pointer_constraints_unstable_v1_types + 0 }, + { "set_region", "?o", pointer_constraints_unstable_v1_types + 12 }, }; static const struct wl_message zwp_locked_pointer_v1_events[] = { - { "locked", "", pointer_constraints_unstable_v1_wl_pc_types + 0 }, - { "unlocked", "", pointer_constraints_unstable_v1_wl_pc_types + 0 }, + { "locked", "", pointer_constraints_unstable_v1_types + 0 }, + { "unlocked", "", pointer_constraints_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zwp_locked_pointer_v1_interface = { @@ -91,13 +91,13 @@ WL_PRIVATE const struct wl_interface zwp_locked_pointer_v1_interface = { }; static const struct wl_message zwp_confined_pointer_v1_requests[] = { - { "destroy", "", pointer_constraints_unstable_v1_wl_pc_types + 0 }, - { "set_region", "?o", pointer_constraints_unstable_v1_wl_pc_types + 13 }, + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "set_region", "?o", pointer_constraints_unstable_v1_types + 13 }, }; static const struct wl_message zwp_confined_pointer_v1_events[] = { - { "confined", "", pointer_constraints_unstable_v1_wl_pc_types + 0 }, - { "unconfined", "", pointer_constraints_unstable_v1_wl_pc_types + 0 }, + { "confined", "", pointer_constraints_unstable_v1_types + 0 }, + { "unconfined", "", pointer_constraints_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zwp_confined_pointer_v1_interface = { diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h index f66f8d75dd..8ae3420296 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ #ifndef POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H #define POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c index de60756936..cb8946b43b 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ /* * Copyright © 2014 Jonas Ådahl @@ -41,7 +41,7 @@ extern const struct wl_interface wl_pointer_interface; extern const struct wl_interface zwp_relative_pointer_v1_interface; -static const struct wl_interface *relative_pointer_unstable_v1_wl_rp_types[] = { +static const struct wl_interface *relative_pointer_unstable_v1_types[] = { NULL, NULL, NULL, @@ -53,8 +53,8 @@ static const struct wl_interface *relative_pointer_unstable_v1_wl_rp_types[] = { }; static const struct wl_message zwp_relative_pointer_manager_v1_requests[] = { - { "destroy", "", relative_pointer_unstable_v1_wl_rp_types + 0 }, - { "get_relative_pointer", "no", relative_pointer_unstable_v1_wl_rp_types + 6 }, + { "destroy", "", relative_pointer_unstable_v1_types + 0 }, + { "get_relative_pointer", "no", relative_pointer_unstable_v1_types + 6 }, }; WL_PRIVATE const struct wl_interface zwp_relative_pointer_manager_v1_interface = { @@ -64,11 +64,11 @@ WL_PRIVATE const struct wl_interface zwp_relative_pointer_manager_v1_interface = }; static const struct wl_message zwp_relative_pointer_v1_requests[] = { - { "destroy", "", relative_pointer_unstable_v1_wl_rp_types + 0 }, + { "destroy", "", relative_pointer_unstable_v1_types + 0 }, }; static const struct wl_message zwp_relative_pointer_v1_events[] = { - { "relative_motion", "uuffff", relative_pointer_unstable_v1_wl_rp_types + 0 }, + { "relative_motion", "uuffff", relative_pointer_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zwp_relative_pointer_v1_interface = { diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h index 633d084ded..ead5e5d727 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ #ifndef RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H #define RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-viewporter-client-protocol.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-viewporter-client-protocol.c index cee474e442..9526e837be 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-viewporter-client-protocol.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-viewporter-client-protocol.c @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ /* * Copyright © 2013-2016 Collabora, Ltd. diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-viewporter-client-protocol.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-viewporter-client-protocol.h index 90a4407d80..bade25aa21 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-viewporter-client-protocol.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-viewporter-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ #ifndef VIEWPORTER_CLIENT_PROTOCOL_H #define VIEWPORTER_CLIENT_PROTOCOL_H diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-decoration-unstable-v1-client-protocol.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-decoration-unstable-v1-client-protocol.c index 1aadb23c3a..d8dbb2fd3e 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-decoration-unstable-v1-client-protocol.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-decoration-unstable-v1-client-protocol.c @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ /* * Copyright © 2018 Simon Ser diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-decoration-unstable-v1-client-protocol.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-decoration-unstable-v1-client-protocol.h index 88378542bc..6bd1bb3e77 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-decoration-unstable-v1-client-protocol.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-decoration-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ #ifndef XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H #define XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-shell-client-protocol.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-shell-client-protocol.c index 6bcc9fd165..9f8f0a5807 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-shell-client-protocol.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-shell-client-protocol.c @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ /* * Copyright © 2008-2013 Kristian Høgsberg diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-shell-client-protocol.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-shell-client-protocol.h index 14aa30527c..40baa88844 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-shell-client-protocol.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wayland-xdg-shell-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.18.0 */ +/* Generated by wayland-scanner */ #ifndef XDG_SHELL_CLIENT_PROTOCOL_H #define XDG_SHELL_CLIENT_PROTOCOL_H diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wgl_context.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wgl_context.h index d982118809..1603f15f36 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wgl_context.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wgl_context.h @@ -104,10 +104,6 @@ typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); #define wglMakeCurrent _glfw.wgl.MakeCurrent #define wglShareLists _glfw.wgl.ShareLists -#define _GLFW_RECREATION_NOT_NEEDED 0 -#define _GLFW_RECREATION_REQUIRED 1 -#define _GLFW_RECREATION_IMPOSSIBLE 2 - #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_init.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_init.c index b2aa0a3d51..a7a6be6acc 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_init.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_init.c @@ -143,6 +143,8 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); + _glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); } _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_joystick.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_joystick.c index 4ffc9fbf40..62ad7a5306 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_joystick.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_joystick.c @@ -356,7 +356,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - _GLFWjoystick* js = _glfw.joysticks + jid; + js = _glfw.joysticks + jid; if (js->present) { if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0) @@ -672,11 +672,11 @@ int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) }; // Screams of horror are appropriate at this point - int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); - if (state < 0 || state > 8) - state = 8; + int stateIndex = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); + if (stateIndex < 0 || stateIndex > 8) + stateIndex = 8; - _glfwInputJoystickHat(js, pi, states[state]); + _glfwInputJoystickHat(js, pi, states[stateIndex]); pi++; break; } diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_monitor.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_monitor.c index 4f5af36a4c..c8bae35965 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_monitor.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_monitor.c @@ -185,6 +185,8 @@ void _glfwPollMonitorsWin32(void) display.DeviceName) == 0) { disconnected[i] = NULL; + // handle may have changed, update + EnumDisplayMonitors(NULL, NULL, monitorCallback, (LPARAM) _glfw.monitors[i]); break; } } diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_platform.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_platform.h index be1dc54491..003b8a1466 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_platform.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_platform.h @@ -77,6 +77,9 @@ #ifndef WM_DWMCOMPOSITIONCHANGED #define WM_DWMCOMPOSITIONCHANGED 0x031E #endif +#ifndef WM_DWMCOLORIZATIONCOLORCHANGED + #define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 +#endif #ifndef WM_COPYGLOBALDATA #define WM_COPYGLOBALDATA 0x0049 #endif @@ -99,7 +102,7 @@ #define DISPLAY_DEVICE_ACTIVE 0x00000001 #endif #ifndef _WIN32_WINNT_WINBLUE - #define _WIN32_WINNT_WINBLUE 0x0602 + #define _WIN32_WINNT_WINBLUE 0x0603 #endif #ifndef _WIN32_WINNT_WIN8 #define _WIN32_WINNT_WIN8 0x0602 @@ -247,9 +250,11 @@ typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UIN typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); +typedef HRESULT (WINAPI * PFN_DwmGetColorizationColor)(DWORD*,BOOL*); #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush #define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow +#define DwmGetColorizationColor _glfw.win32.dwmapi.GetColorizationColor // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); @@ -316,8 +321,13 @@ typedef struct _GLFWwindowWin32 GLFWbool transparent; GLFWbool scaleToMonitor; + // Cached size used to filter out duplicate events + int width, height; + // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; + // The last recevied high surrogate when decoding pairs of UTF-16 messages + WCHAR highSurrogate; } _GLFWwindowWin32; @@ -373,6 +383,7 @@ typedef struct _GLFWlibraryWin32 PFN_DwmIsCompositionEnabled IsCompositionEnabled; PFN_DwmFlush Flush; PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; + PFN_DwmGetColorizationColor GetColorizationColor; } dwmapi; struct { diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_window.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_window.c index 0ce22ee7ad..d17b6da480 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_window.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/win32_window.c @@ -377,12 +377,17 @@ static void updateWindowStyles(const _GLFWwindow* window) // static void updateFramebufferTransparency(const _GLFWwindow* window) { - BOOL enabled; + BOOL composition, opaque; + DWORD color; if (!IsWindowsVistaOrGreater()) return; - if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) + if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition) + return; + + if (IsWindows8OrGreater() || + (SUCCEEDED(DwmGetColorizationColor(&color, &opaque)) && !opaque)) { HRGN region = CreateRectRgn(0, 0, -1, -1); DWM_BLURBEHIND bb = {0}; @@ -390,37 +395,18 @@ static void updateFramebufferTransparency(const _GLFWwindow* window) bb.hRgnBlur = region; bb.fEnable = TRUE; - if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb))) - { - // Decorated windows don't repaint the transparent background - // leaving a trail behind animations - // HACK: Making the window layered with a transparency color key - // seems to fix this. Normally, when specifying - // a transparency color key to be used when composing the - // layered window, all pixels painted by the window in this - // color will be transparent. That doesn't seem to be the - // case anymore, at least when used with blur behind window - // plus negative region. - LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - exStyle |= WS_EX_LAYERED; - SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); - - // Using a color key not equal to black to fix the trailing - // issue. When set to black, something is making the hit test - // not resize with the window frame. - SetLayeredWindowAttributes(window->win32.handle, - RGB(255, 0, 255), 255, LWA_COLORKEY); - } - + DwmEnableBlurBehindWindow(window->win32.handle, &bb); DeleteObject(region); } else { - LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); - exStyle &= ~WS_EX_LAYERED; - SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); - RedrawWindow(window->win32.handle, NULL, NULL, - RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); + // HACK: Disable framebuffer transparency on Windows 7 when the + // colorization color is opaque, because otherwise the window + // contents is blended additively with the previous frame instead + // of replacing it + DWM_BLURBEHIND bb = {0}; + bb.dwFlags = DWM_BB_ENABLE; + DwmEnableBlurBehindWindow(window->win32.handle, &bb); } } @@ -519,7 +505,17 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_NCCREATE: { if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) - EnableNonClientDpiScaling(hWnd); + { + const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam; + const _GLFWwndconfig* wndconfig = cs->lpCreateParams; + + // On per-monitor DPI aware V1 systems, only enable + // non-client scaling for windows that scale the client area + // We need WM_GETDPISCALEDSIZE from V2 to keep the client + // area static when the non-client area is scaled + if (wndconfig && wndconfig->scaleToMonitor) + EnableNonClientDpiScaling(hWnd); + } break; } @@ -645,11 +641,35 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_CHAR: case WM_SYSCHAR: - case WM_UNICHAR: { - const GLFWbool plain = (uMsg != WM_SYSCHAR); + if (wParam >= 0xd800 && wParam <= 0xdbff) + window->win32.highSurrogate = (WCHAR) wParam; + else + { + unsigned int codepoint = 0; + + if (wParam >= 0xdc00 && wParam <= 0xdfff) + { + if (window->win32.highSurrogate) + { + codepoint += (window->win32.highSurrogate - 0xd800) << 10; + codepoint += (WCHAR) wParam - 0xdc00; + codepoint += 0x10000; + } + } + else + codepoint = (WCHAR) wParam; + + window->win32.highSurrogate = 0; + _glfwInputChar(window, codepoint, getKeyMods(), uMsg != WM_SYSCHAR); + } - if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR) + return 0; + } + + case WM_UNICHAR: + { + if (wParam == UNICODE_NOCHAR) { // WM_UNICHAR is not sent by Windows, but is sent by some // third-party input method engine @@ -657,7 +677,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return TRUE; } - _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain); + _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), GLFW_TRUE); return 0; } @@ -944,6 +964,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_SIZE: { + const int width = LOWORD(lParam); + const int height = HIWORD(lParam); const GLFWbool iconified = wParam == SIZE_MINIMIZED; const GLFWbool maximized = wParam == SIZE_MAXIMIZED || (window->win32.maximized && @@ -958,8 +980,14 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (window->win32.maximized != maximized) _glfwInputWindowMaximize(window, maximized); - _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam)); - _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam)); + if (width != window->win32.width || height != window->win32.height) + { + window->win32.width = width; + window->win32.height = height; + + _glfwInputFramebufferSize(window, width, height); + _glfwInputWindowSize(window, width, height); + } if (window->monitor && window->win32.iconified != iconified) { @@ -1073,6 +1101,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, } case WM_DWMCOMPOSITIONCHANGED: + case WM_DWMCOLORIZATIONCOLORCHANGED: { if (window->win32.transparent) updateFramebufferTransparency(window); @@ -1112,9 +1141,11 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; - // Only apply the suggested size if the OS is new enough to have - // sent a WM_GETDPISCALEDSIZE before this - if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + // Resize windowed mode windows that either permit rescaling or that + // need it to compensate for non-client area scaling + if (!window->monitor && + (window->win32.scaleToMonitor || + _glfwIsWindows10CreatorsUpdateOrGreaterWin32())) { RECT* suggested = (RECT*) lParam; SetWindowPos(window->win32.handle, HWND_TOP, @@ -1229,7 +1260,7 @@ static int createNativeWindow(_GLFWwindow* window, NULL, // No parent window NULL, // No window menu GetModuleHandleW(NULL), - NULL); + (LPVOID) wndconfig); free(wideTitle); @@ -1296,6 +1327,8 @@ static int createNativeWindow(_GLFWwindow* window, window->win32.transparent = GLFW_TRUE; } + _glfwPlatformGetWindowSize(window, &window->win32.width, &window->win32.height); + return GLFW_TRUE; } @@ -1797,7 +1830,8 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window) int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { - BOOL enabled; + BOOL composition, opaque; + DWORD color; if (!window->win32.transparent) return GLFW_FALSE; @@ -1805,7 +1839,20 @@ int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) if (!IsWindowsVistaOrGreater()) return GLFW_FALSE; - return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled; + if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition) + return GLFW_FALSE; + + if (!IsWindows8OrGreater()) + { + // HACK: Disable framebuffer transparency on Windows 7 when the + // colorization color is opaque, because otherwise the window + // contents is blended additively with the previous frame instead + // of replacing it + if (FAILED(DwmGetColorizationColor(&color, &opaque)) || opaque) + return GLFW_FALSE; + } + + return GLFW_TRUE; } void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/window.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/window.c index 4b356b2167..44de03bbca 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/window.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/window.c @@ -1099,3 +1099,4 @@ GLFWAPI void glfwPostEmptyEvent(void) _GLFW_REQUIRE_INIT(); _glfwPlatformPostEmptyEvent(); } + diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_init.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_init.c index 97b53673f1..49e7cc522a 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_init.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_init.c @@ -347,9 +347,9 @@ static void pointerHandleAxis(void* data, axis == WL_POINTER_AXIS_VERTICAL_SCROLL); if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) - x = wl_fixed_to_double(value) * scrollFactor; + x = -wl_fixed_to_double(value) * scrollFactor; else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) - y = wl_fixed_to_double(value) * scrollFactor; + y = -wl_fixed_to_double(value) * scrollFactor; _glfwInputScroll(window, x, y); } diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_monitor.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_monitor.c index 48b25b9ff6..a7b05c60da 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_monitor.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_monitor.c @@ -88,6 +88,14 @@ static void outputHandleDone(void* data, struct wl_output* output) { struct _GLFWmonitor *monitor = data; + if (monitor->widthMM <= 0 || monitor->heightMM <= 0) + { + // If Wayland does not provide a physical size, assume the default 96 DPI + const GLFWvidmode* mode = &monitor->modes[monitor->wl.currentMode]; + monitor->widthMM = (int) (mode->width * 25.4f / 96.f); + monitor->heightMM = (int) (mode->height * 25.4f / 96.f); + } + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_platform.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_platform.h index 0324e67ac9..41a7fdfa7a 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_platform.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_platform.h @@ -57,7 +57,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #include "osmesa_context.h" #include "wayland-xdg-shell-client-protocol.h" -#include "wayland-xdg-decoration-unstable-v1-client-protocol.h" +#include "wayland-xdg-decoration-client-protocol.h" #include "wayland-viewporter-client-protocol.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_window.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_window.c index a90257189c..d10861cd84 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_window.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/wl_window.c @@ -208,8 +208,8 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) if (fd < 0) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Creating a buffer file for %d B failed: %m", - length); + "Wayland: Creating a buffer file for %d B failed: %s", + length, strerror(errno)); return NULL; } @@ -217,7 +217,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) if (data == MAP_FAILED) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: mmap failed: %m"); + "Wayland: mmap failed: %s", strerror(errno)); close(fd); return NULL; } @@ -312,10 +312,10 @@ static void createDecorations(_GLFWwindow* window) static void destroyDecoration(_GLFWdecorationWayland* decoration) { - if (decoration->surface) - wl_surface_destroy(decoration->surface); if (decoration->subsurface) wl_subsurface_destroy(decoration->subsurface); + if (decoration->surface) + wl_surface_destroy(decoration->surface); if (decoration->viewport) wp_viewport_destroy(decoration->viewport); decoration->surface = NULL; @@ -870,10 +870,17 @@ static void handleEvents(int timeout) if (read_ret != 8) return; - for (i = 0; i < repeats; ++i) - _glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey, - _glfw.wl.keyboardLastScancode, GLFW_REPEAT, - _glfw.wl.xkb.modifiers); + if (_glfw.wl.keyboardFocus) + { + for (i = 0; i < repeats; ++i) + { + _glfwInputKey(_glfw.wl.keyboardFocus, + _glfw.wl.keyboardLastKey, + _glfw.wl.keyboardLastScancode, + GLFW_REPEAT, + _glfw.wl.xkb.modifiers); + } + } } if (fds[2].revents & POLLIN) @@ -1111,8 +1118,10 @@ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { _glfwPlatformGetWindowSize(window, width, height); - *width *= window->wl.scale; - *height *= window->wl.scale; + if (width) + *width *= window->wl.scale; + if (height) + *height *= window->wl.scale; } void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_init.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_init.c index 2f220ec508..87b3eb781b 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_init.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_init.c @@ -38,24 +38,15 @@ #include -// Translate an X11 key code to a GLFW key code. +// Translate the X11 KeySyms for a key to a GLFW key code +// NOTE: This is only used as a fallback, in case the XKB method fails +// It is layout-dependent and will fail partially on most non-US layouts // -static int translateKeyCode(int scancode) +static int translateKeySyms(const KeySym* keysyms, int width) { - int keySym; - - // Valid key code range is [8,255], according to the Xlib manual - if (scancode < 8 || scancode > 255) - return GLFW_KEY_UNKNOWN; - - if (_glfw.x11.xkb.available) + if (width > 1) { - // Try secondary keysym, for numeric keypad keys - // Note: This way we always force "NumLock = ON", which is intentional - // since the returned key code should correspond to a physical - // location. - keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 1); - switch (keySym) + switch (keysyms[1]) { case XK_KP_0: return GLFW_KEY_KP_0; case XK_KP_1: return GLFW_KEY_KP_1; @@ -73,22 +64,9 @@ static int translateKeyCode(int scancode) case XK_KP_Enter: return GLFW_KEY_KP_ENTER; default: break; } - - // Now try primary keysym for function keys (non-printable keys) - // These should not depend on the current keyboard layout - keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 0); - } - else - { - int dummy; - KeySym* keySyms; - - keySyms = XGetKeyboardMapping(_glfw.x11.display, scancode, 1, &dummy); - keySym = keySyms[0]; - XFree(keySyms); } - switch (keySym) + switch (keysyms[0]) { case XK_Escape: return GLFW_KEY_ESCAPE; case XK_Tab: return GLFW_KEY_TAB; @@ -232,7 +210,7 @@ static int translateKeyCode(int scancode) // static void createKeyTables(void) { - int scancode, key; + int scancode, scancodeMin, scancodeMax; memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); @@ -242,89 +220,217 @@ static void createKeyTables(void) // Use XKB to determine physical key locations independently of the // current keyboard layout - char name[XkbKeyNameLength + 1]; XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); - XkbGetNames(_glfw.x11.display, XkbKeyNamesMask, desc); + XkbGetNames(_glfw.x11.display, XkbKeyNamesMask | XkbKeyAliasesMask, desc); + + scancodeMin = desc->min_key_code; + scancodeMax = desc->max_key_code; + + const struct + { + int key; + char* name; + } keymap[] = + { + { GLFW_KEY_GRAVE_ACCENT, "TLDE" }, + { GLFW_KEY_1, "AE01" }, + { GLFW_KEY_2, "AE02" }, + { GLFW_KEY_3, "AE03" }, + { GLFW_KEY_4, "AE04" }, + { GLFW_KEY_5, "AE05" }, + { GLFW_KEY_6, "AE06" }, + { GLFW_KEY_7, "AE07" }, + { GLFW_KEY_8, "AE08" }, + { GLFW_KEY_9, "AE09" }, + { GLFW_KEY_0, "AE10" }, + { GLFW_KEY_MINUS, "AE11" }, + { GLFW_KEY_EQUAL, "AE12" }, + { GLFW_KEY_Q, "AD01" }, + { GLFW_KEY_W, "AD02" }, + { GLFW_KEY_E, "AD03" }, + { GLFW_KEY_R, "AD04" }, + { GLFW_KEY_T, "AD05" }, + { GLFW_KEY_Y, "AD06" }, + { GLFW_KEY_U, "AD07" }, + { GLFW_KEY_I, "AD08" }, + { GLFW_KEY_O, "AD09" }, + { GLFW_KEY_P, "AD10" }, + { GLFW_KEY_LEFT_BRACKET, "AD11" }, + { GLFW_KEY_RIGHT_BRACKET, "AD12" }, + { GLFW_KEY_A, "AC01" }, + { GLFW_KEY_S, "AC02" }, + { GLFW_KEY_D, "AC03" }, + { GLFW_KEY_F, "AC04" }, + { GLFW_KEY_G, "AC05" }, + { GLFW_KEY_H, "AC06" }, + { GLFW_KEY_J, "AC07" }, + { GLFW_KEY_K, "AC08" }, + { GLFW_KEY_L, "AC09" }, + { GLFW_KEY_SEMICOLON, "AC10" }, + { GLFW_KEY_APOSTROPHE, "AC11" }, + { GLFW_KEY_Z, "AB01" }, + { GLFW_KEY_X, "AB02" }, + { GLFW_KEY_C, "AB03" }, + { GLFW_KEY_V, "AB04" }, + { GLFW_KEY_B, "AB05" }, + { GLFW_KEY_N, "AB06" }, + { GLFW_KEY_M, "AB07" }, + { GLFW_KEY_COMMA, "AB08" }, + { GLFW_KEY_PERIOD, "AB09" }, + { GLFW_KEY_SLASH, "AB10" }, + { GLFW_KEY_BACKSLASH, "BKSL" }, + { GLFW_KEY_WORLD_1, "LSGT" }, + { GLFW_KEY_SPACE, "SPCE" }, + { GLFW_KEY_ESCAPE, "ESC" }, + { GLFW_KEY_ENTER, "RTRN" }, + { GLFW_KEY_TAB, "TAB" }, + { GLFW_KEY_BACKSPACE, "BKSP" }, + { GLFW_KEY_INSERT, "INS" }, + { GLFW_KEY_DELETE, "DELE" }, + { GLFW_KEY_RIGHT, "RGHT" }, + { GLFW_KEY_LEFT, "LEFT" }, + { GLFW_KEY_DOWN, "DOWN" }, + { GLFW_KEY_UP, "UP" }, + { GLFW_KEY_PAGE_UP, "PGUP" }, + { GLFW_KEY_PAGE_DOWN, "PGDN" }, + { GLFW_KEY_HOME, "HOME" }, + { GLFW_KEY_END, "END" }, + { GLFW_KEY_CAPS_LOCK, "CAPS" }, + { GLFW_KEY_SCROLL_LOCK, "SCLK" }, + { GLFW_KEY_NUM_LOCK, "NMLK" }, + { GLFW_KEY_PRINT_SCREEN, "PRSC" }, + { GLFW_KEY_PAUSE, "PAUS" }, + { GLFW_KEY_F1, "FK01" }, + { GLFW_KEY_F2, "FK02" }, + { GLFW_KEY_F3, "FK03" }, + { GLFW_KEY_F4, "FK04" }, + { GLFW_KEY_F5, "FK05" }, + { GLFW_KEY_F6, "FK06" }, + { GLFW_KEY_F7, "FK07" }, + { GLFW_KEY_F8, "FK08" }, + { GLFW_KEY_F9, "FK09" }, + { GLFW_KEY_F10, "FK10" }, + { GLFW_KEY_F11, "FK11" }, + { GLFW_KEY_F12, "FK12" }, + { GLFW_KEY_F13, "FK13" }, + { GLFW_KEY_F14, "FK14" }, + { GLFW_KEY_F15, "FK15" }, + { GLFW_KEY_F16, "FK16" }, + { GLFW_KEY_F17, "FK17" }, + { GLFW_KEY_F18, "FK18" }, + { GLFW_KEY_F19, "FK19" }, + { GLFW_KEY_F20, "FK20" }, + { GLFW_KEY_F21, "FK21" }, + { GLFW_KEY_F22, "FK22" }, + { GLFW_KEY_F23, "FK23" }, + { GLFW_KEY_F24, "FK24" }, + { GLFW_KEY_F25, "FK25" }, + { GLFW_KEY_KP_0, "KP0" }, + { GLFW_KEY_KP_1, "KP1" }, + { GLFW_KEY_KP_2, "KP2" }, + { GLFW_KEY_KP_3, "KP3" }, + { GLFW_KEY_KP_4, "KP4" }, + { GLFW_KEY_KP_5, "KP5" }, + { GLFW_KEY_KP_6, "KP6" }, + { GLFW_KEY_KP_7, "KP7" }, + { GLFW_KEY_KP_8, "KP8" }, + { GLFW_KEY_KP_9, "KP9" }, + { GLFW_KEY_KP_DECIMAL, "KPDL" }, + { GLFW_KEY_KP_DIVIDE, "KPDV" }, + { GLFW_KEY_KP_MULTIPLY, "KPMU" }, + { GLFW_KEY_KP_SUBTRACT, "KPSU" }, + { GLFW_KEY_KP_ADD, "KPAD" }, + { GLFW_KEY_KP_ENTER, "KPEN" }, + { GLFW_KEY_KP_EQUAL, "KPEQ" }, + { GLFW_KEY_LEFT_SHIFT, "LFSH" }, + { GLFW_KEY_LEFT_CONTROL, "LCTL" }, + { GLFW_KEY_LEFT_ALT, "LALT" }, + { GLFW_KEY_LEFT_SUPER, "LWIN" }, + { GLFW_KEY_RIGHT_SHIFT, "RTSH" }, + { GLFW_KEY_RIGHT_CONTROL, "RCTL" }, + { GLFW_KEY_RIGHT_ALT, "RALT" }, + { GLFW_KEY_RIGHT_ALT, "LVL3" }, + { GLFW_KEY_RIGHT_ALT, "MDSW" }, + { GLFW_KEY_RIGHT_SUPER, "RWIN" }, + { GLFW_KEY_MENU, "MENU" } + }; // Find the X11 key code -> GLFW key code mapping - for (scancode = desc->min_key_code; scancode <= desc->max_key_code; scancode++) + for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) { - memcpy(name, desc->names->keys[scancode].name, XkbKeyNameLength); - name[XkbKeyNameLength] = '\0'; - - // Map the key name to a GLFW key code. Note: We only map printable - // keys here, and we use the US keyboard layout. The rest of the - // keys (function keys) are mapped using traditional KeySym - // translations. - if (strcmp(name, "TLDE") == 0) key = GLFW_KEY_GRAVE_ACCENT; - else if (strcmp(name, "AE01") == 0) key = GLFW_KEY_1; - else if (strcmp(name, "AE02") == 0) key = GLFW_KEY_2; - else if (strcmp(name, "AE03") == 0) key = GLFW_KEY_3; - else if (strcmp(name, "AE04") == 0) key = GLFW_KEY_4; - else if (strcmp(name, "AE05") == 0) key = GLFW_KEY_5; - else if (strcmp(name, "AE06") == 0) key = GLFW_KEY_6; - else if (strcmp(name, "AE07") == 0) key = GLFW_KEY_7; - else if (strcmp(name, "AE08") == 0) key = GLFW_KEY_8; - else if (strcmp(name, "AE09") == 0) key = GLFW_KEY_9; - else if (strcmp(name, "AE10") == 0) key = GLFW_KEY_0; - else if (strcmp(name, "AE11") == 0) key = GLFW_KEY_MINUS; - else if (strcmp(name, "AE12") == 0) key = GLFW_KEY_EQUAL; - else if (strcmp(name, "AD01") == 0) key = GLFW_KEY_Q; - else if (strcmp(name, "AD02") == 0) key = GLFW_KEY_W; - else if (strcmp(name, "AD03") == 0) key = GLFW_KEY_E; - else if (strcmp(name, "AD04") == 0) key = GLFW_KEY_R; - else if (strcmp(name, "AD05") == 0) key = GLFW_KEY_T; - else if (strcmp(name, "AD06") == 0) key = GLFW_KEY_Y; - else if (strcmp(name, "AD07") == 0) key = GLFW_KEY_U; - else if (strcmp(name, "AD08") == 0) key = GLFW_KEY_I; - else if (strcmp(name, "AD09") == 0) key = GLFW_KEY_O; - else if (strcmp(name, "AD10") == 0) key = GLFW_KEY_P; - else if (strcmp(name, "AD11") == 0) key = GLFW_KEY_LEFT_BRACKET; - else if (strcmp(name, "AD12") == 0) key = GLFW_KEY_RIGHT_BRACKET; - else if (strcmp(name, "AC01") == 0) key = GLFW_KEY_A; - else if (strcmp(name, "AC02") == 0) key = GLFW_KEY_S; - else if (strcmp(name, "AC03") == 0) key = GLFW_KEY_D; - else if (strcmp(name, "AC04") == 0) key = GLFW_KEY_F; - else if (strcmp(name, "AC05") == 0) key = GLFW_KEY_G; - else if (strcmp(name, "AC06") == 0) key = GLFW_KEY_H; - else if (strcmp(name, "AC07") == 0) key = GLFW_KEY_J; - else if (strcmp(name, "AC08") == 0) key = GLFW_KEY_K; - else if (strcmp(name, "AC09") == 0) key = GLFW_KEY_L; - else if (strcmp(name, "AC10") == 0) key = GLFW_KEY_SEMICOLON; - else if (strcmp(name, "AC11") == 0) key = GLFW_KEY_APOSTROPHE; - else if (strcmp(name, "AB01") == 0) key = GLFW_KEY_Z; - else if (strcmp(name, "AB02") == 0) key = GLFW_KEY_X; - else if (strcmp(name, "AB03") == 0) key = GLFW_KEY_C; - else if (strcmp(name, "AB04") == 0) key = GLFW_KEY_V; - else if (strcmp(name, "AB05") == 0) key = GLFW_KEY_B; - else if (strcmp(name, "AB06") == 0) key = GLFW_KEY_N; - else if (strcmp(name, "AB07") == 0) key = GLFW_KEY_M; - else if (strcmp(name, "AB08") == 0) key = GLFW_KEY_COMMA; - else if (strcmp(name, "AB09") == 0) key = GLFW_KEY_PERIOD; - else if (strcmp(name, "AB10") == 0) key = GLFW_KEY_SLASH; - else if (strcmp(name, "BKSL") == 0) key = GLFW_KEY_BACKSLASH; - else if (strcmp(name, "LSGT") == 0) key = GLFW_KEY_WORLD_1; - else key = GLFW_KEY_UNKNOWN; - - if ((scancode >= 0) && (scancode < 256)) - _glfw.x11.keycodes[scancode] = key; + int key = GLFW_KEY_UNKNOWN; + + // Map the key name to a GLFW key code. Note: We use the US + // keyboard layout. Because function keys aren't mapped correctly + // when using traditional KeySym translations, they are mapped + // here instead. + for (int i = 0; i < sizeof(keymap) / sizeof(keymap[0]); i++) + { + if (strncmp(desc->names->keys[scancode].name, + keymap[i].name, + XkbKeyNameLength) == 0) + { + key = keymap[i].key; + break; + } + } + + // Fall back to key aliases in case the key name did not match + for (int i = 0; i < desc->names->num_key_aliases; i++) + { + if (key != GLFW_KEY_UNKNOWN) + break; + + if (strncmp(desc->names->key_aliases[i].real, + desc->names->keys[scancode].name, + XkbKeyNameLength) != 0) + { + continue; + } + + for (int j = 0; j < sizeof(keymap) / sizeof(keymap[0]); j++) + { + if (strncmp(desc->names->key_aliases[i].alias, + keymap[j].name, + XkbKeyNameLength) == 0) + { + key = keymap[j].key; + break; + } + } + } + + _glfw.x11.keycodes[scancode] = key; } XkbFreeNames(desc, XkbKeyNamesMask, True); XkbFreeKeyboard(desc, 0, True); } + else + XDisplayKeycodes(_glfw.x11.display, &scancodeMin, &scancodeMax); + + int width; + KeySym* keysyms = XGetKeyboardMapping(_glfw.x11.display, + scancodeMin, + scancodeMax - scancodeMin + 1, + &width); - for (scancode = 0; scancode < 256; scancode++) + for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) { // Translate the un-translated key codes using traditional X11 KeySym // lookups if (_glfw.x11.keycodes[scancode] < 0) - _glfw.x11.keycodes[scancode] = translateKeyCode(scancode); + { + const size_t base = (scancode - scancodeMin) * width; + _glfw.x11.keycodes[scancode] = translateKeySyms(&keysyms[base], width); + } // Store the reverse translation for faster key name lookup if (_glfw.x11.keycodes[scancode] > 0) _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode; } + + XFree(keysyms); } // Check whether the IM has a usable style @@ -352,13 +458,13 @@ static GLFWbool hasUsableInputMethodStyle(void) // Check whether the specified atom is supported // -static Atom getSupportedAtom(Atom* supportedAtoms, - unsigned long atomCount, - const char* atomName) +static Atom getAtomIfSupported(Atom* supportedAtoms, + unsigned long atomCount, + const char* atomName) { const Atom atom = XInternAtom(_glfw.x11.display, atomName, False); - for (unsigned int i = 0; i < atomCount; i++) + for (unsigned long i = 0; i < atomCount; i++) { if (supportedAtoms[i] == atom) return atom; @@ -426,33 +532,33 @@ static void detectEWMH(void) // See which of the atoms we support that are supported by the WM _glfw.x11.NET_WM_STATE = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE"); _glfw.x11.NET_WM_STATE_ABOVE = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); _glfw.x11.NET_WM_STATE_FULLSCREEN = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION"); _glfw.x11.NET_WM_FULLSCREEN_MONITORS = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); _glfw.x11.NET_WM_WINDOW_TYPE = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); _glfw.x11.NET_WORKAREA = - getSupportedAtom(supportedAtoms, atomCount, "_NET_WORKAREA"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WORKAREA"); _glfw.x11.NET_CURRENT_DESKTOP = - getSupportedAtom(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP"); _glfw.x11.NET_ACTIVE_WINDOW = - getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); _glfw.x11.NET_FRAME_EXTENTS = - getSupportedAtom(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); _glfw.x11.NET_REQUEST_FRAME_EXTENTS = - getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); + getAtomIfSupported(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); if (supportedAtoms) XFree(supportedAtoms); @@ -660,13 +766,12 @@ static GLFWbool initExtensions(void) _glfw.x11.xkb.detectable = GLFW_TRUE; } - _glfw.x11.xkb.group = 0; XkbStateRec state; if (XkbGetState(_glfw.x11.display, XkbUseCoreKbd, &state) == Success) - { - XkbSelectEventDetails(_glfw.x11.display, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask); _glfw.x11.xkb.group = (unsigned int)state.group; - } + + XkbSelectEventDetails(_glfw.x11.display, XkbUseCoreKbd, XkbStateNotify, + XkbGroupStateMask, XkbGroupStateMask); } #if defined(__CYGWIN__) @@ -851,6 +956,9 @@ static Window createHelperWindow(void) // static int errorHandler(Display *display, XErrorEvent* event) { + if (_glfw.x11.display != display) + return 0; + _glfw.x11.errorCode = event->error_code; return 0; } @@ -931,15 +1039,12 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) int _glfwPlatformInit(void) { -#if !defined(X_HAVE_UTF8_STRING) - // HACK: If the current locale is "C" and the Xlib UTF-8 functions are - // unavailable, apply the environment's locale in the hope that it's - // both available and not "C" - // This is done because the "C" locale breaks wide character input, - // which is what we fall back on when UTF-8 support is missing + // HACK: If the application has left the locale as "C" then both wide + // character text input and explicit UTF-8 input via XIM will break + // This sets the CTYPE part of the current locale from the environment + // in the hope that it is set to something more sane than "C" if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) setlocale(LC_CTYPE, ""); -#endif XInitThreads(); XrmInitialize(); diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_monitor.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_monitor.c index 1f804aeb40..fb3a67bace 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_monitor.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_monitor.c @@ -164,7 +164,7 @@ void _glfwPollMonitorsX11(void) if (widthMM <= 0 || heightMM <= 0) { // HACK: If RandR does not provide a physical size, assume the - // X11 default 96 DPI and calcuate from the CRTC viewport + // X11 default 96 DPI and calculate from the CRTC viewport // NOTE: These members are affected by rotation, unlike the mode // info and output info members widthMM = (int) (ci->width * 25.4f / 96.f); diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_platform.h b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_platform.h index 7377b2c082..4873bd7483 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_platform.h +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_platform.h @@ -199,8 +199,9 @@ typedef struct _GLFWwindowX11 // The last position the cursor was warped to by GLFW int warpCursorPosX, warpCursorPosY; - // The time of the last KeyPress event - Time lastKeyTime; + // The time of the last KeyPress event per keycode, for discarding + // duplicate key events generated for some keys by ibus + Time keyPressTimes[256]; } _GLFWwindowX11; diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_window.c b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_window.c index 271e108094..90c4d9bed6 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_window.c +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw/src/x11_window.c @@ -1195,6 +1195,8 @@ static void processEvent(XEvent *event) { _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group; } + + return; } } @@ -1273,16 +1275,20 @@ static void processEvent(XEvent *event) if (window->x11.ic) { - // HACK: Ignore duplicate key press events generated by ibus - // These have the same timestamp as the original event - // Corresponding release events are filtered out - // implicitly by the GLFW key repeat logic - if (window->x11.lastKeyTime < event->xkey.time) + // HACK: Do not report the key press events duplicated by XIM + // Duplicate key releases are filtered out implicitly by + // the GLFW key repeat logic in _glfwInputKey + // A timestamp per key is used to handle simultaneous keys + // NOTE: Always allow the first event for each key through + // (the server never sends a timestamp of zero) + // NOTE: Timestamp difference is compared to handle wrap-around + Time diff = event->xkey.time - window->x11.keyPressTimes[keycode]; + if (diff == event->xkey.time || (diff > 0 && diff < (1 << 31))) { if (keycode) _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); - window->x11.lastKeyTime = event->xkey.time; + window->x11.keyPressTimes[keycode] = event->xkey.time; } if (!filtered) @@ -1557,6 +1563,8 @@ static void processEvent(XEvent *event) // the position into root (screen) coordinates if (!event->xany.send_event && window->x11.parent != _glfw.x11.root) { + _glfwGrabErrorHandlerX11(); + Window dummy; XTranslateCoordinates(_glfw.x11.display, window->x11.parent, @@ -1564,6 +1572,10 @@ static void processEvent(XEvent *event) xpos, ypos, &xpos, &ypos, &dummy); + + _glfwReleaseErrorHandlerX11(); + if (_glfw.x11.errorCode == BadWindow) + return; } if (xpos != window->x11.xpos || ypos != window->x11.ypos) @@ -1971,7 +1983,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - Visual* visual; + Visual* visual = NULL; int depth; if (ctxconfig->client != GLFW_NO_API) @@ -1997,8 +2009,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, } } - if (ctxconfig->client == GLFW_NO_API || - ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + if (!visual) { visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); @@ -2580,13 +2591,19 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window) int rootX, rootY, childX, childY; unsigned int mask; - if (!XQueryPointer(_glfw.x11.display, w, - &root, &w, &rootX, &rootY, &childX, &childY, &mask)) - { - return GLFW_FALSE; - } + _glfwGrabErrorHandlerX11(); + + const Bool result = XQueryPointer(_glfw.x11.display, w, + &root, &w, &rootX, &rootY, + &childX, &childY, &mask); - if (w == window->x11.handle) + _glfwReleaseErrorHandlerX11(); + + if (_glfw.x11.errorCode == BadWindow) + w = _glfw.x11.root; + else if (!result) + return GLFW_FALSE; + else if (w == window->x11.handle) return GLFW_TRUE; } @@ -2950,8 +2967,9 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) void _glfwPlatformSetClipboardString(const char* string) { + char* copy = _glfw_strdup(string); free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = _glfw_strdup(string); + _glfw.x11.clipboardString = copy; XSetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD, diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw_tree_rebuild.go b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw_tree_rebuild.go new file mode 100644 index 0000000000..22ff0280b2 --- /dev/null +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/glfw_tree_rebuild.go @@ -0,0 +1,10 @@ +package glfw + +//go:generate ../../scripts/glfw_tree_rebuild.sh + +// upstreamTreeSHA is a recursive hash of the full contents of the upstream +// glfw, as generated by git (doesn't need to be committed) when you run `go +// generate` on this package. This exists to invalidate the build cache (see +// https://github.com/go-gl/glfw/issues/269), which is unaffected by C source +// inputs. +const upstreamTreeSHA = "4490c2c270a92046291b021c15e33340289b33db" diff --git a/vendor/github.com/go-gl/glfw/v3.3/glfw/util.go b/vendor/github.com/go-gl/glfw/v3.3/glfw/util.go index 0d0a2c9baf..2bf56ed2d6 100644 --- a/vendor/github.com/go-gl/glfw/v3.3/glfw/util.go +++ b/vendor/github.com/go-gl/glfw/v3.3/glfw/util.go @@ -5,11 +5,6 @@ package glfw //#include "glfw/include/GLFW/glfw3.h" import "C" -import ( - "reflect" - "unsafe" -) - func glfwbool(b C.int) bool { if b == C.int(True) { return true @@ -19,20 +14,10 @@ func glfwbool(b C.int) bool { func bytes(origin []byte) (pointer *uint8, free func()) { n := len(origin) - if n == 0 { return nil, func() {} } - data := C.malloc(C.size_t(n)) - - dataSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(data), - Len: n, - Cap: n, - })) - - copy(dataSlice, origin) - - return &dataSlice[0], func() { C.free(data) } + ptr := C.CBytes(origin) + return (*uint8)(ptr), func() { C.free(ptr) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 79a1e924fd..96f646bd03 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -25,7 +25,7 @@ github.com/fyne-io/mobile/internal/mobileinit # github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 github.com/go-gl/gl/v3.1/gles2 github.com/go-gl/gl/v3.2-core/gl -# github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 => github.com/fyne-io/glfw/v3.3/glfw v0.0.0-20201123143003-f2279069162d +# github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48 github.com/go-gl/glfw/v3.3/glfw github.com/go-gl/glfw/v3.3/glfw/glfw/deps github.com/go-gl/glfw/v3.3/glfw/glfw/deps/glad From e93f96b0797b04e7553a228341da75e85f175c78 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 22 Mar 2021 22:50:36 +0000 Subject: [PATCH 43/62] Update vendor for mobile dep --- vendor/github.com/fyne-io/mobile/app/android.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/github.com/fyne-io/mobile/app/android.go b/vendor/github.com/fyne-io/mobile/app/android.go index 37c7a45e7c..7fc65902c2 100644 --- a/vendor/github.com/fyne-io/mobile/app/android.go +++ b/vendor/github.com/fyne-io/mobile/app/android.go @@ -35,8 +35,8 @@ package app #include #include -EGLDisplay display; -EGLSurface surface; +extern EGLDisplay display; +extern EGLSurface surface; char* createEGLSurface(ANativeWindow* window); char* destroyEGLSurface(); From 619d18abffc93b5f1a33978c33403f1e7e88d6a8 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 12 Feb 2021 22:35:19 +0000 Subject: [PATCH 44/62] Roll back the ability to focus elements not yet in tree --- internal/app/focus_manager.go | 12 ------------ internal/driver/glfw/canvas.go | 2 +- internal/driver/glfw/canvas_test.go | 3 --- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/internal/app/focus_manager.go b/internal/app/focus_manager.go index d24f26680a..cd86b3ddb3 100644 --- a/internal/app/focus_manager.go +++ b/internal/app/focus_manager.go @@ -64,18 +64,6 @@ func (f *FocusManager) Focus(obj fyne.Focusable) bool { return true } -// FocusBeforeAdded allows an object to be focused before it is added to the object tree. -// This is typically used before a canvas is visible and should be used with care. -func (f *FocusManager) FocusBeforeAdded(obj fyne.Focusable) { - f.RLock() - defer f.RUnlock() - if dis, ok := obj.(fyne.Disableable); ok && dis.Disabled() { - return - } - - f.focus(obj) -} - // Focused returns the currently focused object or nil if none. func (f *FocusManager) Focused() fyne.Focusable { f.RLock() diff --git a/internal/driver/glfw/canvas.go b/internal/driver/glfw/canvas.go index e2662b6637..ae6c305fe2 100644 --- a/internal/driver/glfw/canvas.go +++ b/internal/driver/glfw/canvas.go @@ -94,7 +94,7 @@ func (c *glCanvas) Focus(obj fyne.Focusable) { } } - c.contentFocusMgr.FocusBeforeAdded(obj) // not found yet assume we are preparing the UI + fyne.LogError("Failed to focus object which is not part of the canvas’ content, menu or overlays.", nil) } func (c *glCanvas) Focused() fyne.Focusable { diff --git a/internal/driver/glfw/canvas_test.go b/internal/driver/glfw/canvas_test.go index 4b9dc6d013..bb6a8890d2 100644 --- a/internal/driver/glfw/canvas_test.go +++ b/internal/driver/glfw/canvas_test.go @@ -210,9 +210,6 @@ func TestGlCanvas_Focus_BeforeVisible(t *testing.T) { e := widget.NewEntry() c := w.Canvas().(*glCanvas) c.Focus(e) // this crashed in the past - - w.SetContent(e) - assert.Equal(t, e, c.Focused(), "Item set to focus before SetContent was not focused after") } func TestGlCanvas_FocusHandlingWhenAddingAndRemovingOverlays(t *testing.T) { From 0551d752c0ba2854ee3fd27d1f8e0c411f487ed3 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 15 Feb 2021 10:04:03 +0000 Subject: [PATCH 45/62] Add missing test --- internal/driver/glfw/canvas_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/internal/driver/glfw/canvas_test.go b/internal/driver/glfw/canvas_test.go index bb6a8890d2..7d97ab99ef 100644 --- a/internal/driver/glfw/canvas_test.go +++ b/internal/driver/glfw/canvas_test.go @@ -212,6 +212,19 @@ func TestGlCanvas_Focus_BeforeVisible(t *testing.T) { c.Focus(e) // this crashed in the past } +func TestGlCanvas_Focus_SetContent(t *testing.T) { + w := createWindow("Test") + w.SetPadded(false) + e := widget.NewEntry() + w.SetContent(e) + c := w.Canvas().(*glCanvas) + c.Focus(e) + assert.Equal(t, e, c.Focused()) + + w.SetContent(e) + assert.Equal(t, e, c.Focused()) +} + func TestGlCanvas_FocusHandlingWhenAddingAndRemovingOverlays(t *testing.T) { w := createWindow("Test") w.SetPadded(false) From 61583b7b0c9f242280c7f32a1f579d7be9696e96 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Mon, 22 Mar 2021 22:58:11 +0000 Subject: [PATCH 46/62] Preparing changelog --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc4714e2b..1b7b2c93fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,30 @@ This file lists the main changes with each version of the Fyne toolkit. More detailed release notes can be found on the [releases page](https://github.com/fyne-io/fyne/releases). +## 2.0.2 - In progress + +### Changed + +* Text can now be copied from a disable Entry using keyboard shortcuts +* Updated GLFW to 3.3.3 - fixes Chinese input on Linux (#73) + +### Fixed + +* Slider offset position could be incorrect for mobile apps +* Correct error in example code +* When graphics init fails then don't try to continue running (#1593) +* Don't show global settings on mobile in fyne_demo as it's not supported (#2062) +* Empty selection would render small rectangle in Entry +* Do not show validation state for disabled Entry +* dialog.ShowFileSave did not support mobile (#2076) +* Fix issue that storage could not write to files on iOS and Android +* mobile app could crash in some focus calls +* Duplicate symbol error when compiling for Android with NDK 23 (#2064) +* Add internet permission by default for Android apps (#1715) +* Child and Parent support in storage were missing for mobile appps +* Various crashes with Entry and multiline selections (including #1989) + + ## 2.0.1 - 4 March 2021 ### Changed From b63dc367f6baf46b37092ed54b4b01edfa9a0a0c Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Mon, 22 Mar 2021 22:17:13 -0500 Subject: [PATCH 47/62] call OnChanged only when the slider.Value has changed --- widget/slider.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/widget/slider.go b/widget/slider.go index 9623555f7a..c452e2bb17 100644 --- a/widget/slider.go +++ b/widget/slider.go @@ -100,10 +100,12 @@ func (s *Slider) DragEnd() { func (s *Slider) Dragged(e *fyne.DragEvent) { ratio := s.getRatio(&(e.PointEvent)) + lastValue := s.Value + s.updateValue(ratio) s.Refresh() - if s.OnChanged != nil { + if s.OnChanged != nil && lastValue != s.Value { s.OnChanged(s.Value) } } From c852a36b72ba79b58f70d1799549f5434987c2cd Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Mon, 22 Mar 2021 22:47:54 -0500 Subject: [PATCH 48/62] fix slider.SetValue method, update TestSlider_OnChanged --- widget/slider.go | 6 ++++++ widget/slider_test.go | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/widget/slider.go b/widget/slider.go index c452e2bb17..c05cc2a95d 100644 --- a/widget/slider.go +++ b/widget/slider.go @@ -176,9 +176,15 @@ func (s *Slider) SetValue(value float64) { return } + lastValue := s.Value + s.Value = value s.clampValueToRange() + if lastValue == s.Value { + return + } + if s.OnChanged != nil { s.OnChanged(s.Value) } diff --git a/widget/slider_test.go b/widget/slider_test.go index 52292d76bc..67eb4e230c 100644 --- a/widget/slider_test.go +++ b/widget/slider_test.go @@ -118,7 +118,8 @@ func TestSlider_VerticalLayout(t *testing.T) { } func TestSlider_OnChanged(t *testing.T) { - slider := NewSlider(0, 1) + slider := NewSlider(0, 2) + slider.Resize(slider.MinSize()) assert.Empty(t, slider.OnChanged) changes := 0 @@ -130,9 +131,18 @@ func TestSlider_OnChanged(t *testing.T) { assert.Equal(t, 0, changes) slider.SetValue(0.5) + assert.Equal(t, 0, changes) + + drag := &fyne.DragEvent{} + drag.PointEvent.Position = fyne.NewPos(25, 2) + slider.Dragged(drag) + assert.Equal(t, 1, changes) + + drag.PointEvent.Position = fyne.NewPos(25, 2) + slider.Dragged(drag) assert.Equal(t, 1, changes) - drag := &fyne.DragEvent{Dragged: fyne.NewDelta(10, 2)} + drag.PointEvent.Position = fyne.NewPos(50, 2) slider.Dragged(drag) assert.Equal(t, 2, changes) } From b1bf77d3e2b6852f030543838b552bf9d863375a Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Mon, 22 Mar 2021 22:58:25 -0500 Subject: [PATCH 49/62] don't call Refresh if slider.Value has not changed --- widget/slider.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/widget/slider.go b/widget/slider.go index c05cc2a95d..0798939fa6 100644 --- a/widget/slider.go +++ b/widget/slider.go @@ -103,9 +103,14 @@ func (s *Slider) Dragged(e *fyne.DragEvent) { lastValue := s.Value s.updateValue(ratio) + + if lastValue == s.Value { + return + } + s.Refresh() - if s.OnChanged != nil && lastValue != s.Value { + if s.OnChanged != nil { s.OnChanged(s.Value) } } From 822e942ba6343f7db648eb97378638a4a56a12e2 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 23 Mar 2021 10:26:40 +0000 Subject: [PATCH 50/62] Fix issue where temporary binaries would be left Fixes #1910 --- CHANGELOG.md | 2 ++ cmd/fyne/commands/package.go | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b7b2c93fd..c207e8f539 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ More detailed release notes can be found on the [releases page](https://github.c * Add internet permission by default for Android apps (#1715) * Child and Parent support in storage were missing for mobile appps * Various crashes with Entry and multiline selections (including #1989) +* Slider calls OnChanged for each value between steps (#1748) +* fyne command doesn't remove temporary binary from src (#1910) ## 2.0.1 - 4 March 2021 diff --git a/cmd/fyne/commands/package.go b/cmd/fyne/commands/package.go index ed29ed1227..86556640fa 100644 --- a/cmd/fyne/commands/package.go +++ b/cmd/fyne/commands/package.go @@ -3,6 +3,7 @@ package commands import ( "flag" "fmt" + "log" "strconv" "strings" @@ -105,6 +106,7 @@ func (p *packager) doPackage() error { if !util.Exists(p.exe) { return fmt.Errorf("unable to build directory to expected executable, %s", p.exe) } + defer p.removeBuild() } switch p.os { @@ -123,6 +125,13 @@ func (p *packager) doPackage() error { } } +func (p *packager) removeBuild() { + err := os.Remove(p.exe) + if err != nil { + log.Println("Unable to remove temporary build file", p.exe) + } +} + func (p *packager) validate() error { if p.os == "" { p.os = targetOS() From e253fcfc9f34d2bc351d6eb3ba00706e778dc675 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 23 Mar 2021 09:51:27 +0000 Subject: [PATCH 51/62] Improve floating point comparison for 0.1 step etc --- widget/slider.go | 9 +++++++-- widget/slider_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/widget/slider.go b/widget/slider.go index 0798939fa6..2632abdde5 100644 --- a/widget/slider.go +++ b/widget/slider.go @@ -104,7 +104,7 @@ func (s *Slider) Dragged(e *fyne.DragEvent) { s.updateValue(ratio) - if lastValue == s.Value { + if s.almostEqual(lastValue, s.Value) { return } @@ -186,7 +186,7 @@ func (s *Slider) SetValue(value float64) { s.Value = value s.clampValueToRange() - if lastValue == s.Value { + if s.almostEqual(lastValue, s.Value) { return } @@ -219,6 +219,11 @@ func (s *Slider) CreateRenderer() fyne.WidgetRenderer { return slide } +func (s *Slider) almostEqual(a, b float64) bool { + delta := math.Abs(a - b) + return delta <= s.Step/2 +} + // Unbind disconnects any configured data source from this Slider. // The current value will remain at the last value of the data source. // diff --git a/widget/slider_test.go b/widget/slider_test.go index 67eb4e230c..38c5e539e1 100644 --- a/widget/slider_test.go +++ b/widget/slider_test.go @@ -147,6 +147,40 @@ func TestSlider_OnChanged(t *testing.T) { assert.Equal(t, 2, changes) } +func TestSlider_OnChanged_Float(t *testing.T) { + slider := NewSlider(0, 1) + slider.Step = 0.5 + slider.Resize(slider.MinSize()) + assert.Empty(t, slider.OnChanged) + + changes := 0 + + slider.OnChanged = func(_ float64) { + changes++ + } + + assert.Equal(t, 0, changes) + + slider.SetValue(0.15) + assert.Equal(t, 0, changes) + + drag := &fyne.DragEvent{} + drag.PointEvent.Position = fyne.NewPos(25, 2) + slider.Dragged(drag) + assert.Equal(t, 1, changes) + + drag.PointEvent.Position = fyne.NewPos(25, 2) + slider.Dragged(drag) + assert.Equal(t, 1, changes) + + drag.PointEvent.Position = fyne.NewPos(50, 2) + slider.Dragged(drag) + assert.Equal(t, 2, changes) + + slider.SetValue(0.9) + assert.Equal(t, 2, changes) +} + func TestSlider_SetValue(t *testing.T) { slider := NewSlider(0, 2) assert.Equal(t, 0.0, slider.Value) From 6d05b0b1dede368ff3ffb39451b8889917c56d34 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 23 Mar 2021 09:55:46 +0000 Subject: [PATCH 52/62] Simple and less rounding-error-prone rounding --- widget/slider.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/widget/slider.go b/widget/slider.go index 2632abdde5..63822a2dc6 100644 --- a/widget/slider.go +++ b/widget/slider.go @@ -163,10 +163,15 @@ func (s *Slider) clampValueToRange() { return } - i := -(math.Log10(s.Step)) - p := math.Pow(10, i) - - s.Value = float64(int(s.Value*p)) / p + rem := math.Mod(s.Value, s.Step) + if rem == 0 { + return + } + min := s.Value - rem + if rem > s.Step/2 { + min += s.Step + } + s.Value = min } func (s *Slider) updateValue(ratio float64) { From bc1e0781afae5ad23f11e53176f9a477aaa2cd75 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 26 Mar 2021 08:49:17 +0000 Subject: [PATCH 53/62] Truncate any existing content before writing a file in Android Fixes #2118 --- internal/driver/gomobile/android.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/driver/gomobile/android.c b/internal/driver/gomobile/android.c index 06d66d6768..cee6f2ab39 100644 --- a/internal/driver/gomobile/android.c +++ b/internal/driver/gomobile/android.c @@ -149,10 +149,11 @@ void* saveStream(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { jobject resolver = getContentResolver(jni_env, ctx); jclass resolverClass = (*env)->GetObjectClass(env, resolver); - jmethodID saveOutputStream = find_method(env, resolverClass, "openOutputStream", "(Landroid/net/Uri;)Ljava/io/OutputStream;"); + jmethodID saveOutputStream = find_method(env, resolverClass, "openOutputStream", "(Landroid/net/Uri;Ljava/lang/String;)Ljava/io/OutputStream;"); jobject uri = parseURI(jni_env, ctx, uriCstr); - jobject stream = (jobject)(*env)->CallObjectMethod(env, resolver, saveOutputStream, uri); + jstring modes = (*env)->NewStringUTF(env, "wt"); // truncate before write + jobject stream = (jobject)(*env)->CallObjectMethod(env, resolver, saveOutputStream, uri, modes); jthrowable loadErr = (*env)->ExceptionOccurred(env); if (loadErr != NULL) { From 42d12378926e9b7e8c5c04b3190d42c034a8caf9 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 26 Mar 2021 10:23:48 +0000 Subject: [PATCH 54/62] Re-add accidentally removed warning about preference access --- app/app.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/app.go b/app/app.go index dbc338b07b..b8c85ff3fe 100644 --- a/app/app.go +++ b/app/app.go @@ -90,6 +90,9 @@ func (app *fyneApp) Storage() fyne.Storage { } func (app *fyneApp) Preferences() fyne.Preferences { + if app.uniqueID == "" { + fyne.LogError("Preferences API requires a unique ID, use app.NewWithID()", nil) + } return app.prefs } From 78c1d1307737996c114bbbbda3c1f0bd256a8608 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 26 Mar 2021 11:18:50 +0000 Subject: [PATCH 55/62] When we leave the window invalidate our tracked cursor position Fixes #1857 --- internal/driver/glfw/window.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/driver/glfw/window.go b/internal/driver/glfw/window.go index 2e1c4418f3..b3d41b20b3 100644 --- a/internal/driver/glfw/window.go +++ b/internal/driver/glfw/window.go @@ -702,6 +702,11 @@ func (w *window) mouseOut() { } func (w *window) mouseClicked(_ *glfw.Window, btn glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) { + if w.mousePos.IsZero() { // window may not be focused (darwin mostly) and so position callbacks not happening + xpos, ypos := w.viewport.GetCursorPos() + w.mousePos = fyne.NewPos(internal.UnscaleInt(w.canvas, int(xpos)), internal.UnscaleInt(w.canvas, int(ypos))) + } + co, pos, _ := w.findObjectAtPositionMatching(w.canvas, w.mousePos, func(object fyne.CanvasObject) bool { switch object.(type) { case fyne.Tappable, fyne.SecondaryTappable, fyne.DoubleTappable, fyne.Focusable, desktop.Mouseable, desktop.Hoverable: @@ -1184,6 +1189,7 @@ func (w *window) focused(_ *glfw.Window, isFocused bool) { w.canvas.FocusGained() } else { w.canvas.FocusLost() + w.mousePos = fyne.Position{} } } From acdcdf6516384b9340daa89037c3f46da62b3aa3 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Tue, 23 Mar 2021 02:21:29 -0500 Subject: [PATCH 56/62] do not double update color channels when their associated entry text is changed programmatically --- dialog/color_channel.go | 76 +++++++++++++++++++++++++++++++++-------- dialog/color_picker.go | 23 ++++++------- 2 files changed, 72 insertions(+), 27 deletions(-) diff --git a/dialog/color_channel.go b/dialog/color_channel.go index 313698c964..4904ea9e57 100644 --- a/dialog/color_channel.go +++ b/dialog/color_channel.go @@ -2,6 +2,7 @@ package dialog import ( "strconv" + "sync" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" @@ -124,25 +125,24 @@ func (r *colorChannelRenderer) updateObjects() { } type colorChannelEntry struct { - widget.Entry + userChangeEntry + + lock sync.RWMutex + userTyped bool } func newColorChannelEntry(c *colorChannel) *colorChannelEntry { - e := &colorChannelEntry{ - Entry: widget.Entry{ - Text: "0", - OnChanged: func(text string) { - value, err := strconv.Atoi(text) - if err != nil { - fyne.LogError("Couldn't parse: "+text, err) - } else { - c.SetValue(value) - } - }, - // TODO add number min/max validator - }, - } + e := &colorChannelEntry{} + e.Text = "0" e.ExtendBaseWidget(e) + e.setOnChanged(func(text string) { + value, err := strconv.Atoi(text) + if err != nil { + fyne.LogError("Couldn't parse: "+text, err) + return + } + c.SetValue(value) + }) return e } @@ -152,3 +152,49 @@ func (e *colorChannelEntry) MinSize() fyne.Size { min = min.Add(fyne.NewSize(theme.Padding()*6, theme.Padding()*4)) return min.Max(e.Entry.MinSize()) } + +type userChangeEntry struct { + widget.Entry + + lock sync.RWMutex + userTyped bool +} + +func newUserChangeEntry(text string) *userChangeEntry { + e := &userChangeEntry{} + e.Entry.Text = text + e.ExtendBaseWidget(e) + return e +} + +func (e *userChangeEntry) setOnChanged(onChanged func(s string)) { + e.Entry.OnChanged = func(text string) { + e.lock.Lock() + userTyped := e.userTyped + if userTyped { + e.userTyped = false + } + e.lock.Unlock() + if !userTyped { + return + } + if onChanged != nil { + onChanged(text) + } + } + e.ExtendBaseWidget(e) +} + +func (e *userChangeEntry) TypedRune(r rune) { + e.lock.Lock() + e.userTyped = true + e.lock.Unlock() + e.Entry.TypedRune(r) +} + +func (e *userChangeEntry) TypedKey(ev *fyne.KeyEvent) { + e.lock.Lock() + e.userTyped = true + e.lock.Unlock() + e.Entry.TypedKey(ev) +} diff --git a/dialog/color_picker.go b/dialog/color_picker.go index 835c9fadde..6c0a784078 100644 --- a/dialog/color_picker.go +++ b/dialog/color_picker.go @@ -162,17 +162,16 @@ func (p *colorAdvancedPicker) CreateRenderer() fyne.WidgetRenderer { }) // Hex - hex := &widget.Entry{ - OnChanged: func(text string) { - c, err := stringToColor(text) - if err != nil { - fyne.LogError("Error parsing color: "+text, err) - // TODO trigger entry invalid state - } else { - p.SetColor(c) - } - }, - } + hex := newUserChangeEntry("") + hex.setOnChanged(func(text string) { + c, err := stringToColor(text) + if err != nil { + fyne.LogError("Error parsing color: "+text, err) + // TODO trigger entry invalid state + } else { + p.SetColor(c) + } + }) contents := fyne.NewContainerWithLayout(layout.NewPaddedLayout(), container.NewVBox( container.NewGridWithColumns(3, @@ -264,7 +263,7 @@ type colorPickerRenderer struct { wheel *colorWheel preview *canvas.Rectangle alphaChannel *colorChannel - hex *widget.Entry + hex *userChangeEntry contents fyne.CanvasObject } From cf4d41f1aced0bec0b227d5b95cb8de631633594 Mon Sep 17 00:00:00 2001 From: FPabl0 Date: Tue, 23 Mar 2021 02:37:18 -0500 Subject: [PATCH 57/62] remove unused fields --- dialog/color_channel.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/dialog/color_channel.go b/dialog/color_channel.go index 4904ea9e57..6ab7d311ae 100644 --- a/dialog/color_channel.go +++ b/dialog/color_channel.go @@ -126,9 +126,6 @@ func (r *colorChannelRenderer) updateObjects() { type colorChannelEntry struct { userChangeEntry - - lock sync.RWMutex - userTyped bool } func newColorChannelEntry(c *colorChannel) *colorChannelEntry { From 88dffb93bf32b940edf33c4da92bc74f2dd1412c Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 26 Mar 2021 15:55:01 +0000 Subject: [PATCH 58/62] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c207e8f539..0cc129d42a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ More detailed release notes can be found on the [releases page](https://github.c * Various crashes with Entry and multiline selections (including #1989) * Slider calls OnChanged for each value between steps (#1748) * fyne command doesn't remove temporary binary from src (#1910) +* Advanced Color picker on mobile keeps updating values forever after sliding (#2075) ## 2.0.1 - 4 March 2021 From 43350b20928e75f9e57773ec7b5f00a508a66a7f Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 26 Mar 2021 17:06:49 +0000 Subject: [PATCH 59/62] Prepped for release vote --- CHANGELOG.md | 4 +++- README.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc129d42a..26e277b7f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ This file lists the main changes with each version of the Fyne toolkit. More detailed release notes can be found on the [releases page](https://github.com/fyne-io/fyne/releases). -## 2.0.2 - In progress +## 2.0.2 - 30 March 2021 ### Changed @@ -28,6 +28,8 @@ More detailed release notes can be found on the [releases page](https://github.c * Slider calls OnChanged for each value between steps (#1748) * fyne command doesn't remove temporary binary from src (#1910) * Advanced Color picker on mobile keeps updating values forever after sliding (#2075) +* After clicking a link on macOS, click everywhere in the app will be linked (#2112) +* Text selection - Shift+Tab bug (#1787) ## 2.0.1 - 4 March 2021 diff --git a/README.md b/README.md index 552a88a870..27908ea92e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

Go API Reference - 2.0.1 release + 2.0.2 release Join us on Slack
Code Status From 23ba3f46ed8d50357cffa0f89f50c5fe9a1278da Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Fri, 26 Mar 2021 18:49:43 +0000 Subject: [PATCH 60/62] Missed a merge --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26e277b7f5..150d2e03b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ More detailed release notes can be found on the [releases page](https://github.c * Slider calls OnChanged for each value between steps (#1748) * fyne command doesn't remove temporary binary from src (#1910) * Advanced Color picker on mobile keeps updating values forever after sliding (#2075) +* exec.Command and widget.Button combination not working (#1857) * After clicking a link on macOS, click everywhere in the app will be linked (#2112) * Text selection - Shift+Tab bug (#1787) From 1dfd83be86ded779441419decf128a2ff94622da Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Tue, 30 Mar 2021 13:44:24 +0100 Subject: [PATCH 61/62] Fix performance issue where images could re-draw a lot when not needed --- canvas/image.go | 4 ++++ canvas/raster.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/canvas/image.go b/canvas/image.go index 36334ba372..53d4e6c9b2 100644 --- a/canvas/image.go +++ b/canvas/image.go @@ -71,6 +71,10 @@ func (i *Image) Alpha() float64 { // Resize on an image will scale the content or reposition it according to FillMode. // It will normally cause a Refresh to ensure the pixels are recalculated. func (i *Image) Resize(s fyne.Size) { + if s == i.Size() { + return + } + i.baseObject.Resize(s) Refresh(i) diff --git a/canvas/raster.go b/canvas/raster.go index 50972cb48d..9f809034e8 100644 --- a/canvas/raster.go +++ b/canvas/raster.go @@ -34,6 +34,10 @@ func (r *Raster) Alpha() float64 { // Resize on a raster image causes the new size to be set and then calls Refresh. // This causes the underlying data to be recalculated and a new output to be drawn. func (r *Raster) Resize(s fyne.Size) { + if s == r.Size() { + return + } + r.baseObject.Resize(s) Refresh(r) } From c42bea594b6b37d00cf531c3fd5b85f57eb22ce4 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Thu, 1 Apr 2021 17:03:25 +0100 Subject: [PATCH 62/62] The GLFW upgrade appears not to be correct upstream, remove as some issues were not fixed --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 150d2e03b6..50ad3e8977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,11 @@ This file lists the main changes with each version of the Fyne toolkit. More detailed release notes can be found on the [releases page](https://github.com/fyne-io/fyne/releases). -## 2.0.2 - 30 March 2021 +## 2.0.2 - 1 April 2021 ### Changed * Text can now be copied from a disable Entry using keyboard shortcuts -* Updated GLFW to 3.3.3 - fixes Chinese input on Linux (#73) ### Fixed