proper widgetid + slot vec instead of map
This commit is contained in:
@@ -1,7 +1,4 @@
|
|||||||
use crate::{
|
use crate::{Ui, UiModule, WidgetId, WidgetView, util::HashMap};
|
||||||
Ui, UiModule, WidgetView,
|
|
||||||
util::{HashMap, Id},
|
|
||||||
};
|
|
||||||
use std::{hash::Hash, rc::Rc};
|
use std::{hash::Hash, rc::Rc};
|
||||||
|
|
||||||
pub trait Event: Sized {
|
pub trait Event: Sized {
|
||||||
@@ -41,23 +38,23 @@ impl<E: DefaultEvent> Event for E {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait EventModule<E: Event, Ctx>: UiModule + Default {
|
pub trait EventModule<E: Event, Ctx>: UiModule + Default {
|
||||||
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, E::Data>);
|
fn register(&mut self, id: WidgetId, event: E, f: impl EventFn<Ctx, E::Data>);
|
||||||
fn run<'a>(
|
fn run<'a>(
|
||||||
&self,
|
&self,
|
||||||
id: &Id,
|
id: WidgetId,
|
||||||
event: E,
|
event: E,
|
||||||
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, Self, E, Ctx>>;
|
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, Self, E, Ctx>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventFnMap<Ctx, Data> = HashMap<Id, Vec<Rc<dyn EventFn<Ctx, Data>>>>;
|
type EventFnMap<Ctx, Data> = HashMap<WidgetId, Vec<Rc<dyn EventFn<Ctx, Data>>>>;
|
||||||
pub struct DefaultEventModule<E: Event, Ctx> {
|
pub struct DefaultEventModule<E: Event, Ctx> {
|
||||||
map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>,
|
map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Event + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> {
|
impl<E: Event + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> {
|
||||||
fn on_remove(&mut self, id: &Id) {
|
fn on_remove(&mut self, id: WidgetId) {
|
||||||
for map in self.map.values_mut() {
|
for map in self.map.values_mut() {
|
||||||
map.remove(id);
|
map.remove(&id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +63,7 @@ pub trait HashableEvent: Event + Hash + Eq + 'static {}
|
|||||||
impl<E: Event + Hash + Eq + 'static> HashableEvent for E {}
|
impl<E: Event + Hash + Eq + 'static> HashableEvent for E {}
|
||||||
|
|
||||||
impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<E, Ctx> {
|
impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<E, Ctx> {
|
||||||
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
|
fn register(&mut self, id: WidgetId, event: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
|
||||||
self.map
|
self.map
|
||||||
.entry(event)
|
.entry(event)
|
||||||
.or_default()
|
.or_default()
|
||||||
@@ -77,11 +74,11 @@ impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<
|
|||||||
|
|
||||||
fn run<'a>(
|
fn run<'a>(
|
||||||
&self,
|
&self,
|
||||||
id: &Id,
|
id: WidgetId,
|
||||||
event: E,
|
event: E,
|
||||||
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, E, Ctx>> {
|
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, E, Ctx>> {
|
||||||
if let Some(map) = self.map.get(&event)
|
if let Some(map) = self.map.get(&event)
|
||||||
&& let Some(fs) = map.get(id)
|
&& let Some(fs) = map.get(&id)
|
||||||
{
|
{
|
||||||
let fs = fs.clone();
|
let fs = fs.clone();
|
||||||
Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| {
|
Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| {
|
||||||
@@ -138,7 +135,7 @@ impl Ui {
|
|||||||
.data
|
.data
|
||||||
.modules
|
.modules
|
||||||
.get_mut::<E::Module<Ctx>>()
|
.get_mut::<E::Module<Ctx>>()
|
||||||
.run(&id.id(), event)
|
.run(id.id(), event)
|
||||||
{
|
{
|
||||||
f(EventCtx {
|
f(EventCtx {
|
||||||
ui: self,
|
ui: self,
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
|
|
||||||
use crate::{
|
use crate::{ActiveData, WidgetId, util::HashMap};
|
||||||
ActiveData,
|
|
||||||
util::{HashMap, Id},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub trait UiModule: Any {
|
pub trait UiModule: Any {
|
||||||
fn on_draw(&mut self, inst: &ActiveData) {}
|
fn on_draw(&mut self, inst: &ActiveData) {}
|
||||||
fn on_undraw(&mut self, inst: &ActiveData) {}
|
fn on_undraw(&mut self, inst: &ActiveData) {}
|
||||||
fn on_remove(&mut self, id: &Id) {}
|
fn on_remove(&mut self, id: WidgetId) {}
|
||||||
fn on_move(&mut self, inst: &ActiveData) {}
|
fn on_move(&mut self, inst: &ActiveData) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData,
|
Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData,
|
||||||
TextureHandle, Textures, UiRegion, UiVec2, WidgetHandle, Widgets,
|
TextureHandle, Textures, UiRegion, UiVec2, WidgetHandle, WidgetId, Widgets,
|
||||||
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
|
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
|
||||||
util::{HashMap, HashSet, Id, TrackedArena, Vec2},
|
util::{HashMap, HashSet, TrackedArena, Vec2},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// makes your surfaces look pretty
|
/// makes your surfaces look pretty
|
||||||
@@ -12,46 +12,46 @@ pub struct Painter<'a, 'c> {
|
|||||||
mask: MaskIdx,
|
mask: MaskIdx,
|
||||||
textures: Vec<TextureHandle>,
|
textures: Vec<TextureHandle>,
|
||||||
primitives: Vec<PrimitiveHandle>,
|
primitives: Vec<PrimitiveHandle>,
|
||||||
children: Vec<Id>,
|
children: Vec<WidgetId>,
|
||||||
children_width: HashMap<Id, (UiVec2, Len)>,
|
children_width: HashMap<WidgetId, (UiVec2, Len)>,
|
||||||
children_height: HashMap<Id, (UiVec2, Len)>,
|
children_height: HashMap<WidgetId, (UiVec2, Len)>,
|
||||||
pub layer: usize,
|
pub layer: usize,
|
||||||
id: Id,
|
id: WidgetId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// context for a painter; lets you draw and redraw widgets
|
/// context for a painter; lets you draw and redraw widgets
|
||||||
struct PainterCtx<'a> {
|
struct PainterCtx<'a> {
|
||||||
pub widgets: &'a Widgets,
|
pub widgets: &'a Widgets,
|
||||||
pub active: &'a mut HashMap<Id, ActiveData>,
|
pub active: &'a mut HashMap<WidgetId, ActiveData>,
|
||||||
pub layers: &'a mut PrimitiveLayers,
|
pub layers: &'a mut PrimitiveLayers,
|
||||||
pub textures: &'a mut Textures,
|
pub textures: &'a mut Textures,
|
||||||
pub masks: &'a mut TrackedArena<Mask, u32>,
|
pub masks: &'a mut TrackedArena<Mask, u32>,
|
||||||
pub text: &'a mut TextData,
|
pub text: &'a mut TextData,
|
||||||
pub output_size: Vec2,
|
pub output_size: Vec2,
|
||||||
pub modules: &'a mut Modules,
|
pub modules: &'a mut Modules,
|
||||||
pub cache_width: HashMap<Id, (UiVec2, Len)>,
|
pub cache_width: HashMap<WidgetId, (UiVec2, Len)>,
|
||||||
pub cache_height: HashMap<Id, (UiVec2, Len)>,
|
pub cache_height: HashMap<WidgetId, (UiVec2, Len)>,
|
||||||
pub needs_redraw: &'a mut HashSet<Id>,
|
pub needs_redraw: &'a mut HashSet<WidgetId>,
|
||||||
draw_started: HashSet<Id>,
|
draw_started: HashSet<WidgetId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// stores information for children about the highest level parent that needed their size
|
/// stores information for children about the highest level parent that needed their size
|
||||||
/// so that they can redraw the parent if their size changes
|
/// so that they can redraw the parent if their size changes
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct ResizeRef {
|
pub struct ResizeRef {
|
||||||
x: Option<(Id, (UiVec2, Len))>,
|
x: Option<(WidgetId, (UiVec2, Len))>,
|
||||||
y: Option<(Id, (UiVec2, Len))>,
|
y: Option<(WidgetId, (UiVec2, Len))>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// important non rendering data for retained drawing
|
/// important non rendering data for retained drawing
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ActiveData {
|
pub struct ActiveData {
|
||||||
pub id: Id,
|
pub id: WidgetId,
|
||||||
pub region: UiRegion,
|
pub region: UiRegion,
|
||||||
pub parent: Option<Id>,
|
pub parent: Option<WidgetId>,
|
||||||
pub textures: Vec<TextureHandle>,
|
pub textures: Vec<TextureHandle>,
|
||||||
pub primitives: Vec<PrimitiveHandle>,
|
pub primitives: Vec<PrimitiveHandle>,
|
||||||
pub children: Vec<Id>,
|
pub children: Vec<WidgetId>,
|
||||||
pub resize: ResizeRef,
|
pub resize: ResizeRef,
|
||||||
pub mask: MaskIdx,
|
pub mask: MaskIdx,
|
||||||
pub layer: usize,
|
pub layer: usize,
|
||||||
@@ -61,13 +61,13 @@ pub struct ActiveData {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PainterData {
|
pub struct PainterData {
|
||||||
pub widgets: Widgets,
|
pub widgets: Widgets,
|
||||||
pub active: HashMap<Id, ActiveData>,
|
pub active: HashMap<WidgetId, ActiveData>,
|
||||||
pub layers: PrimitiveLayers,
|
pub layers: PrimitiveLayers,
|
||||||
pub textures: Textures,
|
pub textures: Textures,
|
||||||
pub text: TextData,
|
pub text: TextData,
|
||||||
pub output_size: Vec2,
|
pub output_size: Vec2,
|
||||||
pub modules: Modules,
|
pub modules: Modules,
|
||||||
pub px_dependent: HashSet<Id>,
|
pub px_dependent: HashSet<WidgetId>,
|
||||||
pub masks: TrackedArena<Mask, u32>,
|
pub masks: TrackedArena<Mask, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
/// redraws a widget that's currently active (drawn)
|
/// redraws a widget that's currently active (drawn)
|
||||||
/// can be called on something already drawn or removed,
|
/// can be called on something already drawn or removed,
|
||||||
/// will just return if so
|
/// will just return if so
|
||||||
pub fn redraw(&mut self, id: Id) {
|
pub fn redraw(&mut self, id: WidgetId) {
|
||||||
self.needs_redraw.remove(&id);
|
self.needs_redraw.remove(&id);
|
||||||
if self.draw_started.contains(&id) {
|
if self.draw_started.contains(&id) {
|
||||||
return;
|
return;
|
||||||
@@ -168,11 +168,11 @@ impl<'a> PainterCtx<'a> {
|
|||||||
fn draw_inner(
|
fn draw_inner(
|
||||||
&mut self,
|
&mut self,
|
||||||
layer: usize,
|
layer: usize,
|
||||||
id: Id,
|
id: WidgetId,
|
||||||
region: UiRegion,
|
region: UiRegion,
|
||||||
parent: Option<Id>,
|
parent: Option<WidgetId>,
|
||||||
mask: MaskIdx,
|
mask: MaskIdx,
|
||||||
old_children: Option<Vec<Id>>,
|
old_children: Option<Vec<WidgetId>>,
|
||||||
) {
|
) {
|
||||||
// I have no idea if these checks work lol
|
// I have no idea if these checks work lol
|
||||||
// the idea is u can't redraw stuff u already drew,
|
// the idea is u can't redraw stuff u already drew,
|
||||||
@@ -272,7 +272,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
self.active.insert(id, instance);
|
self.active.insert(id, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mov(&mut self, id: Id, from: UiRegion, to: UiRegion) {
|
fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
|
||||||
let active = self.active.get_mut(&id).unwrap();
|
let active = self.active.get_mut(&id).unwrap();
|
||||||
for h in &active.primitives {
|
for h in &active.primitives {
|
||||||
let region = self.layers[h.layer].region_mut(h);
|
let region = self.layers[h.layer].region_mut(h);
|
||||||
@@ -291,7 +291,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// NOTE: instance textures are cleared and self.textures freed
|
/// NOTE: instance textures are cleared and self.textures freed
|
||||||
fn remove(&mut self, id: Id) -> Option<ActiveData> {
|
fn remove(&mut self, id: WidgetId) -> Option<ActiveData> {
|
||||||
let mut inst = self.active.remove(&id);
|
let mut inst = self.active.remove(&id);
|
||||||
if let Some(inst) = &mut inst {
|
if let Some(inst) = &mut inst {
|
||||||
for h in &inst.primitives {
|
for h in &inst.primitives {
|
||||||
@@ -309,7 +309,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
inst
|
inst
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_rec(&mut self, id: Id) -> Option<ActiveData> {
|
fn remove_rec(&mut self, id: WidgetId) -> Option<ActiveData> {
|
||||||
let inst = self.remove(id);
|
let inst = self.remove(id);
|
||||||
if let Some(inst) = &inst {
|
if let Some(inst) = &inst {
|
||||||
for c in &inst.children {
|
for c in &inst.children {
|
||||||
@@ -321,7 +321,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PainterData {
|
impl PainterData {
|
||||||
fn ctx<'a>(&'a mut self, needs_redraw: &'a mut HashSet<Id>) -> PainterCtx<'a> {
|
fn ctx<'a>(&'a mut self, needs_redraw: &'a mut HashSet<WidgetId>) -> PainterCtx<'a> {
|
||||||
PainterCtx {
|
PainterCtx {
|
||||||
widgets: &self.widgets,
|
widgets: &self.widgets,
|
||||||
active: &mut self.active,
|
active: &mut self.active,
|
||||||
@@ -338,7 +338,7 @@ impl PainterData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self, id: Id) {
|
pub fn draw(&mut self, id: WidgetId) {
|
||||||
let mut need_redraw = HashSet::default();
|
let mut need_redraw = HashSet::default();
|
||||||
let mut ctx = self.ctx(&mut need_redraw);
|
let mut ctx = self.ctx(&mut need_redraw);
|
||||||
ctx.draw_started.clear();
|
ctx.draw_started.clear();
|
||||||
@@ -346,7 +346,7 @@ impl PainterData {
|
|||||||
ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None);
|
ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redraw(&mut self, ids: &mut HashSet<Id>) {
|
pub fn redraw(&mut self, ids: &mut HashSet<WidgetId>) {
|
||||||
let mut ctx = self.ctx(ids);
|
let mut ctx = self.ctx(ids);
|
||||||
while let Some(&id) = ctx.needs_redraw.iter().next() {
|
while let Some(&id) = ctx.needs_redraw.iter().next() {
|
||||||
ctx.redraw(id);
|
ctx.redraw(id);
|
||||||
@@ -475,10 +475,10 @@ impl<'a, 'c> Painter<'a, 'c> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn label(&self) -> &str {
|
pub fn label(&self) -> &str {
|
||||||
&self.ctx.widgets.data(&self.id).unwrap().label
|
&self.ctx.widgets.data(self.id).unwrap().label
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> &Id {
|
pub fn id(&self) -> &WidgetId {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,28 +486,28 @@ impl<'a, 'c> Painter<'a, 'c> {
|
|||||||
pub struct SizeCtx<'a> {
|
pub struct SizeCtx<'a> {
|
||||||
pub text: &'a mut TextData,
|
pub text: &'a mut TextData,
|
||||||
pub textures: &'a mut Textures,
|
pub textures: &'a mut Textures,
|
||||||
source: Id,
|
source: WidgetId,
|
||||||
widgets: &'a Widgets,
|
widgets: &'a Widgets,
|
||||||
cache_width: &'a mut HashMap<Id, (UiVec2, Len)>,
|
cache_width: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
|
||||||
cache_height: &'a mut HashMap<Id, (UiVec2, Len)>,
|
cache_height: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
|
||||||
checked_width: &'a mut HashMap<Id, (UiVec2, Len)>,
|
checked_width: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
|
||||||
checked_height: &'a mut HashMap<Id, (UiVec2, Len)>,
|
checked_height: &'a mut HashMap<WidgetId, (UiVec2, Len)>,
|
||||||
/// TODO: should this be pub? rn used for sized
|
/// TODO: should this be pub? rn used for sized
|
||||||
pub outer: UiVec2,
|
pub outer: UiVec2,
|
||||||
output_size: Vec2,
|
output_size: Vec2,
|
||||||
id: Id,
|
id: WidgetId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SizeCtx<'_> {
|
impl SizeCtx<'_> {
|
||||||
pub fn id(&self) -> &Id {
|
pub fn id(&self) -> &WidgetId {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source(&self) -> &Id {
|
pub fn source(&self) -> &WidgetId {
|
||||||
&self.source
|
&self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
fn width_inner(&mut self, id: Id) -> Len {
|
fn width_inner(&mut self, id: WidgetId) -> Len {
|
||||||
// first check cache
|
// first check cache
|
||||||
// TODO: is this needed? broken rn bc does not store children during upper size check,
|
// TODO: is this needed? broken rn bc does not store children during upper size check,
|
||||||
// so if something actually using check_* hits cache it fails to add them
|
// so if something actually using check_* hits cache it fails to add them
|
||||||
@@ -532,7 +532,7 @@ impl SizeCtx<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should be refactored to share code w width_inner
|
// TODO: should be refactored to share code w width_inner
|
||||||
fn height_inner(&mut self, id: Id) -> Len {
|
fn height_inner(&mut self, id: WidgetId) -> Len {
|
||||||
// if let Some(&(outer, len)) = self.cache_height.get(&id)
|
// if let Some(&(outer, len)) = self.cache_height.get(&id)
|
||||||
// && outer == self.outer
|
// && outer == self.outer
|
||||||
// {
|
// {
|
||||||
@@ -584,7 +584,7 @@ impl SizeCtx<'_> {
|
|||||||
self.text.draw(buffer, attrs, self.textures)
|
self.text.draw(buffer, attrs, self.textures)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label(&self, id: &Id) -> &String {
|
pub fn label(&self, id: WidgetId) -> &String {
|
||||||
self.widgets.label(id)
|
self.widgets.label(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Color, UiRegion,
|
Color, UiRegion, WidgetId,
|
||||||
render::{
|
render::{
|
||||||
ArrBuf,
|
ArrBuf,
|
||||||
data::{MaskIdx, PrimitiveInstance},
|
data::{MaskIdx, PrimitiveInstance},
|
||||||
},
|
},
|
||||||
util::Id,
|
|
||||||
};
|
};
|
||||||
use bytemuck::Pod;
|
use bytemuck::Pod;
|
||||||
use wgpu::*;
|
use wgpu::*;
|
||||||
|
|
||||||
pub struct Primitives {
|
pub struct Primitives {
|
||||||
instances: Vec<PrimitiveInstance>,
|
instances: Vec<PrimitiveInstance>,
|
||||||
assoc: Vec<Id>,
|
assoc: Vec<WidgetId>,
|
||||||
data: PrimitiveData,
|
data: PrimitiveData,
|
||||||
free: Vec<usize>,
|
free: Vec<usize>,
|
||||||
pub updated: bool,
|
pub updated: bool,
|
||||||
@@ -99,7 +98,7 @@ macro_rules! primitives {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct PrimitiveInst<P> {
|
pub struct PrimitiveInst<P> {
|
||||||
pub id: Id,
|
pub id: WidgetId,
|
||||||
pub primitive: P,
|
pub primitive: P,
|
||||||
pub region: UiRegion,
|
pub region: UiRegion,
|
||||||
pub mask_idx: MaskIdx,
|
pub mask_idx: MaskIdx,
|
||||||
@@ -175,7 +174,7 @@ impl Primitives {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct PrimitiveChange {
|
pub struct PrimitiveChange {
|
||||||
pub id: Id,
|
pub id: WidgetId,
|
||||||
pub old: usize,
|
pub old: usize,
|
||||||
pub new: usize,
|
pub new: usize,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ActiveData, Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle,
|
ActiveData, Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle,
|
||||||
Widget, WidgetHandle, WidgetLike,
|
Widget, WidgetHandle, WidgetId, WidgetLike, util::Vec2,
|
||||||
util::{Id, Vec2},
|
|
||||||
};
|
};
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
ops::{Index, IndexMut},
|
ops::{Index, IndexMut},
|
||||||
sync::mpsc::{Receiver, Sender, channel},
|
sync::mpsc::{Receiver, Sender, channel},
|
||||||
};
|
};
|
||||||
@@ -14,8 +12,8 @@ pub struct Ui {
|
|||||||
// TODO: make this at least pub(super)
|
// TODO: make this at least pub(super)
|
||||||
pub data: PainterData,
|
pub data: PainterData,
|
||||||
root: Option<WidgetHandle>,
|
root: Option<WidgetHandle>,
|
||||||
recv: Receiver<Id>,
|
recv: Receiver<WidgetId>,
|
||||||
pub(super) send: Sender<Id>,
|
pub(super) send: Sender<WidgetId>,
|
||||||
full_redraw: bool,
|
full_redraw: bool,
|
||||||
resized: bool,
|
resized: bool,
|
||||||
}
|
}
|
||||||
@@ -27,17 +25,15 @@ impl Ui {
|
|||||||
|
|
||||||
/// useful for debugging
|
/// useful for debugging
|
||||||
pub fn set_label<W: ?Sized>(&mut self, id: &WidgetHandle<W>, label: String) {
|
pub fn set_label<W: ?Sized>(&mut self, id: &WidgetHandle<W>, label: String) {
|
||||||
self.data.widgets.data_mut(&id.id()).unwrap().label = label;
|
self.data.widgets.data_mut(id.id()).unwrap().label = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label<W: ?Sized>(&self, id: &WidgetHandle<W>) -> &String {
|
pub fn label<W: ?Sized>(&self, id: &WidgetHandle<W>) -> &String {
|
||||||
&self.data.widgets.data(&id.id()).unwrap().label
|
&self.data.widgets.data(id.id()).unwrap().label
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetHandle<W> {
|
pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetHandle<W> {
|
||||||
let id = self.new_id();
|
WidgetHandle::new(self.data.widgets.add(w), self.send.clone())
|
||||||
self.data.widgets.insert(id.id(), w);
|
|
||||||
id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) {
|
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) {
|
||||||
@@ -63,10 +59,6 @@ impl Ui {
|
|||||||
self.data.widgets.get_mut(id)
|
self.data.widgets.get_mut(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_id<W: Widget>(&mut self) -> WidgetHandle<W> {
|
|
||||||
WidgetHandle::new(self.data.widgets.reserve(), self.send.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
|
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
|
||||||
self.data.textures.add(image)
|
self.data.textures.add(image)
|
||||||
}
|
}
|
||||||
@@ -125,7 +117,7 @@ impl Ui {
|
|||||||
fn free(&mut self) {
|
fn free(&mut self) {
|
||||||
for id in self.recv.try_iter() {
|
for id in self.recv.try_iter() {
|
||||||
for m in self.data.modules.iter_mut() {
|
for m in self.data.modules.iter_mut() {
|
||||||
m.on_remove(&id);
|
m.on_remove(id);
|
||||||
}
|
}
|
||||||
self.data.widgets.delete(id);
|
self.data.widgets.delete(id);
|
||||||
}
|
}
|
||||||
@@ -162,9 +154,9 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug(&self, label: &str) -> impl Iterator<Item = &ActiveData> {
|
pub fn debug(&self, label: &str) -> impl Iterator<Item = &ActiveData> {
|
||||||
self.data.active.iter().filter_map(move |(id, inst)| {
|
self.data.active.iter().filter_map(move |(&id, inst)| {
|
||||||
let l = &self.data.widgets.label(id);
|
let l = self.data.widgets.label(id);
|
||||||
if *l == label { Some(inst) } else { None }
|
if l == label { Some(inst) } else { None }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,16 +181,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Widget {
|
|
||||||
pub fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Ui {
|
impl Default for Ui {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let (send, recv) = channel();
|
let (send, recv) = channel();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod arena;
|
mod arena;
|
||||||
mod borrow;
|
mod borrow;
|
||||||
mod change;
|
mod change;
|
||||||
|
mod slot;
|
||||||
mod id;
|
mod id;
|
||||||
mod math;
|
mod math;
|
||||||
mod refcount;
|
mod refcount;
|
||||||
@@ -9,6 +10,7 @@ mod vec2;
|
|||||||
pub use arena::*;
|
pub use arena::*;
|
||||||
pub use borrow::*;
|
pub use borrow::*;
|
||||||
pub use change::*;
|
pub use change::*;
|
||||||
|
pub use slot::*;
|
||||||
pub use id::*;
|
pub use id::*;
|
||||||
pub use math::*;
|
pub use math::*;
|
||||||
pub use refcount::*;
|
pub use refcount::*;
|
||||||
|
|||||||
69
core/src/util/slot.rs
Normal file
69
core/src/util/slot.rs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SlotId {
|
||||||
|
idx: u32,
|
||||||
|
genr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SlotVec<T> {
|
||||||
|
data: Vec<(u32, Option<T>)>,
|
||||||
|
free: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SlotVec<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
data: Default::default(),
|
||||||
|
free: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, x: T) -> SlotId {
|
||||||
|
if let Some(idx) = self.free.pop() {
|
||||||
|
let (genr, data) = &mut self.data[idx as usize];
|
||||||
|
*data = Some(x);
|
||||||
|
SlotId { idx, genr: *genr }
|
||||||
|
} else {
|
||||||
|
let idx = self.data.len() as u32;
|
||||||
|
let genr = 0;
|
||||||
|
self.data.push((genr, Some(x)));
|
||||||
|
SlotId { idx, genr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free(&mut self, id: SlotId) {
|
||||||
|
let (genr, data) = &mut self.data[id.idx as usize];
|
||||||
|
*genr += 1;
|
||||||
|
*data = None;
|
||||||
|
self.free.push(id.idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, id: SlotId) -> Option<&T> {
|
||||||
|
let slot = &self.data[id.idx as usize];
|
||||||
|
if slot.0 != id.genr {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
slot.1.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, id: SlotId) -> Option<&mut T> {
|
||||||
|
let slot = &mut self.data[id.idx as usize];
|
||||||
|
if slot.0 != id.genr {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
slot.1.as_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.data.len() - self.free.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for SlotVec<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,11 @@ use std::{marker::Unsize, mem::MaybeUninit, ops::CoerceUnsized, sync::mpsc::Send
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Ui, Widget,
|
Ui, Widget,
|
||||||
util::{Id, RefCounter},
|
util::{RefCounter, SlotId},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub type WidgetId = SlotId;
|
||||||
|
|
||||||
/// An identifier for a widget that can index a UI to get the associated widget.
|
/// An identifier for a widget that can index a UI to get the associated widget.
|
||||||
/// It should always remain valid; it keeps a ref count and removes the widget from the UI if all
|
/// It should always remain valid; it keeps a ref count and removes the widget from the UI if all
|
||||||
/// references are dropped.
|
/// references are dropped.
|
||||||
@@ -14,14 +16,14 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
|
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
|
||||||
pub struct WidgetHandle<W: ?Sized = dyn Widget> {
|
pub struct WidgetHandle<W: ?Sized = dyn Widget> {
|
||||||
pub(super) id: Id,
|
pub(super) id: WidgetId,
|
||||||
counter: RefCounter,
|
counter: RefCounter,
|
||||||
send: Sender<Id>,
|
send: Sender<WidgetId>,
|
||||||
ty: *const W,
|
ty: *const W,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WidgetView<W: ?Sized = dyn Widget> {
|
pub struct WidgetView<W: ?Sized = dyn Widget> {
|
||||||
pub(super) id: Id,
|
pub(super) id: WidgetId,
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
ty: *const W,
|
ty: *const W,
|
||||||
}
|
}
|
||||||
@@ -35,7 +37,7 @@ impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetHandle<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<W: ?Sized> WidgetHandle<W> {
|
impl<W: ?Sized> WidgetHandle<W> {
|
||||||
pub(crate) fn new(id: Id, send: Sender<Id>) -> Self {
|
pub(crate) fn new(id: WidgetId, send: Sender<WidgetId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
counter: RefCounter::new(),
|
counter: RefCounter::new(),
|
||||||
@@ -49,7 +51,7 @@ impl<W: ?Sized> WidgetHandle<W> {
|
|||||||
unsafe { std::mem::transmute(self) }
|
unsafe { std::mem::transmute(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> Id {
|
pub fn id(&self) -> WidgetId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +71,7 @@ impl<W: ?Sized> WidgetHandle<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<W: ?Sized> WidgetView<W> {
|
impl<W: ?Sized> WidgetView<W> {
|
||||||
pub fn id(&self) -> Id {
|
pub fn id(&self) -> WidgetId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,19 +89,19 @@ impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetHandle<W>> WidgetIdFn<W> for F {}
|
|||||||
|
|
||||||
pub trait IdLike {
|
pub trait IdLike {
|
||||||
type Widget: Widget + ?Sized + 'static;
|
type Widget: Widget + ?Sized + 'static;
|
||||||
fn id(&self) -> Id;
|
fn id(&self) -> WidgetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Widget + ?Sized> IdLike for WidgetHandle<W> {
|
impl<W: Widget + ?Sized> IdLike for WidgetHandle<W> {
|
||||||
type Widget = W;
|
type Widget = W;
|
||||||
fn id(&self) -> Id {
|
fn id(&self) -> WidgetId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Widget + ?Sized> IdLike for WidgetView<W> {
|
impl<W: Widget + ?Sized> IdLike for WidgetView<W> {
|
||||||
type Widget = W;
|
type Widget = W;
|
||||||
fn id(&self) -> Id {
|
fn id(&self) -> WidgetId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,16 @@ impl Widget for () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl dyn Widget {
|
||||||
|
pub fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
IdLike, Widget,
|
IdLike, Widget, WidgetId,
|
||||||
util::{DynBorrower, HashMap, HashSet, Id, IdTracker},
|
util::{DynBorrower, HashSet, SlotVec},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Widgets {
|
pub struct Widgets {
|
||||||
pub updates: HashSet<Id>,
|
pub updates: HashSet<WidgetId>,
|
||||||
ids: IdTracker,
|
vec: SlotVec<WidgetData>,
|
||||||
map: HashMap<Id, WidgetData>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WidgetData {
|
pub struct WidgetData {
|
||||||
@@ -22,23 +21,23 @@ impl Widgets {
|
|||||||
!self.updates.is_empty()
|
!self.updates.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dyn(&self, id: Id) -> Option<&dyn Widget> {
|
pub fn get_dyn(&self, id: WidgetId) -> Option<&dyn Widget> {
|
||||||
Some(self.map.get(&id)?.widget.as_ref())
|
Some(self.vec.get(id)?.widget.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dyn_mut(&mut self, id: Id) -> Option<&mut dyn Widget> {
|
pub fn get_dyn_mut(&mut self, id: WidgetId) -> Option<&mut dyn Widget> {
|
||||||
self.updates.insert(id);
|
self.updates.insert(id);
|
||||||
Some(self.map.get_mut(&id)?.widget.as_mut())
|
Some(self.vec.get_mut(id)?.widget.as_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get_dyn but dynamic borrow checking of widgets
|
/// get_dyn but dynamic borrow checking of widgets
|
||||||
/// lets you do recursive (tree) operations, like the painter does
|
/// lets you do recursive (tree) operations, like the painter does
|
||||||
pub fn get_dyn_dynamic(&self, id: Id) -> WidgetWrapper<'_> {
|
pub fn get_dyn_dynamic(&self, id: WidgetId) -> WidgetWrapper<'_> {
|
||||||
// SAFETY: must guarantee no other mutable references to this widget exist
|
// SAFETY: must guarantee no other mutable references to this widget exist
|
||||||
// done through the borrow variable
|
// done through the borrow variable
|
||||||
#[allow(mutable_transmutes)]
|
#[allow(mutable_transmutes)]
|
||||||
let data = unsafe {
|
let data = unsafe {
|
||||||
std::mem::transmute::<&WidgetData, &mut WidgetData>(self.map.get(&id).unwrap())
|
std::mem::transmute::<&WidgetData, &mut WidgetData>(self.vec.get(id).unwrap())
|
||||||
};
|
};
|
||||||
if data.borrowed {
|
if data.borrowed {
|
||||||
panic!("tried to mutably borrow the same widget twice");
|
panic!("tried to mutably borrow the same widget twice");
|
||||||
@@ -46,60 +45,57 @@ impl Widgets {
|
|||||||
WidgetWrapper::new(data.widget.as_mut(), &mut data.borrowed)
|
WidgetWrapper::new(data.widget.as_mut(), &mut data.borrowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<I: IdLike>(&self, id: &I) -> Option<&I::Widget> where I::Widget: Sized {
|
pub fn get<I: IdLike>(&self, id: &I) -> Option<&I::Widget>
|
||||||
|
where
|
||||||
|
I::Widget: Sized,
|
||||||
|
{
|
||||||
self.get_dyn(id.id())?.as_any().downcast_ref()
|
self.get_dyn(id.id())?.as_any().downcast_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut<I: IdLike>(&mut self, id: &I) -> Option<&mut I::Widget> where I::Widget: Sized {
|
pub fn get_mut<I: IdLike>(&mut self, id: &I) -> Option<&mut I::Widget>
|
||||||
|
where
|
||||||
|
I::Widget: Sized,
|
||||||
|
{
|
||||||
self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut()
|
self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert<W: Widget>(&mut self, id: Id, widget: W) {
|
pub fn add<W: Widget>(&mut self, widget: W) -> WidgetId {
|
||||||
let mut label = std::any::type_name::<W>().to_string();
|
let mut label = std::any::type_name::<W>().to_string();
|
||||||
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
|
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
|
||||||
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
|
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
|
||||||
}
|
}
|
||||||
self.insert_any(id, Box::new(widget), label);
|
self.insert_any(Box::new(widget), label)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self, id: &Id) -> Option<&WidgetData> {
|
pub fn data(&self, id: WidgetId) -> Option<&WidgetData> {
|
||||||
self.map.get(id)
|
self.vec.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label(&self, id: &Id) -> &String {
|
pub fn label(&self, id: WidgetId) -> &String {
|
||||||
&self.data(id).unwrap().label
|
&self.data(id).unwrap().label
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_mut(&mut self, id: &Id) -> Option<&mut WidgetData> {
|
pub fn data_mut(&mut self, id: WidgetId) -> Option<&mut WidgetData> {
|
||||||
self.map.get_mut(id)
|
self.vec.get_mut(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_any(&mut self, id: Id, widget: Box<dyn Widget>, label: String) {
|
pub fn insert_any(&mut self, widget: Box<dyn Widget>, label: String) -> WidgetId {
|
||||||
self.map.insert(
|
self.vec.add(WidgetData {
|
||||||
id,
|
widget,
|
||||||
WidgetData {
|
label,
|
||||||
widget,
|
borrowed: false,
|
||||||
label,
|
})
|
||||||
borrowed: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&mut self, id: Id) {
|
pub fn delete(&mut self, id: WidgetId) {
|
||||||
self.map.remove(&id);
|
self.vec.free(id);
|
||||||
self.ids.free(id);
|
// not sure if there's any point in this
|
||||||
}
|
// self.updates.remove(&id);
|
||||||
|
|
||||||
pub fn reserve(&mut self) -> Id {
|
|
||||||
self.ids.next()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::len_without_is_empty)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.map.len()
|
self.vec.len()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.map.is_empty()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
400
fm:w
Normal file
400
fm:w
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use iris_core::util::{HashMap};
|
||||||
|
use std::{
|
||||||
|
ops::{BitOr, Deref, DerefMut},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum Button {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Middle,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum CursorSense {
|
||||||
|
PressStart(Button),
|
||||||
|
Pressing(Button),
|
||||||
|
PressEnd(Button),
|
||||||
|
HoverStart,
|
||||||
|
Hovering,
|
||||||
|
HoverEnd,
|
||||||
|
Scroll,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CursorSenses(Vec<CursorSense>);
|
||||||
|
|
||||||
|
impl CursorSense {
|
||||||
|
pub fn click() -> Self {
|
||||||
|
Self::PressStart(Button::Left)
|
||||||
|
}
|
||||||
|
pub fn click_or_drag() -> CursorSenses {
|
||||||
|
Self::click() | Self::Pressing(Button::Left)
|
||||||
|
}
|
||||||
|
pub fn unclick() -> Self {
|
||||||
|
Self::PressEnd(Button::Left)
|
||||||
|
}
|
||||||
|
pub fn is_dragging(&self) -> bool {
|
||||||
|
matches!(self, CursorSense::Pressing(Button::Left))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct CursorState {
|
||||||
|
pub pos: Vec2,
|
||||||
|
pub exists: bool,
|
||||||
|
pub buttons: CursorButtons,
|
||||||
|
pub scroll_delta: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CursorState {
|
||||||
|
pub fn end_frame(&mut self) {
|
||||||
|
self.buttons.end_frame();
|
||||||
|
self.scroll_delta = Vec2::ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default, PartialEq)]
|
||||||
|
pub enum ActivationState {
|
||||||
|
Start,
|
||||||
|
On,
|
||||||
|
End,
|
||||||
|
#[default]
|
||||||
|
Off,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// this and other similar stuff has a generic
|
||||||
|
/// because I kind of want to make CursorModule generic
|
||||||
|
/// or basically have some way to have custom senses
|
||||||
|
/// that depend on active widget positions
|
||||||
|
/// but I'm not sure how or if worth it
|
||||||
|
pub struct Sensor<Ctx, Data> {
|
||||||
|
pub senses: CursorSenses,
|
||||||
|
pub f: Rc<dyn EventFn<Ctx, Data>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SensorMap<Ctx, Data> = HashMap<Id, SensorGroup<Ctx, Data>>;
|
||||||
|
pub type SenseShape = UiRegion;
|
||||||
|
pub struct SensorGroup<Ctx, Data> {
|
||||||
|
pub hover: ActivationState,
|
||||||
|
pub sensors: Vec<Sensor<Ctx, Data>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CursorData {
|
||||||
|
pub cursor: Vec2,
|
||||||
|
pub size: Vec2,
|
||||||
|
pub scroll_delta: Vec2,
|
||||||
|
/// the (first) sense that triggered this event
|
||||||
|
/// the senses are checked in order
|
||||||
|
pub sense: CursorSense,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CursorModule<Ctx> {
|
||||||
|
map: SensorMap<Ctx, CursorData>,
|
||||||
|
active: HashMap<usize, HashMap<Id, SenseShape>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
|
||||||
|
fn on_draw(&mut self, inst: &ActiveData) {
|
||||||
|
if self.map.contains_key(&inst.id) {
|
||||||
|
self.active
|
||||||
|
.entry(inst.layer)
|
||||||
|
.or_default()
|
||||||
|
.insert(inst.id, inst.region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_undraw(&mut self, inst: &ActiveData) {
|
||||||
|
if let Some(layer) = self.active.get_mut(&inst.layer) {
|
||||||
|
layer.remove(&inst.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_remove(&mut self, id: &Id) {
|
||||||
|
self.map.remove(id);
|
||||||
|
for layer in self.active.values_mut() {
|
||||||
|
layer.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_move(&mut self, inst: &ActiveData) {
|
||||||
|
if let Some(map) = self.active.get_mut(&inst.layer)
|
||||||
|
&& let Some(region) = map.get_mut(&inst.id)
|
||||||
|
{
|
||||||
|
*region = inst.region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx> CursorModule<Ctx> {
|
||||||
|
pub fn merge(&mut self, other: Self) {
|
||||||
|
for (id, group) in other.map {
|
||||||
|
for sensor in group.sensors {
|
||||||
|
self.map.entry(id).or_default().sensors.push(sensor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SensorUi {
|
||||||
|
fn run_sensors<Ctx: 'static>(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SensorUi for Ui {
|
||||||
|
fn run_sensors<Ctx: 'static>(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut Ctx,
|
||||||
|
cursor: &CursorState,
|
||||||
|
window_size: Vec2,
|
||||||
|
) {
|
||||||
|
CursorModule::<Ctx>::run(self, ctx, cursor, window_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: 'static> CursorModule<Ctx> {
|
||||||
|
pub fn run(ui: &mut Ui, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
|
||||||
|
let layers = std::mem::take(&mut ui.data.layers);
|
||||||
|
let mut module = std::mem::take(ui.data.modules.get_mut::<Self>());
|
||||||
|
|
||||||
|
for i in layers.indices().rev() {
|
||||||
|
let Some(list) = module.active.get_mut(&i) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let mut sensed = false;
|
||||||
|
for (id, shape) in list.iter() {
|
||||||
|
let group = module.map.get_mut(id).unwrap();
|
||||||
|
let region = shape.to_px(window_size);
|
||||||
|
let in_shape = cursor.exists && region.contains(cursor.pos);
|
||||||
|
group.hover.update(in_shape);
|
||||||
|
if group.hover == ActivationState::Off {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sensed = true;
|
||||||
|
|
||||||
|
for sensor in &mut group.sensors {
|
||||||
|
if let Some(sense) = should_run(&sensor.senses, cursor, group.hover) {
|
||||||
|
let data = CursorData {
|
||||||
|
cursor: cursor.pos - region.top_left,
|
||||||
|
size: region.bot_right - region.top_left,
|
||||||
|
scroll_delta: cursor.scroll_delta,
|
||||||
|
sense,
|
||||||
|
};
|
||||||
|
(sensor.f)(EventCtx {
|
||||||
|
ui,
|
||||||
|
state: ctx,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sensed {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ui_mod = ui.data.modules.get_mut::<Self>();
|
||||||
|
std::mem::swap(ui_mod, &mut module);
|
||||||
|
ui_mod.merge(module);
|
||||||
|
ui.data.layers = layers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_run(
|
||||||
|
senses: &CursorSenses,
|
||||||
|
cursor: &CursorState,
|
||||||
|
hover: ActivationState,
|
||||||
|
) -> Option<CursorSense> {
|
||||||
|
for sense in senses.iter() {
|
||||||
|
if match sense {
|
||||||
|
CursorSense::PressStart(button) => cursor.buttons.select(button).is_start(),
|
||||||
|
CursorSense::Pressing(button) => cursor.buttons.select(button).is_on(),
|
||||||
|
CursorSense::PressEnd(button) => cursor.buttons.select(button).is_end(),
|
||||||
|
CursorSense::HoverStart => hover.is_start(),
|
||||||
|
CursorSense::Hovering => hover.is_on(),
|
||||||
|
CursorSense::HoverEnd => hover.is_end(),
|
||||||
|
CursorSense::Scroll => cursor.scroll_delta != Vec2::ZERO,
|
||||||
|
} {
|
||||||
|
return Some(*sense);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActivationState {
|
||||||
|
pub fn is_start(&self) -> bool {
|
||||||
|
*self == Self::Start
|
||||||
|
}
|
||||||
|
pub fn is_on(&self) -> bool {
|
||||||
|
*self == Self::Start || *self == Self::On
|
||||||
|
}
|
||||||
|
pub fn is_end(&self) -> bool {
|
||||||
|
*self == Self::End
|
||||||
|
}
|
||||||
|
pub fn is_off(&self) -> bool {
|
||||||
|
*self == Self::End || *self == Self::Off
|
||||||
|
}
|
||||||
|
pub fn update(&mut self, on: bool) {
|
||||||
|
*self = match *self {
|
||||||
|
Self::Start => match on {
|
||||||
|
true => Self::On,
|
||||||
|
false => Self::End,
|
||||||
|
},
|
||||||
|
Self::On => match on {
|
||||||
|
true => Self::On,
|
||||||
|
false => Self::End,
|
||||||
|
},
|
||||||
|
Self::End => match on {
|
||||||
|
true => Self::Start,
|
||||||
|
false => Self::Off,
|
||||||
|
},
|
||||||
|
Self::Off => match on {
|
||||||
|
true => Self::Start,
|
||||||
|
false => Self::Off,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_frame(&mut self) {
|
||||||
|
match self {
|
||||||
|
Self::Start => *self = Self::On,
|
||||||
|
Self::End => *self = Self::Off,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event for CursorSenses {
|
||||||
|
type Module<Ctx: 'static> = CursorModule<Ctx>;
|
||||||
|
type Data = CursorData;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event for CursorSense {
|
||||||
|
type Module<Ctx: 'static> = CursorModule<Ctx>;
|
||||||
|
type Data = CursorData;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static>
|
||||||
|
EventModule<E, Ctx> for CursorModule<Ctx>
|
||||||
|
{
|
||||||
|
fn register(&mut self, id: Id, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
|
||||||
|
// TODO: does not add to active if currently active
|
||||||
|
self.map.entry(id).or_default().sensors.push(Sensor {
|
||||||
|
senses: senses.into(),
|
||||||
|
f: Rc::new(f),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run<'a>(
|
||||||
|
&self,
|
||||||
|
id: &Id,
|
||||||
|
event: E,
|
||||||
|
) -> Option<impl Fn(EventCtx<Ctx, <E as Event>::Data>) + use<'a, E, Ctx>> {
|
||||||
|
let senses = event.into();
|
||||||
|
if let Some(group) = self.map.get(id) {
|
||||||
|
let fs: Vec<_> = group
|
||||||
|
.sensors
|
||||||
|
.iter()
|
||||||
|
.filter_map(|sensor| {
|
||||||
|
if sensor.senses.iter().any(|s| senses.contains(s)) {
|
||||||
|
Some(sensor.f.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Some(move |ctx: EventCtx<Ctx, CursorData>| {
|
||||||
|
for f in &fs {
|
||||||
|
f(EventCtx {
|
||||||
|
state: ctx.state,
|
||||||
|
ui: ctx.ui,
|
||||||
|
data: ctx.data.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx, Data> Default for SensorGroup<Ctx, Data> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hover: Default::default(),
|
||||||
|
sensors: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for CursorSenses {
|
||||||
|
type Target = Vec<CursorSense>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for CursorSenses {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CursorSense> for CursorSenses {
|
||||||
|
fn from(val: CursorSense) -> Self {
|
||||||
|
CursorSenses(vec![val])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr for CursorSense {
|
||||||
|
type Output = CursorSenses;
|
||||||
|
|
||||||
|
fn bitor(self, rhs: Self) -> Self::Output {
|
||||||
|
CursorSenses(vec![self, rhs])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr<CursorSense> for CursorSenses {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn bitor(mut self, rhs: CursorSense) -> Self::Output {
|
||||||
|
self.0.push(rhs);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx> Default for CursorModule<Ctx> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
map: Default::default(),
|
||||||
|
active: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use iris_core::util::{HashMap, Id};
|
use iris_core::util::HashMap;
|
||||||
use std::{
|
use std::{
|
||||||
ops::{BitOr, Deref, DerefMut},
|
ops::{BitOr, Deref, DerefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
@@ -97,7 +97,7 @@ pub struct Sensor<Ctx, Data> {
|
|||||||
pub f: Rc<dyn EventFn<Ctx, Data>>,
|
pub f: Rc<dyn EventFn<Ctx, Data>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SensorMap<Ctx, Data> = HashMap<Id, SensorGroup<Ctx, Data>>;
|
pub type SensorMap<Ctx, Data> = HashMap<WidgetId, SensorGroup<Ctx, Data>>;
|
||||||
pub type SenseShape = UiRegion;
|
pub type SenseShape = UiRegion;
|
||||||
pub struct SensorGroup<Ctx, Data> {
|
pub struct SensorGroup<Ctx, Data> {
|
||||||
pub hover: ActivationState,
|
pub hover: ActivationState,
|
||||||
@@ -116,7 +116,7 @@ pub struct CursorData {
|
|||||||
|
|
||||||
pub struct CursorModule<Ctx> {
|
pub struct CursorModule<Ctx> {
|
||||||
map: SensorMap<Ctx, CursorData>,
|
map: SensorMap<Ctx, CursorData>,
|
||||||
active: HashMap<usize, HashMap<Id, SenseShape>>,
|
active: HashMap<usize, HashMap<WidgetId, SenseShape>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
|
impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
|
||||||
@@ -135,10 +135,10 @@ impl<Ctx: 'static> UiModule for CursorModule<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_remove(&mut self, id: &Id) {
|
fn on_remove(&mut self, id: WidgetId) {
|
||||||
self.map.remove(id);
|
self.map.remove(&id);
|
||||||
for layer in self.active.values_mut() {
|
for layer in self.active.values_mut() {
|
||||||
layer.remove(id);
|
layer.remove(&id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ impl Event for CursorSense {
|
|||||||
impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static>
|
impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: 'static>
|
||||||
EventModule<E, Ctx> for CursorModule<Ctx>
|
EventModule<E, Ctx> for CursorModule<Ctx>
|
||||||
{
|
{
|
||||||
fn register(&mut self, id: Id, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
|
fn register(&mut self, id: WidgetId, senses: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
|
||||||
// TODO: does not add to active if currently active
|
// TODO: does not add to active if currently active
|
||||||
self.map.entry(id).or_default().sensors.push(Sensor {
|
self.map.entry(id).or_default().sensors.push(Sensor {
|
||||||
senses: senses.into(),
|
senses: senses.into(),
|
||||||
@@ -311,11 +311,11 @@ impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: '
|
|||||||
|
|
||||||
fn run<'a>(
|
fn run<'a>(
|
||||||
&self,
|
&self,
|
||||||
id: &Id,
|
id: WidgetId,
|
||||||
event: E,
|
event: E,
|
||||||
) -> Option<impl Fn(EventCtx<Ctx, <E as Event>::Data>) + use<'a, E, Ctx>> {
|
) -> Option<impl Fn(EventCtx<Ctx, <E as Event>::Data>) + use<'a, E, Ctx>> {
|
||||||
let senses = event.into();
|
let senses = event.into();
|
||||||
if let Some(group) = self.map.get(id) {
|
if let Some(group) = self.map.get(&id) {
|
||||||
let fs: Vec<_> = group
|
let fs: Vec<_> = group
|
||||||
.sensors
|
.sensors
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
Reference in New Issue
Block a user