perspective_viewer/utils/browser/
selection.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use perspective_js::utils::global;
14use wasm_bindgen::JsCast;
15
16use crate::*;
17
18/// Utilities for caret position.  DOM elements have different APIs for this
19/// but `Deref` makes them fall through, so it is important that this method
20/// be called on the correct struct type!
21pub trait CaretPosition {
22    fn select_all(&self) -> ApiResult<()>;
23    fn set_caret_position(&self, offset: usize) -> ApiResult<()>;
24    fn get_caret_position(&self) -> Option<u32>;
25}
26
27impl CaretPosition for web_sys::HtmlElement {
28    fn select_all(&self) -> ApiResult<()> {
29        let range = global::document().create_range()?;
30        let selection = global::window().get_selection()?.into_apierror()?;
31        range.set_start(self, 0_u32)?;
32        range.set_end(self, 10000000_u32)?;
33        selection.remove_all_ranges()?;
34        selection.add_range(&range)?;
35        Ok(())
36    }
37
38    fn set_caret_position(&self, offset: usize) -> ApiResult<()> {
39        let range = global::document().create_range()?;
40        let selection = global::window().get_selection()?.into_apierror()?;
41        range.set_start(self, offset as u32)?;
42        range.collapse_with_to_start(true);
43        selection.remove_all_ranges()?;
44        selection.add_range(&range)?;
45        Ok(())
46    }
47
48    fn get_caret_position(&self) -> Option<u32> {
49        maybe! {
50            let root = self.get_root_node().unchecked_into::<web_sys::Document>();
51            let selection = root.get_selection()?.into_apierror()?;
52            if selection.range_count() > 0 {
53                let range = selection.get_range_at(0)?;
54                range.end_offset()
55            } else {
56                Err(wasm_bindgen::JsValue::UNDEFINED)
57            }
58        }
59        .ok()
60    }
61}
62
63impl CaretPosition for web_sys::HtmlTextAreaElement {
64    fn select_all(&self) -> ApiResult<()> {
65        self.set_selection_start(Some(0_u32))?;
66        self.set_selection_end(Some(1000000000_u32))?;
67        Ok(())
68    }
69
70    fn set_caret_position(&self, offset: usize) -> ApiResult<()> {
71        self.set_selection_end(Some(offset as u32))?;
72        self.set_selection_start(Some(offset as u32))?;
73        Ok(())
74    }
75
76    fn get_caret_position(&self) -> Option<u32> {
77        self.selection_end().ok()?
78    }
79}