diff --git a/Cargo.lock b/Cargo.lock
index 9d1ce321..a78dfa28 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -380,6 +380,7 @@ dependencies = [
"console_error_panic_hook",
"ratatui",
"thiserror",
+ "unicode-width 0.2.0",
"web-sys",
]
diff --git a/Cargo.toml b/Cargo.toml
index eba627e4..fb5afbfe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -43,3 +43,4 @@ console_error_panic_hook = "0.1.7"
thiserror = "2.0.12"
bitvec = { version = "1.0.1", default-features = false, features = ["alloc", "std"] }
beamterm-renderer = "0.1.1"
+unicode-width = "0.2.0"
diff --git a/examples/unicode/Cargo.toml b/examples/unicode/Cargo.toml
new file mode 100644
index 00000000..2b516941
--- /dev/null
+++ b/examples/unicode/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "minimal"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+ratzilla = { path = "../../" }
+examples-shared = { path = "../shared" }
diff --git a/examples/unicode/index.html b/examples/unicode/index.html
new file mode 100644
index 00000000..f8fd79ad
--- /dev/null
+++ b/examples/unicode/index.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+ Ratzilla
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/unicode/src/main.rs b/examples/unicode/src/main.rs
new file mode 100644
index 00000000..1750b5c6
--- /dev/null
+++ b/examples/unicode/src/main.rs
@@ -0,0 +1,40 @@
+use std::io;
+
+use ratzilla::ratatui::{
+ layout::Alignment,
+ style::Color,
+ widgets::{Block, Paragraph},
+};
+
+use ratzilla::WebRenderer;
+
+use examples_shared::backend::{BackendType, MultiBackendBuilder};
+
+fn main() -> io::Result<()> {
+ let terminal = MultiBackendBuilder::with_fallback(BackendType::Dom).build_terminal()?;
+
+ terminal.draw_web(move |f| {
+ f.render_widget(
+ Paragraph::new(
+ [
+ "Hello, world!",
+ "你好,世界!",
+ "世界、こんにちは。",
+ // "헬로우 월드!",
+ // "👨💻👋🌐",
+ ]
+ .join("\n"),
+ )
+ .alignment(Alignment::Center)
+ .block(
+ Block::bordered()
+ .title("Ratzilla")
+ .title_alignment(Alignment::Center)
+ .border_style(Color::Yellow),
+ ),
+ f.area(),
+ );
+ });
+
+ Ok(())
+}
diff --git a/src/backend/dom.rs b/src/backend/dom.rs
index 2ce52ebd..13f7fed4 100644
--- a/src/backend/dom.rs
+++ b/src/backend/dom.rs
@@ -6,6 +6,7 @@ use ratatui::{
layout::{Position, Size},
prelude::Backend,
};
+use unicode_width::UnicodeWidthStr;
use web_sys::{
wasm_bindgen::{prelude::Closure, JsCast},
window, Document, Element, Window,
@@ -148,10 +149,13 @@ impl DomBackend {
fn prerender(&mut self) -> Result<(), Error> {
for line in self.buffer.iter() {
let mut line_cells: Vec = Vec::new();
- let mut hyperlink: Vec = Vec::new();
+ let mut hyperlink: Vec<(Cell, bool)> = Vec::new();
+ let mut skip = 0;
for (i, cell) in line.iter().enumerate() {
+ let overwritten = skip > 0;
+ skip = std::cmp::max(skip, cell.symbol().width()).saturating_sub(1);
if cell.modifier.contains(HYPERLINK_MODIFIER) {
- hyperlink.push(cell.clone());
+ hyperlink.push((cell.clone(), overwritten));
// If the next cell is not part of the hyperlink, close it
if !line
.get(i + 1)
@@ -159,8 +163,8 @@ impl DomBackend {
.unwrap_or(false)
{
let anchor = create_anchor(&self.document, &hyperlink)?;
- for link_cell in &hyperlink {
- let span = create_span(&self.document, link_cell)?;
+ for (link_cell, overwritten) in &hyperlink {
+ let span = create_span(&self.document, link_cell, *overwritten)?;
self.cells.push(span.clone());
anchor.append_child(&span)?;
}
@@ -168,7 +172,7 @@ impl DomBackend {
hyperlink.clear();
}
} else {
- let span = create_span(&self.document, cell)?;
+ let span = create_span(&self.document, cell, overwritten)?;
self.cells.push(span.clone());
line_cells.push(span);
}
@@ -192,14 +196,22 @@ impl DomBackend {
/// accordingly.
fn update_grid(&mut self) -> Result<(), Error> {
for (y, line) in self.buffer.iter().enumerate() {
+ let mut skip = 0;
for (x, cell) in line.iter().enumerate() {
+ let overwritten = skip > 0;
+ skip = std::cmp::max(skip, cell.symbol().width()).saturating_sub(1);
if cell.modifier.contains(HYPERLINK_MODIFIER) {
continue;
}
if cell != &self.prev_buffer[y][x] {
let elem = self.cells[y * self.buffer[0].len() + x].clone();
elem.set_inner_html(cell.symbol());
- elem.set_attribute("style", &get_cell_style_as_css(cell))?;
+ if overwritten {
+ // If the cell is overwritten, hide it
+ elem.set_attribute("style", "display: none;")?;
+ } else {
+ elem.set_attribute("style", &get_cell_style_as_css(cell))?;
+ }
}
}
}
diff --git a/src/backend/utils.rs b/src/backend/utils.rs
index ed9d2565..726f9b57 100644
--- a/src/backend/utils.rs
+++ b/src/backend/utils.rs
@@ -14,23 +14,31 @@ use web_sys::{
};
/// Creates a new `` element with the given cell.
-pub(crate) fn create_span(document: &Document, cell: &Cell) -> Result {
+pub(crate) fn create_span(
+ document: &Document,
+ cell: &Cell,
+ overwritten: bool,
+) -> Result {
let span = document.create_element("span")?;
span.set_inner_html(cell.symbol());
- let style = get_cell_style_as_css(cell);
- span.set_attribute("style", &style)?;
+ if overwritten {
+ span.set_attribute("style", "display: none;")?;
+ } else {
+ let style = get_cell_style_as_css(cell);
+ span.set_attribute("style", &style)?;
+ }
Ok(span)
}
/// Creates a new `` element with the given cells.
-pub(crate) fn create_anchor(document: &Document, cells: &[Cell]) -> Result {
+pub(crate) fn create_anchor(document: &Document, cells: &[(Cell, bool)]) -> Result {
let anchor = document.create_element("a")?;
anchor.set_attribute(
"href",
- &cells.iter().map(|c| c.symbol()).collect::(),
+ &cells.iter().map(|c| c.0.symbol()).collect::(),
)?;
- anchor.set_attribute("style", &get_cell_style_as_css(&cells[0]))?;
+ anchor.set_attribute("style", &get_cell_style_as_css(&cells[0].0))?;
Ok(anchor)
}
|