mirror of
https://github.com/mum4k/termdash.git
synced 2025-04-25 13:48:50 +08:00
328 lines
8.8 KiB
Go
328 lines
8.8 KiB
Go
// Copyright 2020 Google Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Binary formdemo creates a form that accepts text inputs and supports
|
|
// keyboard navigation.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os/user"
|
|
"time"
|
|
|
|
"github.com/mum4k/termdash"
|
|
"github.com/mum4k/termdash/align"
|
|
"github.com/mum4k/termdash/cell"
|
|
"github.com/mum4k/termdash/container"
|
|
"github.com/mum4k/termdash/keyboard"
|
|
"github.com/mum4k/termdash/linestyle"
|
|
"github.com/mum4k/termdash/terminal/tcell"
|
|
"github.com/mum4k/termdash/widgets/button"
|
|
"github.com/mum4k/termdash/widgets/text"
|
|
"github.com/mum4k/termdash/widgets/textinput"
|
|
)
|
|
|
|
// buttonChunks creates the text chunks for a button from the provided text.
|
|
func buttonChunks(text string) []*button.TextChunk {
|
|
if len(text) == 0 {
|
|
return nil
|
|
}
|
|
first := string(text[0])
|
|
rest := string(text[1:])
|
|
|
|
return []*button.TextChunk{
|
|
button.NewChunk(
|
|
"<",
|
|
button.TextCellOpts(cell.FgColor(cell.ColorWhite)),
|
|
button.FocusedTextCellOpts(cell.FgColor(cell.ColorBlack)),
|
|
button.PressedTextCellOpts(cell.FgColor(cell.ColorBlack)),
|
|
),
|
|
button.NewChunk(
|
|
first,
|
|
button.TextCellOpts(cell.FgColor(cell.ColorRed)),
|
|
),
|
|
button.NewChunk(
|
|
rest,
|
|
button.TextCellOpts(cell.FgColor(cell.ColorWhite)),
|
|
button.FocusedTextCellOpts(cell.FgColor(cell.ColorBlack)),
|
|
button.PressedTextCellOpts(cell.FgColor(cell.ColorBlack)),
|
|
),
|
|
button.NewChunk(
|
|
">",
|
|
button.TextCellOpts(cell.FgColor(cell.ColorWhite)),
|
|
button.FocusedTextCellOpts(cell.FgColor(cell.ColorBlack)),
|
|
button.PressedTextCellOpts(cell.FgColor(cell.ColorBlack)),
|
|
),
|
|
}
|
|
}
|
|
|
|
// form contains the elements of a text input form.
|
|
type form struct {
|
|
// userInput is a text input that accepts user name.
|
|
userInput *textinput.TextInput
|
|
// uidInput is a text input that accepts UID.
|
|
uidInput *textinput.TextInput
|
|
// gidInput is a text input that accepts GID.
|
|
gidInput *textinput.TextInput
|
|
// homeInput is a text input that accepts path to the home folder.
|
|
homeInput *textinput.TextInput
|
|
|
|
// submitB is a button that submits the form.
|
|
submitB *button.Button
|
|
// cancelB is a button that exist the application.
|
|
cancelB *button.Button
|
|
}
|
|
|
|
// newForm returns a new form instance.
|
|
// The cancel argument is a function that terminates the application when called.
|
|
func newForm(cancel context.CancelFunc) (*form, error) {
|
|
var username string
|
|
u, err := user.Current()
|
|
if err != nil {
|
|
username = "mum4k"
|
|
} else {
|
|
username = u.Username
|
|
}
|
|
|
|
userInput, err := textinput.New(
|
|
textinput.Label("Username: ", cell.FgColor(cell.ColorNumber(33))),
|
|
textinput.DefaultText(username),
|
|
textinput.MaxWidthCells(20),
|
|
textinput.ExclusiveKeyboardOnFocus(),
|
|
)
|
|
uidInput, err := textinput.New(
|
|
textinput.Label("UID: ", cell.FgColor(cell.ColorNumber(33))),
|
|
textinput.DefaultText("1000"),
|
|
textinput.MaxWidthCells(20),
|
|
textinput.ExclusiveKeyboardOnFocus(),
|
|
)
|
|
gidInput, err := textinput.New(
|
|
textinput.Label("GID: ", cell.FgColor(cell.ColorNumber(33))),
|
|
textinput.DefaultText("1000"),
|
|
textinput.MaxWidthCells(20),
|
|
textinput.ExclusiveKeyboardOnFocus(),
|
|
)
|
|
homeInput, err := textinput.New(
|
|
textinput.Label("Home: ", cell.FgColor(cell.ColorNumber(33))),
|
|
textinput.DefaultText(fmt.Sprintf("/home/%s", username)),
|
|
textinput.MaxWidthCells(20),
|
|
textinput.ExclusiveKeyboardOnFocus(),
|
|
)
|
|
|
|
submitB, err := button.NewFromChunks(buttonChunks("Submit"), nil,
|
|
button.Key(keyboard.KeyEnter),
|
|
button.GlobalKeys('s', 'S'),
|
|
button.DisableShadow(),
|
|
button.Height(1),
|
|
button.TextHorizontalPadding(0),
|
|
button.FillColor(cell.ColorBlack),
|
|
button.FocusedFillColor(cell.ColorNumber(117)),
|
|
button.PressedFillColor(cell.ColorNumber(220)),
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
cancelB, err := button.NewFromChunks(buttonChunks("Cancel"), func() error {
|
|
cancel()
|
|
return nil
|
|
},
|
|
button.FillColor(cell.ColorNumber(220)),
|
|
button.Key(keyboard.KeyEnter),
|
|
button.GlobalKeys('c', 'C'),
|
|
button.DisableShadow(),
|
|
button.Height(1),
|
|
button.TextHorizontalPadding(0),
|
|
button.FillColor(cell.ColorBlack),
|
|
button.FocusedFillColor(cell.ColorNumber(117)),
|
|
button.PressedFillColor(cell.ColorNumber(220)),
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &form{
|
|
userInput: userInput,
|
|
uidInput: uidInput,
|
|
gidInput: gidInput,
|
|
homeInput: homeInput,
|
|
submitB: submitB,
|
|
cancelB: cancelB,
|
|
}, nil
|
|
}
|
|
|
|
// formLayout updates the container into a layout with text inputs and buttons.
|
|
func formLayout(c *container.Container, f *form) error {
|
|
return c.Update("root",
|
|
container.KeyFocusNext(keyboard.KeyTab),
|
|
container.KeyFocusPrevious(keyboard.KeyBacktab),
|
|
container.KeyFocusGroupsNext(keyboard.KeyArrowDown, 1),
|
|
container.KeyFocusGroupsPrevious(keyboard.KeyArrowUp, 1),
|
|
container.KeyFocusGroupsNext(keyboard.KeyArrowRight, 2),
|
|
container.KeyFocusGroupsPrevious(keyboard.KeyArrowLeft, 2),
|
|
container.SplitHorizontal(
|
|
container.Top(
|
|
container.Border(linestyle.Light),
|
|
container.SplitHorizontal(
|
|
container.Top(
|
|
container.SplitHorizontal(
|
|
container.Top(
|
|
container.Focused(),
|
|
container.KeyFocusGroups(1),
|
|
container.PlaceWidget(f.userInput),
|
|
),
|
|
container.Bottom(
|
|
container.KeyFocusGroups(1),
|
|
container.KeyFocusSkip(),
|
|
container.PlaceWidget(f.uidInput),
|
|
),
|
|
),
|
|
),
|
|
container.Bottom(
|
|
container.SplitHorizontal(
|
|
container.Top(
|
|
container.KeyFocusGroups(1),
|
|
container.KeyFocusSkip(),
|
|
container.PlaceWidget(f.gidInput),
|
|
),
|
|
container.Bottom(
|
|
container.KeyFocusGroups(1),
|
|
container.KeyFocusSkip(),
|
|
container.PlaceWidget(f.homeInput),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
container.Bottom(
|
|
container.SplitHorizontal(
|
|
container.Top(
|
|
container.SplitVertical(
|
|
container.Left(
|
|
container.KeyFocusGroups(1, 2),
|
|
container.PlaceWidget(f.submitB),
|
|
container.AlignHorizontal(align.HorizontalRight),
|
|
container.PaddingRight(5),
|
|
),
|
|
container.Right(
|
|
container.KeyFocusGroups(1, 2),
|
|
container.PlaceWidget(f.cancelB),
|
|
container.AlignHorizontal(align.HorizontalLeft),
|
|
container.PaddingLeft(5),
|
|
),
|
|
),
|
|
),
|
|
container.Bottom(
|
|
container.KeyFocusSkip(),
|
|
),
|
|
container.SplitFixed(3),
|
|
),
|
|
),
|
|
container.SplitFixed(6),
|
|
),
|
|
)
|
|
}
|
|
|
|
// submitLayout updates the container into a layout that displays the submitted data.
|
|
// The cancel argument is a function that terminates Termdash when called.
|
|
func submitLayout(c *container.Container, f *form, cancel context.CancelFunc) error {
|
|
t, err := text.New()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := t.Write("Submitted data:\n\n"); err != nil {
|
|
return err
|
|
}
|
|
if err := t.Write(fmt.Sprintf("Username: %s\n", f.userInput.Read())); err != nil {
|
|
return err
|
|
}
|
|
if err := t.Write(fmt.Sprintf("UID: %s\n", f.uidInput.Read())); err != nil {
|
|
return err
|
|
}
|
|
if err := t.Write(fmt.Sprintf("GID: %s\n", f.gidInput.Read())); err != nil {
|
|
return err
|
|
}
|
|
if err := t.Write(fmt.Sprintf("Home: %s\n", f.homeInput.Read())); err != nil {
|
|
return err
|
|
}
|
|
|
|
okB, err := button.NewFromChunks(buttonChunks("OK"), func() error {
|
|
cancel()
|
|
return nil
|
|
},
|
|
button.FillColor(cell.ColorNumber(220)),
|
|
button.Key(keyboard.KeyEnter),
|
|
button.GlobalKeys('o', 'O'),
|
|
button.DisableShadow(),
|
|
button.Height(1),
|
|
button.TextHorizontalPadding(0),
|
|
button.FillColor(cell.ColorBlack),
|
|
button.FocusedFillColor(cell.ColorNumber(117)),
|
|
button.PressedFillColor(cell.ColorNumber(220)),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.Update("root",
|
|
container.SplitHorizontal(
|
|
container.Top(
|
|
container.SplitVertical(
|
|
container.Left(),
|
|
container.Right(
|
|
container.PlaceWidget(t),
|
|
),
|
|
container.SplitPercent(33),
|
|
),
|
|
),
|
|
container.Bottom(
|
|
container.Focused(),
|
|
container.PlaceWidget(okB),
|
|
),
|
|
container.SplitFixed(7),
|
|
),
|
|
)
|
|
}
|
|
|
|
func main() {
|
|
t, err := tcell.New()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer t.Close()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
c, err := container.New(t, container.ID("root"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
f, err := newForm(cancel)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
f.submitB.SetCallback(func() error {
|
|
return submitLayout(c, f, cancel)
|
|
})
|
|
if err := formLayout(c, f); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := termdash.Run(ctx, t, c, termdash.RedrawInterval(100*time.Millisecond)); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|