implemented move/resize/pinpoint menu

This commit is contained in:
sqshq 2019-02-10 21:51:55 -05:00
parent f5ccf6c464
commit b9cc09c1e4
9 changed files with 401 additions and 53 deletions

View File

@ -8,7 +8,7 @@ runcharts:
script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com/ script: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com/
- label: BING - label: BING
script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com/ script: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com/
refresh-rate-ms: 500 refresh-rate-ms: 200
decimal-places: 3 decimal-places: 3
alert: alert:
value: value:
@ -52,8 +52,8 @@ runcharts:
x: 0 x: 0
y: 15 y: 15
size: size:
x: 15 x: 4
y: 15 y: 4
- title: MONGO COLLECTIONS COUNT - title: MONGO COLLECTIONS COUNT
items: items:
- label: POSTS - label: POSTS

14
console/key.go Normal file
View File

@ -0,0 +1,14 @@
package console
const (
KeyPause = "p"
KeyQuit = "q"
KeyResize = "<Resize>"
KeyExit = "<C-c>"
KeyLeft = "<Left>"
KeyRight = "<Right>"
KeyUp = "<Up>"
KeyDown = "<Down>"
KeyEnter = "<Enter>"
KeyEsc = "<Escape>"
)

View File

@ -17,6 +17,9 @@ const (
ColorDeepSkyBlue ui.Color = 39 ColorDeepSkyBlue ui.Color = 39
ColorDeepPink ui.Color = 162 ColorDeepPink ui.Color = 162
ColorDarkGrey ui.Color = 240 ColorDarkGrey ui.Color = 240
ColorWhite ui.Color = 7
ColorBlack ui.Color = 0
ColorClear ui.Color = -1
) )
type Palette struct { type Palette struct {

View File

@ -1,15 +0,0 @@
package event
type Event string
const (
EventPause = "p"
EventQuit = "q"
EventResize = "<Resize>"
EventExit = "<C-c>"
EventMouseClick = "<MouseLeft>"
EventKeyboardLeft = "<Left>"
EventKeyboardRight = "<Right>"
EventKeyboardUp = "<Up>"
EventKeyboardDown = "<Down>"
)

View File

@ -1,6 +1,7 @@
package event package event
import ( import (
"github.com/sqshq/sampler/console"
"github.com/sqshq/sampler/widgets" "github.com/sqshq/sampler/widgets"
ui "github.com/sqshq/termui" ui "github.com/sqshq/termui"
"time" "time"
@ -24,23 +25,17 @@ func (self *Handler) HandleEvents() {
} }
case e := <-self.ConsoleEvents: case e := <-self.ConsoleEvents:
switch e.ID { switch e.ID {
case EventQuit, EventExit: case console.KeyQuit, console.KeyExit:
return return
case EventPause: case console.KeyPause:
pause = !pause pause = !pause
case EventResize: case console.KeyResize:
payload := e.Payload.(ui.Resize) payload := e.Payload.(ui.Resize)
self.Layout.ChangeDimensions(payload.Width, payload.Height) self.Layout.ChangeDimensions(payload.Width, payload.Height)
case "a": //case "a":
self.Layout.GetComponent(0).DisableSelection() // self.Layout.GetComponent(0).DisableSelection()
case EventKeyboardLeft: default:
self.Layout.GetComponent(0).MoveSelection(-1) self.Layout.HandleConsoleEvent(e.ID)
case EventKeyboardRight:
self.Layout.GetComponent(0).MoveSelection(+1)
case EventKeyboardDown:
//layout.GetItem(0).Move(0, 1)
case EventKeyboardUp:
//layout.GetItem(0).Move(0, -1)
} }
} }
} }

View File

@ -17,12 +17,13 @@ func main() {
csl.Init() csl.Init()
defer csl.Close() defer csl.Close()
layout := widgets.NewLayout(ui.TerminalDimensions()) width, height := ui.TerminalDimensions()
layout := widgets.NewLayout(width, height, widgets.NewMenu())
for _, chartConfig := range cfg.RunCharts { for _, chartConfig := range cfg.RunCharts {
chart := widgets.NewRunChart(chartConfig.Title, chartConfig.Precision, chartConfig.RefreshRateMs) chart := widgets.NewRunChart(chartConfig.Title, chartConfig.Precision, chartConfig.RefreshRateMs)
layout.AddComponent(chart, chartConfig.Position, chartConfig.Size, widgets.TypeRunChart) layout.AddComponent(chart, chartConfig.Title, chartConfig.Position, chartConfig.Size, widgets.TypeRunChart)
for _, item := range chartConfig.Items { for _, item := range chartConfig.Items {
data.NewSampler(chart, item, chartConfig.RefreshRateMs) data.NewSampler(chart, item, chartConfig.RefreshRateMs)

View File

@ -6,15 +6,17 @@ import (
type Component struct { type Component struct {
Drawable Drawable Drawable Drawable
Title string
Position Position Position Position
Size Size Size Size
Type ComponentType Type ComponentType
} }
type ComponentType string type ComponentType rune
const ( const (
TypeRunChart ComponentType = "runchart" TypeRunChart ComponentType = 0
TypeBarChart ComponentType = 1
) )
type Position struct { type Position struct {

View File

@ -1,39 +1,57 @@
package widgets package widgets
import ( import (
. "github.com/sqshq/termui" "github.com/sqshq/sampler/console"
ui "github.com/sqshq/termui"
) )
type Layout struct { type Layout struct {
Block ui.Block
components []Component components []Component
menu *Menu
mode Mode
selection int
} }
type Mode rune
const (
ModeDefault Mode = 0
ModeComponentSelect Mode = 1
ModeMenuOptionSelect Mode = 2
ModeComponentMove Mode = 3
ModeComponentResize Mode = 4
ModeChartPinpoint Mode = 5
)
const ( const (
columnsCount = 30 columnsCount = 30
rowsCount = 30 rowsCount = 30
) )
func NewLayout(width, height int) *Layout { func NewLayout(width, height int, menu *Menu) *Layout {
block := *NewBlock() block := *ui.NewBlock()
block.SetRect(0, 0, width, height) block.SetRect(0, 0, width, height)
return &Layout{ return &Layout{
Block: block, Block: block,
components: make([]Component, 0), components: make([]Component, 0),
menu: menu,
mode: ModeDefault,
selection: 0,
} }
} }
func (self *Layout) AddComponent(drawable Drawable, position Position, size Size, Type ComponentType) { func (l *Layout) AddComponent(drawable ui.Drawable, title string, position Position, size Size, Type ComponentType) {
self.components = append(self.components, Component{drawable, position, size, Type}) l.components = append(l.components, Component{drawable, title, position, size, Type})
} }
func (self *Layout) GetComponents(Type ComponentType) []Drawable { func (l *Layout) GetComponents(Type ComponentType) []ui.Drawable {
var components []Drawable var components []ui.Drawable
for _, component := range self.components { for _, component := range l.components {
if component.Type == Type { if component.Type == Type {
components = append(components, component.Drawable) components = append(components, component.Drawable)
} }
@ -42,21 +60,127 @@ func (self *Layout) GetComponents(Type ComponentType) []Drawable {
return components return components
} }
// temp function until ui item selection is implemented // TODO func to get prev/next component navigating left/right/top/bottom
func (self *Layout) GetComponent(i int) *RunChart { func (l *Layout) getComponent(i int) Component {
return self.components[i].Drawable.(*RunChart) return l.components[i]
} }
func (self *Layout) ChangeDimensions(width, height int) { func (l *Layout) getSelectedComponent() *Component {
self.SetRect(0, 0, width, height) return &l.components[l.selection]
} }
func (self *Layout) Draw(buffer *Buffer) { func (l *Layout) HandleConsoleEvent(e string) {
switch e {
case console.KeyEnter:
switch l.mode {
case ModeComponentSelect:
l.menu.choose()
l.mode = ModeMenuOptionSelect
case ModeMenuOptionSelect:
option := l.menu.getSelectedOption()
switch option {
case MenuOptionMove:
l.mode = ModeComponentMove
l.menu.moveOrResize()
case MenuOptionResize:
l.mode = ModeComponentResize
l.menu.moveOrResize()
case MenuOptionPinpoint:
l.mode = ModeChartPinpoint
l.menu.idle()
chart := l.getSelectedComponent().Drawable.(*RunChart)
chart.MoveSelection(0)
case MenuOptionExit:
l.mode = ModeDefault
l.menu.idle()
}
case ModeComponentMove:
fallthrough
case ModeComponentResize:
l.menu.idle()
l.mode = ModeDefault
}
case console.KeyEsc:
switch l.mode {
case ModeChartPinpoint:
chart := l.getSelectedComponent().Drawable.(*RunChart)
chart.DisableSelection()
fallthrough
case ModeComponentSelect:
fallthrough
case ModeMenuOptionSelect:
l.menu.idle()
l.mode = ModeDefault
}
case console.KeyLeft:
switch l.mode {
case ModeDefault:
l.mode = ModeComponentSelect
l.selection = 0
l.menu.highlight(l.getComponent(l.selection))
case ModeChartPinpoint:
chart := l.getSelectedComponent().Drawable.(*RunChart)
chart.MoveSelection(-1)
case ModeComponentSelect:
if l.selection > 0 {
l.selection--
}
l.menu.highlight(l.getComponent(l.selection))
case ModeComponentMove:
l.getSelectedComponent().Move(-1, 0)
case ModeComponentResize:
l.getSelectedComponent().Resize(-1, 0)
}
case console.KeyRight:
switch l.mode {
case ModeDefault:
l.mode = ModeComponentSelect
l.selection = 0
l.menu.highlight(l.getComponent(l.selection))
case ModeChartPinpoint:
chart := l.getSelectedComponent().Drawable.(*RunChart)
chart.MoveSelection(1)
case ModeComponentSelect:
if l.selection < len(l.components)-1 {
l.selection++
}
l.menu.highlight(l.getComponent(l.selection))
case ModeComponentMove:
l.getSelectedComponent().Move(1, 0)
case ModeComponentResize:
l.getSelectedComponent().Resize(1, 0)
}
case console.KeyUp:
switch l.mode {
case ModeMenuOptionSelect:
l.menu.up()
case ModeComponentMove:
l.getSelectedComponent().Move(0, -1)
case ModeComponentResize:
l.getSelectedComponent().Resize(0, -1)
}
case console.KeyDown:
switch l.mode {
case ModeMenuOptionSelect:
l.menu.down()
case ModeComponentMove:
l.getSelectedComponent().Move(0, 1)
case ModeComponentResize:
l.getSelectedComponent().Resize(0, 1)
}
}
}
columnWidth := float64(self.GetRect().Dx()) / columnsCount func (l *Layout) ChangeDimensions(width, height int) {
rowHeight := float64(self.GetRect().Dy()) / rowsCount l.SetRect(0, 0, width, height)
}
for _, component := range self.components { func (l *Layout) Draw(buffer *ui.Buffer) {
columnWidth := float64(l.GetRect().Dx()) / columnsCount
rowHeight := float64(l.GetRect().Dy()) / rowsCount
for _, component := range l.components {
x1 := float64(component.Position.X) * columnWidth x1 := float64(component.Position.X) * columnWidth
y1 := float64(component.Position.Y) * rowHeight y1 := float64(component.Position.Y) * rowHeight
@ -66,4 +190,6 @@ func (self *Layout) Draw(buffer *Buffer) {
component.Drawable.SetRect(int(x1), int(y1), int(x2), int(y2)) component.Drawable.SetRect(int(x1), int(y1), int(x2), int(y2))
component.Drawable.Draw(buffer) component.Drawable.Draw(buffer)
} }
l.menu.Draw(buffer)
} }

222
widgets/menu.go Normal file
View File

@ -0,0 +1,222 @@
package widgets
import (
"github.com/sqshq/sampler/console"
ui "github.com/sqshq/termui"
"image"
)
type Menu struct {
ui.Block
options []MenuOption
component Component
mode MenuMode
option MenuOption
}
type MenuMode rune
const (
MenuModeIdle MenuMode = 0
MenuModeHighlight MenuMode = 1
MenuModeOptionSelect MenuMode = 2
MenuModeMoveAndResize MenuMode = 3
)
type MenuOption string
const (
MenuOptionMove MenuOption = "MOVE"
MenuOptionResize MenuOption = "RESIZE"
MenuOptionPinpoint MenuOption = "PINPOINT"
MenuOptionExit MenuOption = "EXIT"
)
func NewMenu() *Menu {
block := *ui.NewBlock()
block.Border = true
block.BorderStyle = ui.NewStyle(console.ColorDarkGrey)
return &Menu{
Block: block,
options: []MenuOption{MenuOptionMove, MenuOptionResize, MenuOptionPinpoint, MenuOptionExit},
mode: MenuModeIdle,
option: MenuOptionMove,
}
}
func (m *Menu) getSelectedOption() MenuOption {
return m.option
}
func (m *Menu) highlight(component Component) {
m.component = component
m.updateDimensions()
m.mode = MenuModeHighlight
m.Title = component.Title
}
func (m *Menu) choose() {
m.mode = MenuModeOptionSelect
}
func (m *Menu) idle() {
m.mode = MenuModeIdle
}
func (m *Menu) up() {
for i := 1; i < len(m.options); i++ {
if m.options[i] == m.option {
m.option = m.options[i-1]
break
}
}
}
func (m *Menu) down() {
for i := 0; i < len(m.options)-1; i++ {
if m.options[i] == m.option {
m.option = m.options[i+1]
break
}
}
}
func (m *Menu) moveOrResize() {
m.mode = MenuModeMoveAndResize
}
func (m *Menu) Draw(buffer *ui.Buffer) {
if m.mode == MenuModeIdle {
return
}
m.updateDimensions()
buffer.Fill(
ui.NewCell(' ', ui.NewStyle(ui.ColorClear, ui.ColorBlack)),
m.GetRect(),
)
switch m.mode {
case MenuModeHighlight:
m.renderHighlight(buffer)
case MenuModeMoveAndResize:
m.renderMoveAndResize(buffer)
case MenuModeOptionSelect:
m.renderOptions(buffer)
}
m.drawInnerBorder(buffer)
m.Block.Draw(buffer)
}
func (m *Menu) renderHighlight(buffer *ui.Buffer) {
m.printAllDirectionsArrowSign(buffer, -2)
arrowsText := "Use arrows for selection"
buffer.SetString(
arrowsText,
ui.NewStyle(console.ColorDarkGrey),
getMiddlePoint(m.Block, arrowsText, 2),
)
optionsText := "<ENTER> to view options"
buffer.SetString(
optionsText,
ui.NewStyle(console.ColorDarkGrey),
getMiddlePoint(m.Block, optionsText, 3),
)
resumeText := "<ESC> to resume"
buffer.SetString(
resumeText,
ui.NewStyle(console.ColorDarkGrey),
getMiddlePoint(m.Block, resumeText, 4),
)
}
func (m *Menu) renderMoveAndResize(buffer *ui.Buffer) {
m.printAllDirectionsArrowSign(buffer, -2)
saveText := "<ENTER> to save changes"
textPoint := getMiddlePoint(m.Block, saveText, 4)
if textPoint.In(m.Rectangle) {
buffer.SetString(
saveText,
ui.NewStyle(console.ColorDarkGrey),
textPoint,
)
}
}
func (m *Menu) printAllDirectionsArrowSign(buffer *ui.Buffer, y int) {
arrows := []string{
" ↑ ",
"←· →",
" ↓ ",
}
for i, a := range arrows {
buffer.SetString(
a,
ui.NewStyle(console.ColorOlive),
getMiddlePoint(m.Block, a, i+y),
)
}
}
func (m *Menu) renderOptions(buffer *ui.Buffer) {
// TODO extract styles to console.Palette
highlightedStyle := ui.NewStyle(console.ColorWhite, console.ColorClear, ui.ModifierReverse)
regularStyle := ui.NewStyle(console.ColorWhite)
offset := 1
for _, option := range m.options {
style := regularStyle
if m.option == option {
style = highlightedStyle
}
if option != MenuOptionPinpoint || m.component.Type == TypeRunChart {
offset += 2
buffer.SetString(
string(option),
style,
getMiddlePoint(m.Block, string(option), offset-5),
)
}
}
}
func (m *Menu) updateDimensions() {
r := m.component.Drawable.GetRect()
m.SetRect(r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
}
func (m *Menu) drawInnerBorder(buffer *ui.Buffer) {
verticalCell := ui.Cell{ui.VERTICAL_LINE, m.BorderStyle}
horizontalCell := ui.Cell{ui.HORIZONTAL_LINE, m.BorderStyle}
// draw lines
buffer.Fill(horizontalCell, image.Rect(m.Min.X+2, m.Min.Y+2, m.Max.X-2, m.Min.Y))
buffer.Fill(horizontalCell, image.Rect(m.Min.X+2, m.Max.Y-2, m.Max.X-2, m.Max.Y))
buffer.Fill(verticalCell, image.Rect(m.Min.X+2, m.Min.Y+1, m.Min.X+3, m.Max.Y-1))
buffer.Fill(verticalCell, image.Rect(m.Max.X-2, m.Min.Y, m.Max.X-3, m.Max.Y))
// draw corners
buffer.SetCell(ui.Cell{ui.TOP_LEFT, m.BorderStyle}, image.Pt(m.Min.X+2, m.Min.Y+1))
buffer.SetCell(ui.Cell{ui.TOP_RIGHT, m.BorderStyle}, image.Pt(m.Max.X-3, m.Min.Y+1))
buffer.SetCell(ui.Cell{ui.BOTTOM_LEFT, m.BorderStyle}, image.Pt(m.Min.X+2, m.Max.Y-2))
buffer.SetCell(ui.Cell{ui.BOTTOM_RIGHT, m.BorderStyle}, image.Pt(m.Max.X-3, m.Max.Y-2))
}
func getMiddlePoint(block ui.Block, text string, offset int) image.Point {
return image.Pt(block.Min.X+block.Dx()/2-len(text)/2, block.Max.Y-block.Dy()/2+offset)
}