canvas.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. package common
  2. import (
  3. "sync"
  4. "sync/atomic"
  5. "fyne.io/fyne/v2"
  6. "fyne.io/fyne/v2/internal"
  7. "fyne.io/fyne/v2/internal/app"
  8. "fyne.io/fyne/v2/internal/async"
  9. "fyne.io/fyne/v2/internal/cache"
  10. "fyne.io/fyne/v2/internal/driver"
  11. "fyne.io/fyne/v2/internal/painter/gl"
  12. )
  13. // SizeableCanvas defines a canvas with size related functions.
  14. type SizeableCanvas interface {
  15. fyne.Canvas
  16. Resize(fyne.Size)
  17. MinSize() fyne.Size
  18. }
  19. // Canvas defines common canvas implementation.
  20. type Canvas struct {
  21. sync.RWMutex
  22. OnFocus func(obj fyne.Focusable)
  23. OnUnfocus func()
  24. impl SizeableCanvas
  25. contentFocusMgr *app.FocusManager
  26. menuFocusMgr *app.FocusManager
  27. overlays *overlayStack
  28. shortcut fyne.ShortcutHandler
  29. painter gl.Painter
  30. // Any object that requestes to enter to the refresh queue should
  31. // not be omitted as it is always a rendering task's decision
  32. // for skipping frames or drawing calls.
  33. //
  34. // If an object failed to ender the refresh queue, the object may
  35. // disappear or blink from the view at any frames. As of this reason,
  36. // the refreshQueue is an unbounded queue which is bale to cache
  37. // arbitrary number of fyne.CanvasObject for the rendering.
  38. refreshQueue *async.CanvasObjectQueue
  39. dirty uint32 // atomic
  40. mWindowHeadTree, contentTree, menuTree *renderCacheTree
  41. }
  42. // AddShortcut adds a shortcut to the canvas.
  43. func (c *Canvas) AddShortcut(shortcut fyne.Shortcut, handler func(shortcut fyne.Shortcut)) {
  44. c.shortcut.AddShortcut(shortcut, handler)
  45. }
  46. // EnsureMinSize ensure canvas min size.
  47. //
  48. // This function uses lock.
  49. func (c *Canvas) EnsureMinSize() bool {
  50. if c.impl.Content() == nil {
  51. return false
  52. }
  53. var lastParent fyne.CanvasObject
  54. windowNeedsMinSizeUpdate := false
  55. csize := c.impl.Size()
  56. min := c.impl.MinSize()
  57. ensureMinSize := func(node *RenderCacheNode) {
  58. obj := node.obj
  59. cache.SetCanvasForObject(obj, c.impl)
  60. if !obj.Visible() {
  61. return
  62. }
  63. minSize := obj.MinSize()
  64. minSizeChanged := node.minSize != minSize
  65. if minSizeChanged {
  66. objToLayout := obj
  67. node.minSize = minSize
  68. if node.parent != nil {
  69. objToLayout = node.parent.obj
  70. } else {
  71. windowNeedsMinSizeUpdate = true
  72. size := obj.Size()
  73. expectedSize := minSize.Max(size)
  74. if expectedSize != size && size != csize {
  75. objToLayout = nil
  76. obj.Resize(expectedSize)
  77. }
  78. }
  79. if objToLayout != lastParent {
  80. updateLayout(lastParent)
  81. lastParent = objToLayout
  82. }
  83. }
  84. }
  85. c.WalkTrees(nil, ensureMinSize)
  86. shouldResize := windowNeedsMinSizeUpdate && (csize.Width < min.Width || csize.Height < min.Height)
  87. if shouldResize {
  88. c.impl.Resize(csize.Max(min))
  89. }
  90. if lastParent != nil {
  91. c.RLock()
  92. updateLayout(lastParent)
  93. c.RUnlock()
  94. }
  95. return windowNeedsMinSizeUpdate
  96. }
  97. // Focus makes the provided item focused.
  98. func (c *Canvas) Focus(obj fyne.Focusable) {
  99. focusMgr := c.focusManager()
  100. if focusMgr != nil && focusMgr.Focus(obj) { // fast path – probably >99.9% of all cases
  101. if c.OnFocus != nil {
  102. c.OnFocus(obj)
  103. }
  104. return
  105. }
  106. c.RLock()
  107. focusMgrs := append([]*app.FocusManager{c.contentFocusMgr, c.menuFocusMgr}, c.overlays.ListFocusManagers()...)
  108. c.RUnlock()
  109. for _, mgr := range focusMgrs {
  110. if mgr == nil {
  111. continue
  112. }
  113. if focusMgr != mgr {
  114. if mgr.Focus(obj) {
  115. if c.OnFocus != nil {
  116. c.OnFocus(obj)
  117. }
  118. return
  119. }
  120. }
  121. }
  122. fyne.LogError("Failed to focus object which is not part of the canvas’ content, menu or overlays.", nil)
  123. }
  124. // Focused returns the current focused object.
  125. func (c *Canvas) Focused() fyne.Focusable {
  126. mgr := c.focusManager()
  127. if mgr == nil {
  128. return nil
  129. }
  130. return mgr.Focused()
  131. }
  132. // FocusGained signals to the manager that its content got focus.
  133. // Valid only on Desktop.
  134. func (c *Canvas) FocusGained() {
  135. mgr := c.focusManager()
  136. if mgr == nil {
  137. return
  138. }
  139. mgr.FocusGained()
  140. }
  141. // FocusLost signals to the manager that its content lost focus.
  142. // Valid only on Desktop.
  143. func (c *Canvas) FocusLost() {
  144. mgr := c.focusManager()
  145. if mgr == nil {
  146. return
  147. }
  148. mgr.FocusLost()
  149. }
  150. // FocusNext focuses the next focusable item.
  151. func (c *Canvas) FocusNext() {
  152. mgr := c.focusManager()
  153. if mgr == nil {
  154. return
  155. }
  156. mgr.FocusNext()
  157. }
  158. // FocusPrevious focuses the previous focusable item.
  159. func (c *Canvas) FocusPrevious() {
  160. mgr := c.focusManager()
  161. if mgr == nil {
  162. return
  163. }
  164. mgr.FocusPrevious()
  165. }
  166. // FreeDirtyTextures frees dirty textures and returns the number of freed textures.
  167. func (c *Canvas) FreeDirtyTextures() (freed uint64) {
  168. freeObject := func(object fyne.CanvasObject) {
  169. freeWalked := func(obj fyne.CanvasObject, _ fyne.Position, _ fyne.Position, _ fyne.Size) bool {
  170. if c.painter != nil {
  171. c.painter.Free(obj)
  172. }
  173. return false
  174. }
  175. driver.WalkCompleteObjectTree(object, freeWalked, nil)
  176. }
  177. // Within a frame, refresh tasks are requested from the Refresh method,
  178. // and we desire to clear out all requested operations within a frame.
  179. // See https://github.com/fyne-io/fyne/issues/2548.
  180. tasksToDo := c.refreshQueue.Len()
  181. shouldFilterDuplicates := (tasksToDo > 200) // filtering has overhead, not worth enabling for few tasks
  182. var refreshSet map[fyne.CanvasObject]struct{}
  183. if shouldFilterDuplicates {
  184. refreshSet = make(map[fyne.CanvasObject]struct{})
  185. }
  186. for c.refreshQueue.Len() > 0 {
  187. object := c.refreshQueue.Out()
  188. if !shouldFilterDuplicates {
  189. freed++
  190. freeObject(object)
  191. } else {
  192. refreshSet[object] = struct{}{}
  193. tasksToDo--
  194. if tasksToDo == 0 {
  195. shouldFilterDuplicates = false // stop collecting messages to avoid starvation
  196. for object := range refreshSet {
  197. freed++
  198. freeObject(object)
  199. }
  200. }
  201. }
  202. }
  203. cache.RangeExpiredTexturesFor(c.impl, func(obj fyne.CanvasObject) {
  204. if c.painter != nil {
  205. c.painter.Free(obj)
  206. }
  207. })
  208. return
  209. }
  210. // Initialize initializes the canvas.
  211. func (c *Canvas) Initialize(impl SizeableCanvas, onOverlayChanged func()) {
  212. c.impl = impl
  213. c.refreshQueue = async.NewCanvasObjectQueue()
  214. c.overlays = &overlayStack{
  215. OverlayStack: internal.OverlayStack{
  216. OnChange: onOverlayChanged,
  217. Canvas: impl,
  218. },
  219. }
  220. }
  221. // ObjectTrees return canvas object trees.
  222. //
  223. // This function uses lock.
  224. func (c *Canvas) ObjectTrees() []fyne.CanvasObject {
  225. c.RLock()
  226. var content, menu fyne.CanvasObject
  227. if c.contentTree != nil && c.contentTree.root != nil {
  228. content = c.contentTree.root.obj
  229. }
  230. if c.menuTree != nil && c.menuTree.root != nil {
  231. menu = c.menuTree.root.obj
  232. }
  233. c.RUnlock()
  234. trees := make([]fyne.CanvasObject, 0, len(c.Overlays().List())+2)
  235. trees = append(trees, content)
  236. if menu != nil {
  237. trees = append(trees, menu)
  238. }
  239. trees = append(trees, c.Overlays().List()...)
  240. return trees
  241. }
  242. // Overlays returns the overlay stack.
  243. func (c *Canvas) Overlays() fyne.OverlayStack {
  244. // we don't need to lock here, because overlays never changes
  245. return c.overlays
  246. }
  247. // Painter returns the canvas painter.
  248. func (c *Canvas) Painter() gl.Painter {
  249. return c.painter
  250. }
  251. // Refresh refreshes a canvas object.
  252. func (c *Canvas) Refresh(obj fyne.CanvasObject) {
  253. c.refreshQueue.In(obj)
  254. c.SetDirty()
  255. }
  256. // RemoveShortcut removes a shortcut from the canvas.
  257. func (c *Canvas) RemoveShortcut(shortcut fyne.Shortcut) {
  258. c.shortcut.RemoveShortcut(shortcut)
  259. }
  260. // SetContentTreeAndFocusMgr sets content tree and focus manager.
  261. //
  262. // This function does not use the canvas lock.
  263. func (c *Canvas) SetContentTreeAndFocusMgr(content fyne.CanvasObject) {
  264. c.contentTree = &renderCacheTree{root: &RenderCacheNode{obj: content}}
  265. var focused fyne.Focusable
  266. if c.contentFocusMgr != nil {
  267. focused = c.contentFocusMgr.Focused() // keep old focus if possible
  268. }
  269. c.contentFocusMgr = app.NewFocusManager(content)
  270. if focused != nil {
  271. c.contentFocusMgr.Focus(focused)
  272. }
  273. }
  274. // CheckDirtyAndClear returns true if the canvas is dirty and
  275. // clears the dirty state atomically.
  276. func (c *Canvas) CheckDirtyAndClear() bool {
  277. return atomic.SwapUint32(&c.dirty, 0) != 0
  278. }
  279. // SetDirty sets canvas dirty flag atomically.
  280. func (c *Canvas) SetDirty() {
  281. atomic.AddUint32(&c.dirty, 1)
  282. }
  283. // SetMenuTreeAndFocusMgr sets menu tree and focus manager.
  284. //
  285. // This function does not use the canvas lock.
  286. func (c *Canvas) SetMenuTreeAndFocusMgr(menu fyne.CanvasObject) {
  287. c.menuTree = &renderCacheTree{root: &RenderCacheNode{obj: menu}}
  288. if menu != nil {
  289. c.menuFocusMgr = app.NewFocusManager(menu)
  290. } else {
  291. c.menuFocusMgr = nil
  292. }
  293. }
  294. // SetMobileWindowHeadTree sets window head tree.
  295. //
  296. // This function does not use the canvas lock.
  297. func (c *Canvas) SetMobileWindowHeadTree(head fyne.CanvasObject) {
  298. c.mWindowHeadTree = &renderCacheTree{root: &RenderCacheNode{obj: head}}
  299. }
  300. // SetPainter sets the canvas painter.
  301. func (c *Canvas) SetPainter(p gl.Painter) {
  302. c.painter = p
  303. }
  304. // TypedShortcut handle the registered shortcut.
  305. func (c *Canvas) TypedShortcut(shortcut fyne.Shortcut) {
  306. c.shortcut.TypedShortcut(shortcut)
  307. }
  308. // Unfocus unfocuses all the objects in the canvas.
  309. func (c *Canvas) Unfocus() {
  310. mgr := c.focusManager()
  311. if mgr == nil {
  312. return
  313. }
  314. if mgr.Focus(nil) && c.OnUnfocus != nil {
  315. c.OnUnfocus()
  316. }
  317. }
  318. // WalkTrees walks over the trees.
  319. func (c *Canvas) WalkTrees(
  320. beforeChildren func(*RenderCacheNode, fyne.Position),
  321. afterChildren func(*RenderCacheNode),
  322. ) {
  323. c.walkTree(c.contentTree, beforeChildren, afterChildren)
  324. if c.mWindowHeadTree != nil && c.mWindowHeadTree.root.obj != nil {
  325. c.walkTree(c.mWindowHeadTree, beforeChildren, afterChildren)
  326. }
  327. if c.menuTree != nil && c.menuTree.root.obj != nil {
  328. c.walkTree(c.menuTree, beforeChildren, afterChildren)
  329. }
  330. for _, tree := range c.overlays.renderCaches {
  331. if tree != nil {
  332. c.walkTree(tree, beforeChildren, afterChildren)
  333. }
  334. }
  335. }
  336. func (c *Canvas) focusManager() *app.FocusManager {
  337. if focusMgr := c.overlays.TopFocusManager(); focusMgr != nil {
  338. return focusMgr
  339. }
  340. c.RLock()
  341. defer c.RUnlock()
  342. if c.isMenuActive() {
  343. return c.menuFocusMgr
  344. }
  345. return c.contentFocusMgr
  346. }
  347. func (c *Canvas) isMenuActive() bool {
  348. if c.menuTree == nil || c.menuTree.root == nil || c.menuTree.root.obj == nil {
  349. return false
  350. }
  351. menu := c.menuTree.root.obj
  352. if am, ok := menu.(activatableMenu); ok {
  353. return am.IsActive()
  354. }
  355. return true
  356. }
  357. func (c *Canvas) walkTree(
  358. tree *renderCacheTree,
  359. beforeChildren func(*RenderCacheNode, fyne.Position),
  360. afterChildren func(*RenderCacheNode),
  361. ) {
  362. tree.Lock()
  363. defer tree.Unlock()
  364. var node, parent, prev *RenderCacheNode
  365. node = tree.root
  366. bc := func(obj fyne.CanvasObject, pos fyne.Position, _ fyne.Position, _ fyne.Size) bool {
  367. if node != nil && node.obj != obj {
  368. if parent.firstChild == node {
  369. parent.firstChild = nil
  370. }
  371. node = nil
  372. }
  373. if node == nil {
  374. node = &RenderCacheNode{parent: parent, obj: obj}
  375. if parent.firstChild == nil {
  376. parent.firstChild = node
  377. } else {
  378. prev.nextSibling = node
  379. }
  380. }
  381. if prev != nil && prev.parent != parent {
  382. prev = nil
  383. }
  384. if beforeChildren != nil {
  385. beforeChildren(node, pos)
  386. }
  387. parent = node
  388. node = parent.firstChild
  389. return false
  390. }
  391. ac := func(obj fyne.CanvasObject, _ fyne.CanvasObject) {
  392. node = parent
  393. parent = node.parent
  394. if prev != nil && prev.parent != parent {
  395. prev.nextSibling = nil
  396. }
  397. if afterChildren != nil {
  398. afterChildren(node)
  399. }
  400. prev = node
  401. node = node.nextSibling
  402. }
  403. driver.WalkVisibleObjectTree(tree.root.obj, bc, ac)
  404. }
  405. // RenderCacheNode represents a node in a render cache tree.
  406. type RenderCacheNode struct {
  407. // structural data
  408. firstChild *RenderCacheNode
  409. nextSibling *RenderCacheNode
  410. obj fyne.CanvasObject
  411. parent *RenderCacheNode
  412. // cache data
  413. minSize fyne.Size
  414. // painterData is some data from the painter associated with the drawed node
  415. // it may for instance point to a GL texture
  416. // it should free all associated resources when released
  417. // i.e. it should not simply be a texture reference integer
  418. painterData interface{}
  419. }
  420. // Obj returns the node object.
  421. func (r *RenderCacheNode) Obj() fyne.CanvasObject {
  422. return r.obj
  423. }
  424. type activatableMenu interface {
  425. IsActive() bool
  426. }
  427. type overlayStack struct {
  428. internal.OverlayStack
  429. propertyLock sync.RWMutex
  430. renderCaches []*renderCacheTree
  431. }
  432. func (o *overlayStack) Add(overlay fyne.CanvasObject) {
  433. if overlay == nil {
  434. return
  435. }
  436. o.propertyLock.Lock()
  437. defer o.propertyLock.Unlock()
  438. o.add(overlay)
  439. }
  440. func (o *overlayStack) Remove(overlay fyne.CanvasObject) {
  441. if overlay == nil || len(o.List()) == 0 {
  442. return
  443. }
  444. o.propertyLock.Lock()
  445. defer o.propertyLock.Unlock()
  446. o.remove(overlay)
  447. }
  448. func (o *overlayStack) add(overlay fyne.CanvasObject) {
  449. o.renderCaches = append(o.renderCaches, &renderCacheTree{root: &RenderCacheNode{obj: overlay}})
  450. o.OverlayStack.Add(overlay)
  451. }
  452. func (o *overlayStack) remove(overlay fyne.CanvasObject) {
  453. o.OverlayStack.Remove(overlay)
  454. overlayCount := len(o.List())
  455. o.renderCaches = o.renderCaches[:overlayCount]
  456. }
  457. type renderCacheTree struct {
  458. sync.RWMutex
  459. root *RenderCacheNode
  460. }
  461. func updateLayout(objToLayout fyne.CanvasObject) {
  462. switch cont := objToLayout.(type) {
  463. case *fyne.Container:
  464. if cont.Layout != nil {
  465. cont.Layout.Layout(cont.Objects, cont.Size())
  466. }
  467. case fyne.Widget:
  468. cache.Renderer(cont).Layout(cont.Size())
  469. }
  470. }