Skip to content

Conversation

@faogustavo
Copy link
Collaborator

@faogustavo faogustavo commented Nov 19, 2025

  • Add LazyTable component for large datasets
  • Create OverflowBox utility for smart cell content overflow on hover
  • Provide themed TableViewCell and TableViewHeader components with IntelliJ theme integration
  • Added theme classes based on the IJ table

Note

This PR includes the platform/jewel/docs/lazy-table.md file. It's a base documentation of features and how to implement them to help during review
My plan was to use it mainly to support review, but I can put more effort into it if we want to store it somewhere

Note

Most of the work was imported from another branch/fork that had an implementation of it almost complete.

This PR only addresses a few remaining items and adapts the code to Jewel code-style/standards and update to match proposal from the ticket comments

Original Patch File

Evidences

Case Gif
Simple Matrix Screen Recording 2025-11-19 at 15 15 47
Selection Handling Screen Recording 2025-11-19 at 15 16 06
Movable Columns/Rows Screen Recording 2025-11-19 at 15 16 31
Selection, Moving content, Resizing Content Screen Recording 2025-11-19 at 15 17 14

Release notes

New features

  • Added LazyTable
    • We've added a new experimental component (LazyTable) to render table content in a lazy format
    • This component is still experimental and it's evolving, so changes on the API may happen
    • Supported features:
      • Lazy data loading
      • Bi-directional scroll support
      • Fixed/Pinned Columns/Rows
      • Draggable Columns/Rows
      • Resizable Columns/Rows
      • Selection management
  • Added cell components to be used alongside with LazyTable to customize the Look-and-feel
    • Automatically hooks to IJ theme using the LaF bridge
    • Pre-defined theme for stand-alone
    • Automatic support for Selection, Resize and Drag
    • Components:
      • TableViewCell
      • TableViewHeaderCell
      • SimpleTextTableViewCell
      • SimpleTextTableViewHeaderCell

Note

Introduce experimental LazyTable with 2D lazy layout, pinned rows/cols, selection, drag-and-drop, resizing, themed table cells/headers, docs, samples, and styling/theme plumbing (incl. OverflowBox and scrollbar adapters).

  • Foundation:
    • Add org.jetbrains.jewel.foundation.lazy.table.LazyTable and full stack: state, measure/layout, animate/scroll, scrollbar adapters, pinned rows/columns, beyond-bounds, and APIs (LazyTableScope, LazyTableState, etc.).
    • Add table selection and dragging subsystems (lazy.table.selectable.*, lazy.table.draggable.*) plus general lazy.selectable.* and lazy.draggable.* utilities.
    • Introduce OverflowBox for hover-based overflow rendering.
  • UI:
    • New themed components: TableViewCell, TableViewHeaderCell, SimpleTextTableViewCell, SimpleTextTableViewHeaderCell.
    • Add TableStyle, TableColors, TableMetrics, wire through JewelTheme/DefaultComponentStyling and expose LocalTableStyle.
    • Scrollbar API: add overloads accepting ScrollbarAdapter providers for table integration.
  • Themes:
    • IntelliJ LaF bridge and Int UI standalone: provide default TableStyle (light/dark) and bridge table colors.
  • Samples:
    • Add Tables showcase (simple, selection, draggable, interactable, all-features), new icons and navigation entry.
  • Docs:
    • Add platform/jewel/docs/lazy-table.md with usage, features, theming, and requirements.

Written by Cursor Bugbot for commit 71a1a1a. This will update automatically on new commits. Configure here.

}

fun getAndMeasureOrNull(column: Int, row: Int): LazyTableMeasuredItem? {
if (column >= columns - 1 || row >= rows - 1) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Off-by-one error in last cell measurement check

The condition column >= columns - 1 || row >= rows - 1 incorrectly rejects the last valid column and row indices. Valid indices range from 0 to columns - 1 and 0 to rows - 1 inclusive, so the check should be column >= columns || row >= rows. Currently, cells at the last column or last row cannot be measured, causing layout failures when those positions contain the first visible cell.

Fix in Cursor Fix in Web

* @param position The 2D position (column, row) of the cell.
* @return The content type of the cell, or null if not specified.
*/
override fun getContentType(position: IntOffset): Any? = withCellAt(position) { index -> key?.invoke(index) }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: getContentType returns key instead of type in position variant

The getContentType(position: IntOffset) method returns key?.invoke(index) but should return type.invoke(index). This is inconsistent with the getContentType(index: Int) method at line 162 which correctly returns type.invoke(cellIndex). Content types should be determined by the type property, not the key property. This will cause content type mismatches for cell composition and reuse optimization.

Fix in Cursor Fix in Web

@m-asadullah
Copy link

m-asadullah commented Nov 21, 2025

Caution

After merging this #3289 then this #3312 cause merge conflict on

platform/jewel/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Scrollbar.kt

@rock3r
Copy link
Collaborator

rock3r commented Nov 21, 2025

That's fine, this PR isn't going to be merged soon. Gustavo will rebase.

- Add LazyTable component for large datasets
- Create OverflowBox utility for smart cell content overflow on hover
- Provide themed TableViewCell and TableViewHeader components with IntelliJ theme integration
- Added theme classes based on IJ table

Co-authored-by: James Rose <[email protected]>
override fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
if (state.layoutInfo.pinnedColumns > index) {
return 0f
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Vertical scroll animation checks wrong pinned field

In LazyTableAnimateVerticalScrollScope.expectedDistanceTo(), the code checks state.layoutInfo.pinnedColumns > index when it should check state.layoutInfo.pinnedRows > index. The vertical scroll scope is checking the wrong pinned dimension, which causes incorrect distance calculations for animated vertical scrolling. The horizontal scroll scope correctly checks pinnedColumns at line 266, but the vertical scope has this backwards.

Fix in Cursor Fix in Web

@morki
Copy link

morki commented Nov 22, 2025

I tried it and it looks very impressive, but one thing is very bad and it is performance. On pretty medium harware on linux it feels very very laggy. 🙁

Anyway thank you for this awesome work 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants