window.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  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/app"
  11. "fyne.io/fyne/v2/internal/cache"
  12. "fyne.io/fyne/v2/internal/driver"
  13. "fyne.io/fyne/v2/internal/driver/common"
  14. "fyne.io/fyne/v2/internal/scale"
  15. )
  16. const (
  17. doubleClickDelay = 300 // ms (maximum interval between clicks for double click detection)
  18. dragMoveThreshold = 2 // how far can we move before it is a drag
  19. windowIconSize = 256
  20. )
  21. func (w *window) Title() string {
  22. return w.title
  23. }
  24. func (w *window) SetTitle(title string) {
  25. w.title = title
  26. w.runOnMainWhenCreated(func() {
  27. w.view().SetTitle(title)
  28. })
  29. }
  30. func (w *window) FullScreen() bool {
  31. return w.fullScreen
  32. }
  33. // minSizeOnScreen gets the padded minimum size of a window content in screen pixels
  34. func (w *window) minSizeOnScreen() (int, int) {
  35. // get minimum size of content inside the window
  36. return w.screenSize(w.canvas.MinSize())
  37. }
  38. // screenSize computes the actual output size of the given content size in screen pixels
  39. func (w *window) screenSize(canvasSize fyne.Size) (int, int) {
  40. return scale.ToScreenCoordinate(w.canvas, canvasSize.Width), scale.ToScreenCoordinate(w.canvas, canvasSize.Height)
  41. }
  42. func (w *window) Resize(size fyne.Size) {
  43. // we cannot perform this until window is prepared as we don't know it's scale!
  44. bigEnough := size.Max(w.canvas.canvasSize(w.canvas.Content().MinSize()))
  45. w.runOnMainWhenCreated(func() {
  46. w.viewLock.Lock()
  47. width, height := scale.ToScreenCoordinate(w.canvas, bigEnough.Width), scale.ToScreenCoordinate(w.canvas, bigEnough.Height)
  48. if w.fixedSize || !w.visible { // fixed size ignores future `resized` and if not visible we may not get the event
  49. w.shouldWidth, w.shouldHeight = width, height
  50. w.width, w.height = width, height
  51. }
  52. w.viewLock.Unlock()
  53. w.requestedWidth, w.requestedHeight = width, height
  54. w.view().SetSize(width, height)
  55. })
  56. }
  57. func (w *window) FixedSize() bool {
  58. return w.fixedSize
  59. }
  60. func (w *window) SetFixedSize(fixed bool) {
  61. w.fixedSize = fixed
  62. if w.view() != nil {
  63. w.runOnMainWhenCreated(w.fitContent)
  64. }
  65. }
  66. func (w *window) Padded() bool {
  67. return w.canvas.padded
  68. }
  69. func (w *window) SetPadded(padded bool) {
  70. w.canvas.SetPadded(padded)
  71. w.runOnMainWhenCreated(w.fitContent)
  72. }
  73. func (w *window) Icon() fyne.Resource {
  74. if w.icon == nil {
  75. return fyne.CurrentApp().Icon()
  76. }
  77. return w.icon
  78. }
  79. func (w *window) MainMenu() *fyne.MainMenu {
  80. return w.mainmenu
  81. }
  82. func (w *window) SetMainMenu(menu *fyne.MainMenu) {
  83. w.mainmenu = menu
  84. w.runOnMainWhenCreated(func() {
  85. w.canvas.buildMenu(w, menu)
  86. })
  87. }
  88. func (w *window) SetOnClosed(closed func()) {
  89. w.onClosed = closed
  90. }
  91. func (w *window) SetCloseIntercept(callback func()) {
  92. w.onCloseIntercepted = callback
  93. }
  94. func (w *window) calculatedScale() float32 {
  95. return calculateScale(userScale(), fyne.CurrentDevice().SystemScaleForWindow(w), w.detectScale())
  96. }
  97. func (w *window) detectTextureScale() float32 {
  98. winWidth, _ := w.view().GetSize()
  99. texWidth, _ := w.view().GetFramebufferSize()
  100. return float32(texWidth) / float32(winWidth)
  101. }
  102. func (w *window) Show() {
  103. go w.doShow()
  104. }
  105. func (w *window) doShow() {
  106. if w.view() != nil {
  107. w.doShowAgain()
  108. return
  109. }
  110. run.L.Lock()
  111. for !run.flag {
  112. run.Wait()
  113. }
  114. run.L.Unlock()
  115. w.createLock.Do(w.create)
  116. if w.view() == nil {
  117. return
  118. }
  119. runOnMain(func() {
  120. w.viewLock.Lock()
  121. w.visible = true
  122. w.viewLock.Unlock()
  123. w.view().SetTitle(w.title)
  124. if w.centered {
  125. w.doCenterOnScreen() // lastly center if that was requested
  126. }
  127. w.view().Show()
  128. // save coordinates
  129. w.xpos, w.ypos = w.view().GetPos()
  130. if w.fullScreen { // this does not work if called before viewport.Show()
  131. go func() {
  132. time.Sleep(time.Millisecond * 100)
  133. w.SetFullScreen(true)
  134. }()
  135. }
  136. })
  137. // show top canvas element
  138. if w.canvas.Content() != nil {
  139. w.canvas.Content().Show()
  140. runOnDraw(w, func() {
  141. w.driver.repaintWindow(w)
  142. })
  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, _ fyne.Position) {
  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 = scale.ToScreenCoordinate(w.canvas, canvasSize.Width)
  253. w.height = scale.ToScreenCoordinate(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(scale.ToFyneCoordinate(w.canvas, int(xpos)), scale.ToFyneCoordinate(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 := &desktop.MouseEvent{Button: mouseButton}
  340. ev.AbsolutePosition = mousePos
  341. ev.Position = pos
  342. if hovered, ok := obj.(desktop.Hoverable); ok {
  343. if hovered == mouseOver {
  344. w.QueueEvent(func() { hovered.MouseMoved(ev) })
  345. } else {
  346. w.mouseOut()
  347. w.mouseIn(hovered, ev)
  348. }
  349. } else if mouseOver != nil {
  350. isChild := false
  351. driver.WalkCompleteObjectTree(mouseOver.(fyne.CanvasObject),
  352. func(co fyne.CanvasObject, p1, p2 fyne.Position, s fyne.Size) bool {
  353. if co == obj {
  354. isChild = true
  355. return true
  356. }
  357. return false
  358. }, nil)
  359. if !isChild {
  360. w.mouseOut()
  361. }
  362. }
  363. } else if mouseOver != nil && !isMouseOverDragged {
  364. w.mouseOut()
  365. }
  366. w.mouseLock.RLock()
  367. mouseButton = w.mouseButton
  368. mouseDragged := w.mouseDragged
  369. mouseDraggedObjStart := w.mouseDraggedObjStart
  370. mouseDraggedOffset := w.mouseDraggedOffset
  371. mouseDragPos = w.mouseDragPos
  372. w.mouseLock.RUnlock()
  373. if mouseDragged != nil && mouseButton != desktop.MouseButtonSecondary {
  374. if w.mouseButton > 0 {
  375. draggedObjDelta := mouseDraggedObjStart.Subtract(mouseDragged.(fyne.CanvasObject).Position())
  376. ev := &fyne.DragEvent{}
  377. ev.AbsolutePosition = mousePos
  378. ev.Position = mousePos.Subtract(mouseDraggedOffset).Add(draggedObjDelta)
  379. ev.Dragged = fyne.NewDelta(mousePos.X-mouseDragPos.X, mousePos.Y-mouseDragPos.Y)
  380. wd := mouseDragged
  381. w.QueueEvent(func() { wd.Dragged(ev) })
  382. }
  383. w.mouseLock.Lock()
  384. w.mouseDragStarted = true
  385. w.mouseDragPos = mousePos
  386. w.mouseLock.Unlock()
  387. }
  388. }
  389. func (w *window) objIsDragged(obj interface{}) bool {
  390. if w.mouseDragged != nil && obj != nil {
  391. draggedObj, _ := obj.(fyne.Draggable)
  392. return draggedObj == w.mouseDragged
  393. }
  394. return false
  395. }
  396. func (w *window) mouseIn(obj desktop.Hoverable, ev *desktop.MouseEvent) {
  397. w.QueueEvent(func() {
  398. if obj != nil {
  399. obj.MouseIn(ev)
  400. }
  401. w.mouseLock.Lock()
  402. w.mouseOver = obj
  403. w.mouseLock.Unlock()
  404. })
  405. }
  406. func (w *window) mouseOut() {
  407. w.QueueEvent(func() {
  408. w.mouseLock.RLock()
  409. mouseOver := w.mouseOver
  410. w.mouseLock.RUnlock()
  411. if mouseOver != nil {
  412. mouseOver.MouseOut()
  413. w.mouseLock.Lock()
  414. w.mouseOver = nil
  415. w.mouseLock.Unlock()
  416. }
  417. })
  418. }
  419. func (w *window) processMouseClicked(button desktop.MouseButton, action action, modifiers fyne.KeyModifier) {
  420. w.mouseLock.RLock()
  421. w.mouseDragPos = w.mousePos
  422. mousePos := w.mousePos
  423. mouseDragStarted := w.mouseDragStarted
  424. w.mouseLock.RUnlock()
  425. if mousePos.IsZero() { // window may not be focused (darwin mostly) and so position callbacks not happening
  426. xpos, ypos := w.view().GetCursorPos()
  427. w.mouseLock.Lock()
  428. w.mousePos = fyne.NewPos(scale.ToFyneCoordinate(w.canvas, int(xpos)), scale.ToFyneCoordinate(w.canvas, int(ypos)))
  429. mousePos = w.mousePos
  430. w.mouseLock.Unlock()
  431. }
  432. co, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
  433. switch object.(type) {
  434. case fyne.Tappable, fyne.SecondaryTappable, fyne.DoubleTappable, fyne.Focusable, desktop.Mouseable, desktop.Hoverable:
  435. return true
  436. case fyne.Draggable:
  437. if mouseDragStarted {
  438. return true
  439. }
  440. }
  441. return false
  442. })
  443. ev := &fyne.PointEvent{
  444. Position: pos,
  445. AbsolutePosition: mousePos,
  446. }
  447. coMouse := co
  448. if wid, ok := co.(desktop.Mouseable); ok {
  449. mev := &desktop.MouseEvent{
  450. Button: button,
  451. Modifier: modifiers,
  452. }
  453. mev.Position = ev.Position
  454. mev.AbsolutePosition = mousePos
  455. w.mouseClickedHandleMouseable(mev, action, wid)
  456. }
  457. if wid, ok := co.(fyne.Focusable); !ok || wid != w.canvas.Focused() {
  458. ignore := false
  459. _, _, _ = w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
  460. switch object.(type) {
  461. case fyne.Focusable:
  462. ignore = true
  463. return true
  464. }
  465. return false
  466. })
  467. if !ignore { // if a parent item under the mouse has focus then ignore this tap unfocus
  468. w.canvas.Unfocus()
  469. }
  470. }
  471. w.mouseLock.Lock()
  472. if action == press {
  473. w.mouseButton |= button
  474. } else if action == release {
  475. w.mouseButton &= ^button
  476. }
  477. mouseDragged := w.mouseDragged
  478. mouseDragStarted = w.mouseDragStarted
  479. mouseOver := w.mouseOver
  480. shouldMouseOut := w.objIsDragged(mouseOver) && !w.objIsDragged(coMouse)
  481. mousePressed := w.mousePressed
  482. w.mouseLock.Unlock()
  483. if action == release && mouseDragged != nil {
  484. if mouseDragStarted {
  485. w.QueueEvent(mouseDragged.DragEnd)
  486. w.mouseLock.Lock()
  487. w.mouseDragStarted = false
  488. w.mouseLock.Unlock()
  489. }
  490. if shouldMouseOut {
  491. w.mouseOut()
  492. }
  493. w.mouseLock.Lock()
  494. w.mouseDragged = nil
  495. w.mouseLock.Unlock()
  496. }
  497. _, tap := co.(fyne.Tappable)
  498. secondary, altTap := co.(fyne.SecondaryTappable)
  499. if tap || altTap {
  500. if action == press {
  501. w.mouseLock.Lock()
  502. w.mousePressed = co
  503. w.mouseLock.Unlock()
  504. } else if action == release {
  505. if co == mousePressed {
  506. if button == desktop.MouseButtonSecondary && altTap {
  507. w.QueueEvent(func() { secondary.TappedSecondary(ev) })
  508. }
  509. }
  510. }
  511. }
  512. // Check for double click/tap on left mouse button
  513. if action == release && button == desktop.MouseButtonPrimary && !mouseDragStarted {
  514. w.mouseClickedHandleTapDoubleTap(co, ev)
  515. }
  516. }
  517. func (w *window) mouseClickedHandleMouseable(mev *desktop.MouseEvent, action action, wid desktop.Mouseable) {
  518. mousePos := mev.AbsolutePosition
  519. if action == press {
  520. w.QueueEvent(func() { wid.MouseDown(mev) })
  521. } else if action == release {
  522. w.mouseLock.RLock()
  523. mouseDragged := w.mouseDragged
  524. mouseDraggedOffset := w.mouseDraggedOffset
  525. w.mouseLock.RUnlock()
  526. if mouseDragged == nil {
  527. w.QueueEvent(func() { wid.MouseUp(mev) })
  528. } else {
  529. if dragged, ok := mouseDragged.(desktop.Mouseable); ok {
  530. mev.Position = mousePos.Subtract(mouseDraggedOffset)
  531. w.QueueEvent(func() { dragged.MouseUp(mev) })
  532. } else {
  533. w.QueueEvent(func() { wid.MouseUp(mev) })
  534. }
  535. }
  536. }
  537. }
  538. func (w *window) mouseClickedHandleTapDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
  539. _, doubleTap := co.(fyne.DoubleTappable)
  540. if doubleTap {
  541. w.mouseLock.Lock()
  542. w.mouseClickCount++
  543. w.mouseLastClick = co
  544. mouseCancelFunc := w.mouseCancelFunc
  545. w.mouseLock.Unlock()
  546. if mouseCancelFunc != nil {
  547. mouseCancelFunc()
  548. return
  549. }
  550. go w.waitForDoubleTap(co, ev)
  551. } else {
  552. w.mouseLock.Lock()
  553. if wid, ok := co.(fyne.Tappable); ok && co == w.mousePressed {
  554. w.QueueEvent(func() { wid.Tapped(ev) })
  555. }
  556. w.mousePressed = nil
  557. w.mouseLock.Unlock()
  558. }
  559. }
  560. func (w *window) waitForDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
  561. var ctx context.Context
  562. w.mouseLock.Lock()
  563. ctx, w.mouseCancelFunc = context.WithDeadline(context.TODO(), time.Now().Add(time.Millisecond*doubleClickDelay))
  564. defer w.mouseCancelFunc()
  565. w.mouseLock.Unlock()
  566. <-ctx.Done()
  567. w.mouseLock.Lock()
  568. defer w.mouseLock.Unlock()
  569. if w.mouseClickCount == 2 && w.mouseLastClick == co {
  570. if wid, ok := co.(fyne.DoubleTappable); ok {
  571. w.QueueEvent(func() { wid.DoubleTapped(ev) })
  572. }
  573. } else if co == w.mousePressed {
  574. if wid, ok := co.(fyne.Tappable); ok {
  575. w.QueueEvent(func() { wid.Tapped(ev) })
  576. }
  577. }
  578. w.mouseClickCount = 0
  579. w.mousePressed = nil
  580. w.mouseCancelFunc = nil
  581. w.mouseLastClick = nil
  582. }
  583. func (w *window) processMouseScrolled(xoff float64, yoff float64) {
  584. w.mouseLock.RLock()
  585. mousePos := w.mousePos
  586. w.mouseLock.RUnlock()
  587. co, pos, _ := w.findObjectAtPositionMatching(w.canvas, mousePos, func(object fyne.CanvasObject) bool {
  588. _, ok := object.(fyne.Scrollable)
  589. return ok
  590. })
  591. switch wid := co.(type) {
  592. case fyne.Scrollable:
  593. if math.Abs(xoff) >= scrollAccelerateCutoff {
  594. xoff *= scrollAccelerateRate
  595. }
  596. if math.Abs(yoff) >= scrollAccelerateCutoff {
  597. yoff *= scrollAccelerateRate
  598. }
  599. ev := &fyne.ScrollEvent{}
  600. ev.Scrolled = fyne.NewDelta(float32(xoff)*scrollSpeed, float32(yoff)*scrollSpeed)
  601. ev.Position = pos
  602. ev.AbsolutePosition = mousePos
  603. wid.Scrolled(ev)
  604. }
  605. }
  606. func (w *window) capturesTab(modifier fyne.KeyModifier) bool {
  607. captures := false
  608. if ent, ok := w.canvas.Focused().(fyne.Tabbable); ok {
  609. captures = ent.AcceptsTab()
  610. }
  611. if !captures {
  612. switch modifier {
  613. case 0:
  614. w.QueueEvent(w.canvas.FocusNext)
  615. return false
  616. case fyne.KeyModifierShift:
  617. w.QueueEvent(w.canvas.FocusPrevious)
  618. return false
  619. }
  620. }
  621. return captures
  622. }
  623. func (w *window) processKeyPressed(keyName fyne.KeyName, keyASCII fyne.KeyName, scancode int, action action, keyDesktopModifier fyne.KeyModifier) {
  624. keyEvent := &fyne.KeyEvent{Name: keyName, Physical: fyne.HardwareKey{ScanCode: scancode}}
  625. pendingMenuToggle := w.menuTogglePending
  626. pendingMenuDeactivation := w.menuDeactivationPending
  627. w.menuTogglePending = desktop.KeyNone
  628. w.menuDeactivationPending = desktop.KeyNone
  629. switch action {
  630. case release:
  631. if action == release && keyName != "" {
  632. switch keyName {
  633. case pendingMenuToggle:
  634. w.canvas.ToggleMenu()
  635. case pendingMenuDeactivation:
  636. if w.canvas.DismissMenu() {
  637. return
  638. }
  639. }
  640. }
  641. if w.canvas.Focused() != nil {
  642. if focused, ok := w.canvas.Focused().(desktop.Keyable); ok {
  643. w.QueueEvent(func() { focused.KeyUp(keyEvent) })
  644. }
  645. } else if w.canvas.onKeyUp != nil {
  646. w.QueueEvent(func() { w.canvas.onKeyUp(keyEvent) })
  647. }
  648. return // ignore key up in other core events
  649. case press:
  650. switch keyName {
  651. case desktop.KeyAltLeft, desktop.KeyAltRight:
  652. // compensate for GLFW modifiers bug https://github.com/glfw/glfw/issues/1630
  653. if (runtime.GOOS == "linux" && keyDesktopModifier == 0) || (runtime.GOOS != "linux" && keyDesktopModifier == fyne.KeyModifierAlt) {
  654. w.menuTogglePending = keyName
  655. }
  656. case fyne.KeyEscape:
  657. w.menuDeactivationPending = keyName
  658. }
  659. if w.canvas.Focused() != nil {
  660. if focused, ok := w.canvas.Focused().(desktop.Keyable); ok {
  661. w.QueueEvent(func() { focused.KeyDown(keyEvent) })
  662. }
  663. } else if w.canvas.onKeyDown != nil {
  664. w.QueueEvent(func() { w.canvas.onKeyDown(keyEvent) })
  665. }
  666. default:
  667. // key repeat will fall through to TypedKey and TypedShortcut
  668. }
  669. modifierOtherThanShift := (keyDesktopModifier & fyne.KeyModifierControl) |
  670. (keyDesktopModifier & fyne.KeyModifierAlt) |
  671. (keyDesktopModifier & fyne.KeyModifierSuper)
  672. if (keyName == fyne.KeyTab && modifierOtherThanShift == 0 && !w.capturesTab(keyDesktopModifier)) ||
  673. w.triggersShortcut(keyName, keyASCII, keyDesktopModifier) {
  674. return
  675. }
  676. // No shortcut detected, pass down to TypedKey
  677. focused := w.canvas.Focused()
  678. if focused != nil {
  679. w.QueueEvent(func() { focused.TypedKey(keyEvent) })
  680. } else if w.canvas.onTypedKey != nil {
  681. w.QueueEvent(func() { w.canvas.onTypedKey(keyEvent) })
  682. }
  683. }
  684. // charInput defines the character with modifiers callback which is called when a
  685. // Unicode character is input.
  686. //
  687. // Characters do not map 1:1 to physical keys, as a key may produce zero, one or more characters.
  688. func (w *window) processCharInput(char rune) {
  689. if focused := w.canvas.Focused(); focused != nil {
  690. w.QueueEvent(func() { focused.TypedRune(char) })
  691. } else if w.canvas.onTypedRune != nil {
  692. w.QueueEvent(func() { w.canvas.onTypedRune(char) })
  693. }
  694. }
  695. func (w *window) processFocused(focus bool) {
  696. if focus {
  697. if curWindow == nil {
  698. fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerEnteredForeground()
  699. }
  700. curWindow = w
  701. w.canvas.FocusGained()
  702. } else {
  703. w.canvas.FocusLost()
  704. w.mouseLock.Lock()
  705. w.mousePos = fyne.Position{}
  706. w.mouseLock.Unlock()
  707. go func() { // check whether another window was focused or not
  708. time.Sleep(time.Millisecond * 100)
  709. if curWindow != w {
  710. return
  711. }
  712. curWindow = nil
  713. fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerExitedForeground()
  714. }()
  715. }
  716. }
  717. func (w *window) triggersShortcut(localizedKeyName fyne.KeyName, key fyne.KeyName, modifier fyne.KeyModifier) bool {
  718. var shortcut fyne.Shortcut
  719. ctrlMod := fyne.KeyModifierControl
  720. if runtime.GOOS == "darwin" {
  721. ctrlMod = fyne.KeyModifierSuper
  722. }
  723. // User pressing physical keys Ctrl+V while using a Russian (or any non-ASCII) keyboard layout
  724. // is reported as a fyne.KeyUnknown key with Control modifier. We should still consider this
  725. // as a "Paste" shortcut.
  726. // See https://github.com/fyne-io/fyne/pull/2587 for discussion.
  727. keyName := localizedKeyName
  728. resemblesShortcut := (modifier&(fyne.KeyModifierControl|fyne.KeyModifierSuper) != 0)
  729. if (localizedKeyName == fyne.KeyUnknown) && resemblesShortcut {
  730. if key != fyne.KeyUnknown {
  731. keyName = key
  732. }
  733. }
  734. if modifier == ctrlMod {
  735. switch keyName {
  736. case fyne.KeyV:
  737. // detect paste shortcut
  738. shortcut = &fyne.ShortcutPaste{
  739. Clipboard: w.Clipboard(),
  740. }
  741. case fyne.KeyC, fyne.KeyInsert:
  742. // detect copy shortcut
  743. shortcut = &fyne.ShortcutCopy{
  744. Clipboard: w.Clipboard(),
  745. }
  746. case fyne.KeyX:
  747. // detect cut shortcut
  748. shortcut = &fyne.ShortcutCut{
  749. Clipboard: w.Clipboard(),
  750. }
  751. case fyne.KeyA:
  752. // detect selectAll shortcut
  753. shortcut = &fyne.ShortcutSelectAll{}
  754. }
  755. }
  756. if modifier == fyne.KeyModifierShift {
  757. switch keyName {
  758. case fyne.KeyInsert:
  759. // detect paste shortcut
  760. shortcut = &fyne.ShortcutPaste{
  761. Clipboard: w.Clipboard(),
  762. }
  763. case fyne.KeyDelete:
  764. // detect cut shortcut
  765. shortcut = &fyne.ShortcutCut{
  766. Clipboard: w.Clipboard(),
  767. }
  768. }
  769. }
  770. if shortcut == nil && modifier != 0 && !isKeyModifier(keyName) && modifier != fyne.KeyModifierShift {
  771. shortcut = &desktop.CustomShortcut{
  772. KeyName: keyName,
  773. Modifier: modifier,
  774. }
  775. }
  776. if shortcut != nil {
  777. if focused, ok := w.canvas.Focused().(fyne.Shortcutable); ok {
  778. shouldRunShortcut := true
  779. type selectableText interface {
  780. fyne.Disableable
  781. SelectedText() string
  782. }
  783. if selectableTextWid, ok := focused.(selectableText); ok && selectableTextWid.Disabled() {
  784. shouldRunShortcut = shortcut.ShortcutName() == "Copy"
  785. }
  786. if shouldRunShortcut {
  787. w.QueueEvent(func() { focused.TypedShortcut(shortcut) })
  788. }
  789. return shouldRunShortcut
  790. }
  791. w.QueueEvent(func() { w.canvas.TypedShortcut(shortcut) })
  792. return true
  793. }
  794. return false
  795. }
  796. func (w *window) RunWithContext(f func()) {
  797. if w.isClosing() {
  798. return
  799. }
  800. w.view().MakeContextCurrent()
  801. f()
  802. w.DetachCurrentContext()
  803. }
  804. func (w *window) RescaleContext() {
  805. runOnMain(w.rescaleOnMain)
  806. }
  807. func (w *window) Context() interface{} {
  808. return nil
  809. }
  810. func (w *window) runOnMainWhenCreated(fn func()) {
  811. if w.view() != nil {
  812. runOnMain(fn)
  813. return
  814. }
  815. w.pending = append(w.pending, fn)
  816. }
  817. func (d *gLDriver) CreateWindow(title string) fyne.Window {
  818. return d.createWindow(title, true)
  819. }
  820. func (d *gLDriver) createWindow(title string, decorate bool) fyne.Window {
  821. var ret *window
  822. if title == "" {
  823. title = defaultTitle
  824. }
  825. runOnMain(func() {
  826. d.initGLFW()
  827. ret = &window{title: title, decorate: decorate, driver: d}
  828. // This queue is destroyed when the window is closed.
  829. ret.InitEventQueue()
  830. go ret.RunEventQueue()
  831. ret.canvas = newCanvas()
  832. ret.canvas.context = ret
  833. ret.SetIcon(ret.icon)
  834. d.addWindow(ret)
  835. })
  836. return ret
  837. }
  838. func (w *window) doShowAgain() {
  839. if w.isClosing() {
  840. return
  841. }
  842. runOnMain(func() {
  843. // show top canvas element
  844. if w.canvas.Content() != nil {
  845. w.canvas.Content().Show()
  846. }
  847. w.view().SetPos(w.xpos, w.ypos)
  848. w.view().Show()
  849. w.viewLock.Lock()
  850. w.visible = true
  851. w.viewLock.Unlock()
  852. })
  853. }
  854. func (w *window) isClosing() bool {
  855. w.viewLock.RLock()
  856. closing := w.closing || w.viewport == nil
  857. w.viewLock.RUnlock()
  858. return closing
  859. }
  860. func (d *gLDriver) CreateSplashWindow() fyne.Window {
  861. win := d.createWindow("", false)
  862. win.SetPadded(false)
  863. win.CenterOnScreen()
  864. return win
  865. }
  866. func (d *gLDriver) AllWindows() []fyne.Window {
  867. return d.windows
  868. }
  869. func isKeyModifier(keyName fyne.KeyName) bool {
  870. return keyName == desktop.KeyShiftLeft || keyName == desktop.KeyShiftRight ||
  871. keyName == desktop.KeyControlLeft || keyName == desktop.KeyControlRight ||
  872. keyName == desktop.KeyAltLeft || keyName == desktop.KeyAltRight ||
  873. keyName == desktop.KeySuperLeft || keyName == desktop.KeySuperRight
  874. }