canvas.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. package glfw
  2. import (
  3. "image"
  4. "math"
  5. "fyne.io/fyne/v2"
  6. "fyne.io/fyne/v2/canvas"
  7. "fyne.io/fyne/v2/internal"
  8. "fyne.io/fyne/v2/internal/app"
  9. "fyne.io/fyne/v2/internal/driver"
  10. "fyne.io/fyne/v2/internal/driver/common"
  11. "fyne.io/fyne/v2/theme"
  12. "fyne.io/fyne/v2/widget"
  13. )
  14. // Declare conformity with Canvas interface
  15. var _ fyne.Canvas = (*glCanvas)(nil)
  16. type glCanvas struct {
  17. common.Canvas
  18. content fyne.CanvasObject
  19. menu fyne.CanvasObject
  20. padded, debug bool
  21. size fyne.Size
  22. onTypedRune func(rune)
  23. onTypedKey func(*fyne.KeyEvent)
  24. onKeyDown func(*fyne.KeyEvent)
  25. onKeyUp func(*fyne.KeyEvent)
  26. // shortcut fyne.ShortcutHandler
  27. scale, detectedScale, texScale float32
  28. context driver.WithContext
  29. }
  30. func (c *glCanvas) Capture() image.Image {
  31. var img image.Image
  32. runOnDraw(c.context.(*window), func() {
  33. img = c.Painter().Capture(c)
  34. })
  35. return img
  36. }
  37. func (c *glCanvas) Content() fyne.CanvasObject {
  38. c.RLock()
  39. retval := c.content
  40. c.RUnlock()
  41. return retval
  42. }
  43. func (c *glCanvas) DismissMenu() bool {
  44. c.RLock()
  45. menu := c.menu
  46. c.RUnlock()
  47. if menu != nil && menu.(*MenuBar).IsActive() {
  48. menu.(*MenuBar).Toggle()
  49. return true
  50. }
  51. return false
  52. }
  53. func (c *glCanvas) InteractiveArea() (fyne.Position, fyne.Size) {
  54. return fyne.Position{}, c.Size()
  55. }
  56. func (c *glCanvas) MinSize() fyne.Size {
  57. c.RLock()
  58. defer c.RUnlock()
  59. return c.canvasSize(c.content.MinSize())
  60. }
  61. func (c *glCanvas) OnKeyDown() func(*fyne.KeyEvent) {
  62. return c.onKeyDown
  63. }
  64. func (c *glCanvas) OnKeyUp() func(*fyne.KeyEvent) {
  65. return c.onKeyUp
  66. }
  67. func (c *glCanvas) OnTypedKey() func(*fyne.KeyEvent) {
  68. return c.onTypedKey
  69. }
  70. func (c *glCanvas) OnTypedRune() func(rune) {
  71. return c.onTypedRune
  72. }
  73. func (c *glCanvas) Padded() bool {
  74. return c.padded
  75. }
  76. func (c *glCanvas) PixelCoordinateForPosition(pos fyne.Position) (int, int) {
  77. c.RLock()
  78. texScale := c.texScale
  79. scale := c.scale
  80. c.RUnlock()
  81. multiple := scale * texScale
  82. scaleInt := func(x float32) int {
  83. return int(math.Round(float64(x * multiple)))
  84. }
  85. return scaleInt(pos.X), scaleInt(pos.Y)
  86. }
  87. func (c *glCanvas) Resize(size fyne.Size) {
  88. // This might not be the ideal solution, but it effectively avoid the first frame to be blurry due to the
  89. // rounding of the size to the loower integer when scale == 1. It does not affect the other cases as far as we tested.
  90. // This can easily be seen with fyne/cmd/hello and a scale == 1 as the text will happear blurry without the following line.
  91. nearestSize := fyne.NewSize(float32(math.Ceil(float64(size.Width))), float32(math.Ceil(float64(size.Height))))
  92. c.Lock()
  93. c.size = nearestSize
  94. c.Unlock()
  95. for _, overlay := range c.Overlays().List() {
  96. if p, ok := overlay.(*widget.PopUp); ok {
  97. // TODO: remove this when #707 is being addressed.
  98. // “Notifies” the PopUp of the canvas size change.
  99. p.Refresh()
  100. } else {
  101. overlay.Resize(nearestSize)
  102. }
  103. }
  104. c.RLock()
  105. content := c.content
  106. contentSize := c.contentSize(nearestSize)
  107. contentPos := c.contentPos()
  108. menu := c.menu
  109. menuHeight := c.menuHeight()
  110. c.RUnlock()
  111. content.Resize(contentSize)
  112. content.Move(contentPos)
  113. if menu != nil {
  114. menu.Refresh()
  115. menu.Resize(fyne.NewSize(nearestSize.Width, menuHeight))
  116. }
  117. }
  118. func (c *glCanvas) Scale() float32 {
  119. c.RLock()
  120. defer c.RUnlock()
  121. return c.scale
  122. }
  123. func (c *glCanvas) SetContent(content fyne.CanvasObject) {
  124. content.Resize(content.MinSize()) // give it the space it wants then calculate the real min
  125. c.Lock()
  126. // the pass above makes some layouts wide enough to wrap, so we ask again what the true min is.
  127. newSize := c.size.Max(c.canvasSize(content.MinSize()))
  128. c.setContent(content)
  129. c.Unlock()
  130. c.Resize(newSize)
  131. c.SetDirty()
  132. }
  133. func (c *glCanvas) SetOnKeyDown(typed func(*fyne.KeyEvent)) {
  134. c.onKeyDown = typed
  135. }
  136. func (c *glCanvas) SetOnKeyUp(typed func(*fyne.KeyEvent)) {
  137. c.onKeyUp = typed
  138. }
  139. func (c *glCanvas) SetOnTypedKey(typed func(*fyne.KeyEvent)) {
  140. c.onTypedKey = typed
  141. }
  142. func (c *glCanvas) SetOnTypedRune(typed func(rune)) {
  143. c.onTypedRune = typed
  144. }
  145. func (c *glCanvas) SetPadded(padded bool) {
  146. c.Lock()
  147. content := c.content
  148. c.padded = padded
  149. pos := c.contentPos()
  150. c.Unlock()
  151. content.Move(pos)
  152. }
  153. func (c *glCanvas) reloadScale() {
  154. w := c.context.(*window)
  155. w.viewLock.RLock()
  156. windowVisible := w.visible
  157. w.viewLock.RUnlock()
  158. if !windowVisible {
  159. return
  160. }
  161. c.Lock()
  162. c.scale = w.calculatedScale()
  163. c.Unlock()
  164. c.SetDirty()
  165. c.context.RescaleContext()
  166. }
  167. func (c *glCanvas) Size() fyne.Size {
  168. c.RLock()
  169. defer c.RUnlock()
  170. return c.size
  171. }
  172. func (c *glCanvas) ToggleMenu() {
  173. c.RLock()
  174. menu := c.menu
  175. c.RUnlock()
  176. if menu != nil {
  177. menu.(*MenuBar).Toggle()
  178. }
  179. }
  180. func (c *glCanvas) buildMenu(w *window, m *fyne.MainMenu) {
  181. c.Lock()
  182. defer c.Unlock()
  183. c.setMenuOverlay(nil)
  184. if m == nil {
  185. return
  186. }
  187. if hasNativeMenu() {
  188. setupNativeMenu(w, m)
  189. } else {
  190. c.setMenuOverlay(buildMenuOverlay(m, w))
  191. }
  192. }
  193. // canvasSize computes the needed canvas size for the given content size
  194. func (c *glCanvas) canvasSize(contentSize fyne.Size) fyne.Size {
  195. canvasSize := contentSize.Add(fyne.NewSize(0, c.menuHeight()))
  196. if c.Padded() {
  197. return canvasSize.Add(fyne.NewSquareSize(theme.Padding() * 2))
  198. }
  199. return canvasSize
  200. }
  201. func (c *glCanvas) contentPos() fyne.Position {
  202. contentPos := fyne.NewPos(0, c.menuHeight())
  203. if c.Padded() {
  204. return contentPos.Add(fyne.NewSquareOffsetPos(theme.Padding()))
  205. }
  206. return contentPos
  207. }
  208. func (c *glCanvas) contentSize(canvasSize fyne.Size) fyne.Size {
  209. contentSize := fyne.NewSize(canvasSize.Width, canvasSize.Height-c.menuHeight())
  210. if c.Padded() {
  211. return contentSize.Subtract(fyne.NewSquareSize(theme.Padding() * 2))
  212. }
  213. return contentSize
  214. }
  215. func (c *glCanvas) menuHeight() float32 {
  216. if c.menu == nil {
  217. return 0 // no menu or native menu -> does not consume space on the canvas
  218. }
  219. return c.menu.MinSize().Height
  220. }
  221. func (c *glCanvas) overlayChanged() {
  222. c.SetDirty()
  223. }
  224. func (c *glCanvas) paint(size fyne.Size) {
  225. clips := &internal.ClipStack{}
  226. if c.Content() == nil {
  227. return
  228. }
  229. c.Painter().Clear()
  230. paint := func(node *common.RenderCacheNode, pos fyne.Position) {
  231. obj := node.Obj()
  232. if _, ok := obj.(fyne.Scrollable); ok {
  233. inner := clips.Push(pos, obj.Size())
  234. c.Painter().StartClipping(inner.Rect())
  235. }
  236. if size.Width <= 0 || size.Height <= 0 { // iconifying on Windows can do bad things
  237. return
  238. }
  239. c.Painter().Paint(obj, pos, size)
  240. }
  241. afterPaint := func(node *common.RenderCacheNode, pos fyne.Position) {
  242. if _, ok := node.Obj().(fyne.Scrollable); ok {
  243. clips.Pop()
  244. if top := clips.Top(); top != nil {
  245. c.Painter().StartClipping(top.Rect())
  246. } else {
  247. c.Painter().StopClipping()
  248. }
  249. }
  250. if c.debug {
  251. c.DrawDebugOverlay(node.Obj(), pos, size)
  252. }
  253. }
  254. c.WalkTrees(paint, afterPaint)
  255. }
  256. func (c *glCanvas) setContent(content fyne.CanvasObject) {
  257. c.content = content
  258. c.SetContentTreeAndFocusMgr(content)
  259. }
  260. func (c *glCanvas) setMenuOverlay(b fyne.CanvasObject) {
  261. c.menu = b
  262. c.SetMenuTreeAndFocusMgr(b)
  263. if c.menu != nil && !c.size.IsZero() {
  264. c.content.Resize(c.contentSize(c.size))
  265. c.content.Move(c.contentPos())
  266. c.menu.Refresh()
  267. c.menu.Resize(fyne.NewSize(c.size.Width, c.menu.MinSize().Height))
  268. }
  269. }
  270. func (c *glCanvas) applyThemeOutOfTreeObjects() {
  271. c.RLock()
  272. menu := c.menu
  273. padded := c.padded
  274. c.RUnlock()
  275. if menu != nil {
  276. app.ApplyThemeTo(menu, c) // Ensure our menu gets the theme change message as it's out-of-tree
  277. }
  278. c.SetPadded(padded) // refresh the padding for potential theme differences
  279. }
  280. func newCanvas() *glCanvas {
  281. c := &glCanvas{scale: 1.0, texScale: 1.0, padded: true}
  282. c.Initialize(c, c.overlayChanged)
  283. c.setContent(&canvas.Rectangle{FillColor: theme.BackgroundColor()})
  284. c.debug = fyne.CurrentApp().Settings().BuildType() == fyne.BuildDebug
  285. return c
  286. }