Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
fscarponi committed May 5, 2023
1 parent fe55906 commit 385a8bc
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 140 deletions.
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
package org.jetbrains.jewel.foundation.lazy

import org.jetbrains.jewel.foundation.lazy.SelectableKey.Selectable
import org.jetbrains.jewel.foundation.tree.SelectableColumnOnKeyEvent
import kotlin.math.max
import kotlin.math.min

open class DefaultSelectableOnKeyEvent(
override val keybindings: SelectableColumnKeybindings,
private val selectableState: FocusableLazyListState
private val selectableState: SelectableLazyListState
) : SelectableColumnOnKeyEvent {

override suspend fun onSelectFirstItem() {
val firstSelectable = selectableState.keys.indexOfFirst { it is Selectable }
val firstSelectable = selectableState.keys.indexOfFirst { it.selectable }
if (firstSelectable >= 0) selectableState.selectSingleItem(firstSelectable)
}

override suspend fun onExtendSelectionToFirst(currentIndex: Int) {
if (selectableState.keys.isNotEmpty()) {
buildList {
for (i in currentIndex downTo 0) {
if (selectableState.keys[i] is Selectable) add(i)
if (selectableState.keys[i].selectable) add(i)
}
}.let {
selectableState.addElementsToSelection(it, it.last())
Expand All @@ -28,7 +27,7 @@ open class DefaultSelectableOnKeyEvent(
}

override suspend fun onSelectLastItem() {
val lastSelectable = selectableState.keys.indexOfLast { it is Selectable }
val lastSelectable = selectableState.keys.indexOfLast { it.selectable }
if (lastSelectable >= 0) selectableState.selectSingleItem(lastSelectable)
}

Expand All @@ -37,7 +36,7 @@ open class DefaultSelectableOnKeyEvent(
val lastKey = selectableState.keys.lastIndex
buildList {
for (i in currentIndex..lastKey) {
if (selectableState.keys[i] is Selectable) add(element = i)
if (selectableState.keys[i].selectable) add(element = i)
}
}.let {
selectableState.addElementsToSelection(it)
Expand All @@ -48,7 +47,7 @@ open class DefaultSelectableOnKeyEvent(
override suspend fun onSelectPreviousItem(currentIndex: Int) {
if (currentIndex - 1 >= 0) {
for (i in currentIndex - 1 downTo 0) {
if (selectableState.keys[i] is Selectable) {
if (selectableState.keys[i].selectable) {
selectableState.selectSingleItem(i)
break
}
Expand Down Expand Up @@ -112,7 +111,7 @@ open class DefaultSelectableOnKeyEvent(
override suspend fun onScrollPageUpAndSelectItem(currentIndex: Int) {
val visibleSize = selectableState.layoutInfo.visibleItemsInfo.size
val targetIndex = max(currentIndex - visibleSize, 0)
if (selectableState.keys[targetIndex] !is Selectable) {
if (!selectableState.keys[targetIndex].selectable) {
selectableState.indexOfPreviousSelectable(currentIndex) ?: selectableState.indexOfNextSelectable(currentIndex)?.let {
selectableState.selectSingleItem(it)
}
Expand All @@ -125,15 +124,15 @@ open class DefaultSelectableOnKeyEvent(
val visibleSize = selectableState.layoutInfo.visibleItemsInfo.size
val targetIndex = max(currentIndex - visibleSize, 0)
val indexList =
selectableState.keys.subList(targetIndex, currentIndex).withIndex().filter { it.value is Selectable }.map { it.index }.toList()
selectableState.keys.subList(targetIndex, currentIndex).withIndex().filter { it.value.selectable }.map { it.index }.toList()
selectableState.toggleElementsToSelection(indexList)
}

override suspend fun onScrollPageDownAndSelectItem(currentIndex: Int) {
val firstVisible = selectableState.firstVisibleItemIndex
val visibleSize = selectableState.layoutInfo.visibleItemsInfo.size
val targetIndex = min(firstVisible + visibleSize, selectableState.keys.lastIndex)
if (selectableState.keys[targetIndex] !is Selectable) {
if (!selectableState.keys[targetIndex].selectable) {
selectableState.indexOfNextSelectable(currentIndex) ?: selectableState.indexOfPreviousSelectable(currentIndex)?.let {
selectableState.selectSingleItem(it)
}
Expand All @@ -147,7 +146,7 @@ open class DefaultSelectableOnKeyEvent(
val visibleSize = selectableState.layoutInfo.visibleItemsInfo.size
val targetIndex = min(firstVisible + visibleSize, selectableState.keys.lastIndex)
val indexList =
selectableState.keys.subList(currentIndex, targetIndex).withIndex().filter { it.value is Selectable }.map { it.index }.toList()
selectableState.keys.subList(currentIndex, targetIndex).withIndex().filter { it.value.selectable }.map { it.index }.toList()
selectableState.toggleElementsToSelection(indexList)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
package org.jetbrains.jewel.foundation.lazy

import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.Composable
Expand All @@ -20,54 +14,21 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import org.jetbrains.jewel.foundation.lazy.FocusableLazyListScopeContainer.Entry
import org.jetbrains.jewel.foundation.lazy.FocusableLazyListState.LastFocusedKeyContainer
import org.jetbrains.jewel.foundation.lazy.SelectableLazyListScopeContainer.Entry
import org.jetbrains.jewel.foundation.lazy.SelectableLazyListState.LastFocusedKeyContainer
import org.jetbrains.jewel.foundation.utils.Log

@Composable
fun FocusableLazyColumn(
modifier: Modifier = Modifier,
state: FocusableLazyListState = rememberFocusableLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
onKeyPressed: KeyEvent.(Int) -> Boolean = { _ -> false },
content: FocusableLazyListScope.() -> Unit
) {
val entries = remember(content) { FocusableLazyListScopeContainer().apply(content).entries.toList() }
// BaseFocusableLazyColumn( todo should become LazyColumn
// modifier,
// state,
// contentPadding,
// reverseLayout,
// verticalArrangement,
// horizontalAlignment,
// flingBehavior,
// interactionSource,
// onKeyPressed,
// entries
// )
}


fun <T> FocusableLazyListScope.items(
fun <T> SelectableLazyListScope.items(
items: List<T>,
key: ((item: T) -> Any),
contentType: (item: T) -> Any? = { null },
focusable: (item: T) -> Boolean = { true },
itemContent: @Composable FocusableLazyListScope.(item: T) -> Unit
itemContent: @Composable SelectableLazyListScope.(item: T) -> Unit
) = items(
count = items.size,
key = { key(items[it]) },
Expand All @@ -78,12 +39,12 @@ fun <T> FocusableLazyListScope.items(
}

@Suppress("unused")
fun <T> FocusableLazyListScope.itemsIndexed(
fun <T> SelectableLazyListScope.itemsIndexed(
items: List<T>,
key: ((item: T) -> Any),
contentType: (item: T) -> Any? = { null },
focusable: (item: T) -> Boolean = { true },
itemContent: @Composable FocusableLazyItemScope.(item: T, index: Int) -> Unit
itemContent: @Composable SelectableLazyItemScope.(item: T, index: Int) -> Unit
) = repeat(items.size) { index ->
item(
key = key.invoke(items[index]),
Expand All @@ -94,10 +55,9 @@ fun <T> FocusableLazyListScope.itemsIndexed(
}
}


private fun LazyListScope.items(
entry: Entry.Items,
state: FocusableLazyListState
state: SelectableLazyListState
) {
val lastKey = state.lastFocusedKeyState.value
val requesters = Array(entry.count) { if (entry.focusable(it)) FocusRequester() else null }
Expand All @@ -112,17 +72,17 @@ private fun LazyListScope.items(
count = entry.count,
key = {
if (entry.focusable(it)) {
FocusableKey.Focusable(requesters[it]!!, entry.key?.invoke(it))
SelectableKey.Focusable(requesters[it]!!, entry.key?.invoke(it), entry.selectable.invoke(it))
} else {
FocusableKey.NotFocusable(entry.key?.invoke(it))
SelectableKey.NotFocusable(entry.key?.invoke(it), entry.selectable.invoke(it))
}
},
contentType = entry.contentType
) { itemIndex ->
val itemFinalIndex = entry.innerIndex + itemIndex
var hasFocus by remember { mutableStateOf(false) }
if (entry.focusable(itemIndex)) {
BoxWithConstraints(
Box(
Modifier
.focusRequester(requesters[itemIndex]!!)
.onFocusChanged {
Expand All @@ -145,10 +105,10 @@ private fun LazyListScope.items(
}
}
) {
entry.itemContent(FocusableLazyItemScope(), itemIndex)
entry.itemContent(SelectableLazyItemScope(), itemIndex)
}
} else {
entry.itemContent(FocusableLazyItemScope(), itemIndex)
entry.itemContent(SelectableLazyItemScope(), itemIndex)
}
if (state.lastFocusedIndex == itemFinalIndex) {
LaunchedEffect(hasFocus) { if (!hasFocus) requesters[itemIndex]!!.requestFocus() }
Expand All @@ -158,7 +118,7 @@ private fun LazyListScope.items(

private fun LazyListScope.singleElement(
entry: Entry.Single,
state: FocusableLazyListState
state: SelectableLazyListState
) {
if (entry.focusable) {
val lastKey = state.lastFocusedKeyState.value
Expand Down Expand Up @@ -188,23 +148,23 @@ private fun LazyListScope.singleElement(
}
) {
val isFocused by remember { derivedStateOf(structuralEqualityPolicy()) { state.lastFocusedIndex == entry.innerIndex } }
entry.content(FocusableLazyItemScope(isFocused))
entry.content(SelectableLazyItemScope(isFocused))
}
if (state.lastFocusedIndex == entry.innerIndex) SideEffect { fr.requestFocus() }
}
if (entry is Entry.Single.Item) {
item(FocusableKey.Focusable(fr, entry.key), entry.contentType, newContent)
item(SelectableKey.Focusable(fr, entry.key, entry.selectable), entry.contentType, newContent)
} else {
stickyHeader(FocusableKey.Focusable(fr, entry.key), entry.contentType, newContent)
stickyHeader(SelectableKey.Focusable(fr, entry.key, entry.selectable), entry.contentType, newContent)
}
} else {
if (entry is Entry.Single.Item) {
item(FocusableKey.NotFocusable(entry.key), entry.contentType) {
entry.content(FocusableLazyItemScope())
item(SelectableKey.NotFocusable(entry.key, entry.selectable), entry.contentType) {
entry.content(SelectableLazyItemScope())
}
} else {
stickyHeader(FocusableKey.NotFocusable(entry.key), entry.contentType) {
entry.content(FocusableLazyItemScope())
stickyHeader(SelectableKey.NotFocusable(entry.key, entry.selectable), entry.contentType) {
entry.content(SelectableLazyItemScope())
}
}
}
Expand Down
Loading

0 comments on commit 385a8bc

Please sign in to comment.