Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion examples/shared/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ratzilla::{
error::Error,
event::{KeyEvent, MouseEvent},
ratatui::{backend::Backend, prelude::backend::ClearType, Terminal, TerminalOptions},
CanvasBackend, DomBackend, WebEventHandler, WebGl2Backend,
CanvasBackend, CellSized, DomBackend, WebEventHandler, WebGl2Backend,
};
use std::{convert::TryFrom, fmt, io};
use web_sys::{window, Url};
Expand Down Expand Up @@ -78,6 +78,24 @@ impl RatzillaBackend {
}
}

impl CellSized for RatzillaBackend {
fn cell_size_px(&self) -> (f32, f32) {
match self {
RatzillaBackend::Dom(backend) => backend.cell_size_px(),
RatzillaBackend::Canvas(backend) => backend.cell_size_px(),
RatzillaBackend::WebGl2(backend) => backend.cell_size_px(),
}
}

fn cell_size_css_px(&self) -> (f32, f32) {
match self {
RatzillaBackend::Dom(backend) => backend.cell_size_css_px(),
RatzillaBackend::Canvas(backend) => backend.cell_size_css_px(),
RatzillaBackend::WebGl2(backend) => backend.cell_size_css_px(),
}
}
}

impl Backend for RatzillaBackend {
type Error = io::Error;

Expand Down Expand Up @@ -244,6 +262,16 @@ impl From<RatzillaBackend> for FpsTrackingBackend {
}
}

impl CellSized for FpsTrackingBackend {
fn cell_size_px(&self) -> (f32, f32) {
self.inner.cell_size_px()
}

fn cell_size_css_px(&self) -> (f32, f32) {
self.inner.cell_size_css_px()
}
}

impl Backend for FpsTrackingBackend {
type Error = io::Error;

Expand Down
12 changes: 12 additions & 0 deletions src/backend/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use crate::{
backend::{
cell_sized::CellSized,
color::{actual_bg_color, actual_fg_color},
event_callback::{
create_mouse_event, EventCallback, MouseConfig, KEY_EVENT_TYPES, MOUSE_EVENT_TYPES,
Expand Down Expand Up @@ -287,7 +288,7 @@
/// 1. Only processes cells that have changed since the last render.
/// 2. Tracks the last foreground color used to avoid unnecessary style changes
/// 3. Only creates clipping paths for potentially problematic glyphs (non-ASCII)
/// or when `always_clip_cells` is enabled.

Check warning on line 291 in src/backend/canvas.rs

View workflow job for this annotation

GitHub Actions / clippy

doc list item without indentation

warning: doc list item without indentation --> src/backend/canvas.rs:291:9 | 291 | /// or when `always_clip_cells` is enabled. | ^ | = help: if this is supposed to be its own paragraph, add a blank line = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#doc_lazy_continuation = note: `#[warn(clippy::doc_lazy_continuation)]` on by default help: indent this line | 291 | /// or when `always_clip_cells` is enabled. | +++
fn draw_symbols(&mut self) -> Result<(), Error> {
let changed_cells = &self.changed_cells;
let mut index = 0;
Expand Down Expand Up @@ -419,7 +420,7 @@
fn draw_debug(&mut self) -> Result<(), Error> {
self.canvas.context.save();

let color = self.debug_mode.as_ref().unwrap();

Check warning on line 423 in src/backend/canvas.rs

View workflow job for this annotation

GitHub Actions / clippy

used `unwrap()` on an `Option` value

warning: used `unwrap()` on an `Option` value --> src/backend/canvas.rs:423:21 | 423 | let color = self.debug_mode.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#unwrap_used note: the lint level is defined here --> src/lib.rs:1:23 | 1 | #![warn(missing_docs, clippy::unwrap_used)] | ^^^^^^^^^^^^^^^^^^^
for (y, line) in self.buffer.iter().enumerate() {
for (x, _) in line.iter().enumerate() {
self.canvas.context.set_stroke_style_str(color);
Expand All @@ -438,6 +439,17 @@
}
}

impl CellSized for CanvasBackend {
fn cell_size_px(&self) -> (f32, f32) {
let dpr = get_device_pixel_ratio();
(CELL_WIDTH as f32 * dpr, CELL_HEIGHT as f32 * dpr)
}

fn cell_size_css_px(&self) -> (f32, f32) {
(CELL_WIDTH as f32, CELL_HEIGHT as f32)
}
}

impl Backend for CanvasBackend {
type Error = IoError;

Expand Down
19 changes: 19 additions & 0 deletions src/backend/cell_sized.rs
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should include this trait in either util module or move it under backend

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll move it to backend! (util is an option too ofc, but it feels a bit wrong - maybe it's just me).

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// A type that knows the pixel dimensions of its terminal cells.
///
/// # Physical vs. CSS pixels
///
/// On HiDPI / Retina displays the device pixel ratio (DPR) causes one
/// CSS pixel to map to multiple physical (device) pixels.
///
/// - **Physical pixels** (`cell_size_px`): the actual device pixels
/// occupied by a cell. Useful for pixel-perfect rendering and GPU work.
/// - **CSS pixels** (`cell_size_css_px`): the logical size as seen by
/// the browser layout engine. Useful for DOM positioning, mouse
/// coordinate translation, and canvas drawing.
pub trait CellSized {
/// Returns the size of a cell in physical (device) pixels as `(width, height)`.
fn cell_size_px(&self) -> (f32, f32);

/// Returns the size of a cell in CSS (logical) pixels as `(width, height)`.
fn cell_size_css_px(&self) -> (f32, f32);
}
12 changes: 12 additions & 0 deletions src/backend/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use crate::{
backend::{
cell_sized::CellSized,
event_callback::{
create_mouse_event, EventCallback, MouseConfig, KEY_EVENT_TYPES, MOUSE_EVENT_TYPES,
},
Expand Down Expand Up @@ -52,7 +53,7 @@
///
/// - If the grid ID is not set, it returns `"grid"`.
/// - If the grid ID is set, it returns the grid ID suffixed with
/// `"_ratzilla_grid"`.

Check warning on line 56 in src/backend/dom.rs

View workflow job for this annotation

GitHub Actions / clippy

doc list item overindented

warning: doc list item overindented --> src/backend/dom.rs:56:9 | 56 | /// `"_ratzilla_grid"`. | ^^^^ help: try using ` ` (2 spaces) | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#doc_overindented_list_items = note: `#[warn(clippy::doc_overindented_list_items)]` on by default
pub fn grid_id(&self) -> String {
match &self.grid_id {
Some(id) => format!("{id}_ratzilla_grid"),
Expand Down Expand Up @@ -265,6 +266,17 @@
}
}

impl CellSized for DomBackend {
fn cell_size_px(&self) -> (f32, f32) {
let dpr = get_device_pixel_ratio();
(self.cell_size.0 as f32 * dpr, self.cell_size.1 as f32 * dpr)
}

fn cell_size_css_px(&self) -> (f32, f32) {
(self.cell_size.0 as f32, self.cell_size.1 as f32)
}
}

impl Backend for DomBackend {
type Error = IoError;

Expand Down Expand Up @@ -313,15 +325,15 @@
.map_err(Error::from)?;

// don't display the next cell if a fullwidth glyph preceeds it
if cell.symbol().len() > 1 && cell.symbol().width() == 2 {
if (cell_position + 1) < self.cells.len() {
let next_elem = &self.cells[cell_position + 1];
next_elem.set_inner_html("");
next_elem
.set_attribute("style", &get_cell_style_as_css(&Cell::new("")))
.map_err(Error::from)?;
}
}

Check warning on line 336 in src/backend/dom.rs

View workflow job for this annotation

GitHub Actions / clippy

this `if` statement can be collapsed

warning: this `if` statement can be collapsed --> src/backend/dom.rs:328:13 | 328 | / if cell.symbol().len() > 1 && cell.symbol().width() == 2 { 329 | | if (cell_position + 1) < self.cells.len() { 330 | | let next_elem = &self.cells[cell_position + 1]; 331 | | next_elem.set_inner_html(""); ... | 336 | | } | |_____________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#collapsible_if = note: `#[warn(clippy::collapsible_if)]` on by default help: collapse nested if block | 328 ~ if cell.symbol().len() > 1 && cell.symbol().width() == 2 329 ~ && (cell_position + 1) < self.cells.len() { 330 | let next_elem = &self.cells[cell_position + 1]; ... 334 | .map_err(Error::from)?; 335 ~ } |
}

Ok(())
Expand Down
2 changes: 2 additions & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,7 @@ pub(super) mod event_callback;
/// Backend utilities.
pub(crate) mod utils;

/// Cell size metrics for backends.
pub mod cell_sized;
/// Cursor shapes.
pub mod cursor;
5 changes: 5 additions & 0 deletions src/backend/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@

/// Converts a Color to a CSS style.
pub(crate) fn get_canvas_color(color: Color, fallback_color: Color) -> CompactString {
let color = ansi_to_rgb(color).unwrap_or_else(|| ansi_to_rgb(fallback_color).unwrap());

Check warning on line 188 in src/backend/utils.rs

View workflow job for this annotation

GitHub Actions / clippy

used `unwrap()` on an `Option` value

warning: used `unwrap()` on an `Option` value --> src/backend/utils.rs:188:54 | 188 | let color = ansi_to_rgb(color).unwrap_or_else(|| ansi_to_rgb(fallback_color).unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#unwrap_used

format_compact!("rgb({}, {}, {})", color.0, color.1, color.2)
}
Expand All @@ -208,7 +208,7 @@

/// Returns the number of pixels that can fit in the window.
pub(crate) fn get_raw_screen_size() -> (i32, i32) {
let s = web_sys::window().unwrap().screen().unwrap();

Check warning on line 211 in src/backend/utils.rs

View workflow job for this annotation

GitHub Actions / clippy

used `unwrap()` on an `Option` value

warning: used `unwrap()` on an `Option` value --> src/backend/utils.rs:211:13 | 211 | let s = web_sys::window().unwrap().screen().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: if this value is `None`, it will panic = help: consider using `expect()` to provide a better panic message = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#unwrap_used

Check warning on line 211 in src/backend/utils.rs

View workflow job for this annotation

GitHub Actions / clippy

used `unwrap()` on a `Result` value

warning: used `unwrap()` on a `Result` value --> src/backend/utils.rs:211:13 | 211 | let s = web_sys::window().unwrap().screen().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: if this value is an `Err`, it will panic = help: consider using `expect()` to provide a better panic message = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#unwrap_used
(s.width().unwrap(), s.height().unwrap())
}

Expand Down Expand Up @@ -246,6 +246,11 @@
window().ok_or(Error::UnableToRetrieveWindow)
}

/// Returns the device pixel ratio from the window.
pub(crate) fn get_device_pixel_ratio() -> f32 {
get_window().map(|w| w.device_pixel_ratio()).unwrap_or(1.0) as f32
}

/// Returns an element by its ID or the body element if no ID is provided.
pub(crate) fn get_element_by_id_or_body(id: Option<&String>) -> Result<web_sys::Element, Error> {
match id {
Expand Down
23 changes: 20 additions & 3 deletions src/backend/webgl2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
mem::swap,
rc::Rc,
};
use web_sys::{wasm_bindgen::JsCast, window, Element};
use web_sys::{wasm_bindgen::JsCast, Element};

use crate::backend::cell_sized::CellSized;
/// Re-export beamterm's atlas data type. Used by [`FontAtlasConfig::Static`].
pub use beamterm_renderer::FontAtlasData;

Expand Down Expand Up @@ -205,7 +206,7 @@
/// Sets up a default mouse handler using [`WebGl2BackendOptions::on_hyperlink_click`].
pub fn enable_hyperlinks(self) -> Self {
self.on_hyperlink_click(|url| {
if let Some(w) = window() {
if let Ok(w) = get_window() {
w.open_with_url_and_target(url, "_blank")
.unwrap_or_default();
}
Expand Down Expand Up @@ -425,8 +426,13 @@
///
/// For static atlases, this is the cell size from the atlas data.
/// For dynamic atlases, this is measured from the rasterized font.
#[deprecated(
since = "0.4.0",
note = "Use cell_size_px instead, which returns physical pixel dimensions"
)]
pub fn cell_size(&self) -> (i32, i32) {
self.beamterm.cell_size()
let (w, h) = self.cell_size_px();
(w as i32, h as i32)
}

/// Resizes the canvas and terminal grid to the specified logical pixel dimensions.
Expand Down Expand Up @@ -618,7 +624,7 @@
.is_some();
if self.cursor_over_hyperlink != is_over {
self.cursor_over_hyperlink = is_over;
Self::update_canvas_cursor_style(&self.beamterm.canvas(), is_over);

Check warning on line 627 in src/backend/webgl2.rs

View workflow job for this annotation

GitHub Actions / clippy

this expression creates a reference which is immediately dereferenced by the compiler

warning: this expression creates a reference which is immediately dereferenced by the compiler --> src/backend/webgl2.rs:627:50 | 627 | Self::update_canvas_cursor_style(&self.beamterm.canvas(), is_over); | ^^^^^^^^^^^^^^^^^^^^^^^ help: change this to: `self.beamterm.canvas()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#needless_borrow = note: `#[warn(clippy::needless_borrow)]` on by default
}
}

Expand Down Expand Up @@ -672,6 +678,17 @@
}
}

impl CellSized for WebGl2Backend {
fn cell_size_px(&self) -> (f32, f32) {
let (w, h) = self.beamterm.cell_size();
(w as f32, h as f32)
}

fn cell_size_css_px(&self) -> (f32, f32) {
self.beamterm.grid().borrow().css_cell_size()
}
}

impl Backend for WebGl2Backend {
type Error = IoError;

Expand Down Expand Up @@ -818,7 +835,7 @@
/// A `Debug`-derive friendly convenience wrapper
#[derive(Clone)]
struct HyperlinkCallback {
callback: Rc<RefCell<dyn FnMut(&str)>>,

Check warning on line 838 in src/backend/webgl2.rs

View workflow job for this annotation

GitHub Actions / clippy

very complex type used. Consider factoring parts into `type` definitions

warning: very complex type used. Consider factoring parts into `type` definitions --> src/backend/webgl2.rs:838:15 | 838 | callback: Rc<RefCell<dyn FnMut(&str)>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#type_complexity = note: `#[warn(clippy::type_complexity)]` on by default
}

impl HyperlinkCallback {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub use web_sys;

pub use backend::{
canvas::CanvasBackend,
cell_sized::CellSized,
cursor::CursorShape,
dom::DomBackend,
webgl2::{FontAtlasConfig, SelectionMode, WebGl2Backend},
Expand Down
Loading