1818
1919package com.movtery.zalithlauncher.ui.screens.game.elements
2020
21+ import androidx.compose.foundation.layout.Arrangement
22+ import androidx.compose.foundation.layout.Column
23+ import androidx.compose.foundation.layout.Row
24+ import androidx.compose.foundation.layout.fillMaxHeight
2125import androidx.compose.foundation.layout.fillMaxSize
26+ import androidx.compose.foundation.layout.padding
27+ import androidx.compose.foundation.layout.size
2228import androidx.compose.foundation.lazy.LazyColumn
2329import androidx.compose.foundation.lazy.items
2430import androidx.compose.foundation.lazy.rememberLazyListState
31+ import androidx.compose.foundation.rememberScrollState
32+ import androidx.compose.foundation.verticalScroll
33+ import androidx.compose.material3.Icon
34+ import androidx.compose.material3.IconButton
35+ import androidx.compose.material3.IconButtonDefaults
36+ import androidx.compose.material3.MaterialTheme
2537import androidx.compose.material3.Surface
2638import androidx.compose.material3.Text
39+ import androidx.compose.material3.VerticalDivider
2740import androidx.compose.runtime.Composable
2841import androidx.compose.runtime.LaunchedEffect
42+ import androidx.compose.runtime.getValue
2943import androidx.compose.runtime.mutableStateListOf
44+ import androidx.compose.runtime.mutableStateOf
3045import androidx.compose.runtime.remember
46+ import androidx.compose.runtime.setValue
3147import androidx.compose.ui.Modifier
3248import androidx.compose.ui.graphics.Color
49+ import androidx.compose.ui.res.painterResource
3350import androidx.compose.ui.text.AnnotatedString
3451import androidx.compose.ui.unit.TextUnit
3552import androidx.compose.ui.unit.TextUnitType
53+ import androidx.compose.ui.unit.dp
54+ import com.movtery.zalithlauncher.R
3655import com.movtery.zalithlauncher.bridge.LoggerBridge
3756import com.movtery.zalithlauncher.setting.AllSettings
3857import com.movtery.zalithlauncher.ui.screens.game.elements.log_parser.LogHighlighter
@@ -52,14 +71,17 @@ import java.util.Collections
5271@Composable
5372fun LogBox (
5473 enableLog : Boolean ,
74+ onClose : () -> Unit ,
5575 modifier : Modifier = Modifier
5676) {
5777 val scrollState = rememberLazyListState()
5878
5979 val logList = remember { mutableStateListOf<AnnotatedString >() }
6080 val buffer = remember { Collections .synchronizedList(mutableListOf<AnnotatedString >()) }
81+ val scrollChannel = remember { mutableStateOf<Channel <Unit >? > (null ) }
6182
6283 val logHighlighter = remember { LogHighlighter () }
84+ var autoScrollDown by remember { mutableStateOf(true ) }
6385
6486 val config = remember {
6587 object {
@@ -70,7 +92,7 @@ fun LogBox(
7092
7193 LaunchedEffect (enableLog) {
7294 if (enableLog) {
73- val scrollChannel = Channel < Unit > (capacity = 100 )
95+ scrollChannel.value = Channel (capacity = 100 )
7496
7597 LoggerBridge .setListener { log ->
7698 synchronized(buffer) {
@@ -98,8 +120,10 @@ fun LogBox(
98120 if (pending.isNotEmpty()) {
99121 withContext(Dispatchers .Main ) {
100122 logList.addAll(pending)
101- // 尝试进行滚动
102- scrollChannel.trySend(Unit )
123+ if (autoScrollDown) {
124+ // 尝试进行滚动
125+ scrollChannel.value?.trySend(Unit )
126+ }
103127 }
104128 }
105129 } catch (_: CancellationException ) {
@@ -110,7 +134,7 @@ fun LogBox(
110134
111135 // 自动滚动部分
112136 launch(Dispatchers .Main ) {
113- scrollChannel.consumeAsFlow().collect {
137+ scrollChannel.value?. consumeAsFlow()? .collect {
114138 runCatching {
115139 val targetIndex = logList.lastIndex
116140 if (targetIndex >= 0 && targetIndex < logList.size) {
@@ -120,40 +144,143 @@ fun LogBox(
120144 }
121145 }
122146 } else {
147+ scrollChannel.value = null
123148 LoggerBridge .setListener(null )
124149 logList.clear()
125150 buffer.clear()
126151 }
127152 }
128153
129154 if (enableLog) {
130- Surface (
131- modifier = modifier.fillMaxSize(),
132- color = Color .Black .copy(alpha = 0.5f ),
133- contentColor = Color .White
155+ Row (
156+ modifier = modifier.fillMaxSize()
134157 ) {
135- LazyColumn (
136- modifier = Modifier .fillMaxSize(),
137- state = scrollState
158+ Surface (
159+ modifier = modifier
160+ .weight(1f )
161+ .fillMaxHeight(),
162+ color = Color .Black .copy(alpha = 0.5f ),
163+ contentColor = Color .White
138164 ) {
139- items(logList) { log ->
140- val textSize = AllSettings .logTextSize.state
141- val fontSize = remember(textSize) {
142- TextUnit (textSize.toFloat(), TextUnitType .Sp )
143- }
144- val lineHeight = remember(textSize) {
145- val height = textSize.toFloat() * 1.1f
146- TextUnit (height, TextUnitType .Sp )
165+ LazyColumn (
166+ modifier = Modifier .fillMaxSize(),
167+ state = scrollState
168+ ) {
169+ items(logList) { log ->
170+ val textSize = AllSettings .logTextSize.state
171+ val fontSize = remember(textSize) {
172+ TextUnit (textSize.toFloat(), TextUnitType .Sp )
173+ }
174+ val lineHeight = remember(textSize) {
175+ val height = textSize.toFloat() * 1.1f
176+ TextUnit (height, TextUnitType .Sp )
177+ }
178+
179+ Text (
180+ text = log,
181+ modifier = Modifier .fillParentMaxWidth(),
182+ fontSize = fontSize,
183+ lineHeight = lineHeight
184+ )
147185 }
186+ }
187+ }
148188
149- Text (
150- text = log,
151- modifier = Modifier .fillParentMaxWidth(),
152- fontSize = fontSize,
153- lineHeight = lineHeight
154- )
189+ // 右侧控制区域
190+ VerticalDivider (
191+ modifier = Modifier .fillMaxHeight(),
192+ color = Color .White .copy(0.4f )
193+ )
194+ Surface (
195+ modifier = Modifier .fillMaxHeight(),
196+ color = Color .Black .copy(alpha = 0.7f ),
197+ contentColor = Color .White
198+ ) {
199+ Column (
200+ modifier = Modifier
201+ .padding(horizontal = 4 .dp, vertical = 12 .dp)
202+ .verticalScroll(rememberScrollState()),
203+ verticalArrangement = Arrangement .spacedBy(4 .dp)
204+ ) {
205+ // 关闭
206+ LogBoxIconButton (
207+ onClick = onClose,
208+ toggle = false
209+ ) {
210+ Icon (
211+ modifier = Modifier .size(18 .dp),
212+ painter = painterResource(R .drawable.ic_close),
213+ contentDescription = null
214+ )
215+ }
216+ // 清理
217+ LogBoxIconButton (
218+ onClick = {
219+ synchronized(buffer) {
220+ logList.clear()
221+ buffer.clear()
222+ }
223+ },
224+ toggle = false
225+ ) {
226+ Icon (
227+ modifier = Modifier .size(18 .dp),
228+ painter = painterResource(R .drawable.ic_delete_outlined),
229+ contentDescription = null
230+ )
231+ }
232+ // 自动滚动
233+ LogBoxIconButton (
234+ onClick = {
235+ val value = ! autoScrollDown
236+ autoScrollDown = value
237+ if (value) {
238+ scrollChannel.value?.trySend(Unit )
239+ }
240+ },
241+ toggle = autoScrollDown,
242+ ) {
243+ Icon (
244+ modifier = Modifier .size(18 .dp),
245+ painter = painterResource(R .drawable.ic_list_down),
246+ contentDescription = null
247+ )
248+ }
249+ // 滚动到底部
250+ LogBoxIconButton (
251+ onClick = {
252+ scrollChannel.value?.trySend(Unit )
253+ },
254+ toggle = false
255+ ) {
256+ Icon (
257+ modifier = Modifier .size(18 .dp),
258+ painter = painterResource(R .drawable.ic_arrow_cool_down),
259+ contentDescription = null
260+ )
261+ }
155262 }
156263 }
157264 }
158265 }
266+ }
267+
268+ @Composable
269+ private fun LogBoxIconButton (
270+ onClick : () -> Unit ,
271+ toggle : Boolean ,
272+ icon : @Composable (() -> Unit )
273+ ) {
274+ IconButton (
275+ onClick = onClick,
276+ colors = IconButtonDefaults .iconButtonColors(
277+ containerColor = if (toggle) {
278+ Color .White .copy(0.4f )
279+ } else {
280+ Color .Transparent
281+ }
282+ ),
283+ shape = MaterialTheme .shapes.medium,
284+ content = icon
285+ )
159286}
0 commit comments