Browse Source

Split pane. Refactoring (#408)

pull/417/head
Igor Demin 3 years ago committed by GitHub
parent
commit
1b634255e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      components/.gitignore
  2. 6
      components/SplitPane/common/build.gradle.kts
  3. 6
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/ExperimentalSplitPaneApi.kt
  4. 3
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPane.kt
  5. 15
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPaneDSL.kt
  6. 23
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPaneState.kt
  7. 2
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/Splitter.kt
  8. 17
      components/SplitPane/common/src/desktopMain/kotlin/org/jetbrains/compose/splitpane/DesktopSplitPane.kt
  9. 6
      components/SplitPane/common/src/desktopMain/kotlin/org/jetbrains/compose/splitpane/DesktopSplitter.kt
  10. 5
      components/SplitPane/desktop/build.gradle.kts
  11. 24
      components/SplitPane/desktop/src/jvmMain/kotlin/org/jetbrains/compose/splitpane/demo/Main.kt

15
components/.gitignore vendored

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
build/
/captures
.externalNativeBuild
.cxx

6
components/SplitPane/common/build.gradle.kts

@ -1,4 +1,5 @@
import org.jetbrains.compose.compose
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("multiplatform")
@ -18,4 +19,9 @@ kotlin {
}
named("desktopMain") {}
}
}
// TODO it seems that argument isn't applied to the common sourceSet. Figure out why
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}

6
components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/ExperimentalSplitPaneApi.kt

@ -0,0 +1,6 @@
package org.jetbrains.compose.splitpane
@MustBeDocumented
@Retention(value = AnnotationRetention.BINARY)
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
annotation class ExperimentalSplitPaneApi

3
components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPane.kt

@ -20,6 +20,7 @@ internal data class MinimalSizes(
* @param content a block which describes the content. Inside this block you can use methods like
* [SplitPaneScope.first], [SplitPaneScope.second], to describe parts of split pane.
*/
@ExperimentalSplitPaneApi
@Composable
fun VerticalSplitPane(
modifier: Modifier = Modifier,
@ -54,6 +55,7 @@ fun VerticalSplitPane(
* @param content a block which describes the content. Inside this block you can use methods like
* [SplitPaneScope.first], [SplitPaneScope.second], to describe parts of split pane.
*/
@ExperimentalSplitPaneApi
@Composable
fun HorizontalSplitPane(
modifier: Modifier = Modifier,
@ -85,6 +87,7 @@ fun HorizontalSplitPane(
* @param isHorizontal describes is it horizontal or vertical split pane
* @param splitPaneState the state object to be used to control or observe the split pane state
*/
@OptIn(ExperimentalSplitPaneApi::class)
internal expect fun defaultSplitter(
isHorizontal: Boolean,
splitPaneState: SplitPaneState

15
components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPaneDSL.kt

@ -11,6 +11,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/** Receiver scope which is used by [HorizontalSplitPane] and [VerticalSplitPane] */
@ExperimentalSplitPaneApi
interface SplitPaneScope {
/**
@ -44,12 +45,14 @@ interface SplitPaneScope {
}
/** Receiver scope which is used by [SplitterScope] */
@ExperimentalSplitPaneApi
interface HandleScope {
/** allow mark composable as movable handle */
fun Modifier.markAsHandle(): Modifier
}
/** Receiver scope which is used by [SplitPaneScope] */
@ExperimentalSplitPaneApi
interface SplitterScope {
/**
* Set up visible part of splitter. This part will be measured and placed between split pane
@ -65,7 +68,7 @@ interface SplitterScope {
*
* @param alignment alignment of handle according to [visiblePart] could be:
* * [SplitterHandleAlign.BEFORE] if you place handle before [visiblePart],
* * [SplitterHandleAlign.ABOVE] if you place handle above [visiblePart] (will be centred)
* * [SplitterHandleAlign.ABOVE] if you place handle above [visiblePart] (will be centered)
* * and [SplitterHandleAlign.AFTER] if you place handle after [visiblePart].
*
* @param content composable item content provider. Uses [HandleScope] to allow mark any provided composable part
@ -74,10 +77,11 @@ interface SplitterScope {
*/
fun handle(
alignment: SplitterHandleAlign = SplitterHandleAlign.ABOVE,
content: HandleScope.() -> @Composable () -> Unit
content: @Composable HandleScope.() -> Unit
)
}
@OptIn(ExperimentalSplitPaneApi::class)
internal class HandleScopeImpl(
private val containerScope: SplitPaneScopeImpl
) : HandleScope {
@ -91,6 +95,7 @@ internal class HandleScopeImpl(
}
}
@OptIn(ExperimentalSplitPaneApi::class)
internal class SplitterScopeImpl(
private val containerScope: SplitPaneScopeImpl
) : SplitterScope {
@ -101,15 +106,16 @@ internal class SplitterScopeImpl(
override fun handle(
alignment: SplitterHandleAlign,
content: HandleScope.() -> @Composable () -> Unit
content: @Composable HandleScope.() -> Unit
) {
containerScope.handle = HandleScopeImpl(containerScope).content()
containerScope.handle = { HandleScopeImpl(containerScope).content() }
containerScope.alignment = alignment
}
}
private typealias ComposableSlot = @Composable () -> Unit
@OptIn(ExperimentalSplitPaneApi::class)
internal class SplitPaneScopeImpl(
internal val isHorizontal: Boolean,
internal val splitPaneState: SplitPaneState
@ -168,6 +174,7 @@ internal class SplitPaneScopeImpl(
* @param moveEnabled the initial value for [SplitPaneState.moveEnabled]
* @param interactionState the initial value for [SplitPaneState.interactionState]
* */
@ExperimentalSplitPaneApi
@Composable
fun rememberSplitPaneState(
initialPositionPercentage: Float = 0f,

23
components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPaneState.kt

@ -2,31 +2,22 @@ package org.jetbrains.compose.splitpane
import androidx.compose.foundation.Interaction
import androidx.compose.foundation.InteractionState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.runtime.setValue
@ExperimentalSplitPaneApi
class SplitPaneState(
initialPositionPercentage: Float,
moveEnabled: Boolean,
private val interactionState: InteractionState
) {
private var _moveEnabled = mutableStateOf(moveEnabled, structuralEqualityPolicy())
var moveEnabled: Boolean
get() = _moveEnabled.value
set(newValue) {
_moveEnabled.value = newValue
}
var moveEnabled by mutableStateOf(moveEnabled)
internal set
private val _positionPercentage = mutableStateOf(initialPositionPercentage, structuralEqualityPolicy())
var positionPercentage: Float
get() = _positionPercentage.value
internal set(newPosition) {
_positionPercentage.value = newPosition
}
var positionPercentage by mutableStateOf(initialPositionPercentage)
internal set
internal var minPosition: Float = 0f

2
components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/Splitter.kt

@ -2,12 +2,14 @@ package org.jetbrains.compose.splitpane
import androidx.compose.runtime.Composable
@ExperimentalSplitPaneApi
enum class SplitterHandleAlign {
BEFORE,
ABOVE,
AFTER
}
@OptIn(ExperimentalSplitPaneApi::class)
internal data class Splitter(
val measuredPart: @Composable () -> Unit,
val handlePart: @Composable () -> Unit = measuredPart,

17
components/SplitPane/common/src/desktopMain/kotlin/org/jetbrains/compose/splitpane/DesktopSplitPane.kt

@ -11,6 +11,7 @@ private fun Constraints.maxByDirection(isHorizontal: Boolean): Int = if (isHoriz
private fun Constraints.minByDirection(isHorizontal: Boolean): Int = if (isHorizontal) minWidth else minHeight
private fun Placeable.valueByDirection(isHorizontal: Boolean): Int = if (isHorizontal) width else height
@OptIn(ExperimentalSplitPaneApi::class)
@Composable
internal actual fun SplitPane(
modifier: Modifier,
@ -50,10 +51,7 @@ internal actual fun SplitPane(
if (maxPosition != constrainedMax) {
maxPosition =
if ((firstPlaceableMinimalSize + secondPlaceableMinimalSize).value < constraints.maxByDirection(
isHorizontal
)
) {
if ((firstPlaceableMinimalSize + secondPlaceableMinimalSize).value < constraints.maxByDirection(isHorizontal)) {
constrainedMax
} else {
minPosition
@ -85,7 +83,8 @@ internal actual fun SplitPane(
)
val splitterPlaceable = measurables[1].measure(constraints)
val secondPlaceablePosition = constrainedPosition + splitterPlaceable.valueByDirection(isHorizontal)
val splitterSize = splitterPlaceable.valueByDirection(isHorizontal)
val secondPlaceablePosition = constrainedPosition + splitterSize
val secondPlaceableSize =
(constraints.maxByDirection(isHorizontal) - secondPlaceablePosition).coerceIn(
@ -112,10 +111,12 @@ internal actual fun SplitPane(
)
val handlePlaceable = measurables[3].measure(constraints)
val handleSize = handlePlaceable.valueByDirection(isHorizontal)
// TODO support RTL
val handlePosition = when (splitter.align) {
SplitterHandleAlign.BEFORE -> constrainedPosition - handlePlaceable.valueByDirection(isHorizontal)
SplitterHandleAlign.ABOVE -> constrainedPosition - (handlePlaceable.valueByDirection(isHorizontal) / 2)
SplitterHandleAlign.AFTER -> constrainedPosition + handlePlaceable.valueByDirection(isHorizontal)
SplitterHandleAlign.BEFORE -> constrainedPosition - handleSize
SplitterHandleAlign.ABOVE -> constrainedPosition + (splitterSize - handleSize) / 2
SplitterHandleAlign.AFTER -> constrainedPosition + splitterSize + handleSize
}
layout(constraints.maxWidth, constraints.maxHeight) {

6
components/SplitPane/common/src/desktopMain/kotlin/org/jetbrains/compose/splitpane/DesktopSplitter.kt

@ -59,8 +59,9 @@ private fun DesktopSplitPaneSeparator(
.background(color)
)
@OptIn(ExperimentalSplitPaneApi::class)
@Composable
private fun DesctopHandle(
private fun DesktopHandle(
isHorizontal: Boolean,
splitPaneState: SplitPaneState
) = Box(
@ -85,6 +86,7 @@ private fun DesctopHandle(
}
)
@OptIn(ExperimentalSplitPaneApi::class)
internal actual fun defaultSplitter(
isHorizontal: Boolean,
splitPaneState: SplitPaneState
@ -93,7 +95,7 @@ internal actual fun defaultSplitter(
DesktopSplitPaneSeparator(isHorizontal)
},
handlePart = {
DesctopHandle(isHorizontal, splitPaneState)
DesktopHandle(isHorizontal, splitPaneState)
}
)

5
components/SplitPane/desktop/build.gradle.kts

@ -1,4 +1,5 @@
import org.jetbrains.compose.compose
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("multiplatform")
@ -21,4 +22,8 @@ compose.desktop {
application {
mainClass = "org.jetbrains.compose.splitpane.demo.MainKt"
}
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}

24
components/SplitPane/desktop/src/jvmMain/kotlin/org/jetbrains/compose/splitpane/demo/Main.kt

@ -9,8 +9,6 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -20,9 +18,8 @@ import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.input.pointer.pointerMoveFilter
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
import org.jetbrains.compose.splitpane.HorizontalSplitPane
import org.jetbrains.compose.splitpane.VerticalSplitPane
import org.jetbrains.compose.splitpane.rememberSplitPaneState
@ -44,6 +41,7 @@ private fun Modifier.cursorForHorizontalResize(
)
}
@OptIn(ExperimentalSplitPaneApi::class)
fun main() = Window(
"SplitPane demo"
) {
@ -77,16 +75,14 @@ fun main() = Window(
)
}
handle {
{
Box(
Modifier
.markAsHandle()
.cursorForHorizontalResize()
.background(SolidColor(Color.Gray), alpha = 0.50f)
.width(8.dp)
.fillMaxHeight()
)
}
Box(
Modifier
.markAsHandle()
.cursorForHorizontalResize()
.background(SolidColor(Color.Gray), alpha = 0.50f)
.width(9.dp)
.fillMaxHeight()
)
}
}
}

Loading…
Cancel
Save