Skip to main content

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    /// Select the entired clip
23    fn select_all(&self) -> ApiResult<()>;
24
25    /// Set the caret to a specific offset.
26    fn set_caret_position(&self, offset: usize) -> ApiResult<()>;
27
28    /// Get the caret's offset (if it has focus)
29    fn get_caret_position(&self) -> Option<u32>;
30}
31
32impl CaretPosition for web_sys::HtmlElement {
33    fn select_all(&self) -> ApiResult<()> {
34        let range = global::document().create_range()?;
35        let selection = global::window().get_selection()?.into_apierror()?;
36        range.set_start(self, 0_u32)?;
37        range.set_end(self, 10000000_u32)?;
38        selection.remove_all_ranges()?;
39        selection.add_range(&range)?;
40        Ok(())
41    }
42
43    fn set_caret_position(&self, offset: usize) -> ApiResult<()> {
44        let range = global::document().create_range()?;
45        let selection = global::window().get_selection()?.into_apierror()?;
46        range.set_start(self, offset as u32)?;
47        range.collapse_with_to_start(true);
48        selection.remove_all_ranges()?;
49        selection.add_range(&range)?;
50        Ok(())
51    }
52
53    fn get_caret_position(&self) -> Option<u32> {
54        maybe! {
55            let root = self.get_root_node().unchecked_into::<web_sys::Document>();
56            let selection = root.get_selection()?.into_apierror()?;
57            if selection.range_count() > 0 {
58                let range = selection.get_range_at(0)?;
59                range.end_offset()
60            } else {
61                Err(wasm_bindgen::JsValue::UNDEFINED)
62            }
63        }
64        .ok()
65    }
66}
67
68impl CaretPosition for web_sys::HtmlTextAreaElement {
69    fn select_all(&self) -> ApiResult<()> {
70        self.set_selection_start(Some(0_u32))?;
71        self.set_selection_end(Some(1000000000_u32))?;
72        Ok(())
73    }
74
75    fn set_caret_position(&self, offset: usize) -> ApiResult<()> {
76        self.set_selection_end(Some(offset as u32))?;
77        self.set_selection_start(Some(offset as u32))?;
78        Ok(())
79    }
80
81    fn get_caret_position(&self) -> Option<u32> {
82        self.selection_end().ok()?
83    }
84}