Igor Demin
3 years ago
committed by
GitHub
25 changed files with 505 additions and 438 deletions
@ -1,21 +0,0 @@ |
|||||||
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")) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
@ -1,29 +1,29 @@ |
|||||||
import org.jetbrains.compose.compose |
import org.jetbrains.compose.compose |
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
||||||
|
|
||||||
plugins { |
plugins { |
||||||
kotlin("multiplatform") |
kotlin("multiplatform") |
||||||
id("org.jetbrains.compose") |
id("org.jetbrains.compose") |
||||||
} |
} |
||||||
|
|
||||||
kotlin { |
kotlin { |
||||||
jvm {} |
jvm {} |
||||||
sourceSets { |
sourceSets { |
||||||
named("jvmMain") { |
named("jvmMain") { |
||||||
dependencies { |
dependencies { |
||||||
implementation(compose.desktop.currentOs) |
implementation(compose.desktop.currentOs) |
||||||
implementation(project(":SplitPane:common")) |
implementation(project(":SplitPane:library")) |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
compose.desktop { |
compose.desktop { |
||||||
application { |
application { |
||||||
mainClass = "org.jetbrains.compose.splitpane.demo.MainKt" |
mainClass = "org.jetbrains.compose.splitpane.demo.MainKt" |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
tasks.withType<KotlinCompile>().configureEach { |
tasks.withType<KotlinCompile>().configureEach { |
||||||
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" |
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" |
||||||
} |
} |
@ -1,91 +1,91 @@ |
|||||||
package org.jetbrains.compose.splitpane.demo |
package org.jetbrains.compose.splitpane.demo |
||||||
|
|
||||||
import androidx.compose.desktop.DesktopTheme |
import androidx.compose.desktop.DesktopTheme |
||||||
import androidx.compose.desktop.LocalAppWindow |
import androidx.compose.desktop.LocalAppWindow |
||||||
import androidx.compose.desktop.Window |
import androidx.compose.desktop.Window |
||||||
import androidx.compose.foundation.background |
import androidx.compose.foundation.background |
||||||
import androidx.compose.foundation.layout.Box |
import androidx.compose.foundation.layout.Box |
||||||
import androidx.compose.foundation.layout.fillMaxHeight |
import androidx.compose.foundation.layout.fillMaxHeight |
||||||
import androidx.compose.foundation.layout.fillMaxSize |
import androidx.compose.foundation.layout.fillMaxSize |
||||||
import androidx.compose.foundation.layout.width |
import androidx.compose.foundation.layout.width |
||||||
import androidx.compose.material.MaterialTheme |
import androidx.compose.material.MaterialTheme |
||||||
import androidx.compose.runtime.getValue |
import androidx.compose.runtime.getValue |
||||||
import androidx.compose.runtime.mutableStateOf |
import androidx.compose.runtime.mutableStateOf |
||||||
import androidx.compose.runtime.remember |
import androidx.compose.runtime.remember |
||||||
import androidx.compose.runtime.setValue |
import androidx.compose.runtime.setValue |
||||||
import androidx.compose.ui.Modifier |
import androidx.compose.ui.Modifier |
||||||
import androidx.compose.ui.composed |
import androidx.compose.ui.composed |
||||||
import androidx.compose.ui.graphics.Color |
import androidx.compose.ui.graphics.Color |
||||||
import androidx.compose.ui.graphics.SolidColor |
import androidx.compose.ui.graphics.SolidColor |
||||||
import androidx.compose.ui.input.pointer.pointerMoveFilter |
import androidx.compose.ui.input.pointer.pointerMoveFilter |
||||||
import androidx.compose.ui.unit.dp |
import androidx.compose.ui.unit.dp |
||||||
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi |
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi |
||||||
import org.jetbrains.compose.splitpane.HorizontalSplitPane |
import org.jetbrains.compose.splitpane.HorizontalSplitPane |
||||||
import org.jetbrains.compose.splitpane.VerticalSplitPane |
import org.jetbrains.compose.splitpane.VerticalSplitPane |
||||||
import org.jetbrains.compose.splitpane.rememberSplitPaneState |
import org.jetbrains.compose.splitpane.rememberSplitPaneState |
||||||
import java.awt.Cursor |
import java.awt.Cursor |
||||||
|
|
||||||
private fun Modifier.cursorForHorizontalResize( |
private fun Modifier.cursorForHorizontalResize( |
||||||
): Modifier = composed { |
): Modifier = composed { |
||||||
var isHover by remember { mutableStateOf(false) } |
var isHover by remember { mutableStateOf(false) } |
||||||
|
|
||||||
if (isHover) { |
if (isHover) { |
||||||
LocalAppWindow.current.window.cursor = Cursor(Cursor.E_RESIZE_CURSOR) |
LocalAppWindow.current.window.cursor = Cursor(Cursor.E_RESIZE_CURSOR) |
||||||
} else { |
} else { |
||||||
LocalAppWindow.current.window.cursor = Cursor.getDefaultCursor() |
LocalAppWindow.current.window.cursor = Cursor.getDefaultCursor() |
||||||
} |
} |
||||||
|
|
||||||
pointerMoveFilter( |
pointerMoveFilter( |
||||||
onEnter = { isHover = true; true }, |
onEnter = { isHover = true; true }, |
||||||
onExit = { isHover = false; true } |
onExit = { isHover = false; true } |
||||||
) |
) |
||||||
} |
} |
||||||
|
|
||||||
@OptIn(ExperimentalSplitPaneApi::class) |
@OptIn(ExperimentalSplitPaneApi::class) |
||||||
fun main() = Window( |
fun main() = Window( |
||||||
"SplitPane demo" |
"SplitPane demo" |
||||||
) { |
) { |
||||||
MaterialTheme { |
MaterialTheme { |
||||||
DesktopTheme { |
DesktopTheme { |
||||||
val splitterState = rememberSplitPaneState() |
val splitterState = rememberSplitPaneState() |
||||||
val hSplitterState = rememberSplitPaneState() |
val hSplitterState = rememberSplitPaneState() |
||||||
HorizontalSplitPane( |
HorizontalSplitPane( |
||||||
splitPaneState = splitterState |
splitPaneState = splitterState |
||||||
) { |
) { |
||||||
first(20.dp) { |
first(20.dp) { |
||||||
Box(Modifier.background(Color.Red).fillMaxSize()) |
Box(Modifier.background(Color.Red).fillMaxSize()) |
||||||
} |
} |
||||||
second(50.dp) { |
second(50.dp) { |
||||||
VerticalSplitPane(splitPaneState = hSplitterState) { |
VerticalSplitPane(splitPaneState = hSplitterState) { |
||||||
first(50.dp) { |
first(50.dp) { |
||||||
Box(Modifier.background(Color.Blue).fillMaxSize()) |
Box(Modifier.background(Color.Blue).fillMaxSize()) |
||||||
} |
} |
||||||
second(20.dp) { |
second(20.dp) { |
||||||
Box(Modifier.background(Color.Green).fillMaxSize()) |
Box(Modifier.background(Color.Green).fillMaxSize()) |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
splitter { |
splitter { |
||||||
visiblePart { |
visiblePart { |
||||||
Box( |
Box( |
||||||
Modifier |
Modifier |
||||||
.width(1.dp) |
.width(1.dp) |
||||||
.fillMaxHeight() |
.fillMaxHeight() |
||||||
.background(MaterialTheme.colors.background) |
.background(MaterialTheme.colors.background) |
||||||
) |
) |
||||||
} |
} |
||||||
handle { |
handle { |
||||||
Box( |
Box( |
||||||
Modifier |
Modifier |
||||||
.markAsHandle() |
.markAsHandle() |
||||||
.cursorForHorizontalResize() |
.cursorForHorizontalResize() |
||||||
.background(SolidColor(Color.Gray), alpha = 0.50f) |
.background(SolidColor(Color.Gray), alpha = 0.50f) |
||||||
.width(9.dp) |
.width(9.dp) |
||||||
.fillMaxHeight() |
.fillMaxHeight() |
||||||
) |
) |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
@ -1,27 +1,34 @@ |
|||||||
import org.jetbrains.compose.compose |
import org.jetbrains.compose.compose |
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
||||||
|
|
||||||
plugins { |
plugins { |
||||||
kotlin("multiplatform") |
kotlin("multiplatform") |
||||||
id("org.jetbrains.compose") |
id("org.jetbrains.compose") |
||||||
} |
id("maven-publish") |
||||||
|
} |
||||||
kotlin { |
|
||||||
jvm("desktop") |
kotlin { |
||||||
|
jvm("desktop") |
||||||
sourceSets { |
|
||||||
named("commonMain") { |
sourceSets { |
||||||
dependencies { |
named("commonMain") { |
||||||
api(compose.runtime) |
dependencies { |
||||||
api(compose.foundation) |
api(compose.runtime) |
||||||
api(compose.material) |
api(compose.foundation) |
||||||
} |
api(compose.material) |
||||||
} |
} |
||||||
named("desktopMain") {} |
} |
||||||
} |
named("desktopMain") {} |
||||||
} |
} |
||||||
|
} |
||||||
// TODO it seems that argument isn't applied to the common sourceSet. Figure out why |
|
||||||
tasks.withType<KotlinCompile>().configureEach { |
// TODO it seems that argument isn't applied to the common sourceSet. Figure out why |
||||||
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" |
tasks.withType<KotlinCompile>().configureEach { |
||||||
} |
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" |
||||||
|
} |
||||||
|
|
||||||
|
configureMavenPublication( |
||||||
|
groupId = "org.jetbrains.compose.components", |
||||||
|
artifactId = "components-splitpane", |
||||||
|
name = "SplitPane for Compose JB" |
||||||
|
) |
@ -1,141 +1,141 @@ |
|||||||
package org.jetbrains.compose.splitpane |
package org.jetbrains.compose.splitpane |
||||||
|
|
||||||
import androidx.compose.runtime.Composable |
import androidx.compose.runtime.Composable |
||||||
import androidx.compose.ui.Modifier |
import androidx.compose.ui.Modifier |
||||||
import androidx.compose.ui.layout.Layout |
import androidx.compose.ui.layout.Layout |
||||||
import androidx.compose.ui.layout.Placeable |
import androidx.compose.ui.layout.Placeable |
||||||
import androidx.compose.ui.unit.Constraints |
import androidx.compose.ui.unit.Constraints |
||||||
import kotlin.math.roundToInt |
import kotlin.math.roundToInt |
||||||
|
|
||||||
private fun Constraints.maxByDirection(isHorizontal: Boolean): Int = if (isHorizontal) maxWidth else maxHeight |
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 Constraints.minByDirection(isHorizontal: Boolean): Int = if (isHorizontal) minWidth else minHeight |
||||||
private fun Placeable.valueByDirection(isHorizontal: Boolean): Int = if (isHorizontal) width else height |
private fun Placeable.valueByDirection(isHorizontal: Boolean): Int = if (isHorizontal) width else height |
||||||
|
|
||||||
@OptIn(ExperimentalSplitPaneApi::class) |
@OptIn(ExperimentalSplitPaneApi::class) |
||||||
@Composable |
@Composable |
||||||
internal actual fun SplitPane( |
internal actual fun SplitPane( |
||||||
modifier: Modifier, |
modifier: Modifier, |
||||||
isHorizontal: Boolean, |
isHorizontal: Boolean, |
||||||
splitPaneState: SplitPaneState, |
splitPaneState: SplitPaneState, |
||||||
minimalSizesConfiguration: MinimalSizes, |
minimalSizesConfiguration: MinimalSizes, |
||||||
first: @Composable () -> Unit, |
first: @Composable () -> Unit, |
||||||
second: @Composable () -> Unit, |
second: @Composable () -> Unit, |
||||||
splitter: Splitter |
splitter: Splitter |
||||||
) { |
) { |
||||||
Layout( |
Layout( |
||||||
{ |
{ |
||||||
first() |
first() |
||||||
splitter.measuredPart() |
splitter.measuredPart() |
||||||
second() |
second() |
||||||
splitter.handlePart() |
splitter.handlePart() |
||||||
}, |
}, |
||||||
modifier, |
modifier, |
||||||
) { measurables, constraints -> |
) { measurables, constraints -> |
||||||
with(minimalSizesConfiguration) { |
with(minimalSizesConfiguration) { |
||||||
with(splitPaneState) { |
with(splitPaneState) { |
||||||
|
|
||||||
val constrainedMin = constraints.minByDirection(isHorizontal) + firstPlaceableMinimalSize.value |
val constrainedMin = constraints.minByDirection(isHorizontal) + firstPlaceableMinimalSize.value |
||||||
|
|
||||||
val constrainedMax = |
val constrainedMax = |
||||||
(constraints.maxByDirection(isHorizontal).toFloat() - secondPlaceableMinimalSize.value).let { |
(constraints.maxByDirection(isHorizontal).toFloat() - secondPlaceableMinimalSize.value).let { |
||||||
if (it <= 0 || it <= constrainedMin) { |
if (it <= 0 || it <= constrainedMin) { |
||||||
constraints.maxByDirection(isHorizontal).toFloat() |
constraints.maxByDirection(isHorizontal).toFloat() |
||||||
} else { |
} else { |
||||||
it |
it |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
if (minPosition != constrainedMin) { |
if (minPosition != constrainedMin) { |
||||||
maxPosition = constrainedMin |
maxPosition = constrainedMin |
||||||
} |
} |
||||||
|
|
||||||
if (maxPosition != constrainedMax) { |
if (maxPosition != constrainedMax) { |
||||||
maxPosition = |
maxPosition = |
||||||
if ((firstPlaceableMinimalSize + secondPlaceableMinimalSize).value < constraints.maxByDirection(isHorizontal)) { |
if ((firstPlaceableMinimalSize + secondPlaceableMinimalSize).value < constraints.maxByDirection(isHorizontal)) { |
||||||
constrainedMax |
constrainedMax |
||||||
} else { |
} else { |
||||||
minPosition |
minPosition |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
val constrainedPosition = |
val constrainedPosition = |
||||||
(constraints.maxByDirection(isHorizontal) - (firstPlaceableMinimalSize + secondPlaceableMinimalSize).value).let { |
(constraints.maxByDirection(isHorizontal) - (firstPlaceableMinimalSize + secondPlaceableMinimalSize).value).let { |
||||||
if (it > 0f) { |
if (it > 0f) { |
||||||
(it * positionPercentage).coerceIn(constrainedMin, constrainedMax).roundToInt() |
(it * positionPercentage).coerceIn(constrainedMin, constrainedMax).roundToInt() |
||||||
} else { |
} else { |
||||||
constrainedMin.roundToInt() |
constrainedMin.roundToInt() |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
val firstPlaceable = measurables[0].measure( |
val firstPlaceable = measurables[0].measure( |
||||||
if (isHorizontal) { |
if (isHorizontal) { |
||||||
constraints.copy( |
constraints.copy( |
||||||
minWidth = 0, |
minWidth = 0, |
||||||
maxWidth = constrainedPosition |
maxWidth = constrainedPosition |
||||||
) |
) |
||||||
} else { |
} else { |
||||||
constraints.copy( |
constraints.copy( |
||||||
minHeight = 0, |
minHeight = 0, |
||||||
maxHeight = constrainedPosition |
maxHeight = constrainedPosition |
||||||
) |
) |
||||||
} |
} |
||||||
) |
) |
||||||
|
|
||||||
val splitterPlaceable = measurables[1].measure(constraints) |
val splitterPlaceable = measurables[1].measure(constraints) |
||||||
val splitterSize = splitterPlaceable.valueByDirection(isHorizontal) |
val splitterSize = splitterPlaceable.valueByDirection(isHorizontal) |
||||||
val secondPlaceablePosition = constrainedPosition + splitterSize |
val secondPlaceablePosition = constrainedPosition + splitterSize |
||||||
|
|
||||||
val secondPlaceableSize = |
val secondPlaceableSize = |
||||||
(constraints.maxByDirection(isHorizontal) - secondPlaceablePosition).coerceIn( |
(constraints.maxByDirection(isHorizontal) - secondPlaceablePosition).coerceIn( |
||||||
0, |
0, |
||||||
if (secondPlaceablePosition < constraints.maxByDirection(isHorizontal)) { |
if (secondPlaceablePosition < constraints.maxByDirection(isHorizontal)) { |
||||||
constraints.maxByDirection(isHorizontal) - secondPlaceablePosition |
constraints.maxByDirection(isHorizontal) - secondPlaceablePosition |
||||||
} else { |
} else { |
||||||
constraints.maxByDirection(isHorizontal) |
constraints.maxByDirection(isHorizontal) |
||||||
} |
} |
||||||
) |
) |
||||||
|
|
||||||
val secondPlaceable = measurables[2].measure( |
val secondPlaceable = measurables[2].measure( |
||||||
if (isHorizontal) { |
if (isHorizontal) { |
||||||
constraints.copy( |
constraints.copy( |
||||||
minWidth = 0, |
minWidth = 0, |
||||||
maxWidth = secondPlaceableSize |
maxWidth = secondPlaceableSize |
||||||
) |
) |
||||||
} else { |
} else { |
||||||
constraints.copy( |
constraints.copy( |
||||||
minHeight = 0, |
minHeight = 0, |
||||||
maxHeight = secondPlaceableSize |
maxHeight = secondPlaceableSize |
||||||
) |
) |
||||||
} |
} |
||||||
) |
) |
||||||
|
|
||||||
val handlePlaceable = measurables[3].measure(constraints) |
val handlePlaceable = measurables[3].measure(constraints) |
||||||
val handleSize = handlePlaceable.valueByDirection(isHorizontal) |
val handleSize = handlePlaceable.valueByDirection(isHorizontal) |
||||||
// TODO support RTL |
// TODO support RTL |
||||||
val handlePosition = when (splitter.alignment) { |
val handlePosition = when (splitter.alignment) { |
||||||
SplitterHandleAlignment.BEFORE -> constrainedPosition + splitterSize - handleSize |
SplitterHandleAlignment.BEFORE -> constrainedPosition + splitterSize - handleSize |
||||||
SplitterHandleAlignment.ABOVE -> constrainedPosition + (splitterSize - handleSize) / 2 |
SplitterHandleAlignment.ABOVE -> constrainedPosition + (splitterSize - handleSize) / 2 |
||||||
SplitterHandleAlignment.AFTER -> constrainedPosition |
SplitterHandleAlignment.AFTER -> constrainedPosition |
||||||
} |
} |
||||||
|
|
||||||
layout(constraints.maxWidth, constraints.maxHeight) { |
layout(constraints.maxWidth, constraints.maxHeight) { |
||||||
firstPlaceable.place(0, 0) |
firstPlaceable.place(0, 0) |
||||||
if (isHorizontal) { |
if (isHorizontal) { |
||||||
secondPlaceable.place(secondPlaceablePosition, 0) |
secondPlaceable.place(secondPlaceablePosition, 0) |
||||||
splitterPlaceable.place(constrainedPosition, 0) |
splitterPlaceable.place(constrainedPosition, 0) |
||||||
if (moveEnabled) { |
if (moveEnabled) { |
||||||
handlePlaceable.place(handlePosition, 0) |
handlePlaceable.place(handlePosition, 0) |
||||||
} |
} |
||||||
} else { |
} else { |
||||||
secondPlaceable.place(0, secondPlaceablePosition) |
secondPlaceable.place(0, secondPlaceablePosition) |
||||||
splitterPlaceable.place(0, constrainedPosition) |
splitterPlaceable.place(0, constrainedPosition) |
||||||
if (moveEnabled) { |
if (moveEnabled) { |
||||||
handlePlaceable.place(0, handlePosition) |
handlePlaceable.place(0, handlePosition) |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
@ -1,101 +1,101 @@ |
|||||||
package org.jetbrains.compose.splitpane |
package org.jetbrains.compose.splitpane |
||||||
|
|
||||||
import androidx.compose.desktop.LocalAppWindow |
import androidx.compose.desktop.LocalAppWindow |
||||||
import androidx.compose.foundation.background |
import androidx.compose.foundation.background |
||||||
import androidx.compose.foundation.gestures.detectDragGestures |
import androidx.compose.foundation.gestures.detectDragGestures |
||||||
import androidx.compose.foundation.layout.Box |
import androidx.compose.foundation.layout.Box |
||||||
import androidx.compose.foundation.layout.fillMaxHeight |
import androidx.compose.foundation.layout.fillMaxHeight |
||||||
import androidx.compose.foundation.layout.fillMaxWidth |
import androidx.compose.foundation.layout.fillMaxWidth |
||||||
import androidx.compose.foundation.layout.height |
import androidx.compose.foundation.layout.height |
||||||
import androidx.compose.foundation.layout.width |
import androidx.compose.foundation.layout.width |
||||||
import androidx.compose.material.MaterialTheme |
import androidx.compose.material.MaterialTheme |
||||||
import androidx.compose.runtime.Composable |
import androidx.compose.runtime.Composable |
||||||
import androidx.compose.runtime.getValue |
import androidx.compose.runtime.getValue |
||||||
import androidx.compose.runtime.mutableStateOf |
import androidx.compose.runtime.mutableStateOf |
||||||
import androidx.compose.runtime.remember |
import androidx.compose.runtime.remember |
||||||
import androidx.compose.runtime.setValue |
import androidx.compose.runtime.setValue |
||||||
import androidx.compose.ui.Modifier |
import androidx.compose.ui.Modifier |
||||||
import androidx.compose.ui.composed |
import androidx.compose.ui.composed |
||||||
import androidx.compose.ui.graphics.Color |
import androidx.compose.ui.graphics.Color |
||||||
import androidx.compose.ui.input.pointer.consumeAllChanges |
import androidx.compose.ui.input.pointer.consumeAllChanges |
||||||
import androidx.compose.ui.input.pointer.pointerInput |
import androidx.compose.ui.input.pointer.pointerInput |
||||||
import androidx.compose.ui.input.pointer.pointerMoveFilter |
import androidx.compose.ui.input.pointer.pointerMoveFilter |
||||||
import androidx.compose.ui.unit.dp |
import androidx.compose.ui.unit.dp |
||||||
import java.awt.Cursor |
import java.awt.Cursor |
||||||
|
|
||||||
private fun Modifier.cursorForHorizontalResize( |
private fun Modifier.cursorForHorizontalResize( |
||||||
isHorizontal: Boolean |
isHorizontal: Boolean |
||||||
): Modifier = composed { |
): Modifier = composed { |
||||||
var isHover by remember { mutableStateOf(false) } |
var isHover by remember { mutableStateOf(false) } |
||||||
|
|
||||||
if (isHover) { |
if (isHover) { |
||||||
LocalAppWindow.current.window.cursor = Cursor( |
LocalAppWindow.current.window.cursor = Cursor( |
||||||
if (isHorizontal) Cursor.E_RESIZE_CURSOR else Cursor.S_RESIZE_CURSOR |
if (isHorizontal) Cursor.E_RESIZE_CURSOR else Cursor.S_RESIZE_CURSOR |
||||||
) |
) |
||||||
} else { |
} else { |
||||||
LocalAppWindow.current.window.cursor = Cursor.getDefaultCursor() |
LocalAppWindow.current.window.cursor = Cursor.getDefaultCursor() |
||||||
} |
} |
||||||
pointerMoveFilter( |
pointerMoveFilter( |
||||||
onEnter = { isHover = true; true }, |
onEnter = { isHover = true; true }, |
||||||
onExit = { isHover = false; true } |
onExit = { isHover = false; true } |
||||||
) |
) |
||||||
} |
} |
||||||
|
|
||||||
@Composable |
@Composable |
||||||
private fun DesktopSplitPaneSeparator( |
private fun DesktopSplitPaneSeparator( |
||||||
isHorizontal: Boolean, |
isHorizontal: Boolean, |
||||||
color: Color = MaterialTheme.colors.background |
color: Color = MaterialTheme.colors.background |
||||||
) = Box( |
) = Box( |
||||||
Modifier |
Modifier |
||||||
.run { |
.run { |
||||||
if (isHorizontal) { |
if (isHorizontal) { |
||||||
this.width(1.dp) |
this.width(1.dp) |
||||||
.fillMaxHeight() |
.fillMaxHeight() |
||||||
} else { |
} else { |
||||||
this.height(1.dp) |
this.height(1.dp) |
||||||
.fillMaxWidth() |
.fillMaxWidth() |
||||||
} |
} |
||||||
} |
} |
||||||
.background(color) |
.background(color) |
||||||
) |
) |
||||||
|
|
||||||
@OptIn(ExperimentalSplitPaneApi::class) |
@OptIn(ExperimentalSplitPaneApi::class) |
||||||
@Composable |
@Composable |
||||||
private fun DesktopHandle( |
private fun DesktopHandle( |
||||||
isHorizontal: Boolean, |
isHorizontal: Boolean, |
||||||
splitPaneState: SplitPaneState |
splitPaneState: SplitPaneState |
||||||
) = Box( |
) = Box( |
||||||
Modifier |
Modifier |
||||||
.pointerInput(splitPaneState) { |
.pointerInput(splitPaneState) { |
||||||
detectDragGestures { change, _ -> |
detectDragGestures { change, _ -> |
||||||
change.consumeAllChanges() |
change.consumeAllChanges() |
||||||
splitPaneState.dispatchRawMovement( |
splitPaneState.dispatchRawMovement( |
||||||
if (isHorizontal) change.position.x else change.position.y |
if (isHorizontal) change.position.x else change.position.y |
||||||
) |
) |
||||||
} |
} |
||||||
} |
} |
||||||
.cursorForHorizontalResize(isHorizontal) |
.cursorForHorizontalResize(isHorizontal) |
||||||
.run { |
.run { |
||||||
if (isHorizontal) { |
if (isHorizontal) { |
||||||
this.width(8.dp) |
this.width(8.dp) |
||||||
.fillMaxHeight() |
.fillMaxHeight() |
||||||
} else { |
} else { |
||||||
this.height(8.dp) |
this.height(8.dp) |
||||||
.fillMaxWidth() |
.fillMaxWidth() |
||||||
} |
} |
||||||
} |
} |
||||||
) |
) |
||||||
|
|
||||||
@OptIn(ExperimentalSplitPaneApi::class) |
@OptIn(ExperimentalSplitPaneApi::class) |
||||||
internal actual fun defaultSplitter( |
internal actual fun defaultSplitter( |
||||||
isHorizontal: Boolean, |
isHorizontal: Boolean, |
||||||
splitPaneState: SplitPaneState |
splitPaneState: SplitPaneState |
||||||
): Splitter = Splitter( |
): Splitter = Splitter( |
||||||
measuredPart = { |
measuredPart = { |
||||||
DesktopSplitPaneSeparator(isHorizontal) |
DesktopSplitPaneSeparator(isHorizontal) |
||||||
}, |
}, |
||||||
handlePart = { |
handlePart = { |
||||||
DesktopHandle(isHorizontal, splitPaneState) |
DesktopHandle(isHorizontal, splitPaneState) |
||||||
} |
} |
||||||
) |
) |
||||||
|
|
@ -1,21 +0,0 @@ |
|||||||
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")) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
@ -0,0 +1,11 @@ |
|||||||
|
plugins { |
||||||
|
`kotlin-dsl` |
||||||
|
} |
||||||
|
|
||||||
|
repositories { |
||||||
|
gradlePluginPortal() |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
compileOnly(gradleApi()) |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
pluginManagement { |
||||||
|
repositories { |
||||||
|
gradlePluginPortal() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
import org.gradle.api.Project |
||||||
|
import org.gradle.api.publish.PublishingExtension |
||||||
|
import org.gradle.api.publish.maven.MavenPublication |
||||||
|
import org.gradle.kotlin.dsl.configure |
||||||
|
|
||||||
|
fun Project.configureMavenPublication( |
||||||
|
groupId: String, |
||||||
|
artifactId: String, |
||||||
|
name: String |
||||||
|
) { |
||||||
|
extensions.configure<PublishingExtension> { |
||||||
|
publications { |
||||||
|
all { |
||||||
|
this as MavenPublication |
||||||
|
|
||||||
|
this.groupId = groupId |
||||||
|
mppArtifactId = artifactId |
||||||
|
|
||||||
|
pom { |
||||||
|
this.name.set(name) |
||||||
|
url.set("https://github.com/JetBrains/compose-jb") |
||||||
|
licenses { |
||||||
|
license { |
||||||
|
this.name.set("The Apache License, Version 2.0") |
||||||
|
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
import org.gradle.api.Project |
||||||
|
import org.gradle.api.publish.maven.MavenPublication |
||||||
|
import java.lang.UnsupportedOperationException |
||||||
|
|
||||||
|
inline fun <reified T> Project.configureIfExists(fn: T.() -> Unit) { |
||||||
|
extensions.findByType(T::class.java)?.fn() |
||||||
|
} |
||||||
|
|
||||||
|
var MavenPublication.mppArtifactId: String |
||||||
|
get() = throw UnsupportedOperationException() |
||||||
|
set(value) { |
||||||
|
val target = this.name |
||||||
|
artifactId = if ("kotlinMultiplatform" in target) value else "$value-$target" |
||||||
|
} |
@ -1,4 +1,7 @@ |
|||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 |
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 |
||||||
android.useAndroidX=true |
android.useAndroidX=true |
||||||
android.enableJetifier=true |
android.enableJetifier=true |
||||||
kotlin.code.style=official |
kotlin.code.style=official |
||||||
|
|
||||||
|
# __LATEST_COMPOSE_RELEASE_VERSION__ |
||||||
|
compose.version=0.4.0 |
@ -1,4 +1,4 @@ |
|||||||
include(":VideoPlayer:common") |
include(":VideoPlayer:library") |
||||||
include(":VideoPlayer:desktop") |
include(":VideoPlayer:demo") |
||||||
include(":SplitPane:common") |
include(":SplitPane:library") |
||||||
include(":SplitPane:desktop") |
include(":SplitPane:demo") |
Loading…
Reference in new issue