Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
diamondburned
GitHub Repository: diamondburned/gtkcord4
Path: blob/main/internal/sidebar/channels/banner.go
366 views
1
package channels
2
3
import (
4
"context"
5
"math"
6
7
"github.com/diamondburned/arikawa/v3/discord"
8
"github.com/diamondburned/arikawa/v3/gateway"
9
"github.com/diamondburned/gotk4/pkg/gtk/v4"
10
"github.com/diamondburned/gotkit/components/onlineimage"
11
"github.com/diamondburned/gotkit/gtkutil/cssutil"
12
"github.com/diamondburned/gotkit/gtkutil/imgutil"
13
"libdb.so/dissent/internal/gtkcord"
14
)
15
16
const (
17
bannerWidth = 240
18
bannerHeight = 120
19
)
20
21
// Banner is the guild banner display on top of the channel view.
22
type Banner struct {
23
*gtk.Overlay
24
Shadows *gtk.Box
25
Picture *onlineimage.Picture
26
ctx context.Context
27
gID discord.GuildID
28
}
29
30
var bannerCSS = cssutil.Applier("channels-banner", `
31
.channels-banner-shadow {
32
transition: all 0.25s;
33
}
34
.channels-banner-shadow {
35
/* ease-in-out-opacity -max 0 -min 0.25 -start 24px -end 75px -steps 10 */
36
background-image: linear-gradient(
37
to top,
38
alpha(black, 0.25) 24px,
39
alpha(black, 0.25) 30px,
40
alpha(black, 0.24) 35px,
41
alpha(black, 0.21) 41px,
42
alpha(black, 0.16) 47px,
43
alpha(black, 0.09) 52px,
44
alpha(black, 0.04) 58px,
45
alpha(black, 0.01) 64px,
46
alpha(black, 0.00) 69px,
47
alpha(black, 0.00) 75px
48
);
49
}
50
.channels-scrolled .channels-banner-shadow {
51
/* ease-in-out-opacity -max 0.45 -min 0.65 -steps 10 */
52
background-image: linear-gradient(
53
to top,
54
alpha(black, 0.65) 0%,
55
alpha(black, 0.65) 11%,
56
alpha(black, 0.64) 22%,
57
alpha(black, 0.62) 33%,
58
alpha(black, 0.58) 44%,
59
alpha(black, 0.52) 56%,
60
alpha(black, 0.48) 67%,
61
alpha(black, 0.46) 78%,
62
alpha(black, 0.45) 89%,
63
alpha(black, 0.45) 100%
64
);
65
}
66
`)
67
68
// NewBanner creates a new Banner.
69
func NewBanner(ctx context.Context, guildID discord.GuildID) *Banner {
70
b := Banner{
71
ctx: ctx,
72
gID: guildID,
73
}
74
75
b.Picture = onlineimage.NewPicture(ctx, imgutil.HTTPProvider)
76
b.Picture.SetLayoutManager(gtk.NewBinLayout()) // magically force min size
77
b.Picture.SetContentFit(gtk.ContentFitCover)
78
b.Picture.SetSizeRequest(bannerWidth, bannerHeight)
79
80
b.Shadows = gtk.NewBox(gtk.OrientationVertical, 0)
81
b.Shadows.AddCSSClass("channels-banner-shadow")
82
83
b.Overlay = gtk.NewOverlay()
84
b.Overlay.SetHAlign(gtk.AlignStart)
85
b.Overlay.SetChild(b.Picture)
86
b.Overlay.AddOverlay(b.Shadows)
87
b.Overlay.SetCanTarget(false)
88
b.Overlay.SetCanFocus(false)
89
b.SetVisible(false)
90
bannerCSS(b)
91
92
state := gtkcord.FromContext(ctx)
93
state.AddHandlerForWidget(b, func(ev *gateway.GuildUpdateEvent) {
94
if ev.Guild.ID == guildID {
95
b.Invalidate()
96
}
97
})
98
99
return &b
100
}
101
102
// Invalidate invalidates and updates the Banner image.
103
func (b *Banner) Invalidate() {
104
state := gtkcord.FromContext(b.ctx)
105
106
g, err := state.Cabinet.Guild(b.gID)
107
if err != nil {
108
b.SetVisible(false)
109
return
110
}
111
112
url := g.BannerURL()
113
if url == "" {
114
b.SetVisible(false)
115
return
116
}
117
118
b.SetVisible(true)
119
b.SetURL(gtkcord.InjectSize(url, bannerWidth))
120
}
121
122
// HasBanner returns true if the banner is visible.
123
func (b *Banner) HasBanner() bool {
124
return b.Visible()
125
}
126
127
func (b *Banner) SetURL(url string) { b.Picture.SetURL(url) }
128
129
// SetScrollOpacity sets the opacity of the shadow depending on the scroll
130
// level. If the scroll goes past the banner, then scrolled is true.
131
func (b *Banner) SetScrollOpacity(scrollY float64) (scrolled bool) {
132
// Calculate the height of the banner but account for the height of the
133
// header bar.
134
// height := float64(b.Height()) - (gtkcord.HeaderHeight)
135
// opacity := clamp(scrollY/height, 0, 1)
136
137
// b.Shadows.SetOpacity(opacity)
138
// return opacity >= 0.995
139
140
return scrollY > 0 // delegate to styling
141
}
142
143
func clamp(f, min, max float64) float64 {
144
return math.Max(math.Min(f, max), min)
145
}
146
147
func strsEq(strs1, strs2 []string) bool {
148
if len(strs1) != len(strs2) {
149
return false
150
}
151
for i := range strs1 {
152
if strs1[i] != strs2[i] {
153
return false
154
}
155
}
156
return true
157
}
158
159