Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
diamondburned
GitHub Repository: diamondburned/gtkcord4
Path: blob/main/internal/window/window.go
366 views
1
package window
2
3
import (
4
"context"
5
"log/slog"
6
7
"github.com/diamondburned/arikawa/v3/discord"
8
"github.com/diamondburned/gotk4-adwaita/pkg/adw"
9
"github.com/diamondburned/gotk4/pkg/glib/v2"
10
"github.com/diamondburned/gotk4/pkg/gtk/v4"
11
"github.com/diamondburned/gotkit/app"
12
"github.com/diamondburned/gotkit/app/prefs"
13
"github.com/diamondburned/gotkit/gtkutil"
14
"github.com/diamondburned/gotkit/gtkutil/cssutil"
15
"github.com/pkg/errors"
16
"libdb.so/ctxt"
17
"libdb.so/dissent/internal/gtkcord"
18
"libdb.so/dissent/internal/window/login"
19
"libdb.so/dissent/internal/window/quickswitcher"
20
)
21
22
var useDiscordColorScheme = prefs.NewBool(true, prefs.PropMeta{
23
Name: "Use Discord's color preference",
24
Section: "Discord",
25
Description: "Whether or not to use Discord's dark/light mode preference.",
26
})
27
28
// SetPreferDarkTheme sets whether or not GTK should use a dark theme.
29
func SetPreferDarkTheme(prefer bool) {
30
if !useDiscordColorScheme.Value() {
31
return
32
}
33
34
scheme := adw.ColorSchemePreferLight
35
if prefer {
36
scheme = adw.ColorSchemePreferDark
37
}
38
39
adwStyles := adw.StyleManagerGetDefault()
40
adwStyles.SetColorScheme(scheme)
41
}
42
43
// Window is the main gtkcord window.
44
type Window struct {
45
*adw.ApplicationWindow
46
win *app.Window
47
ctx context.Context
48
49
Stack *gtk.Stack
50
Login *login.Page
51
Loading *login.LoadingPage
52
Chat *ChatPage
53
}
54
55
// NewWindow creates a new Window.
56
func NewWindow(ctx context.Context) *Window {
57
appInstance := app.FromContext(ctx)
58
59
win := adw.NewApplicationWindow(appInstance.Application)
60
win.SetSizeRequest(320, 320)
61
win.SetDefaultSize(800, 600)
62
63
appWindow := app.WrapWindow(appInstance, &win.ApplicationWindow)
64
ctx = app.WithWindow(ctx, appWindow)
65
66
w := Window{
67
ApplicationWindow: win,
68
win: appWindow,
69
ctx: ctx,
70
}
71
w.ctx = ctxt.With(w.ctx, &w)
72
73
w.Login = login.NewPage(ctx, &loginWindow{Window: &w})
74
w.Login.LoadKeyring()
75
76
w.Loading = login.NewLoadingPage(ctx)
77
78
w.Stack = gtk.NewStack()
79
w.Stack.SetTransitionType(gtk.StackTransitionTypeCrossfade)
80
w.Stack.AddChild(w.Login)
81
w.Stack.AddChild(w.Loading)
82
w.Stack.SetVisibleChild(w.Login)
83
win.SetContent(w.Stack)
84
85
w.SwitchToLoginPage()
86
return &w
87
}
88
89
func (w *Window) Context() context.Context {
90
return w.ctx
91
}
92
93
func (w *Window) initChatPage() {
94
w.Chat = NewChatPage(w.ctx, w)
95
w.Stack.AddChild(w.Chat)
96
}
97
98
// It's not happy with how this requires a check for ChatPage, but it makes
99
// sense why these actions are bounded to Window and not ChatPage. Maybe?
100
// This requires long and hard thinking, which is simply too much for its
101
// brain.
102
func (w *Window) initActions() {
103
gtkutil.AddActions(w, map[string]func(){
104
"set-online": func() { w.setStatus(discord.OnlineStatus) },
105
"set-idle": func() { w.setStatus(discord.IdleStatus) },
106
"set-dnd": func() { w.setStatus(discord.DoNotDisturbStatus) },
107
"set-invisible": func() { w.setStatus(discord.InvisibleStatus) },
108
"open-dms": func() { w.useChatPage((*ChatPage).OpenDMs) },
109
"reset-view": func() { w.useChatPage((*ChatPage).ResetView) },
110
"quick-switcher": func() { w.useChatPage((*ChatPage).OpenQuickSwitcher) },
111
})
112
113
gtkutil.AddActionCallbacks(w, map[string]gtkutil.ActionCallback{
114
"open-channel": {
115
ArgType: gtkcord.SnowflakeVariant,
116
Func: func(variant *glib.Variant) {
117
id := discord.ChannelID(variant.Int64())
118
slog.Debug(
119
"opening channel from window-scoped action",
120
"channel_id", id)
121
w.useChatPage(func(p *ChatPage) { p.OpenChannel(id) })
122
},
123
},
124
"open-guild": {
125
ArgType: gtkcord.SnowflakeVariant,
126
Func: func(variant *glib.Variant) {
127
id := discord.GuildID(variant.Int64())
128
slog.Debug(
129
"opening guild from window-scoped action",
130
"guild_id", id)
131
w.useChatPage(func(p *ChatPage) { p.OpenGuild(id) })
132
},
133
},
134
})
135
136
gtkutil.AddActionShortcuts(w, map[string]string{
137
"<Ctrl>K": "win.quick-switcher",
138
})
139
}
140
141
func (w *Window) SwitchToChatPage() {
142
w.Stack.SetVisibleChild(w.Chat)
143
w.Chat.SwitchToMessages()
144
w.SetTitle("")
145
}
146
147
func (w *Window) SwitchToLoginPage() {
148
w.Stack.SetVisibleChild(w.Login)
149
w.SetTitle("Login")
150
}
151
152
func (w *Window) SetLoading() {
153
panic("not implemented")
154
}
155
156
// SetTitle sets the window title.
157
func (w *Window) SetTitle(title string) {
158
w.ApplicationWindow.SetTitle(app.FromContext(w.ctx).SuffixedTitle(title))
159
}
160
161
func (w *Window) showQuickSwitcher() {
162
w.useChatPage(func(*ChatPage) {
163
quickswitcher.ShowDialog(w.ctx)
164
})
165
}
166
167
func (w *Window) useChatPage(f func(*ChatPage)) {
168
if w.Chat != nil {
169
f(w.Chat)
170
}
171
}
172
173
func (w *Window) setStatus(status discord.Status) {
174
w.useChatPage(func(*ChatPage) {
175
state := gtkcord.FromContext(w.ctx).Online()
176
go func() {
177
if err := state.SetStatus(status, nil); err != nil {
178
app.Error(w.ctx, errors.Wrap(err, "invalid status"))
179
}
180
}()
181
})
182
}
183
184
var emptyHeaderCSS = cssutil.Applier("empty-header", `
185
.empty-header {
186
min-height: 0;
187
min-width: 0;
188
padding: 0;
189
margin: 0;
190
border: 0;
191
}
192
`)
193
194
func newEmptyHeader() *gtk.Box {
195
b := gtk.NewBox(gtk.OrientationVertical, 0)
196
emptyHeaderCSS(b)
197
return b
198
}
199
200