1package tview 2 3import ( 4 "github.com/gdamore/tcell" 5) 6 7// Modal is a centered message window used to inform the user or prompt them 8// for an immediate decision. It needs to have at least one button (added via 9// AddButtons()) or it will never disappear. 10// 11// See https://github.com/rivo/tview/wiki/Modal for an example. 12type Modal struct { 13 *Box 14 15 // The frame embedded in the modal. 16 frame *Frame 17 18 // The form embedded in the modal's frame. 19 form *Form 20 21 // The message text (original, not word-wrapped). 22 text string 23 24 // The text color. 25 textColor tcell.Color 26 27 // The optional callback for when the user clicked one of the buttons. It 28 // receives the index of the clicked button and the button's label. 29 done func(buttonIndex int, buttonLabel string) 30} 31 32// NewModal returns a new modal message window. 33func NewModal() *Modal { 34 m := &Modal{ 35 Box: NewBox(), 36 textColor: Styles.PrimaryTextColor, 37 } 38 m.form = NewForm(). 39 SetButtonsAlign(AlignCenter). 40 SetButtonBackgroundColor(Styles.PrimitiveBackgroundColor). 41 SetButtonTextColor(Styles.PrimaryTextColor) 42 m.form.SetBackgroundColor(Styles.ContrastBackgroundColor).SetBorderPadding(0, 0, 0, 0) 43 m.form.SetCancelFunc(func() { 44 if m.done != nil { 45 m.done(-1, "") 46 } 47 }) 48 m.frame = NewFrame(m.form).SetBorders(0, 0, 1, 0, 0, 0) 49 m.frame.SetBorder(true). 50 SetBackgroundColor(Styles.ContrastBackgroundColor). 51 SetBorderPadding(1, 1, 1, 1) 52 m.focus = m 53 return m 54} 55 56// SetBackgroundColor sets the color of the modal frame background. 57func (m *Modal) SetBackgroundColor(color tcell.Color) *Modal { 58 m.form.SetBackgroundColor(color) 59 m.frame.SetBackgroundColor(color) 60 return m 61} 62 63// SetTextColor sets the color of the message text. 64func (m *Modal) SetTextColor(color tcell.Color) *Modal { 65 m.textColor = color 66 return m 67} 68 69// SetButtonBackgroundColor sets the background color of the buttons. 70func (m *Modal) SetButtonBackgroundColor(color tcell.Color) *Modal { 71 m.form.SetButtonBackgroundColor(color) 72 return m 73} 74 75// SetButtonTextColor sets the color of the button texts. 76func (m *Modal) SetButtonTextColor(color tcell.Color) *Modal { 77 m.form.SetButtonTextColor(color) 78 return m 79} 80 81// SetDoneFunc sets a handler which is called when one of the buttons was 82// pressed. It receives the index of the button as well as its label text. The 83// handler is also called when the user presses the Escape key. The index will 84// then be negative and the label text an emptry string. 85func (m *Modal) SetDoneFunc(handler func(buttonIndex int, buttonLabel string)) *Modal { 86 m.done = handler 87 return m 88} 89 90// SetText sets the message text of the window. The text may contain line 91// breaks. Note that words are wrapped, too, based on the final size of the 92// window. 93func (m *Modal) SetText(text string) *Modal { 94 m.text = text 95 return m 96} 97 98// AddButtons adds buttons to the window. There must be at least one button and 99// a "done" handler so the window can be closed again. 100func (m *Modal) AddButtons(labels []string) *Modal { 101 for index, label := range labels { 102 func(i int, l string) { 103 m.form.AddButton(label, func() { 104 if m.done != nil { 105 m.done(i, l) 106 } 107 }) 108 button := m.form.GetButton(m.form.GetButtonCount() - 1) 109 button.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { 110 switch event.Key() { 111 case tcell.KeyDown, tcell.KeyRight: 112 return tcell.NewEventKey(tcell.KeyTab, 0, tcell.ModNone) 113 case tcell.KeyUp, tcell.KeyLeft: 114 return tcell.NewEventKey(tcell.KeyBacktab, 0, tcell.ModNone) 115 } 116 return event 117 }) 118 }(index, label) 119 } 120 return m 121} 122 123// ClearButtons removes all buttons from the window. 124func (m *Modal) ClearButtons() *Modal { 125 m.form.ClearButtons() 126 return m 127} 128 129// SetFocus shifts the focus to the button with the given index. 130func (m *Modal) SetFocus(index int) *Modal { 131 m.form.SetFocus(index) 132 return m 133} 134 135// Focus is called when this primitive receives focus. 136func (m *Modal) Focus(delegate func(p Primitive)) { 137 delegate(m.form) 138} 139 140// HasFocus returns whether or not this primitive has focus. 141func (m *Modal) HasFocus() bool { 142 return m.form.HasFocus() 143} 144 145// Draw draws this primitive onto the screen. 146func (m *Modal) Draw(screen tcell.Screen) { 147 // Calculate the width of this modal. 148 buttonsWidth := 0 149 for _, button := range m.form.buttons { 150 buttonsWidth += TaggedStringWidth(button.label) + 4 + 2 151 } 152 buttonsWidth -= 2 153 screenWidth, screenHeight := screen.Size() 154 width := screenWidth / 3 155 if width < buttonsWidth { 156 width = buttonsWidth 157 } 158 // width is now without the box border. 159 160 // Reset the text and find out how wide it is. 161 m.frame.Clear() 162 lines := WordWrap(m.text, width) 163 for _, line := range lines { 164 m.frame.AddText(line, true, AlignCenter, m.textColor) 165 } 166 167 // Set the modal's position and size. 168 height := len(lines) + 6 169 width += 4 170 x := (screenWidth - width) / 2 171 y := (screenHeight - height) / 2 172 m.SetRect(x, y, width, height) 173 174 // Draw the frame. 175 m.frame.SetRect(x, y, width, height) 176 m.frame.Draw(screen) 177} 178 179// MouseHandler returns the mouse handler for this primitive. 180func (m *Modal) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { 181 return m.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { 182 // Pass mouse events on to the form. 183 consumed, capture = m.form.MouseHandler()(action, event, setFocus) 184 if !consumed && action == MouseLeftClick && m.InRect(event.Position()) { 185 setFocus(m) 186 consumed = true 187 } 188 return 189 }) 190} 191