feat(ui): add backdrop for dialogs (#736)
This commit is contained in:
parent
5f884cdf28
commit
f0a2e8d5da
8 changed files with 97 additions and 38 deletions
4
go.mod
4
go.mod
|
|
@ -7,7 +7,7 @@ go 1.25.3
|
|||
require (
|
||||
github.com/BurntSushi/toml v1.6.0
|
||||
github.com/andybalholm/brotli v1.2.0
|
||||
github.com/ayn2op/tview v0.0.0-20260131155949-225a892e84f6
|
||||
github.com/ayn2op/tview v0.0.0-20260205013318-8c0da03daa84
|
||||
github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
|
||||
github.com/diamondburned/arikawa/v3 v3.6.1-0.20250928004212-a891a653eb26
|
||||
github.com/diamondburned/ningen/v3 v3.0.1-0.20250920191746-98fbd92e134d
|
||||
|
|
@ -51,7 +51,7 @@ require (
|
|||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96 // indirect
|
||||
golang.org/x/image v0.35.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20260120165949-40bd9ace6ce4 // indirect
|
||||
golang.org/x/mobile v0.0.0-20260204172633-1dceadbbeea3 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/term v0.39.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -8,8 +8,8 @@ github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
|
|||
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/ayn2op/tview v0.0.0-20260131155949-225a892e84f6 h1:3j6AxMluaTkZKkLmPiuzY1nranGEEJOs3wVbJQjReuQ=
|
||||
github.com/ayn2op/tview v0.0.0-20260131155949-225a892e84f6/go.mod h1:g6IOdF9SlnVZMDnRABANP8I0LFseKyYxWqEkzBSL5ho=
|
||||
github.com/ayn2op/tview v0.0.0-20260205013318-8c0da03daa84 h1:SGBLuL9Ks85RRmLuY9SSJwW4//q1yR5OnO1r//2gMa4=
|
||||
github.com/ayn2op/tview v0.0.0-20260205013318-8c0da03daa84/go.mod h1:g6IOdF9SlnVZMDnRABANP8I0LFseKyYxWqEkzBSL5ho=
|
||||
github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
|
||||
github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -111,8 +111,8 @@ golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96 h1:wJ3cDLvYRAWzRt6f3e2
|
|||
golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96/go.mod h1:hq/Ge0xSczE7aHicXVhn3Kd0j3hOtWQR4KEgAwemgdk=
|
||||
golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
|
||||
golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
|
||||
golang.org/x/mobile v0.0.0-20260120165949-40bd9ace6ce4 h1:C3JuLOLhdaE75vk5m7u18NvZciRk+lnO34xcXl3NPTU=
|
||||
golang.org/x/mobile v0.0.0-20260120165949-40bd9ace6ce4/go.mod h1:yHJY0EGzMJ0i5ONrrhdpDSSnoyres5LO7D2hSIbJJ5I=
|
||||
golang.org/x/mobile v0.0.0-20260204172633-1dceadbbeea3 h1:NiJtT7g4ncNFVjVZMAYNBrPSNhIjFYPj8UKA8MEw2A4=
|
||||
golang.org/x/mobile v0.0.0-20260204172633-1dceadbbeea3/go.mod h1:wReH3Q1agKmmLapipWFnd4NSs8KPz3fK6mSEZjXLkrg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
|
|
|||
|
|
@ -190,3 +190,8 @@ min_width = 20
|
|||
# Maximum height
|
||||
# 0 = make the list as tall as needed
|
||||
max_height = 0
|
||||
|
||||
[theme.dialog]
|
||||
style = {}
|
||||
# Background style: everything else behind the dialog
|
||||
background_style = { attributes = "dim" }
|
||||
|
|
|
|||
|
|
@ -156,6 +156,11 @@ type (
|
|||
MaxHeight uint `toml:"max_height"`
|
||||
}
|
||||
|
||||
DialogTheme struct {
|
||||
Style StyleWrapper `toml:"foreground_style"`
|
||||
BackgroundStyle StyleWrapper `toml:"background_style"`
|
||||
}
|
||||
|
||||
Theme struct {
|
||||
Title TitleTheme `toml:"title"`
|
||||
Footer FooterTheme `toml:"footer"`
|
||||
|
|
@ -163,6 +168,7 @@ type (
|
|||
GuildsTree GuildsTreeTheme `toml:"guilds_tree"`
|
||||
MessagesList MessagesListTheme `toml:"messages_list"`
|
||||
MentionsList MentionsListTheme `toml:"mentions_list"`
|
||||
Dialog DialogTheme `toml:"dialog"`
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package chat
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/ayn2op/tview/layers"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
|
@ -532,8 +533,12 @@ func (mi *messageInput) showMentionList() {
|
|||
l.SetRect(x, y, w, h)
|
||||
|
||||
mi.chatView.
|
||||
AddAndSwitchToPage(mentionsListPageName, l, false).
|
||||
ShowPage(flexPageName)
|
||||
AddLayer(l,
|
||||
layers.WithName(mentionsListPageName),
|
||||
layers.WithResize(false),
|
||||
layers.WithVisible(true),
|
||||
).
|
||||
SendToFront(mentionsListPageName)
|
||||
mi.chatView.app.SetFocus(mi)
|
||||
}
|
||||
|
||||
|
|
@ -586,8 +591,7 @@ func (mi *messageInput) addMentionUser(user *discord.User) {
|
|||
// used by chatView
|
||||
func (mi *messageInput) removeMentionsList() {
|
||||
mi.chatView.
|
||||
RemovePage(mentionsListPageName).
|
||||
SwitchToPage(flexPageName)
|
||||
RemoveLayer(mentionsListPageName)
|
||||
}
|
||||
|
||||
func (mi *messageInput) stopTabCompletion() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ayn2op/tview/layers"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
|
@ -509,7 +510,7 @@ func (ml *messagesList) showAttachmentsList(urls []string, attachments []discord
|
|||
SetHighlightFullLine(true).
|
||||
ShowSecondaryText(false).
|
||||
SetDoneFunc(func() {
|
||||
ml.chatView.RemovePage(attachmentsListPageName).SwitchToPage(flexPageName)
|
||||
ml.chatView.RemoveLayer(attachmentsListPageName)
|
||||
ml.chatView.app.SetFocus(ml)
|
||||
})
|
||||
list.
|
||||
|
|
@ -546,8 +547,14 @@ func (ml *messagesList) showAttachmentsList(urls []string, attachments []discord
|
|||
}
|
||||
|
||||
ml.chatView.
|
||||
AddAndSwitchToPage(attachmentsListPageName, ui.Centered(list, 0, 0), true).
|
||||
ShowPage(flexPageName)
|
||||
AddLayer(
|
||||
ui.Centered(list, 0, 0),
|
||||
layers.WithName(attachmentsListPageName),
|
||||
layers.WithResize(true),
|
||||
layers.WithVisible(true),
|
||||
layers.WithOverlay(),
|
||||
).
|
||||
SendToFront(attachmentsListPageName)
|
||||
}
|
||||
|
||||
func (ml *messagesList) openAttachment(attachment discord.Attachment) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package chat
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ayn2op/tview/layers"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -27,7 +28,7 @@ const (
|
|||
)
|
||||
|
||||
type View struct {
|
||||
*tview.Pages
|
||||
*layers.Layers
|
||||
|
||||
mainFlex *tview.Flex
|
||||
rightFlex *tview.Flex
|
||||
|
|
@ -52,7 +53,7 @@ type View struct {
|
|||
|
||||
func NewView(app *tview.Application, cfg *config.Config, onLogout func()) *View {
|
||||
v := &View{
|
||||
Pages: tview.NewPages(),
|
||||
Layers: layers.New(),
|
||||
|
||||
mainFlex: tview.NewFlex(),
|
||||
rightFlex: tview.NewFlex(),
|
||||
|
|
@ -63,12 +64,14 @@ func NewView(app *tview.Application, cfg *config.Config, onLogout func()) *View
|
|||
cfg: cfg,
|
||||
onLogout: onLogout,
|
||||
}
|
||||
|
||||
v.guildsTree = newGuildsTree(cfg, v)
|
||||
v.messagesList = newMessagesList(cfg, v)
|
||||
v.messageInput = newMessageInput(cfg, v)
|
||||
v.channelsPicker = newChannelsPicker(cfg, v)
|
||||
v.channelsPicker.SetCancelFunc(v.closePicker)
|
||||
|
||||
v.SetBackgroundLayerStyle(v.cfg.Theme.Dialog.BackgroundStyle.Style)
|
||||
v.SetInputCapture(v.onInputCapture)
|
||||
v.buildLayout()
|
||||
return v
|
||||
|
|
@ -100,11 +103,11 @@ func (v *View) buildLayout() {
|
|||
AddItem(v.guildsTree, 0, 1, true).
|
||||
AddItem(v.rightFlex, 0, 4, false)
|
||||
|
||||
v.AddAndSwitchToPage(flexPageName, v.mainFlex, true)
|
||||
v.AddLayer(v.mainFlex, layers.WithName(flexPageName), layers.WithResize(true), layers.WithVisible(true))
|
||||
}
|
||||
|
||||
func (v *View) togglePicker() {
|
||||
if v.HasPage(channelsPickerPageName) {
|
||||
if v.HasLayer(channelsPickerPageName) {
|
||||
v.closePicker()
|
||||
} else {
|
||||
v.openPicker()
|
||||
|
|
@ -112,12 +115,18 @@ func (v *View) togglePicker() {
|
|||
}
|
||||
|
||||
func (v *View) openPicker() {
|
||||
v.AddAndSwitchToPage(channelsPickerPageName, ui.Centered(v.channelsPicker, v.cfg.Picker.Width, v.cfg.Picker.Height), true).ShowPage(flexPageName)
|
||||
v.AddLayer(
|
||||
ui.Centered(v.channelsPicker, v.cfg.Picker.Width, v.cfg.Picker.Height),
|
||||
layers.WithName(channelsPickerPageName),
|
||||
layers.WithResize(true),
|
||||
layers.WithVisible(true),
|
||||
layers.WithOverlay(),
|
||||
).SendToFront(channelsPickerPageName)
|
||||
v.channelsPicker.update()
|
||||
}
|
||||
|
||||
func (v *View) closePicker() {
|
||||
v.RemovePage(channelsPickerPageName).SwitchToPage(flexPageName)
|
||||
v.RemoveLayer(channelsPickerPageName)
|
||||
v.channelsPicker.Update()
|
||||
}
|
||||
|
||||
|
|
@ -235,17 +244,22 @@ func (v *View) showConfirmModal(prompt string, buttons []string, onDone func(lab
|
|||
SetText(prompt).
|
||||
AddButtons(buttons).
|
||||
SetDoneFunc(func(_ int, buttonLabel string) {
|
||||
v.RemovePage(confirmModalPageName).SwitchToPage(flexPageName)
|
||||
v.RemoveLayer(confirmModalPageName)
|
||||
v.app.SetFocus(previousFocus)
|
||||
|
||||
if onDone != nil {
|
||||
onDone(buttonLabel)
|
||||
}
|
||||
})
|
||||
|
||||
v.
|
||||
AddAndSwitchToPage(confirmModalPageName, ui.Centered(modal, 0, 0), true).
|
||||
ShowPage(flexPageName)
|
||||
AddLayer(
|
||||
ui.Centered(modal, 0, 0),
|
||||
layers.WithName(confirmModalPageName),
|
||||
layers.WithResize(true),
|
||||
layers.WithVisible(true),
|
||||
layers.WithOverlay(),
|
||||
).
|
||||
SendToFront(confirmModalPageName)
|
||||
}
|
||||
|
||||
func (v *View) onReadUpdate(event *read.UpdateEvent) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import (
|
|||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/ayn2op/tview/layers"
|
||||
"github.com/gdamore/tcell/v3"
|
||||
|
||||
"github.com/ayn2op/discordo/internal/config"
|
||||
"github.com/ayn2op/discordo/internal/keyring"
|
||||
"github.com/ayn2op/discordo/internal/ui"
|
||||
|
|
@ -20,7 +23,7 @@ const (
|
|||
type DoneFn = func(token string)
|
||||
|
||||
type Form struct {
|
||||
*tview.Pages
|
||||
*layers.Layers
|
||||
app *tview.Application
|
||||
cfg *config.Config
|
||||
form *tview.Form
|
||||
|
|
@ -29,18 +32,19 @@ type Form struct {
|
|||
|
||||
func NewForm(app *tview.Application, cfg *config.Config, done DoneFn) *Form {
|
||||
f := &Form{
|
||||
Pages: tview.NewPages(),
|
||||
app: app,
|
||||
cfg: cfg,
|
||||
form: tview.NewForm(),
|
||||
done: done,
|
||||
Layers: layers.New(),
|
||||
app: app,
|
||||
cfg: cfg,
|
||||
form: tview.NewForm(),
|
||||
done: done,
|
||||
}
|
||||
|
||||
f.form.
|
||||
AddPasswordField("Token", "", 0, 0, nil).
|
||||
AddButton("Login", f.login).
|
||||
AddButton("Login with QR", f.loginWithQR)
|
||||
f.AddAndSwitchToPage(formPageName, f.form, true)
|
||||
f.SetBackgroundLayerStyle(f.cfg.Theme.Dialog.BackgroundStyle.Style)
|
||||
f.AddLayer(f.form, layers.WithName(formPageName), layers.WithResize(true), layers.WithVisible(true))
|
||||
return f
|
||||
}
|
||||
|
||||
|
|
@ -69,14 +73,33 @@ func (f *Form) onError(err error) {
|
|||
if buttonIndex == 0 {
|
||||
go clipboard.Write(clipboard.FmtText, []byte(message))
|
||||
} else {
|
||||
f.
|
||||
RemovePage(errorPageName).
|
||||
SwitchToPage(formPageName)
|
||||
f.RemoveLayer(errorPageName)
|
||||
}
|
||||
})
|
||||
{
|
||||
bg := f.cfg.Theme.Dialog.Style.GetBackground()
|
||||
if bg != tcell.ColorDefault {
|
||||
modal.SetBackgroundColor(bg)
|
||||
modal.SetButtonBackgroundColor(bg)
|
||||
}
|
||||
fg := f.cfg.Theme.Dialog.Style.GetForeground()
|
||||
if fg != tcell.ColorDefault {
|
||||
modal.SetTextColor(fg)
|
||||
modal.SetButtonTextColor(fg)
|
||||
}
|
||||
// Keep button styles aligned with dialog content without hiding text.
|
||||
modal.SetButtonStyle(f.cfg.Theme.Dialog.Style.Style)
|
||||
modal.SetButtonActivatedStyle(f.cfg.Theme.Dialog.Style.Style)
|
||||
}
|
||||
f.
|
||||
AddAndSwitchToPage(errorPageName, ui.Centered(modal, 0, 0), true).
|
||||
ShowPage(formPageName)
|
||||
AddLayer(
|
||||
ui.Centered(modal, 0, 0),
|
||||
layers.WithName(errorPageName),
|
||||
layers.WithResize(true),
|
||||
layers.WithVisible(true),
|
||||
layers.WithOverlay(),
|
||||
).
|
||||
SendToFront(errorPageName)
|
||||
}
|
||||
|
||||
func (f *Form) loginWithQR() {
|
||||
|
|
@ -87,18 +110,18 @@ func (f *Form) loginWithQR() {
|
|||
}
|
||||
|
||||
if token == "" {
|
||||
f.RemovePage(qrPageName).SwitchToPage(formPageName)
|
||||
f.RemoveLayer(qrPageName)
|
||||
return
|
||||
}
|
||||
|
||||
go keyring.SetToken(token)
|
||||
|
||||
f.RemovePage(qrPageName)
|
||||
f.RemoveLayer(qrPageName)
|
||||
if f.done != nil {
|
||||
f.done(token)
|
||||
}
|
||||
})
|
||||
|
||||
f.AddAndSwitchToPage(qrPageName, qr, true)
|
||||
f.AddLayer(qr, layers.WithName(qrPageName), layers.WithResize(true), layers.WithVisible(true))
|
||||
qr.start()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue