REAL SENSORS
This commit is contained in:
@@ -1,58 +1,52 @@
|
|||||||
use crate::{
|
use crate::{Sense, SenseFn, SenseTrigger, Sensor, Ui, Widget, WidgetIdFn, WidgetLike};
|
||||||
Painter, Sense, SenseFn, SenseShape, SenseTrigger, Ui, Widget, WidgetFn, WidgetId, WidgetLike,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Sensor<Ctx> {
|
|
||||||
inner: WidgetId,
|
|
||||||
sense: Sense,
|
|
||||||
f: Box<dyn SenseFn<Ctx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: 'static> Widget<Ctx> for Sensor<Ctx> {
|
|
||||||
fn draw(&self, painter: &mut Painter<Ctx>) {
|
|
||||||
painter.sense(
|
|
||||||
SenseTrigger {
|
|
||||||
shape: painter.region,
|
|
||||||
sense: self.sense,
|
|
||||||
},
|
|
||||||
self.f.box_clone(),
|
|
||||||
);
|
|
||||||
painter.draw(self.inner.as_any());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait SenseCtx: 'static {
|
pub trait SenseCtx: 'static {
|
||||||
fn active(&mut self, trigger: &SenseTrigger) -> bool;
|
fn active(&mut self, trigger: &SenseTrigger) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WidgetSenseFn<W: Widget<Ctx>, Ctx> = Fn(&WidgetId<W>, &mut Ui<Ctx>, &mut Ctx) + 'static;
|
pub trait Sensable<W, Ctx: SenseCtx, Tag> {
|
||||||
|
fn sense(
|
||||||
pub trait Sensable<W: Widget<Ctx>, Ctx: 'static, Tag> {
|
|
||||||
// copied here so LSP can at least get the UI and id
|
|
||||||
fn sense<F: WidgetSenseFn<W, Ctx> + Clone>(
|
|
||||||
self,
|
self,
|
||||||
sense: Sense,
|
sense: Sense,
|
||||||
f: F,
|
// trait copied here bc rust analyzer skill issue
|
||||||
) -> impl WidgetFn<Sensor<Ctx>, Ctx>;
|
f: impl Fn(&mut Ui<Ctx>, &mut Ctx) + 'static + Clone,
|
||||||
|
) -> impl WidgetIdFn<W, Ctx>;
|
||||||
|
fn sense_and_edit(
|
||||||
|
self,
|
||||||
|
sense: Sense,
|
||||||
|
// trait copied here bc rust analyzer skill issue
|
||||||
|
f: impl Fn(&mut W, &mut Ctx) + 'static + Clone,
|
||||||
|
) -> impl WidgetIdFn<W, Ctx>
|
||||||
|
where
|
||||||
|
W: Widget<Ctx>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: WidgetLike<Ctx, Tag>, Ctx: SenseCtx, Tag> Sensable<W::Widget, Ctx, Tag> for W
|
impl<W: WidgetLike<Ctx, Tag>, Ctx: SenseCtx, Tag> Sensable<W::Widget, Ctx, Tag> for W {
|
||||||
where
|
fn sense(self, sense: Sense, f: impl SenseFn<Ctx> + Clone) -> impl WidgetIdFn<W::Widget, Ctx> {
|
||||||
W::Widget: Widget<Ctx>,
|
|
||||||
{
|
|
||||||
fn sense<F: WidgetSenseFn<W::Widget, Ctx> + Clone>(
|
|
||||||
self,
|
|
||||||
sense: Sense,
|
|
||||||
f: F,
|
|
||||||
) -> impl WidgetFn<Sensor<Ctx>, Ctx> {
|
|
||||||
move |ui| {
|
move |ui| {
|
||||||
let inner_arg = self.add(ui);
|
let id = self.add(ui);
|
||||||
let inner = inner_arg.clone().erase_type();
|
ui.add_sensor(
|
||||||
Sensor {
|
&id,
|
||||||
inner,
|
Sensor {
|
||||||
sense,
|
sense,
|
||||||
f: Box::new(move |ui, ctx| (f)(&inner_arg, ui, ctx)),
|
f: Box::new(f),
|
||||||
}
|
},
|
||||||
|
);
|
||||||
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn sense_and_edit(
|
||||||
|
self,
|
||||||
|
sense: Sense,
|
||||||
|
// trait copied here bc rust analyzer skill issue
|
||||||
|
f: impl Fn(&mut W::Widget, &mut Ctx) + 'static + Clone,
|
||||||
|
) -> impl WidgetIdFn<W::Widget, Ctx>
|
||||||
|
where
|
||||||
|
W::Widget: Widget<Ctx>,
|
||||||
|
{
|
||||||
|
self.with_id(move |ui, id| {
|
||||||
|
let id2 = id.clone();
|
||||||
|
ui.add(id.sense(sense, move |ui, ctx| f(&mut ui[&id2], ctx)))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#![allow(clippy::multiple_bound_locations)]
|
#![allow(clippy::multiple_bound_locations)]
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, bytemuck::Zeroable)]
|
#[derive(Clone, Copy, bytemuck::Zeroable, Debug)]
|
||||||
pub struct Color<T: ColorNum> {
|
pub struct Color<T: ColorNum> {
|
||||||
pub r: T,
|
pub r: T,
|
||||||
pub g: T,
|
pub g: T,
|
||||||
@@ -20,10 +20,9 @@ impl<T: ColorNum> Color<T> {
|
|||||||
pub const GREEN: Self = Self::rgb(T::MIN, T::MAX, T::MIN);
|
pub const GREEN: Self = Self::rgb(T::MIN, T::MAX, T::MIN);
|
||||||
pub const CYAN: Self = Self::rgb(T::MIN, T::MAX, T::MAX);
|
pub const CYAN: Self = Self::rgb(T::MIN, T::MAX, T::MAX);
|
||||||
pub const BLUE: Self = Self::rgb(T::MIN, T::MIN, T::MAX);
|
pub const BLUE: Self = Self::rgb(T::MIN, T::MIN, T::MAX);
|
||||||
|
pub const INDIGO: Self = Self::rgb(T::MIN, T::MID, T::MAX);
|
||||||
pub const MAGENTA: Self = Self::rgb(T::MAX, T::MIN, T::MAX);
|
pub const MAGENTA: Self = Self::rgb(T::MAX, T::MIN, T::MAX);
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ColorNum> Color<T> {
|
|
||||||
pub const fn new(r: T, g: T, b: T, a: T) -> Self {
|
pub const fn new(r: T, g: T, b: T, a: T) -> Self {
|
||||||
Self { r, g, b, a }
|
Self { r, g, b, a }
|
||||||
}
|
}
|
||||||
@@ -48,4 +47,54 @@ impl ColorNum for u8 {
|
|||||||
const MAX: Self = u8::MAX;
|
const MAX: Self = u8::MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ColorNum for f32 {
|
||||||
|
const MIN: Self = 0.0;
|
||||||
|
const MID: Self = 0.5;
|
||||||
|
const MAX: Self = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl bytemuck::Pod for Color<u8> {}
|
unsafe impl bytemuck::Pod for Color<u8> {}
|
||||||
|
|
||||||
|
pub trait F32Conversion {
|
||||||
|
fn to(self) -> f32;
|
||||||
|
fn from(x: f32) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ColorNum + F32Conversion> Color<T> {
|
||||||
|
pub fn mul_rgb(self, x: impl F32Conversion) -> Self {
|
||||||
|
let x = x.to();
|
||||||
|
Self {
|
||||||
|
r: T::from(self.r.to() * x),
|
||||||
|
g: T::from(self.g.to() * x),
|
||||||
|
b: T::from(self.b.to() * x),
|
||||||
|
a: self.a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn add_rgb(self, x: impl F32Conversion) -> Self {
|
||||||
|
let x = x.to();
|
||||||
|
Self {
|
||||||
|
r: T::from(self.r.to() + x),
|
||||||
|
g: T::from(self.g.to() + x),
|
||||||
|
b: T::from(self.b.to() + x),
|
||||||
|
a: self.a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl F32Conversion for f32 {
|
||||||
|
fn to(self) -> f32 {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn from(x: f32) -> Self {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl F32Conversion for u8 {
|
||||||
|
fn to(self) -> f32 {
|
||||||
|
self as f32 / 255.0
|
||||||
|
}
|
||||||
|
fn from(x: f32) -> Self {
|
||||||
|
(x * 255.0).clamp(0.0, 255.0) as Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod color;
|
mod color;
|
||||||
mod painter;
|
mod painter;
|
||||||
mod region;
|
mod region;
|
||||||
|
mod sense;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod vec2;
|
mod vec2;
|
||||||
mod widget;
|
mod widget;
|
||||||
@@ -8,6 +9,7 @@ mod widget;
|
|||||||
pub use color::*;
|
pub use color::*;
|
||||||
pub use painter::*;
|
pub use painter::*;
|
||||||
pub use region::*;
|
pub use region::*;
|
||||||
|
pub use sense::*;
|
||||||
pub use ui::*;
|
pub use ui::*;
|
||||||
pub use vec2::*;
|
pub use vec2::*;
|
||||||
pub use widget::*;
|
pub use widget::*;
|
||||||
|
|||||||
@@ -1,22 +1,29 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
SenseFn, SenseTrigger, Sensors, UiRegion, WidgetId, Widgets,
|
ActiveSensor, ActiveSensors, SenseTrigger, SensorMap, UiRegion, WidgetId, Widgets,
|
||||||
primitive::{PrimitiveData, PrimitiveInstance, Primitives},
|
primitive::{PrimitiveData, PrimitiveInstance, Primitives},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Painter<'a, Ctx> {
|
pub struct Painter<'a, Ctx: 'static> {
|
||||||
nodes: &'a Widgets<Ctx>,
|
nodes: &'a Widgets<Ctx>,
|
||||||
ctx: &'a mut Ctx,
|
ctx: &'a mut Ctx,
|
||||||
sensors: &'a mut Sensors<Ctx>,
|
sensors_map: &'a mut SensorMap<Ctx>,
|
||||||
|
active_sensors: &'a mut ActiveSensors<Ctx>,
|
||||||
primitives: Primitives,
|
primitives: Primitives,
|
||||||
pub region: UiRegion,
|
pub region: UiRegion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Ctx> Painter<'a, Ctx> {
|
impl<'a, Ctx> Painter<'a, Ctx> {
|
||||||
pub fn new(nodes: &'a Widgets<Ctx>, ctx: &'a mut Ctx, sensors: &'a mut Sensors<Ctx>) -> Self {
|
pub fn new(
|
||||||
|
nodes: &'a Widgets<Ctx>,
|
||||||
|
ctx: &'a mut Ctx,
|
||||||
|
sensors_map: &'a mut SensorMap<Ctx>,
|
||||||
|
active_sensors: &'a mut ActiveSensors<Ctx>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
nodes,
|
nodes,
|
||||||
ctx,
|
ctx,
|
||||||
sensors,
|
active_sensors,
|
||||||
|
sensors_map,
|
||||||
primitives: Primitives::default(),
|
primitives: Primitives::default(),
|
||||||
region: UiRegion::full(),
|
region: UiRegion::full(),
|
||||||
}
|
}
|
||||||
@@ -37,6 +44,20 @@ impl<'a, Ctx> Painter<'a, Ctx> {
|
|||||||
where
|
where
|
||||||
Ctx: 'static,
|
Ctx: 'static,
|
||||||
{
|
{
|
||||||
|
if let Some(sensors) = self.sensors_map.get(&id.id) {
|
||||||
|
self.active_sensors.push(
|
||||||
|
sensors
|
||||||
|
.iter()
|
||||||
|
.map(|sensor| ActiveSensor {
|
||||||
|
trigger: SenseTrigger {
|
||||||
|
shape: self.region,
|
||||||
|
sense: sensor.sense,
|
||||||
|
},
|
||||||
|
f: sensor.f.box_clone(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
self.nodes.get_dyn(id).draw(self);
|
self.nodes.get_dyn(id).draw(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,10 +71,6 @@ impl<'a, Ctx> Painter<'a, Ctx> {
|
|||||||
self.region = old;
|
self.region = old;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sense(&mut self, trigger: SenseTrigger, f: Box<dyn SenseFn<Ctx>>) {
|
|
||||||
self.sensors.push((trigger, f));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish(self) -> Primitives {
|
pub fn finish(self) -> Primitives {
|
||||||
self.primitives
|
self.primitives
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ impl UiRegion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ScreenRect {
|
pub struct ScreenRect {
|
||||||
top_left: Vec2,
|
top_left: Vec2,
|
||||||
bot_right: Vec2,
|
bot_right: Vec2,
|
||||||
|
|||||||
44
src/layout/sense.rs
Normal file
44
src/layout/sense.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use crate::{HashMap, Ui, UiRegion, util::Id};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum Sense {
|
||||||
|
Press,
|
||||||
|
Held,
|
||||||
|
Hover,
|
||||||
|
NoHover,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sensor<Ctx> {
|
||||||
|
pub sense: Sense,
|
||||||
|
pub f: Box<dyn SenseFn<Ctx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ActiveSensor<Ctx> {
|
||||||
|
pub trigger: SenseTrigger,
|
||||||
|
pub f: Box<dyn SenseFn<Ctx>>,
|
||||||
|
}
|
||||||
|
impl<Ctx: 'static> Clone for ActiveSensor<Ctx> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
trigger: self.trigger.clone(),
|
||||||
|
f: self.f.box_clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub type SensorMap<Ctx> = HashMap<Id, Vec<Sensor<Ctx>>>;
|
||||||
|
pub type ActiveSensors<Ctx> = Vec<Vec<ActiveSensor<Ctx>>>;
|
||||||
|
pub trait SenseFn_<Ctx> = FnMut(&mut Ui<Ctx>, &mut Ctx) + 'static;
|
||||||
|
pub type SenseShape = UiRegion;
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SenseTrigger {
|
||||||
|
pub shape: SenseShape,
|
||||||
|
pub sense: Sense,
|
||||||
|
}
|
||||||
|
pub trait SenseFn<Ctx>: SenseFn_<Ctx> {
|
||||||
|
fn box_clone(&self) -> Box<dyn SenseFn<Ctx>>;
|
||||||
|
}
|
||||||
|
impl<F: SenseFn_<Ctx> + Clone, Ctx> SenseFn<Ctx> for F {
|
||||||
|
fn box_clone(&self) -> Box<dyn SenseFn<Ctx>> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
HashMap, Painter, SenseCtx, UiRegion, Widget, WidgetId, WidgetLike,
|
ActiveSensors, HashMap, Painter, SenseCtx, Sensor, SensorMap, Widget, WidgetId, WidgetLike,
|
||||||
primitive::Primitives,
|
primitive::Primitives,
|
||||||
util::{IDTracker, Id},
|
util::{IDTracker, Id},
|
||||||
};
|
};
|
||||||
@@ -13,7 +13,8 @@ pub struct Ui<Ctx> {
|
|||||||
base: Option<WidgetId>,
|
base: Option<WidgetId>,
|
||||||
widgets: Widgets<Ctx>,
|
widgets: Widgets<Ctx>,
|
||||||
updates: Vec<WidgetId>,
|
updates: Vec<WidgetId>,
|
||||||
sensors: Sensors<Ctx>,
|
active_sensors: ActiveSensors<Ctx>,
|
||||||
|
sensor_map: SensorMap<Ctx>,
|
||||||
primitives: Primitives,
|
primitives: Primitives,
|
||||||
full_redraw: bool,
|
full_redraw: bool,
|
||||||
}
|
}
|
||||||
@@ -21,27 +22,6 @@ pub struct Ui<Ctx> {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Widgets<Ctx>(HashMap<Id, Box<dyn Widget<Ctx>>>);
|
pub struct Widgets<Ctx>(HashMap<Id, Box<dyn Widget<Ctx>>>);
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum Sense {
|
|
||||||
Click,
|
|
||||||
Hover,
|
|
||||||
}
|
|
||||||
pub type Sensors<Ctx> = Vec<(SenseTrigger, Box<dyn SenseFn<Ctx>>)>;
|
|
||||||
pub trait SenseFn_<Ctx> = Fn(&mut Ui<Ctx>, &mut Ctx) + 'static;
|
|
||||||
pub type SenseShape = UiRegion;
|
|
||||||
pub struct SenseTrigger {
|
|
||||||
pub shape: SenseShape,
|
|
||||||
pub sense: Sense,
|
|
||||||
}
|
|
||||||
pub trait SenseFn<Ctx>: SenseFn_<Ctx> {
|
|
||||||
fn box_clone(&self) -> Box<dyn SenseFn<Ctx>>;
|
|
||||||
}
|
|
||||||
impl<F: SenseFn_<Ctx> + Clone, Ctx> SenseFn<Ctx> for F {
|
|
||||||
fn box_clone(&self) -> Box<dyn SenseFn<Ctx>> {
|
|
||||||
Box::new(self.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx> Ui<Ctx> {
|
impl<Ctx> Ui<Ctx> {
|
||||||
pub fn add<W: Widget<Ctx>, Tag>(
|
pub fn add<W: Widget<Ctx>, Tag>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -69,7 +49,10 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
self.full_redraw = true;
|
self.full_redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self
|
||||||
|
where
|
||||||
|
Ctx: 'static,
|
||||||
|
{
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,23 +72,35 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
where
|
where
|
||||||
Ctx: 'static,
|
Ctx: 'static,
|
||||||
{
|
{
|
||||||
self.sensors.clear();
|
self.active_sensors.clear();
|
||||||
let mut painter = Painter::new(&self.widgets, ctx, &mut self.sensors);
|
let mut painter = Painter::new(
|
||||||
|
&self.widgets,
|
||||||
|
ctx,
|
||||||
|
&mut self.sensor_map,
|
||||||
|
&mut self.active_sensors,
|
||||||
|
);
|
||||||
if let Some(base) = &self.base {
|
if let Some(base) = &self.base {
|
||||||
painter.draw(base);
|
painter.draw(base);
|
||||||
}
|
}
|
||||||
self.primitives = painter.finish();
|
self.primitives = painter.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_sensor<W>(&mut self, id: &WidgetId<W>, f: Sensor<Ctx>) {
|
||||||
|
self.sensor_map
|
||||||
|
.entry(id.id.duplicate())
|
||||||
|
.or_default()
|
||||||
|
.push(f);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run_sensors(&mut self, ctx: &mut Ctx)
|
pub fn run_sensors(&mut self, ctx: &mut Ctx)
|
||||||
where
|
where
|
||||||
Ctx: SenseCtx,
|
Ctx: SenseCtx + 'static,
|
||||||
{
|
{
|
||||||
for (t, f) in self.sensors.iter().rev() {
|
for sensors in self.active_sensors.clone().iter().rev() {
|
||||||
if ctx.active(t) {
|
for sensor in sensors {
|
||||||
let f = f.as_ref().box_clone();
|
if ctx.active(&sensor.trigger) {
|
||||||
f(self, ctx);
|
(sensor.f.box_clone())(self, ctx);
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +178,7 @@ impl<Ctx> dyn Widget<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx> Default for Ui<Ctx> {
|
impl<Ctx: 'static> Default for Ui<Ctx> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ids: Default::default(),
|
ids: Default::default(),
|
||||||
@@ -192,7 +187,8 @@ impl<Ctx> Default for Ui<Ctx> {
|
|||||||
updates: Default::default(),
|
updates: Default::default(),
|
||||||
primitives: Default::default(),
|
primitives: Default::default(),
|
||||||
full_redraw: false,
|
full_redraw: false,
|
||||||
sensors: Default::default(),
|
active_sensors: Default::default(),
|
||||||
|
sensor_map: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,19 @@ pub struct AnyWidget;
|
|||||||
/// W does not need to implement widget so that AnyWidget is valid;
|
/// W does not need to implement widget so that AnyWidget is valid;
|
||||||
/// Instead, add generic bounds on methods that take an ID if they need specific data.
|
/// Instead, add generic bounds on methods that take an ID if they need specific data.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Eq, Hash, PartialEq, Debug)]
|
#[derive(Eq, Hash, PartialEq)]
|
||||||
pub struct WidgetId<W = AnyWidget> {
|
pub struct WidgetId<W = AnyWidget> {
|
||||||
pub(super) ty: TypeId,
|
pub(super) ty: TypeId,
|
||||||
pub(super) id: Id,
|
pub(super) id: Id,
|
||||||
_pd: PhantomData<W>,
|
_pd: PhantomData<W>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<W> std::fmt::Debug for WidgetId<W> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.id.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: temp
|
// TODO: temp
|
||||||
impl<W> Clone for WidgetId<W> {
|
impl<W> Clone for WidgetId<W> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
@@ -63,30 +69,42 @@ pub struct FnTag;
|
|||||||
pub struct IdTag;
|
pub struct IdTag;
|
||||||
|
|
||||||
pub trait WidgetLike<Ctx, Tag> {
|
pub trait WidgetLike<Ctx, Tag> {
|
||||||
type Widget;
|
type Widget: 'static;
|
||||||
fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<Self::Widget>;
|
fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<Self::Widget>;
|
||||||
|
fn with_id<W2>(
|
||||||
|
self,
|
||||||
|
f: impl FnOnce(&mut Ui<Ctx>, WidgetId<Self::Widget>) -> WidgetId<W2>,
|
||||||
|
) -> impl WidgetIdFn<W2, Ctx>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
move |ui| {
|
||||||
|
let id = self.add(ui);
|
||||||
|
f(ui, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function that returns a widget given a UI.
|
/// A function that returns a widget given a UI.
|
||||||
/// Useful for defining trait functions on widgets that create a parent widget so that the children
|
/// Useful for defining trait functions on widgets that create a parent widget so that the children
|
||||||
/// don't need to be IDs yet
|
/// don't need to be IDs yet
|
||||||
pub trait WidgetFn<W: Widget<Ctx>, Ctx> = FnOnce(&mut Ui<Ctx>) -> W;
|
pub trait WidgetFn<W: Widget<Ctx>, Ctx> = FnOnce(&mut Ui<Ctx>) -> W;
|
||||||
pub trait WidgetIdFn<W: Widget<Ctx>, Ctx> = FnOnce(&mut Ui<Ctx>) -> WidgetId<W>;
|
pub trait WidgetIdFn<W, Ctx> = FnOnce(&mut Ui<Ctx>) -> WidgetId<W>;
|
||||||
|
|
||||||
pub trait Idable<Ctx, Tag> {
|
pub trait Idable<Ctx, Tag> {
|
||||||
type Widget: Widget<Ctx>;
|
type Widget: Widget<Ctx>;
|
||||||
fn set(self, ui: &mut Ui<Ctx>, id: &WidgetId<Self::Widget>);
|
fn set(self, ui: &mut Ui<Ctx>, id: &WidgetId<Self::Widget>);
|
||||||
}
|
fn id<'a>(
|
||||||
|
self,
|
||||||
pub trait WidgetFns<W: Widget<Ctx>, Ctx, Tag> {
|
id: &WidgetId<Self::Widget>,
|
||||||
fn id(self, id: &WidgetId<W>) -> impl WidgetIdFn<W, Ctx>;
|
) -> impl WidgetIdFn<Self::Widget, Ctx> + use<'a, Self, Ctx, Tag>
|
||||||
}
|
where
|
||||||
|
Self: Sized,
|
||||||
impl<I: Idable<Ctx, Tag>, Ctx, Tag> WidgetFns<I::Widget, Ctx, Tag> for I {
|
{
|
||||||
fn id(self, id: &WidgetId<I::Widget>) -> impl WidgetIdFn<I::Widget, Ctx> {
|
let id = id.clone();
|
||||||
|ui| {
|
move |ui| {
|
||||||
self.set(ui, id);
|
self.set(ui, &id);
|
||||||
id.clone()
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +134,7 @@ impl<W: Widget<Ctx>, Ctx, F: FnOnce(&mut Ui<Ctx>) -> W> WidgetLike<Ctx, FnTag> f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Widget<Ctx>, F: FnOnce(&mut Ui<Ctx>) -> WidgetId<W>, Ctx> WidgetLike<Ctx, IdTag> for F {
|
impl<W: 'static, F: FnOnce(&mut Ui<Ctx>) -> WidgetId<W>, Ctx> WidgetLike<Ctx, IdTag> for F {
|
||||||
type Widget = W;
|
type Widget = W;
|
||||||
fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<W> {
|
fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<W> {
|
||||||
self(ui)
|
self(ui)
|
||||||
@@ -130,7 +148,7 @@ impl<W: Widget<Ctx>, Ctx> WidgetLike<Ctx, WidgetTag> for W {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Widget<Ctx>, Ctx> WidgetLike<Ctx, FnTag> for WidgetId<W> {
|
impl<W: 'static, Ctx> WidgetLike<Ctx, FnTag> for WidgetId<W> {
|
||||||
type Widget = W;
|
type Widget = W;
|
||||||
fn add(self, _: &mut Ui<Ctx>) -> WidgetId<W> {
|
fn add(self, _: &mut Ui<Ctx>) -> WidgetId<W> {
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use gui::{SenseCtx, SenseTrigger, Vec2};
|
use gui::{Sense, SenseCtx, SenseTrigger, Vec2};
|
||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
|
|
||||||
use crate::testing::Client;
|
use crate::testing::Client;
|
||||||
@@ -9,28 +9,29 @@ pub struct Input {
|
|||||||
mouse_pos: Vec2,
|
mouse_pos: Vec2,
|
||||||
mouse_pressed: bool,
|
mouse_pressed: bool,
|
||||||
mouse_just_pressed: bool,
|
mouse_just_pressed: bool,
|
||||||
|
mouse_just_released: bool,
|
||||||
|
mouse_in: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn event(&mut self, event: &WindowEvent) {
|
pub fn event(&mut self, event: &WindowEvent) {
|
||||||
self.mouse_just_pressed = false;
|
self.mouse_just_pressed = false;
|
||||||
|
self.mouse_just_released = false;
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::Resized(size) => {
|
WindowEvent::Resized(size) => {
|
||||||
self.size = Vec2::new(size.width as f32, size.height as f32)
|
self.size = Vec2::new(size.width as f32, size.height as f32);
|
||||||
}
|
}
|
||||||
WindowEvent::CursorMoved { position, .. } => {
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
self.mouse_pos = Vec2::new(position.x as f32, position.y as f32)
|
self.mouse_pos = Vec2::new(position.x as f32, position.y as f32);
|
||||||
|
self.mouse_in = true;
|
||||||
}
|
}
|
||||||
WindowEvent::MouseInput { state, button, .. } => match button {
|
WindowEvent::MouseInput { state, button, .. } => match button {
|
||||||
winit::event::MouseButton::Left => {
|
winit::event::MouseButton::Left => {
|
||||||
if state.is_pressed() {
|
if state.is_pressed() {
|
||||||
if !self.mouse_pressed {
|
self.mouse_just_pressed = !self.mouse_pressed;
|
||||||
self.mouse_just_pressed = true;
|
|
||||||
} else {
|
|
||||||
self.mouse_just_pressed = false;
|
|
||||||
}
|
|
||||||
self.mouse_pressed = true;
|
self.mouse_pressed = true;
|
||||||
} else {
|
} else {
|
||||||
|
self.mouse_just_released = self.mouse_pressed;
|
||||||
self.mouse_pressed = false;
|
self.mouse_pressed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,12 +42,14 @@ impl Input {
|
|||||||
}
|
}
|
||||||
fn active(&mut self, trigger: &SenseTrigger) -> bool {
|
fn active(&mut self, trigger: &SenseTrigger) -> bool {
|
||||||
let region = trigger.shape.to_screen(self.size);
|
let region = trigger.shape.to_screen(self.size);
|
||||||
if !region.contains(self.mouse_pos) {
|
if !self.mouse_in || !region.contains(self.mouse_pos) {
|
||||||
return false;
|
return trigger.sense == Sense::NoHover;
|
||||||
}
|
}
|
||||||
match trigger.sense {
|
match trigger.sense {
|
||||||
gui::Sense::Click => self.mouse_just_pressed,
|
Sense::Press => self.mouse_just_pressed,
|
||||||
gui::Sense::Hover => true,
|
Sense::Held => self.mouse_pressed,
|
||||||
|
Sense::Hover => true,
|
||||||
|
Sense::NoHover => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,12 +80,22 @@ impl Client {
|
|||||||
color: UiColor,
|
color: UiColor,
|
||||||
main: &WidgetId<Regioned>,
|
main: &WidgetId<Regioned>,
|
||||||
to: &WidgetId<To>,
|
to: &WidgetId<To>,
|
||||||
) -> impl WidgetLike<Client, FnTag> {
|
) -> impl WidgetLike<Client, IdTag> {
|
||||||
let main = main.clone();
|
let main = main.clone();
|
||||||
let to = to.clone().erase_type();
|
let to = to.clone().erase_type();
|
||||||
Rect::new(color).sense(Sense::Click, move |_, ui: &mut Ui<Client>, _| {
|
Rect::new(color)
|
||||||
ui[&main].inner = to.clone();
|
.sense(Sense::Press, move |ui, _| {
|
||||||
})
|
ui[&main].inner = to.clone();
|
||||||
|
})
|
||||||
|
.sense_and_edit(Sense::Hover, move |r, _| {
|
||||||
|
r.color = color.add_rgb(0.1);
|
||||||
|
})
|
||||||
|
.sense_and_edit(Sense::NoHover, move |r, _| {
|
||||||
|
r.color = color;
|
||||||
|
})
|
||||||
|
.sense_and_edit(Sense::Held, move |r, _| {
|
||||||
|
r.color = color.add_rgb(-0.1);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttons = ui.add(
|
let buttons = ui.add(
|
||||||
|
|||||||
Reference in New Issue
Block a user