sensors now run in correct order

This commit is contained in:
2025-09-15 21:13:23 -04:00
parent 2700c31c13
commit 90cbc2524a
5 changed files with 129 additions and 32 deletions

View File

@@ -1,6 +1,10 @@
use crate::prelude::*; use crate::prelude::*;
use cosmic_text::{Attrs, Cursor, Family, FontSystem, Metrics, Motion, Shaping}; use cosmic_text::{Attrs, Cursor, Family, FontSystem, Metrics, Motion, Shaping};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use winit::{
event::KeyEvent,
keyboard::{Key, NamedKey},
};
pub struct TextEdit { pub struct TextEdit {
pub attrs: TextAttrs, pub attrs: TextAttrs,
@@ -178,6 +182,41 @@ impl<'a> TextEditCtx<'a> {
pub fn deselect(&mut self) { pub fn deselect(&mut self) {
self.text.cursor = None; 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<Ctx> FnOnce<(&mut Ui<Ctx>,)> for TextEditBuilder { impl<Ctx> FnOnce<(&mut Ui<Ctx>,)> for TextEditBuilder {

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
layout::{Ui, UiRegion, Vec2}, layout::{Ui, UiRegion, Vec2},
util::{HashMap, Id, bitflags}, util::{HashMap, Id, IdVec, bitflags},
}; };
bitflags!(Sense, Senses, senses { bitflags!(Sense, Senses, senses {
@@ -34,7 +34,7 @@ pub struct Sensor<Ctx> {
} }
pub type SensorMap<Ctx> = HashMap<Id, SensorGroup<Ctx>>; pub type SensorMap<Ctx> = HashMap<Id, SensorGroup<Ctx>>;
pub type ActiveSensors = HashMap<Id, SenseShape>; pub type ActiveSensors = IdVec<SenseShape>;
pub type SenseShape = UiRegion; pub type SenseShape = UiRegion;
pub struct SensorGroup<Ctx> { pub struct SensorGroup<Ctx> {
pub hover: ActivationState, pub hover: ActivationState,
@@ -59,15 +59,17 @@ pub trait UiCtx {
pub fn run_sensors<Ctx: UiCtx>(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) { pub fn run_sensors<Ctx: UiCtx>(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
let active = std::mem::take(&mut ctx.ui().active.sensors); let active = std::mem::take(&mut ctx.ui().active.sensors);
let mut map = std::mem::take(&mut ctx.ui().sensor_map); 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 group = &mut map.get_mut(id).unwrap();
let region = shape.to_screen(window_size); let region = shape.to_screen(window_size);
let in_shape = cursor.exists && region.contains(cursor.pos); let in_shape = cursor.exists && region.contains(cursor.pos);
group.hover.update(in_shape); group.hover.update(in_shape);
group.cursor.update(cursor.pressed && in_shape); group.cursor.update(cursor.pressed && in_shape);
let mut ran = false;
for sensor in &mut group.sensors { for sensor in &mut group.sensors {
if should_run(sensor.senses, group.cursor, group.hover) { if should_run(sensor.senses, group.cursor, group.hover) {
ran = true;
let sctx = SenseCtx { let sctx = SenseCtx {
cursor: cursor.pos - region.top_left, cursor: cursor.pos - region.top_left,
size: region.bot_right - region.top_left, size: region.bot_right - region.top_left,
@@ -75,6 +77,9 @@ pub fn run_sensors<Ctx: UiCtx>(ctx: &mut Ctx, cursor: &CursorState, window_size:
(sensor.f)(ctx, sctx); (sensor.f)(ctx, sctx);
} }
} }
if ran {
break;
}
} }
ctx.ui().sensor_map = map; ctx.ui().sensor_map = map;
ctx.ui().active.sensors = active; ctx.ui().active.sensors = active;

View File

@@ -1,16 +1,11 @@
use std::sync::Arc; use std::sync::Arc;
use app::App; use app::App;
use cosmic_text::{Family, Motion}; use cosmic_text::Family;
use render::Renderer; use render::Renderer;
use senses::*; use senses::*;
use ui::prelude::*; use ui::prelude::*;
use winit::{ use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window};
event::WindowEvent,
event_loop::ActiveEventLoop,
keyboard::{Key, NamedKey},
window::Window,
};
use crate::testing::input::Input; use crate::testing::input::Input;
@@ -207,29 +202,9 @@ impl Client {
WindowEvent::KeyboardInput { event, .. } => { WindowEvent::KeyboardInput { event, .. } => {
if let Some(sel) = &self.selected if let Some(sel) = &self.selected
&& event.state.is_pressed() && event.state.is_pressed()
&& self.ui.text(sel).apply_event(&event).unfocus()
{ {
let w = &mut self.ui.text(sel); self.selected = None;
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);
}
_ => (),
}
} }
} }
_ => (), _ => (),

76
src/util/idvec.rs Normal file
View File

@@ -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<T> {
map: HashMap<Id, usize>,
vec: Vec<(Id, T)>,
}
impl<T> IdVec<T> {
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<T> {
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<T> Default for IdVec<T> {
fn default() -> Self {
Self {
map: Default::default(),
vec: Default::default(),
}
}
}
impl<T> Index<&Id> for IdVec<T> {
type Output = T;
fn index(&self, id: &Id) -> &Self::Output {
let i = self.idx(id);
&self.vec[i].1
}
}
impl<T> IndexMut<&Id> for IdVec<T> {
fn index_mut(&mut self, id: &Id) -> &mut Self::Output {
let i = self.idx(id);
&mut self.vec[i].1
}
}
impl<T> Deref for IdVec<T> {
type Target = Vec<(Id, T)>;
fn deref(&self) -> &Self::Target {
&self.vec
}
}

View File

@@ -1,12 +1,14 @@
mod bitflags; mod bitflags;
mod borrow; mod borrow;
mod id; mod id;
mod idvec;
mod math; mod math;
mod refcount; mod refcount;
pub(crate) use bitflags::*; pub(crate) use bitflags::*;
pub(crate) use borrow::*; pub(crate) use borrow::*;
pub(crate) use id::*; pub(crate) use id::*;
pub(crate) use idvec::*;
pub(crate) use math::*; pub(crate) use math::*;
pub(crate) use refcount::*; pub(crate) use refcount::*;