元素

文字选择

设置了 selectable 的 View 让其中的文字一起选择,就像网页一样:一个选区可以从一段文字中的某处延伸到另一段文字中的某处,按阅读顺序跨越视图、Text 元素和 TextView。让聊天记录可选择后,选区可以从一条消息开始、在下一条结束;复制会把选中的文字放到剪贴板,并排的文字用制表符连接,其他用换行连接。它和 Text 的 selectable 是同一个属性:Rust 用 view().selectable(true) 设置,TSX 用 <View selectable>。

el-text-selection.tsx
el-text-selection.tsx运行中
import { mount, Scroll, signal, Text, View } from "nsh";
import { Input } from "nsh/base";
import { TextView } from "nsh/base";
import { platform } from "nsh";
import { field, ui } from "./palette";

const NOTES = "- Sync runs **in the background**\n- Conflicts merge *per field*";

function App() {
  const pasted = signal("");
  return (
    <Scroll style={{ flex: 1, backgroundColor: ui().page }}>
      <View style={{ width: "100%", maxWidth: 560, alignSelf: "center", padding: 20, paddingTop: 24, gap: 14 }}>
        <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>selectable view</Text>
        <Text style={{ color: ui().muted, lineHeight: 20 }}>
          {platform.idiom === "desktop"
            ? "Drag with the mouse from the title into the list: one selection runs across them. Double click a word, triple click a paragraph, Ctrl / Cmd+C copies."
            : "Long press a word, then drag the handles across the texts: one selection runs across them. Copy is in the menu."}
        </Text>

        <View selectable style={{ ...ui().card, borderRadius: 16, padding: 16, gap: 10 }}>
          <Text style={{ fontSize: 17, fontWeight: "600", color: ui().text }}>Release notes</Text>
          <Text style={{ color: ui().text, lineHeight: 22 }}>Version 2.4 moves the app to offline sync.</Text>
          <TextView markdown={NOTES} />
          <View style={{ flexDirection: "row", gap: 8 }}>
            <Text selectable={false} style={{ color: ui().faint }}>
              Updated (selectable=false)
            </Text>
            <Text style={{ color: ui().text }}>October 21</Text>
          </View>
        </View>

        <Text style={{ fontSize: 12, color: ui().muted }}>Paste what you copied: the label marked selectable=false stays out of it.</Text>
        <Input
          multiline
          value={pasted()}
          onChange={(text) => pasted.set(text)}
          placeholder="Paste here"
          {...field({ style: { height: 110 } })}
        />
        <Text style={{ fontSize: 12, color: ui().muted, fontVariant: ["tabular-nums"] }}>
          {pasted().length} characters, {pasted() ? pasted().split("\n").length : 0} lines
        </Text>
      </View>
    </Scroll>
  );
}

mount(App);
use nsh_base::text_view;
use nsh_core::prelude::*;

fn release_notes(notes: &'static str) -> impl IntoElement {
    view()
        .selectable(true)
        .gap_3()
        .p_4()
        .child(text("Release notes").font_weight(FontWeight::SEMIBOLD))
        .child(text_view().markdown(notes))
        .child(
            view()
                .flex_row()
                .gap_2()
                .child(text("Updated").selectable(false).text_color(rgb(0x737373)))
                .child(text("October 21")),
        )
}

视图的布局和外观是应用的样式,内容是它的子元素。选区本身是平台的:视图不为它绘制任何东西。

#聊天记录

放在一个可选择视图中的聊天记录像一个页面一样选择:拖动从一条回复开始、在另一条结束,复制会取得其间每条消息的文字。

use nsh_base::text_view;
use nsh_core::prelude::*;

fn transcript(replies: Signal<Vec<SharedString>>) -> impl IntoElement {
    scroll().flex_1().child(
        view().selectable(true).p_4().gap_4().child(each(
            replies,
            |reply: &SharedString| reply.clone(),
            |reply, _index| text_view().markdown(move || reply.get()),
        )),
    )
}

#如何选择

平台如何选择
macOS、Windows、Linux、GPUI在文字上拖动;双击选择一个词,三击选择一个段落,之后的拖动按词或段落继续。Shift+点击扩展选区。Ctrl / Cmd+A 选择视图中的全部内容,Ctrl / Cmd+C 复制,Escape 或在外部按下会清除选区。
iOS、Android长按选择一个词;平台的选择手柄可以扩展它,编辑菜单中有拷贝和全选。
Web浏览器自己的选择,以及它的手势、菜单和快捷键。

#选择的范围

  • 不参与的文字:设置了 selectable={false}(Rust 中为 .selectable(false);标签、按钮的文字)的文字会被选区及其手势跳过。
  • 嵌套的视图:一个可选择视图放在另一个之中时,属于外层视图,一个选区覆盖两者。
  • TextView:设置了 selectable 的 TextView 自己作为一体选择:它的段落、列表项、单元格和代码块一起选择。放在可选择视图中时,它加入外层的选区。
  • 单独的文字:不在任何可选择视图中的 selectable 文字单独选择,见 Text。
  • 外观是平台的高亮,颜色为每段文字的 selectionColor(selection:text-*)。
  • 列表:在虚拟化的 List 中,选区覆盖已挂载的行;回收到视野之外的行会离开选区。

#相关内容

  • View:selectable 属性。
  • Text:selectable、selectionColor 以及单段文字的选择。
  • TextView:各个块作为一体选择的文档。