Browse Source

Add split pane (#355)

Components. SplitPane
pull/408/head
LAP2 3 years ago committed by GitHub
parent
commit
901406269a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      README.md
  2. 21
      components/SplitPane/build.gradle.kts
  3. 21
      components/SplitPane/common/build.gradle.kts
  4. 113
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPane.kt
  5. 184
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPaneDSL.kt
  6. 45
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/SplitPaneState.kt
  7. 15
      components/SplitPane/common/src/commonMain/kotlin/org/jetbrains/compose/splitpane/Splitter.kt
  8. 140
      components/SplitPane/common/src/desktopMain/kotlin/org/jetbrains/compose/splitpane/DesktopSplitPane.kt
  9. 99
      components/SplitPane/common/src/desktopMain/kotlin/org/jetbrains/compose/splitpane/DesktopSplitter.kt
  10. 24
      components/SplitPane/desktop/build.gradle.kts
  11. 95
      components/SplitPane/desktop/src/jvmMain/kotlin/org/jetbrains/compose/splitpane/demo/Main.kt
  12. 4
      components/build.gradle.kts
  13. 2
      components/settings.gradle.kts

1
README.md

@ -35,6 +35,7 @@ at https://android.googlesource.com/platform/frameworks/support.
* [Navigation](tutorials/Navigation)
* [components](components) - custom components of Compose for Desktop
* [Video Player](components/VideoPlayer)
* [Split Pane](components/SplitPane)
## Getting latest version of Compose for Desktop ##

21
components/SplitPane/build.gradle.kts

@ -0,0 +1,21 @@
import org.jetbrains.compose.compose
plugins {
kotlin("multiplatform")
id("org.jetbrains.compose")
}
kotlin {
jvm {
withJava()
}
sourceSets {
named("jvmMain") {
dependencies {
implementation(compose.desktop.currentOs)
implementation(project("common"))
}
}
}
}

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

@ -0,0 +1,21 @@
import org.jetbrains.compose.compose
plugins {
kotlin("multiplatform")
id("org.jetbrains.compose")
}
kotlin {
jvm("desktop")
sourceSets {
named("commonMain") {
dependencies {
api(compose.runtime)
api(compose.foundation)
api(compose.material)
}
}
named("desktopMain") {}
}
}

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

@ -0,0 +1,113 @@
package org.jetbrains.compose.splitpane
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
internal data class MinimalSizes(
val firstPlaceableMinimalSize: Dp,
val secondPlaceableMinimalSize: Dp
)
/**
* Pane that place it parts **vertically** from top to bottom and allows to change items **heights**.
* The [content] block defines DSL which allow you to configure top ([SplitPaneScope.first]),
* bottom ([SplitPaneScope.second]).
*
* @param modifier the modifier to apply to this layout
* @param splitPaneState the state object to be used to control or observe the split pane state
* @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.
*/
@Composable
fun VerticalSplitPane(
modifier: Modifier = Modifier,
splitPaneState: SplitPaneState = rememberSplitPaneState(),
content: SplitPaneScope.() -> Unit
) {
with(SplitPaneScopeImpl(isHorizontal = false, splitPaneState).apply(content)) {
if (firstPlaceableContent != null && secondPlaceableContent != null) {
SplitPane(
modifier = modifier,
isHorizontal = false,
splitPaneState = splitPaneState,
minimalSizesConfiguration = minimalSizes,
first = firstPlaceableContent!!,
second = secondPlaceableContent!!,
splitter = splitter
)
} else {
firstPlaceableContent?.invoke()
secondPlaceableContent?.invoke()
}
}
}
/**
* Pane that place it parts **horizontally** from left to right and allows to change items **width**.
* The [content] block defines DSL which allow you to configure left ([SplitPaneScope.first]),
* right ([SplitPaneScope.second]) parts of split pane.
*
* @param modifier the modifier to apply to this layout
* @param splitPaneState the state object to be used to control or observe the split pane state
* @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.
*/
@Composable
fun HorizontalSplitPane(
modifier: Modifier = Modifier,
splitPaneState: SplitPaneState = rememberSplitPaneState(),
content: SplitPaneScope.() -> Unit
) {
with(SplitPaneScopeImpl(isHorizontal = true, splitPaneState).apply(content)) {
if (firstPlaceableContent != null && secondPlaceableContent != null) {
SplitPane(
modifier = modifier,
isHorizontal = true,
splitPaneState = splitPaneState,
minimalSizesConfiguration = minimalSizes,
first = firstPlaceableContent!!,
second = secondPlaceableContent!!,
splitter = splitter
)
} else {
firstPlaceableContent?.invoke()
secondPlaceableContent?.invoke()
}
}
}
/**
* Internal implementation of default splitter
*
* @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
*/
internal expect fun defaultSplitter(
isHorizontal: Boolean,
splitPaneState: SplitPaneState
): Splitter
/**
* Internal implementation of split pane that used in all public composable functions
*
* @param modifier the modifier to apply to this layout
* @param isHorizontal describes is it horizontal of vertical split pane
* @param splitPaneState the state object to be used to control or observe the split pane state
* @param minimalSizesConfiguration data class ([MinimalSizes]) that provides minimal size for split pane parts
* @param first first part of split pane, left or top according to [isHorizontal]
* @param second second part of split pane, right or bottom according to [isHorizontal]
* @param splitter separator composable, by default [Splitter] is used
* */
@Composable
internal expect fun SplitPane(
modifier: Modifier = Modifier,
isHorizontal: Boolean = true,
splitPaneState: SplitPaneState,
minimalSizesConfiguration: MinimalSizes = MinimalSizes(0.dp, 0.dp),
first: @Composable () -> Unit,
second: @Composable () -> Unit,
splitter: Splitter
)

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

@ -0,0 +1,184 @@
package org.jetbrains.compose.splitpane
import androidx.compose.foundation.InteractionState
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.consumeAllChanges
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/** Receiver scope which is used by [HorizontalSplitPane] and [VerticalSplitPane] */
interface SplitPaneScope {
/**
* Set up first composable item if SplitPane, for [HorizontalSplitPane] it will be
* Right part, for [VerticalSplitPane] it will be Top part
* @param minSize a minimal size of composable item.
* For [HorizontalSplitPane] it will be minimal width, for [VerticalSplitPane] it wil be minimal Heights.
* In this context minimal mean that this composable item could not be smaller than specified value.
* @param content composable item content.
* */
fun first(
minSize: Dp = 0.dp,
content: @Composable () -> Unit
)
/**
* Set up second composable item if SplitPane.
* For [HorizontalSplitPane] it will be Left part, for [VerticalSplitPane] it will be Bottom part
* @param minSize a minimal size of composable item.
* For [HorizontalSplitPane] it will be minimal width, for [VerticalSplitPane] it wil be minimal Heights.
* In this context minimal mean that this composable item could not be smaller than specified value.
* @param content composable item content.
* */
fun second(
minSize: Dp = 0.dp,
content: @Composable () -> Unit
)
fun splitter(block: SplitterScope.() -> Unit)
}
/** Receiver scope which is used by [SplitterScope] */
interface HandleScope {
/** allow mark composable as movable handle */
fun Modifier.markAsHandle(): Modifier
}
/** Receiver scope which is used by [SplitPaneScope] */
interface SplitterScope {
/**
* Set up visible part of splitter. This part will be measured and placed between split pane
* parts (first and second)
*
* @param content composable item content
* */
fun visiblePart(content: @Composable () -> Unit)
/**
* Set up handle part, this part of splitter would be measured and placed above [visiblePart] content.
* Size of handle will have no effect on split pane parts (first and second) sizes.
*
* @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)
* * and [SplitterHandleAlign.AFTER] if you place handle after [visiblePart].
*
* @param content composable item content provider. Uses [HandleScope] to allow mark any provided composable part
* as handle.
* [content] will be placed only if [SplitPaneState.moveEnabled] is true
*/
fun handle(
alignment: SplitterHandleAlign = SplitterHandleAlign.ABOVE,
content: HandleScope.() -> @Composable () -> Unit
)
}
internal class HandleScopeImpl(
private val containerScope: SplitPaneScopeImpl
) : HandleScope {
override fun Modifier.markAsHandle(): Modifier = this.pointerInput(containerScope.splitPaneState) {
detectDragGestures { change, _ ->
change.consumeAllChanges()
containerScope.splitPaneState.dispatchRawMovement(
if (containerScope.isHorizontal) change.position.x else change.position.y
)
}
}
}
internal class SplitterScopeImpl(
private val containerScope: SplitPaneScopeImpl
) : SplitterScope {
override fun visiblePart(content: @Composable () -> Unit) {
containerScope.visiblePart = content
}
override fun handle(
alignment: SplitterHandleAlign,
content: HandleScope.() -> @Composable () -> Unit
) {
containerScope.handle = HandleScopeImpl(containerScope).content()
containerScope.alignment = alignment
}
}
private typealias ComposableSlot = @Composable () -> Unit
internal class SplitPaneScopeImpl(
internal val isHorizontal: Boolean,
internal val splitPaneState: SplitPaneState
) : SplitPaneScope {
private var firstPlaceableMinimalSize: Dp = 0.dp
private var secondPlaceableMinimalSize: Dp = 0.dp
internal val minimalSizes: MinimalSizes
get() = MinimalSizes(firstPlaceableMinimalSize, secondPlaceableMinimalSize)
internal var firstPlaceableContent: ComposableSlot? = null
private set
internal var secondPlaceableContent: ComposableSlot? = null
private set
internal lateinit var visiblePart: ComposableSlot
internal lateinit var handle: ComposableSlot
internal var alignment: SplitterHandleAlign = SplitterHandleAlign.ABOVE
internal val splitter
get() =
if (this::visiblePart.isInitialized && this::handle.isInitialized) {
Splitter(visiblePart, handle, alignment)
} else {
defaultSplitter(isHorizontal, splitPaneState)
}
override fun first(
minSize: Dp,
content: @Composable () -> Unit
) {
firstPlaceableMinimalSize = minSize
firstPlaceableContent = content
}
override fun second(
minSize: Dp,
content: @Composable () -> Unit
) {
secondPlaceableMinimalSize = minSize
secondPlaceableContent = content
}
override fun splitter(block: SplitterScope.() -> Unit) {
SplitterScopeImpl(this).block()
}
}
/**
* creates a [SplitPaneState] and remembers it across composition
*
* Changes to the provided initial values will **not** result in the state being recreated or
* changed in any way if it has already been created.
*
* @param initialPositionPercentage the initial value for [SplitPaneState.positionPercentage]
* @param moveEnabled the initial value for [SplitPaneState.moveEnabled]
* @param interactionState the initial value for [SplitPaneState.interactionState]
* */
@Composable
fun rememberSplitPaneState(
initialPositionPercentage: Float = 0f,
moveEnabled: Boolean = true,
interactionState: InteractionState = remember { InteractionState() }
): SplitPaneState {
return remember {
SplitPaneState(
moveEnabled = moveEnabled,
initialPositionPercentage = initialPositionPercentage,
interactionState = interactionState
)
}
}

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

@ -0,0 +1,45 @@
package org.jetbrains.compose.splitpane
import androidx.compose.foundation.Interaction
import androidx.compose.foundation.InteractionState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.structuralEqualityPolicy
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
}
private val _positionPercentage = mutableStateOf(initialPositionPercentage, structuralEqualityPolicy())
var positionPercentage: Float
get() = _positionPercentage.value
internal set(newPosition) {
_positionPercentage.value = newPosition
}
internal var minPosition: Float = 0f
internal var maxPosition: Float = Float.POSITIVE_INFINITY
fun dispatchRawMovement(delta: Float) {
interactionState.addInteraction(Interaction.Dragged)
val movableArea = maxPosition - minPosition
if (movableArea > 0) {
positionPercentage =
((movableArea * positionPercentage) + delta).coerceIn(minPosition, maxPosition) / movableArea
}
interactionState.removeInteraction(Interaction.Dragged)
}
}

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

@ -0,0 +1,15 @@
package org.jetbrains.compose.splitpane
import androidx.compose.runtime.Composable
enum class SplitterHandleAlign {
BEFORE,
ABOVE,
AFTER
}
internal data class Splitter(
val measuredPart: @Composable () -> Unit,
val handlePart: @Composable () -> Unit = measuredPart,
val align: SplitterHandleAlign = SplitterHandleAlign.ABOVE
)

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

@ -0,0 +1,140 @@
package org.jetbrains.compose.splitpane
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.unit.Constraints
import kotlin.math.roundToInt
private fun Constraints.maxByDirection(isHorizontal: Boolean): Int = if (isHorizontal) maxWidth else maxHeight
private fun Constraints.minByDirection(isHorizontal: Boolean): Int = if (isHorizontal) minWidth else minHeight
private fun Placeable.valueByDirection(isHorizontal: Boolean): Int = if (isHorizontal) width else height
@Composable
internal actual fun SplitPane(
modifier: Modifier,
isHorizontal: Boolean,
splitPaneState: SplitPaneState,
minimalSizesConfiguration: MinimalSizes,
first: @Composable () -> Unit,
second: @Composable () -> Unit,
splitter: Splitter
) {
Layout(
{
first()
splitter.measuredPart()
second()
splitter.handlePart()
},
modifier,
) { measurables, constraints ->
with(minimalSizesConfiguration) {
with(splitPaneState) {
val constrainedMin = constraints.minByDirection(isHorizontal) + firstPlaceableMinimalSize.value
val constrainedMax =
(constraints.maxByDirection(isHorizontal).toFloat() - secondPlaceableMinimalSize.value).let {
if (it <= 0 || it <= constrainedMin) {
constraints.maxByDirection(isHorizontal).toFloat()
} else {
it
}
}
if (minPosition != constrainedMin) {
maxPosition = constrainedMin
}
if (maxPosition != constrainedMax) {
maxPosition =
if ((firstPlaceableMinimalSize + secondPlaceableMinimalSize).value < constraints.maxByDirection(
isHorizontal
)
) {
constrainedMax
} else {
minPosition
}
}
val constrainedPosition =
(constraints.maxByDirection(isHorizontal) - (firstPlaceableMinimalSize + secondPlaceableMinimalSize).value).let {
if (it > 0f) {
(it * positionPercentage).coerceIn(constrainedMin, constrainedMax).roundToInt()
} else {
constrainedMin.roundToInt()
}
}
val firstPlaceable = measurables[0].measure(
if (isHorizontal) {
constraints.copy(
minWidth = 0,
maxWidth = constrainedPosition
)
} else {
constraints.copy(
minHeight = 0,
maxHeight = constrainedPosition
)
}
)
val splitterPlaceable = measurables[1].measure(constraints)
val secondPlaceablePosition = constrainedPosition + splitterPlaceable.valueByDirection(isHorizontal)
val secondPlaceableSize =
(constraints.maxByDirection(isHorizontal) - secondPlaceablePosition).coerceIn(
0,
if (secondPlaceablePosition < constraints.maxByDirection(isHorizontal)) {
constraints.maxByDirection(isHorizontal) - secondPlaceablePosition
} else {
constraints.maxByDirection(isHorizontal)
}
)
val secondPlaceable = measurables[2].measure(
if (isHorizontal) {
constraints.copy(
minWidth = 0,
maxWidth = secondPlaceableSize
)
} else {
constraints.copy(
minHeight = 0,
maxHeight = secondPlaceableSize
)
}
)
val handlePlaceable = measurables[3].measure(constraints)
val handlePosition = when (splitter.align) {
SplitterHandleAlign.BEFORE -> constrainedPosition - handlePlaceable.valueByDirection(isHorizontal)
SplitterHandleAlign.ABOVE -> constrainedPosition - (handlePlaceable.valueByDirection(isHorizontal) / 2)
SplitterHandleAlign.AFTER -> constrainedPosition + handlePlaceable.valueByDirection(isHorizontal)
}
layout(constraints.maxWidth, constraints.maxHeight) {
firstPlaceable.place(0, 0)
if (isHorizontal) {
secondPlaceable.place(secondPlaceablePosition, 0)
splitterPlaceable.place(constrainedPosition, 0)
if (moveEnabled) {
handlePlaceable.place(handlePosition, 0)
}
} else {
secondPlaceable.place(0, secondPlaceablePosition)
splitterPlaceable.place(0, constrainedPosition)
if (moveEnabled) {
handlePlaceable.place(0, handlePosition)
}
}
}
}
}
}
}

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

@ -0,0 +1,99 @@
package org.jetbrains.compose.splitpane
import androidx.compose.desktop.LocalAppWindow
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.consumeAllChanges
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.pointerMoveFilter
import androidx.compose.ui.unit.dp
import java.awt.Cursor
private fun Modifier.cursorForHorizontalResize(
isHorizontal: Boolean
): Modifier = composed {
var isHover by remember { mutableStateOf(false) }
if (isHover) {
LocalAppWindow.current.window.cursor = Cursor(
if (isHorizontal) Cursor.E_RESIZE_CURSOR else Cursor.S_RESIZE_CURSOR
)
} else {
LocalAppWindow.current.window.cursor = Cursor.getDefaultCursor()
}
pointerMoveFilter(
onEnter = { isHover = true; true },
onExit = { isHover = false; true }
)
}
@Composable
private fun DesktopSplitPaneSeparator(
isHorizontal: Boolean,
color: Color = MaterialTheme.colors.background
) = Box(
Modifier
.run {
if (isHorizontal) {
this.width(1.dp)
.fillMaxHeight()
} else {
this.height(1.dp)
.fillMaxWidth()
}
}
.background(color)
)
@Composable
private fun DesctopHandle(
isHorizontal: Boolean,
splitPaneState: SplitPaneState
) = Box(
Modifier
.pointerInput(splitPaneState) {
detectDragGestures { change, _ ->
change.consumeAllChanges()
splitPaneState.dispatchRawMovement(
if (isHorizontal) change.position.x else change.position.y
)
}
}
.cursorForHorizontalResize(isHorizontal)
.run {
if (isHorizontal) {
this.width(8.dp)
.fillMaxHeight()
} else {
this.height(8.dp)
.fillMaxWidth()
}
}
)
internal actual fun defaultSplitter(
isHorizontal: Boolean,
splitPaneState: SplitPaneState
): Splitter = Splitter(
measuredPart = {
DesktopSplitPaneSeparator(isHorizontal)
},
handlePart = {
DesctopHandle(isHorizontal, splitPaneState)
}
)

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

@ -0,0 +1,24 @@
import org.jetbrains.compose.compose
plugins {
kotlin("multiplatform")
id("org.jetbrains.compose")
}
kotlin {
jvm {}
sourceSets {
named("jvmMain") {
dependencies {
implementation(compose.desktop.currentOs)
implementation(project(":SplitPane:common"))
}
}
}
}
compose.desktop {
application {
mainClass = "org.jetbrains.compose.splitpane.demo.MainKt"
}
}

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

@ -0,0 +1,95 @@
package org.jetbrains.compose.splitpane.demo
import androidx.compose.desktop.DesktopTheme
import androidx.compose.desktop.LocalAppWindow
import androidx.compose.desktop.Window
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
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
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
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.HorizontalSplitPane
import org.jetbrains.compose.splitpane.VerticalSplitPane
import org.jetbrains.compose.splitpane.rememberSplitPaneState
import java.awt.Cursor
private fun Modifier.cursorForHorizontalResize(
): Modifier = composed {
var isHover by remember { mutableStateOf(false) }
if (isHover) {
LocalAppWindow.current.window.cursor = Cursor(Cursor.E_RESIZE_CURSOR)
} else {
LocalAppWindow.current.window.cursor = Cursor.getDefaultCursor()
}
pointerMoveFilter(
onEnter = { isHover = true; true },
onExit = { isHover = false; true }
)
}
fun main() = Window(
"SplitPane demo"
) {
MaterialTheme {
DesktopTheme {
val splitterState = rememberSplitPaneState()
val hSplitterState = rememberSplitPaneState()
HorizontalSplitPane(
splitPaneState = splitterState
) {
first(20.dp) {
Box(Modifier.background(Color.Red).fillMaxSize())
}
second(50.dp) {
VerticalSplitPane(splitPaneState = hSplitterState) {
first(50.dp) {
Box(Modifier.background(Color.Blue).fillMaxSize())
}
second(20.dp) {
Box(Modifier.background(Color.Green).fillMaxSize())
}
}
}
splitter {
visiblePart {
Box(
Modifier
.width(1.dp)
.fillMaxHeight()
.background(MaterialTheme.colors.background)
)
}
handle {
{
Box(
Modifier
.markAsHandle()
.cursorForHorizontalResize()
.background(SolidColor(Color.Gray), alpha = 0.50f)
.width(8.dp)
.fillMaxHeight()
)
}
}
}
}
}
}
}

4
components/build.gradle.kts

@ -1,6 +1,6 @@
buildscript {
// __LATEST_COMPOSE_RELEASE_VERSION__
val composeVersion = System.getenv("COMPOSE_RELEASE_VERSION") ?: "0.3.0-build135"
val composeVersion = System.getenv("COMPOSE_RELEASE_VERSION") ?: "0.3.0-build150"
repositories {
google()
@ -11,7 +11,7 @@ buildscript {
dependencies {
classpath("org.jetbrains.compose:compose-gradle-plugin:$composeVersion")
// __KOTLIN_COMPOSE_VERSION__
classpath(kotlin("gradle-plugin", version = "1.4.21"))
classpath(kotlin("gradle-plugin", version = "1.4.30"))
}
}

2
components/settings.gradle.kts

@ -1,2 +1,4 @@
include(":VideoPlayer:common")
include(":VideoPlayer:desktop")
include(":SplitPane:common")
include(":SplitPane:desktop")
Loading…
Cancel
Save