fix reactivity 😭 + visual widget counter
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
content: String,
|
pub content: String,
|
||||||
attrs: TextAttrs,
|
pub attrs: TextAttrs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Text {
|
impl Text {
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::layout::{Dir, UiPos, UiRegion, Vec2, WidgetArrLike, WidgetFnRet, WidgetLike};
|
use crate::layout::{
|
||||||
|
Dir, UiPos, UiRegion, Vec2, WidgetArrLike, WidgetFnRet, WidgetIdFnRet, WidgetLike,
|
||||||
|
};
|
||||||
|
|
||||||
pub trait CoreWidget<W: 'static, Ctx: 'static, Tag> {
|
pub trait CoreWidget<W, Ctx: 'static, Tag> {
|
||||||
fn pad(self, padding: impl Into<Padding>) -> WidgetFnRet!(Regioned, Ctx);
|
fn pad(self, padding: impl Into<Padding>) -> WidgetFnRet!(Regioned, Ctx);
|
||||||
fn center(self, size: impl Into<Vec2>) -> WidgetFnRet!(Regioned, Ctx);
|
fn center(self, size: impl Into<Vec2>) -> WidgetFnRet!(Regioned, Ctx);
|
||||||
fn region(self, region: UiRegion) -> WidgetFnRet!(Regioned, Ctx);
|
fn region(self, region: UiRegion) -> WidgetFnRet!(Regioned, Ctx);
|
||||||
|
fn label(self, label: impl Into<String>) -> WidgetIdFnRet!(W, Ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: WidgetLike<Ctx, Tag>, Ctx: 'static, Tag> CoreWidget<W::Widget, Ctx, Tag> for W {
|
impl<W: WidgetLike<Ctx, Tag>, Ctx: 'static, Tag> CoreWidget<W::Widget, Ctx, Tag> for W {
|
||||||
@@ -28,6 +31,14 @@ impl<W: WidgetLike<Ctx, Tag>, Ctx: 'static, Tag> CoreWidget<W::Widget, Ctx, Tag>
|
|||||||
inner: self.add(ui).erase_type(),
|
inner: self.add(ui).erase_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn label(self, label: impl Into<String>) -> WidgetIdFnRet!(W::Widget, Ctx) {
|
||||||
|
|ui| {
|
||||||
|
let id = self.add(ui);
|
||||||
|
ui.set_label(&id, label.into());
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CoreWidgetArr<const LEN: usize, Ctx: 'static, Tag> {
|
pub trait CoreWidgetArr<const LEN: usize, Ctx: 'static, Tag> {
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ impl<W> Drop for WidgetId<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct IdTag;
|
pub struct IdTag;
|
||||||
|
pub struct IdFnTag;
|
||||||
|
|
||||||
// pub trait WidgetIdFn<W, Ctx> = FnOnce(&mut Ui<Ctx>) -> WidgetId<W>;
|
// pub trait WidgetIdFn<W, Ctx> = FnOnce(&mut Ui<Ctx>) -> WidgetId<W>;
|
||||||
macro_rules! WidgetIdFnRet {
|
macro_rules! WidgetIdFnRet {
|
||||||
@@ -126,14 +127,14 @@ impl<F: FnOnce(&mut Ui<Ctx>) -> W, W: Widget<Ctx>, Ctx> Idable<Ctx, FnTag> for F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: 'static, Ctx> WidgetLike<Ctx, FnTag> for WidgetId<W> {
|
impl<W: 'static, Ctx> WidgetLike<Ctx, IdTag> 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: 'static, 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, IdFnTag> 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)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
WidgetInstance, Widgets,
|
WidgetInstance, Widgets,
|
||||||
},
|
},
|
||||||
render::{Primitive, PrimitiveHandle, Primitives},
|
render::{Primitive, PrimitiveHandle, Primitives},
|
||||||
util::Id,
|
util::{HashMap, Id},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
@@ -25,6 +25,7 @@ pub struct Painter<'a, Ctx: 'static> {
|
|||||||
pub(super) primitives: &'a mut Primitives,
|
pub(super) primitives: &'a mut Primitives,
|
||||||
textures: &'a mut Textures,
|
textures: &'a mut Textures,
|
||||||
text: &'a mut TextData,
|
text: &'a mut TextData,
|
||||||
|
labels: &'a HashMap<Id, String>,
|
||||||
screen_size: Vec2,
|
screen_size: Vec2,
|
||||||
/// state of what's currently being drawn
|
/// state of what's currently being drawn
|
||||||
state: State,
|
state: State,
|
||||||
@@ -41,6 +42,7 @@ impl<'a, Ctx> Painter<'a, Ctx> {
|
|||||||
text: &'a mut TextData,
|
text: &'a mut TextData,
|
||||||
textures: &'a mut Textures,
|
textures: &'a mut Textures,
|
||||||
screen_size: Vec2,
|
screen_size: Vec2,
|
||||||
|
labels: &'a HashMap<Id, String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
widgets: nodes,
|
widgets: nodes,
|
||||||
@@ -51,6 +53,7 @@ impl<'a, Ctx> Painter<'a, Ctx> {
|
|||||||
text,
|
text,
|
||||||
textures,
|
textures,
|
||||||
screen_size,
|
screen_size,
|
||||||
|
labels,
|
||||||
state: State {
|
state: State {
|
||||||
region: UiRegion::full(),
|
region: UiRegion::full(),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
@@ -123,7 +126,7 @@ impl<'a, Ctx> Painter<'a, Ctx> {
|
|||||||
id,
|
id,
|
||||||
WidgetInstance {
|
WidgetInstance {
|
||||||
region,
|
region,
|
||||||
primitives: (start_i..end_i).into(),
|
span: start_i..end_i,
|
||||||
children: child_state.children,
|
children: child_state.children,
|
||||||
parent: child_state.parent,
|
parent: child_state.parent,
|
||||||
},
|
},
|
||||||
@@ -168,21 +171,51 @@ impl<'a, Ctx> Painter<'a, Ctx> {
|
|||||||
}
|
}
|
||||||
let instance = self.free(id);
|
let instance = self.free(id);
|
||||||
self.state.id = instance.parent;
|
self.state.id = instance.parent;
|
||||||
self.primitives.prepare(instance.primitives.into());
|
self.primitives.prepare(instance.span.clone());
|
||||||
self.draw_raw_at(id, instance.region);
|
self.draw_raw_at(id, instance.region);
|
||||||
let delta = self.primitives.apply(instance.primitives.into());
|
let start = instance.span.start;
|
||||||
if let Some(parent) = self.state.id.take() {
|
let delta = self.primitives.apply(instance.span);
|
||||||
self.shift_end(parent, delta);
|
if delta != 0
|
||||||
|
&& let Some(parent) = self.state.id.take()
|
||||||
|
{
|
||||||
|
self.shift_parent(parent, start, delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shift_end(&mut self, id: Id, delta: isize) {
|
fn shift_parent(&mut self, parent: Id, start: usize, delta: isize) {
|
||||||
let instance = self.active.widgets.get_mut(&id).unwrap();
|
let instance = self.active.widgets.get_mut(&parent).unwrap();
|
||||||
let end = &mut instance.primitives.end;
|
let end = &mut instance.span.end;
|
||||||
*end = end.strict_add_signed(delta);
|
*end = end.strict_add_signed(delta);
|
||||||
if let Some(parent) = &instance.parent {
|
// ids are supposed to be unique so theoretically no cloning is needed
|
||||||
let parent = parent.duplicate();
|
// leaving for now for testing
|
||||||
self.shift_end(parent, delta);
|
let parent = instance.parent.as_ref().map(|p| p.duplicate());
|
||||||
|
for child in instance
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|id| id.duplicate())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
self.shift_child(&child, start, delta);
|
||||||
|
}
|
||||||
|
if let Some(parent) = parent {
|
||||||
|
self.shift_parent(parent, start, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shift_child(&mut self, child: &Id, start: usize, delta: isize) {
|
||||||
|
let instance = self.active.widgets.get_mut(child).unwrap();
|
||||||
|
if instance.span.start <= start {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
instance.span.start = instance.span.start.strict_add_signed(delta);
|
||||||
|
instance.span.end = instance.span.end.strict_add_signed(delta);
|
||||||
|
for child in instance
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|id| id.duplicate())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
self.shift_child(&child, start, delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
ops::{Index, IndexMut},
|
ops::{Index, IndexMut, Range},
|
||||||
range::Range,
|
|
||||||
sync::mpsc::{Receiver, Sender, channel},
|
sync::mpsc::{Receiver, Sender, channel},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Ui<Ctx> {
|
pub struct Ui<Ctx> {
|
||||||
base: Option<WidgetId>,
|
base: Option<WidgetId>,
|
||||||
widgets: Widgets<Ctx>,
|
widgets: Widgets<Ctx>,
|
||||||
|
labels: HashMap<Id, String>,
|
||||||
updates: Vec<WidgetId>,
|
updates: Vec<WidgetId>,
|
||||||
recv: Receiver<Id>,
|
recv: Receiver<Id>,
|
||||||
send: Sender<Id>,
|
send: Sender<Id>,
|
||||||
@@ -46,6 +46,11 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
w.add(self)
|
w.add(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// useful for debugging
|
||||||
|
pub fn set_label<W>(&mut self, id: &WidgetId<W>, label: String) {
|
||||||
|
self.labels.insert(id.id.duplicate(), label);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_widget<W: Widget<Ctx>>(&mut self, w: W) -> WidgetId<W> {
|
pub fn add_widget<W: Widget<Ctx>>(&mut self, w: W) -> WidgetId<W> {
|
||||||
self.push(w)
|
self.push(w)
|
||||||
}
|
}
|
||||||
@@ -56,8 +61,8 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set<W: Widget<Ctx>>(&mut self, i: &WidgetId<W>, w: W) {
|
pub fn set<W: Widget<Ctx>>(&mut self, id: &WidgetId<W>, w: W) {
|
||||||
self.widgets.insert(i.id.duplicate(), w);
|
self.widgets.insert(id.id.duplicate(), w);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_base<Tag>(&mut self, w: impl WidgetLike<Ctx, Tag>) {
|
pub fn set_base<Tag>(&mut self, w: impl WidgetLike<Ctx, Tag>) {
|
||||||
@@ -108,6 +113,7 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
&mut self.text,
|
&mut self.text,
|
||||||
&mut self.textures,
|
&mut self.textures,
|
||||||
self.size,
|
self.size,
|
||||||
|
&self.labels,
|
||||||
);
|
);
|
||||||
if let Some(base) = &self.base {
|
if let Some(base) = &self.base {
|
||||||
painter.draw(base);
|
painter.draw(base);
|
||||||
@@ -131,6 +137,7 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
where
|
where
|
||||||
Ctx: 'static,
|
Ctx: 'static,
|
||||||
{
|
{
|
||||||
|
self.free();
|
||||||
let mut painter = Painter::new(
|
let mut painter = Painter::new(
|
||||||
&self.widgets,
|
&self.widgets,
|
||||||
&mut self.primitives,
|
&mut self.primitives,
|
||||||
@@ -140,6 +147,7 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
&mut self.text,
|
&mut self.text,
|
||||||
&mut self.textures,
|
&mut self.textures,
|
||||||
self.size,
|
self.size,
|
||||||
|
&self.labels,
|
||||||
);
|
);
|
||||||
for id in self.updates.drain(..) {
|
for id in self.updates.drain(..) {
|
||||||
painter.redraw(&id.id);
|
painter.redraw(&id.id);
|
||||||
@@ -149,6 +157,7 @@ impl<Ctx> Ui<Ctx> {
|
|||||||
/// free any resources that don't have references anymore
|
/// free any resources that don't have references anymore
|
||||||
fn free(&mut self) {
|
fn free(&mut self) {
|
||||||
for id in self.recv.try_iter() {
|
for id in self.recv.try_iter() {
|
||||||
|
self.labels.remove(&id);
|
||||||
self.widgets.delete(id);
|
self.widgets.delete(id);
|
||||||
}
|
}
|
||||||
self.textures.free();
|
self.textures.free();
|
||||||
@@ -244,6 +253,7 @@ impl<Ctx: 'static> Default for Ui<Ctx> {
|
|||||||
Self {
|
Self {
|
||||||
base: Default::default(),
|
base: Default::default(),
|
||||||
widgets: Widgets::new(),
|
widgets: Widgets::new(),
|
||||||
|
labels: Default::default(),
|
||||||
updates: Default::default(),
|
updates: Default::default(),
|
||||||
primitives: Default::default(),
|
primitives: Default::default(),
|
||||||
textures: Textures::new(),
|
textures: Textures::new(),
|
||||||
@@ -260,7 +270,7 @@ impl<Ctx: 'static> Default for Ui<Ctx> {
|
|||||||
|
|
||||||
pub struct WidgetInstance {
|
pub struct WidgetInstance {
|
||||||
pub region: UiRegion,
|
pub region: UiRegion,
|
||||||
pub primitives: Range<usize>,
|
pub span: Range<usize>,
|
||||||
pub children: Vec<Id>,
|
pub children: Vec<Id>,
|
||||||
pub parent: Option<Id>,
|
pub parent: Option<Id>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
#![feature(const_from)]
|
#![feature(const_from)]
|
||||||
#![feature(map_try_insert)]
|
#![feature(map_try_insert)]
|
||||||
#![feature(new_range_api)]
|
|
||||||
|
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ impl GpuTextures {
|
|||||||
TextureUpdate::Free(i) => self.free(i),
|
TextureUpdate::Free(i) => self.free(i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if changed {
|
||||||
|
// println!("{}", self.views.len());
|
||||||
|
// }
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
fn set(&mut self, i: u32, image: &DynamicImage) {
|
fn set(&mut self, i: u32, image: &DynamicImage) {
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ impl ApplicationHandler for App {
|
|||||||
let window = event_loop
|
let window = event_loop
|
||||||
.create_window(Window::default_attributes())
|
.create_window(Window::default_attributes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (ui, ids) = Client::create_ui();
|
let (ui, cui) = Client::create_ui();
|
||||||
let client = Client::new(window.into(), ids);
|
let client = Client::new(window.into(), cui);
|
||||||
self.client = Some((client, ui));
|
self.client = Some((client, ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,15 +18,16 @@ pub fn main() {
|
|||||||
pub struct Client {
|
pub struct Client {
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
input: Input,
|
input: Input,
|
||||||
ui: UiIds,
|
ui: ClientUi,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UiIds {
|
pub struct ClientUi {
|
||||||
span_add: WidgetId<Span>,
|
info: WidgetId<Text>,
|
||||||
|
old_num: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn create_ui() -> (Ui<Self>, UiIds) {
|
pub fn create_ui() -> (Ui<Self>, ClientUi) {
|
||||||
let mut ui = Ui::new();
|
let mut ui = Ui::new();
|
||||||
let span_add = ui.id();
|
let span_add = ui.id();
|
||||||
let rect = Rect {
|
let rect = Rect {
|
||||||
@@ -109,10 +110,15 @@ impl Client {
|
|||||||
)
|
)
|
||||||
.span(Dir::RIGHT, [1, 1, 1]),
|
.span(Dir::RIGHT, [1, 1, 1]),
|
||||||
);
|
);
|
||||||
let test_button = Rect::new(Color::PURPLE)
|
let s = span_add.clone();
|
||||||
|
let add_button = Rect::new(Color::LIME)
|
||||||
.radius(30)
|
.radius(30)
|
||||||
.on(Sense::PressStart, move |ctx| {
|
.on(Sense::PressStart, move |ctx| {
|
||||||
println!("{}", ctx.ui.num_widgets());
|
let child = ctx
|
||||||
|
.ui
|
||||||
|
.add(image(include_bytes!("assets/sungals.png")))
|
||||||
|
.erase_type();
|
||||||
|
ctx.ui[&s].children.push((child, ratio(1)));
|
||||||
})
|
})
|
||||||
.region(
|
.region(
|
||||||
UiPos::corner(Corner::BotRight)
|
UiPos::corner(Corner::BotRight)
|
||||||
@@ -131,22 +137,35 @@ impl Client {
|
|||||||
.expand((150, 150))
|
.expand((150, 150))
|
||||||
.shifted((75, -75)),
|
.shifted((75, -75)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let info = ui.add(text(""));
|
||||||
|
let info_sect = info.clone().region(
|
||||||
|
UiPos::corner(Corner::TopRight)
|
||||||
|
.expand((150, 150))
|
||||||
|
.shifted((-75, 0)),
|
||||||
|
);
|
||||||
ui.set_base(
|
ui.set_base(
|
||||||
(
|
(
|
||||||
tabs,
|
tabs.label("tabs"),
|
||||||
(pad_test.pad(10).id(&main), test_button, del_button).stack(),
|
(
|
||||||
|
pad_test.pad(10).id(&main),
|
||||||
|
add_button.label("add button"),
|
||||||
|
del_button.label("del button"),
|
||||||
|
info_sect.label("info sect"),
|
||||||
|
)
|
||||||
|
.stack().label("main stack"),
|
||||||
)
|
)
|
||||||
.span(Dir::DOWN, [fixed(40), ratio(1)]),
|
.span(Dir::DOWN, [fixed(40), ratio(1)]).label("root"),
|
||||||
);
|
);
|
||||||
(ui, UiIds { span_add })
|
(ui, ClientUi { info, old_num: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(window: Arc<Window>, ui: UiIds) -> Self {
|
pub fn new(window: Arc<Window>, ui: ClientUi) -> Self {
|
||||||
let renderer = Renderer::new(window);
|
let renderer = Renderer::new(window);
|
||||||
Self {
|
Self {
|
||||||
renderer,
|
renderer,
|
||||||
ui,
|
|
||||||
input: Input::default(),
|
input: Input::default(),
|
||||||
|
ui,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,17 +185,13 @@ impl Client {
|
|||||||
ui.resize((size.width, size.height));
|
ui.resize((size.width, size.height));
|
||||||
self.renderer.resize(&size)
|
self.renderer.resize(&size)
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput { event, .. } => {
|
|
||||||
if event.state.is_pressed() {
|
|
||||||
let child = ui
|
|
||||||
.add(image(include_bytes!("assets/sungals.png")))
|
|
||||||
.erase_type();
|
|
||||||
ui[&self.ui.span_add].children.push((child, ratio(1)));
|
|
||||||
self.renderer.window().request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
let num = ui.num_widgets();
|
||||||
|
if num != self.ui.old_num {
|
||||||
|
ui[&self.ui.info].content = format!("widgets: {}", num);
|
||||||
|
self.ui.old_num = num;
|
||||||
|
}
|
||||||
if ui.needs_redraw() {
|
if ui.needs_redraw() {
|
||||||
self.renderer.window().request_redraw();
|
self.renderer.window().request_redraw();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user