|
@@ -2,13 +2,27 @@
|
|
|
|
|
|
|
|
package eu.mjdev.desktop.extensions
|
|
package eu.mjdev.desktop.extensions
|
|
|
|
|
|
|
|
|
|
+import androidx.compose.foundation.ExperimentalFoundationApi
|
|
|
|
|
+import androidx.compose.foundation.gestures.detectTapGestures
|
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
|
|
|
+import androidx.compose.foundation.relocation.BringIntoViewResponder
|
|
|
|
|
+import androidx.compose.foundation.relocation.bringIntoViewResponder
|
|
|
import androidx.compose.material.ButtonDefaults
|
|
import androidx.compose.material.ButtonDefaults
|
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.runtime.*
|
|
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
+import androidx.compose.ui.composed
|
|
|
|
|
+import androidx.compose.ui.focus.FocusRequester
|
|
|
|
|
+import androidx.compose.ui.focus.FocusState
|
|
|
|
|
+import androidx.compose.ui.focus.focusRequester
|
|
|
|
|
+import androidx.compose.ui.focus.onFocusChanged
|
|
|
|
|
+import androidx.compose.ui.geometry.Offset
|
|
|
|
|
+import androidx.compose.ui.geometry.Rect
|
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.graphics.Color
|
|
|
|
|
+import androidx.compose.ui.input.key.*
|
|
|
import androidx.compose.ui.input.pointer.*
|
|
import androidx.compose.ui.input.pointer.*
|
|
|
|
|
+import androidx.compose.ui.layout.onSizeChanged
|
|
|
|
|
+import androidx.compose.ui.platform.debugInspectorInfo
|
|
|
import androidx.compose.ui.unit.*
|
|
import androidx.compose.ui.unit.*
|
|
|
import coil3.ImageLoader
|
|
import coil3.ImageLoader
|
|
|
import coil3.PlatformContext
|
|
import coil3.PlatformContext
|
|
@@ -16,7 +30,11 @@ import coil3.compose.LocalPlatformContext
|
|
|
import coil3.memory.MemoryCache
|
|
import coil3.memory.MemoryCache
|
|
|
import coil3.request.CachePolicy
|
|
import coil3.request.CachePolicy
|
|
|
import coil3.svg.SvgDecoder
|
|
import coil3.svg.SvgDecoder
|
|
|
|
|
+import eu.mjdev.desktop.helpers.compose.FocusHelper
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
|
|
+import kotlinx.coroutines.Dispatchers
|
|
|
|
|
+import kotlinx.coroutines.launch
|
|
|
|
|
+import kotlinx.coroutines.withContext
|
|
|
|
|
|
|
|
object Compose {
|
|
object Compose {
|
|
|
|
|
|
|
@@ -43,7 +61,7 @@ object Compose {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fun Color.rgbaToHex(): String {
|
|
fun Color.rgbaToHex(): String {
|
|
|
- val alpha : Int= (alpha * 255).toInt()
|
|
|
|
|
|
|
+ val alpha: Int = (alpha * 255).toInt()
|
|
|
val red = (red * 255).toInt()
|
|
val red = (red * 255).toInt()
|
|
|
val green = (green * 255).toInt()
|
|
val green = (green * 255).toInt()
|
|
|
val blue = (blue * 255).toInt()
|
|
val blue = (blue * 255).toInt()
|
|
@@ -82,13 +100,9 @@ object Compose {
|
|
|
|
|
|
|
|
@Composable
|
|
@Composable
|
|
|
fun <T> textFrom(text: T?): String = when (text) {
|
|
fun <T> textFrom(text: T?): String = when (text) {
|
|
|
- null -> ""
|
|
|
|
|
- is Unit -> ""
|
|
|
|
|
- is Int -> text.toString()
|
|
|
|
|
- is String -> text
|
|
|
|
|
is MutableState<*> -> textFrom(text.value)
|
|
is MutableState<*> -> textFrom(text.value)
|
|
|
else -> text.toString()
|
|
else -> text.toString()
|
|
|
- }
|
|
|
|
|
|
|
+ }.toString()
|
|
|
|
|
|
|
|
@Composable
|
|
@Composable
|
|
|
fun ButtonDefaults.noElevation() =
|
|
fun ButtonDefaults.noElevation() =
|
|
@@ -174,6 +188,19 @@ object Compose {
|
|
|
onEvent = onEvent
|
|
onEvent = onEvent
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+ @Suppress("UNUSED_PARAMETER")
|
|
|
|
|
+ fun Modifier.onMouseLongPress(
|
|
|
|
|
+ pass: PointerEventPass = PointerEventPass.Main,
|
|
|
|
|
+ onEvent: AwaitPointerEventScope.(event: PointerEvent) -> Unit
|
|
|
|
|
+ ): Modifier {
|
|
|
|
|
+// return onPointerEvent(
|
|
|
|
|
+// eventType = PointerEventType.Press,
|
|
|
|
|
+// pass = pass,
|
|
|
|
|
+// onEvent = onEvent
|
|
|
|
|
+// )
|
|
|
|
|
+ return this
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@OptIn(ExperimentalComposeUiApi::class)
|
|
@OptIn(ExperimentalComposeUiApi::class)
|
|
|
val AwaitPointerEventScope.isPrimary
|
|
val AwaitPointerEventScope.isPrimary
|
|
|
get() = currentEvent.button.isPrimary
|
|
get() = currentEvent.button.isPrimary
|
|
@@ -188,12 +215,138 @@ object Compose {
|
|
|
if (isPrimary) block()
|
|
if (isPrimary) block()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fun AwaitPointerEventScope.onRigntClick(
|
|
|
|
|
|
|
+ fun AwaitPointerEventScope.onRightClick(
|
|
|
block: AwaitPointerEventScope.() -> Unit
|
|
block: AwaitPointerEventScope.() -> Unit
|
|
|
) {
|
|
) {
|
|
|
if (isSecondary) block()
|
|
if (isSecondary) block()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ fun Modifier.onKey(
|
|
|
|
|
+ keyCode: Key,
|
|
|
|
|
+ action: KeyEventType = KeyEventType.KeyDown,
|
|
|
|
|
+ block: () -> Unit
|
|
|
|
|
+ ): Modifier = this then onKeyEvent { ev ->
|
|
|
|
|
+ if (ev.type == action) {
|
|
|
|
|
+ if (ev.key == keyCode) {
|
|
|
|
|
+ block()
|
|
|
|
|
+ true
|
|
|
|
|
+ } else false
|
|
|
|
|
+ } else false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Composable
|
|
|
|
|
+ fun Modifier.requestFocusOnTouch(
|
|
|
|
|
+ focusRequester: FocusRequester,
|
|
|
|
|
+ requestFocus: Boolean = true,
|
|
|
|
|
+ scope: CoroutineScope = rememberCoroutineScope(),
|
|
|
|
|
+ onTouch: (() -> Unit)? = null
|
|
|
|
|
+ ): Modifier = this then focusRequester(
|
|
|
|
|
+ focusRequester
|
|
|
|
|
+ ).pointerInput(this) {
|
|
|
|
|
+ detectTapGestures(
|
|
|
|
|
+ onTap = {
|
|
|
|
|
+ try {
|
|
|
|
|
+ scope.launch {
|
|
|
|
|
+ onTouch?.invoke()
|
|
|
|
|
+ if (requestFocus) {
|
|
|
|
|
+ focusRequester.requestFocus()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e: Throwable) {
|
|
|
|
|
+ e.printStackTrace()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @OptIn(ExperimentalFoundationApi::class)
|
|
|
|
|
+ fun Modifier.bringIntoViewIfChildrenAreFocused(): Modifier = composed(
|
|
|
|
|
+ inspectorInfo = debugInspectorInfo { name = "bringIntoViewIfChildrenAreFocused" },
|
|
|
|
|
+ factory = {
|
|
|
|
|
+ var myRect: Rect = Rect.Zero
|
|
|
|
|
+ this.onSizeChanged {
|
|
|
|
|
+ myRect = Rect(Offset.Zero, Offset(it.width.toFloat(), it.height.toFloat()))
|
|
|
|
|
+ }.bringIntoViewResponder(
|
|
|
|
|
+ remember {
|
|
|
|
|
+ object : BringIntoViewResponder {
|
|
|
|
|
+ @ExperimentalFoundationApi
|
|
|
|
|
+ override fun calculateRectForParent(localRect: Rect): Rect = myRect
|
|
|
|
|
+
|
|
|
|
|
+ @ExperimentalFoundationApi
|
|
|
|
|
+ override suspend fun bringChildIntoView(localRect: () -> Rect?) {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ @Composable
|
|
|
|
|
+ fun rememberFocusRequester(
|
|
|
|
|
+ key: Any? = Unit
|
|
|
|
|
+ ) = remember(key) { FocusRequester() }
|
|
|
|
|
+
|
|
|
|
|
+ @Composable
|
|
|
|
|
+ fun <T> produceStateInCoroutine(
|
|
|
|
|
+ initialValue: T,
|
|
|
|
|
+ key: Any? = null,
|
|
|
|
|
+ block: suspend () -> T
|
|
|
|
|
+ ) = produceState(
|
|
|
|
|
+ initialValue = initialValue,
|
|
|
|
|
+ key1 = key
|
|
|
|
|
+ ) {
|
|
|
|
|
+ withContext(Dispatchers.IO) {
|
|
|
|
|
+ value = block()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Composable
|
|
|
|
|
+ fun <T> rememberDerivedState(
|
|
|
|
|
+ key: Any? = Unit,
|
|
|
|
|
+ initialValue: T,
|
|
|
|
|
+ block: suspend () -> T
|
|
|
|
|
+ ) = produceStateInCoroutine(initialValue, key, block)
|
|
|
|
|
+
|
|
|
|
|
+ @Composable
|
|
|
|
|
+ fun <T> rememberDerivedStateOf(
|
|
|
|
|
+ function: () -> T,
|
|
|
|
|
+ key: Any? = function
|
|
|
|
|
+ ): State<T> = remember(key) { derivedStateOf(function) }
|
|
|
|
|
+
|
|
|
|
|
+ @Composable
|
|
|
|
|
+ fun rememberFocusState(
|
|
|
|
|
+ initial: FocusState = FocusHelper(false)
|
|
|
|
|
+ ) = remember(initial) {
|
|
|
|
|
+ mutableStateOf(initial)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Composable
|
|
|
|
|
+ fun rememberFocusState(
|
|
|
|
|
+ key: Any?,
|
|
|
|
|
+ initial: FocusState = FocusHelper(false),
|
|
|
|
|
+ ) = remember(key) {
|
|
|
|
|
+ mutableStateOf(initial)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fun Modifier.focusState(
|
|
|
|
|
+ focusState: MutableState<FocusState>
|
|
|
|
|
+ ): Modifier = onFocusChanged { state ->
|
|
|
|
|
+ focusState.value = state
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ val MutableState<FocusState>.isFocused
|
|
|
|
|
+ get() = value.isFocused || value.hasFocus
|
|
|
|
|
+
|
|
|
|
|
+ val MutableState<FocusState>.isNotFocused
|
|
|
|
|
+ get() = !isFocused
|
|
|
|
|
+
|
|
|
|
|
+ @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
|
|
|
|
+ val FocusState.isFocused
|
|
|
|
|
+ get() = this.isFocused || this.hasFocus
|
|
|
|
|
+
|
|
|
|
|
+ val FocusState.isNotFocused
|
|
|
|
|
+ get() = !isFocused
|
|
|
|
|
+
|
|
|
// Toolkit.getDefaultToolkit().addAWTEventListener({ event ->
|
|
// Toolkit.getDefaultToolkit().addAWTEventListener({ event ->
|
|
|
// }, AWTEvent.MOUSE_EVENT_MASK or AWTEvent.FOCUS_EVENT_MASK
|
|
// }, AWTEvent.MOUSE_EVENT_MASK or AWTEvent.FOCUS_EVENT_MASK
|
|
|
// )
|
|
// )
|