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
} }
@ -34,10 +35,14 @@ type TableContainer struct {
contentBounds image.Rectangle contentBounds image.Rectangle
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 ()
} }
@ -48,8 +53,10 @@ func NewTableContainer (
element *TableContainer, element *TableContainer,
) { ) {
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)