190 lines
4.8 KiB
Go
190 lines
4.8 KiB
Go
|
package widget
|
||
|
|
||
|
import (
|
||
|
"image/color"
|
||
|
|
||
|
"fyne.io/fyne/v2"
|
||
|
"fyne.io/fyne/v2/canvas"
|
||
|
"fyne.io/fyne/v2/theme"
|
||
|
)
|
||
|
|
||
|
var _ fyne.Widget = (*Shadow)(nil)
|
||
|
|
||
|
// Shadow is a widget that renders a shadow.
|
||
|
type Shadow struct {
|
||
|
Base
|
||
|
level ElevationLevel
|
||
|
typ ShadowType
|
||
|
}
|
||
|
|
||
|
// ElevationLevel is the level of elevation of the shadow casting object.
|
||
|
type ElevationLevel int
|
||
|
|
||
|
// ElevationLevel constants inspired by:
|
||
|
// https://storage.googleapis.com/spec-host/mio-staging%2Fmio-design%2F1584058305895%2Fassets%2F0B6xUSjjSulxceF9udnA4Sk5tdU0%2Fbaselineelevation-chart.png
|
||
|
const (
|
||
|
BaseLevel ElevationLevel = 0
|
||
|
CardLevel ElevationLevel = 1
|
||
|
ButtonLevel ElevationLevel = 2
|
||
|
MenuLevel ElevationLevel = 4
|
||
|
PopUpLevel ElevationLevel = 8
|
||
|
SubmergedContentLevel ElevationLevel = 8
|
||
|
DialogLevel ElevationLevel = 24
|
||
|
)
|
||
|
|
||
|
// ShadowType specifies the type of the shadow.
|
||
|
type ShadowType int
|
||
|
|
||
|
// ShadowType constants
|
||
|
const (
|
||
|
ShadowAround ShadowType = iota
|
||
|
ShadowLeft
|
||
|
ShadowRight
|
||
|
ShadowBottom
|
||
|
ShadowTop
|
||
|
)
|
||
|
|
||
|
// NewShadow create a new Shadow.
|
||
|
func NewShadow(typ ShadowType, level ElevationLevel) *Shadow {
|
||
|
s := &Shadow{typ: typ, level: level}
|
||
|
s.ExtendBaseWidget(s)
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// CreateRenderer returns a new renderer for the shadow.
|
||
|
//
|
||
|
// Implements: fyne.Widget
|
||
|
func (s *Shadow) CreateRenderer() fyne.WidgetRenderer {
|
||
|
r := &shadowRenderer{s: s}
|
||
|
r.createShadows()
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
type shadowRenderer struct {
|
||
|
BaseRenderer
|
||
|
b, l, r, t *canvas.LinearGradient
|
||
|
bl, br, tl, tr *canvas.RadialGradient
|
||
|
minSize fyne.Size
|
||
|
s *Shadow
|
||
|
}
|
||
|
|
||
|
func (r *shadowRenderer) Layout(size fyne.Size) {
|
||
|
depth := float32(r.s.level)
|
||
|
if r.tl != nil {
|
||
|
r.tl.Resize(fyne.NewSize(depth, depth))
|
||
|
r.tl.Move(fyne.NewPos(-depth, -depth))
|
||
|
}
|
||
|
if r.t != nil {
|
||
|
r.t.Resize(fyne.NewSize(size.Width, depth))
|
||
|
r.t.Move(fyne.NewPos(0, -depth))
|
||
|
}
|
||
|
if r.tr != nil {
|
||
|
r.tr.Resize(fyne.NewSize(depth, depth))
|
||
|
r.tr.Move(fyne.NewPos(size.Width, -depth))
|
||
|
}
|
||
|
if r.r != nil {
|
||
|
r.r.Resize(fyne.NewSize(depth, size.Height))
|
||
|
r.r.Move(fyne.NewPos(size.Width, 0))
|
||
|
}
|
||
|
if r.br != nil {
|
||
|
r.br.Resize(fyne.NewSize(depth, depth))
|
||
|
r.br.Move(fyne.NewPos(size.Width, size.Height))
|
||
|
}
|
||
|
if r.b != nil {
|
||
|
r.b.Resize(fyne.NewSize(size.Width, depth))
|
||
|
r.b.Move(fyne.NewPos(0, size.Height))
|
||
|
}
|
||
|
if r.bl != nil {
|
||
|
r.bl.Resize(fyne.NewSize(depth, depth))
|
||
|
r.bl.Move(fyne.NewPos(-depth, size.Height))
|
||
|
}
|
||
|
if r.l != nil {
|
||
|
r.l.Resize(fyne.NewSize(depth, size.Height))
|
||
|
r.l.Move(fyne.NewPos(-depth, 0))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *shadowRenderer) MinSize() fyne.Size {
|
||
|
return r.minSize
|
||
|
}
|
||
|
|
||
|
func (r *shadowRenderer) Refresh() {
|
||
|
r.refreshShadows()
|
||
|
r.Layout(r.s.Size())
|
||
|
canvas.Refresh(r.s)
|
||
|
}
|
||
|
|
||
|
func (r *shadowRenderer) createShadows() {
|
||
|
switch r.s.typ {
|
||
|
case ShadowLeft:
|
||
|
r.l = canvas.NewHorizontalGradient(color.Transparent, theme.ShadowColor())
|
||
|
r.SetObjects([]fyne.CanvasObject{r.l})
|
||
|
case ShadowRight:
|
||
|
r.r = canvas.NewHorizontalGradient(theme.ShadowColor(), color.Transparent)
|
||
|
r.SetObjects([]fyne.CanvasObject{r.r})
|
||
|
case ShadowBottom:
|
||
|
r.b = canvas.NewVerticalGradient(theme.ShadowColor(), color.Transparent)
|
||
|
r.SetObjects([]fyne.CanvasObject{r.b})
|
||
|
case ShadowTop:
|
||
|
r.t = canvas.NewVerticalGradient(color.Transparent, theme.ShadowColor())
|
||
|
r.SetObjects([]fyne.CanvasObject{r.t})
|
||
|
case ShadowAround:
|
||
|
r.tl = canvas.NewRadialGradient(theme.ShadowColor(), color.Transparent)
|
||
|
r.tl.CenterOffsetX = 0.5
|
||
|
r.tl.CenterOffsetY = 0.5
|
||
|
r.t = canvas.NewVerticalGradient(color.Transparent, theme.ShadowColor())
|
||
|
r.tr = canvas.NewRadialGradient(theme.ShadowColor(), color.Transparent)
|
||
|
r.tr.CenterOffsetX = -0.5
|
||
|
r.tr.CenterOffsetY = 0.5
|
||
|
r.r = canvas.NewHorizontalGradient(theme.ShadowColor(), color.Transparent)
|
||
|
r.br = canvas.NewRadialGradient(theme.ShadowColor(), color.Transparent)
|
||
|
r.br.CenterOffsetX = -0.5
|
||
|
r.br.CenterOffsetY = -0.5
|
||
|
r.b = canvas.NewVerticalGradient(theme.ShadowColor(), color.Transparent)
|
||
|
r.bl = canvas.NewRadialGradient(theme.ShadowColor(), color.Transparent)
|
||
|
r.bl.CenterOffsetX = 0.5
|
||
|
r.bl.CenterOffsetY = -0.5
|
||
|
r.l = canvas.NewHorizontalGradient(color.Transparent, theme.ShadowColor())
|
||
|
r.SetObjects([]fyne.CanvasObject{r.tl, r.t, r.tr, r.r, r.br, r.b, r.bl, r.l})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *shadowRenderer) refreshShadows() {
|
||
|
updateShadowEnd(r.l)
|
||
|
updateShadowStart(r.r)
|
||
|
updateShadowStart(r.b)
|
||
|
updateShadowEnd(r.t)
|
||
|
|
||
|
updateShadowRadial(r.tl)
|
||
|
updateShadowRadial(r.tr)
|
||
|
updateShadowRadial(r.bl)
|
||
|
updateShadowRadial(r.br)
|
||
|
}
|
||
|
|
||
|
func updateShadowEnd(g *canvas.LinearGradient) {
|
||
|
if g == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
g.EndColor = theme.ShadowColor()
|
||
|
g.Refresh()
|
||
|
}
|
||
|
|
||
|
func updateShadowRadial(g *canvas.RadialGradient) {
|
||
|
if g == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
g.StartColor = theme.ShadowColor()
|
||
|
g.Refresh()
|
||
|
}
|
||
|
|
||
|
func updateShadowStart(g *canvas.LinearGradient) {
|
||
|
if g == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
g.StartColor = theme.ShadowColor()
|
||
|
g.Refresh()
|
||
|
}
|