Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
diamondburned
GitHub Repository: diamondburned/gtkcord4
Path: blob/main/internal/sidebar/guilds/folder.go
366 views
1
package guilds
2
3
import (
4
"context"
5
6
"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/gtkutil/cssutil"
10
"libdb.so/dissent/internal/sidebar/sidebutton"
11
)
12
13
const (
14
FolderSize = 32
15
FolderMiniSize = 16
16
)
17
18
// Folder is the widget containing the folder icon on top and a child list of
19
// guilds beneath it.
20
type Folder struct {
21
*gtk.Box
22
23
Button struct {
24
*gtk.Overlay
25
Pill *sidebutton.Pill
26
Folder *FolderButton
27
}
28
29
Revealer *gtk.Revealer
30
GuildBox *gtk.Box
31
Guilds []*Guild
32
33
ctx context.Context
34
open bool
35
36
// TODO: if we ever track the unread indicator, then our unread container
37
// will need to count the guilds for us for SetGuildOpen to work
38
// correctly.
39
}
40
41
var folderCSS = cssutil.Applier("guild-folder", `
42
.guild-folder .guild-guild > button {
43
padding: 0px 12px;
44
}
45
.guild-folder .guild-guild > button > * {
46
padding: 4px 0;
47
transition: 200ms ease;
48
background-color: @theme_bg_color;
49
}
50
.guild-folder .guild-guild > button avatar {
51
padding: 0;
52
}
53
.guild-folder .guild-guild > button avatar,
54
.guild-folder .guild-guild > button:hover avatar {
55
background-color: @theme_bg_color;
56
}
57
.guild-folder .guild-guild:last-child > button {
58
padding-bottom: 4px;
59
}
60
.guild-folder .guild-guild:last-child > button > * {
61
padding: 0;
62
padding-top: 4px;
63
border-radius: 0 0 99px 99px;
64
}
65
`)
66
67
// NewFolder creates a new Folder.
68
func NewFolder(ctx context.Context) *Folder {
69
f := Folder{
70
ctx: ctx,
71
}
72
73
f.Button.Folder = NewFolderButton(ctx)
74
f.Button.Folder.SetRevealed(false)
75
f.Button.Folder.ConnectClicked(f.toggle)
76
77
f.Button.Pill = sidebutton.NewPill()
78
79
f.Button.Overlay = gtk.NewOverlay()
80
f.Button.Overlay.SetChild(f.Button.Folder)
81
f.Button.Overlay.AddOverlay(f.Button.Pill)
82
83
f.GuildBox = gtk.NewBox(gtk.OrientationVertical, 0)
84
85
f.Revealer = gtk.NewRevealer()
86
f.Revealer.SetTransitionType(gtk.RevealerTransitionTypeSlideDown)
87
f.Revealer.SetRevealChild(false)
88
f.Revealer.SetChild(f.GuildBox)
89
90
f.Box = gtk.NewBox(gtk.OrientationVertical, 0)
91
f.Box.Append(f.Button.Overlay)
92
f.Box.Append(f.Revealer)
93
f.AddCSSClass("guild-folder-collapsed")
94
folderCSS(f.Box)
95
96
return &f
97
}
98
99
// Unselect unselects the folder visually.
100
func (f *Folder) Unselect() {
101
f.setGuildOpen(false)
102
}
103
104
// SetSelected sets the folder's selected state.
105
func (f *Folder) SetSelected(selected bool) {
106
f.setGuildOpen(selected)
107
}
108
109
func (f *Folder) setGuildOpen(open bool) {
110
f.open = open
111
112
if f.Revealer.RevealChild() {
113
f.Button.Pill.State = sidebutton.PillOpened
114
} else {
115
if open {
116
f.Button.Pill.State = sidebutton.PillActive
117
} else {
118
f.Button.Pill.State = sidebutton.PillInactive
119
}
120
}
121
122
f.Button.Pill.Invalidate()
123
}
124
125
func (f *Folder) toggle() {
126
reveal := !f.Revealer.RevealChild()
127
f.Revealer.SetRevealChild(reveal)
128
f.Button.Folder.SetRevealed(reveal)
129
f.Button.Folder.Mentions.SetRevealChild(!reveal)
130
f.setGuildOpen(f.open)
131
132
if reveal {
133
f.RemoveCSSClass("guild-folder-collapsed")
134
} else {
135
f.AddCSSClass("guild-folder-collapsed")
136
}
137
}
138
139
// Set sets a fresh list of guilds.
140
func (f *Folder) Set(folder *gateway.GuildFolder) {
141
f.Button.Folder.SetIcons(folder.GuildIDs)
142
if folder.Color != discord.NullColor {
143
f.Button.Folder.SetColor(folder.Color)
144
}
145
146
for _, guild := range f.Guilds {
147
f.GuildBox.Remove(guild)
148
}
149
150
f.Guilds = make([]*Guild, len(folder.GuildIDs))
151
152
for i, id := range folder.GuildIDs {
153
g := NewGuild(f.ctx, id)
154
g.SetParentFolder(f)
155
156
f.Guilds[i] = g
157
f.GuildBox.Append(g)
158
}
159
160
// Invalidate after, since guilds can call back onto our Folder list.
161
for _, g := range f.Guilds {
162
g.Invalidate()
163
}
164
165
// After guilds are loaded, read their labels and set the folder name if unset.
166
folderName := folder.Name
167
if folderName == "" {
168
for i, g := range f.Guilds {
169
folderName += g.Name()
170
if (i + 1) < len(f.Guilds) {
171
folderName += ", "
172
}
173
if len(folderName) > 40 {
174
folderName += "..."
175
break
176
}
177
}
178
}
179
180
f.Button.SetTooltipText(folderName)
181
}
182
183
// Remove removes the given guild by its ID.
184
func (f *Folder) Remove(id discord.GuildID) {
185
for i, guild := range f.Guilds {
186
if guild.ID() == id {
187
f.GuildBox.Remove(guild)
188
f.Guilds = append(f.Guilds[:i], f.Guilds[i+1:]...)
189
return
190
}
191
}
192
}
193
194
func (f *Folder) viewChild() {}
195
196
// InvalidateUnread invalidates the folder's unread state.
197
func (f *Folder) InvalidateUnread() {
198
f.Button.Pill.Attrs = 0
199
for _, guild := range f.Guilds {
200
f.Button.Pill.Attrs |= guild.Pill.Attrs
201
}
202
203
f.Button.Pill.Invalidate()
204
205
var mentions int
206
for _, guild := range f.Guilds {
207
mentions += guild.Mentions.Count()
208
}
209
210
f.Button.Folder.Mentions.SetCount(mentions)
211
}
212
213