2017-12-20 19:54:49 +00:00
|
|
|
package tview
|
|
|
|
|
|
|
|
import (
|
2020-10-18 12:15:57 +00:00
|
|
|
"github.com/gdamore/tcell/v2"
|
2017-12-20 19:54:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Modal is a centered message window used to inform the user or prompt them
|
|
|
|
// for an immediate decision. It needs to have at least one button (added via
|
2023-06-21 16:48:36 +00:00
|
|
|
// [Modal.AddButtons]) or it will never disappear.
|
2018-01-07 15:39:06 +00:00
|
|
|
//
|
|
|
|
// See https://github.com/rivo/tview/wiki/Modal for an example.
|
2017-12-20 19:54:49 +00:00
|
|
|
type Modal struct {
|
2017-12-30 21:08:43 +00:00
|
|
|
*Box
|
|
|
|
|
2020-03-27 17:41:44 +00:00
|
|
|
// The frame embedded in the modal.
|
2017-12-30 21:08:43 +00:00
|
|
|
frame *Frame
|
2017-12-20 19:54:49 +00:00
|
|
|
|
|
|
|
// The form embedded in the modal's frame.
|
|
|
|
form *Form
|
|
|
|
|
|
|
|
// The message text (original, not word-wrapped).
|
|
|
|
text string
|
|
|
|
|
|
|
|
// The text color.
|
|
|
|
textColor tcell.Color
|
|
|
|
|
|
|
|
// The optional callback for when the user clicked one of the buttons. It
|
|
|
|
// receives the index of the clicked button and the button's label.
|
|
|
|
done func(buttonIndex int, buttonLabel string)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewModal returns a new modal message window.
|
|
|
|
func NewModal() *Modal {
|
|
|
|
m := &Modal{
|
2017-12-30 21:08:43 +00:00
|
|
|
Box: NewBox(),
|
2018-01-10 08:43:32 +00:00
|
|
|
textColor: Styles.PrimaryTextColor,
|
2017-12-20 19:54:49 +00:00
|
|
|
}
|
|
|
|
m.form = NewForm().
|
|
|
|
SetButtonsAlign(AlignCenter).
|
2018-01-10 08:43:32 +00:00
|
|
|
SetButtonBackgroundColor(Styles.PrimitiveBackgroundColor).
|
|
|
|
SetButtonTextColor(Styles.PrimaryTextColor)
|
|
|
|
m.form.SetBackgroundColor(Styles.ContrastBackgroundColor).SetBorderPadding(0, 0, 0, 0)
|
2018-08-03 18:02:59 +00:00
|
|
|
m.form.SetCancelFunc(func() {
|
|
|
|
if m.done != nil {
|
|
|
|
m.done(-1, "")
|
|
|
|
}
|
|
|
|
})
|
2017-12-30 21:08:43 +00:00
|
|
|
m.frame = NewFrame(m.form).SetBorders(0, 0, 1, 0, 0, 0)
|
|
|
|
m.frame.SetBorder(true).
|
2018-01-10 08:43:32 +00:00
|
|
|
SetBackgroundColor(Styles.ContrastBackgroundColor).
|
|
|
|
SetBorderPadding(1, 1, 1, 1)
|
2017-12-20 19:54:49 +00:00
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2019-04-17 17:03:36 +00:00
|
|
|
// SetBackgroundColor sets the color of the modal frame background.
|
|
|
|
func (m *Modal) SetBackgroundColor(color tcell.Color) *Modal {
|
|
|
|
m.form.SetBackgroundColor(color)
|
|
|
|
m.frame.SetBackgroundColor(color)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2017-12-20 19:54:49 +00:00
|
|
|
// SetTextColor sets the color of the message text.
|
|
|
|
func (m *Modal) SetTextColor(color tcell.Color) *Modal {
|
|
|
|
m.textColor = color
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2019-04-17 17:03:36 +00:00
|
|
|
// SetButtonBackgroundColor sets the background color of the buttons.
|
|
|
|
func (m *Modal) SetButtonBackgroundColor(color tcell.Color) *Modal {
|
|
|
|
m.form.SetButtonBackgroundColor(color)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetButtonTextColor sets the color of the button texts.
|
|
|
|
func (m *Modal) SetButtonTextColor(color tcell.Color) *Modal {
|
|
|
|
m.form.SetButtonTextColor(color)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2023-06-21 16:48:36 +00:00
|
|
|
// SetButtonStyle sets the style of the buttons when they are not focused.
|
|
|
|
func (m *Modal) SetButtonStyle(style tcell.Style) *Modal {
|
|
|
|
m.form.SetButtonStyle(style)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetButtonActivatedStyle sets the style of the buttons when they are focused.
|
|
|
|
func (m *Modal) SetButtonActivatedStyle(style tcell.Style) *Modal {
|
|
|
|
m.form.SetButtonActivatedStyle(style)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2017-12-20 19:54:49 +00:00
|
|
|
// SetDoneFunc sets a handler which is called when one of the buttons was
|
|
|
|
// pressed. It receives the index of the button as well as its label text. The
|
|
|
|
// handler is also called when the user presses the Escape key. The index will
|
2023-05-24 21:33:20 +00:00
|
|
|
// then be negative and the label text an empty string.
|
2017-12-20 19:54:49 +00:00
|
|
|
func (m *Modal) SetDoneFunc(handler func(buttonIndex int, buttonLabel string)) *Modal {
|
|
|
|
m.done = handler
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetText sets the message text of the window. The text may contain line
|
2023-08-22 20:16:59 +00:00
|
|
|
// breaks but style tag states will not transfer to following lines. Note that
|
2022-02-16 10:27:59 +00:00
|
|
|
// words are wrapped, too, based on the final size of the window.
|
2017-12-20 19:54:49 +00:00
|
|
|
func (m *Modal) SetText(text string) *Modal {
|
|
|
|
m.text = text
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddButtons adds buttons to the window. There must be at least one button and
|
|
|
|
// a "done" handler so the window can be closed again.
|
|
|
|
func (m *Modal) AddButtons(labels []string) *Modal {
|
|
|
|
for index, label := range labels {
|
|
|
|
func(i int, l string) {
|
|
|
|
m.form.AddButton(label, func() {
|
|
|
|
if m.done != nil {
|
|
|
|
m.done(i, l)
|
|
|
|
}
|
|
|
|
})
|
2018-09-26 09:35:00 +00:00
|
|
|
button := m.form.GetButton(m.form.GetButtonCount() - 1)
|
|
|
|
button.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
|
|
|
switch event.Key() {
|
|
|
|
case tcell.KeyDown, tcell.KeyRight:
|
|
|
|
return tcell.NewEventKey(tcell.KeyTab, 0, tcell.ModNone)
|
|
|
|
case tcell.KeyUp, tcell.KeyLeft:
|
|
|
|
return tcell.NewEventKey(tcell.KeyBacktab, 0, tcell.ModNone)
|
|
|
|
}
|
|
|
|
return event
|
|
|
|
})
|
2017-12-20 19:54:49 +00:00
|
|
|
}(index, label)
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2019-05-14 14:48:29 +00:00
|
|
|
// ClearButtons removes all buttons from the window.
|
|
|
|
func (m *Modal) ClearButtons() *Modal {
|
|
|
|
m.form.ClearButtons()
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2019-11-27 17:27:26 +00:00
|
|
|
// SetFocus shifts the focus to the button with the given index.
|
|
|
|
func (m *Modal) SetFocus(index int) *Modal {
|
|
|
|
m.form.SetFocus(index)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2017-12-20 19:54:49 +00:00
|
|
|
// Focus is called when this primitive receives focus.
|
|
|
|
func (m *Modal) Focus(delegate func(p Primitive)) {
|
|
|
|
delegate(m.form)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasFocus returns whether or not this primitive has focus.
|
|
|
|
func (m *Modal) HasFocus() bool {
|
|
|
|
return m.form.HasFocus()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw draws this primitive onto the screen.
|
|
|
|
func (m *Modal) Draw(screen tcell.Screen) {
|
|
|
|
// Calculate the width of this modal.
|
|
|
|
buttonsWidth := 0
|
|
|
|
for _, button := range m.form.buttons {
|
2022-12-17 18:20:43 +00:00
|
|
|
buttonsWidth += TaggedStringWidth(button.text) + 4 + 2
|
2017-12-20 19:54:49 +00:00
|
|
|
}
|
|
|
|
buttonsWidth -= 2
|
|
|
|
screenWidth, screenHeight := screen.Size()
|
|
|
|
width := screenWidth / 3
|
|
|
|
if width < buttonsWidth {
|
|
|
|
width = buttonsWidth
|
|
|
|
}
|
|
|
|
// width is now without the box border.
|
|
|
|
|
|
|
|
// Reset the text and find out how wide it is.
|
2018-01-01 20:50:20 +00:00
|
|
|
m.frame.Clear()
|
2023-08-22 20:16:59 +00:00
|
|
|
lines := WordWrap(m.text, width)
|
2017-12-20 19:54:49 +00:00
|
|
|
for _, line := range lines {
|
2017-12-30 21:08:43 +00:00
|
|
|
m.frame.AddText(line, true, AlignCenter, m.textColor)
|
2017-12-20 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the modal's position and size.
|
|
|
|
height := len(lines) + 6
|
2017-12-30 21:08:43 +00:00
|
|
|
width += 4
|
2017-12-20 19:54:49 +00:00
|
|
|
x := (screenWidth - width) / 2
|
|
|
|
y := (screenHeight - height) / 2
|
|
|
|
m.SetRect(x, y, width, height)
|
|
|
|
|
|
|
|
// Draw the frame.
|
2017-12-30 21:08:43 +00:00
|
|
|
m.frame.SetRect(x, y, width, height)
|
|
|
|
m.frame.Draw(screen)
|
2017-12-20 19:54:49 +00:00
|
|
|
}
|
2019-11-04 05:34:46 +00:00
|
|
|
|
2020-01-24 20:40:34 +00:00
|
|
|
// MouseHandler returns the mouse handler for this primitive.
|
2020-02-14 02:09:09 +00:00
|
|
|
func (m *Modal) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
|
|
|
return m.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
2020-03-27 17:41:44 +00:00
|
|
|
// Pass mouse events on to the form.
|
|
|
|
consumed, capture = m.form.MouseHandler()(action, event, setFocus)
|
2022-09-02 15:49:12 +00:00
|
|
|
if !consumed && action == MouseLeftDown && m.InRect(event.Position()) {
|
2020-03-27 17:41:44 +00:00
|
|
|
setFocus(m)
|
|
|
|
consumed = true
|
2020-01-24 20:40:34 +00:00
|
|
|
}
|
2020-03-27 17:41:44 +00:00
|
|
|
return
|
2020-01-24 20:40:34 +00:00
|
|
|
})
|
2019-11-04 05:34:46 +00:00
|
|
|
}
|
2020-08-18 10:05:43 +00:00
|
|
|
|
|
|
|
// InputHandler returns the handler for this primitive.
|
|
|
|
func (m *Modal) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
|
|
|
return m.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
2020-11-17 18:33:25 +00:00
|
|
|
if m.frame.HasFocus() {
|
2020-08-18 10:05:43 +00:00
|
|
|
if handler := m.frame.InputHandler(); handler != nil {
|
|
|
|
handler(event, setFocus)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|