...

Source file src/github.com/rivo/tview/modal.go

Documentation: github.com/rivo/tview

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

View as plain text