DrawStr: made more efficient and renders newline characters
Before, DrawStr would copy all bytes into a []rune slice, and later copy the rune slice (by accident) in the for i, v := range loop. Now, it uses a []byte slice and iterates with for i := range slice.
This commit is contained in:
parent
9192d35b3c
commit
f64af6fa29
@ -18,9 +18,19 @@ func DrawRect(s tcell.Screen, x, y, width, height int, char rune, style tcell.St
|
|||||||
|
|
||||||
// DrawStr will render each character of a string at `x` and `y`.
|
// DrawStr will render each character of a string at `x` and `y`.
|
||||||
func DrawStr(s tcell.Screen, x, y int, str string, style tcell.Style) {
|
func DrawStr(s tcell.Screen, x, y int, str string, style tcell.Style) {
|
||||||
runes := []rune(str)
|
var col int
|
||||||
for idx, r := range runes {
|
bytes := []byte(str)
|
||||||
s.SetContent(x+idx, y, r, nil, style)
|
var i int
|
||||||
|
for i < len(bytes) {
|
||||||
|
r, size := utf8.DecodeRune(bytes[i:])
|
||||||
|
if r == '\n' {
|
||||||
|
col = 0
|
||||||
|
y++
|
||||||
|
} else {
|
||||||
|
s.SetContent(x+col, y, r, nil, style)
|
||||||
|
}
|
||||||
|
i += size
|
||||||
|
col++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
135
ui/messagedialog.go
Executable file
135
ui/messagedialog.go
Executable file
@ -0,0 +1,135 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MessageDialogKind uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
MessageKindNormal MessageDialogKind = iota
|
||||||
|
MessageKindWarning
|
||||||
|
MessageKindError
|
||||||
|
)
|
||||||
|
|
||||||
|
// Index of messageDialogKindTitles is any MessageDialogKind.
|
||||||
|
var messageDialogKindTitles [3]string = [3]string{
|
||||||
|
"Message",
|
||||||
|
"Warning!",
|
||||||
|
"Error!",
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageDialog struct {
|
||||||
|
Title string
|
||||||
|
Kind MessageDialogKind
|
||||||
|
Callback func(string)
|
||||||
|
|
||||||
|
message string
|
||||||
|
messageWrapped string
|
||||||
|
|
||||||
|
x, y int
|
||||||
|
width, height int
|
||||||
|
focused bool
|
||||||
|
theme *Theme
|
||||||
|
|
||||||
|
buttons []*Button
|
||||||
|
selectedIdx int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessageDialog(title string, message string, kind MessageDialogKind, options []string, theme *Theme, callback func(string)) *MessageDialog {
|
||||||
|
if title == "" {
|
||||||
|
title = messageDialogKindTitles[kind] // Use default title
|
||||||
|
}
|
||||||
|
|
||||||
|
if options == nil || len(options) == 0 {
|
||||||
|
options = []string{"OK"}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog := MessageDialog{
|
||||||
|
Title: title,
|
||||||
|
Kind: kind,
|
||||||
|
Callback: callback,
|
||||||
|
|
||||||
|
theme: theme,
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.buttons = make([]*Button, len(options))
|
||||||
|
for i := range options {
|
||||||
|
dialog.buttons[i] = NewButton(options[i], theme, func() {
|
||||||
|
if dialog.Callback != nil {
|
||||||
|
dialog.Callback(dialog.buttons[dialog.selectedIdx].Text)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the dialog's size to its minimum size
|
||||||
|
dialog.SetSize(0, 0)
|
||||||
|
dialog.SetMessage(message)
|
||||||
|
|
||||||
|
return &dialog
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) SetMessage(message string) {
|
||||||
|
d.message = message
|
||||||
|
d.messageWrapped = runewidth.Wrap(message, d.width-2)
|
||||||
|
// Update height:
|
||||||
|
_, minHeight := d.GetMinSize()
|
||||||
|
d.height = Max(d.height, minHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) Draw(s tcell.Screen) {
|
||||||
|
DrawWindow(s, d.x, d.y, d.width, d.height, d.Title, d.theme)
|
||||||
|
|
||||||
|
// DrawStr will handle '\n' characters and wrap for us.
|
||||||
|
DrawStr(s, d.x+1, d.y+2, d.messageWrapped, d.theme.GetOrDefault("Window"))
|
||||||
|
|
||||||
|
col := d.width // Start from the right side
|
||||||
|
for i := range d.buttons {
|
||||||
|
width, _ := d.buttons[i].GetSize()
|
||||||
|
col -= width + 1 // Move left enough for each button (1 for padding)
|
||||||
|
d.buttons[i].SetPos(d.x+col, d.y+d.height-2)
|
||||||
|
d.buttons[i].Draw(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) SetFocused(v bool) {
|
||||||
|
d.focused = v
|
||||||
|
d.buttons[d.selectedIdx].SetFocused(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) SetTheme(theme *Theme) {
|
||||||
|
d.theme = theme
|
||||||
|
for i := range d.buttons {
|
||||||
|
d.buttons[i].SetTheme(theme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) GetPos() (int, int) {
|
||||||
|
return d.x, d.y
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) SetPos(x, y int) {
|
||||||
|
d.x, d.y = x, y
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) GetMinSize() (int, int) {
|
||||||
|
lines := strings.Count(d.messageWrapped, "\n") + 1
|
||||||
|
|
||||||
|
return Max(len(d.Title)+2, 30), 2+lines+2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) GetSize() (int, int) {
|
||||||
|
return d.width, d.height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) SetSize(width, height int) {
|
||||||
|
minWidth, minHeight := d.GetMinSize()
|
||||||
|
d.width, d.height = Max(width, minWidth), Max(height, minHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) HandleEvent(event tcell.Event) bool {
|
||||||
|
return d.buttons[d.selectedIdx].HandleEvent(event)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user