/ egui_web / src / input.rs
input.rs
  1  use crate::{canvas_element, canvas_origin, AppRunner};
  2  
  3  pub fn pos_from_mouse_event(canvas_id: &str, event: &web_sys::MouseEvent) -> egui::Pos2 {
  4      let canvas = canvas_element(canvas_id).unwrap();
  5      let rect = canvas.get_bounding_client_rect();
  6      egui::Pos2 {
  7          x: event.client_x() as f32 - rect.left() as f32,
  8          y: event.client_y() as f32 - rect.top() as f32,
  9      }
 10  }
 11  
 12  pub fn button_from_mouse_event(event: &web_sys::MouseEvent) -> Option<egui::PointerButton> {
 13      match event.button() {
 14          0 => Some(egui::PointerButton::Primary),
 15          1 => Some(egui::PointerButton::Middle),
 16          2 => Some(egui::PointerButton::Secondary),
 17          _ => None,
 18      }
 19  }
 20  
 21  /// A single touch is translated to a pointer movement. When a second touch is added, the pointer
 22  /// should not jump to a different position. Therefore, we do not calculate the average position
 23  /// of all touches, but we keep using the same touch as long as it is available.
 24  ///
 25  /// `touch_id_for_pos` is the [`TouchId`](egui::TouchId) of the [`Touch`](web_sys::Touch) we previously used to determine the
 26  /// pointer position.
 27  pub fn pos_from_touch_event(
 28      canvas_id: &str,
 29      event: &web_sys::TouchEvent,
 30      touch_id_for_pos: &mut Option<egui::TouchId>,
 31  ) -> egui::Pos2 {
 32      let touch_for_pos;
 33      if let Some(touch_id_for_pos) = touch_id_for_pos {
 34          // search for the touch we previously used for the position
 35          // (unfortunately, `event.touches()` is not a rust collection):
 36          touch_for_pos = (0..event.touches().length())
 37              .into_iter()
 38              .map(|i| event.touches().get(i).unwrap())
 39              .find(|touch| egui::TouchId::from(touch.identifier()) == *touch_id_for_pos);
 40      } else {
 41          touch_for_pos = None;
 42      }
 43      // Use the touch found above or pick the first, or return a default position if there is no
 44      // touch at all. (The latter is not expected as the current method is only called when there is
 45      // at least one touch.)
 46      touch_for_pos
 47          .or_else(|| event.touches().get(0))
 48          .map_or(Default::default(), |touch| {
 49              *touch_id_for_pos = Some(egui::TouchId::from(touch.identifier()));
 50              pos_from_touch(canvas_origin(canvas_id), &touch)
 51          })
 52  }
 53  
 54  fn pos_from_touch(canvas_origin: egui::Pos2, touch: &web_sys::Touch) -> egui::Pos2 {
 55      egui::Pos2 {
 56          x: touch.page_x() as f32 - canvas_origin.x as f32,
 57          y: touch.page_y() as f32 - canvas_origin.y as f32,
 58      }
 59  }
 60  
 61  pub fn push_touches(runner: &mut AppRunner, phase: egui::TouchPhase, event: &web_sys::TouchEvent) {
 62      let canvas_origin = canvas_origin(runner.canvas_id());
 63      for touch_idx in 0..event.changed_touches().length() {
 64          if let Some(touch) = event.changed_touches().item(touch_idx) {
 65              runner.input.raw.events.push(egui::Event::Touch {
 66                  device_id: egui::TouchDeviceId(0),
 67                  id: egui::TouchId::from(touch.identifier()),
 68                  phase,
 69                  pos: pos_from_touch(canvas_origin, &touch),
 70                  force: touch.force(),
 71              });
 72          }
 73      }
 74  }
 75  
 76  /// Web sends all keys as strings, so it is up to us to figure out if it is
 77  /// a real text input or the name of a key.
 78  pub fn should_ignore_key(key: &str) -> bool {
 79      let is_function_key = key.starts_with('F') && key.len() > 1;
 80      is_function_key
 81          || matches!(
 82              key,
 83              "Alt"
 84                  | "ArrowDown"
 85                  | "ArrowLeft"
 86                  | "ArrowRight"
 87                  | "ArrowUp"
 88                  | "Backspace"
 89                  | "CapsLock"
 90                  | "ContextMenu"
 91                  | "Control"
 92                  | "Delete"
 93                  | "End"
 94                  | "Enter"
 95                  | "Esc"
 96                  | "Escape"
 97                  | "Help"
 98                  | "Home"
 99                  | "Insert"
100                  | "Meta"
101                  | "NumLock"
102                  | "PageDown"
103                  | "PageUp"
104                  | "Pause"
105                  | "ScrollLock"
106                  | "Shift"
107                  | "Tab"
108          )
109  }
110  
111  /// Web sends all all keys as strings, so it is up to us to figure out if it is
112  /// a real text input or the name of a key.
113  pub fn translate_key(key: &str) -> Option<egui::Key> {
114      match key {
115          "ArrowDown" => Some(egui::Key::ArrowDown),
116          "ArrowLeft" => Some(egui::Key::ArrowLeft),
117          "ArrowRight" => Some(egui::Key::ArrowRight),
118          "ArrowUp" => Some(egui::Key::ArrowUp),
119  
120          "Esc" | "Escape" => Some(egui::Key::Escape),
121          "Tab" => Some(egui::Key::Tab),
122          "Backspace" => Some(egui::Key::Backspace),
123          "Enter" => Some(egui::Key::Enter),
124          "Space" | " " => Some(egui::Key::Space),
125  
126          "Help" | "Insert" => Some(egui::Key::Insert),
127          "Delete" => Some(egui::Key::Delete),
128          "Home" => Some(egui::Key::Home),
129          "End" => Some(egui::Key::End),
130          "PageUp" => Some(egui::Key::PageUp),
131          "PageDown" => Some(egui::Key::PageDown),
132  
133          "0" => Some(egui::Key::Num0),
134          "1" => Some(egui::Key::Num1),
135          "2" => Some(egui::Key::Num2),
136          "3" => Some(egui::Key::Num3),
137          "4" => Some(egui::Key::Num4),
138          "5" => Some(egui::Key::Num5),
139          "6" => Some(egui::Key::Num6),
140          "7" => Some(egui::Key::Num7),
141          "8" => Some(egui::Key::Num8),
142          "9" => Some(egui::Key::Num9),
143  
144          "a" | "A" => Some(egui::Key::A),
145          "b" | "B" => Some(egui::Key::B),
146          "c" | "C" => Some(egui::Key::C),
147          "d" | "D" => Some(egui::Key::D),
148          "e" | "E" => Some(egui::Key::E),
149          "f" | "F" => Some(egui::Key::F),
150          "g" | "G" => Some(egui::Key::G),
151          "h" | "H" => Some(egui::Key::H),
152          "i" | "I" => Some(egui::Key::I),
153          "j" | "J" => Some(egui::Key::J),
154          "k" | "K" => Some(egui::Key::K),
155          "l" | "L" => Some(egui::Key::L),
156          "m" | "M" => Some(egui::Key::M),
157          "n" | "N" => Some(egui::Key::N),
158          "o" | "O" => Some(egui::Key::O),
159          "p" | "P" => Some(egui::Key::P),
160          "q" | "Q" => Some(egui::Key::Q),
161          "r" | "R" => Some(egui::Key::R),
162          "s" | "S" => Some(egui::Key::S),
163          "t" | "T" => Some(egui::Key::T),
164          "u" | "U" => Some(egui::Key::U),
165          "v" | "V" => Some(egui::Key::V),
166          "w" | "W" => Some(egui::Key::W),
167          "x" | "X" => Some(egui::Key::X),
168          "y" | "Y" => Some(egui::Key::Y),
169          "z" | "Z" => Some(egui::Key::Z),
170  
171          _ => None,
172      }
173  }
174  
175  pub fn modifiers_from_event(event: &web_sys::KeyboardEvent) -> egui::Modifiers {
176      egui::Modifiers {
177          alt: event.alt_key(),
178          ctrl: event.ctrl_key(),
179          shift: event.shift_key(),
180  
181          // Ideally we should know if we are running or mac or not,
182          // but this works good enough for now.
183          mac_cmd: event.meta_key(),
184  
185          // Ideally we should know if we are running or mac or not,
186          // but this works good enough for now.
187          command: event.ctrl_key() || event.meta_key(),
188      }
189  }