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 import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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 import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon 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 import androidx.compose.ui.unit.dp 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 /** * @author Runt(qingingrunt2010@qq.com) * @purpose * @date 5/18/25 */ /** * 超出父容器 */ @Composable fun UnboundedBox( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout(content, modifier) { measurables, parent -> // 给子树宽高用"无限"上限测量 val loose = Constraints(minWidth = 0, minHeight = 0, maxWidth = Int.MAX_VALUE, maxHeight = Int.MAX_VALUE) val placeables = measurables.map { it.measure(loose) } // 自己仍按父约束的大小布局(不撑爆父) val w = parent.maxWidth val h = parent.maxHeight layout(w, h) { // 放在(0,0)。如需往外探出,可用负偏移:placeRelative(-50, 0) placeables.forEach { it.placeRelative(0, 0) } } } } @Composable fun LoadingDialog( loadingState : LoadingState) { if (loadingState.isVisible) { Dialog(onDismissRequest = {}) { Column( 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) } } } } @Composable fun MessageDialog(message : MessageState){ if(message.isVisible){ Dialog(onDismissRequest = { //系统响应 Log.i("PublicViews" , "MessageDialog: onDismiss") if(message.touchOutside){ message.setDismiss.invoke() message.onDismissRequest.invoke() } }) { Card( 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), 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), 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.cancelDismiss){ message.setDismiss.invoke() message.onDismissRequest.invoke() } message.onCancelRequest.invoke() }, colors = ButtonDefaults.buttonColors( containerColor = Color.Gray, // 背景色 contentColor = Color.White, // 文字/图标颜色 disabledContainerColor = Color.Gray // 禁用时背景色 ),) { Text(text = message.cancelText) } } Spacer(modifier = Modifier.weight(1f)) Button(onClick = { if(message.confirmDismiss){ message.setDismiss.invoke() message.onDismissRequest.invoke() } message.onConfirmRequest.invoke() }) { Text(text = message.confirmText) } } } } } } } @Composable fun InputDialog(message : InputMessageState) { if(!message.isVisible){ return } var text by remember { mutableStateOf(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 = if(message.maxLines < 1) Int.MAX_VALUE else 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){ Popup( alignment = Alignment.TopCenter, ) { Surface( modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(10.dp), shadowElevation = 2.dp, border = BorderStroke(1.dp, Color.Gray) , shape = RoundedCornerShape(8.dp) ) { Column( modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text(popupMessage.title) Spacer(modifier = Modifier.height(16.dp)) Text(popupMessage.message) Spacer(modifier = Modifier.height(15.dp)) } } } } } @Composable fun TitleBarView(title:String,onBackClick:()->Unit){ 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)) Spacer(modifier = Modifier.size(30.dp)) Spacer(modifier = Modifier.size(15.dp)) } } @Preview @Composable fun previewLayout(){ MessageDialog(message = MessageState(cancelText = "lll")) }