diff --git a/elements/containers/table.go b/elements/containers/table.go
index 449763b..8e0b2fe 100644
--- a/elements/containers/table.go
+++ b/elements/containers/table.go
@@ -2,6 +2,7 @@ package containers
 
 import "image"
 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/artist"
 import "git.tebibyte.media/sashakoshka/tomo/elements/core"
@@ -10,7 +11,7 @@ import "git.tebibyte.media/sashakoshka/tomo/default/config"
 
 type tableCell struct {
 	tomo.Element
-	artist.Pattern
+	tomo.Pattern
 	image.Rectangle
 }
 
@@ -34,10 +35,14 @@ type TableContainer struct {
 	contentBounds image.Rectangle
 	forcedMinimumWidth  int
 	forcedMinimumHeight int
+
+	selectedColumn int
+	selectedRow int
 	
 	config config.Wrapped
 	theme  theme.Wrapped
 
+	onSelect func ()
 	onScrollBoundsChange func ()
 }
 
@@ -48,8 +53,10 @@ func NewTableContainer (
 	element *TableContainer,
 ) {
 	element = &TableContainer {
-		topHeading:  topHeading,
-		leftHeading: leftHeading,
+		topHeading:     topHeading,
+		leftHeading:    leftHeading,
+		selectedColumn: -1,
+		selectedRow:    -1,
 	}
 	
 	element.theme.Case = tomo.C("tomo", "tableContainer")
@@ -153,6 +160,17 @@ func (element *TableContainer) Resize (columns, rows int) {
 	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
 // until the callback has finished executing. This allows for aplications to
 // 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) {
 	if !bounds.Overlaps(element.core.Bounds()) { return }
 	
-	for _, row   := range element.grid {
-	for _, child := range row {
+	for rowIndex,    row   := range element.grid {
+	for columnIndex, child := range row {
 	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
 	}}}
 }
 
+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) {
 	if child0, ok := child.(tomo.Themeable); ok {
 		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 () {
 	if element.warping || !element.core.HasImage() {
 		element.updateMinimumSize()
@@ -294,7 +370,7 @@ func (element *TableContainer) redoAll () {
 	x := float64(bounds.Min.X)
 	y := float64(bounds.Min.Y)
 	for rowIndex, row := range element.grid {
-		for columnIndex, child := range row {
+		for columnIndex, _ := range row {
 			width  := columnWidths[columnIndex]
 			height := rowHeights[rowIndex]
 			cellBounds := image.Rect (
@@ -310,26 +386,10 @@ func (element *TableContainer) redoAll () {
 			} else {
 				id = tomo.PatternTableCell
 			}
-			pattern := element.theme.Pattern(id, tomo.State { })
 			element.grid[rowIndex][columnIndex].Rectangle = cellBounds
-			element.grid[rowIndex][columnIndex].Pattern = pattern
+			element.grid[rowIndex][columnIndex].Pattern   = id
 			
-			if child.Element != nil {
-				// 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)
-			}
+			element.redoCell(columnIndex, rowIndex)
 			x += float64(width)
 		}
 		
@@ -371,3 +431,7 @@ func (element *TableContainer) updateMinimumSize () {
 
 	element.core.SetMinimumSize(minWidth, minHeight)
 }
+
+func (element *TableContainer) childDrawCallback (region image.Rectangle) {
+	element.core.DamageRegion(region)
+}
diff --git a/examples/table/main.go b/examples/table/main.go
index 796f10e..b058bbe 100644
--- a/examples/table/main.go
+++ b/examples/table/main.go
@@ -29,10 +29,18 @@ func run () {
 		}
 		index ++
 	}}
-
 	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(statusLabel, false)
 	window.Adopt(container)
 	
 	window.OnClose(tomo.Stop)