diff --git a/src/core/text_edit.rs b/src/core/text_edit.rs index 028607f..64a9a7c 100644 --- a/src/core/text_edit.rs +++ b/src/core/text_edit.rs @@ -1,6 +1,10 @@ use crate::prelude::*; use cosmic_text::{Attrs, Cursor, Family, FontSystem, Metrics, Motion, Shaping}; use unicode_segmentation::UnicodeSegmentation; +use winit::{ + event::KeyEvent, + keyboard::{Key, NamedKey}, +}; pub struct TextEdit { pub attrs: TextAttrs, @@ -178,6 +182,41 @@ impl<'a> TextEditCtx<'a> { pub fn deselect(&mut self) { self.text.cursor = None; } + + pub fn apply_event(&mut self, event: &KeyEvent) -> TextInputResult { + match &event.logical_key { + Key::Named(named) => match named { + NamedKey::Backspace => self.backspace(), + NamedKey::Delete => self.delete(), + NamedKey::Space => self.insert(" "), + NamedKey::Enter => self.newline(), + NamedKey::ArrowRight => self.motion(Motion::Right), + NamedKey::ArrowLeft => self.motion(Motion::Left), + NamedKey::ArrowUp => self.motion(Motion::Up), + NamedKey::ArrowDown => self.motion(Motion::Down), + NamedKey::Escape => { + self.deselect(); + return TextInputResult::Unfocus; + } + _ => return TextInputResult::Unused, + }, + Key::Character(text) => self.insert(text), + _ => return TextInputResult::Unused, + } + TextInputResult::Used + } +} + +pub enum TextInputResult { + Used, + Unused, + Unfocus, +} + +impl TextInputResult { + pub fn unfocus(&self) -> bool { + matches!(self, TextInputResult::Unfocus) + } } impl FnOnce<(&mut Ui,)> for TextEditBuilder { diff --git a/src/layout/sense.rs b/src/layout/sense.rs index 2f8eb5b..2459695 100644 --- a/src/layout/sense.rs +++ b/src/layout/sense.rs @@ -1,6 +1,6 @@ use crate::{ layout::{Ui, UiRegion, Vec2}, - util::{HashMap, Id, bitflags}, + util::{HashMap, Id, IdVec, bitflags}, }; bitflags!(Sense, Senses, senses { @@ -34,7 +34,7 @@ pub struct Sensor { } pub type SensorMap = HashMap>; -pub type ActiveSensors = HashMap; +pub type ActiveSensors = IdVec; pub type SenseShape = UiRegion; pub struct SensorGroup { pub hover: ActivationState, @@ -59,15 +59,17 @@ pub trait UiCtx { pub fn run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { let active = std::mem::take(&mut ctx.ui().active.sensors); let mut map = std::mem::take(&mut ctx.ui().sensor_map); - for (id, shape) in active.iter() { + for (id, shape) in active.iter().rev() { let group = &mut map.get_mut(id).unwrap(); let region = shape.to_screen(window_size); let in_shape = cursor.exists && region.contains(cursor.pos); group.hover.update(in_shape); group.cursor.update(cursor.pressed && in_shape); + let mut ran = false; for sensor in &mut group.sensors { if should_run(sensor.senses, group.cursor, group.hover) { + ran = true; let sctx = SenseCtx { cursor: cursor.pos - region.top_left, size: region.bot_right - region.top_left, @@ -75,6 +77,9 @@ pub fn run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: (sensor.f)(ctx, sctx); } } + if ran { + break; + } } ctx.ui().sensor_map = map; ctx.ui().active.sensors = active; diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 417fe77..65916a1 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -1,16 +1,11 @@ use std::sync::Arc; use app::App; -use cosmic_text::{Family, Motion}; +use cosmic_text::Family; use render::Renderer; use senses::*; use ui::prelude::*; -use winit::{ - event::WindowEvent, - event_loop::ActiveEventLoop, - keyboard::{Key, NamedKey}, - window::Window, -}; +use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window}; use crate::testing::input::Input; @@ -207,29 +202,9 @@ impl Client { WindowEvent::KeyboardInput { event, .. } => { if let Some(sel) = &self.selected && event.state.is_pressed() + && self.ui.text(sel).apply_event(&event).unfocus() { - let w = &mut self.ui.text(sel); - match &event.logical_key { - Key::Named(named) => match named { - NamedKey::Backspace => w.backspace(), - NamedKey::Delete => w.delete(), - NamedKey::Space => w.insert(" "), - NamedKey::Enter => w.newline(), - NamedKey::ArrowRight => w.motion(Motion::Right), - NamedKey::ArrowLeft => w.motion(Motion::Left), - NamedKey::ArrowUp => w.motion(Motion::Up), - NamedKey::ArrowDown => w.motion(Motion::Down), - NamedKey::Escape => { - w.deselect(); - self.selected = None; - } - _ => (), - }, - Key::Character(text) => { - w.insert(text); - } - _ => (), - } + self.selected = None; } } _ => (), diff --git a/src/util/idvec.rs b/src/util/idvec.rs new file mode 100644 index 0000000..88f8dd1 --- /dev/null +++ b/src/util/idvec.rs @@ -0,0 +1,76 @@ +use std::ops::{Deref, Index, IndexMut}; + +use crate::util::{HashMap, id::Id}; + +/// a vec that's indexed with permanent ids rather than an offset +pub struct IdVec { + map: HashMap, + vec: Vec<(Id, T)>, +} + +impl IdVec { + pub fn insert(&mut self, id: Id, v: T) { + let idx = self.vec.len(); + self.map.insert(id.duplicate(), idx); + self.vec.push((id, v)); + } + + pub fn remove(&mut self, id: &Id) -> Option { + let i = self.map.remove(id)?; + let v = self.vec.remove(i).1; + for (id, _) in &self.vec[i..] { + *self.map.get_mut(id).unwrap() -= 1; + } + Some(v) + } + + pub fn len(&self) -> usize { + self.map.len() + } + + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + pub fn idx(&self, id: &Id) -> usize { + self.map[id] + } + + pub fn clear(&mut self) { + self.map.clear(); + self.vec.clear(); + } +} + +impl Default for IdVec { + fn default() -> Self { + Self { + map: Default::default(), + vec: Default::default(), + } + } +} + +impl Index<&Id> for IdVec { + type Output = T; + + fn index(&self, id: &Id) -> &Self::Output { + let i = self.idx(id); + &self.vec[i].1 + } +} + +impl IndexMut<&Id> for IdVec { + fn index_mut(&mut self, id: &Id) -> &mut Self::Output { + let i = self.idx(id); + &mut self.vec[i].1 + } +} + +impl Deref for IdVec { + type Target = Vec<(Id, T)>; + + fn deref(&self) -> &Self::Target { + &self.vec + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs index df7090f..ad58966 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,12 +1,14 @@ mod bitflags; mod borrow; mod id; +mod idvec; mod math; mod refcount; pub(crate) use bitflags::*; pub(crate) use borrow::*; pub(crate) use id::*; +pub(crate) use idvec::*; pub(crate) use math::*; pub(crate) use refcount::*;