window.go 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  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. view := w.view()
  99. winWidth, _ := view.GetSize()
  100. texWidth, _ := view.GetFramebufferSize()
  101. return float32(texWidth) / float32(winWidth)
  102. }
  103. func (w *window) Show() {
  104. go w.doShow()
  105. }
  106. func (w *window) doShow() {
  107. if w.view() != nil {
  108. w.doShowAgain()
  109. return
  110. }
  111. run.L.Lock()
  112. for !run.flag {
  113. run.Wait()
  114. }
  115. run.L.Unlock()
  116. w.createLock.Do(w.create)
  117. if w.view() == nil {
  118. return
  119. }
  120. runOnMain(func() {
  121. w.viewLock.Lock()
  122. w.visible = true
  123. w.viewLock.Unlock()
  124. view := w.view()
  125. view.SetTitle(w.title)
  126. if w.centered {
  127. w.doCenterOnScreen() // lastly center if that was requested
  128. }
  129. view.Show()
  130. // save coordinates
  131. w.xpos, w.ypos = view.GetPos()
  132. if w.fullScreen { // this does not work if called before viewport.Show()
  133. go func() {
  134. time.Sleep(time.Millisecond * 100)
  135. w.SetFullScreen(true)
  136. }()
  137. }
  138. })
  139. // show top canvas element
  140. if content := w.canvas.Content(); content != nil {
  141. content.Show()
  142. runOnDraw(w, func() {
  143. w.driver.repaintWindow(w)
  144. })
  145. }
  146. }
  147. func (w *window) Hide() {
  148. runOnMain(func() {
  149. w.viewLock.Lock()
  150. if w.closing || w.viewport == nil {
  151. w.viewLock.Unlock()
  152. return
  153. }
  154. w.visible = false
  155. v := w.viewport
  156. w.viewLock.Unlock()
  157. v.Hide()
  158. // hide top canvas element
  159. if content := w.canvas.Content(); content != nil {
  160. content.Hide()
  161. }
  162. })
  163. }
  164. func (w *window) Close() {
  165. if w.isClosing() {
  166. return
  167. }
  168. // trigger callbacks - early so window still exists
  169. if w.onClosed != nil {
  170. w.QueueEvent(w.onClosed)
  171. }
  172. // set w.closing flag inside draw thread to ensure we can free textures
  173. runOnDraw(w, func() {
  174. w.viewLock.Lock()
  175. w.closing = true
  176. w.viewLock.Unlock()
  177. w.viewport.SetShouldClose(true)
  178. cache.RangeTexturesFor(w.canvas, w.canvas.Painter().Free)
  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 content := w.canvas.Content(); content != nil {
  845. content.Show()
  846. }
  847. view := w.view()
  848. view.SetPos(w.xpos, w.ypos)
  849. view.Show()
  850. w.viewLock.Lock()
  851. w.visible = true
  852. w.viewLock.Unlock()
  853. })
  854. }
  855. func (w *window) isClosing() bool {
  856. w.viewLock.RLock()
  857. closing := w.closing || w.viewport == nil
  858. w.viewLock.RUnlock()
  859. return closing
  860. }
  861. func (d *gLDriver) CreateSplashWindow() fyne.Window {
  862. win := d.createWindow("", false)
  863. win.SetPadded(false)
  864. win.CenterOnScreen()
  865. return win
  866. }
  867. func (d *gLDriver) AllWindows() []fyne.Window {
  868. return d.windows
  869. }
  870. func isKeyModifier(keyName fyne.KeyName) bool {
  871. return keyName == desktop.KeyShiftLeft || keyName == desktop.KeyShiftRight ||
  872. keyName == desktop.KeyControlLeft || keyName == desktop.KeyControlRight ||
  873. keyName == desktop.KeyAltLeft || keyName == desktop.KeyAltRight ||
  874. keyName == desktop.KeySuperLeft || keyName == desktop.KeySuperRight
  875. }