Skip to content

[JEWEL-938] Split SelectableLazyColumn into Single/Multi APIs and add initial selected keys support#3450

Open
nebojsa-vuksic wants to merge 1 commit intoJetBrains:masterfrom
nebojsa-vuksic:nebojsa.vuksic/JEWEL-938-Support-item-preselection-in-SelectableLazyColumn-without-triggering-onSelectedIndexesChange-callback
Open

[JEWEL-938] Split SelectableLazyColumn into Single/Multi APIs and add initial selected keys support#3450
nebojsa-vuksic wants to merge 1 commit intoJetBrains:masterfrom
nebojsa-vuksic:nebojsa.vuksic/JEWEL-938-Support-item-preselection-in-SelectableLazyColumn-without-triggering-onSelectedIndexesChange-callback

Conversation

@nebojsa-vuksic
Copy link
Collaborator

@nebojsa-vuksic nebojsa-vuksic commented Mar 6, 2026

Summary

This PR splits Jewel SelectableLazyColumn into explicit single-selection and multi-selection APIs (SingleSelectionLazyColumn, MultiSelectionLazyColumn) and adds support for initial selected keys during state creation, while preserving legacy API behavior and
compatibility.

Dependency

This PR depends on #3446 and should be merged only after that PR lands. Until then, the PR stays in draft mode.

Changes

  • Added SingleSelectionLazyColumn and MultiSelectionLazyColumn as new composable entry points.
  • Added dedicated typed states: SingleSelectionLazyListState and MultiSelectionLazyListState.
  • Added typed remember APIs:
    • rememberSingleSelectionLazyListState(initialSelectedKey = ...)
    • rememberMultiSelectionLazyListState(initialSelectedKeys = ...)
  • Extended SelectableLazyListState with selectionMode and initialSelectedKeys metadata used in legacy compatibility scenarios.
  • Kept legacy SelectableLazyColumn/rememberSelectableLazyListState APIs for source and binary compatibility and marked migration path with deprecations.
  • Deprecated 2-arg rememberSelectableLazyListState(firstVisibleItemIndex, firstVisibleItemScrollOffset) and directed users to the 4-arg overload.
  • Preserved legacy contract: if both are provided and mismatch, explicit selectionMode parameter wins over state.selectionMode.
  • Added mismatch warnings for legacy selection mode/state inconsistencies, with no runtime exceptions.
  • Kept first composition callback behavior stable: initial preselected keys do not trigger onSelectedIndexesChange.
  • Added tests for typed APIs and legacy compatibility edge cases.

Edge cases and behavior details

  • Legacy mismatch: selectionMode = SelectionMode.Single with legacy state containing multiple selected keys.
    • Warning is logged.
    • Existing initial selection is shown as-is until interaction.
    • Interaction follows explicit selectionMode = SelectionMode.Single.
  • Legacy mismatch: selectionMode = SelectionMode.None with legacy state already containing selected keys.
    • Warning is logged.
    • Existing keys are preserved initially.
    • New selection through interaction is suppressed by SelectionMode.None semantics.
  • Legacy remember normalization:
    • SelectionMode.None drops all initial keys.
    • SelectionMode.Single keeps first key from list order.
    • SelectionMode.Multiple keeps all keys, deduplicated in insertion order.
  • Multi-selection keyboard edge case:
    • Non-contiguous selection (e.g., 1 and 3), then Shift+Down, correctly adds next item while preserving existing picks.

Why new APIs and why keep old ones

  • New APIs provide explicit single/multi selection entry points and prevent passing arbitrary modes through the new surface.
  • Legacy APIs are retained to avoid source/binary breakage for existing consumers and to enable gradual migration.
  • Deprecations guide users toward the new APIs without forcing immediate breakage.

Release notes

New features

  • [JEWEL-938] Added SingleSelectionLazyColumn and MultiSelectionLazyColumn with dedicated typed state and remember APIs.
  • [JEWEL-938] Added initial selection support for new/legacy state creation (initialSelectedKey/initialSelectedKeys).

Bug fixes

  • [JEWEL-938] Fixed multi-selection edge-case behavior for non-contiguous selection extension with keyboard range actions.

Deprecated API

  • [JEWEL-938] Deprecated legacy SelectableLazyColumn entry points in favor of explicit single/multi APIs while preserving
    compatibility overloads.
  • [JEWEL-938] Deprecated legacy 2-arg rememberSelectableLazyListState(...); use the 4-arg overload.

f:org.jetbrains.jewel.foundation.lazy.SelectableLazyListStateKt
- sf:getVisibleItemsRange(androidx.compose.foundation.lazy.LazyListState):kotlin.ranges.IntRange
- sf:getVisibleItemsRange(org.jetbrains.jewel.foundation.lazy.SelectableLazyListState):kotlin.ranges.IntRange
- sf:rememberSelectableLazyListState(I,I,androidx.compose.runtime.Composer,I,I):org.jetbrains.jewel.foundation.lazy.SelectableLazyListState
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The rememberSelectableLazyListState 2-args function is depricated

fun rememberSelectableLazyListState(
    initialFirstVisibleItemIndex: Int = 0,
    initialFirstVisibleItemScrollOffset: Int = 0,
)

in favor of rememberSelectableLazyListState 4-args function:

public fun rememberSingleSelectionLazyListState(
    initialFirstVisibleItemIndex: Int = 0,
    initialFirstVisibleItemScrollOffset: Int = 0,
    initialSelectedKey: Any? = null,
)

@nebojsa-vuksic nebojsa-vuksic changed the title Nebojsa.vuksic/jewel 938 support item preselection in selectable lazy column without triggering on selected indexes change callback [JEWEL-938] Split SelectableLazyColumn into Single/Multi APIs and add initial selected keys support Yesterday 10:43 Mar 6, 2026
@nebojsa-vuksic nebojsa-vuksic changed the title [JEWEL-938] Split SelectableLazyColumn into Single/Multi APIs and add initial selected keys support Yesterday 10:43 [JEWEL-938] Split SelectableLazyColumn into Single/Multi APIs and add initial selected keys support Mar 6, 2026
@nebojsa-vuksic nebojsa-vuksic changed the title [JEWEL-938] Split SelectableLazyColumn into Single/Multi APIs and add initial selected keys support [JEWEL-938] Split SelectableLazyColumn into Single/Multi APIs and add initial selected keys support Mar 6, 2026
@nebojsa-vuksic nebojsa-vuksic self-assigned this Mar 6, 2026
@nebojsa-vuksic nebojsa-vuksic force-pushed the nebojsa.vuksic/JEWEL-938-Support-item-preselection-in-SelectableLazyColumn-without-triggering-onSelectedIndexesChange-callback branch 4 times, most recently from ef805e1 to fb0e75d Compare March 10, 2026 12:11
… initial selected keys support

- Split SelectableLazyColumn into new typed entry points `SingleSelectionLazyColumn` and `MultiSelectionLazyColumn`, each bound to the correct selection mode through dedicated state types.
- Add support for initializing legacy and new states with preselected keys (`initialSelectedKeys` / `initialSelectedKey`) without triggering `onSelectedIndexesChange` on first composition.
- Extend `SelectableLazyListState` with `selectionMode` and `initialSelectedKeys` metadata used for legacy compatibility paths and
  mismatch diagnostics.
- Preserve source and binary compatibility by keeping legacy `SelectableLazyColumn` and `rememberSelectableLazyListState` overloads, with migration-focused deprecations.
- Preserve legacy precedence contract where the explicit ` selectionMode ` parameter wins over `state.selectionMode` and emits warnings on mismatches instead of throwing or mutating state during composition.
- Add tests for typed states, typed columns, remember overload normalization, legacy mismatch scenarios, and multi-selection keyboard edge cases.

Signed-off-by: Nebojsa.Vuksic <nebojsa.vuksic@jetbrains.com>
@nebojsa-vuksic nebojsa-vuksic force-pushed the nebojsa.vuksic/JEWEL-938-Support-item-preselection-in-SelectableLazyColumn-without-triggering-onSelectedIndexesChange-callback branch from fb0e75d to 1133634 Compare March 13, 2026 10:57
@nebojsa-vuksic nebojsa-vuksic marked this pull request as ready for review March 13, 2026 10:57
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.

1 participant