使用 Dioxus 0.6 实现桌面应用中的搜索高亮效果
在使用 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
}
CSS 样式
.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
}
CSS 样式
.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 交互:
通过 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;"
}
})
}
优点
- 功能丰富,支持多种高级特性,如语法高亮、自动补全等。
- 社区活跃,维护频繁,易于获取支持和更新。
- 高度可定制,适应不同需求。
缺点
- 增加了项目的依赖和体积。
- 可能需要一定的学习成本,尤其是对于不熟悉 JavaScript 的开发者。
- 复杂的集成过程,需要处理 Rust 与 JavaScript 之间的交互。
解决方案四:结合 WebAssembly 和 JavaScript
为了提高性能和实现更复杂的文本处理,可以结合 WebAssembly(Wasm)和 JavaScript 来实现搜索高亮功能。使用 Rust 编写高效的文本解析器,并通过 wasm-bindgen 将其导出为 WebAssembly 模块,再在 Dioxus 中调用这些模块进行高亮处理。
原理
通过 WebAssembly,可以利用 Rust 的高性能计算能力来处理大文本内容的高亮逻辑。Wasm 模块可以被 JavaScript 调用,将处理结果返回给 Dioxus 进行渲染。
实现步骤
-
编写 Rust 解析器并导出为 WebAssembly:
使用
wasm-bindgen将 Rust 函数导出为 JavaScript 可调用的模块。 -
在 Dioxus 中集成 WebAssembly 模块:
通过 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;"
}
}
})
}
优点
- 高性能,适合处理大文本内容。
- 充分利用 Rust 的计算能力,提升整体应用性能。
缺点
- 需要掌握 WebAssembly 和 JavaScript 的交互,增加了开发复杂度。
- 调试和维护可能更加复杂。
其他注意事项
性能优化
在处理大文本内容时,高亮逻辑可能会成为性能瓶颈。可以考虑以下优化措施:
- **分块处理**:将文本分割成小块,逐步处理和渲染,避免一次性加载过多内容。
- **节流和防抖**:限制高亮逻辑的触发频率,减少不必要的计算。
- **缓存结果**:对已处理的文本片段进行缓存,避免重复计算。
光标位置管理
在动态更新内容时,光标位置可能会丢失或跳动,影响用户体验。可以通过以下方法进行管理:
- **保存和恢复光标位置**:在更新内容前保存当前光标位置,更新后恢复光标位置。
- **最小化 DOM 操作**:尽量减少对 DOM 的大规模更新,保持用户输入的连贯性。
用户体验
确保高亮功能的响应速度和准确性,以提升用户体验。以下是一些建议:
- **实时高亮**:高亮应随用户输入实时更新,避免延迟。
- **清晰的高亮样式**:选择对比度高且不干扰阅读的颜色进行高亮。
- **可访问性**:确保高亮文本对所有用户友好,包括色盲用户。
无障碍支持
在实现高亮功能时,应遵循无障碍设计原则:
- **语义化 HTML**:使用语义化标签,如
<span>,以便辅助技术正确识别高亮内容。 - **键盘导航**:确保用户可以通过键盘进行搜索和导航。
- **颜色对比**:选择具有足够对比度的颜色,确保高亮文本易于识别。
总结
在 Dioxus 0.6 框架下实现桌面应用的搜索高亮效果,尽管 textarea 无法直接支持部分文本的 CSS 渲染,但通过以下几种替代方法,可以有效实现类似的功能:
-
**使用
contenteditable的div替代textarea**:灵活性高,但需要处理光标位置和性能优化。 -
**使用
textarea和覆盖层**:保留原生输入体验,适合简单场景,但实现较为复杂。 -
**使用第三方库或自定义渲染器**:功能强大,适用于复杂需求,但增加了项目依赖和集成复杂度。
-
**结合 WebAssembly 和 JavaScript**:高性能,适合大文本处理,但开发复杂度高。
选择合适的实现方案,应根据项目的具体需求和开发团队的技术栈熟悉程度进行权衡。希望本文提供的解决方案和示例代码,能够帮助你在 Dioxus 框架下顺利实现搜索高亮功能。
参考链接
Last updated January 3, 2025