UI: removed WindowContainer, and replaced it with DrawWindow function
This commit is contained in:
parent
a4d5f3ccf4
commit
0dcdd62773
@ -74,5 +74,15 @@ func DrawRectOutlineDefault(s tcell.Screen, x, y, width, height int, style tcell
|
|||||||
DrawRectOutline(s, x, y, width, height, '┌', '┐', '└', '┘', '─', '│', style)
|
DrawRectOutline(s, x, y, width, height, '┌', '┐', '└', '┘', '─', '│', style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DrawWindow draws a window-like object at x and y as the top-left corner. This window
|
||||||
|
// has an optional title. The Theme values "WindowHeader" and "Window" are used.
|
||||||
|
func DrawWindow(s tcell.Screen, x, y, width, height int, title string, theme *Theme) {
|
||||||
|
headerStyle := theme.GetOrDefault("WindowHeader")
|
||||||
|
|
||||||
|
DrawRect(s, x, y, width, 1, ' ', headerStyle) // Draw header background
|
||||||
|
DrawStr(s, x + width/2 - len(title)/2, y, title, headerStyle) // Draw header title
|
||||||
|
|
||||||
|
DrawRect(s, x, y+1, width, height-1, ' ', theme.GetOrDefault("Window")) // Draw body
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add DrawShadow(x, y, width, height int)
|
// TODO: add DrawShadow(x, y, width, height int)
|
||||||
// TODO: add DrawWindow(x, y, width, height int, style tcell.Style)
|
|
||||||
|
@ -11,9 +11,9 @@ import (
|
|||||||
type FileSelectorDialog struct {
|
type FileSelectorDialog struct {
|
||||||
MustExist bool // Whether the dialog should have a user select an existing file.
|
MustExist bool // Whether the dialog should have a user select an existing file.
|
||||||
FilesChosenCallback func([]string) // Returns slice of filenames selected. nil if user canceled.
|
FilesChosenCallback func([]string) // Returns slice of filenames selected. nil if user canceled.
|
||||||
CancelCallback func() // Called when the dialog has been canceled by the user
|
Theme *Theme
|
||||||
|
|
||||||
container *WindowContainer
|
title string
|
||||||
x, y int
|
x, y int
|
||||||
width, height int
|
width, height int
|
||||||
focused bool
|
focused bool
|
||||||
@ -24,16 +24,14 @@ type FileSelectorDialog struct {
|
|||||||
inputField *InputField
|
inputField *InputField
|
||||||
confirmButton *Button
|
confirmButton *Button
|
||||||
cancelButton *Button
|
cancelButton *Button
|
||||||
|
|
||||||
Theme *Theme
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFileSelectorDialog(screen *tcell.Screen, title string, mustExist bool, theme *Theme, filesChosenCallback func([]string), cancelCallback func()) *FileSelectorDialog {
|
func NewFileSelectorDialog(screen *tcell.Screen, title string, mustExist bool, theme *Theme, filesChosenCallback func([]string), cancelCallback func()) *FileSelectorDialog {
|
||||||
dialog := &FileSelectorDialog{
|
dialog := &FileSelectorDialog{
|
||||||
MustExist: mustExist,
|
MustExist: mustExist,
|
||||||
FilesChosenCallback: filesChosenCallback,
|
FilesChosenCallback: filesChosenCallback,
|
||||||
container: NewWindowContainer(title, nil, theme),
|
|
||||||
Theme: theme,
|
Theme: theme,
|
||||||
|
title: title,
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.inputField = NewInputField(screen, "", theme)
|
dialog.inputField = NewInputField(screen, "", theme)
|
||||||
@ -55,12 +53,16 @@ func (d *FileSelectorDialog) onConfirm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *FileSelectorDialog) SetCancelCallback(callback func()) {
|
||||||
|
d.cancelButton.Callback = callback
|
||||||
|
}
|
||||||
|
|
||||||
func (d *FileSelectorDialog) SetTitle(title string) {
|
func (d *FileSelectorDialog) SetTitle(title string) {
|
||||||
d.container.Title = title
|
d.title = title
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *FileSelectorDialog) Draw(s tcell.Screen) {
|
func (d *FileSelectorDialog) Draw(s tcell.Screen) {
|
||||||
d.container.Draw(s)
|
DrawWindow(s, d.x, d.y, d.width, d.height, d.title, d.Theme)
|
||||||
|
|
||||||
// Update positions of child components (dependent on size information that may not be available at SetPos() )
|
// Update positions of child components (dependent on size information that may not be available at SetPos() )
|
||||||
btnWidth, _ := d.confirmButton.GetSize()
|
btnWidth, _ := d.confirmButton.GetSize()
|
||||||
@ -78,6 +80,9 @@ func (d *FileSelectorDialog) SetFocused(v bool) {
|
|||||||
|
|
||||||
func (d *FileSelectorDialog) SetTheme(theme *Theme) {
|
func (d *FileSelectorDialog) SetTheme(theme *Theme) {
|
||||||
d.Theme = theme
|
d.Theme = theme
|
||||||
|
d.inputField.SetTheme(theme)
|
||||||
|
d.confirmButton.SetTheme(theme)
|
||||||
|
d.cancelButton.SetTheme(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *FileSelectorDialog) GetPos() (int, int) {
|
func (d *FileSelectorDialog) GetPos() (int, int) {
|
||||||
@ -86,13 +91,12 @@ func (d *FileSelectorDialog) GetPos() (int, int) {
|
|||||||
|
|
||||||
func (d *FileSelectorDialog) SetPos(x, y int) {
|
func (d *FileSelectorDialog) SetPos(x, y int) {
|
||||||
d.x, d.y = x, y
|
d.x, d.y = x, y
|
||||||
d.container.SetPos(x, y)
|
|
||||||
d.inputField.SetPos(d.x+1, d.y+2) // Center input field
|
d.inputField.SetPos(d.x+1, d.y+2) // Center input field
|
||||||
d.cancelButton.SetPos(d.x+1, d.y+4) // Place "Cancel" button on left, bottom
|
d.cancelButton.SetPos(d.x+1, d.y+4) // Place "Cancel" button on left, bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *FileSelectorDialog) GetMinSize() (int, int) {
|
func (d *FileSelectorDialog) GetMinSize() (int, int) {
|
||||||
return len(d.container.Title) + 2, 6
|
return Max(len(d.title), 8) + 2, 6
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *FileSelectorDialog) GetSize() (int, int) {
|
func (d *FileSelectorDialog) GetSize() (int, int) {
|
||||||
@ -102,7 +106,6 @@ func (d *FileSelectorDialog) GetSize() (int, int) {
|
|||||||
func (d *FileSelectorDialog) SetSize(width, height int) {
|
func (d *FileSelectorDialog) SetSize(width, height int) {
|
||||||
minX, minY := d.GetMinSize()
|
minX, minY := d.GetMinSize()
|
||||||
d.width, d.height = Max(width, minX), Max(height, minY)
|
d.width, d.height = Max(width, minX), Max(height, minY)
|
||||||
d.container.SetSize(d.width, d.height)
|
|
||||||
|
|
||||||
d.inputField.SetSize(d.width-2, 1)
|
d.inputField.SetSize(d.width-2, 1)
|
||||||
d.cancelButton.SetSize(d.cancelButton.GetMinSize())
|
d.cancelButton.SetSize(d.cancelButton.GetMinSize())
|
||||||
@ -112,7 +115,8 @@ func (d *FileSelectorDialog) SetSize(width, height int) {
|
|||||||
func (d *FileSelectorDialog) HandleEvent(event tcell.Event) bool {
|
func (d *FileSelectorDialog) HandleEvent(event tcell.Event) bool {
|
||||||
switch ev := event.(type) {
|
switch ev := event.(type) {
|
||||||
case *tcell.EventKey:
|
case *tcell.EventKey:
|
||||||
if ev.Key() == tcell.KeyTab {
|
switch ev.Key() {
|
||||||
|
case tcell.KeyTab:
|
||||||
d.tabOrder[d.tabOrderIdx].SetFocused(false)
|
d.tabOrder[d.tabOrderIdx].SetFocused(false)
|
||||||
|
|
||||||
d.tabOrderIdx++
|
d.tabOrderIdx++
|
||||||
@ -123,6 +127,16 @@ func (d *FileSelectorDialog) HandleEvent(event tcell.Event) bool {
|
|||||||
d.tabOrder[d.tabOrderIdx].SetFocused(true)
|
d.tabOrder[d.tabOrderIdx].SetFocused(true)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
case tcell.KeyEsc:
|
||||||
|
if d.cancelButton.Callback != nil {
|
||||||
|
d.cancelButton.Callback()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case tcell.KeyEnter:
|
||||||
|
if d.tabOrder[d.tabOrderIdx] == d.inputField {
|
||||||
|
d.onConfirm()
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return d.tabOrder[d.tabOrderIdx].HandleEvent(event)
|
return d.tabOrder[d.tabOrderIdx].HandleEvent(event)
|
||||||
|
@ -6,99 +6,6 @@ import (
|
|||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Container has zero or more Components. Containers decide how Components are
|
|
||||||
// laid out in view, and may draw decorations like bounding boxes.
|
|
||||||
type Container interface {
|
|
||||||
Component
|
|
||||||
}
|
|
||||||
|
|
||||||
// A BoxContainer draws an outline using the `Character` with `Style` attributes
|
|
||||||
// around the `Child` Component.
|
|
||||||
type BoxContainer struct {
|
|
||||||
Child Component
|
|
||||||
|
|
||||||
x, y int
|
|
||||||
width, height int
|
|
||||||
ULRune rune // Rune for upper-left
|
|
||||||
URRune rune // Rune for upper-right
|
|
||||||
BLRune rune // Rune for bottom-left
|
|
||||||
BRRune rune // Rune for bottom-right
|
|
||||||
HorRune rune // Rune for horizontals
|
|
||||||
VertRune rune // Rune for verticals
|
|
||||||
|
|
||||||
Style tcell.Style
|
|
||||||
}
|
|
||||||
|
|
||||||
// New constructs a default BoxContainer using the terminal default style.
|
|
||||||
func NewBoxContainer(child Component, style tcell.Style) *BoxContainer {
|
|
||||||
return &BoxContainer{
|
|
||||||
Child: child,
|
|
||||||
ULRune: '╭',
|
|
||||||
URRune: '╮',
|
|
||||||
BLRune: '╰',
|
|
||||||
BRRune: '╯',
|
|
||||||
HorRune: '—',
|
|
||||||
VertRune: '│',
|
|
||||||
Style: style,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw will draws the border of the BoxContainer, then it draws its child component.
|
|
||||||
func (c *BoxContainer) Draw(s tcell.Screen) {
|
|
||||||
DrawRectOutline(s, c.x, c.y, c.width, c.height, c.ULRune, c.URRune, c.BLRune, c.BRRune, c.HorRune, c.VertRune, c.Style)
|
|
||||||
|
|
||||||
if c.Child != nil {
|
|
||||||
c.Child.Draw(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFocused calls SetFocused on the child Component.
|
|
||||||
func (c *BoxContainer) SetFocused(v bool) {
|
|
||||||
if c.Child != nil {
|
|
||||||
c.Child.SetFocused(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *BoxContainer) SetTheme(theme *Theme) {}
|
|
||||||
|
|
||||||
// GetPos returns the position of the container.
|
|
||||||
func (c *BoxContainer) GetPos() (int, int) {
|
|
||||||
return c.x, c.y
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPos sets the position of the container and updates the child Component.
|
|
||||||
func (c *BoxContainer) SetPos(x, y int) {
|
|
||||||
c.x, c.y = x, y
|
|
||||||
if c.Child != nil {
|
|
||||||
c.Child.SetPos(x+1, y+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *BoxContainer) GetMinSize() (int, int) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize gets the size of the container.
|
|
||||||
func (c *BoxContainer) GetSize() (int, int) {
|
|
||||||
return c.width, c.height
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSize sets the size of the container and updates the size of the child Component.
|
|
||||||
func (c *BoxContainer) SetSize(width, height int) {
|
|
||||||
c.width, c.height = width, height
|
|
||||||
if c.Child != nil {
|
|
||||||
c.Child.SetSize(width-2, height-2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleEvent forwards the event to the child Component and returns whether it was handled.
|
|
||||||
func (c *BoxContainer) HandleEvent(event tcell.Event) bool {
|
|
||||||
if c.Child != nil {
|
|
||||||
return c.Child.HandleEvent(event)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Tab is a child of a TabContainer; has a name and child Component.
|
// A Tab is a child of a TabContainer; has a name and child Component.
|
||||||
type Tab struct {
|
type Tab struct {
|
||||||
Name string
|
Name string
|
||||||
@ -297,84 +204,3 @@ func (c *TabContainer) HandleEvent(event tcell.Event) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace window container with draw function
|
|
||||||
// A WindowContainer has a border, a title, and a button to close the window.
|
|
||||||
type WindowContainer struct {
|
|
||||||
Title string
|
|
||||||
Child Component
|
|
||||||
|
|
||||||
x, y int
|
|
||||||
width, height int
|
|
||||||
focused bool
|
|
||||||
|
|
||||||
Theme *Theme
|
|
||||||
}
|
|
||||||
|
|
||||||
// New constructs a default WindowContainer using the terminal default style.
|
|
||||||
func NewWindowContainer(title string, child Component, theme *Theme) *WindowContainer {
|
|
||||||
return &WindowContainer{
|
|
||||||
Title: title,
|
|
||||||
Child: child,
|
|
||||||
Theme: theme,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw will draws the border of the WindowContainer, then it draws its child component.
|
|
||||||
func (w *WindowContainer) Draw(s tcell.Screen) {
|
|
||||||
headerStyle := w.Theme.GetOrDefault("WindowHeader")
|
|
||||||
|
|
||||||
DrawRect(s, w.x, w.y, w.width, 1, ' ', headerStyle) // Draw header
|
|
||||||
DrawStr(s, w.x+w.width/2-len(w.Title)/2, w.y, w.Title, headerStyle) // Draw title
|
|
||||||
DrawRect(s, w.x, w.y+1, w.width, w.height-1, ' ', w.Theme.GetOrDefault("Window")) // Draw body background
|
|
||||||
|
|
||||||
if w.Child != nil {
|
|
||||||
w.Child.Draw(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFocused calls SetFocused on the child Component.
|
|
||||||
func (w *WindowContainer) SetFocused(v bool) {
|
|
||||||
w.focused = v
|
|
||||||
if w.Child != nil {
|
|
||||||
w.Child.SetFocused(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPos returns the position of the container.
|
|
||||||
func (w *WindowContainer) GetPos() (int, int) {
|
|
||||||
return w.x, w.y
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPos sets the position of the container and updates the child Component.
|
|
||||||
func (w *WindowContainer) SetPos(x, y int) {
|
|
||||||
w.x, w.y = x, y
|
|
||||||
if w.Child != nil {
|
|
||||||
w.Child.SetPos(x, y+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WindowContainer) GetMinSize() (int, int) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize gets the size of the container.
|
|
||||||
func (w *WindowContainer) GetSize() (int, int) {
|
|
||||||
return w.width, w.height
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSize sets the size of the container and updates the size of the child Component.
|
|
||||||
func (w *WindowContainer) SetSize(width, height int) {
|
|
||||||
w.width, w.height = width, height
|
|
||||||
if w.Child != nil {
|
|
||||||
w.Child.SetSize(width, height-2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleEvent forwards the event to the child Component and returns whether it was handled.
|
|
||||||
func (w *WindowContainer) HandleEvent(event tcell.Event) bool {
|
|
||||||
if w.Child != nil {
|
|
||||||
return w.Child.HandleEvent(event)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user