Path: blob/main/internal/sidebar/channels/banner.go
366 views
package channels12import (3"context"4"math"56"github.com/diamondburned/arikawa/v3/discord"7"github.com/diamondburned/arikawa/v3/gateway"8"github.com/diamondburned/gotk4/pkg/gtk/v4"9"github.com/diamondburned/gotkit/components/onlineimage"10"github.com/diamondburned/gotkit/gtkutil/cssutil"11"github.com/diamondburned/gotkit/gtkutil/imgutil"12"libdb.so/dissent/internal/gtkcord"13)1415const (16bannerWidth = 24017bannerHeight = 12018)1920// Banner is the guild banner display on top of the channel view.21type Banner struct {22*gtk.Overlay23Shadows *gtk.Box24Picture *onlineimage.Picture25ctx context.Context26gID discord.GuildID27}2829var bannerCSS = cssutil.Applier("channels-banner", `30.channels-banner-shadow {31transition: all 0.25s;32}33.channels-banner-shadow {34/* ease-in-out-opacity -max 0 -min 0.25 -start 24px -end 75px -steps 10 */35background-image: linear-gradient(36to top,37alpha(black, 0.25) 24px,38alpha(black, 0.25) 30px,39alpha(black, 0.24) 35px,40alpha(black, 0.21) 41px,41alpha(black, 0.16) 47px,42alpha(black, 0.09) 52px,43alpha(black, 0.04) 58px,44alpha(black, 0.01) 64px,45alpha(black, 0.00) 69px,46alpha(black, 0.00) 75px47);48}49.channels-scrolled .channels-banner-shadow {50/* ease-in-out-opacity -max 0.45 -min 0.65 -steps 10 */51background-image: linear-gradient(52to top,53alpha(black, 0.65) 0%,54alpha(black, 0.65) 11%,55alpha(black, 0.64) 22%,56alpha(black, 0.62) 33%,57alpha(black, 0.58) 44%,58alpha(black, 0.52) 56%,59alpha(black, 0.48) 67%,60alpha(black, 0.46) 78%,61alpha(black, 0.45) 89%,62alpha(black, 0.45) 100%63);64}65`)6667// NewBanner creates a new Banner.68func NewBanner(ctx context.Context, guildID discord.GuildID) *Banner {69b := Banner{70ctx: ctx,71gID: guildID,72}7374b.Picture = onlineimage.NewPicture(ctx, imgutil.HTTPProvider)75b.Picture.SetLayoutManager(gtk.NewBinLayout()) // magically force min size76b.Picture.SetContentFit(gtk.ContentFitCover)77b.Picture.SetSizeRequest(bannerWidth, bannerHeight)7879b.Shadows = gtk.NewBox(gtk.OrientationVertical, 0)80b.Shadows.AddCSSClass("channels-banner-shadow")8182b.Overlay = gtk.NewOverlay()83b.Overlay.SetHAlign(gtk.AlignStart)84b.Overlay.SetChild(b.Picture)85b.Overlay.AddOverlay(b.Shadows)86b.Overlay.SetCanTarget(false)87b.Overlay.SetCanFocus(false)88b.SetVisible(false)89bannerCSS(b)9091state := gtkcord.FromContext(ctx)92state.AddHandlerForWidget(b, func(ev *gateway.GuildUpdateEvent) {93if ev.Guild.ID == guildID {94b.Invalidate()95}96})9798return &b99}100101// Invalidate invalidates and updates the Banner image.102func (b *Banner) Invalidate() {103state := gtkcord.FromContext(b.ctx)104105g, err := state.Cabinet.Guild(b.gID)106if err != nil {107b.SetVisible(false)108return109}110111url := g.BannerURL()112if url == "" {113b.SetVisible(false)114return115}116117b.SetVisible(true)118b.SetURL(gtkcord.InjectSize(url, bannerWidth))119}120121// HasBanner returns true if the banner is visible.122func (b *Banner) HasBanner() bool {123return b.Visible()124}125126func (b *Banner) SetURL(url string) { b.Picture.SetURL(url) }127128// SetScrollOpacity sets the opacity of the shadow depending on the scroll129// level. If the scroll goes past the banner, then scrolled is true.130func (b *Banner) SetScrollOpacity(scrollY float64) (scrolled bool) {131// Calculate the height of the banner but account for the height of the132// header bar.133// height := float64(b.Height()) - (gtkcord.HeaderHeight)134// opacity := clamp(scrollY/height, 0, 1)135136// b.Shadows.SetOpacity(opacity)137// return opacity >= 0.995138139return scrollY > 0 // delegate to styling140}141142func clamp(f, min, max float64) float64 {143return math.Max(math.Min(f, max), min)144}145146func strsEq(strs1, strs2 []string) bool {147if len(strs1) != len(strs2) {148return false149}150for i := range strs1 {151if strs1[i] != strs2[i] {152return false153}154}155return true156}157158159