fix(ui): use dedicated modal model (#821)
This commit is contained in:
parent
226936310d
commit
d13ce0ea4a
9 changed files with 150 additions and 184 deletions
2
go.mod
2
go.mod
|
|
@ -10,7 +10,7 @@ require (
|
|||
github.com/andybalholm/brotli v1.2.2
|
||||
github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e
|
||||
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6
|
||||
github.com/ayn2op/tview v0.0.0-20260723005648-f29aace80bb2
|
||||
github.com/ayn2op/tview v0.0.0-20260730175844-eae642abc285
|
||||
github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
|
||||
github.com/gdamore/tcell/v3 v3.4.1
|
||||
github.com/gen2brain/beeep v0.11.2
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -16,8 +16,8 @@ github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e h1:rsMifRXwxkbha
|
|||
github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e/go.mod h1:gqklpHK7WHYTzpd1ZY6Ho/p9GURBghzBCzRiBYpaUP8=
|
||||
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6 h1:/khHm6G+GWwXpmwA8bJoaXoUhubqMEUG6spjZsnklIw=
|
||||
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6/go.mod h1:vqLIvETLkjc2vIgQzVEBqpGNCrgNe7YJqS9TUAJsXRI=
|
||||
github.com/ayn2op/tview v0.0.0-20260723005648-f29aace80bb2 h1:tAZcwU2ZM4zSndksuFkhqAQKmmAJ+wXYSZWCatrUdQ0=
|
||||
github.com/ayn2op/tview v0.0.0-20260723005648-f29aace80bb2/go.mod h1:bJZdl/FfspPLEW3nuX6ubt7YbmDoSj7f2IoWH37w1NE=
|
||||
github.com/ayn2op/tview v0.0.0-20260730175844-eae642abc285 h1:EaV8lNpt2vWTjc/8nPFtb8S0qA6a6EIsALNy4VYiHww=
|
||||
github.com/ayn2op/tview v0.0.0-20260730175844-eae642abc285/go.mod h1:bJZdl/FfspPLEW3nuX6ubt7YbmDoSj7f2IoWH37w1NE=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
|
|
|
|||
|
|
@ -908,8 +908,7 @@ func (ml *messagesList) Update(msg tview.Msg) tview.Cmd {
|
|||
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.Delete.Keybind):
|
||||
return ml.deleteSelectedMessage()
|
||||
case keybind.Matches(msg, ml.cfg.Keybinds.MessagesList.DeleteConfirm.Keybind):
|
||||
ml.confirmDelete()
|
||||
return nil
|
||||
return ml.confirmDelete()
|
||||
}
|
||||
return ml.Model.Update(msg)
|
||||
|
||||
|
|
@ -1294,19 +1293,16 @@ func (ml *messagesList) editSelectedMessage() tview.Cmd {
|
|||
return tview.SetFocus(ml.chat.composer)
|
||||
}
|
||||
|
||||
func (ml *messagesList) confirmDelete() {
|
||||
onChoice := func(choice string) {
|
||||
if choice == "Yes" {
|
||||
if cmd := ml.deleteSelectedMessage(); cmd != nil {
|
||||
cmd()
|
||||
}
|
||||
}
|
||||
func (ml *messagesList) confirmDelete() tview.Cmd {
|
||||
selectedMessage, ok := ml.selectedMessage()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
ml.chat.showConfirmDialog(
|
||||
message := *selectedMessage
|
||||
return ui.ShowModal(
|
||||
"Are you sure you want to delete this message?",
|
||||
[]string{"Yes", "No"},
|
||||
onChoice,
|
||||
ui.ModalButton{Label: "Yes", Result: deleteMessageMsg(message)},
|
||||
ui.ModalButton{Label: "No"},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1315,22 +1311,25 @@ func (ml *messagesList) deleteSelectedMessage() tview.Cmd {
|
|||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return ml.deleteMessageRequest(*selectedMessage)
|
||||
}
|
||||
|
||||
func (ml *messagesList) deleteMessageRequest(message discord.Message) tview.Cmd {
|
||||
return func() tview.Msg {
|
||||
if selectedMessage.GuildID.IsValid() {
|
||||
if !ml.chat.isMe(selectedMessage.Author.ID) && !ml.chat.state.HasPermissions(selectedMessage.ChannelID, discord.PermissionManageMessages) {
|
||||
slog.Error("failed to delete message; missing relevant permissions", "channel_id", selectedMessage.ChannelID, "message_id", selectedMessage.ID)
|
||||
if message.GuildID.IsValid() {
|
||||
if !ml.chat.isMe(message.Author.ID) && !ml.chat.state.HasPermissions(message.ChannelID, discord.PermissionManageMessages) {
|
||||
slog.Error("failed to delete message; missing relevant permissions", "channel_id", message.ChannelID, "message_id", message.ID)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := ml.chat.state.DeleteMessage(selectedMessage.ChannelID, selectedMessage.ID, ""); err != nil {
|
||||
slog.Error("failed to delete message", "channel_id", selectedMessage.ChannelID, "message_id", selectedMessage.ID, "err", err)
|
||||
if err := ml.chat.state.DeleteMessage(message.ChannelID, message.ID, ""); err != nil {
|
||||
slog.Error("failed to delete message", "channel_id", message.ChannelID, "message_id", message.ID, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := ml.chat.state.MessageRemove(selectedMessage.ChannelID, selectedMessage.ID); err != nil {
|
||||
slog.Error("failed to delete message", "channel_id", selectedMessage.ChannelID, "message_id", selectedMessage.ID, "err", err)
|
||||
if err := ml.chat.state.MessageRemove(message.ChannelID, message.ID); err != nil {
|
||||
slog.Error("failed to delete message", "channel_id", message.ChannelID, "message_id", message.ID, "err", err)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"github.com/ayn2op/ningen/v3/states/read"
|
||||
"github.com/ayn2op/tview"
|
||||
"github.com/ayn2op/tview/flex"
|
||||
"github.com/ayn2op/tview/frame"
|
||||
"github.com/ayn2op/tview/keybind"
|
||||
"github.com/ayn2op/tview/layers"
|
||||
"github.com/gdamore/tcell/v3"
|
||||
|
|
@ -30,9 +29,8 @@ import (
|
|||
const typingDuration = 10 * time.Second
|
||||
|
||||
const (
|
||||
flexLayerName = "flex"
|
||||
mentionsListLayerName = "mentionsList"
|
||||
confirmDialogLayerName = "confirmDialog"
|
||||
flexLayerName = "flex"
|
||||
mentionsListLayerName = "mentionsList"
|
||||
|
||||
channelsPickerLayerName = "channelsPicker"
|
||||
attachmentsPickerLayerName = "attachmentsPicker"
|
||||
|
|
@ -54,9 +52,6 @@ type Model struct {
|
|||
selectedChannel *discord.Channel
|
||||
selectedChannelMu sync.RWMutex
|
||||
|
||||
confirmDialogDone func(label string)
|
||||
confirmDialogPreviousFocus tview.Model
|
||||
|
||||
state *ningen.State
|
||||
events chan gateway.Event
|
||||
|
||||
|
|
@ -327,38 +322,10 @@ func (m *Model) Update(msg tview.Msg) tview.Cmd {
|
|||
return tview.Batch(focusCmd, m.messagesList.requestGuildMembers(msg.Channel.GuildID, msg.Messages))
|
||||
}
|
||||
return focusCmd
|
||||
case deleteMessageMsg:
|
||||
return m.messagesList.deleteMessageRequest(discord.Message(msg))
|
||||
case QuitMsg:
|
||||
return m.closeState()
|
||||
case tview.FormSubmitMsg:
|
||||
if m.HasLayer(confirmDialogLayerName) {
|
||||
m.RemoveLayer(confirmDialogLayerName)
|
||||
var focusCmd tview.Cmd
|
||||
if m.confirmDialogPreviousFocus != nil {
|
||||
focusCmd = tview.SetFocus(m.confirmDialogPreviousFocus)
|
||||
}
|
||||
onDone := m.confirmDialogDone
|
||||
m.confirmDialogDone = nil
|
||||
m.confirmDialogPreviousFocus = nil
|
||||
if onDone != nil {
|
||||
onDone(msg.ButtonLabel)
|
||||
}
|
||||
return focusCmd
|
||||
}
|
||||
case tview.FormCancelMsg:
|
||||
if m.HasLayer(confirmDialogLayerName) {
|
||||
m.RemoveLayer(confirmDialogLayerName)
|
||||
var focusCmd tview.Cmd
|
||||
if m.confirmDialogPreviousFocus != nil {
|
||||
focusCmd = tview.SetFocus(m.confirmDialogPreviousFocus)
|
||||
}
|
||||
onDone := m.confirmDialogDone
|
||||
m.confirmDialogDone = nil
|
||||
m.confirmDialogPreviousFocus = nil
|
||||
if onDone != nil {
|
||||
onDone("")
|
||||
}
|
||||
return focusCmd
|
||||
}
|
||||
case tview.KeyMsg:
|
||||
switch {
|
||||
case keybind.Matches(msg, m.cfg.Keybinds.FocusGuildsTree.Keybind):
|
||||
|
|
@ -392,29 +359,6 @@ func (m *Model) Update(msg tview.Msg) tview.Cmd {
|
|||
return m.Layers.Update(msg)
|
||||
}
|
||||
|
||||
func (m *Model) showConfirmDialog(prompt string, buttons []string, onDone func(label string)) {
|
||||
m.confirmDialogPreviousFocus = m.app.Focused()
|
||||
m.confirmDialogDone = onDone
|
||||
|
||||
form := tview.NewForm().SetButtonsAlignment(tview.AlignmentCenter)
|
||||
for _, button := range buttons {
|
||||
form.AddButton(button)
|
||||
}
|
||||
dialog := frame.NewModel(form).SetBorders(0, 0, 1, 0, 0, 0)
|
||||
for _, line := range tview.WordWrap(prompt, 80) {
|
||||
dialog.AddText(line, true, tview.AlignmentCenter, tview.Styles.PrimaryTextColor)
|
||||
}
|
||||
m.
|
||||
AddLayer(
|
||||
ui.Centered(dialog, 0, 0),
|
||||
layers.WithName(confirmDialogLayerName),
|
||||
layers.WithResize(true),
|
||||
layers.WithVisible(true),
|
||||
layers.WithOverlay(),
|
||||
).
|
||||
SendToFront(confirmDialogLayerName)
|
||||
}
|
||||
|
||||
func (m *Model) clearTypers() {
|
||||
m.typersMu.Lock()
|
||||
for _, timer := range m.typers {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ type olderMessagesLoadedMsg struct {
|
|||
Older []discord.Message
|
||||
}
|
||||
|
||||
type deleteMessageMsg discord.Message
|
||||
|
||||
type LogoutMsg struct{}
|
||||
|
||||
func (m *Model) logout() tview.Cmd {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@ package login
|
|||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/ayn2op/tview/frame"
|
||||
"github.com/ayn2op/tview/layers"
|
||||
"github.com/ayn2op/tview/tabs"
|
||||
"github.com/gdamore/tcell/v3"
|
||||
|
||||
"github.com/ayn2op/discordo/internal/config"
|
||||
"github.com/ayn2op/discordo/internal/ui"
|
||||
|
|
@ -16,16 +14,12 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
tabsLayerName = "tabs"
|
||||
errorLayerName = "error"
|
||||
tabsLayerName = "tabs"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
*layers.Layers
|
||||
tabs *tabs.Model
|
||||
|
||||
cfg *config.Config
|
||||
errorDialogText string
|
||||
}
|
||||
|
||||
func NewModel(cfg *config.Config) *Model {
|
||||
|
|
@ -38,79 +32,24 @@ func NewModel(cfg *config.Config) *Model {
|
|||
return &Model{
|
||||
Layers: l,
|
||||
tabs: tabs,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) Update(msg tview.Msg) tview.Cmd {
|
||||
switch msg := msg.(type) {
|
||||
case errMsg:
|
||||
if m.HasLayer(errorLayerName) {
|
||||
return nil
|
||||
}
|
||||
return m.showErrorDialog(msg.err)
|
||||
case tview.FormSubmitMsg:
|
||||
if !m.HasLayer(errorLayerName) {
|
||||
break
|
||||
}
|
||||
if msg.ButtonIndex == 0 {
|
||||
return setClipboard(m.errorDialogText)
|
||||
}
|
||||
m.RemoveLayer(errorLayerName)
|
||||
m.errorDialogText = ""
|
||||
return nil
|
||||
case tview.FormCancelMsg:
|
||||
if !m.HasLayer(errorLayerName) {
|
||||
return nil
|
||||
}
|
||||
m.RemoveLayer(errorLayerName)
|
||||
m.errorDialogText = ""
|
||||
return nil
|
||||
case copyErrorMsg:
|
||||
return setClipboard(string(msg))
|
||||
}
|
||||
return m.Layers.Update(msg)
|
||||
}
|
||||
|
||||
func (m *Model) showErrorDialog(err error) tview.Cmd {
|
||||
slog.Error("failed to login", "err", err)
|
||||
|
||||
message := err.Error()
|
||||
m.errorDialogText = message
|
||||
form := tview.NewForm().
|
||||
SetButtonsAlignment(tview.AlignmentCenter).
|
||||
AddButton("Copy").
|
||||
AddButton("Close")
|
||||
dialog := frame.NewModel(form).SetBorders(0, 0, 1, 0, 0, 0)
|
||||
for _, line := range tview.WordWrap(message, 80) {
|
||||
dialog.AddText(line, true, tview.AlignmentCenter, tview.Styles.PrimaryTextColor)
|
||||
}
|
||||
{
|
||||
bg := m.cfg.Theme.Dialog.Style.GetBackground()
|
||||
buttonStyle := m.cfg.Theme.Dialog.Style.Style
|
||||
if bg != tcell.ColorDefault {
|
||||
form.SetBackgroundColor(bg)
|
||||
dialog.SetBackgroundColor(bg)
|
||||
buttonStyle = buttonStyle.Background(bg)
|
||||
}
|
||||
fg := m.cfg.Theme.Dialog.Style.GetForeground()
|
||||
if fg != tcell.ColorDefault {
|
||||
dialog.Clear()
|
||||
for _, line := range tview.WordWrap(message, 80) {
|
||||
dialog.AddText(line, true, tview.AlignmentCenter, fg)
|
||||
}
|
||||
buttonStyle = buttonStyle.Foreground(fg)
|
||||
}
|
||||
// Keep button styles aligned with dialog content and still show focus.
|
||||
form.SetButtonStyle(buttonStyle)
|
||||
form.SetButtonActivatedStyle(buttonStyle.Reverse(true))
|
||||
}
|
||||
m.
|
||||
AddLayer(
|
||||
ui.Centered(dialog, 0, 0),
|
||||
layers.WithName(errorLayerName),
|
||||
layers.WithResize(true),
|
||||
layers.WithVisible(true),
|
||||
layers.WithOverlay(),
|
||||
).
|
||||
SendToFront(errorLayerName)
|
||||
return tview.SetFocus(dialog)
|
||||
return ui.ShowModal(message,
|
||||
ui.ModalButton{Label: "Copy", Result: copyErrorMsg(message), KeepOpen: true},
|
||||
ui.ModalButton{Label: "Close"},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ type errMsg struct {
|
|||
err error
|
||||
}
|
||||
|
||||
type copyErrorMsg string
|
||||
|
||||
func setClipboard(content string) tview.Cmd {
|
||||
return func() tview.Msg {
|
||||
_ = clipboard.Write(clipboard.FmtText, []byte(content))
|
||||
|
|
|
|||
18
internal/ui/modal.go
Normal file
18
internal/ui/modal.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package ui
|
||||
|
||||
import "github.com/ayn2op/tview"
|
||||
|
||||
type ModalButton struct {
|
||||
Label string
|
||||
Result tview.Msg
|
||||
KeepOpen bool
|
||||
}
|
||||
|
||||
type ModalMsg struct {
|
||||
Text string
|
||||
Buttons []ModalButton
|
||||
}
|
||||
|
||||
func ShowModal(text string, buttons ...ModalButton) tview.Cmd {
|
||||
return func() tview.Msg { return ModalMsg{Text: text, Buttons: buttons} }
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/ayn2op/discordo/internal/config"
|
||||
"github.com/ayn2op/discordo/internal/consts"
|
||||
"github.com/ayn2op/discordo/internal/ui"
|
||||
"github.com/ayn2op/discordo/internal/ui/chat"
|
||||
"github.com/ayn2op/discordo/internal/ui/login"
|
||||
"github.com/ayn2op/discordo/internal/ui/login/qr"
|
||||
|
|
@ -13,28 +14,41 @@ import (
|
|||
"github.com/ayn2op/tview/flex"
|
||||
"github.com/ayn2op/tview/help"
|
||||
"github.com/ayn2op/tview/keybind"
|
||||
"github.com/ayn2op/tview/layers"
|
||||
"github.com/ayn2op/tview/modal"
|
||||
"github.com/gdamore/tcell/v3"
|
||||
)
|
||||
|
||||
const tokenEnvVarKey = "DISCORDO_TOKEN"
|
||||
const (
|
||||
tokenEnvVarKey = "DISCORDO_TOKEN"
|
||||
contentLayerName = "content"
|
||||
modalLayerName = "modal"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
*layers.Layers
|
||||
|
||||
app *tview.Application
|
||||
rootFlex *flex.Model // inner + help
|
||||
inner tview.Model
|
||||
help *help.Model
|
||||
|
||||
cfg *config.Config
|
||||
modalRequest *ui.ModalMsg
|
||||
modalDialog *modal.Model
|
||||
modalPreviousFocus tview.Model
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func NewModel(cfg *config.Config, app *tview.Application) *Model {
|
||||
m := &Model{
|
||||
Layers: layers.New(),
|
||||
app: app,
|
||||
rootFlex: flex.NewModel(),
|
||||
help: help.NewModel(),
|
||||
|
||||
cfg: cfg,
|
||||
}
|
||||
m.SetBackgroundLayerStyle(cfg.Theme.Dialog.BackgroundStyle.Style)
|
||||
|
||||
m.rootFlex.SetDirection(flex.DirectionRow)
|
||||
|
||||
|
|
@ -66,20 +80,21 @@ func (m *Model) showChat(token string) tview.Cmd {
|
|||
}
|
||||
|
||||
func (m *Model) buildLayout() {
|
||||
m.Clear()
|
||||
m.modalRequest = nil
|
||||
m.modalDialog = nil
|
||||
m.modalPreviousFocus = nil
|
||||
m.rootFlex.Clear()
|
||||
if m.inner != nil {
|
||||
m.rootFlex.AddItem(m.inner, 0, 1, true)
|
||||
}
|
||||
m.rootFlex.AddItem(m.help, 1, 0, false)
|
||||
m.AddLayer(m.rootFlex, layers.WithName(contentLayerName), layers.WithResize(true), layers.WithVisible(true))
|
||||
m.updateHelpHeight()
|
||||
}
|
||||
|
||||
var _ tview.Model = (*Model)(nil)
|
||||
|
||||
func (m *Model) View(screen tcell.Screen) {
|
||||
m.rootFlex.View(screen)
|
||||
}
|
||||
|
||||
func (m *Model) Update(msg tview.Msg) tview.Cmd {
|
||||
switch msg := msg.(type) {
|
||||
case tview.InitMsg:
|
||||
|
|
@ -110,8 +125,18 @@ func (m *Model) Update(msg tview.Msg) tview.Cmd {
|
|||
m.showLogin(),
|
||||
deleteToken(),
|
||||
)
|
||||
case ui.ModalMsg:
|
||||
return m.showModal(msg)
|
||||
case modal.DoneMsg:
|
||||
return m.finishModal(msg)
|
||||
|
||||
case tview.KeyMsg:
|
||||
if m.modalRequest != nil {
|
||||
if !m.modalDialog.HasFocus() {
|
||||
return tview.Sequence(tview.SetFocus(m.modalDialog), func() tview.Msg { return msg })
|
||||
}
|
||||
return m.Layers.Update(msg)
|
||||
}
|
||||
switch {
|
||||
case keybind.Matches(msg, m.cfg.Keybinds.ToggleHelp.Keybind):
|
||||
m.help.SetShowAll(!m.help.ShowAll())
|
||||
|
|
@ -127,6 +152,10 @@ func (m *Model) Update(msg tview.Msg) tview.Cmd {
|
|||
}
|
||||
return tview.Sequence(innerCmd, tview.Quit())
|
||||
}
|
||||
case tview.MouseMsg, tview.PasteMsg, tview.FormSubmitMsg, tview.FormCancelMsg, tview.ButtonExitMsg:
|
||||
if m.modalRequest != nil {
|
||||
return m.Layers.Update(msg)
|
||||
}
|
||||
}
|
||||
|
||||
if m.inner != nil {
|
||||
|
|
@ -135,6 +164,66 @@ func (m *Model) Update(msg tview.Msg) tview.Cmd {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) showModal(request ui.ModalMsg) tview.Cmd {
|
||||
if m.modalRequest != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
labels := make([]string, len(request.Buttons))
|
||||
for i, button := range request.Buttons {
|
||||
labels[i] = button.Label
|
||||
}
|
||||
dialog := modal.NewModel().SetText(request.Text).AddButtons(labels)
|
||||
style := m.cfg.Theme.Dialog.Style.Style
|
||||
if bg := style.GetBackground(); bg != tcell.ColorDefault {
|
||||
dialog.SetBackgroundColor(bg)
|
||||
}
|
||||
if fg := style.GetForeground(); fg != tcell.ColorDefault {
|
||||
dialog.SetTextColor(fg)
|
||||
}
|
||||
dialog.SetButtonStyle(style).SetButtonActivatedStyle(style.Reverse(true))
|
||||
|
||||
m.modalRequest = &request
|
||||
m.modalDialog = dialog
|
||||
m.modalPreviousFocus = m.app.Focused()
|
||||
m.AddLayer(dialog,
|
||||
layers.WithName(modalLayerName),
|
||||
layers.WithResize(true),
|
||||
layers.WithVisible(true),
|
||||
layers.WithOverlay(),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) finishModal(done modal.DoneMsg) tview.Cmd {
|
||||
request := m.modalRequest
|
||||
if request == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result tview.Msg
|
||||
if done.ButtonIndex >= 0 && done.ButtonIndex < len(request.Buttons) {
|
||||
button := request.Buttons[done.ButtonIndex]
|
||||
if button.KeepOpen {
|
||||
return func() tview.Msg { return button.Result }
|
||||
}
|
||||
result = button.Result
|
||||
}
|
||||
|
||||
m.RemoveLayer(modalLayerName)
|
||||
focus := tview.Cmd(nil)
|
||||
if m.modalPreviousFocus != nil {
|
||||
focus = tview.SetFocus(m.modalPreviousFocus)
|
||||
}
|
||||
m.modalRequest = nil
|
||||
m.modalDialog = nil
|
||||
m.modalPreviousFocus = nil
|
||||
if result == nil {
|
||||
return focus
|
||||
}
|
||||
return tview.Sequence(focus, func() tview.Msg { return result })
|
||||
}
|
||||
|
||||
func (m *Model) updateHelpHeight() {
|
||||
height := 1
|
||||
if m.help.ShowAll() {
|
||||
|
|
@ -142,30 +231,3 @@ func (m *Model) updateHelpHeight() {
|
|||
}
|
||||
m.rootFlex.ResizeItem(m.help, height, 0)
|
||||
}
|
||||
|
||||
func (m *Model) Rect() (int, int, int, int) {
|
||||
return m.rootFlex.Rect()
|
||||
}
|
||||
|
||||
func (m *Model) SetRect(x int, y int, width int, height int) {
|
||||
m.rootFlex.SetRect(x, y, width, height)
|
||||
}
|
||||
|
||||
func (m *Model) Focus(delegate func(tview.Model)) {
|
||||
if m.inner != nil {
|
||||
delegate(m.inner)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) HasFocus() bool {
|
||||
if m.inner != nil {
|
||||
return m.inner.HasFocus()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *Model) Blur() {
|
||||
if m.inner != nil {
|
||||
m.inner.Blur()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue