Skip to content

Add a data table widget with sorting, filtering, and in-place editing - #517

Merged
matt-edmondson merged 31 commits into
mainfrom
feat/data-table
Sep 29, 2026
Merged

matt-edmondson merged 31 commits into
mainfrom
feat/data-table

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Adds ImGuiWidgets.DataTable, a table of typed rows that sorts and filters itself, moves an active cell with the keyboard, and edits one cell at a time in place. The table never writes to your rows. Each editable column reports the edit through OnEdit, and you apply it, which is what makes undo straightforward.

What it does

  • Typed columns. DataTableColumn<TRow, TValue> reads a value, formats it, compares it, and optionally edits it. Strings, numbers, bool and enums have built-in editors, and any other type supplies an Editor.
  • Sorting and filtering. Header clicks cycle ascending, descending, and source order. A filter box under each header matches fuzzily by default, and its right-click menu switches to glob or regex. RowFilter adds a condition of your own.
  • Keyboard and editing. Arrows, Page Up and Down, Home, End, Tab, and Shift to extend the selection. F2, Enter, double-click, or typing begins an edit. Enter commits and moves down, and Escape cancels.
  • Selection. Click, Ctrl+click and Shift+click select rows as shown. Ctrl+C copies them as tab-separated text. SelectRows, ClearSelection and ActivateCell set the selection from code.
  • Context menu. OnContextMenu draws caller-supplied items for the right-clicked cell and the selection.
  • Keymap. Commands follow a ktsu.Keybinding keymap when one is passed. DataTableCommands.Register registers them.
  • Virtualized. Only the rows on screen are drawn, so tens of thousands of rows cost the same per frame as a screenful.

An edited row stays where it is until you call Refresh(), so it doesn't jump away while someone is working on it.

How it's built

DataTableState<TRow> holds the view, the active cell, the selection, and the one edit session, and makes no ImGui calls, so its rules are unit tests. The renderer draws a frame from it and turns input into its commands. The clipper setup is now shared with VirtualTable through TableClipping, with no change to VirtualTable's behavior.

The key chord matcher moved from ImGui.NodeEditor into ImGui.Widgets so both widgets share it. It gained optional key repeat and an option to ignore Shift, and now maps Tab, which it had silently dropped. The node editor's behavior is otherwise unchanged.

The widgets demo gains a Data Table section with undo and redo through ktsu.UndoRedo. The design and plan are in docs/superpowers/specs/2026-09-29-data-table-design.md and docs/superpowers/plans/2026-09-29-data-table.md.

Things to look at

  • ImGui.Widgets now references ktsu.Keybinding, which brings Microsoft.Extensions.DependencyInjection.Abstractions to every consumer, plus System.Text.Json on net8.0 and net9.0.
  • ImGui.Widgets grants InternalsVisibleTo to ImGui.NodeEditor and the UI tests, and sets PolyUseEmbeddedAttribute so Polyfill's generated extension methods don't become ambiguous across the two assemblies.
  • The Ctrl+C UI test reaches the OS clipboard, because ImGui.App installs no clipboard functions. It saves and restores the clipboard around the test.
  • Known follow-ups, not in this PR: mouse-triggered commits still run while rows are drawn, the active cell can stay in a column the user hides, and VirtualTable has its own row height mismatch when RowHeight is set.

Testing

The solution builds with 0 warnings. Widget unit tests 458 passed, widget UI tests 423 passed, node editor tests 266 passed, and widgets demo UI tests 30 passed. The UI tests drive the table headlessly, including sorting, filtering, editing, Tab while editing, typing into a filter, keypad Enter, a keymap, and scrolling the last row fully into view.

🤖 Generated with Claude Code

Comment thread tests/ImGui.Widgets.Tests/DataTableStateNavigationTests.cs Fixed
Comment thread ImGui.Widgets/DataTableEditors.cs Fixed
Keeps both the Waveform section from main and the data table section from this
branch. The extra section class pushed ImGuiWidgetsDemo over the CA1506
coupling limit, so the extracted sections now go through SectionDemos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DpVgY5FC9FuBkBuBAzXZJ6
matt-edmondson and others added 4 commits September 29, 2026 12:30
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCL6GX9wshgqiBeq6z98ZH
…lity findings

The Ctrl+C test failed on Linux, where Dear ImGui keeps its own clipboard and an empty one reads back as a null pointer. GetClipboardTextS throws on that null, so the test now reads the pointer directly and treats null as empty.

The enum editor draws the clicked name before checking it, which makes plain that every name is drawn and only a different one is a change. The navigation test that removes rows checks the active cell with a pattern, so the null case fails the test instead of being dereferenced.

Also removes the "Review focus:" label from 13 test comments. It referred to the review process, not the code, and the explanations after it are unchanged.
SectionDemos drew the data table among the basic widgets as well as ShowAdvancedDemos drawing it, so the section appeared in two tabs with the same ImGui ids. It stays with the advanced demos, where the demo UI suite expects it.
Main's widget gallery checks that every public ImGuiWidgets member has a
tile, and it failed on this branch for DataTable and its column and state
types. The DataTable tile covers them, and DataTableCommands is listed as
keymap registration rather than a widget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCL6GX9wshgqiBeq6z98ZH
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 994bd1b into main Sep 29, 2026
16 checks passed
@matt-edmondson
matt-edmondson deleted the feat/data-table branch September 29, 2026 21:08
matt-edmondson pushed a commit that referenced this pull request Sep 29, 2026
Keep both CatalogCoverage entries: the piano keyboard's layout helper and
the data table's command registration from #517.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv8Si9qNfudF41QNYfL2YB
matt-edmondson pushed a commit that referenced this pull request Sep 29, 2026
Keep the zoomable Waveform description and add the DataTable entry that
#517 introduced beside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv8Si9qNfudF41QNYfL2YB
matt-edmondson pushed a commit that referenced this pull request Sep 29, 2026
Main's data table (#517) added its demo to the old ShowAdvancedDemos list
and SectionDemos.ResetState. This branch replaces both with the
DemoSections registry, so the data table becomes one registry line in the
Advanced Demos list, after the virtual table, where main shows it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv8Si9qNfudF41QNYfL2YB
matt-edmondson pushed a commit that referenced this pull request Sep 29, 2026
…els branch

Main's data table (#517) sits after the virtual table in the Advanced Demos
order, and the levels control keeps its place after the image canvas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv8Si9qNfudF41QNYfL2YB
matt-edmondson pushed a commit that referenced this pull request Sep 29, 2026
…et browser branch

Main's data table (#517) sits after the virtual table in the Advanced Demos
order, and the asset browser keeps its place after the image canvas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv8Si9qNfudF41QNYfL2YB
matt-edmondson pushed a commit that referenced this pull request Sep 29, 2026
Brings in the data table (#517) and the float-comparison fixes (#527).
No conflicts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PndoA4WHoRsyywzECYDNX3
matt-edmondson pushed a commit that referenced this pull request Sep 29, 2026
Brings in the data table (#517) and the float-comparison fixes (#527).
No conflicts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PndoA4WHoRsyywzECYDNX3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants