shadow.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package widget
  2. import (
  3. "image/color"
  4. "fyne.io/fyne/v2"
  5. "fyne.io/fyne/v2/canvas"
  6. "fyne.io/fyne/v2/theme"
  7. )
  8. var _ fyne.Widget = (*Shadow)(nil)
  9. // Shadow is a widget that renders a shadow.
  10. type Shadow struct {
  11. Base
  12. level ElevationLevel
  13. typ ShadowType
  14. }
  15. // ElevationLevel is the level of elevation of the shadow casting object.
  16. type ElevationLevel int
  17. // ElevationLevel constants inspired by:
  18. // https://storage.googleapis.com/spec-host/mio-staging%2Fmio-design%2F1584058305895%2Fassets%2F0B6xUSjjSulxceF9udnA4Sk5tdU0%2Fbaselineelevation-chart.png
  19. const (
  20. BaseLevel ElevationLevel = 0
  21. CardLevel ElevationLevel = 1
  22. ButtonLevel ElevationLevel = 2
  23. MenuLevel ElevationLevel = 4
  24. PopUpLevel ElevationLevel = 8
  25. SubmergedContentLevel ElevationLevel = 8
  26. DialogLevel ElevationLevel = 24
  27. )
  28. // ShadowType specifies the type of the shadow.
  29. type ShadowType int
  30. // ShadowType constants
  31. const (
  32. ShadowAround ShadowType = iota
  33. ShadowLeft
  34. ShadowRight
  35. ShadowBottom
  36. ShadowTop
  37. )
  38. // NewShadow create a new Shadow.
  39. func NewShadow(typ ShadowType, level ElevationLevel) *Shadow {
  40. s := &Shadow{typ: typ, level: level}
  41. s.ExtendBaseWidget(s)
  42. return s
  43. }
  44. // CreateRenderer returns a new renderer for the shadow.
  45. //
  46. // Implements: fyne.Widget
  47. func (s *Shadow) CreateRenderer() fyne.WidgetRenderer {
  48. r := &shadowRenderer{s: s}
  49. r.createShadows()
  50. return r
  51. }
  52. type shadowRenderer struct {
  53. BaseRenderer
  54. b, l, r, t *canvas.LinearGradient
  55. bl, br, tl, tr *canvas.RadialGradient
  56. minSize fyne.Size
  57. s *Shadow
  58. }
  59. func (r *shadowRenderer) Layout(size fyne.Size) {
  60. depth := float32(r.s.level)
  61. if r.tl != nil {
  62. r.tl.Resize(fyne.NewSize(depth, depth))
  63. r.tl.Move(fyne.NewPos(-depth, -depth))
  64. }
  65. if r.t != nil {
  66. r.t.Resize(fyne.NewSize(size.Width, depth))
  67. r.t.Move(fyne.NewPos(0, -depth))
  68. }
  69. if r.tr != nil {
  70. r.tr.Resize(fyne.NewSize(depth, depth))
  71. r.tr.Move(fyne.NewPos(size.Width, -depth))
  72. }
  73. if r.r != nil {
  74. r.r.Resize(fyne.NewSize(depth, size.Height))
  75. r.r.Move(fyne.NewPos(size.Width, 0))
  76. }
  77. if r.br != nil {
  78. r.br.Resize(fyne.NewSize(depth, depth))
  79. r.br.Move(fyne.NewPos(size.Width, size.Height))
  80. }
  81. if r.b != nil {
  82. r.b.Resize(fyne.NewSize(size.Width, depth))
  83. r.b.Move(fyne.NewPos(0, size.Height))
  84. }
  85. if r.bl != nil {
  86. r.bl.Resize(fyne.NewSize(depth, depth))
  87. r.bl.Move(fyne.NewPos(-depth, size.Height))
  88. }
  89. if r.l != nil {
  90. r.l.Resize(fyne.NewSize(depth, size.Height))
  91. r.l.Move(fyne.NewPos(-depth, 0))
  92. }
  93. }
  94. func (r *shadowRenderer) MinSize() fyne.Size {
  95. return r.minSize
  96. }
  97. func (r *shadowRenderer) Refresh() {
  98. r.refreshShadows()
  99. r.Layout(r.s.Size())
  100. canvas.Refresh(r.s)
  101. }
  102. func (r *shadowRenderer) createShadows() {
  103. switch r.s.typ {
  104. case ShadowLeft:
  105. r.l = canvas.NewHorizontalGradient(color.Transparent, theme.ShadowColor())
  106. r.SetObjects([]fyne.CanvasObject{r.l})
  107. case ShadowRight:
  108. r.r = canvas.NewHorizontalGradient(theme.ShadowColor(), color.Transparent)
  109. r.SetObjects([]fyne.CanvasObject{r.r})
  110. case ShadowBottom:
  111. r.b = canvas.NewVerticalGradient(theme.ShadowColor(), color.Transparent)
  112. r.SetObjects([]fyne.CanvasObject{r.b})
  113. case ShadowTop:
  114. r.t = canvas.NewVerticalGradient(color.Transparent, theme.ShadowColor())
  115. r.SetObjects([]fyne.CanvasObject{r.t})
  116. case ShadowAround:
  117. r.tl = canvas.NewRadialGradient(theme.ShadowColor(), color.Transparent)
  118. r.tl.CenterOffsetX = 0.5
  119. r.tl.CenterOffsetY = 0.5
  120. r.t = canvas.NewVerticalGradient(color.Transparent, theme.ShadowColor())
  121. r.tr = canvas.NewRadialGradient(theme.ShadowColor(), color.Transparent)
  122. r.tr.CenterOffsetX = -0.5
  123. r.tr.CenterOffsetY = 0.5
  124. r.r = canvas.NewHorizontalGradient(theme.ShadowColor(), color.Transparent)
  125. r.br = canvas.NewRadialGradient(theme.ShadowColor(), color.Transparent)
  126. r.br.CenterOffsetX = -0.5
  127. r.br.CenterOffsetY = -0.5
  128. r.b = canvas.NewVerticalGradient(theme.ShadowColor(), color.Transparent)
  129. r.bl = canvas.NewRadialGradient(theme.ShadowColor(), color.Transparent)
  130. r.bl.CenterOffsetX = 0.5
  131. r.bl.CenterOffsetY = -0.5
  132. r.l = canvas.NewHorizontalGradient(color.Transparent, theme.ShadowColor())
  133. r.SetObjects([]fyne.CanvasObject{r.tl, r.t, r.tr, r.r, r.br, r.b, r.bl, r.l})
  134. }
  135. }
  136. func (r *shadowRenderer) refreshShadows() {
  137. updateShadowEnd(r.l)
  138. updateShadowStart(r.r)
  139. updateShadowStart(r.b)
  140. updateShadowEnd(r.t)
  141. updateShadowRadial(r.tl)
  142. updateShadowRadial(r.tr)
  143. updateShadowRadial(r.bl)
  144. updateShadowRadial(r.br)
  145. }
  146. func updateShadowEnd(g *canvas.LinearGradient) {
  147. if g == nil {
  148. return
  149. }
  150. g.EndColor = theme.ShadowColor()
  151. g.Refresh()
  152. }
  153. func updateShadowRadial(g *canvas.RadialGradient) {
  154. if g == nil {
  155. return
  156. }
  157. g.StartColor = theme.ShadowColor()
  158. g.Refresh()
  159. }
  160. func updateShadowStart(g *canvas.LinearGradient) {
  161. if g == nil {
  162. return
  163. }
  164. g.StartColor = theme.ShadowColor()
  165. g.Refresh()
  166. }