diff --git a/bin/darkwallet/src/ui/editbox.rs b/bin/darkwallet/src/ui/editbox.rs index cb560d5fb..bcd703e6b 100644 --- a/bin/darkwallet/src/ui/editbox.rs +++ b/bin/darkwallet/src/ui/editbox.rs @@ -311,7 +311,12 @@ impl EditBox { TextRenderInfo { mesh, texture_id: atlas.texture_id } } - fn draw_selected(&self, mesh: &mut MeshBuilder, glyphs: &Vec, clip_h: f32) -> Result<()> { + fn draw_selected( + &self, + mesh: &mut MeshBuilder, + glyphs: &Vec, + clip_h: f32, + ) -> Result<()> { if self.selected.is_null(0)? || self.selected.is_null(1)? { // Nothing selected so do nothing return Ok(()) @@ -355,12 +360,7 @@ impl EditBox { } // We don't need to do manual clipping since MeshBuilder should do that - let select_rect = Rectangle { - x: start_x, - y: 0., - w: end_x - start_x, - h: clip_h - }; + let select_rect = Rectangle { x: start_x, y: 0., w: end_x - start_x, h: clip_h }; mesh.draw_box(&select_rect, hi_bg_color, &Rectangle::zero()); Ok(()) } @@ -379,8 +379,7 @@ impl EditBox { }; // First filter for only single digit keys - let disallowed_keys = ['\r', '\u{8}', '\u{7f}', '\t', '\n']; - if disallowed_keys.contains(&key) { + if DISALLOWED_CHARS.contains(&key) { return } @@ -389,6 +388,14 @@ impl EditBox { panic!("self destroyed before char_task was stopped!"); }; + if mods.ctrl || mods.alt { + if repeat { + return + } + self_.handle_shortcut(key, &mods).await; + return + } + let actions = { let mut repeater = self_.key_repeat.lock().unwrap(); repeater.key_down(PressedKey::Char(key), repeat) @@ -462,6 +469,26 @@ impl EditBox { self.redraw().await; } + async fn handle_shortcut(&self, key: char, mods: &KeyMods) { + debug!(target: "ui::editbox", "handle_shortcut({:?}, {:?})", key, mods); + + match key { + 'c' => { + if mods.ctrl { + self.copy_highlighted().unwrap(); + } + } + 'v' => { + if mods.ctrl { + if let Some(text) = window::clipboard_get() { + self.paste_text(text).await; + } + } + } + _ => {} + } + } + async fn handle_key(&self, key: &KeyCode, mods: &KeyMods) { debug!(target: "ui::editbox", "handle_key({:?}, {:?})", key, mods); match key { @@ -625,6 +652,52 @@ impl EditBox { self.cursor_pos.set(sel_start as u32); } + fn copy_highlighted(&self) -> Result<()> { + let start = self.selected.get_u32(0)? as usize; + let end = self.selected.get_u32(1)? as usize; + + let sel_start = std::cmp::min(start, end); + let sel_end = std::cmp::max(start, end); + + let mut text = String::new(); + + let glyphs = self.glyphs.lock().unwrap().clone(); + for (glyph_idx, glyph) in glyphs.iter().enumerate() { + if sel_start <= glyph_idx && glyph_idx < sel_end { + text.push_str(&glyph.substr); + } + } + + info!(target: "ui::editbox", "Copied '{}'", text); + window::clipboard_set(&text); + Ok(()) + } + + async fn paste_text(&self, key: String) { + let mut text = String::new(); + + let cursor_pos = self.cursor_pos.get(); + + if cursor_pos == 0 { + text = key.clone(); + } + + let glyphs = self.glyphs.lock().unwrap().clone(); + for (glyph_idx, glyph) in glyphs.iter().enumerate() { + text.push_str(&glyph.substr); + if cursor_pos == glyph_idx as u32 + 1 { + text.push_str(&key); + } + } + + self.text.set(text); + // Not always true lol + self.cursor_pos.set(cursor_pos + 1); + + self.apply_cursor_scrolling(); + self.redraw().await; + } + /// Beware of this method. Here be dragons. /// Possibly racy so we limit it just to cursor scrolling. fn cached_rect(&self) -> Rectangle { @@ -780,6 +853,13 @@ impl Stoppable for EditBox { } } +/// Filter these char events from being handled since we handle them +/// using the key_up/key_down events. +/// Avoids duplicate processing of keyboard input events. +static DISALLOWED_CHARS: &'static [char] = &['\r', '\u{8}', '\u{7f}', '\t', '\n']; + +/// These keycodes are handled via normal key_up/key_down events. +/// Anything in this list must be disallowed char events. static ALLOWED_KEYCODES: &'static [KeyCode] = &[ KeyCode::Left, KeyCode::Right,