Runt
2025-12-12 53f2a4266f3e42296bdcc0f0c8365b2cd4a152b1
libmvi/src/main/java/com/runt/open/mvi/views/PublicViews.kt
@@ -1,6 +1,8 @@
package com.runt.open.mvi.views
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@@ -9,13 +11,17 @@
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
@@ -24,12 +30,19 @@
import androidx.compose.material3.IconButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Constraints
@@ -37,6 +50,8 @@
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.Popup
import com.runt.open.mvi.OpenApplication
import com.runt.open.mvi.data.InputMessageState
import com.runt.open.mvi.data.LoadingState
import com.runt.open.mvi.data.MessageState
import com.runt.open.mvi.data.PopupMessage
@@ -57,10 +72,7 @@
) {
    Layout(content, modifier) { measurables, parent ->
        // 给子树宽高用"无限"上限测量
        val loose = Constraints(
            minWidth = 0, minHeight = 0,
            maxWidth = Int.MAX_VALUE, maxHeight = Int.MAX_VALUE
        )
        val loose = Constraints(minWidth = 0, minHeight = 0, maxWidth = Int.MAX_VALUE, maxHeight = Int.MAX_VALUE)
        val placeables = measurables.map { it.measure(loose) }
        // 自己仍按父约束的大小布局(不撑爆父)
@@ -78,20 +90,13 @@
    if (loadingState.isVisible) {
        Dialog(onDismissRequest = {}) {
            Column(
                modifier = Modifier
                    .size(140.dp)
                    .clip(RoundedCornerShape(16.dp))
                    .background(Color.White),
                modifier = Modifier.size(140.dp).clip(RoundedCornerShape(16.dp)).background(Color.White),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                CircularProgressIndicator()
                Spacer(modifier = Modifier.height(12.dp))
                Text(
                    text = loadingState.message,
                    fontSize = 14.sp,
                    color = Color.Black
                )
                Text(text = loadingState.message, fontSize = 14.sp, color = Color.Black)
            }
        }
    }
@@ -109,33 +114,23 @@
            }
        }) {
            Card(
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight()
                    .padding(16.dp),
                modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(16.dp),
                shape = RoundedCornerShape(16.dp),
            ) {
                Column(modifier = Modifier.wrapContentHeight()){
                    Text(text = message.title, fontSize = 16.sp,
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(top = 20.dp , start = 20.dp)
                            .wrapContentSize(Alignment.Center),
                        modifier = Modifier.fillMaxWidth().padding(top = 20.dp , start = 20.dp).wrapContentSize(Alignment.Center),
                        textAlign = TextAlign.Center)
                    Text(
                        text = message.message,
                        modifier = Modifier
                            .fillMaxWidth()
                            .wrapContentSize()
                            .padding(top = 30.dp , bottom = 20.dp , start = 14.dp , end = 15.dp)
                            .wrapContentSize(Alignment.Center),
                        modifier = Modifier.fillMaxWidth().wrapContentSize().padding(top = 30.dp , bottom = 20.dp , start = 14.dp , end = 15.dp).wrapContentSize(Alignment.Center),
                        textAlign = TextAlign.Center,
                    )
                    Row (modifier = Modifier.padding(start = 15.dp, end = 15.dp, bottom = 20.dp)){
                        if(!message.cancelText.equals("")){
                            Spacer(modifier = Modifier.weight(1f))
                            Button(onClick = {
                                if(message.cancelDissmiss){
                                if(message.cancelDismiss){
                                    message.setDismiss.invoke()
                                    message.onDismissRequest.invoke()
                                }
@@ -151,7 +146,7 @@
                        }
                        Spacer(modifier = Modifier.weight(1f))
                        Button(onClick = {
                            if(message.confirmDissmiss){
                            if(message.confirmDismiss){
                                message.setDismiss.invoke()
                                message.onDismissRequest.invoke()
                            }
@@ -166,6 +161,80 @@
    }
}
@Composable
fun InputDialog(message : InputMessageState) {
    if(!message.isVisible){
        return
    }
    var text by remember {
        mutableStateOf(if(TextUtils.isEmpty(message.message)) message.hint else message.message)
    }
    var modifier : Modifier;
    var keyboardType = message.inputType;
    if (message.maxLines == 1) {
        modifier = Modifier.width(260.dp).height(50.dp).padding(0.dp)
    } else {
        modifier = Modifier.width(260.dp).padding(0.dp).heightIn(min = 50.dp, max = 200.dp)
    }
    AlertDialog(onDismissRequest = {
            if(message.touchOutside){
                message.setDismiss.invoke()
                message.onDismissRequest.invoke()
            }
        } , confirmButton = {
        TextButton(onClick = {
            if (message.minLength > 0 || message.maxLength > 0  || !TextUtils.isEmpty(message.regex)) {
                var flag = true;
                if(text.length < message.minLength){
                    flag = false;
                }
                if(text.length > message.maxLength && message.maxLength > 0){
                    flag = false;
                }
                if (!message.regex.isEmpty() && !message.regex.toRegex().matches(text)) {
                    flag = false;
                }
                if(flag){
                    if(message.confirmDismiss){
                        message.setDismiss.invoke()
                        message.onDismissRequest.invoke()
                    }
                    message.onConfirmRequest(text)
                } else {
                    Toast.makeText(OpenApplication.getApplication() , message.hint , Toast.LENGTH_SHORT).show();
                }
            } else {
                if(message.confirmDismiss){
                    message.setDismiss.invoke()
                    message.onDismissRequest.invoke()
                }
                message.onConfirmRequest(text)
            }
        }) {
            Text(text = message.confirmText)
        }
    } , dismissButton = {
        TextButton(onClick = {
            if(message.cancelDismiss){
                message.setDismiss.invoke()
                message.onDismissRequest.invoke()
            }
            message.onCancelRequest.invoke()
        }) {
            Text(text = message.cancelText)
        }
    } , title = {
        Text(text = message.title , fontSize = 16.sp)
    } , text = {
        TextField(value = text , onValueChange = { //val pattern = """(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]""".toRegex()
            text = it;
        } , singleLine = message.maxLines == 1 , maxLines = message.maxLines, modifier = modifier , keyboardOptions = KeyboardOptions(keyboardType = keyboardType) , placeholder = {
            Text(text = message.hint , fontSize = 14.sp)
        } , textStyle = TextStyle(fontSize = 14.sp , color = Color.Black))
    })
}
@Composable
fun PopupWindow(popupMessage : PopupMessage){
    if(popupMessage.isVisible){
@@ -173,10 +242,7 @@
            alignment = Alignment.TopCenter,
        ) {
            Surface(
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight()
                    .padding(10.dp),
                modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(10.dp),
                shadowElevation = 2.dp,
                border = BorderStroke(1.dp, Color.Gray) ,
                shape = RoundedCornerShape(8.dp)
@@ -197,18 +263,13 @@
@Composable
fun TitleBarView(title:String,onBackClick:()->Unit){
    Row(modifier = Modifier
        .wrapContentSize(Alignment.Center)
        .height(50.dp), verticalAlignment = Alignment.CenterVertically) {
    Row(modifier = Modifier.wrapContentSize(Alignment.Center).height(50.dp), verticalAlignment = Alignment.CenterVertically) {
        Spacer(modifier = Modifier.size(15.dp))
        IconButton(onClick = onBackClick, modifier = Modifier.size(30.dp,30.dp)) {
            Icon(Icons.Default.ArrowBack, contentDescription = null, tint = Color.Black)
        }
        Text(text = "${title}", modifier = Modifier
            .weight(1f)
            .fillMaxWidth()
            .wrapContentSize(Alignment.Center))
        Text(text = "${title}", modifier = Modifier.weight(1f).fillMaxWidth().wrapContentSize(Alignment.Center))
        Spacer(modifier = Modifier.size(30.dp))
        Spacer(modifier = Modifier.size(15.dp))
    }