A lot of work. Started working on the session objects and added a new text display component and componant switcher.
This commit is contained in:
parent
e86cb4fd63
commit
910bdbc854
126
commandmap.go
Normal file
126
commandmap.go
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"./ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ModAlt rune = 1 << 31 - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// A command
|
||||||
|
type Command struct {
|
||||||
|
Name string
|
||||||
|
Doc string
|
||||||
|
Action func(ctx CommandContext) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
func (cmd *Command) Do(ctx CommandContext) error {
|
||||||
|
return cmd.Action(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The command context
|
||||||
|
type CommandContext interface {
|
||||||
|
// Returns the current frame. If no frame is defined, returns nil.
|
||||||
|
Frame() *Frame
|
||||||
|
}
|
||||||
|
|
||||||
|
// A command mapping
|
||||||
|
type CommandMapping struct {
|
||||||
|
Commands map[string]*Command
|
||||||
|
KeyMappings map[rune]*Command
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new, empty command mapping
|
||||||
|
func NewCommandMapping() *CommandMapping {
|
||||||
|
return &CommandMapping{make(map[string]*Command), make(map[rune]*Command)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a new command
|
||||||
|
func (cm *CommandMapping) Define(name string, doc string, opts string, fn func(ctx CommandContext) error) {
|
||||||
|
cm.Commands[name] = &Command{name, doc, fn}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a key mapping
|
||||||
|
func (cm *CommandMapping) MapKey(key rune, cmd *Command) {
|
||||||
|
cm.KeyMappings[key] = cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Searches for a command by name. Returns the command or null
|
||||||
|
func (cm *CommandMapping) Command(name string) *Command {
|
||||||
|
return cm.Commands[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Searches for a command by key mapping
|
||||||
|
func (cm *CommandMapping) KeyMapping(key rune) *Command {
|
||||||
|
return cm.KeyMappings[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers the standard view navigation commands. These commands require the frame
|
||||||
|
func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
|
cm.Define("move-down", "Moves the cursor down one row", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(0, 1) }))
|
||||||
|
cm.Define("move-up", "Moves the cursor up one row", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(0, -1) }))
|
||||||
|
cm.Define("move-left", "Moves the cursor left one column", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(-1, 0) }))
|
||||||
|
cm.Define("move-right", "Moves the cursor right one column", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(1, 0) }))
|
||||||
|
|
||||||
|
// TODO: Pages are just 25 rows and 15 columns at the moment
|
||||||
|
cm.Define("page-down", "Moves the cursor down one page", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(0, 25) }))
|
||||||
|
cm.Define("page-up", "Moves the cursor up one page", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(0, -25) }))
|
||||||
|
cm.Define("page-left", "Moves the cursor left one page", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(-15, 0) }))
|
||||||
|
cm.Define("page-right", "Moves the cursor right one page", "", gridNavOperation(func(grid *ui.Grid) { grid.MoveBy(15, 0) }))
|
||||||
|
|
||||||
|
cm.Define("row-top", "Moves the cursor to the top of the row", "", gridNavOperation(func(grid *ui.Grid) {
|
||||||
|
cellX, _ := grid.CellPosition()
|
||||||
|
grid.MoveTo(cellX, 0)
|
||||||
|
}))
|
||||||
|
cm.Define("row-bottom", "Moves the cursor to the bottom of the row", "", gridNavOperation(func(grid *ui.Grid) {
|
||||||
|
cellX, _ := grid.CellPosition()
|
||||||
|
_, dimY := grid.Model().Dimensions()
|
||||||
|
grid.MoveTo(cellX, dimY - 1)
|
||||||
|
}))
|
||||||
|
cm.Define("col-left", "Moves the cursor to the left-most column", "", gridNavOperation(func(grid *ui.Grid) {
|
||||||
|
_, cellY := grid.CellPosition()
|
||||||
|
grid.MoveTo(0, cellY)
|
||||||
|
}))
|
||||||
|
|
||||||
|
cm.Define("col-right", "Moves the cursor to the right-most column", "", gridNavOperation(func(grid *ui.Grid) {
|
||||||
|
_, cellY := grid.CellPosition()
|
||||||
|
dimX, _ := grid.Model().Dimensions()
|
||||||
|
grid.MoveTo(dimX - 1, cellY)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers the standard view key bindings. These commands require the frame
|
||||||
|
func (cm *CommandMapping) RegisterViewKeyBindings() {
|
||||||
|
cm.MapKey('i', cm.Command("move-up"))
|
||||||
|
cm.MapKey('k', cm.Command("move-down"))
|
||||||
|
cm.MapKey('j', cm.Command("move-left"))
|
||||||
|
cm.MapKey('l', cm.Command("move-right"))
|
||||||
|
cm.MapKey('I', cm.Command("page-up"))
|
||||||
|
cm.MapKey('K', cm.Command("page-down"))
|
||||||
|
cm.MapKey('J', cm.Command("page-left"))
|
||||||
|
cm.MapKey('L', cm.Command("page-right"))
|
||||||
|
cm.MapKey(ui.KeyCtrlI, cm.Command("row-top"))
|
||||||
|
cm.MapKey(ui.KeyCtrlK, cm.Command("row-bottom"))
|
||||||
|
cm.MapKey(ui.KeyCtrlJ, cm.Command("col-left"))
|
||||||
|
cm.MapKey(ui.KeyCtrlL, cm.Command("col-right"))
|
||||||
|
|
||||||
|
cm.MapKey(ui.KeyArrowUp, cm.Command("move-up"))
|
||||||
|
cm.MapKey(ui.KeyArrowDown, cm.Command("move-down"))
|
||||||
|
cm.MapKey(ui.KeyArrowLeft, cm.Command("move-left"))
|
||||||
|
cm.MapKey(ui.KeyArrowRight, cm.Command("move-right"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A nativation command factory. This will perform the passed in operation with the current grid and
|
||||||
|
// will display the cell value in the message box.
|
||||||
|
func gridNavOperation(op func(grid *ui.Grid)) func(ctx CommandContext) error {
|
||||||
|
return func(ctx CommandContext) error {
|
||||||
|
op(ctx.Frame().Grid())
|
||||||
|
ctx.Frame().ShowCellValue()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
84
frame.go
Normal file
84
frame.go
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"./ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Mode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The grid is selectable
|
||||||
|
GridMode Mode = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// A frame is a UI instance.
|
||||||
|
type Frame struct {
|
||||||
|
Session *Session
|
||||||
|
|
||||||
|
uiManager *ui.Ui
|
||||||
|
clientArea *ui.RelativeLayout
|
||||||
|
grid *ui.Grid
|
||||||
|
messageView *ui.TextView
|
||||||
|
textEntry *ui.TextEntry
|
||||||
|
statusBar *ui.StatusBar
|
||||||
|
textEntrySwitch *ui.ProxyLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the UI and returns a new frame
|
||||||
|
func NewFrame(uiManager *ui.Ui) *Frame {
|
||||||
|
frame := &Frame{
|
||||||
|
uiManager: uiManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.grid = ui.NewGrid(&ui.TestModel{})
|
||||||
|
frame.messageView = &ui.TextView{"Hello"}
|
||||||
|
frame.statusBar = &ui.StatusBar{"Test", "Status"}
|
||||||
|
frame.textEntrySwitch = &ui.ProxyLayout{frame.messageView}
|
||||||
|
frame.textEntry = &ui.TextEntry{}
|
||||||
|
|
||||||
|
// Build the UI frame
|
||||||
|
statusLayout := &ui.VertLinearLayout{}
|
||||||
|
statusLayout.Append(frame.statusBar)
|
||||||
|
statusLayout.Append(frame.textEntrySwitch)
|
||||||
|
|
||||||
|
frame.clientArea = &ui.RelativeLayout{ Client: frame.grid, South: statusLayout }
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the root component of the frame
|
||||||
|
func (frame *Frame) RootComponent() ui.UiComponent {
|
||||||
|
return frame.clientArea
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the grid component
|
||||||
|
func (frame *Frame) Grid() *ui.Grid {
|
||||||
|
return frame.grid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the specific mode.
|
||||||
|
func (frame *Frame) EnterMode(mode Mode) {
|
||||||
|
switch mode {
|
||||||
|
case GridMode:
|
||||||
|
frame.uiManager.SetFocusedComponent(frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show a message. This will switch the bottom to the messageView and select the frame
|
||||||
|
func (frame *Frame) ShowMessage(msg string) {
|
||||||
|
frame.messageView.Text = msg
|
||||||
|
frame.textEntrySwitch.Component = frame.messageView
|
||||||
|
//frame.EnterMode(GridMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows the value of the currently select grid cell
|
||||||
|
func (frame *Frame) ShowCellValue() {
|
||||||
|
displayValue := frame.grid.CurrentCellDisplayValue()
|
||||||
|
frame.ShowMessage(displayValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the main grid input as this is the "component" that handles command input.
|
||||||
|
func (frame *Frame) KeyPressed(key rune, mod int) {
|
||||||
|
if frame.Session != nil {
|
||||||
|
frame.Session.KeyPressed(key, mod)
|
||||||
|
}
|
||||||
|
}
|
||||||
12
main.go
12
main.go
|
|
@ -11,7 +11,14 @@ func main() {
|
||||||
}
|
}
|
||||||
defer uiManager.Close()
|
defer uiManager.Close()
|
||||||
|
|
||||||
|
frame := NewFrame(uiManager)
|
||||||
|
NewSession(frame)
|
||||||
|
|
||||||
|
uiManager.SetRootComponent(frame.RootComponent())
|
||||||
|
frame.EnterMode(GridMode)
|
||||||
|
|
||||||
|
uiManager.Loop()
|
||||||
|
/*
|
||||||
cmdText := &ui.TextEntry{Prompt: "Enter: "}
|
cmdText := &ui.TextEntry{Prompt: "Enter: "}
|
||||||
|
|
||||||
statusLayout := &ui.VertLinearLayout{}
|
statusLayout := &ui.VertLinearLayout{}
|
||||||
|
|
@ -25,10 +32,11 @@ func main() {
|
||||||
clientArea := &ui.RelativeLayout{ Client: grid, South: statusLayout }
|
clientArea := &ui.RelativeLayout{ Client: grid, South: statusLayout }
|
||||||
|
|
||||||
uiManager.SetRootComponent(clientArea)
|
uiManager.SetRootComponent(clientArea)
|
||||||
//uiManager.SetFocusedComponent(grid)
|
uiManager.SetFocusedComponent(grid)
|
||||||
uiManager.SetFocusedComponent(cmdText)
|
//uiManager.SetFocusedComponent(cmdText)
|
||||||
|
|
||||||
uiManager.Loop()
|
uiManager.Loop()
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
uiCtx, _ := NewUI()
|
uiCtx, _ := NewUI()
|
||||||
|
|
||||||
|
|
|
||||||
51
session.go
Normal file
51
session.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "./ui"
|
||||||
|
|
||||||
|
// The session is responsible for managing the UI and the model and handling
|
||||||
|
// the interaction between the two and the user.
|
||||||
|
type Session struct {
|
||||||
|
Frame *Frame
|
||||||
|
Commands *CommandMapping
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSession(frame *Frame) *Session {
|
||||||
|
session := &Session{
|
||||||
|
Frame: frame,
|
||||||
|
Commands: NewCommandMapping(),
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Commands.RegisterViewCommands()
|
||||||
|
session.Commands.RegisterViewKeyBindings()
|
||||||
|
|
||||||
|
// Also assign this session with the frame
|
||||||
|
frame.Session = session
|
||||||
|
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input from the frame
|
||||||
|
func (session *Session) KeyPressed(key rune, mod int) {
|
||||||
|
// Add the mod key modifier
|
||||||
|
if (mod & ui.ModKeyAlt != 0) {
|
||||||
|
key |= ModAlt
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := session.Commands.KeyMapping(key)
|
||||||
|
if cmd != nil {
|
||||||
|
err := cmd.Do(SessionCommandContext{session})
|
||||||
|
if err != nil {
|
||||||
|
session.Frame.ShowMessage(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The command context used by the session
|
||||||
|
type SessionCommandContext struct {
|
||||||
|
Session *Session
|
||||||
|
}
|
||||||
|
|
||||||
|
func (scc SessionCommandContext) Frame() *Frame {
|
||||||
|
return scc.Session.Frame
|
||||||
|
}
|
||||||
109
ui/grid.go
109
ui/grid.go
|
|
@ -13,22 +13,22 @@ type GridModel interface {
|
||||||
/**
|
/**
|
||||||
* Returns the size of the grid model (width x height)
|
* Returns the size of the grid model (width x height)
|
||||||
*/
|
*/
|
||||||
GetDimensions() (int, int)
|
Dimensions() (int, int)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of the particular column. If the size is 0, this indicates that the column is hidden.
|
* Returns the size of the particular column. If the size is 0, this indicates that the column is hidden.
|
||||||
*/
|
*/
|
||||||
GetColWidth(int) int
|
ColWidth(int) int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of the particular row. If the size is 0, this indicates that the row is hidden.
|
* Returns the size of the particular row. If the size is 0, this indicates that the row is hidden.
|
||||||
*/
|
*/
|
||||||
GetRowHeight(int) int
|
RowHeight(int) int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the cell a position X, Y
|
* Returns the value of the cell a position X, Y
|
||||||
*/
|
*/
|
||||||
GetCellValue(int, int) string
|
CellValue(int, int) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -38,7 +38,8 @@ type gridPoint int
|
||||||
* The grid component.
|
* The grid component.
|
||||||
*/
|
*/
|
||||||
type Grid struct {
|
type Grid struct {
|
||||||
model GridModel
|
model GridModel // The grid model
|
||||||
|
|
||||||
viewCellX int // Left most cell
|
viewCellX int // Left most cell
|
||||||
viewCellY int // Top most cell
|
viewCellY int // Top most cell
|
||||||
selCellX int // The currently selected cell
|
selCellX int // The currently selected cell
|
||||||
|
|
@ -72,11 +73,9 @@ func NewGrid(model GridModel) *Grid {
|
||||||
return &Grid{model, 0, 0, 0, 0, -1, -1}
|
return &Grid{model, 0, 0, 0, 0, -1, -1}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Returns the model
|
||||||
* Returns the requested dimensions of a grid (as required by UiComponent)
|
func (grid *Grid) Model() GridModel {
|
||||||
*/
|
return grid.model
|
||||||
func (grid *Grid) Remeasure(w, h int) (int, int) {
|
|
||||||
return w, h
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -88,12 +87,45 @@ func (grid *Grid) ShiftBy(x int, y int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Moves the currently selected cell by a delta.
|
// Returns the display value of the currently selected cell.
|
||||||
|
func (grid *Grid) CurrentCellDisplayValue() string {
|
||||||
|
if grid.isCellValid(grid.selCellX, grid.selCellY) {
|
||||||
|
return grid.model.CellValue(grid.selCellX, grid.selCellY)
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moves the currently selected cell by a delta. This will be implemented as single stepped
|
||||||
|
// moveTo calls to handle invalid cells.
|
||||||
func (grid *Grid) MoveBy(x int, y int) {
|
func (grid *Grid) MoveBy(x int, y int) {
|
||||||
grid.selCellX += x
|
grid.MoveTo(grid.selCellX + x, grid.selCellY + y)
|
||||||
grid.selCellY += y
|
}
|
||||||
|
|
||||||
|
// Moves the currently selected cell to a specific row. The row must be valid, otherwise the
|
||||||
|
// currently selected cell will not be changed. Returns true if the move was successful
|
||||||
|
func (grid *Grid) MoveTo(newX, newY int) {
|
||||||
|
maxX, maxY := grid.model.Dimensions()
|
||||||
|
newX = intMinMax(newX, 0, maxX - 1)
|
||||||
|
newY = intMinMax(newY, 0, maxY - 1)
|
||||||
|
|
||||||
|
if grid.isCellValid(newX, newY) {
|
||||||
|
grid.selCellX = newX
|
||||||
|
grid.selCellY = newY
|
||||||
grid.reposition()
|
grid.reposition()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the currently selected cell position.
|
||||||
|
func (grid *Grid) CellPosition() (int, int) {
|
||||||
|
return grid.selCellX, grid.selCellY
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the user can enter the specific cell
|
||||||
|
func (grid *Grid) isCellValid(x int, y int) bool {
|
||||||
|
maxX, maxY := grid.model.Dimensions()
|
||||||
|
return (x >= 0) && (y >= 0) && (x < maxX) && (y < maxY)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Determine the topmost cell based on the location of the currently selected cell
|
// Determine the topmost cell based on the location of the currently selected cell
|
||||||
|
|
@ -118,17 +150,15 @@ func (grid *Grid) reposition() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Gets the cell value and attributes of a particular cell
|
// Gets the cell value and attributes of a particular cell
|
||||||
func (grid *Grid) getCellData(cellX, cellY int) (text string, fg, bg Attribute) {
|
func (grid *Grid) getCellData(cellX, cellY int) (text string, fg, bg Attribute) {
|
||||||
// The fixed cells
|
// The fixed cells
|
||||||
modelCellX := cellX - 1 + grid.viewCellX
|
modelCellX := cellX - 1 + grid.viewCellX
|
||||||
modelCellY := cellY - 1 + grid.viewCellY
|
modelCellY := cellY - 1 + grid.viewCellY
|
||||||
modelMaxX, modelMaxY := grid.model.GetDimensions()
|
modelMaxX, modelMaxY := grid.model.Dimensions()
|
||||||
|
|
||||||
if (cellX == 0) && (cellY == 0) {
|
if (cellX == 0) && (cellY == 0) {
|
||||||
return strconv.Itoa(grid.cellsWide), AttrBold, AttrBold
|
return "", AttrBold, AttrBold
|
||||||
} else if (cellX == 0) {
|
} else if (cellX == 0) {
|
||||||
if (modelCellY == grid.selCellY) {
|
if (modelCellY == grid.selCellY) {
|
||||||
return strconv.Itoa(modelCellY), AttrBold | AttrReverse, AttrBold | AttrReverse
|
return strconv.Itoa(modelCellY), AttrBold | AttrReverse, AttrBold | AttrReverse
|
||||||
|
|
@ -145,18 +175,14 @@ func (grid *Grid) getCellData(cellX, cellY int) (text string, fg, bg Attribute)
|
||||||
// The data from the model
|
// The data from the model
|
||||||
if (modelCellX >= 0) && (modelCellY >= 0) && (modelCellX < modelMaxX) && (modelCellY < modelMaxY) {
|
if (modelCellX >= 0) && (modelCellY >= 0) && (modelCellX < modelMaxX) && (modelCellY < modelMaxY) {
|
||||||
if (modelCellX == grid.selCellX) && (modelCellY == grid.selCellY) {
|
if (modelCellX == grid.selCellX) && (modelCellY == grid.selCellY) {
|
||||||
return grid.model.GetCellValue(modelCellX, modelCellY), AttrReverse, AttrReverse
|
return grid.model.CellValue(modelCellX, modelCellY), AttrReverse, AttrReverse
|
||||||
} else {
|
} else {
|
||||||
return grid.model.GetCellValue(modelCellX, modelCellY), 0, 0
|
return grid.model.CellValue(modelCellX, modelCellY), 0, 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "~", 0, 0
|
return "~", ColorBlue | AttrBold, 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Workaround for bug in compiler
|
|
||||||
panic("Unreachable code")
|
|
||||||
return "", 0, 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the cell dimensions
|
// Gets the cell dimensions
|
||||||
|
|
@ -166,17 +192,17 @@ func (grid *Grid) getCellDimensions(cellX, cellY int) (width, height int) {
|
||||||
|
|
||||||
modelCellX := cellX - 1 + grid.viewCellX
|
modelCellX := cellX - 1 + grid.viewCellX
|
||||||
modelCellY := cellY - 1 + grid.viewCellY
|
modelCellY := cellY - 1 + grid.viewCellY
|
||||||
modelMaxX, modelMaxY := grid.model.GetDimensions()
|
modelMaxX, modelMaxY := grid.model.Dimensions()
|
||||||
|
|
||||||
// Get the cell width & height from model (if within range)
|
// Get the cell width & height from model (if within range)
|
||||||
if (modelCellX >= 0) && (modelCellX < modelMaxX) {
|
if (modelCellX >= 0) && (modelCellX < modelMaxX) {
|
||||||
cellWidth = grid.model.GetColWidth(modelCellX)
|
cellWidth = grid.model.ColWidth(modelCellX)
|
||||||
} else {
|
} else {
|
||||||
cellWidth = 8
|
cellWidth = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelCellY >= 0) && (modelCellY < modelMaxY) {
|
if (modelCellY >= 0) && (modelCellY < modelMaxY) {
|
||||||
cellHeight = grid.model.GetRowHeight(modelCellY)
|
cellHeight = grid.model.RowHeight(modelCellY)
|
||||||
} else {
|
} else {
|
||||||
cellHeight = 2
|
cellHeight = 2
|
||||||
}
|
}
|
||||||
|
|
@ -212,8 +238,6 @@ func (grid *Grid) renderCell(ctx *DrawContext, cellClipRect gridRect, sx int, sy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//termbox.SetCell(int(x - cellClipRect.x1) + sx, int(y - cellClipRect.y1) + sy, currRune, fg, bg)
|
|
||||||
|
|
||||||
// TODO: This might be better if this wasn't so low-level
|
// TODO: This might be better if this wasn't so low-level
|
||||||
ctx.DrawRuneWithAttrs(int(x - cellClipRect.x1) + sx, int(y - cellClipRect.y1) + sy, currRune, fg, bg)
|
ctx.DrawRuneWithAttrs(int(x - cellClipRect.x1) + sx, int(y - cellClipRect.y1) + sy, currRune, fg, bg)
|
||||||
}
|
}
|
||||||
|
|
@ -288,7 +312,7 @@ func (grid *Grid) renderGrid(ctx *DrawContext, screenViewPort gridRect, cellX in
|
||||||
* Returns the cell of the particular point, along with the top-left position of the cell.
|
* Returns the cell of the particular point, along with the top-left position of the cell.
|
||||||
*/
|
*/
|
||||||
func (grid *Grid) pointToCell(x int, y int) (cellX int, cellY int, posX int, posY int) {
|
func (grid *Grid) pointToCell(x int, y int) (cellX int, cellY int, posX int, posY int) {
|
||||||
var wid, hei int = grid.model.GetDimensions()
|
var wid, hei int = grid.model.Dimensions()
|
||||||
posX = 0
|
posX = 0
|
||||||
posY = 0
|
posY = 0
|
||||||
|
|
||||||
|
|
@ -297,7 +321,7 @@ func (grid *Grid) pointToCell(x int, y int) (cellX int, cellY int, posX int, pos
|
||||||
|
|
||||||
// Go through columns to locate the particular cellX
|
// Go through columns to locate the particular cellX
|
||||||
for cx := 0; cx < wid; cx++ {
|
for cx := 0; cx < wid; cx++ {
|
||||||
if (x >= posX) && (x < posX + grid.model.GetColWidth(cx)) {
|
if (x >= posX) && (x < posX + grid.model.ColWidth(cx)) {
|
||||||
// We found the X position
|
// We found the X position
|
||||||
cellX = int(cx)
|
cellX = int(cx)
|
||||||
break
|
break
|
||||||
|
|
@ -305,7 +329,7 @@ func (grid *Grid) pointToCell(x int, y int) (cellX int, cellY int, posX int, pos
|
||||||
}
|
}
|
||||||
|
|
||||||
for cy := 0; cy < hei; cy++ {
|
for cy := 0; cy < hei; cy++ {
|
||||||
if (y >= posY) && (y < posY + grid.model.GetRowHeight(cy)) {
|
if (y >= posY) && (y < posY + grid.model.RowHeight(cy)) {
|
||||||
// And the Y position
|
// And the Y position
|
||||||
cellY = int(cy)
|
cellY = int(cy)
|
||||||
break
|
break
|
||||||
|
|
@ -315,6 +339,13 @@ func (grid *Grid) pointToCell(x int, y int) (cellX int, cellY int, posX int, pos
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the requested dimensions of a grid (as required by UiComponent)
|
||||||
|
*/
|
||||||
|
func (grid *Grid) Remeasure(w, h int) (int, int) {
|
||||||
|
return w, h
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redraws the grid.
|
* Redraws the grid.
|
||||||
*/
|
*/
|
||||||
|
|
@ -323,8 +354,10 @@ func (grid *Grid) Redraw(ctx *DrawContext) {
|
||||||
grid.cellsWide, grid.cellsHigh = grid.renderGrid(ctx, viewportRect, 0, 0, 0, 0)
|
grid.cellsWide, grid.cellsHigh = grid.renderGrid(ctx, viewportRect, 0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the component has focus and a key has been pressed
|
// Called when the component has focus and a key has been pressed.
|
||||||
func (grid *Grid) KeyPressed(key rune) {
|
// This is the default behaviour of the grid, but it is not used by the main grid.
|
||||||
|
func (grid *Grid) KeyPressed(key rune, mod int) {
|
||||||
|
// TODO: Not sure if this would be better handled using commands
|
||||||
if (key == 'i') || (key == KeyArrowUp) {
|
if (key == 'i') || (key == KeyArrowUp) {
|
||||||
grid.MoveBy(0, -1)
|
grid.MoveBy(0, -1)
|
||||||
} else if (key == 'k') || (key == KeyArrowDown) {
|
} else if (key == 'k') || (key == KeyArrowDown) {
|
||||||
|
|
@ -347,27 +380,27 @@ type TestModel struct {
|
||||||
/**
|
/**
|
||||||
* Returns the size of the grid model (width x height)
|
* Returns the size of the grid model (width x height)
|
||||||
*/
|
*/
|
||||||
func (model *TestModel) GetDimensions() (int, int) {
|
func (model *TestModel) Dimensions() (int, int) {
|
||||||
return 100, 100
|
return 100, 100
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of the particular column. If the size is 0, this indicates that the column is hidden.
|
* Returns the size of the particular column. If the size is 0, this indicates that the column is hidden.
|
||||||
*/
|
*/
|
||||||
func (model *TestModel) GetColWidth(int) int {
|
func (model *TestModel) ColWidth(int) int {
|
||||||
return 16
|
return 16
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of the particular row. If the size is 0, this indicates that the row is hidden.
|
* Returns the size of the particular row. If the size is 0, this indicates that the row is hidden.
|
||||||
*/
|
*/
|
||||||
func (model *TestModel) GetRowHeight(int) int {
|
func (model *TestModel) RowHeight(int) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the cell a position X, Y
|
* Returns the value of the cell a position X, Y
|
||||||
*/
|
*/
|
||||||
func (model *TestModel) GetCellValue(x int, y int) string {
|
func (model *TestModel) CellValue(x int, y int) string {
|
||||||
return strconv.Itoa(x) + "," + strconv.Itoa(y)
|
return strconv.Itoa(x) + "," + strconv.Itoa(y)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
ui/layout.go
23
ui/layout.go
|
|
@ -61,6 +61,29 @@ func (vl *VertLinearLayout) Remeasure(w, h int) (int, int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// A layout component which defers calls to the nested component. If no control is defined,
|
||||||
|
// will not draw itself. Used for switching controls dynamically.
|
||||||
|
type ProxyLayout struct {
|
||||||
|
Component UiComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *ProxyLayout) Remeasure(w, h int) (int, int) {
|
||||||
|
if pl.Component != nil {
|
||||||
|
return pl.Component.Remeasure(w, h)
|
||||||
|
} else {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *ProxyLayout) Redraw(context *DrawContext) {
|
||||||
|
if pl.Component != nil {
|
||||||
|
pl.Component.Redraw(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// A relative layout component. This has a "client" component, bordered by a north,
|
// A relative layout component. This has a "client" component, bordered by a north,
|
||||||
// south, east and west component. The N,S,E,W components will be provided with the full dimensions
|
// south, east and west component. The N,S,E,W components will be provided with the full dimensions
|
||||||
// whereas the client component will be provided with the remaining size. Each one of the components
|
// whereas the client component will be provided with the remaining size. Each one of the components
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,30 @@ package ui
|
||||||
|
|
||||||
import "unicode"
|
import "unicode"
|
||||||
|
|
||||||
|
|
||||||
|
// A text component. This simply renders a text string.
|
||||||
|
type TextView struct {
|
||||||
|
|
||||||
|
// The string to render
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum dimensions
|
||||||
|
func (tv *TextView) Remeasure(w, h int) (int, int) {
|
||||||
|
return w, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status bar redraw
|
||||||
|
func (tv *TextView) Redraw(context *DrawContext) {
|
||||||
|
context.SetFgAttr(0)
|
||||||
|
context.SetBgAttr(0)
|
||||||
|
|
||||||
|
context.HorizRule(0, ' ')
|
||||||
|
context.Print(0, 0, tv.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Status bar component. This component displays text on the left and right of it's
|
// Status bar component. This component displays text on the left and right of it's
|
||||||
// allocated space.
|
// allocated space.
|
||||||
type StatusBar struct {
|
type StatusBar struct {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue