| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998 |
- package glfw
- import (
- "context"
- _ "image/png" // for the icon
- "math"
- "runtime"
- "time"
- "fyne.io/fyne/v2"
- "fyne.io/fyne/v2/driver/desktop"
- "fyne.io/fyne/v2/internal"
- "fyne.io/fyne/v2/internal/app"
- "fyne.io/fyne/v2/internal/cache"
- "fyne.io/fyne/v2/internal/driver"
- "fyne.io/fyne/v2/internal/driver/common"
- )
- const (
- scrollAccelerateRate = float64(5)
- scrollAccelerateCutoff = float64(5)
- scrollSpeed = float32(10)
- doubleClickDelay = 300 // ms (maximum interval between clicks for double click detection)
- dragMoveThreshold = 2 // how far can we move before it is a drag
- windowIconSize = 256
- )
- func (w *window) Title() string {
- return w.title
- }
- func (w *window) SetTitle(title string) {
- w.title = title
- w.runOnMainWhenCreated(func() {
- w.view().SetTitle(title)
- })
- }
- func (w *window) FullScreen() bool {
- return w.fullScreen
- }
- // minSizeOnScreen gets the padded minimum size of a window content in screen pixels
- func (w *window) minSizeOnScreen() (int, int) {
- // get minimum size of content inside the window
- return w.screenSize(w.canvas.MinSize())
- }
- // screenSize computes the actual output size of the given content size in screen pixels
- func (w *window) screenSize(canvasSize fyne.Size) (int, int) {
- return internal.ScaleInt(w.canvas, canvasSize.Width), internal.ScaleInt(w.canvas, canvasSize.Height)
- }
- func (w *window) Resize(size fyne.Size) {
- // we cannot perform this until window is prepared as we don't know it's scale!
- bigEnough := size.Max(w.canvas.canvasSize(w.canvas.Content().MinSize()))
- w.runOnMainWhenCreated(func() {
- w.viewLock.Lock()
- width, height := internal.ScaleInt(w.canvas, bigEnough.Width), internal.ScaleInt(w.canvas, bigEnough.Height)
- if w.fixedSize || !w.visible { // fixed size ignores future `resized` and if not visible we may not get the event
- w.shouldWidth, w.shouldHeight = width, height
- w.width, w.height = width, height
- }
- w.viewLock.Unlock()
- w.requestedWidth, w.requestedHeight = width, height
- w.view().SetSize(width, height)
- })
- }
- func (w *window) FixedSize() bool {
- return w.fixedSize
- }
- func (w *window) SetFixedSize(fixed bool) {
- w.fixedSize = fixed
- if w.view() != nil {
- w.runOnMainWhenCreated(w.fitContent)
- }
- }
- func (w *window) Padded() bool {
- return w.canvas.padded
- }
- func (w *window) SetPadded(padded bool) {
- w.canvas.SetPadded(padded)
- w.runOnMainWhenCreated(w.fitContent)
- }
- func (w *window) Icon() fyne.Resource {
- if w.icon == nil {
- return fyne.CurrentApp().Icon()
- }
- return w.icon
- }
- func (w *window) MainMenu() *fyne.MainMenu {
- return w.mainmenu
- }
- func (w *window) SetMainMenu(menu *fyne.MainMenu) {
- w.mainmenu = menu
- w.runOnMainWhenCreated(func() {
- w.canvas.buildMenu(w, menu)
- })
- }
- func (w *window) SetOnClosed(closed func()) {
- w.onClosed = closed
- }
- func (w *window) SetCloseIntercept(callback func()) {
- w.onCloseIntercepted = callback
- }
- func (w *window) calculatedScale() float32 {
- return calculateScale(userScale(), fyne.CurrentDevice().SystemScaleForWindow(w), w.detectScale())
- }
- func (w *window) detectTextureScale() float32 {
- winWidth, _ := w.view().GetSize()
- texWidth, _ := w.view().GetFramebufferSize()
- return float32(texWidth) / float32(winWidth)
- }
- func (w *window) Show() {
- go w.doShow()
- }
- func (w *window) doShow() {
- if w.view() != nil {
- w.doShowAgain()
- return
- }
- run.Lock()
- for !run.flag {
- run.cond.Wait()
- }
- run.Unlock()
- w.createLock.Do(w.create)
- if w.view() == nil {
- return
- }
- runOnMain(func() {
- w.viewLock.Lock()
- w.visible = true
- w.viewLock.Unlock()
- w.view().SetTitle(w.title)
- if w.centered {
- w.doCenterOnScreen() // lastly center if that was requested
- }
- w.view().Show()
- // save coordinates
- w.xpos, w.ypos = w.view().GetPos()
- if w.fullScreen { // this does not work if called before viewport.Show()
- go func() {
- time.Sleep(time.Millisecond * 100)
- w.SetFullScreen(true)
- }()
- }
- })
- // show top canvas element
- if w.canvas.Content() != nil {
- w.canvas.Content().Show()
- }
- }
- func (w *window) Hide() {
- runOnMain(func() {
- w.viewLock.Lock()
- if w.closing || w.viewport == nil {
- w.viewLock.Unlock()
- return
- }
- w.visible = false
- v := w.viewport
- w.viewLock.Unlock()
- v.Hide()
- // hide top canvas element
- if w.canvas.Content() != nil {
- w.canvas.Content().Hide()
- }
- })
- }
- func (w *window) Close() {
- if w.isClosing() {
- return
- }
- // trigger callbacks - early so window still exists
- if w.onClosed != nil {
- w.QueueEvent(w.onClosed)
- }
- // set w.closing flag inside draw thread to ensure we can free textures
- runOnDraw(w, func() {
- w.viewLock.Lock()
- w.closing = true
- w.viewLock.Unlock()
- w.viewport.SetShouldClose(true)
- cache.RangeTexturesFor(w.canvas, func(obj fyne.CanvasObject) {
- w.canvas.Painter().Free(obj)
- })
- })
- w.canvas.WalkTrees(nil, func(node *common.RenderCacheNode) {
- if wid, ok := node.Obj().(fyne.Widget); ok {
- cache.DestroyRenderer(wid)
- }
- })
- }
- func (w *window) ShowAndRun() {
- w.Show()
- w.driver.Run()
- }
- // Clipboard returns the system clipboard
- func (w *window) Clipboard() fyne.Clipboard {
- if w.view() == nil {
- return nil
- }
- if w.clipboard == nil {
- w.clipboard = &clipboard{window: w.viewport}
- }
- return w.clipboard
- }
- func (w *window) Content() fyne.CanvasObject {
- return w.canvas.Content()
- }
- func (w *window) SetContent(content fyne.CanvasObject) {
- w.viewLock.RLock()
- visible := w.visible
- w.viewLock.RUnlock()
- // hide old canvas element
- if visible && w.canvas.Content() != nil {
- w.canvas.Content().Hide()
- }
- w.canvas.SetContent(content)
- // show new canvas element
- if content != nil {
- content.Show()
- }
- w.RescaleContext()
- }
- func (w *window) Canvas() fyne.Canvas {
- return w.canvas
- }
- func (w *window) processClosed() {
- if w.onCloseIntercepted != nil {
- w.QueueEvent(w.onCloseIntercepted)
- return
- }
- go w.Close() // unsure which thread this comes from, so don't block
- }
- // destroy this window and, if it's the last window quit the app
- func (w *window) destroy(d *gLDriver) {
- w.DestroyEventQueue()
- cache.CleanCanvas(w.canvas)
- if w.master {
- d.Quit()
- } else if runtime.GOOS == "darwin" {
- go d.focusPreviousWindow()
- }
- }
- func (w *window) processMoved(x, y int) {
- if !w.fullScreen { // don't save the move to top left when changing to fullscreen
- // save coordinates
- w.xpos, w.ypos = x, y
- }
- if w.canvas.detectedScale == w.detectScale() {
- return
- }
- w.canvas.detectedScale = w.detectScale()
- go w.canvas.reloadScale()
- }
- func (w *window) processResized(width, height int) {
- canvasSize := w.computeCanvasSize(width, height)
- if !w.fullScreen {
- w.width = internal.ScaleInt(w.canvas, canvasSize.Width)
- w.height = internal.ScaleInt(w.canvas, canvasSize.Height)
- }
- if !w.visible { // don't redraw if hidden
- w.canvas.Resize(canvasSize)
- return
- }
- if w.fixedSize {
- w.canvas.Resize(canvasSize)
- w.fitContent()
- return
- }
- w.platformResize(canvasSize)
- }
- func (w *window) processFrameSized(width, height int) {
- if width == 0 || height == 0 || runtime.GOOS != "darwin" {
- return
- }
- winWidth, _ := w.view().GetSize()
- newTexScale := float32(width) / float32(winWidth) // This will be > 1.0 on a HiDPI screen
- w.canvas.RLock()
- texScale := w.canvas.texScale
- w.canvas.RUnlock()
- if texScale != newTexScale {
- w.canvas.Lock()
- w.canvas.texScale = newTexScale
- w.canvas.Unlock()
- w.canvas.Refresh(w.canvas.Content()) // reset graphics to apply texture scale
- }
- }
- func (w *window) processRefresh() {
- refreshWindow(w)
- }
- func (w *window) findObjectAtPositionMatching(canvas *glCanvas, mouse fyne.Position, matches func(object fyne.CanvasObject) bool) (fyne.CanvasObject, fyne.Position, int) {
- return driver.FindObjectAtPositionMatching(mouse, matches, canvas.Overlays().Top(), canvas.menu, canvas.Content())
- }
- func (w *window) processMouseMoved(xpos float64, ypos float64) {
- w.mouseLock.Lock()
- previousPos := w.mousePos
- w.mousePos = fyne.NewPos(internal.UnscaleInt(w.canvas, int(xpos)), internal.UnscaleInt(w.canvas, int(ypos)))
- mousePos := w.mousePos
- mouseButton := w.mouseButton
- mouseDragPos := w.mouseDragPos
- mouseOver := w.mouseOver
- w.mouseLock.Unlock()
- cursor := desktop.Cursor(desktop.DefaultCursor)
- obj, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
- if cursorable, ok := object.(desktop.Cursorable); ok {
- cursor = cursorable.Cursor()
- }
- _, hover := object.(desktop.Hoverable)
- return hover
- })
- if w.cursor != cursor {
- // cursor has changed, store new cursor and apply change via glfw
- rawCursor, isCustomCursor := fyneToNativeCursor(cursor)
- w.cursor = cursor
- if rawCursor == nil {
- w.view().SetInputMode(CursorMode, CursorHidden)
- } else {
- w.view().SetInputMode(CursorMode, CursorNormal)
- w.SetCursor(rawCursor)
- }
- w.setCustomCursor(rawCursor, isCustomCursor)
- }
- if w.mouseButton != 0 && w.mouseButton != desktop.MouseButtonSecondary && !w.mouseDragStarted {
- obj, pos, _ := w.findObjectAtPositionMatching(w.canvas, previousPos, func(object fyne.CanvasObject) bool {
- _, ok := object.(fyne.Draggable)
- return ok
- })
- deltaX := mousePos.X - mouseDragPos.X
- deltaY := mousePos.Y - mouseDragPos.Y
- overThreshold := math.Abs(float64(deltaX)) >= dragMoveThreshold || math.Abs(float64(deltaY)) >= dragMoveThreshold
- if wid, ok := obj.(fyne.Draggable); ok && overThreshold {
- w.mouseLock.Lock()
- w.mouseDragged = wid
- w.mouseDraggedOffset = previousPos.Subtract(pos)
- w.mouseDraggedObjStart = obj.Position()
- w.mouseDragStarted = true
- w.mouseLock.Unlock()
- }
- }
- w.mouseLock.RLock()
- isObjDragged := w.objIsDragged(obj)
- isMouseOverDragged := w.objIsDragged(mouseOver)
- w.mouseLock.RUnlock()
- if obj != nil && !isObjDragged {
- ev := new(desktop.MouseEvent)
- ev.AbsolutePosition = mousePos
- ev.Position = pos
- ev.Button = mouseButton
- if hovered, ok := obj.(desktop.Hoverable); ok {
- if hovered == mouseOver {
- w.QueueEvent(func() { hovered.MouseMoved(ev) })
- } else {
- w.mouseOut()
- w.mouseIn(hovered, ev)
- }
- } else if mouseOver != nil {
- isChild := false
- driver.WalkCompleteObjectTree(mouseOver.(fyne.CanvasObject),
- func(co fyne.CanvasObject, p1, p2 fyne.Position, s fyne.Size) bool {
- if co == obj {
- isChild = true
- return true
- }
- return false
- }, nil)
- if !isChild {
- w.mouseOut()
- }
- }
- } else if mouseOver != nil && !isMouseOverDragged {
- w.mouseOut()
- }
- w.mouseLock.RLock()
- mouseButton = w.mouseButton
- mouseDragged := w.mouseDragged
- mouseDraggedObjStart := w.mouseDraggedObjStart
- mouseDraggedOffset := w.mouseDraggedOffset
- mouseDragPos = w.mouseDragPos
- w.mouseLock.RUnlock()
- if mouseDragged != nil && mouseButton != desktop.MouseButtonSecondary {
- if w.mouseButton > 0 {
- draggedObjDelta := mouseDraggedObjStart.Subtract(mouseDragged.(fyne.CanvasObject).Position())
- ev := new(fyne.DragEvent)
- ev.AbsolutePosition = mousePos
- ev.Position = mousePos.Subtract(mouseDraggedOffset).Add(draggedObjDelta)
- ev.Dragged = fyne.NewDelta(mousePos.X-mouseDragPos.X, mousePos.Y-mouseDragPos.Y)
- wd := mouseDragged
- w.QueueEvent(func() { wd.Dragged(ev) })
- }
- w.mouseLock.Lock()
- w.mouseDragStarted = true
- w.mouseDragPos = mousePos
- w.mouseLock.Unlock()
- }
- }
- func (w *window) objIsDragged(obj interface{}) bool {
- if w.mouseDragged != nil && obj != nil {
- draggedObj, _ := obj.(fyne.Draggable)
- return draggedObj == w.mouseDragged
- }
- return false
- }
- func (w *window) mouseIn(obj desktop.Hoverable, ev *desktop.MouseEvent) {
- w.QueueEvent(func() {
- if obj != nil {
- obj.MouseIn(ev)
- }
- w.mouseLock.Lock()
- w.mouseOver = obj
- w.mouseLock.Unlock()
- })
- }
- func (w *window) mouseOut() {
- w.QueueEvent(func() {
- w.mouseLock.RLock()
- mouseOver := w.mouseOver
- w.mouseLock.RUnlock()
- if mouseOver != nil {
- mouseOver.MouseOut()
- w.mouseLock.Lock()
- w.mouseOver = nil
- w.mouseLock.Unlock()
- }
- })
- }
- func (w *window) processMouseClicked(button desktop.MouseButton, action action, modifiers fyne.KeyModifier) {
- w.mouseLock.RLock()
- w.mouseDragPos = w.mousePos
- mousePos := w.mousePos
- mouseDragStarted := w.mouseDragStarted
- w.mouseLock.RUnlock()
- if mousePos.IsZero() { // window may not be focused (darwin mostly) and so position callbacks not happening
- xpos, ypos := w.view().GetCursorPos()
- w.mouseLock.Lock()
- w.mousePos = fyne.NewPos(internal.UnscaleInt(w.canvas, int(xpos)), internal.UnscaleInt(w.canvas, int(ypos)))
- mousePos = w.mousePos
- w.mouseLock.Unlock()
- }
- co, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
- switch object.(type) {
- case fyne.Tappable, fyne.SecondaryTappable, fyne.DoubleTappable, fyne.Focusable, desktop.Mouseable, desktop.Hoverable:
- return true
- case fyne.Draggable:
- if mouseDragStarted {
- return true
- }
- }
- return false
- })
- ev := new(fyne.PointEvent)
- ev.Position = pos
- ev.AbsolutePosition = mousePos
- coMouse := co
- if wid, ok := co.(desktop.Mouseable); ok {
- mev := new(desktop.MouseEvent)
- mev.Position = ev.Position
- mev.AbsolutePosition = mousePos
- mev.Button = button
- mev.Modifier = modifiers
- w.mouseClickedHandleMouseable(mev, action, wid)
- }
- if wid, ok := co.(fyne.Focusable); !ok || wid != w.canvas.Focused() {
- w.canvas.Unfocus()
- }
- w.mouseLock.Lock()
- if action == press {
- w.mouseButton |= button
- } else if action == release {
- w.mouseButton &= ^button
- }
- mouseDragged := w.mouseDragged
- mouseDragStarted = w.mouseDragStarted
- mouseOver := w.mouseOver
- shouldMouseOut := w.objIsDragged(mouseOver) && !w.objIsDragged(coMouse)
- mousePressed := w.mousePressed
- w.mouseLock.Unlock()
- if action == release && mouseDragged != nil {
- if mouseDragStarted {
- w.QueueEvent(mouseDragged.DragEnd)
- w.mouseLock.Lock()
- w.mouseDragStarted = false
- w.mouseLock.Unlock()
- }
- if shouldMouseOut {
- w.mouseOut()
- }
- w.mouseLock.Lock()
- w.mouseDragged = nil
- w.mouseLock.Unlock()
- }
- _, tap := co.(fyne.Tappable)
- _, altTap := co.(fyne.SecondaryTappable)
- if tap || altTap {
- if action == press {
- w.mouseLock.Lock()
- w.mousePressed = co
- w.mouseLock.Unlock()
- } else if action == release {
- if co == mousePressed {
- if button == desktop.MouseButtonSecondary && altTap {
- w.QueueEvent(func() { co.(fyne.SecondaryTappable).TappedSecondary(ev) })
- }
- }
- }
- }
- // Check for double click/tap on left mouse button
- if action == release && button == desktop.MouseButtonPrimary && !mouseDragStarted {
- w.mouseClickedHandleTapDoubleTap(co, ev)
- }
- }
- func (w *window) mouseClickedHandleMouseable(mev *desktop.MouseEvent, action action, wid desktop.Mouseable) {
- mousePos := mev.AbsolutePosition
- if action == press {
- w.QueueEvent(func() { wid.MouseDown(mev) })
- } else if action == release {
- w.mouseLock.RLock()
- mouseDragged := w.mouseDragged
- mouseDraggedOffset := w.mouseDraggedOffset
- w.mouseLock.RUnlock()
- if mouseDragged == nil {
- w.QueueEvent(func() { wid.MouseUp(mev) })
- } else {
- if dragged, ok := mouseDragged.(desktop.Mouseable); ok {
- mev.Position = mousePos.Subtract(mouseDraggedOffset)
- w.QueueEvent(func() { dragged.MouseUp(mev) })
- } else {
- w.QueueEvent(func() { wid.MouseUp(mev) })
- }
- }
- }
- }
- func (w *window) mouseClickedHandleTapDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
- _, doubleTap := co.(fyne.DoubleTappable)
- if doubleTap {
- w.mouseLock.Lock()
- w.mouseClickCount++
- w.mouseLastClick = co
- mouseCancelFunc := w.mouseCancelFunc
- w.mouseLock.Unlock()
- if mouseCancelFunc != nil {
- mouseCancelFunc()
- return
- }
- go w.waitForDoubleTap(co, ev)
- } else {
- w.mouseLock.Lock()
- if wid, ok := co.(fyne.Tappable); ok && co == w.mousePressed {
- w.QueueEvent(func() { wid.Tapped(ev) })
- }
- w.mousePressed = nil
- w.mouseLock.Unlock()
- }
- }
- func (w *window) waitForDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
- var ctx context.Context
- w.mouseLock.Lock()
- ctx, w.mouseCancelFunc = context.WithDeadline(context.TODO(), time.Now().Add(time.Millisecond*doubleClickDelay))
- defer w.mouseCancelFunc()
- w.mouseLock.Unlock()
- <-ctx.Done()
- w.mouseLock.Lock()
- defer w.mouseLock.Unlock()
- if w.mouseClickCount == 2 && w.mouseLastClick == co {
- if wid, ok := co.(fyne.DoubleTappable); ok {
- w.QueueEvent(func() { wid.DoubleTapped(ev) })
- }
- } else if co == w.mousePressed {
- if wid, ok := co.(fyne.Tappable); ok {
- w.QueueEvent(func() { wid.Tapped(ev) })
- }
- }
- w.mouseClickCount = 0
- w.mousePressed = nil
- w.mouseCancelFunc = nil
- w.mouseLastClick = nil
- }
- func (w *window) processMouseScrolled(xoff float64, yoff float64) {
- w.mouseLock.RLock()
- mousePos := w.mousePos
- w.mouseLock.RUnlock()
- co, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
- _, ok := object.(fyne.Scrollable)
- return ok
- })
- switch wid := co.(type) {
- case fyne.Scrollable:
- if math.Abs(xoff) >= scrollAccelerateCutoff {
- xoff *= scrollAccelerateRate
- }
- if math.Abs(yoff) >= scrollAccelerateCutoff {
- yoff *= scrollAccelerateRate
- }
- ev := &fyne.ScrollEvent{}
- ev.Scrolled = fyne.NewDelta(float32(xoff)*scrollSpeed, float32(yoff)*scrollSpeed)
- ev.Position = pos
- ev.AbsolutePosition = mousePos
- wid.Scrolled(ev)
- }
- }
- func (w *window) capturesTab(modifier fyne.KeyModifier) bool {
- captures := false
- if ent, ok := w.canvas.Focused().(fyne.Tabbable); ok {
- captures = ent.AcceptsTab()
- }
- if !captures {
- switch modifier {
- case 0:
- w.QueueEvent(w.canvas.FocusNext)
- return false
- case fyne.KeyModifierShift:
- w.QueueEvent(w.canvas.FocusPrevious)
- return false
- }
- }
- return captures
- }
- func (w *window) processKeyPressed(keyName fyne.KeyName, keyASCII fyne.KeyName, scancode int, action action, keyDesktopModifier fyne.KeyModifier) {
- keyEvent := &fyne.KeyEvent{Name: keyName, Physical: fyne.HardwareKey{ScanCode: scancode}}
- pendingMenuToggle := w.menuTogglePending
- pendingMenuDeactivation := w.menuDeactivationPending
- w.menuTogglePending = desktop.KeyNone
- w.menuDeactivationPending = desktop.KeyNone
- switch action {
- case release:
- if action == release && keyName != "" {
- switch keyName {
- case pendingMenuToggle:
- w.canvas.ToggleMenu()
- case pendingMenuDeactivation:
- if w.canvas.DismissMenu() {
- return
- }
- }
- }
- if w.canvas.Focused() != nil {
- if focused, ok := w.canvas.Focused().(desktop.Keyable); ok {
- w.QueueEvent(func() { focused.KeyUp(keyEvent) })
- }
- } else if w.canvas.onKeyUp != nil {
- w.QueueEvent(func() { w.canvas.onKeyUp(keyEvent) })
- }
- return // ignore key up in other core events
- case press:
- switch keyName {
- case desktop.KeyAltLeft, desktop.KeyAltRight:
- // compensate for GLFW modifiers bug https://github.com/glfw/glfw/issues/1630
- if (runtime.GOOS == "linux" && keyDesktopModifier == 0) || (runtime.GOOS != "linux" && keyDesktopModifier == fyne.KeyModifierAlt) {
- w.menuTogglePending = keyName
- }
- case fyne.KeyEscape:
- w.menuDeactivationPending = keyName
- }
- if w.canvas.Focused() != nil {
- if focused, ok := w.canvas.Focused().(desktop.Keyable); ok {
- w.QueueEvent(func() { focused.KeyDown(keyEvent) })
- }
- } else if w.canvas.onKeyDown != nil {
- w.QueueEvent(func() { w.canvas.onKeyDown(keyEvent) })
- }
- default:
- // key repeat will fall through to TypedKey and TypedShortcut
- }
- modifierOtherThanShift := (keyDesktopModifier & fyne.KeyModifierControl) |
- (keyDesktopModifier & fyne.KeyModifierAlt) |
- (keyDesktopModifier & fyne.KeyModifierSuper)
- if (keyName == fyne.KeyTab && modifierOtherThanShift == 0 && !w.capturesTab(keyDesktopModifier)) ||
- w.triggersShortcut(keyName, keyASCII, keyDesktopModifier) {
- return
- }
- // No shortcut detected, pass down to TypedKey
- focused := w.canvas.Focused()
- if focused != nil {
- w.QueueEvent(func() { focused.TypedKey(keyEvent) })
- } else if w.canvas.onTypedKey != nil {
- w.QueueEvent(func() { w.canvas.onTypedKey(keyEvent) })
- }
- }
- // charInput defines the character with modifiers callback which is called when a
- // Unicode character is input.
- //
- // Characters do not map 1:1 to physical keys, as a key may produce zero, one or more characters.
- func (w *window) processCharInput(char rune) {
- if focused := w.canvas.Focused(); focused != nil {
- w.QueueEvent(func() { focused.TypedRune(char) })
- } else if w.canvas.onTypedRune != nil {
- w.QueueEvent(func() { w.canvas.onTypedRune(char) })
- }
- }
- func (w *window) processFocused(focus bool) {
- if focus {
- if curWindow == nil {
- fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerEnteredForeground()
- }
- curWindow = w
- w.canvas.FocusGained()
- } else {
- w.canvas.FocusLost()
- w.mouseLock.Lock()
- w.mousePos = fyne.Position{}
- w.mouseLock.Unlock()
- go func() { // check whether another window was focused or not
- time.Sleep(time.Millisecond * 100)
- if curWindow != w {
- return
- }
- curWindow = nil
- fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerExitedForeground()
- }()
- }
- }
- func (w *window) triggersShortcut(localizedKeyName fyne.KeyName, key fyne.KeyName, modifier fyne.KeyModifier) bool {
- var shortcut fyne.Shortcut
- ctrlMod := fyne.KeyModifierControl
- if runtime.GOOS == "darwin" {
- ctrlMod = fyne.KeyModifierSuper
- }
- // User pressing physical keys Ctrl+V while using a Russian (or any non-ASCII) keyboard layout
- // is reported as a fyne.KeyUnknown key with Control modifier. We should still consider this
- // as a "Paste" shortcut.
- // See https://github.com/fyne-io/fyne/pull/2587 for discussion.
- keyName := localizedKeyName
- resemblesShortcut := (modifier&(fyne.KeyModifierControl|fyne.KeyModifierSuper) != 0)
- if (localizedKeyName == fyne.KeyUnknown) && resemblesShortcut {
- if key != fyne.KeyUnknown {
- keyName = key
- }
- }
- if modifier == ctrlMod {
- switch keyName {
- case fyne.KeyV:
- // detect paste shortcut
- shortcut = &fyne.ShortcutPaste{
- Clipboard: w.Clipboard(),
- }
- case fyne.KeyC, fyne.KeyInsert:
- // detect copy shortcut
- shortcut = &fyne.ShortcutCopy{
- Clipboard: w.Clipboard(),
- }
- case fyne.KeyX:
- // detect cut shortcut
- shortcut = &fyne.ShortcutCut{
- Clipboard: w.Clipboard(),
- }
- case fyne.KeyA:
- // detect selectAll shortcut
- shortcut = &fyne.ShortcutSelectAll{}
- }
- }
- if modifier == fyne.KeyModifierShift {
- switch keyName {
- case fyne.KeyInsert:
- // detect paste shortcut
- shortcut = &fyne.ShortcutPaste{
- Clipboard: w.Clipboard(),
- }
- case fyne.KeyDelete:
- // detect cut shortcut
- shortcut = &fyne.ShortcutCut{
- Clipboard: w.Clipboard(),
- }
- }
- }
- if shortcut == nil && modifier != 0 && !isKeyModifier(keyName) && modifier != fyne.KeyModifierShift {
- shortcut = &desktop.CustomShortcut{
- KeyName: keyName,
- Modifier: modifier,
- }
- }
- if shortcut != nil {
- if focused, ok := w.canvas.Focused().(fyne.Shortcutable); ok {
- shouldRunShortcut := true
- type selectableText interface {
- fyne.Disableable
- SelectedText() string
- }
- if selectableTextWid, ok := focused.(selectableText); ok && selectableTextWid.Disabled() {
- shouldRunShortcut = shortcut.ShortcutName() == "Copy"
- }
- if shouldRunShortcut {
- w.QueueEvent(func() { focused.TypedShortcut(shortcut) })
- }
- return shouldRunShortcut
- }
- w.QueueEvent(func() { w.canvas.TypedShortcut(shortcut) })
- return true
- }
- return false
- }
- func (w *window) RunWithContext(f func()) {
- if w.isClosing() {
- return
- }
- w.view().MakeContextCurrent()
- f()
- w.DetachCurrentContext()
- }
- func (w *window) RescaleContext() {
- runOnMain(func() {
- w.rescaleOnMain()
- })
- }
- func (w *window) Context() interface{} {
- return nil
- }
- func (w *window) runOnMainWhenCreated(fn func()) {
- if w.view() != nil {
- runOnMain(fn)
- return
- }
- w.pending = append(w.pending, fn)
- }
- func (d *gLDriver) CreateWindow(title string) fyne.Window {
- return d.createWindow(title, true)
- }
- func (d *gLDriver) createWindow(title string, decorate bool) fyne.Window {
- var ret *window
- if title == "" {
- title = defaultTitle
- }
- runOnMain(func() {
- d.initGLFW()
- ret = &window{title: title, decorate: decorate, driver: d}
- // This queue is destroyed when the window is closed.
- ret.InitEventQueue()
- go ret.RunEventQueue()
- ret.canvas = newCanvas()
- ret.canvas.context = ret
- ret.SetIcon(ret.icon)
- d.addWindow(ret)
- })
- return ret
- }
- func (w *window) doShowAgain() {
- if w.isClosing() {
- return
- }
- runOnMain(func() {
- // show top canvas element
- if w.canvas.Content() != nil {
- w.canvas.Content().Show()
- }
- w.view().SetPos(w.xpos, w.ypos)
- w.view().Show()
- w.viewLock.Lock()
- w.visible = true
- w.viewLock.Unlock()
- })
- }
- func (w *window) isClosing() bool {
- w.viewLock.RLock()
- closing := w.closing || w.viewport == nil
- w.viewLock.RUnlock()
- return closing
- }
- func (d *gLDriver) CreateSplashWindow() fyne.Window {
- win := d.createWindow("", false)
- win.SetPadded(false)
- win.CenterOnScreen()
- return win
- }
- func (d *gLDriver) AllWindows() []fyne.Window {
- return d.windows
- }
- func isKeyModifier(keyName fyne.KeyName) bool {
- return keyName == desktop.KeyShiftLeft || keyName == desktop.KeyShiftRight ||
- keyName == desktop.KeyControlLeft || keyName == desktop.KeyControlRight ||
- keyName == desktop.KeyAltLeft || keyName == desktop.KeyAltRight ||
- keyName == desktop.KeySuperLeft || keyName == desktop.KeySuperRight
- }
|