diff --git a/src/core/sense.rs b/src/core/sense.rs index 1c1ca8e..869a14d 100644 --- a/src/core/sense.rs +++ b/src/core/sense.rs @@ -1,17 +1,17 @@ use crate::prelude::*; pub trait Sensable { - fn on(self, sense: Senses, f: impl SenseFn) -> impl WidgetIdFn; + fn on(self, sense: impl Into, f: impl SenseFn) -> impl WidgetIdFn; fn id_on( self, - senses: Senses, + senses: impl Into, f: impl FnMut(&WidgetId, &mut Ctx, SenseCtx) + 'static, ) -> impl WidgetIdFn where W: Widget; fn edit_on( self, - senses: Senses, + senses: impl Into, f: impl FnMut(&mut W, SenseCtx) + 'static, ) -> impl WidgetIdFn where @@ -20,13 +20,17 @@ pub trait Sensable { } impl, Ctx, Tag> Sensable for W { - fn on(self, senses: Senses, f: impl SenseFn) -> impl WidgetIdFn { + fn on( + self, + senses: impl Into, + f: impl SenseFn, + ) -> impl WidgetIdFn { move |ui| { let id = self.add(ui); ui.add_sensor( &id, Sensor { - senses, + senses: senses.into(), f: Box::new(f), }, ); @@ -35,7 +39,7 @@ impl, Ctx, Tag> Sensable for W { } fn id_on( self, - senses: Senses, + senses: impl Into, mut f: impl FnMut(&WidgetId, &mut Ctx, SenseCtx) + 'static, ) -> impl WidgetIdFn where @@ -48,7 +52,7 @@ impl, Ctx, Tag> Sensable for W { } fn edit_on( self, - senses: Senses, + senses: impl Into, mut f: impl FnMut(&mut W::Widget, SenseCtx) + 'static, ) -> impl WidgetIdFn where diff --git a/src/core/text_edit.rs b/src/core/text_edit.rs index 64a9a7c..2c2016e 100644 --- a/src/core/text_edit.rs +++ b/src/core/text_edit.rs @@ -123,7 +123,20 @@ impl<'a> TextEditCtx<'a> { self.text.cursor = Some(cursor); } } + pub fn insert(&mut self, text: &str) { + let mut lines = text.split('\n'); + let Some(first) = lines.next() else { + return; + }; + self.insert_inner(first); + for line in lines { + self.newline(); + self.insert_inner(line); + } + } + + fn insert_inner(&mut self, text: &str) { if let Some(cursor) = &mut self.text.cursor { let line = &mut self.text.buf.lines[cursor.line]; let mut line_text = line.text().to_string(); @@ -189,7 +202,7 @@ impl<'a> TextEditCtx<'a> { NamedKey::Backspace => self.backspace(), NamedKey::Delete => self.delete(), NamedKey::Space => self.insert(" "), - NamedKey::Enter => self.newline(), + NamedKey::Enter => self.insert("\n"), NamedKey::ArrowRight => self.motion(Motion::Right), NamedKey::ArrowLeft => self.motion(Motion::Left), NamedKey::ArrowUp => self.motion(Motion::Up), diff --git a/src/layout/sense.rs b/src/layout/sense.rs index 25e5aaf..edff13d 100644 --- a/src/layout/sense.rs +++ b/src/layout/sense.rs @@ -1,21 +1,68 @@ +use std::ops::{BitOr, Deref, DerefMut}; + use crate::{ layout::{Ui, UiRegion, Vec2}, - util::{HashMap, Id, IdVec, bitflags}, + util::{HashMap, Id, IdVec}, }; -bitflags!(Sense, Senses, senses { - 1 << 0; PressStart, PRESS_START, - 1 << 1; Pressing, PRESSING, - 1 << 2; PressEnd, PRESS_END, - 1 << 3; HoverStart, HOVER_START, - 1 << 4; Hovering, HOVERING, - 1 << 5; HoverEnd, HOVER_END, -}); +pub trait CursorCtx { + fn cursor_state(&self) -> &CursorState; +} +pub enum Button { + Left, + Right, + Middle, +} + +pub enum Sense { + PressStart(Button), + Pressing(Button), + PressEnd(Button), + HoverStart, + Hovering, + HoverEnd, +} + +pub struct Senses(Vec); + +impl Sense { + pub fn click() -> Self { + Self::PressStart(Button::Left) + } + pub fn unclick() -> Self { + Self::PressEnd(Button::Left) + } +} + +#[derive(Default, Clone)] pub struct CursorState { pub pos: Vec2, pub exists: bool, - pub pressed: bool, + pub buttons: CursorButtons, +} + +#[derive(Default, Clone)] +pub struct CursorButtons { + pub left: ActivationState, + pub middle: ActivationState, + pub right: ActivationState, +} + +impl CursorButtons { + pub fn select(&self, button: &Button) -> &ActivationState { + match button { + Button::Left => &self.left, + Button::Right => &self.right, + Button::Middle => &self.middle, + } + } + + pub fn end_frame(&mut self) { + self.left.end_frame(); + self.middle.end_frame(); + self.right.end_frame(); + } } #[derive(Debug, Clone, Copy, Default, PartialEq)] @@ -37,7 +84,6 @@ pub type ActiveSensors = IdVec; pub type SenseShape = UiRegion; pub struct SensorGroup { pub hover: ActivationState, - pub cursor: ActivationState, pub sensors: Vec>, } @@ -63,11 +109,13 @@ pub fn run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: 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); + if group.hover == ActivationState::Off { + continue; + } let mut ran = false; for sensor in &mut group.sensors { - if should_run(sensor.senses, group.cursor, group.hover) { + if should_run(&sensor.senses, &cursor.buttons, group.hover) { ran = true; let sctx = SenseCtx { cursor: cursor.pos - region.top_left, @@ -84,12 +132,12 @@ pub fn run_sensors(ctx: &mut Ctx, cursor: &CursorState, window_size: ctx.ui().active.sensors = active; } -pub fn should_run(senses: Senses, cursor: ActivationState, hover: ActivationState) -> bool { +pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool { for sense in senses.iter() { if match sense { - Sense::PressStart => cursor.is_start(), - Sense::Pressing => cursor.is_on(), - Sense::PressEnd => cursor.is_end(), + Sense::PressStart(button) => cursor.select(button).is_start(), + Sense::Pressing(button) => cursor.select(button).is_on(), + Sense::PressEnd(button) => cursor.select(button).is_end(), Sense::HoverStart => hover.is_start(), Sense::Hovering => hover.is_on(), Sense::HoverEnd => hover.is_end(), @@ -133,14 +181,58 @@ impl ActivationState { }, } } + + pub fn end_frame(&mut self) { + match self { + Self::Start => *self = Self::On, + Self::End => *self = Self::Off, + _ => (), + } + } } impl Default for SensorGroup { fn default() -> Self { Self { hover: Default::default(), - cursor: Default::default(), sensors: Default::default(), } } } + +impl Deref for Senses { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Senses { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From for Senses { + fn from(val: Sense) -> Self { + Senses(vec![val]) + } +} + +impl BitOr for Sense { + type Output = Senses; + + fn bitor(self, rhs: Self) -> Self::Output { + Senses(vec![self, rhs]) + } +} + +impl BitOr for Senses { + type Output = Self; + + fn bitor(mut self, rhs: Sense) -> Self::Output { + self.0.push(rhs); + self + } +} diff --git a/src/render/shader.wgsl b/src/render/shader.wgsl index 3d70577..9a3ae21 100644 --- a/src/render/shader.wgsl +++ b/src/render/shader.wgsl @@ -1,5 +1,5 @@ -const RECT: u32 = 0; -const TEXTURE: u32 = 1; +const RECT: u32 = 0u; +const TEXTURE: u32 = 1u; @group(0) @binding(0) var window: WindowUniform; diff --git a/src/testing/input.rs b/src/testing/input.rs index ab1fe97..bd273cc 100644 --- a/src/testing/input.rs +++ b/src/testing/input.rs @@ -1,31 +1,40 @@ use ui::layout::{CursorState, Vec2}; -use winit::event::WindowEvent; +use winit::event::{MouseButton, WindowEvent}; use crate::testing::Client; #[derive(Default)] pub struct Input { - mouse_pos: Vec2, - mouse_pressed: bool, - mouse_exists: bool, + cursor: CursorState, } impl Input { pub fn event(&mut self, event: &WindowEvent) { match event { WindowEvent::CursorMoved { position, .. } => { - self.mouse_pos = Vec2::new(position.x as f32, position.y as f32); - self.mouse_exists = true; + self.cursor.pos = Vec2::new(position.x as f32, position.y as f32); + self.cursor.exists = true; } - WindowEvent::MouseInput { state, button, .. } => match button { - winit::event::MouseButton::Left => { - self.mouse_pressed = state.is_pressed(); + WindowEvent::MouseInput { state, button, .. } => { + let buttons = &mut self.cursor.buttons; + let pressed = state.is_pressed(); + match button { + MouseButton::Left => buttons.left.update(pressed), + MouseButton::Right => buttons.right.update(pressed), + MouseButton::Middle => buttons.middle.update(pressed), + _ => (), } - _ => (), - }, + } + WindowEvent::CursorLeft { .. } => { + self.cursor.exists = false; + } _ => (), } } + + pub fn end_frame(&mut self) { + self.cursor.buttons.end_frame(); + } } impl Client { @@ -34,11 +43,7 @@ impl Client { (size.width, size.height).into() } - pub fn cursor_state(&self) -> CursorState { - CursorState { - pos: self.input.mouse_pos, - exists: self.input.mouse_exists, - pressed: self.input.mouse_pressed, - } + pub fn cursor_state(&self) -> &CursorState { + &self.input.cursor } } diff --git a/src/testing/mod.rs b/src/testing/mod.rs index 65916a1..44ccd2c 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use app::App; use cosmic_text::Family; use render::Renderer; -use senses::*; use ui::prelude::*; use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window}; @@ -73,7 +72,7 @@ impl Client { let add_button = rect(Color::LIME) .radius(30) - .on(PRESS_START, move |ctx: &mut Client, _| { + .on(Sense::click(), move |ctx: &mut Client, _| { let child = ctx .ui .add(image(include_bytes!("assets/sungals.png")).center()) @@ -85,7 +84,7 @@ impl Client { let del_button = rect(Color::RED) .radius(30) - .on(PRESS_START, move |ctx: &mut Client, _| { + .on(Sense::click(), move |ctx: &mut Client, _| { ctx.ui[span_add].children.pop(); }) .size(150) @@ -124,7 +123,7 @@ impl Client { texts, text_edit("add") .font_size(30) - .id_on(PRESS_START, |id, client: &mut Client, ctx| { + .id_on(Sense::click(), |id, client: &mut Client, ctx| { client.ui.text(id).select(ctx.cursor, ctx.size); client.selected = Some(id.clone()); }) @@ -135,14 +134,14 @@ impl Client { let switch_button = |color, to, label| { let rect = rect(color) - .id_on(PRESS_START, move |id, ctx: &mut Client, _| { + .id_on(Sense::click(), move |id, ctx: &mut Client, _| { ctx.ui[main].inner.set_static(to); ctx.ui[id].color = color.add_rgb(-0.2); }) - .edit_on(HOVER_START | PRESS_END, move |r, _| { + .edit_on(Sense::HoverStart | Sense::unclick(), move |r, _| { r.color = color.add_rgb(0.4); }) - .edit_on(HOVER_END, move |r, _| { + .edit_on(Sense::HoverEnd, move |r, _| { r.color = color; }); (rect, text(label).font_size(30)).stack() @@ -185,7 +184,7 @@ impl Client { pub fn event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) { self.input.event(&event); - let cursor_state = self.cursor_state(); + let cursor_state = self.cursor_state().clone(); let window_size = self.window_size(); run_sensors(self, &cursor_state, window_size); match event { @@ -221,6 +220,7 @@ impl Client { if self.ui.needs_redraw() { self.renderer.window().request_redraw(); } + self.input.end_frame(); } } diff --git a/src/util/bitflags.rs b/src/util/bitflags.rs deleted file mode 100644 index 907162c..0000000 --- a/src/util/bitflags.rs +++ /dev/null @@ -1,52 +0,0 @@ -macro_rules! bitflags { - ($enum:ident, $struct:ident, $mod:ident {$($val:expr; $ename:ident, $sname:ident,)*}) => { - #[repr(u32)] - #[derive(Clone, Copy, PartialEq)] - pub enum $enum { - $($ename = $val,)* - } - #[derive(Clone, Copy, PartialEq)] - pub struct $struct(u32); - #[allow(non_upper_case_globals)] - impl $struct { - $(pub const $sname: Self = Self($enum::$ename as u32);)* - - pub fn iter(&self) -> impl Iterator { - $crate::util::Biterator::new(self.0).map(|v| unsafe {std::mem::transmute(v)}) - } - } - - impl std::ops::BitOr for $struct { - type Output = Self; - fn bitor(self, rhs: $struct) -> Self { - Self(self.0 | rhs.0) - } - } - pub mod $mod { - use super::*; - $(pub const $sname: $struct = $struct::$sname;)* - } - }; -} -pub(crate) use bitflags; - -pub struct Biterator(u32); - -impl Iterator for Biterator { - type Item = u32; - - fn next(&mut self) -> Option { - if self.0 == 0 { - return None; - } - let val = 1 << self.0.trailing_zeros(); - self.0 &= !val; - Some(val) - } -} - -impl Biterator { - pub fn new(val: u32) -> Self { - Self(val) - } -} diff --git a/src/util/mod.rs b/src/util/mod.rs index ad58966..19d7865 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,11 +1,9 @@ -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::*;