@@ -4,6 +4,7 @@ import androidx.compose.foundation.background
44import androidx.compose.foundation.horizontalScroll
55import androidx.compose.foundation.layout.*
66import androidx.compose.foundation.rememberScrollState
7+ import androidx.compose.foundation.rememberScrollbarAdapter
78import androidx.compose.foundation.text.selection.SelectionContainer
89import androidx.compose.foundation.verticalScroll
910import androidx.compose.material.icons.Icons
@@ -16,9 +17,6 @@ import androidx.compose.ui.Modifier
1617import androidx.compose.ui.graphics.Color
1718import androidx.compose.ui.platform.ClipEntry
1819import androidx.compose.ui.platform.LocalClipboard
19- import androidx.compose.ui.text.AnnotatedString
20- import androidx.compose.ui.text.SpanStyle
21- import androidx.compose.ui.text.buildAnnotatedString
2220import androidx.compose.ui.text.font.FontFamily
2321import androidx.compose.ui.text.font.FontWeight
2422import androidx.compose.ui.text.style.TextAlign
@@ -209,24 +207,26 @@ private fun FileHeader(content: FileContent) {
209207 */
210208@Composable
211209private fun FileContentBody (content : FileContent ) {
212- val horizontalScrollState = rememberScrollState()
213210 val verticalScrollState = rememberScrollState()
211+ val horizontalScrollState = rememberScrollState()
214212
215213 Box (
216214 modifier = Modifier
217215 .fillMaxSize()
218216 .background(MaterialTheme .colorScheme.surfaceVariant.copy(alpha = 0.1f ))
219217 ) {
220- // 内容区域
221218 Row (
222219 modifier = Modifier
223220 .fillMaxSize()
224- .horizontalScroll(horizontalScrollState)
225- .verticalScroll(verticalScrollState)
226221 .padding(8 .dp)
227222 ) {
228- // 行号列
229- LineNumbers (content = content.content)
223+ // 行号列 - 不参与横向滚动,只跟随纵向滚动
224+ Box (
225+ modifier = Modifier
226+ .verticalScroll(verticalScrollState)
227+ ) {
228+ LineNumbers (content = content.content)
229+ }
230230
231231 // 分隔线
232232 VerticalDivider (
@@ -236,12 +236,20 @@ private fun FileContentBody(content: FileContent) {
236236 color = MaterialTheme .colorScheme.outlineVariant.copy(alpha = 0.3f )
237237 )
238238
239- // 代码内容
240- CodeContent (
241- content = content.content,
242- mimeType = content.mimeType
243- )
239+ // 代码内容区域 - 支持独立的横向和纵向滚动
240+ Box (
241+ modifier = Modifier
242+ .fillMaxSize()
243+ .horizontalScroll(horizontalScrollState)
244+ .verticalScroll(verticalScrollState)
245+ ) {
246+ CodeContent (
247+ content = content.content,
248+ mimeType = content.mimeType
249+ )
250+ }
244251 }
252+
245253 }
246254}
247255
@@ -286,13 +294,9 @@ private fun CodeContent(content: String, mimeType: String) {
286294 Font (Res .font.JetBrainsMono_Medium , FontWeight .Medium )
287295 )
288296
289- val highlightedText = remember(content, mimeType) {
290- highlightCode(content, mimeType)
291- }
292-
293297 SelectionContainer {
294298 Text (
295- text = highlightedText ,
299+ text = content ,
296300 style = MaterialTheme .typography.bodySmall.copy(
297301 fontFamily = jetBrainsMonoFontFamily,
298302 fontSize = 16 .sp,
@@ -303,163 +307,6 @@ private fun CodeContent(content: String, mimeType: String) {
303307 }
304308}
305309
306- /* *
307- * 简单的代码高亮
308- */
309- private fun highlightCode (content : String , mimeType : String ): AnnotatedString {
310- return buildAnnotatedString {
311- append(content)
312-
313- // 根据 MIME 类型应用不同的高亮规则
314- when (mimeType) {
315- " text/x-kotlin" -> applyKotlinHighlight(this , content)
316- " text/x-java" -> applyJavaHighlight(this , content)
317- " application/xml" -> applyXmlHighlight(this , content)
318- " application/json" -> applyJsonHighlight(this , content)
319- else -> applyGenericHighlight(this , content)
320- }
321- }
322- }
323-
324- /* *
325- * Kotlin 语法高亮
326- */
327- private fun applyKotlinHighlight (builder : AnnotatedString .Builder , content : String ) {
328- val keywords = setOf (
329- " class" , " interface" , " fun" , " val" , " var" , " if" , " else" , " when" , " for" ,
330- " while" , " do" , " try" , " catch" , " finally" , " return" , " break" , " continue" ,
331- " object" , " companion" , " data" , " sealed" , " enum" , " annotation" , " suspend" ,
332- " import" , " package" , " private" , " public" , " protected" , " internal"
333- )
334-
335- highlightKeywords(builder, content, keywords, Color (0xFF0000FF )) // 蓝色关键字
336- highlightStrings(builder, content, Color (0xFF008000 )) // 绿色字符串
337- highlightComments(builder, content, Color (0xFF808080 )) // 灰色注释
338- }
339-
340- /* *
341- * Java 语法高亮
342- */
343- private fun applyJavaHighlight (builder : AnnotatedString .Builder , content : String ) {
344- val keywords = setOf (
345- " class" , " interface" , " public" , " private" , " protected" , " static" , " final" ,
346- " abstract" , " synchronized" , " volatile" , " transient" , " native" , " strictfp" ,
347- " if" , " else" , " switch" , " case" , " default" , " for" , " while" , " do" , " try" ,
348- " catch" , " finally" , " throw" , " throws" , " return" , " break" , " continue" ,
349- " import" , " package" , " extends" , " implements" , " super" , " this" , " new"
350- )
351-
352- highlightKeywords(builder, content, keywords, Color (0xFF0000FF ))
353- highlightStrings(builder, content, Color (0xFF008000 ))
354- highlightComments(builder, content, Color (0xFF808080 ))
355- }
356-
357- /* *
358- * XML 语法高亮
359- */
360- private fun applyXmlHighlight (builder : AnnotatedString .Builder , content : String ) {
361- // 简单的 XML 标签高亮
362- val tagRegex = Regex (" <[^>]+>" )
363- tagRegex.findAll(content).forEach { match ->
364- builder.addStyle(
365- style = SpanStyle (color = Color (0xFF0000FF )),
366- start = match.range.first,
367- end = match.range.last + 1
368- )
369- }
370- }
371-
372- /* *
373- * JSON 语法高亮
374- */
375- private fun applyJsonHighlight (builder : AnnotatedString .Builder , content : String ) {
376- // 字符串
377- val stringRegex = Regex (" \" [^\" ]*\" " )
378- stringRegex.findAll(content).forEach { match ->
379- builder.addStyle(
380- style = SpanStyle (color = Color (0xFF008000 )),
381- start = match.range.first,
382- end = match.range.last + 1
383- )
384- }
385-
386- // 数字
387- val numberRegex = Regex (" \\ b\\ d+(\\ .\\ d+)?\\ b" )
388- numberRegex.findAll(content).forEach { match ->
389- builder.addStyle(
390- style = SpanStyle (color = Color (0xFFFF0000 )),
391- start = match.range.first,
392- end = match.range.last + 1
393- )
394- }
395- }
396-
397- /* *
398- * 通用高亮
399- */
400- private fun applyGenericHighlight (builder : AnnotatedString .Builder , content : String ) {
401- // 只高亮字符串和注释
402- highlightStrings(builder, content, Color (0xFF008000 ))
403- highlightComments(builder, content, Color (0xFF808080 ))
404- }
405-
406- /* *
407- * 关键字高亮
408- */
409- private fun highlightKeywords (
410- builder : AnnotatedString .Builder ,
411- content : String ,
412- keywords : Set <String >,
413- color : Color
414- ) {
415- keywords.forEach { keyword ->
416- val regex = Regex (" \\ b$keyword \\ b" )
417- regex.findAll(content).forEach { match ->
418- builder.addStyle(
419- style = SpanStyle (color = color, fontWeight = FontWeight .Bold ),
420- start = match.range.first,
421- end = match.range.last + 1
422- )
423- }
424- }
425- }
426-
427- /* *
428- * 字符串高亮
429- */
430- private fun highlightStrings (builder : AnnotatedString .Builder , content : String , color : Color ) {
431- val stringRegex = Regex (" \" [^\" ]*\" |'[^']*'" )
432- stringRegex.findAll(content).forEach { match ->
433- builder.addStyle(
434- style = SpanStyle (color = color),
435- start = match.range.first,
436- end = match.range.last + 1
437- )
438- }
439- }
440-
441- /* *
442- * 注释高亮
443- */
444- private fun highlightComments (builder : AnnotatedString .Builder , content : String , color : Color ) {
445- val lineCommentRegex = Regex (" //.*$" , RegexOption .MULTILINE )
446- lineCommentRegex.findAll(content).forEach { match ->
447- builder.addStyle(
448- style = SpanStyle (color = color),
449- start = match.range.first,
450- end = match.range.last + 1
451- )
452- }
453-
454- val blockCommentRegex = Regex (" /\\ *.*?\\ */" , RegexOption .DOT_MATCHES_ALL )
455- blockCommentRegex.findAll(content).forEach { match ->
456- builder.addStyle(
457- style = SpanStyle (color = color),
458- start = match.range.first,
459- end = match.range.last + 1
460- )
461- }
462- }
463310
464311/* *
465312 * 格式化文件大小
0 commit comments