From 4bfc04cc7dc5f5957e419ace67cdaca006a927f4 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Wed, 11 Nov 2026 10:59:15 +0800 Subject: [PATCH] Fit Android code language labels in their compact header --- .../logseq/AndroidEditorCompletionTest.kt | 29 +++++++++++++++++++ shared/src/logseq/view_outliner.ml | 22 +++++++++----- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/android/app/src/androidTest/kotlin/com/logseq/logseq/AndroidEditorCompletionTest.kt b/android/app/src/androidTest/kotlin/com/logseq/logseq/AndroidEditorCompletionTest.kt index e3b879f9..fa8742f6 100644 --- a/android/app/src/androidTest/kotlin/com/logseq/logseq/AndroidEditorCompletionTest.kt +++ b/android/app/src/androidTest/kotlin/com/logseq/logseq/AndroidEditorCompletionTest.kt @@ -16,6 +16,12 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.performScrollTo +import androidx.compose.ui.test.performSemanticsAction +import androidx.compose.ui.test.getUnclippedBoundsInRoot +import androidx.compose.ui.semantics.SemanticsActions +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.logseq.logseq.runtime.LogseqNativeBridge @@ -79,6 +85,29 @@ class AndroidEditorCompletionTest { ui.onNodeWithTag("outliner.code-editor.editing").assertIsDisplayed() } + @Test fun codeLanguageLabelsFitInsideACompactHeader() { + start("const answer = 42", null) + for (language in listOf("", "typescript", "custom-language-name")) { + val response = JSONObject(snapshot(null, "const answer = 42")) + response.getJSONObject("result").getJSONArray("outlinerRows").getJSONObject(0) + .getJSONObject("block").put("displayType", "code") + .put("markup", org.json.JSONArray().put(JSONObject() + .put("type", "codeBlock").put("style", language).put("text", "const answer = 42"))) + ui.runOnIdle { bridge.applySnapshot(response.toString()).takeIf { it.isNotEmpty() }?.let(filter::applyJson) } + val label = ui.onNodeWithText(if (language.isEmpty()) "Plain text" else language, useUnmergedTree = true) + label.assertIsDisplayed() + val layouts = mutableListOf() + label.performSemanticsAction(SemanticsActions.GetTextLayoutResult) { it(layouts) } + val layout = layouts.single() + assertTrue("The whole language name fits horizontally", !layout.didOverflowWidth) + assertTrue("The language name fits vertically", !layout.didOverflowHeight) + val header = ui.onNodeWithTag("outliner.code.language-header.editing").getUnclippedBoundsInRoot() + assertTrue("The header stays compact", header.bottom - header.top <= 32.dp) + } + ui.onNodeWithTag("button.outliner.code-language.editing").performClick() + ui.onNodeWithTag("button.outliner.code-language.option.python").performScrollTo().assertIsDisplayed().performClick() + } + @Test fun commandsReferencesAndTagsUseOneNativePanel() { start("/", "command") for ((kind, title) in listOf("command" to "/", "node" to "[[Pro", "tag" to "#Pro")) { diff --git a/shared/src/logseq/view_outliner.ml b/shared/src/logseq/view_outliner.ml index 3dfeffdc..fa893bdf 100644 --- a/shared/src/logseq/view_outliner.ml +++ b/shared/src/logseq/view_outliner.ml @@ -352,12 +352,11 @@ let outliner_priority_control model_source row_source send : t = ~accessibility_identifier:"button.block-task-priority-clear" ~on_press:(fun _ -> choose "") [] ]]))] -let code_language_control model_source row_source send : t = - let current_row = Signal.sample row_source in - if_ ~test:(Signal.map (fun (row : Model.outline_row) -> row.row_display_type = Some "code") row_source) - (row ~main:`start ~padding_horizontal:12 ~padding_vertical:2 - ~accessibility_identifier:("outliner.code.language-header." ^ current_row.row_uuid) [ - box ~height:28 [menu_trigger ~style_class:"caption" ~foreground:"muted-foreground" ~text_signal:(Signal.map (fun row -> let value = code_language row in if value = "" then "Plain text" else value) row_source) +let code_language_control context model_source row_source send : t = + let current_row = (Signal.sample row_source : Model.outline_row) in + let trigger = + menu_trigger ~style_class:"caption" ~foreground:"muted-foreground" + ~text_signal:(Signal.map (fun row -> let value = code_language row in if value = "" then "Plain text" else value) row_source) ~label:(View_base.tr (Signal.sample model_source) "Code language") ~accessibility_identifier:("button.outliner.code-language." ^ current_row.row_uuid) [dropdown_menu (List.map (fun (value, label) -> @@ -370,7 +369,14 @@ let code_language_control model_source row_source send : t = "css", "CSS"; "go", "Go"; "html", "HTML"; "java", "Java"; "javascript", "JavaScript"; "json", "JSON"; "kotlin", "Kotlin"; "markdown", "Markdown"; "ocaml", "OCaml"; "python", "Python"; "ruby", "Ruby"; "rust", "Rust"; "sql", "SQL"; "swift", "Swift"; - "typescript", "TypeScript"; "yaml", "YAML"; "calc", "Calculator"]) ] ] ]) + "typescript", "TypeScript"; "yaml", "YAML"; "calc", "Calculator"]) ] in + let trigger = if Lui_ui.host context = KotlinHost then + View_base.with_string_prop TextAlignment "start" trigger + else trigger in + if_ ~test:(Signal.map (fun (row : Model.outline_row) -> row.row_display_type = Some "code") row_source) + (row ~main:`start ~padding_horizontal:12 ~padding_vertical:2 + ~accessibility_identifier:("outliner.code.language-header." ^ current_row.row_uuid) + [box ~height:28 [trigger]]) let code_block_content model_source retained_source row_source send : t = fun context parent -> @@ -402,7 +408,7 @@ let code_block_content model_source retained_source row_source send : t = ~background_signal:(Signal.map (fun retained -> if View_rows.retained_row_selected_ retained then "#00000000" else "code-container") retained_source) ~accessibility_identifier:("outliner.code." ^ current_row.Model.row_uuid) [ - code_language_control model_source row_source send; + code_language_control context model_source row_source send; dyn (fun calculator -> if not calculator then input else row ~gap:0 ~cross:`start ~accessibility_identifier:("outliner.calculator." ^ current_row.row_uuid) [box ~grow:1.0 [input];