toast.go_ raw
1 package toast
2
3 import (
4 "image"
5 "image/color"
6
7 "github.com/p9c/gio/f32"
8 l "github.com/p9c/gio/layout"
9 "github.com/p9c/gio/op"
10 "github.com/p9c/gio/op/clip"
11 "github.com/p9c/gio/op/paint"
12 "github.com/p9c/gio/unit"
13 icons2 "golang.org/x/exp/shiny/materialdesign/icons"
14
15 "github.com/p9c/pod/pkg/gui"
16 "github.com/p9c/pod/pkg/gui/shadow"
17 )
18
19 type Toasts struct {
20 toasts []toast
21 layout *gui.List
22 theme *gui.Theme
23 offset image.Point
24 duration int
25 singleSize image.Point
26 singleCornerRadius unit.Value
27 singleElevation unit.Value
28 }
29
30 type toast struct {
31 title, content, level string
32 headerBackground color.NRGBA
33 bodyBackground color.NRGBA
34 icon *[]byte
35 ticker float32
36 close gui.Clickable
37 cornerRadius unit.Value
38 elevation unit.Value
39 }
40
41 func New(th *gui.Theme) *Toasts {
42 return &Toasts{
43 layout: th.List(),
44 theme: th,
45 duration: 100,
46 singleSize: image.Pt(300, 80),
47 singleCornerRadius: unit.Dp(5),
48 singleElevation: unit.Dp(5),
49 }
50 }
51 func (t *Toasts) AddToast(title, content, level string) {
52 ic := &icons2.ActionInfo
53 switch level {
54 case "Warning":
55 ic = &icons2.AlertWarning
56 case "Success":
57 ic = &icons2.NavigationCheck
58 case "Danger":
59 ic = &icons2.AlertError
60 case "Info":
61 ic = &icons2.ActionInfo
62 }
63 t.toasts = append(
64 t.toasts, toast{
65 title: title,
66 content: content,
67 level: level,
68 ticker: 0,
69 headerBackground: gui.HexNRGB(t.theme.Colors[level]),
70 bodyBackground: gui.HexNRGB(t.theme.Colors["PanelBg"]),
71 cornerRadius: t.singleCornerRadius,
72 elevation: t.singleElevation,
73 icon: ic,
74 },
75 )
76 }
77
78 func (t *Toasts) DrawToasts() func(gtx l.Context) {
79 return func(gtx l.Context) {
80 defer op.Push(gtx.Ops).Pop()
81 op.Offset(f32.Pt(float32(gtx.Constraints.Max.X)-310, 0)).Add(gtx.Ops)
82 gtx.Constraints.Min = image.Pt(250, gtx.Constraints.Min.Y)
83 gtx.Constraints.Max.X = 250
84 // paint.Fill(gtx.Ops, helper.HexARGB("ff559988"))
85 t.theme.Inset(
86 0,
87 t.layout.Vertical().ScrollToEnd().Length(len(t.toasts)).ListElement(t.singleToast).Fn,
88 ).Fn(gtx)
89 }
90 }
91 func (t *Toasts) singleToast(gtx l.Context, index int) l.Dimensions {
92 if t.toasts[index].ticker < float32(t.duration) {
93 t.toasts[index].ticker += 1
94 gtx.Constraints.Min = t.singleSize
95 // gtx.Constraints.Max = t.singleSize
96 gtx.Constraints.Max.X = t.singleSize.X
97 sz := gtx.Constraints.Min
98 rr := float32(gtx.Px(t.singleCornerRadius))
99
100 r := f32.Rect(0, 0, float32(sz.X), float32(sz.Y))
101
102 return t.theme.Inset(
103 0.05, func(gtx l.Context) l.Dimensions {
104 return shadow.Shadow(
105 gtx, unit.Dp(3), unit.Dp(1), gui.HexNRGB("ee000000"), t.theme.Flex().Flexed(
106 1,
107 func(gtx l.Context) l.Dimensions {
108 clip.UniformRRect(r, rr).Add(gtx.Ops)
109 paint.Fill(gtx.Ops, t.toasts[index].bodyBackground)
110
111 return t.theme.Inset(
112 0.25,
113 t.theme.VFlex().
114 Rigid(
115 t.theme.Inset(
116 0.1,
117 t.theme.Fill(t.toasts[index].level, t.theme.Flex().
118 Rigid(
119 func(gtx l.Context) l.Dimensions {
120 return t.theme.Icon().Color("DocText").Scale(1).Src(t.toasts[index].icon).Fn(gtx)
121 },
122 ).
123 Flexed(
124 1,
125 t.theme.H6(t.toasts[index].title).Color("PanelBg").Fn,
126 ).Fn, l.Center).Fn,
127 ).Fn,
128 ).
129 Rigid(
130 t.theme.Body1(t.toasts[index].content).Color("PanelText").Fn,
131 ).Fn,
132 ).Fn(gtx)
133 },
134 ).Fn,
135 )
136 },
137 ).Fn(gtx)
138 } else {
139 t.toasts = remove(t.toasts, index)
140 return gui.EmptySpace(0, 0)(gtx)
141 }
142 }
143
144 func remove(slice []toast, s int) []toast {
145 return append(slice[:s], slice[s+1:]...)
146 }
147