mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Add basic resizer for floating mode
This commit is contained in:
parent
112b7a291a
commit
2f8d847186
130
java/src/org/futo/inputmethod/latin/uix/ResizerRect.kt
Normal file
130
java/src/org/futo/inputmethod/latin/uix/ResizerRect.kt
Normal file
@ -0,0 +1,130 @@
|
||||
package org.futo.inputmethod.latin.uix
|
||||
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.gestures.detectDragGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toSize
|
||||
|
||||
|
||||
enum class CurrentDraggingTarget {
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight,
|
||||
Center
|
||||
}
|
||||
|
||||
private fun CurrentDraggingTarget.computeOffset(size: Size): Offset = when(this) {
|
||||
CurrentDraggingTarget.TopLeft -> Offset(0.0f, 0.0f)
|
||||
CurrentDraggingTarget.TopRight -> Offset(size.width, 0.0f)
|
||||
CurrentDraggingTarget.BottomLeft -> Offset(0.0f, size.height)
|
||||
CurrentDraggingTarget.BottomRight -> Offset(size.width, size.height)
|
||||
CurrentDraggingTarget.Center -> Offset(size.width * 0.5f, size.height * 0.5f)
|
||||
}
|
||||
|
||||
private fun CurrentDraggingTarget.computeOffset(size: IntSize): Offset =
|
||||
computeOffset(size.toSize())
|
||||
|
||||
private fun CurrentDraggingTarget.dragDelta(offset: Offset): DragDelta = when(this) {
|
||||
CurrentDraggingTarget.TopLeft -> DragDelta(left = offset.x, top = offset.y)
|
||||
CurrentDraggingTarget.TopRight -> DragDelta(right = offset.x, top = offset.y)
|
||||
CurrentDraggingTarget.BottomLeft -> DragDelta(left = offset.x, bottom = offset.y)
|
||||
CurrentDraggingTarget.BottomRight -> DragDelta(right = offset.x, bottom = offset.y)
|
||||
CurrentDraggingTarget.Center -> DragDelta(
|
||||
left = offset.x,
|
||||
right = offset.x,
|
||||
top = offset.y,
|
||||
bottom = offset.y
|
||||
)
|
||||
}
|
||||
|
||||
data class DragDelta(
|
||||
val left: Float = 0.0f,
|
||||
val top: Float = 0.0f,
|
||||
val right: Float = 0.0f,
|
||||
val bottom: Float = 0.0f
|
||||
)
|
||||
|
||||
|
||||
@Composable
|
||||
fun BoxScope.ResizerRect(onDragged: (DragDelta) -> Boolean, showResetApply: Boolean, onApply: () -> Unit, onReset: () -> Unit) {
|
||||
val shape = RectangleShape
|
||||
|
||||
val draggingState = remember { mutableStateOf<CurrentDraggingTarget?>(null) }
|
||||
val wasAccepted = remember { mutableStateOf(true) }
|
||||
|
||||
Box(Modifier
|
||||
.matchParentSize()
|
||||
.background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f), shape)
|
||||
.border(3.dp, MaterialTheme.colorScheme.primary, shape)
|
||||
.pointerInput(Unit) {
|
||||
detectDragGestures(
|
||||
onDragStart = { offset ->
|
||||
draggingState.value = CurrentDraggingTarget.entries.minBy {
|
||||
offset.minus(it.computeOffset(size)).getDistanceSquared()
|
||||
}
|
||||
},
|
||||
onDrag = { _, amount ->
|
||||
draggingState.value?.let {
|
||||
wasAccepted.value = onDragged(it.dragDelta(amount))
|
||||
}
|
||||
},
|
||||
onDragEnd = {
|
||||
draggingState.value = null
|
||||
wasAccepted.value = true
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
val primaryColor = MaterialTheme.colorScheme.primary
|
||||
val primaryInverseColor = MaterialTheme.colorScheme.inversePrimary
|
||||
val errorColor = MaterialTheme.colorScheme.error
|
||||
val radius = with(LocalDensity.current) { 24.dp.toPx() }
|
||||
|
||||
Canvas(Modifier.matchParentSize(), onDraw = {
|
||||
CurrentDraggingTarget.entries.forEach {
|
||||
drawCircle(
|
||||
color = if (!wasAccepted.value) {
|
||||
errorColor
|
||||
} else if (draggingState.value == it) {
|
||||
primaryInverseColor
|
||||
} else {
|
||||
primaryColor
|
||||
},
|
||||
radius = radius,
|
||||
center = it.computeOffset(size)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
if (showResetApply) {
|
||||
Row(Modifier.align(Alignment.BottomCenter).padding(16.dp)) {
|
||||
TextButton({ onReset() }) { Text("Reset") }
|
||||
Spacer(Modifier.width(8.dp))
|
||||
TextButton({ onApply() }) { Text("Apply") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -41,6 +41,10 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
@ -60,6 +64,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
@ -703,6 +708,7 @@ class UixManager(private val latinIME: LatinIME) {
|
||||
pointerInputKey: Any?,
|
||||
onDragged: (Offset) -> Unit,
|
||||
onDragEnd: () -> Unit,
|
||||
onResizerOpen: () -> Unit,
|
||||
content: @Composable BoxScope.(actionBarGap: Dp) -> Unit
|
||||
) {
|
||||
// Content
|
||||
@ -720,6 +726,13 @@ class UixManager(private val latinIME: LatinIME) {
|
||||
onDrag = { _, dragAmount -> onDragged(dragAmount)},
|
||||
onDragEnd = { onDragEnd() })
|
||||
}) {
|
||||
|
||||
IconButton(onClick = {
|
||||
onResizerOpen()
|
||||
}, Modifier.align(Alignment.CenterEnd)) {
|
||||
Icon(Icons.Default.Menu, contentDescription = "resize")
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(0.6f).height(4.dp)
|
||||
.align(Alignment.TopCenter).background(
|
||||
@ -736,6 +749,54 @@ class UixManager(private val latinIME: LatinIME) {
|
||||
content: @Composable BoxScope.(actionBarGap: Dp) -> Unit
|
||||
) = with(LocalDensity.current) {
|
||||
val offset = remember(size) { mutableStateOf(Offset(size.bottomOrigin.first.toFloat(), size.bottomOrigin.second.toFloat())) }
|
||||
|
||||
val resizing = remember { mutableStateOf(false) }
|
||||
|
||||
val onDragDelta: (DragDelta) -> Boolean = remember { { delta ->
|
||||
// Matching the necessary coordinate space
|
||||
var deltaX = delta.left
|
||||
var deltaY = -delta.bottom
|
||||
var deltaWidth = delta.right - delta.left
|
||||
var deltaHeight = delta.bottom - delta.top
|
||||
|
||||
var result = true
|
||||
|
||||
// TODO: Limit the values so that we do not go off-screen
|
||||
// If we have reached a minimum limit, return false
|
||||
|
||||
// Basic limiting for minimum size
|
||||
val currSettings = latinIME.sizingCalculator.getSavedSettings()
|
||||
val currSize = Size(
|
||||
currSettings.floatingWidthDp.dp.toPx(),
|
||||
currSettings.floatingHeightDp.dp.toPx()
|
||||
)
|
||||
|
||||
if(currSize.width + deltaWidth < 200.dp.toPx()) {
|
||||
deltaWidth = deltaWidth.coerceAtLeast(200.dp.toPx() - currSize.width)
|
||||
deltaX = 0.0f
|
||||
result = false
|
||||
}
|
||||
|
||||
if(currSize.height + deltaHeight < 160.dp.toPx()) {
|
||||
deltaHeight = deltaHeight.coerceAtLeast(160.dp.toPx() - currSize.height)
|
||||
deltaY = 0.0f
|
||||
result = false
|
||||
}
|
||||
|
||||
latinIME.sizingCalculator.editSavedSettings { settings ->
|
||||
settings.copy(
|
||||
floatingBottomOriginDp = Pair(
|
||||
settings.floatingBottomOriginDp.first + deltaX.toDp().value,
|
||||
settings.floatingBottomOriginDp.second + deltaY.toDp().value
|
||||
),
|
||||
floatingWidthDp = settings.floatingWidthDp + deltaWidth.toDp().value,
|
||||
floatingHeightDp = settings.floatingHeightDp + deltaHeight.toDp().value
|
||||
)
|
||||
}
|
||||
|
||||
result
|
||||
} }
|
||||
|
||||
OffsetPositioner(offset.value) {
|
||||
KeyboardSurface(
|
||||
requiredWidthPx = size.width,
|
||||
@ -778,9 +839,18 @@ class UixManager(private val latinIME: LatinIME) {
|
||||
)
|
||||
}
|
||||
},
|
||||
onResizerOpen = {
|
||||
resizing.value = true
|
||||
},
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
if(resizing.value) {
|
||||
ResizerRect(onDragDelta, showResetApply = true, onApply = {
|
||||
resizing.value = false
|
||||
}, onReset = { })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user