Added the notion of a grid view model.
This commit is contained in:
parent
598d9bd962
commit
a49613f7e9
122
commandmap.go
122
commandmap.go
|
|
@ -110,23 +110,13 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
grid := ctx.Frame().Grid()
|
grid := ctx.Frame().Grid()
|
||||||
_, cellY := grid.CellPosition()
|
_, cellY := grid.CellPosition()
|
||||||
|
|
||||||
if rwModel, isRwModel := ctx.Session().Model.(RWModel); isRwModel {
|
return ctx.ModelVC().DeleteRow(cellY)
|
||||||
DeleteRow(rwModel, cellY)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("model is read-only")
|
|
||||||
})
|
})
|
||||||
cm.Define("delete-col", "Removes the currently selected column", "", func(ctx *CommandContext) error {
|
cm.Define("delete-col", "Removes the currently selected column", "", func(ctx *CommandContext) error {
|
||||||
grid := ctx.Frame().Grid()
|
grid := ctx.Frame().Grid()
|
||||||
cellX, _ := grid.CellPosition()
|
cellX, _ := grid.CellPosition()
|
||||||
|
|
||||||
if rwModel, isRwModel := ctx.Session().Model.(RWModel); isRwModel {
|
return ctx.ModelVC().DeleteCol(cellX)
|
||||||
DeleteCol(rwModel, cellX)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("model is read-only")
|
|
||||||
})
|
})
|
||||||
cm.Define("search", "Search for a cell", "", func(ctx *CommandContext) error {
|
cm.Define("search", "Search for a cell", "", func(ctx *CommandContext) error {
|
||||||
ctx.Frame().Prompt(PromptOptions{ Prompt: "/" }, func(res string) error {
|
ctx.Frame().Prompt(PromptOptions{ Prompt: "/" }, func(res string) error {
|
||||||
|
|
@ -145,7 +135,7 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
ctx.Session().Commands.Eval(ctx, "search")
|
ctx.Session().Commands.Eval(ctx, "search")
|
||||||
}
|
}
|
||||||
|
|
||||||
height, width := ctx.session.Model.Dimensions()
|
height, width := ctx.ModelVC().Model().Dimensions()
|
||||||
startX, startY := ctx.Frame().Grid().CellPosition()
|
startX, startY := ctx.Frame().Grid().CellPosition()
|
||||||
cellX, cellY := startX, startY
|
cellX, cellY := startX, startY
|
||||||
|
|
||||||
|
|
@ -155,7 +145,7 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
cellX = 0
|
cellX = 0
|
||||||
cellY = (cellY + 1) % height
|
cellY = (cellY + 1) % height
|
||||||
}
|
}
|
||||||
if ctx.session.LastSearch.MatchString(ctx.session.Model.CellValue(cellY, cellX)) {
|
if ctx.session.LastSearch.MatchString(ctx.ModelVC().Model().CellValue(cellY, cellX)) {
|
||||||
ctx.Frame().Grid().MoveTo(cellX, cellY)
|
ctx.Frame().Grid().MoveTo(cellX, cellY)
|
||||||
return nil
|
return nil
|
||||||
} else if (cellX == startX) && (cellY == startY) {
|
} else if (cellX == startX) && (cellY == startY) {
|
||||||
|
|
@ -168,30 +158,22 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
grid := ctx.Frame().Grid()
|
grid := ctx.Frame().Grid()
|
||||||
cellX, _ := grid.CellPosition()
|
cellX, _ := grid.CellPosition()
|
||||||
|
|
||||||
if rwModel, isRwModel := ctx.Session().Model.(RWModel); isRwModel {
|
height, width := ctx.ModelVC().Model().Dimensions()
|
||||||
height, width := rwModel.Dimensions()
|
if cellX == width-1 {
|
||||||
if cellX == width-1 {
|
return ctx.ModelVC().Resize(height, width+1)
|
||||||
rwModel.Resize(height, width+1)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return errors.New("model is read-only")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
cm.Define("open-down", "Inserts a row below the curser", "", func(ctx *CommandContext) error {
|
cm.Define("open-down", "Inserts a row below the curser", "", func(ctx *CommandContext) error {
|
||||||
grid := ctx.Frame().Grid()
|
grid := ctx.Frame().Grid()
|
||||||
_, cellY := grid.CellPosition()
|
_, cellY := grid.CellPosition()
|
||||||
|
|
||||||
if rwModel, isRwModel := ctx.Session().Model.(RWModel); isRwModel {
|
height, width := ctx.ModelVC().Model().Dimensions()
|
||||||
height, width := rwModel.Dimensions()
|
if cellY == height-1 {
|
||||||
if cellY == height-1 {
|
return ctx.ModelVC().Resize(height+1, width)
|
||||||
rwModel.Resize(height+1, width)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return errors.New("model is read-only")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
cm.Define("append", "Inserts a row below the curser", "", func(ctx *CommandContext) error {
|
cm.Define("append", "Inserts a row below the curser", "", func(ctx *CommandContext) error {
|
||||||
|
|
@ -207,6 +189,64 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
return ctx.Session().Commands.Eval(ctx, "set-cell")
|
return ctx.Session().Commands.Eval(ctx, "set-cell")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
cm.Define("inc-col-width", "Increase the width of the current column", "", func(ctx *CommandContext) error {
|
||||||
|
cellX, _ := ctx.Frame().Grid().CellPosition()
|
||||||
|
|
||||||
|
attrs := ctx.ModelVC().ColAttrs(cellX)
|
||||||
|
attrs.Size += 2
|
||||||
|
ctx.ModelVC().SetColAttrs(cellX, attrs)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cm.Define("dec-col-width", "Decrease the width of the current column", "", func(ctx *CommandContext) error {
|
||||||
|
cellX, _ := ctx.Frame().Grid().CellPosition()
|
||||||
|
|
||||||
|
attrs := ctx.ModelVC().ColAttrs(cellX)
|
||||||
|
attrs.Size -= 2
|
||||||
|
if attrs.Size < 4 {
|
||||||
|
attrs.Size = 4
|
||||||
|
}
|
||||||
|
ctx.ModelVC().SetColAttrs(cellX, attrs)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cm.Define("clear-row-marker", "Clears any row markers", "", func(ctx *CommandContext) error {
|
||||||
|
_, cellY := ctx.Frame().Grid().CellPosition()
|
||||||
|
|
||||||
|
attrs := ctx.ModelVC().RowAttrs(cellY)
|
||||||
|
attrs.Marker = MarkerNone
|
||||||
|
ctx.ModelVC().SetRowAttrs(cellY, attrs)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cm.Define("mark-row-red", "Set row marker to red", "", func(ctx *CommandContext) error {
|
||||||
|
_, cellY := ctx.Frame().Grid().CellPosition()
|
||||||
|
|
||||||
|
attrs := ctx.ModelVC().RowAttrs(cellY)
|
||||||
|
attrs.Marker = MarkerRed
|
||||||
|
ctx.ModelVC().SetRowAttrs(cellY, attrs)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cm.Define("mark-row-green", "Set row marker to green", "", func(ctx *CommandContext) error {
|
||||||
|
_, cellY := ctx.Frame().Grid().CellPosition()
|
||||||
|
|
||||||
|
attrs := ctx.ModelVC().RowAttrs(cellY)
|
||||||
|
attrs.Marker = MarkerGreen
|
||||||
|
ctx.ModelVC().SetRowAttrs(cellY, attrs)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cm.Define("mark-row-blue", "Set row marker to blue", "", func(ctx *CommandContext) error {
|
||||||
|
_, cellY := ctx.Frame().Grid().CellPosition()
|
||||||
|
|
||||||
|
attrs := ctx.ModelVC().RowAttrs(cellY)
|
||||||
|
attrs.Marker = MarkerBlue
|
||||||
|
ctx.ModelVC().SetRowAttrs(cellY, attrs)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
cm.Define("enter-command", "Enter command", "", func(ctx *CommandContext) error {
|
cm.Define("enter-command", "Enter command", "", func(ctx *CommandContext) error {
|
||||||
ctx.Frame().Prompt(PromptOptions{ Prompt: ":" }, func(res string) error {
|
ctx.Frame().Prompt(PromptOptions{ Prompt: ":" }, func(res string) error {
|
||||||
return cm.Eval(ctx, res)
|
return cm.Eval(ctx, res)
|
||||||
|
|
@ -217,10 +257,9 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
cm.Define("replace-cell", "Replace the value of the selected cell", "", func(ctx *CommandContext) error {
|
cm.Define("replace-cell", "Replace the value of the selected cell", "", func(ctx *CommandContext) error {
|
||||||
grid := ctx.Frame().Grid()
|
grid := ctx.Frame().Grid()
|
||||||
cellX, cellY := grid.CellPosition()
|
cellX, cellY := grid.CellPosition()
|
||||||
if rwModel, isRwModel := ctx.Session().Model.(RWModel); isRwModel {
|
if _, isRwModel := ctx.ModelVC().Model().(RWModel); isRwModel {
|
||||||
ctx.Frame().Prompt(PromptOptions{ Prompt: "> " }, func(res string) error {
|
ctx.Frame().Prompt(PromptOptions{ Prompt: "> " }, func(res string) error {
|
||||||
rwModel.SetCellValue(cellY, cellX, res)
|
return ctx.ModelVC().SetCellValue(cellY, cellX, res)
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -229,13 +268,12 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
grid := ctx.Frame().Grid()
|
grid := ctx.Frame().Grid()
|
||||||
cellX, cellY := grid.CellPosition()
|
cellX, cellY := grid.CellPosition()
|
||||||
|
|
||||||
if rwModel, isRwModel := ctx.Session().Model.(RWModel); isRwModel {
|
if _, isRwModel := ctx.ModelVC().Model().(RWModel); isRwModel {
|
||||||
ctx.Frame().Prompt(PromptOptions{
|
ctx.Frame().Prompt(PromptOptions{
|
||||||
Prompt: "> ",
|
Prompt: "> ",
|
||||||
InitialValue: grid.Model().CellValue(cellY, cellX),
|
InitialValue: grid.Model().CellValue(cellX, cellY),
|
||||||
}, func(res string) error {
|
}, func(res string) error {
|
||||||
rwModel.SetCellValue(cellY, cellX, res)
|
return ctx.ModelVC().SetCellValue(cellY, cellX, res)
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -247,7 +285,7 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
return fmt.Errorf("model is not writable")
|
return fmt.Errorf("model is not writable")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := wSource.Write(ctx.Session().Model); err != nil {
|
if err := wSource.Write(ctx.ModelVC().Model()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,6 +342,14 @@ func (cm *CommandMapping) RegisterViewKeyBindings() {
|
||||||
cm.MapKey('/', cm.Command("search"))
|
cm.MapKey('/', cm.Command("search"))
|
||||||
cm.MapKey('n', cm.Command("search-next"))
|
cm.MapKey('n', cm.Command("search-next"))
|
||||||
|
|
||||||
|
cm.MapKey('0', cm.Command("clear-row-marker"))
|
||||||
|
cm.MapKey('1', cm.Command("mark-row-red"))
|
||||||
|
cm.MapKey('2', cm.Command("mark-row-green"))
|
||||||
|
cm.MapKey('3', cm.Command("mark-row-blue"))
|
||||||
|
|
||||||
|
cm.MapKey('{', cm.Command("dec-col-width"))
|
||||||
|
cm.MapKey('}', cm.Command("inc-col-width"))
|
||||||
|
|
||||||
cm.MapKey(':', cm.Command("enter-command"))
|
cm.MapKey(':', cm.Command("enter-command"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
21
model.go
21
model.go
|
|
@ -27,25 +27,4 @@ type RWModel interface {
|
||||||
IsDirty() bool
|
IsDirty() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes a row of a model
|
|
||||||
func DeleteRow(model RWModel, row int) {
|
|
||||||
h, w := model.Dimensions()
|
|
||||||
for r := row; r < h-1; r++ {
|
|
||||||
for c := 0; c < w; c++ {
|
|
||||||
model.SetCellValue(r, c, model.CellValue(r+1, c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
model.Resize(h-1, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deletes a column of a model
|
|
||||||
func DeleteCol(model RWModel, col int) {
|
|
||||||
h, w := model.Dimensions()
|
|
||||||
for c := col; c < w-1; c++ {
|
|
||||||
for r := 0; r < h; r++ {
|
|
||||||
model.SetCellValue(r, c, model.CellValue(r, c+1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
model.Resize(h, w-1)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
64
session.go
64
session.go
|
|
@ -8,25 +8,28 @@ import (
|
||||||
// The session is responsible for managing the UI and the model and handling
|
// The session is responsible for managing the UI and the model and handling
|
||||||
// the interaction between the two and the user.
|
// the interaction between the two and the user.
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Model Model
|
model Model
|
||||||
Source ModelSource
|
Source ModelSource
|
||||||
Frame *Frame
|
Frame *Frame
|
||||||
Commands *CommandMapping
|
Commands *CommandMapping
|
||||||
UIManager *ui.Ui
|
UIManager *ui.Ui
|
||||||
|
modelController *ModelViewCtrl
|
||||||
|
|
||||||
LastSearch *regexp.Regexp
|
LastSearch *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSession(uiManager *ui.Ui, frame *Frame, source ModelSource) *Session {
|
func NewSession(uiManager *ui.Ui, frame *Frame, source ModelSource) *Session {
|
||||||
|
model := NewSingleCellStdModel()
|
||||||
session := &Session{
|
session := &Session{
|
||||||
Model: NewSingleCellStdModel(),
|
model: model,
|
||||||
Source: source,
|
Source: source,
|
||||||
Frame: frame,
|
Frame: frame,
|
||||||
Commands: NewCommandMapping(),
|
Commands: NewCommandMapping(),
|
||||||
UIManager: uiManager,
|
UIManager: uiManager,
|
||||||
|
modelController: NewGridViewModel(model),
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.SetModel(&SessionGridModel{session})
|
frame.SetModel(&SessionGridModel{session.modelController})
|
||||||
|
|
||||||
session.Commands.RegisterViewCommands()
|
session.Commands.RegisterViewCommands()
|
||||||
session.Commands.RegisterViewKeyBindings()
|
session.Commands.RegisterViewKeyBindings()
|
||||||
|
|
@ -45,7 +48,8 @@ func (session *Session) LoadFromSource() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
session.Model = newModel
|
session.model = newModel
|
||||||
|
session.modelController.SetModel(newModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input from the frame
|
// Input from the frame
|
||||||
|
|
@ -69,6 +73,10 @@ type CommandContext struct {
|
||||||
session *Session
|
session *Session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (scc *CommandContext) ModelVC() *ModelViewCtrl {
|
||||||
|
return scc.session.modelController
|
||||||
|
}
|
||||||
|
|
||||||
func (scc *CommandContext) Session() *Session {
|
func (scc *CommandContext) Session() *Session {
|
||||||
return scc.session
|
return scc.session
|
||||||
}
|
}
|
||||||
|
|
@ -84,26 +92,44 @@ func (scc *CommandContext) Error(err error) {
|
||||||
|
|
||||||
// Session grid model
|
// Session grid model
|
||||||
type SessionGridModel struct {
|
type SessionGridModel struct {
|
||||||
Session *Session
|
GridViewModel *ModelViewCtrl
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the size of the grid model (width x height)
|
// Returns the size of the grid model (width x height)
|
||||||
func (sgm *SessionGridModel) Dimensions() (int, int) {
|
func (sgm *SessionGridModel) Dimensions() (int, int) {
|
||||||
rs, cs := sgm.Session.Model.Dimensions()
|
rs, cs := sgm.GridViewModel.Model().Dimensions()
|
||||||
return cs, rs
|
return cs, rs
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (sgm *SessionGridModel) ColWidth(int) int {
|
func (sgm *SessionGridModel) ColWidth(col int) int {
|
||||||
return 24
|
return sgm.GridViewModel.ColAttrs(col).Size
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (sgm *SessionGridModel) RowHeight(int) int {
|
func (sgm *SessionGridModel) RowHeight(row int) int {
|
||||||
return 1
|
return sgm.GridViewModel.RowAttrs(row).Size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the value of the cell a position X, Y
|
// Returns the value of the cell a position X, Y
|
||||||
func (sgm *SessionGridModel) CellValue(x int, y int) string {
|
func (sgm *SessionGridModel) CellValue(x int, y int) string {
|
||||||
return sgm.Session.Model.CellValue(y, x)
|
return sgm.GridViewModel.Model().CellValue(y, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sgm *SessionGridModel) CellAttributes(x int, y int) (fg, bg ui.Attribute) {
|
||||||
|
rowAttrs := sgm.GridViewModel.RowAttrs(y)
|
||||||
|
colAttrs := sgm.GridViewModel.ColAttrs(y)
|
||||||
|
|
||||||
|
if rowAttrs.Marker != MarkerNone {
|
||||||
|
return markerAttributes[rowAttrs.Marker], 0
|
||||||
|
} else if colAttrs.Marker != MarkerNone {
|
||||||
|
return markerAttributes[colAttrs.Marker], 0
|
||||||
|
}
|
||||||
|
return 0,0
|
||||||
|
}
|
||||||
|
|
||||||
|
var markerAttributes = map[Marker]ui.Attribute {
|
||||||
|
MarkerRed: ui.ColorRed,
|
||||||
|
MarkerGreen: ui.ColorGreen,
|
||||||
|
MarkerBlue: ui.ColorBlue,
|
||||||
|
}
|
||||||
11
ui/grid.go
11
ui/grid.go
|
|
@ -28,6 +28,8 @@ type GridModel interface {
|
||||||
* Returns the value of the cell a position X, Y
|
* Returns the value of the cell a position X, Y
|
||||||
*/
|
*/
|
||||||
CellValue(int, int) string
|
CellValue(int, int) string
|
||||||
|
|
||||||
|
CellAttributes(int, int) (fg, bg Attribute)
|
||||||
}
|
}
|
||||||
|
|
||||||
type gridPoint int
|
type gridPoint int
|
||||||
|
|
@ -173,10 +175,13 @@ func (grid *Grid) getCellData(cellX, cellY int) (text string, fg, bg Attribute)
|
||||||
} else {
|
} else {
|
||||||
// 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) {
|
||||||
|
value := grid.model.CellValue(modelCellX, modelCellY)
|
||||||
|
fg, bg := grid.model.CellAttributes(modelCellX, modelCellY)
|
||||||
|
|
||||||
if (modelCellX == grid.selCellX) && (modelCellY == grid.selCellY) {
|
if (modelCellX == grid.selCellX) && (modelCellY == grid.selCellY) {
|
||||||
return grid.model.CellValue(modelCellX, modelCellY), AttrReverse, AttrReverse
|
return value, fg | AttrReverse, bg | AttrReverse
|
||||||
} else {
|
} else {
|
||||||
return grid.model.CellValue(modelCellX, modelCellY), 0, 0
|
return value, fg, bg
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "~", ColorBlue, 0
|
return "~", ColorBlue, 0
|
||||||
|
|
@ -365,7 +370,7 @@ func (grid *Grid) KeyPressed(key rune, mod int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------
|
||||||
// Test Model
|
// Test ModelVC
|
||||||
|
|
||||||
type TestModel struct {
|
type TestModel struct {
|
||||||
thing int
|
thing int
|
||||||
|
|
|
||||||
154
viewmodel.go
Normal file
154
viewmodel.go
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ModelViewCtrl struct {
|
||||||
|
model Model
|
||||||
|
rowAttrs []SliceAttr
|
||||||
|
colAttrs []SliceAttr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGridViewModel(model Model) *ModelViewCtrl {
|
||||||
|
gvm := &ModelViewCtrl{}
|
||||||
|
gvm.SetModel(model)
|
||||||
|
return gvm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) Model() Model {
|
||||||
|
return gvm.model
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) SetModel(m Model) {
|
||||||
|
gvm.model = m
|
||||||
|
gvm.modelWasResized()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) RowAttrs(row int) SliceAttr {
|
||||||
|
if row < len(gvm.rowAttrs) {
|
||||||
|
return gvm.rowAttrs[row]
|
||||||
|
}
|
||||||
|
return DefaultRowAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) ColAttrs(col int) SliceAttr {
|
||||||
|
if col < len(gvm.colAttrs) {
|
||||||
|
return gvm.colAttrs[col]
|
||||||
|
}
|
||||||
|
return DefaultColAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) SetRowAttrs(row int, newAttrs SliceAttr) {
|
||||||
|
gvm.rowAttrs[row] = newAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) SetColAttrs(col int, newAttrs SliceAttr) {
|
||||||
|
gvm.colAttrs[col] = newAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) SetCellValue(r, c int, newValue string) error {
|
||||||
|
rwModel, isRWModel := gvm.model.(RWModel)
|
||||||
|
if !isRWModel {
|
||||||
|
return ErrModelReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
rwModel.SetCellValue(r, c, newValue)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) Resize(newRow, newCol int) error {
|
||||||
|
rwModel, isRWModel := gvm.model.(RWModel)
|
||||||
|
if !isRWModel {
|
||||||
|
return ErrModelReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
rwModel.Resize(newRow, newCol)
|
||||||
|
gvm.modelWasResized()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes a row of a model
|
||||||
|
func (gvm *ModelViewCtrl) DeleteRow(row int) error {
|
||||||
|
rwModel, isRWModel := gvm.model.(RWModel)
|
||||||
|
if !isRWModel {
|
||||||
|
return ErrModelReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
h, w := rwModel.Dimensions()
|
||||||
|
for r := row; r < h-1; r++ {
|
||||||
|
for c := 0; c < w; c++ {
|
||||||
|
rwModel.SetCellValue(r, c, rwModel.CellValue(r+1, c))
|
||||||
|
gvm.rowAttrs[r] = gvm.rowAttrs[r+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rwModel.Resize(h-1, w)
|
||||||
|
gvm.modelWasResized()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes a column of a model
|
||||||
|
func (gvm *ModelViewCtrl) DeleteCol(col int) error {
|
||||||
|
rwModel, isRWModel := gvm.model.(RWModel)
|
||||||
|
if !isRWModel {
|
||||||
|
return ErrModelReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
h, w := rwModel.Dimensions()
|
||||||
|
for c := col; c < w-1; c++ {
|
||||||
|
for r := 0; r < h; r++ {
|
||||||
|
rwModel.SetCellValue(r, c, rwModel.CellValue(r, c+1))
|
||||||
|
gvm.colAttrs[c] = gvm.colAttrs[c+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rwModel.Resize(h, w-1)
|
||||||
|
gvm.modelWasResized()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) modelWasResized() {
|
||||||
|
rows, cols := gvm.model.Dimensions()
|
||||||
|
gvm.rowAttrs = gvm.resizeAttrSlice(gvm.rowAttrs, rows, DefaultRowAttrs)
|
||||||
|
gvm.colAttrs = gvm.resizeAttrSlice(gvm.colAttrs, cols, DefaultColAttrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) resizeAttrSlice(oldSlice []SliceAttr, newSize int, defaultAttrs SliceAttr) []SliceAttr {
|
||||||
|
oldLen := len(oldSlice)
|
||||||
|
newSlice := oldSlice
|
||||||
|
|
||||||
|
if newSize > oldLen {
|
||||||
|
newSlice = make([]SliceAttr, newSize)
|
||||||
|
for i := 0; i < newSize; i++ {
|
||||||
|
if i < oldLen {
|
||||||
|
newSlice[i] = oldSlice[i]
|
||||||
|
} else {
|
||||||
|
newSlice[i] = defaultAttrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newSlice = newSlice[:newSize]
|
||||||
|
}
|
||||||
|
return newSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
type SliceAttr struct {
|
||||||
|
Size int
|
||||||
|
Marker Marker
|
||||||
|
}
|
||||||
|
|
||||||
|
type Marker int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MarkerNone Marker = iota
|
||||||
|
MarkerRed
|
||||||
|
MarkerGreen
|
||||||
|
MarkerBlue
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultRowAttrs = SliceAttr{Size: 1}
|
||||||
|
var DefaultColAttrs = SliceAttr{Size: 24}
|
||||||
|
|
||||||
|
var ErrModelReadOnly = errors.New("ModelVC is read-only")
|
||||||
Loading…
Reference in a new issue