User can now select table cells

This commit is contained in:
Sasha Koshka 2023-04-03 22:22:29 -04:00
parent ebefcb03b3
commit b357768c36
2 changed files with 98 additions and 26 deletions

View File

@ -2,6 +2,7 @@ package containers
import "image" import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/elements/core" import "git.tebibyte.media/sashakoshka/tomo/elements/core"
@ -10,7 +11,7 @@ import "git.tebibyte.media/sashakoshka/tomo/default/config"
type tableCell struct { type tableCell struct {
tomo.Element tomo.Element
artist.Pattern tomo.Pattern
image.Rectangle image.Rectangle
} }
@ -35,9 +36,13 @@ type TableContainer struct {
forcedMinimumWidth int forcedMinimumWidth int
forcedMinimumHeight int forcedMinimumHeight int
selectedColumn int
selectedRow int
config config.Wrapped config config.Wrapped
theme theme.Wrapped theme theme.Wrapped
onSelect func ()
onScrollBoundsChange func () onScrollBoundsChange func ()
} }
@ -50,6 +55,8 @@ func NewTableContainer (
element = &TableContainer { element = &TableContainer {
topHeading: topHeading, topHeading: topHeading,
leftHeading: leftHeading, leftHeading: leftHeading,
selectedColumn: -1,
selectedRow: -1,
} }
element.theme.Case = tomo.C("tomo", "tableContainer") element.theme.Case = tomo.C("tomo", "tableContainer")
@ -153,6 +160,17 @@ func (element *TableContainer) Resize (columns, rows int) {
element.redoAll() element.redoAll()
} }
// Selected returns the column and row of the cell that is currently selected.
// If no cell is selected, this method will return (-1, -1).
func (element *TableContainer) Selected () (column, row int) {
return element.selectedColumn, element.selectedRow
}
// OnSelect sets a function to be called when the user selects a table cell.
func (element *TableContainer) OnSelect (callback func ()) {
element.onSelect = callback
}
// Warp runs the specified callback, deferring all layout and rendering updates // Warp runs the specified callback, deferring all layout and rendering updates
// until the callback has finished executing. This allows for aplications to // until the callback has finished executing. This allows for aplications to
// perform batch gui updates without flickering and stuff. // perform batch gui updates without flickering and stuff.
@ -212,14 +230,42 @@ func (element *TableContainer) NotifyMinimumSizeChange (child tomo.Element) {
func (element *TableContainer) DrawBackground (bounds image.Rectangle) { func (element *TableContainer) DrawBackground (bounds image.Rectangle) {
if !bounds.Overlaps(element.core.Bounds()) { return } if !bounds.Overlaps(element.core.Bounds()) { return }
for _, row := range element.grid { for rowIndex, row := range element.grid {
for _, child := range row { for columnIndex, child := range row {
if bounds.Overlaps(child.Rectangle) { if bounds.Overlaps(child.Rectangle) {
child.Draw(canvas.Cut(element.core, bounds), child.Rectangle) element.theme.Pattern (
child.Pattern,
element.state(columnIndex, rowIndex)).
Draw(canvas.Cut(element.core, bounds), child.Rectangle)
return return
}}} }}}
} }
func (element *TableContainer) HandleMouseDown (x, y int, button input.Button) {
element.Propagator.HandleMouseDown(x, y, button)
if button != input.ButtonLeft { return }
for rowIndex, row := range element.grid {
for columnIndex, child := range row {
if image.Pt(x, y).In(child.Rectangle) {
selected :=
rowIndex == element.selectedRow &&
columnIndex == element.selectedColumn
if selected { return }
oldColumn, oldRow := element.selectedColumn, element.selectedRow
element.selectedColumn, element.selectedRow = columnIndex, rowIndex
if oldColumn >= 0 && oldRow >= 0 {
element.core.DamageRegion(element.redoCell(oldColumn, oldRow))
}
element.core.DamageRegion(element.redoCell(columnIndex, rowIndex))
if element.onSelect != nil {
element.onSelect()
}
return
}}}
}
func (element *TableContainer) hook (child tomo.Element) { func (element *TableContainer) hook (child tomo.Element) {
if child0, ok := child.(tomo.Themeable); ok { if child0, ok := child.(tomo.Themeable); ok {
child0.SetTheme(element.theme.Theme) child0.SetTheme(element.theme.Theme)
@ -245,6 +291,36 @@ func (element *TableContainer) rebuildChildList (list []tomo.Element) {
}} }}
} }
func (element *TableContainer) state (column, row int) (state tomo.State) {
if column == element.selectedColumn && row == element.selectedRow {
state.On = true
}
return
}
func (element *TableContainer) redoCell (column, row int) image.Rectangle {
padding := element.theme.Padding(tomo.PatternTableCell)
cell := element.grid[row][column]
pattern := element.theme.Pattern (
cell.Pattern, element.state(column, row))
if cell.Element != nil {
// give child canvas portion
innerCellBounds := padding.Apply(cell.Rectangle)
artist.DrawShatter (
element.core, pattern,
cell.Rectangle, innerCellBounds)
cell.DrawTo (
canvas.Cut(element.core, innerCellBounds),
innerCellBounds,
element.childDrawCallback)
} else {
// draw cell pattern in empty cells
pattern.Draw(element.core, cell.Rectangle)
}
return cell.Rectangle
}
func (element *TableContainer) redoAll () { func (element *TableContainer) redoAll () {
if element.warping || !element.core.HasImage() { if element.warping || !element.core.HasImage() {
element.updateMinimumSize() element.updateMinimumSize()
@ -294,7 +370,7 @@ func (element *TableContainer) redoAll () {
x := float64(bounds.Min.X) x := float64(bounds.Min.X)
y := float64(bounds.Min.Y) y := float64(bounds.Min.Y)
for rowIndex, row := range element.grid { for rowIndex, row := range element.grid {
for columnIndex, child := range row { for columnIndex, _ := range row {
width := columnWidths[columnIndex] width := columnWidths[columnIndex]
height := rowHeights[rowIndex] height := rowHeights[rowIndex]
cellBounds := image.Rect ( cellBounds := image.Rect (
@ -310,26 +386,10 @@ func (element *TableContainer) redoAll () {
} else { } else {
id = tomo.PatternTableCell id = tomo.PatternTableCell
} }
pattern := element.theme.Pattern(id, tomo.State { })
element.grid[rowIndex][columnIndex].Rectangle = cellBounds element.grid[rowIndex][columnIndex].Rectangle = cellBounds
element.grid[rowIndex][columnIndex].Pattern = pattern element.grid[rowIndex][columnIndex].Pattern = id
if child.Element != nil { element.redoCell(columnIndex, rowIndex)
// give child canvas portion
innerCellBounds := padding.Apply(cellBounds)
artist.DrawShatter (
element.core, pattern,
cellBounds, innerCellBounds)
child.DrawTo (
canvas.Cut(element.core, innerCellBounds),
innerCellBounds,
func (region image.Rectangle) {
element.core.DamageRegion(region)
})
} else {
// draw cell pattern in empty cells
pattern.Draw(element.core, cellBounds)
}
x += float64(width) x += float64(width)
} }
@ -371,3 +431,7 @@ func (element *TableContainer) updateMinimumSize () {
element.core.SetMinimumSize(minWidth, minHeight) element.core.SetMinimumSize(minWidth, minHeight)
} }
func (element *TableContainer) childDrawCallback (region image.Rectangle) {
element.core.DamageRegion(region)
}

View File

@ -29,10 +29,18 @@ func run () {
} }
index ++ index ++
}} }}
table.Set(2, 1, elements.NewButton("Look, I'm a button!")) table.Set(2, 1, elements.NewButton("Look, I'm a button!"))
statusLabel := elements.NewLabel("Selected: none", false)
table.OnSelect (func () {
column, row := table.Selected()
statusLabel.SetText (
fmt.Sprintf("Selected: %d, %d",
column, row))
})
container.Adopt(table, true) container.Adopt(table, true)
container.Adopt(statusLabel, false)
window.Adopt(container) window.Adopt(container)
window.OnClose(tomo.Stop) window.OnClose(tomo.Stop)