在使用 textarea 组件开发桌面应用时,常常会遇到无法通过 CSS 对输入内容进行部分高亮显示的问题。由于 textarea 本质上是一个纯文本输入框,它不支持在文本中嵌入 HTML 或应用局部样式,因此直接在 textarea 中实现类似网页搜索的高亮效果是不可能的。不过,我们可以通过一些替代方法来达到类似的效果。本文将详细介绍几种可行的解决方案,帮助你在 Dioxus 0.6 框架下实现搜索高亮功能。
contenteditable 的 div 替代 textarea由于 textarea 无法直接渲染部分样式,我们可以使用一个可编辑的 div 元素来替代它。通过设置 contenteditable="true",div 可以像 textarea 一样接受用户输入,同时支持 HTML 和 CSS 渲染,从而实现部分文本的高亮显示。
利用 div 的可编辑特性,我们可以在用户输入的文本中动态插入 <span> 标签,并应用 CSS 样式来高亮匹配的搜索词。这种方法允许我们对特定的文本片段进行样式化,从而实现搜索高亮效果。
div:
使用 contenteditable="true" 属性,使 div 可以接受用户输入。
通过 oninput 事件捕捉用户输入,并根据搜索关键词动态更新内容。
在用户输入后,使用 Rust 代码将匹配的搜索词包裹在 <span> 标签中,并应用高亮样式。
更新内容时,需要确保光标位置的稳定,避免用户体验受影响。
以下是一个使用 contenteditable 的 div 实现搜索高亮的完整示例:
use dioxus::prelude::*;
fn main() {
dioxus::desktop::launch(App);
}
fn App(cx: Scope) -> Element {
let text = use_state(cx, || String::new());
let search_query = use_state(cx, || String::new());
let highlighted_html = highlight_text(&text, &search_query);
cx.render(rsx! {
div {
style: "font-family: monospace; padding: 20px;",
input {
type: "text",
placeholder: "输入搜索关键词",
value: "{search_query}",
oninput: move |e| search_query.set(e.value.clone()),
style: "margin-bottom: 10px; width: 100%; padding: 8px;"
}
div {
contenteditable: "true",
oninput: move |e| text.set(e.value.clone()),
dangerously_set_inner_html: "{highlighted_html}",
style: "width: 100%; height: 200px; border: 1px solid #ccc; padding: 10px; overflow-y: auto;"
}
}
})
}
fn highlight_text(text: &str, query: &str) -> String {
if query.is_empty() {
return text.to_string();
}
let highlighted = text.replace(
query,
&format!("{}", query),
);
highlighted
}
.editable-area {
border: 1px solid #ccc;
padding: 10px;
min-height: 100px;
overflow-y: auto;
}
.highlight {
background-color: yellow;
color: black;
}
contenteditable 的行为可能有所不同,需进行兼容性测试。textarea 和覆盖层如果必须使用原生的 textarea,可以通过创建一个覆盖层的 div 来实现高亮效果。具体思路是,在 textarea 上方或下方放置一个透明的 textarea,并在其背后使用一个同步内容的 div 来显示高亮文本。
通过将一个 div 叠加在 textarea 上,并同步其内容,可以在 div 中实现高亮效果,而保持用户在 textarea 中的原生输入体验。这种方法将高亮和输入分离,既保留了 textarea 的功能,又实现了高亮效果。
div 和 textarea:
在同一个容器中放置一个 div 和一个透明的 textarea,使它们重叠显示。
监听 textarea 的输入事件,并将内容同步到覆盖层的 div 中。
在 div 中根据搜索关键词动态插入高亮样式。
use dioxus::prelude::*;
fn main() {
dioxus::desktop::launch(App);
}
fn App(cx: Scope) -> Element {
let text = use_state(cx, || String::new());
let search_query = use_state(cx, || String::new());
let highlighted_html = highlight_text(&text, &search_query);
cx.render(rsx! {
div {
style: "position: relative; font-family: monospace;",
input {
type: "text",
placeholder: "输入搜索关键词",
value: "{search_query}",
oninput: move |e| search_query.set(e.value.clone()),
style: "margin-bottom: 10px; width: 100%; padding: 8px;"
}
div {
style: "position: relative; width: 100%; height: 200px;",
div {
id: "highlight-layer",
dangerously_set_inner_html: "{highlighted_html}",
style: "position: absolute; top: 0; left: 0; width: 100%; height: 100%;
pointer-events: none; color: transparent; white-space: pre-wrap;
overflow-wrap: break-word; background: transparent; padding: 10px;
box-sizing: border-box;"
}
textarea {
value: "{text}",
oninput: move |e| text.set(e.value.clone()),
style: "position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: transparent; color: black; border: 1px solid #ccc;
padding: 10px; box-sizing: border-box; resize: none;
overflow-y: auto; caret-color: black;"
}
}
}
})
}
fn highlight_text(text: &str, query: &str) -> String {
if query.is_empty() {
return html_escape::encode_text(text).to_string();
}
let replaced = html_escape::encode_text(text).replace(
query,
&format!("{}", query),
);
replaced
}
.textarea-wrapper {
position: relative;
width: 100%;
height: 200px;
}
.highlight-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: transparent;
white-space: pre-wrap;
overflow-wrap: break-word;
pointer-events: none;
z-index: 1;
}
textarea {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: transparent;
z-index: 2;
color: black;
caret-color: black;
border: 1px solid #ccc;
padding: 10px;
resize: none;
}
textarea 的原生输入体验。如果您的项目需要更复杂的高亮功能,如语法高亮、智能提示等,可以考虑使用现有的第三方库,例如 CodeMirror 或 Monaco Editor。这些库提供了丰富的功能和高度的可定制性,可以满足复杂的需求。
将选定的第三方库集成到 Dioxus 项目中,通常需要通过 WebView 加载相应的 HTML 和 JavaScript 代码。
根据需求,通过库的 API 配置搜索高亮逻辑。例如,使用 CodeMirror 的搜索扩展功能来实现。
通过 Dioxus 的 WebView API 与 JavaScript 代码进行交互,实现数据的双向同步。
以下是一个将 CodeMirror 集成到 Dioxus 应用中的简单示例:
use dioxus::prelude::*;
use dioxus_desktop::tao::window::WindowBuilder;
use dioxus_desktop::Config;
fn main() {
dioxus::desktop::launch_cfg(App, Config::default());
}
fn App(cx: Scope) -> Element {
cx.render(rsx! {
div {
id: "editor",
style: "height: 200px; border: 1px solid #ccc;"
}
script { src: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.js" }
link { rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.css" }
script {
dangerously_set_inner_html: "{`
const editor = CodeMirror(document.getElementById('editor'), {
lineNumbers: true,
mode: 'javascript',
theme: 'default'
});
function highlightSearch(query) {
editor.getAllMarks().forEach(mark => mark.clear());
if (query) {
const regex = new RegExp(query, 'gi');
editor.eachLine(line => {
const text = line.text;
let match;
while ((match = regex.exec(text)) !== null) {
editor.markText(
{ line: editor.getLineNumber(line), ch: match.index },
{ line: editor.getLineNumber(line), ch: match.index + query.length },
{ className: 'highlight' }
);
}
});
}
}
window.highlightSearch = highlightSearch;
`}"
}
style {
dangerously_set_inner_html: "{`
.highlight {
background-color: yellow;
}
`}"
}
input {
type: "text",
placeholder: "输入搜索关键词",
oninput: move |e| {
let query = e.value.clone();
// 调用 JavaScript 函数进行高亮
dioxus_desktop::tao::webview::execute_script(&format!("highlightSearch('{}')", query));
},
style: "margin-top: 10px; width: 100%; padding: 8px;"
}
})
}
为了提高性能和实现更复杂的文本处理,可以结合 WebAssembly(Wasm)和 JavaScript 来实现搜索高亮功能。使用 Rust 编写高效的文本解析器,并通过 wasm-bindgen 将其导出为 WebAssembly 模块,再在 Dioxus 中调用这些模块进行高亮处理。
通过 WebAssembly,可以利用 Rust 的高性能计算能力来处理大文本内容的高亮逻辑。Wasm 模块可以被 JavaScript 调用,将处理结果返回给 Dioxus 进行渲染。
使用 wasm-bindgen 将 Rust 函数导出为 JavaScript 可调用的模块。
通过 JavaScript 调用 WebAssembly 模块,并将高亮处理后的内容渲染到界面上。
在 Rust 中实现高效的文本搜索和高亮算法,将匹配的关键词包裹在 <span> 标签中。
以下是一个简单的 Rust 高亮函数示例,并在 Dioxus 中调用该函数:
// src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn highlight_text(input: &str, query: &str) -> String {
if query.is_empty() {
return input.to_string();
}
input.replace(
query,
&format!("{}", query),
)
}
然后在 Dioxus 中调用:
use dioxus::prelude::*;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::Window;
#[wasm_bindgen(module = "/path/to/your/wasm.js")]
extern "C" {
fn highlight_text(input: &str, query: &str) -> String;
}
fn main() {
dioxus::desktop::launch(App);
}
fn App(cx: Scope) -> Element {
let text = use_state(cx, || String::new());
let search_query = use_state(cx, || String::new());
let highlighted_html = highlight_text(&text, &search_query);
cx.render(rsx! {
div {
style: "font-family: monospace; padding: 20px;",
input {
type: "text",
placeholder: "输入搜索关键词",
value: "{search_query}",
oninput: move |e| search_query.set(e.value.clone()),
style: "margin-bottom: 10px; width: 100%; padding: 8px;"
}
div {
contenteditable: "true",
oninput: move |e| text.set(e.value.clone()),
dangerously_set_inner_html: "{highlighted_html}",
style: "width: 100%; height: 200px; border: 1px solid #ccc; padding: 10px; overflow-y: auto;"
}
}
})
}
在处理大文本内容时,高亮逻辑可能会成为性能瓶颈。可以考虑以下优化措施:
在动态更新内容时,光标位置可能会丢失或跳动,影响用户体验。可以通过以下方法进行管理:
确保高亮功能的响应速度和准确性,以提升用户体验。以下是一些建议:
在实现高亮功能时,应遵循无障碍设计原则:
<span>,以便辅助技术正确识别高亮内容。在 Dioxus 0.6 框架下实现桌面应用的搜索高亮效果,尽管 textarea 无法直接支持部分文本的 CSS 渲染,但通过以下几种替代方法,可以有效实现类似的功能:
contenteditable 的 div 替代 textarea**:灵活性高,但需要处理光标位置和性能优化。
textarea 和覆盖层**:保留原生输入体验,适合简单场景,但实现较为复杂。
选择合适的实现方案,应根据项目的具体需求和开发团队的技术栈熟悉程度进行权衡。希望本文提供的解决方案和示例代码,能够帮助你在 Dioxus 框架下顺利实现搜索高亮功能。