window.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. package glfw
  2. import (
  3. "context"
  4. _ "image/png" // for the icon
  5. "math"
  6. "runtime"
  7. "time"
  8. "fyne.io/fyne/v2"
  9. "fyne.io/fyne/v2/driver/desktop"
  10. "fyne.io/fyne/v2/internal"
  11. "fyne.io/fyne/v2/internal/app"
  12. "fyne.io/fyne/v2/internal/cache"
  13. "fyne.io/fyne/v2/internal/driver"
  14. "fyne.io/fyne/v2/internal/driver/common"
  15. )
  16. const (
  17. scrollAccelerateRate = float64(5)
  18. scrollAccelerateCutoff = float64(5)
  19. scrollSpeed = float32(10)
  20. doubleClickDelay = 300 // ms (maximum interval between clicks for double click detection)
  21. dragMoveThreshold = 2 // how far can we move before it is a drag
  22. windowIconSize = 256
  23. )
  24. func (w *window) Title() string {
  25. return w.title
  26. }
  27. func (w *window) SetTitle(title string) {
  28. w.title = title
  29. w.runOnMainWhenCreated(func() {
  30. w.view().SetTitle(title)
  31. })
  32. }
  33. func (w *window) FullScreen() bool {
  34. return w.fullScreen
  35. }
  36. // minSizeOnScreen gets the padded minimum size of a window content in screen pixels
  37. func (w *window) minSizeOnScreen() (int, int) {
  38. // get minimum size of content inside the window
  39. return w.screenSize(w.canvas.MinSize())
  40. }
  41. // screenSize computes the actual output size of the given content size in screen pixels
  42. func (w *window) screenSize(canvasSize fyne.Size) (int, int) {
  43. return internal.ScaleInt(w.canvas, canvasSize.Width), internal.ScaleInt(w.canvas, canvasSize.Height)
  44. }
  45. func (w *window) Resize(size fyne.Size) {
  46. // we cannot perform this until window is prepared as we don't know it's scale!
  47. bigEnough := size.Max(w.canvas.canvasSize(w.canvas.Content().MinSize()))
  48. w.runOnMainWhenCreated(func() {
  49. w.viewLock.Lock()
  50. width, height := internal.ScaleInt(w.canvas, bigEnough.Width), internal.ScaleInt(w.canvas, bigEnough.Height)
  51. if w.fixedSize || !w.visible { // fixed size ignores future `resized` and if not visible we may not get the event
  52. w.shouldWidth, w.shouldHeight = width, height
  53. w.width, w.height = width, height
  54. }
  55. w.viewLock.Unlock()
  56. w.requestedWidth, w.requestedHeight = width, height
  57. w.view().SetSize(width, height)
  58. })
  59. }
  60. func (w *window) FixedSize() bool {
  61. return w.fixedSize
  62. }
  63. func (w *window) SetFixedSize(fixed bool) {
  64. w.fixedSize = fixed
  65. if w.view() != nil {
  66. w.runOnMainWhenCreated(w.fitContent)
  67. }
  68. }
  69. func (w *window) Padded() bool {
  70. return w.canvas.padded
  71. }
  72. func (w *window) SetPadded(padded bool) {
  73. w.canvas.SetPadded(padded)
  74. w.runOnMainWhenCreated(w.fitContent)
  75. }
  76. func (w *window) Icon() fyne.Resource {
  77. if w.icon == nil {
  78. return fyne.CurrentApp().Icon()
  79. }
  80. return w.icon
  81. }
  82. func (w *window) MainMenu() *fyne.MainMenu {
  83. return w.mainmenu
  84. }
  85. func (w *window) SetMainMenu(menu *fyne.MainMenu) {
  86. w.mainmenu = menu
  87. w.runOnMainWhenCreated(func() {
  88. w.canvas.buildMenu(w, menu)
  89. })
  90. }
  91. func (w *window) SetOnClosed(closed func()) {
  92. w.onClosed = closed
  93. }
  94. func (w *window) SetCloseIntercept(callback func()) {
  95. w.onCloseIntercepted = callback
  96. }
  97. func (w *window) calculatedScale() float32 {
  98. return calculateScale(userScale(), fyne.CurrentDevice().SystemScaleForWindow(w), w.detectScale())
  99. }
  100. func (w *window) detectTextureScale() float32 {
  101. winWidth, _ := w.view().GetSize()
  102. texWidth, _ := w.view().GetFramebufferSize()
  103. return float32(texWidth) / float32(winWidth)
  104. }
  105. func (w *window) Show() {
  106. go w.doShow()
  107. }
  108. func (w *window) doShow() {
  109. if w.view() != nil {
  110. w.doShowAgain()
  111. return
  112. }
  113. run.Lock()
  114. for !run.flag {
  115. run.cond.Wait()
  116. }
  117. run.Unlock()
  118. w.createLock.Do(w.create)
  119. if w.view() == nil {
  120. return
  121. }
  122. runOnMain(func() {
  123. w.viewLock.Lock()
  124. w.visible = true
  125. w.viewLock.Unlock()
  126. w.view().SetTitle(w.title)
  127. if w.centered {
  128. w.doCenterOnScreen() // lastly center if that was requested
  129. }
  130. w.view().Show()
  131. // save coordinates
  132. w.xpos, w.ypos = w.view().GetPos()
  133. if w.fullScreen { // this does not work if called before viewport.Show()
  134. go func() {
  135. time.Sleep(time.Millisecond * 100)
  136. w.SetFullScreen(true)
  137. }()
  138. }
  139. })
  140. // show top canvas element
  141. if w.canvas.Content() != nil {
  142. w.canvas.Content().Show()
  143. }
  144. }
  145. func (w *window) Hide() {
  146. runOnMain(func() {
  147. w.viewLock.Lock()
  148. if w.closing || w.viewport == nil {
  149. w.viewLock.Unlock()
  150. return
  151. }
  152. w.visible = false
  153. v := w.viewport
  154. w.viewLock.Unlock()
  155. v.Hide()
  156. // hide top canvas element
  157. if w.canvas.Content() != nil {
  158. w.canvas.Content().Hide()
  159. }
  160. })
  161. }
  162. func (w *window) Close() {
  163. if w.isClosing() {
  164. return
  165. }
  166. // trigger callbacks - early so window still exists
  167. if w.onClosed != nil {
  168. w.QueueEvent(w.onClosed)
  169. }
  170. // set w.closing flag inside draw thread to ensure we can free textures
  171. runOnDraw(w, func() {
  172. w.viewLock.Lock()
  173. w.closing = true
  174. w.viewLock.Unlock()
  175. w.viewport.SetShouldClose(true)
  176. cache.RangeTexturesFor(w.canvas, func(obj fyne.CanvasObject) {
  177. w.canvas.Painter().Free(obj)
  178. })
  179. })
  180. w.canvas.WalkTrees(nil, func(node *common.RenderCacheNode) {
  181. if wid, ok := node.Obj().(fyne.Widget); ok {
  182. cache.DestroyRenderer(wid)
  183. }
  184. })
  185. }
  186. func (w *window) ShowAndRun() {
  187. w.Show()
  188. w.driver.Run()
  189. }
  190. // Clipboard returns the system clipboard
  191. func (w *window) Clipboard() fyne.Clipboard {
  192. if w.view() == nil {
  193. return nil
  194. }
  195. if w.clipboard == nil {
  196. w.clipboard = &clipboard{window: w.viewport}
  197. }
  198. return w.clipboard
  199. }
  200. func (w *window) Content() fyne.CanvasObject {
  201. return w.canvas.Content()
  202. }
  203. func (w *window) SetContent(content fyne.CanvasObject) {
  204. w.viewLock.RLock()
  205. visible := w.visible
  206. w.viewLock.RUnlock()
  207. // hide old canvas element
  208. if visible && w.canvas.Content() != nil {
  209. w.canvas.Content().Hide()
  210. }
  211. w.canvas.SetContent(content)
  212. // show new canvas element
  213. if content != nil {
  214. content.Show()
  215. }
  216. w.RescaleContext()
  217. }
  218. func (w *window) Canvas() fyne.Canvas {
  219. return w.canvas
  220. }
  221. func (w *window) processClosed() {
  222. if w.onCloseIntercepted != nil {
  223. w.QueueEvent(w.onCloseIntercepted)
  224. return
  225. }
  226. go w.Close() // unsure which thread this comes from, so don't block
  227. }
  228. // destroy this window and, if it's the last window quit the app
  229. func (w *window) destroy(d *gLDriver) {
  230. w.DestroyEventQueue()
  231. cache.CleanCanvas(w.canvas)
  232. if w.master {
  233. d.Quit()
  234. } else if runtime.GOOS == "darwin" {
  235. go d.focusPreviousWindow()
  236. }
  237. }
  238. func (w *window) processMoved(x, y int) {
  239. if !w.fullScreen { // don't save the move to top left when changing to fullscreen
  240. // save coordinates
  241. w.xpos, w.ypos = x, y
  242. }
  243. if w.canvas.detectedScale == w.detectScale() {
  244. return
  245. }
  246. w.canvas.detectedScale = w.detectScale()
  247. go w.canvas.reloadScale()
  248. }
  249. func (w *window) processResized(width, height int) {
  250. canvasSize := w.computeCanvasSize(width, height)
  251. if !w.fullScreen {
  252. w.width = internal.ScaleInt(w.canvas, canvasSize.Width)
  253. w.height = internal.ScaleInt(w.canvas, canvasSize.Height)
  254. }
  255. if !w.visible { // don't redraw if hidden
  256. w.canvas.Resize(canvasSize)
  257. return
  258. }
  259. if w.fixedSize {
  260. w.canvas.Resize(canvasSize)
  261. w.fitContent()
  262. return
  263. }
  264. w.platformResize(canvasSize)
  265. }
  266. func (w *window) processFrameSized(width, height int) {
  267. if width == 0 || height == 0 || runtime.GOOS != "darwin" {
  268. return
  269. }
  270. winWidth, _ := w.view().GetSize()
  271. newTexScale := float32(width) / float32(winWidth) // This will be > 1.0 on a HiDPI screen
  272. w.canvas.RLock()
  273. texScale := w.canvas.texScale
  274. w.canvas.RUnlock()
  275. if texScale != newTexScale {
  276. w.canvas.Lock()
  277. w.canvas.texScale = newTexScale
  278. w.canvas.Unlock()
  279. w.canvas.Refresh(w.canvas.Content()) // reset graphics to apply texture scale
  280. }
  281. }
  282. func (w *window) processRefresh() {
  283. refreshWindow(w)
  284. }
  285. func (w *window) findObjectAtPositionMatching(canvas *glCanvas, mouse fyne.Position, matches func(object fyne.CanvasObject) bool) (fyne.CanvasObject, fyne.Position, int) {
  286. return driver.FindObjectAtPositionMatching(mouse, matches, canvas.Overlays().Top(), canvas.menu, canvas.Content())
  287. }
  288. func (w *window) processMouseMoved(xpos float64, ypos float64) {
  289. w.mouseLock.Lock()
  290. previousPos := w.mousePos
  291. w.mousePos = fyne.NewPos(internal.UnscaleInt(w.canvas, int(xpos)), internal.UnscaleInt(w.canvas, int(ypos)))
  292. mousePos := w.mousePos
  293. mouseButton := w.mouseButton
  294. mouseDragPos := w.mouseDragPos
  295. mouseOver := w.mouseOver
  296. w.mouseLock.Unlock()
  297. cursor := desktop.Cursor(desktop.DefaultCursor)
  298. obj, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
  299. if cursorable, ok := object.(desktop.Cursorable); ok {
  300. cursor = cursorable.Cursor()
  301. }
  302. _, hover := object.(desktop.Hoverable)
  303. return hover
  304. })
  305. if w.cursor != cursor {
  306. // cursor has changed, store new cursor and apply change via glfw
  307. rawCursor, isCustomCursor := fyneToNativeCursor(cursor)
  308. w.cursor = cursor
  309. if rawCursor == nil {
  310. w.view().SetInputMode(CursorMode, CursorHidden)
  311. } else {
  312. w.view().SetInputMode(CursorMode, CursorNormal)
  313. w.SetCursor(rawCursor)
  314. }
  315. w.setCustomCursor(rawCursor, isCustomCursor)
  316. }
  317. if w.mouseButton != 0 && w.mouseButton != desktop.MouseButtonSecondary && !w.mouseDragStarted {
  318. obj, pos, _ := w.findObjectAtPositionMatching(w.canvas, previousPos, func(object fyne.CanvasObject) bool {
  319. _, ok := object.(fyne.Draggable)
  320. return ok
  321. })
  322. deltaX := mousePos.X - mouseDragPos.X
  323. deltaY := mousePos.Y - mouseDragPos.Y
  324. overThreshold := math.Abs(float64(deltaX)) >= dragMoveThreshold || math.Abs(float64(deltaY)) >= dragMoveThreshold
  325. if wid, ok := obj.(fyne.Draggable); ok && overThreshold {
  326. w.mouseLock.Lock()
  327. w.mouseDragged = wid
  328. w.mouseDraggedOffset = previousPos.Subtract(pos)
  329. w.mouseDraggedObjStart = obj.Position()
  330. w.mouseDragStarted = true
  331. w.mouseLock.Unlock()
  332. }
  333. }
  334. w.mouseLock.RLock()
  335. isObjDragged := w.objIsDragged(obj)
  336. isMouseOverDragged := w.objIsDragged(mouseOver)
  337. w.mouseLock.RUnlock()
  338. if obj != nil && !isObjDragged {
  339. ev := new(desktop.MouseEvent)
  340. ev.AbsolutePosition = mousePos
  341. ev.Position = pos
  342. ev.Button = mouseButton
  343. if hovered, ok := obj.(desktop.Hoverable); ok {
  344. if hovered == mouseOver {
  345. w.QueueEvent(func() { hovered.MouseMoved(ev) })
  346. } else {
  347. w.mouseOut()
  348. w.mouseIn(hovered, ev)
  349. }
  350. } else if mouseOver != nil {
  351. isChild := false
  352. driver.WalkCompleteObjectTree(mouseOver.(fyne.CanvasObject),
  353. func(co fyne.CanvasObject, p1, p2 fyne.Position, s fyne.Size) bool {
  354. if co == obj {
  355. isChild = true
  356. return true
  357. }
  358. return false
  359. }, nil)
  360. if !isChild {
  361. w.mouseOut()
  362. }
  363. }
  364. } else if mouseOver != nil && !isMouseOverDragged {
  365. w.mouseOut()
  366. }
  367. w.mouseLock.RLock()
  368. mouseButton = w.mouseButton
  369. mouseDragged := w.mouseDragged
  370. mouseDraggedObjStart := w.mouseDraggedObjStart
  371. mouseDraggedOffset := w.mouseDraggedOffset
  372. mouseDragPos = w.mouseDragPos
  373. w.mouseLock.RUnlock()
  374. if mouseDragged != nil && mouseButton != desktop.MouseButtonSecondary {
  375. if w.mouseButton > 0 {
  376. draggedObjDelta := mouseDraggedObjStart.Subtract(mouseDragged.(fyne.CanvasObject).Position())
  377. ev := new(fyne.DragEvent)
  378. ev.AbsolutePosition = mousePos
  379. ev.Position = mousePos.Subtract(mouseDraggedOffset).Add(draggedObjDelta)
  380. ev.Dragged = fyne.NewDelta(mousePos.X-mouseDragPos.X, mousePos.Y-mouseDragPos.Y)
  381. wd := mouseDragged
  382. w.QueueEvent(func() { wd.Dragged(ev) })
  383. }
  384. w.mouseLock.Lock()
  385. w.mouseDragStarted = true
  386. w.mouseDragPos = mousePos
  387. w.mouseLock.Unlock()
  388. }
  389. }
  390. func (w *window) objIsDragged(obj interface{}) bool {
  391. if w.mouseDragged != nil && obj != nil {
  392. draggedObj, _ := obj.(fyne.Draggable)
  393. return draggedObj == w.mouseDragged
  394. }
  395. return false
  396. }
  397. func (w *window) mouseIn(obj desktop.Hoverable, ev *desktop.MouseEvent) {
  398. w.QueueEvent(func() {
  399. if obj != nil {
  400. obj.MouseIn(ev)
  401. }
  402. w.mouseLock.Lock()
  403. w.mouseOver = obj
  404. w.mouseLock.Unlock()
  405. })
  406. }
  407. func (w *window) mouseOut() {
  408. w.QueueEvent(func() {
  409. w.mouseLock.RLock()
  410. mouseOver := w.mouseOver
  411. w.mouseLock.RUnlock()
  412. if mouseOver != nil {
  413. mouseOver.MouseOut()
  414. w.mouseLock.Lock()
  415. w.mouseOver = nil
  416. w.mouseLock.Unlock()
  417. }
  418. })
  419. }
  420. func (w *window) processMouseClicked(button desktop.MouseButton, action action, modifiers fyne.KeyModifier) {
  421. w.mouseLock.RLock()
  422. w.mouseDragPos = w.mousePos
  423. mousePos := w.mousePos
  424. mouseDragStarted := w.mouseDragStarted
  425. w.mouseLock.RUnlock()
  426. if mousePos.IsZero() { // window may not be focused (darwin mostly) and so position callbacks not happening
  427. xpos, ypos := w.view().GetCursorPos()
  428. w.mouseLock.Lock()
  429. w.mousePos = fyne.NewPos(internal.UnscaleInt(w.canvas, int(xpos)), internal.UnscaleInt(w.canvas, int(ypos)))
  430. mousePos = w.mousePos
  431. w.mouseLock.Unlock()
  432. }
  433. co, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
  434. switch object.(type) {
  435. case fyne.Tappable, fyne.SecondaryTappable, fyne.DoubleTappable, fyne.Focusable, desktop.Mouseable, desktop.Hoverable:
  436. return true
  437. case fyne.Draggable:
  438. if mouseDragStarted {
  439. return true
  440. }
  441. }
  442. return false
  443. })
  444. ev := new(fyne.PointEvent)
  445. ev.Position = pos
  446. ev.AbsolutePosition = mousePos
  447. coMouse := co
  448. if wid, ok := co.(desktop.Mouseable); ok {
  449. mev := new(desktop.MouseEvent)
  450. mev.Position = ev.Position
  451. mev.AbsolutePosition = mousePos
  452. mev.Button = button
  453. mev.Modifier = modifiers
  454. w.mouseClickedHandleMouseable(mev, action, wid)
  455. }
  456. if wid, ok := co.(fyne.Focusable); !ok || wid != w.canvas.Focused() {
  457. w.canvas.Unfocus()
  458. }
  459. w.mouseLock.Lock()
  460. if action == press {
  461. w.mouseButton |= button
  462. } else if action == release {
  463. w.mouseButton &= ^button
  464. }
  465. mouseDragged := w.mouseDragged
  466. mouseDragStarted = w.mouseDragStarted
  467. mouseOver := w.mouseOver
  468. shouldMouseOut := w.objIsDragged(mouseOver) && !w.objIsDragged(coMouse)
  469. mousePressed := w.mousePressed
  470. w.mouseLock.Unlock()
  471. if action == release && mouseDragged != nil {
  472. if mouseDragStarted {
  473. w.QueueEvent(mouseDragged.DragEnd)
  474. w.mouseLock.Lock()
  475. w.mouseDragStarted = false
  476. w.mouseLock.Unlock()
  477. }
  478. if shouldMouseOut {
  479. w.mouseOut()
  480. }
  481. w.mouseLock.Lock()
  482. w.mouseDragged = nil
  483. w.mouseLock.Unlock()
  484. }
  485. _, tap := co.(fyne.Tappable)
  486. _, altTap := co.(fyne.SecondaryTappable)
  487. if tap || altTap {
  488. if action == press {
  489. w.mouseLock.Lock()
  490. w.mousePressed = co
  491. w.mouseLock.Unlock()
  492. } else if action == release {
  493. if co == mousePressed {
  494. if button == desktop.MouseButtonSecondary && altTap {
  495. w.QueueEvent(func() { co.(fyne.SecondaryTappable).TappedSecondary(ev) })
  496. }
  497. }
  498. }
  499. }
  500. // Check for double click/tap on left mouse button
  501. if action == release && button == desktop.MouseButtonPrimary && !mouseDragStarted {
  502. w.mouseClickedHandleTapDoubleTap(co, ev)
  503. }
  504. }
  505. func (w *window) mouseClickedHandleMouseable(mev *desktop.MouseEvent, action action, wid desktop.Mouseable) {
  506. mousePos := mev.AbsolutePosition
  507. if action == press {
  508. w.QueueEvent(func() { wid.MouseDown(mev) })
  509. } else if action == release {
  510. w.mouseLock.RLock()
  511. mouseDragged := w.mouseDragged
  512. mouseDraggedOffset := w.mouseDraggedOffset
  513. w.mouseLock.RUnlock()
  514. if mouseDragged == nil {
  515. w.QueueEvent(func() { wid.MouseUp(mev) })
  516. } else {
  517. if dragged, ok := mouseDragged.(desktop.Mouseable); ok {
  518. mev.Position = mousePos.Subtract(mouseDraggedOffset)
  519. w.QueueEvent(func() { dragged.MouseUp(mev) })
  520. } else {
  521. w.QueueEvent(func() { wid.MouseUp(mev) })
  522. }
  523. }
  524. }
  525. }
  526. func (w *window) mouseClickedHandleTapDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
  527. _, doubleTap := co.(fyne.DoubleTappable)
  528. if doubleTap {
  529. w.mouseLock.Lock()
  530. w.mouseClickCount++
  531. w.mouseLastClick = co
  532. mouseCancelFunc := w.mouseCancelFunc
  533. w.mouseLock.Unlock()
  534. if mouseCancelFunc != nil {
  535. mouseCancelFunc()
  536. return
  537. }
  538. go w.waitForDoubleTap(co, ev)
  539. } else {
  540. w.mouseLock.Lock()
  541. if wid, ok := co.(fyne.Tappable); ok && co == w.mousePressed {
  542. w.QueueEvent(func() { wid.Tapped(ev) })
  543. }
  544. w.mousePressed = nil
  545. w.mouseLock.Unlock()
  546. }
  547. }
  548. func (w *window) waitForDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
  549. var ctx context.Context
  550. w.mouseLock.Lock()
  551. ctx, w.mouseCancelFunc = context.WithDeadline(context.TODO(), time.Now().Add(time.Millisecond*doubleClickDelay))
  552. defer w.mouseCancelFunc()
  553. w.mouseLock.Unlock()
  554. <-ctx.Done()
  555. w.mouseLock.Lock()
  556. defer w.mouseLock.Unlock()
  557. if w.mouseClickCount == 2 && w.mouseLastClick == co {
  558. if wid, ok := co.(fyne.DoubleTappable); ok {
  559. w.QueueEvent(func() { wid.DoubleTapped(ev) })
  560. }
  561. } else if co == w.mousePressed {
  562. if wid, ok := co.(fyne.Tappable); ok {
  563. w.QueueEvent(func() { wid.Tapped(ev) })
  564. }
  565. }
  566. w.mouseClickCount = 0
  567. w.mousePressed = nil
  568. w.mouseCancelFunc = nil
  569. w.mouseLastClick = nil
  570. }
  571. func (w *window) processMouseScrolled(xoff float64, yoff float64) {
  572. w.mouseLock.RLock()
  573. mousePos := w.mousePos
  574. w.mouseLock.RUnlock()
  575. co, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
  576. _, ok := object.(fyne.Scrollable)
  577. return ok
  578. })
  579. switch wid := co.(type) {
  580. case fyne.Scrollable:
  581. if math.Abs(xoff) >= scrollAccelerateCutoff {
  582. xoff *= scrollAccelerateRate
  583. }
  584. if math.Abs(yoff) >= scrollAccelerateCutoff {
  585. yoff *= scrollAccelerateRate
  586. }
  587. ev := &fyne.ScrollEvent{}
  588. ev.Scrolled = fyne.NewDelta(float32(xoff)*scrollSpeed, float32(yoff)*scrollSpeed)
  589. ev.Position = pos
  590. ev.AbsolutePosition = mousePos
  591. wid.Scrolled(ev)
  592. }
  593. }
  594. func (w *window) capturesTab(modifier fyne.KeyModifier) bool {
  595. captures := false
  596. if ent, ok := w.canvas.Focused().(fyne.Tabbable); ok {
  597. captures = ent.AcceptsTab()
  598. }
  599. if !captures {
  600. switch modifier {
  601. case 0:
  602. w.QueueEvent(w.canvas.FocusNext)
  603. return false
  604. case fyne.KeyModifierShift:
  605. w.QueueEvent(w.canvas.FocusPrevious)
  606. return false
  607. }
  608. }
  609. return captures
  610. }
  611. func (w *window) processKeyPressed(keyName fyne.KeyName, keyASCII fyne.KeyName, scancode int, action action, keyDesktopModifier fyne.KeyModifier) {
  612. keyEvent := &fyne.KeyEvent{Name: keyName, Physical: fyne.HardwareKey{ScanCode: scancode}}
  613. pendingMenuToggle := w.menuTogglePending
  614. pendingMenuDeactivation := w.menuDeactivationPending
  615. w.menuTogglePending = desktop.KeyNone
  616. w.menuDeactivationPending = desktop.KeyNone
  617. switch action {
  618. case release:
  619. if action == release && keyName != "" {
  620. switch keyName {
  621. case pendingMenuToggle:
  622. w.canvas.ToggleMenu()
  623. case pendingMenuDeactivation:
  624. if w.canvas.DismissMenu() {
  625. return
  626. }
  627. }
  628. }
  629. if w.canvas.Focused() != nil {
  630. if focused, ok := w.canvas.Focused().(desktop.Keyable); ok {
  631. w.QueueEvent(func() { focused.KeyUp(keyEvent) })
  632. }
  633. } else if w.canvas.onKeyUp != nil {
  634. w.QueueEvent(func() { w.canvas.onKeyUp(keyEvent) })
  635. }
  636. return // ignore key up in other core events
  637. case press:
  638. switch keyName {
  639. case desktop.KeyAltLeft, desktop.KeyAltRight:
  640. // compensate for GLFW modifiers bug https://github.com/glfw/glfw/issues/1630
  641. if (runtime.GOOS == "linux" && keyDesktopModifier == 0) || (runtime.GOOS != "linux" && keyDesktopModifier == fyne.KeyModifierAlt) {
  642. w.menuTogglePending = keyName
  643. }
  644. case fyne.KeyEscape:
  645. w.menuDeactivationPending = keyName
  646. }
  647. if w.canvas.Focused() != nil {
  648. if focused, ok := w.canvas.Focused().(desktop.Keyable); ok {
  649. w.QueueEvent(func() { focused.KeyDown(keyEvent) })
  650. }
  651. } else if w.canvas.onKeyDown != nil {
  652. w.QueueEvent(func() { w.canvas.onKeyDown(keyEvent) })
  653. }
  654. default:
  655. // key repeat will fall through to TypedKey and TypedShortcut
  656. }
  657. modifierOtherThanShift := (keyDesktopModifier & fyne.KeyModifierControl) |
  658. (keyDesktopModifier & fyne.KeyModifierAlt) |
  659. (keyDesktopModifier & fyne.KeyModifierSuper)
  660. if (keyName == fyne.KeyTab && modifierOtherThanShift == 0 && !w.capturesTab(keyDesktopModifier)) ||
  661. w.triggersShortcut(keyName, keyASCII, keyDesktopModifier) {
  662. return
  663. }
  664. // No shortcut detected, pass down to TypedKey
  665. focused := w.canvas.Focused()
  666. if focused != nil {
  667. w.QueueEvent(func() { focused.TypedKey(keyEvent) })
  668. } else if w.canvas.onTypedKey != nil {
  669. w.QueueEvent(func() { w.canvas.onTypedKey(keyEvent) })
  670. }
  671. }
  672. // charInput defines the character with modifiers callback which is called when a
  673. // Unicode character is input.
  674. //
  675. // Characters do not map 1:1 to physical keys, as a key may produce zero, one or more characters.
  676. func (w *window) processCharInput(char rune) {
  677. if focused := w.canvas.Focused(); focused != nil {
  678. w.QueueEvent(func() { focused.TypedRune(char) })
  679. } else if w.canvas.onTypedRune != nil {
  680. w.QueueEvent(func() { w.canvas.onTypedRune(char) })
  681. }
  682. }
  683. func (w *window) processFocused(focus bool) {
  684. if focus {
  685. if curWindow == nil {
  686. fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerEnteredForeground()
  687. }
  688. curWindow = w
  689. w.canvas.FocusGained()
  690. } else {
  691. w.canvas.FocusLost()
  692. w.mouseLock.Lock()
  693. w.mousePos = fyne.Position{}
  694. w.mouseLock.Unlock()
  695. go func() { // check whether another window was focused or not
  696. time.Sleep(time.Millisecond * 100)
  697. if curWindow != w {
  698. return
  699. }
  700. curWindow = nil
  701. fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerExitedForeground()
  702. }()
  703. }
  704. }
  705. func (w *window) triggersShortcut(localizedKeyName fyne.KeyName, key fyne.KeyName, modifier fyne.KeyModifier) bool {
  706. var shortcut fyne.Shortcut
  707. ctrlMod := fyne.KeyModifierControl
  708. if runtime.GOOS == "darwin" {
  709. ctrlMod = fyne.KeyModifierSuper
  710. }
  711. // User pressing physical keys Ctrl+V while using a Russian (or any non-ASCII) keyboard layout
  712. // is reported as a fyne.KeyUnknown key with Control modifier. We should still consider this
  713. // as a "Paste" shortcut.
  714. // See https://github.com/fyne-io/fyne/pull/2587 for discussion.
  715. keyName := localizedKeyName
  716. resemblesShortcut := (modifier&(fyne.KeyModifierControl|fyne.KeyModifierSuper) != 0)
  717. if (localizedKeyName == fyne.KeyUnknown) && resemblesShortcut {
  718. if key != fyne.KeyUnknown {
  719. keyName = key
  720. }
  721. }
  722. if modifier == ctrlMod {
  723. switch keyName {
  724. case fyne.KeyV:
  725. // detect paste shortcut
  726. shortcut = &fyne.ShortcutPaste{
  727. Clipboard: w.Clipboard(),
  728. }
  729. case fyne.KeyC, fyne.KeyInsert:
  730. // detect copy shortcut
  731. shortcut = &fyne.ShortcutCopy{
  732. Clipboard: w.Clipboard(),
  733. }
  734. case fyne.KeyX:
  735. // detect cut shortcut
  736. shortcut = &fyne.ShortcutCut{
  737. Clipboard: w.Clipboard(),
  738. }
  739. case fyne.KeyA:
  740. // detect selectAll shortcut
  741. shortcut = &fyne.ShortcutSelectAll{}
  742. }
  743. }
  744. if modifier == fyne.KeyModifierShift {
  745. switch keyName {
  746. case fyne.KeyInsert:
  747. // detect paste shortcut
  748. shortcut = &fyne.ShortcutPaste{
  749. Clipboard: w.Clipboard(),
  750. }
  751. case fyne.KeyDelete:
  752. // detect cut shortcut
  753. shortcut = &fyne.ShortcutCut{
  754. Clipboard: w.Clipboard(),
  755. }
  756. }
  757. }
  758. if shortcut == nil && modifier != 0 && !isKeyModifier(keyName) && modifier != fyne.KeyModifierShift {
  759. shortcut = &desktop.CustomShortcut{
  760. KeyName: keyName,
  761. Modifier: modifier,
  762. }
  763. }
  764. if shortcut != nil {
  765. if focused, ok := w.canvas.Focused().(fyne.Shortcutable); ok {
  766. shouldRunShortcut := true
  767. type selectableText interface {
  768. fyne.Disableable
  769. SelectedText() string
  770. }
  771. if selectableTextWid, ok := focused.(selectableText); ok && selectableTextWid.Disabled() {
  772. shouldRunShortcut = shortcut.ShortcutName() == "Copy"
  773. }
  774. if shouldRunShortcut {
  775. w.QueueEvent(func() { focused.TypedShortcut(shortcut) })
  776. }
  777. return shouldRunShortcut
  778. }
  779. w.QueueEvent(func() { w.canvas.TypedShortcut(shortcut) })
  780. return true
  781. }
  782. return false
  783. }
  784. func (w *window) RunWithContext(f func()) {
  785. if w.isClosing() {
  786. return
  787. }
  788. w.view().MakeContextCurrent()
  789. f()
  790. w.DetachCurrentContext()
  791. }
  792. func (w *window) RescaleContext() {
  793. runOnMain(func() {
  794. w.rescaleOnMain()
  795. })
  796. }
  797. func (w *window) Context() interface{} {
  798. return nil
  799. }
  800. func (w *window) runOnMainWhenCreated(fn func()) {
  801. if w.view() != nil {
  802. runOnMain(fn)
  803. return
  804. }
  805. w.pending = append(w.pending, fn)
  806. }
  807. func (d *gLDriver) CreateWindow(title string) fyne.Window {
  808. return d.createWindow(title, true)
  809. }
  810. func (d *gLDriver) createWindow(title string, decorate bool) fyne.Window {
  811. var ret *window
  812. if title == "" {
  813. title = defaultTitle
  814. }
  815. runOnMain(func() {
  816. d.initGLFW()
  817. ret = &window{title: title, decorate: decorate, driver: d}
  818. // This queue is destroyed when the window is closed.
  819. ret.InitEventQueue()
  820. go ret.RunEventQueue()
  821. ret.canvas = newCanvas()
  822. ret.canvas.context = ret
  823. ret.SetIcon(ret.icon)
  824. d.addWindow(ret)
  825. })
  826. return ret
  827. }
  828. func (w *window) doShowAgain() {
  829. if w.isClosing() {
  830. return
  831. }
  832. runOnMain(func() {
  833. // show top canvas element
  834. if w.canvas.Content() != nil {
  835. w.canvas.Content().Show()
  836. }
  837. w.view().SetPos(w.xpos, w.ypos)
  838. w.view().Show()
  839. w.viewLock.Lock()
  840. w.visible = true
  841. w.viewLock.Unlock()
  842. })
  843. }
  844. func (w *window) isClosing() bool {
  845. w.viewLock.RLock()
  846. closing := w.closing || w.viewport == nil
  847. w.viewLock.RUnlock()
  848. return closing
  849. }
  850. func (d *gLDriver) CreateSplashWindow() fyne.Window {
  851. win := d.createWindow("", false)
  852. win.SetPadded(false)
  853. win.CenterOnScreen()
  854. return win
  855. }
  856. func (d *gLDriver) AllWindows() []fyne.Window {
  857. return d.windows
  858. }
  859. func isKeyModifier(keyName fyne.KeyName) bool {
  860. return keyName == desktop.KeyShiftLeft || keyName == desktop.KeyShiftRight ||
  861. keyName == desktop.KeyControlLeft || keyName == desktop.KeyControlRight ||
  862. keyName == desktop.KeyAltLeft || keyName == desktop.KeyAltRight ||
  863. keyName == desktop.KeySuperLeft || keyName == desktop.KeySuperRight
  864. }