Compare commits
1 Commits
f6b1143665
...
62aa02847a
| Author | SHA1 | Date | |
|---|---|---|---|
| 62aa02847a |
@@ -1,20 +1,14 @@
|
|||||||
use iris::{prelude::*, winit::*};
|
use iris::{prelude::*, winit::*};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
UiApp::<State>::run();
|
DefaultApp::<State>::run();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State;
|
||||||
ui: DefaultUi,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DefaultUiState for State {
|
impl DefaultAppState for State {
|
||||||
fn new(mut ui: DefaultUi, _proxy: Proxy<Self::Event>) -> Self {
|
fn new(ui: &mut Ui, _state: &UiState, _proxy: Proxy<Self::Event>) -> Self {
|
||||||
rect(Color::RED).set_root(&mut ui);
|
rect(Color::RED).set_root(ui);
|
||||||
Self { ui }
|
Self
|
||||||
}
|
|
||||||
|
|
||||||
fn ui(&mut self) -> &mut DefaultUi {
|
|
||||||
&mut self.ui
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ general ideas trynna use rn / experiment with:
|
|||||||
- retained mode
|
- retained mode
|
||||||
- specifically designed around wgpu
|
- specifically designed around wgpu
|
||||||
- postfix functions for most things to prevent unreadable indentation (going very well)
|
- postfix functions for most things to prevent unreadable indentation (going very well)
|
||||||
- no macros in user code / actual LSP typechecking (variadic generics if you can hear me please save us)
|
- almost no macros in user code & actual LSP typechecking (variadic generics if you can hear me please save us)
|
||||||
- relative anchor + absolute offset coord system (+ "rest" / leftover during widget layout)
|
- relative anchor + absolute offset coord system (+ "rest" / leftover during widget layout)
|
||||||
- single threaded ui & pass context around to make non async usage straightforward (pretty unsure about this)
|
- single threaded ui & pass context around to make non async usage straightforward (pretty unsure about this)
|
||||||
- widgets store outside of the actual rendering so they can be moved around and swapped easily (unsure about this but seems to work good for now)
|
- widgets store outside of the actual rendering so they can be moved around and swapped easily (unsure about this but seems to work good for now)
|
||||||
|
|||||||
@@ -7,16 +7,17 @@ use len_fns::*;
|
|||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
UiApp::<Client>::run();
|
DefaultApp::<Client>::run();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
ui: DefaultUi,
|
|
||||||
info: WidgetId<Text>,
|
info: WidgetId<Text>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultUiState for Client {
|
event_ctx!(Client);
|
||||||
fn new(mut ui: DefaultUi, _proxy: Proxy<Self::Event>) -> Self {
|
|
||||||
|
impl DefaultAppState for Client {
|
||||||
|
fn new(ui: &mut Ui, _state: &UiState, _proxy: Proxy<Self::Event>) -> Self {
|
||||||
let rrect = rect(Color::WHITE).radius(20);
|
let rrect = rect(Color::WHITE).radius(20);
|
||||||
let pad_test = (
|
let pad_test = (
|
||||||
rrect.color(Color::BLUE),
|
rrect.color(Color::BLUE),
|
||||||
@@ -39,7 +40,7 @@ impl DefaultUiState for Client {
|
|||||||
.width(rest(3)),
|
.width(rest(3)),
|
||||||
)
|
)
|
||||||
.span(Dir::RIGHT)
|
.span(Dir::RIGHT)
|
||||||
.add(&mut ui);
|
.add(ui);
|
||||||
|
|
||||||
let span_test = (
|
let span_test = (
|
||||||
rrect.color(Color::GREEN).width(100),
|
rrect.color(Color::GREEN).width(100),
|
||||||
@@ -50,14 +51,14 @@ impl DefaultUiState for Client {
|
|||||||
rrect.color(Color::RED).width(100),
|
rrect.color(Color::RED).width(100),
|
||||||
)
|
)
|
||||||
.span(Dir::LEFT)
|
.span(Dir::LEFT)
|
||||||
.add(&mut ui);
|
.add(ui);
|
||||||
|
|
||||||
let span_add = Span::empty(Dir::RIGHT).add(&mut ui);
|
let span_add = Span::empty(Dir::RIGHT).add(ui);
|
||||||
let span_add_ = span_add.clone();
|
let span_add_ = span_add.clone();
|
||||||
|
|
||||||
let add_button = rect(Color::LIME)
|
let add_button = rect(Color::LIME)
|
||||||
.radius(30)
|
.radius(30)
|
||||||
.on(CursorSense::click(), move |ctx: &mut Client, _| {
|
.on(CursorSense::click(), move |ctx| {
|
||||||
let child = ctx
|
let child = ctx
|
||||||
.ui
|
.ui
|
||||||
.add(image(include_bytes!("assets/sungals.png")).center())
|
.add(image(include_bytes!("assets/sungals.png")).center())
|
||||||
@@ -70,13 +71,13 @@ impl DefaultUiState for Client {
|
|||||||
let span_add_ = span_add.clone();
|
let span_add_ = span_add.clone();
|
||||||
let del_button = rect(Color::RED)
|
let del_button = rect(Color::RED)
|
||||||
.radius(30)
|
.radius(30)
|
||||||
.on(CursorSense::click(), move |ctx: &mut Client, _| {
|
.on(CursorSense::click(), move |ctx| {
|
||||||
ctx.ui[&span_add_].children.pop();
|
ctx.ui[&span_add_].children.pop();
|
||||||
})
|
})
|
||||||
.sized((150, 150))
|
.sized((150, 150))
|
||||||
.align(Align::BOT_LEFT);
|
.align(Align::BOT_LEFT);
|
||||||
|
|
||||||
let span_add_test = (span_add, add_button, del_button).stack().add(&mut ui);
|
let span_add_test = (span_add, add_button, del_button).stack().add(ui);
|
||||||
|
|
||||||
let btext = |content| wtext(content).size(30);
|
let btext = |content| wtext(content).size(30);
|
||||||
|
|
||||||
@@ -99,29 +100,27 @@ impl DefaultUiState for Client {
|
|||||||
wtext("pretty cool right?").size(50),
|
wtext("pretty cool right?").size(50),
|
||||||
)
|
)
|
||||||
.span(Dir::DOWN)
|
.span(Dir::DOWN)
|
||||||
.add(&mut ui);
|
.add(ui);
|
||||||
|
|
||||||
let texts = Span::empty(Dir::DOWN).gap(10).add(&mut ui);
|
let texts = Span::empty(Dir::DOWN).gap(10).add(ui);
|
||||||
let msg_area = texts.clone().scroll().masked().background(rect(Color::SKY));
|
let msg_area = texts.clone().scroll().masked().background(rect(Color::SKY));
|
||||||
let add_text = wtext("add")
|
let add_text = wtext("add")
|
||||||
.editable(false)
|
.editable(false)
|
||||||
.text_align(Align::LEFT)
|
.text_align(Align::LEFT)
|
||||||
.size(30)
|
.size(30)
|
||||||
.attr::<Selectable>(())
|
.attr::<Selectable>(())
|
||||||
.id_on(Submit, move |id, client: &mut Client, _| {
|
.on(Submit, move |ctx| {
|
||||||
let content = client.ui.text(id).take();
|
let content = ctx.ui.text(ctx.id).take();
|
||||||
let text = wtext(content)
|
let text = wtext(content)
|
||||||
.editable(false)
|
.editable(false)
|
||||||
.size(30)
|
.size(30)
|
||||||
.text_align(Align::LEFT)
|
.text_align(Align::LEFT)
|
||||||
.wrap(true)
|
.wrap(true)
|
||||||
.attr::<Selectable>(());
|
.attr::<Selectable>(());
|
||||||
let msg_box = text
|
let msg_box = text.background(rect(Color::WHITE.darker(0.5))).add(ctx.ui);
|
||||||
.background(rect(Color::WHITE.darker(0.5)))
|
ctx.ui[&texts].children.push(msg_box.any());
|
||||||
.add(&mut client.ui);
|
|
||||||
client.ui[&texts].children.push(msg_box.any());
|
|
||||||
})
|
})
|
||||||
.add(&mut ui);
|
.add(ui);
|
||||||
let text_edit_scroll = (
|
let text_edit_scroll = (
|
||||||
msg_area.height(rest(1)),
|
msg_area.height(rest(1)),
|
||||||
(
|
(
|
||||||
@@ -129,8 +128,8 @@ impl DefaultUiState for Client {
|
|||||||
(
|
(
|
||||||
add_text.clone().width(rest(1)),
|
add_text.clone().width(rest(1)),
|
||||||
Rect::new(Color::GREEN)
|
Rect::new(Color::GREEN)
|
||||||
.on(CursorSense::click(), move |client: &mut Client, _| {
|
.on(CursorSense::click(), move |ctx| {
|
||||||
client.run_event(&add_text, Submit, ());
|
ctx.ui.run_event(ctx.state, &add_text, Submit, ());
|
||||||
})
|
})
|
||||||
.sized((40, 40)),
|
.sized((40, 40)),
|
||||||
)
|
)
|
||||||
@@ -143,25 +142,25 @@ impl DefaultUiState for Client {
|
|||||||
.align(Align::BOT),
|
.align(Align::BOT),
|
||||||
)
|
)
|
||||||
.span(Dir::DOWN)
|
.span(Dir::DOWN)
|
||||||
.add(&mut ui);
|
.add(ui);
|
||||||
|
|
||||||
let main = pad_test.clone().pad(10).add(&mut ui);
|
let main = pad_test.clone().pad(10).add(ui);
|
||||||
|
|
||||||
let switch_button = |color, to: WidgetId, label| {
|
let switch_button = |color, to: WidgetId, label| {
|
||||||
let main_ = main.clone();
|
let main_ = main.clone();
|
||||||
let rect = rect(color)
|
let rect = rect(color)
|
||||||
.id_on(CursorSense::click(), move |id, ui: &mut Ui, _| {
|
.on(CursorSense::click(), move |ctx| {
|
||||||
ui[&main_.clone()].inner = to.clone();
|
ctx.ui[&main_.clone()].inner = to.clone();
|
||||||
ui[id].color = color.darker(0.3);
|
ctx.ui[ctx.id].color = color.darker(0.3);
|
||||||
})
|
})
|
||||||
.edit_on(
|
.on(
|
||||||
CursorSense::HoverStart | CursorSense::unclick(),
|
CursorSense::HoverStart | CursorSense::unclick(),
|
||||||
move |r, _| {
|
move |ctx| {
|
||||||
r.color = color.brighter(0.2);
|
ctx.ui[ctx.id].color = color.brighter(0.2);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.edit_on(CursorSense::HoverEnd, move |r, _| {
|
.on(CursorSense::HoverEnd, move |ctx| {
|
||||||
r.color = color;
|
ctx.ui[ctx.id].color = color;
|
||||||
});
|
});
|
||||||
(rect, wtext(label).size(30).text_align(Align::CENTER)).stack()
|
(rect, wtext(label).size(30).text_align(Align::CENTER)).stack()
|
||||||
};
|
};
|
||||||
@@ -179,33 +178,28 @@ impl DefaultUiState for Client {
|
|||||||
)
|
)
|
||||||
.span(Dir::RIGHT);
|
.span(Dir::RIGHT);
|
||||||
|
|
||||||
let info = wtext("").add(&mut ui);
|
let info = wtext("").add(ui);
|
||||||
let info_sect = info.clone().pad(10).align(Align::RIGHT);
|
let info_sect = info.clone().pad(10).align(Align::RIGHT);
|
||||||
|
|
||||||
((tabs.height(40), main).span(Dir::DOWN), info_sect)
|
((tabs.height(40), main).span(Dir::DOWN), info_sect)
|
||||||
.stack()
|
.stack()
|
||||||
.set_root(&mut ui);
|
.set_root(ui);
|
||||||
|
|
||||||
Self { ui, info }
|
Self { info }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self) -> &mut DefaultUi {
|
fn window_event(&mut self, _: WindowEvent, ui: &mut Ui, state: &UiState) {
|
||||||
&mut self.ui
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_event(&mut self, _: WindowEvent) {
|
|
||||||
let new = format!(
|
let new = format!(
|
||||||
"widgets: {}\nactive:{}\nviews: {}",
|
"widgets: {}\nactive:{}\nviews: {}",
|
||||||
self.ui.num_widgets(),
|
ui.num_widgets(),
|
||||||
self.ui.active_widgets(),
|
ui.active_widgets(),
|
||||||
self.ui.renderer.ui.view_count()
|
state.renderer.ui.view_count()
|
||||||
);
|
);
|
||||||
if new != *self.ui[&self.info].content {
|
if new != *ui[&self.info].content {
|
||||||
*self.ui[&self.info].content = new;
|
*ui[&self.info].content = new;
|
||||||
|
}
|
||||||
|
if ui.needs_redraw() {
|
||||||
|
state.window.request_redraw();
|
||||||
}
|
}
|
||||||
if self.ui.needs_redraw() {
|
|
||||||
self.ui.window.request_redraw();
|
|
||||||
}
|
|
||||||
self.ui.input.end_frame();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,20 +166,21 @@ impl<Ctx> CursorModule<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SensorCtx: UiCtx {
|
impl Ui {
|
||||||
fn run_sensors(&mut self, cursor: &CursorState, window_size: Vec2);
|
pub fn run_sensors<Ctx: 'static>(
|
||||||
}
|
&mut self,
|
||||||
|
ctx: &mut Ctx,
|
||||||
impl<Ctx: UiCtx + 'static> SensorCtx for Ctx {
|
cursor: &CursorState,
|
||||||
fn run_sensors(&mut self, cursor: &CursorState, window_size: Vec2) {
|
window_size: Vec2,
|
||||||
CursorModule::<Ctx>::run(self, cursor, window_size);
|
) {
|
||||||
|
CursorModule::<Ctx>::run(self, ctx, cursor, window_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: UiCtx + 'static> CursorModule<Ctx> {
|
impl<Ctx: 'static> CursorModule<Ctx> {
|
||||||
pub fn run(ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
|
pub fn run(ui: &mut Ui, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
|
||||||
let layers = std::mem::take(&mut ctx.ui().data.layers);
|
let layers = std::mem::take(&mut ui.data.layers);
|
||||||
let mut module = std::mem::take(ctx.ui().data.modules.get_mut::<Self>());
|
let mut module = std::mem::take(ui.data.modules.get_mut::<Self>());
|
||||||
|
|
||||||
for i in layers.indices().rev() {
|
for i in layers.indices().rev() {
|
||||||
let Some(list) = module.active.get_mut(&i) else {
|
let Some(list) = module.active.get_mut(&i) else {
|
||||||
@@ -204,7 +205,7 @@ impl<Ctx: UiCtx + 'static> CursorModule<Ctx> {
|
|||||||
scroll_delta: cursor.scroll_delta,
|
scroll_delta: cursor.scroll_delta,
|
||||||
sense,
|
sense,
|
||||||
};
|
};
|
||||||
(sensor.f)(ctx, data);
|
(sensor.f)(EventCtx { ui, state: ctx, data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,10 +214,10 @@ impl<Ctx: UiCtx + 'static> CursorModule<Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ui_mod = ctx.ui().data.modules.get_mut::<Self>();
|
let ui_mod = ui.data.modules.get_mut::<Self>();
|
||||||
std::mem::swap(ui_mod, &mut module);
|
std::mem::swap(ui_mod, &mut module);
|
||||||
ui_mod.merge(module);
|
ui_mod.merge(module);
|
||||||
ctx.ui().data.layers = layers;
|
ui.data.layers = layers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +306,11 @@ impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: '
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run<'a>(&self, id: &Id, event: E) -> Option<impl Fn(&mut Ctx, E::Data) + use<'a, E, Ctx>> {
|
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();
|
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
|
||||||
@@ -319,9 +324,13 @@ impl<E: Event<Data = <CursorSenses as Event>::Data> + Into<CursorSenses>, Ctx: '
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Some(move |ctx: &mut Ctx, data: CursorData| {
|
Some(move |ctx: EventCtx<Ctx, CursorData>| {
|
||||||
for f in &fs {
|
for f in &fs {
|
||||||
f(ctx, data.clone());
|
f(EventCtx {
|
||||||
|
state: ctx.state,
|
||||||
|
ui: ctx.ui,
|
||||||
|
data: ctx.data.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -100,10 +100,13 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scroll(self) -> impl WidgetIdFn<Scroll> {
|
fn scroll(self) -> impl WidgetIdFn<Scroll> {
|
||||||
|
event_ctx!(());
|
||||||
|
|
||||||
move |ui| {
|
move |ui| {
|
||||||
Scroll::new(self.add(ui).any(), Axis::Y)
|
Scroll::new(self.add(ui).any(), Axis::Y)
|
||||||
.edit_on(CursorSense::Scroll, |w, data| {
|
.on(CursorSense::Scroll, |ctx| {
|
||||||
w.scroll(data.scroll_delta.y * 50.0);
|
let s = &mut ctx.ui[ctx.id];
|
||||||
|
s.scroll(ctx.data.scroll_delta.y * 50.0);
|
||||||
})
|
})
|
||||||
.add(ui)
|
.add(ui)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,90 +1,97 @@
|
|||||||
use std::{hash::Hash, rc::Rc};
|
use std::{hash::Hash, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::{IdFnTag, Ui, UiModule, Widget, WidgetId, WidgetIdFn, WidgetLike},
|
layout::{IdFnTag, Ui, UiModule, WidgetId, WidgetIdFn, WidgetLike},
|
||||||
util::{HashMap, Id},
|
util::{HashMap, Id},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait UiCtx {
|
|
||||||
fn ui(&mut self) -> &mut Ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UiCtx for Ui {
|
|
||||||
fn ui(&mut self) -> &mut Ui {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Event: Sized {
|
pub trait Event: Sized {
|
||||||
type Module<Ctx: 'static>: EventModule<Self, Ctx>;
|
type Module<Ctx: 'static>: EventModule<Self, Ctx>;
|
||||||
type Data: Clone;
|
type Data: Clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EventFn<Ctx, Data>: Fn(&mut Ctx, Data) + 'static {}
|
pub struct EventCtx<'a, Ctx, Data> {
|
||||||
impl<F: Fn(&mut Ctx, Data) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
|
pub ui: &'a mut Ui,
|
||||||
|
pub state: &'a mut Ctx,
|
||||||
|
pub data: Data,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ECtx<'a, Ctx, Data, W> = EventIdCtx<'a, Ctx, Data, W>;
|
||||||
|
pub struct EventIdCtx<'a, Ctx, Data, W> {
|
||||||
|
pub id: &'a WidgetId<W>,
|
||||||
|
pub ui: &'a mut Ui,
|
||||||
|
pub state: &'a mut Ctx,
|
||||||
|
pub data: Data,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EventFn<Ctx, Data>: Fn(EventCtx<Ctx, Data>) + 'static {}
|
||||||
|
impl<F: Fn(EventCtx<Ctx, Data>) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
|
||||||
|
|
||||||
|
pub trait WidgetEventFn<Ctx, Data, W>: Fn(EventIdCtx<Ctx, Data, W>) + 'static {}
|
||||||
|
impl<F: Fn(EventIdCtx<Ctx, Data, W>) + 'static, Ctx, Data, W> WidgetEventFn<Ctx, Data, W> for F {}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! event_ctx {
|
||||||
|
($ty: ty) => {
|
||||||
|
mod local_event_trait {
|
||||||
|
use super::*;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use $crate::prelude::*;
|
||||||
|
|
||||||
|
pub trait EventableCtx<W, Tag, Ctx: 'static> {
|
||||||
|
fn on<E: Event>(
|
||||||
|
self,
|
||||||
|
event: E,
|
||||||
|
f: impl WidgetEventFn<Ctx, E::Data, W>,
|
||||||
|
) -> impl WidgetIdFn<W> + EventableCtx<W, IdFnTag, Ctx>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<WL: WidgetLike<Tag>, Tag> EventableCtx<WL::Widget, Tag, $ty> for WL {
|
||||||
|
fn on<E: Event>(
|
||||||
|
self,
|
||||||
|
event: E,
|
||||||
|
f: impl WidgetEventFn<$ty, E::Data, WL::Widget>,
|
||||||
|
) -> impl WidgetIdFn<WL::Widget> + EventableCtx<WL::Widget, IdFnTag, $ty> {
|
||||||
|
eventable::Eventable::on(self, event, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use local_event_trait::*;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub use event_ctx;
|
||||||
|
|
||||||
|
pub mod eventable {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
pub trait Eventable<W, Tag> {
|
pub trait Eventable<W, Tag> {
|
||||||
fn on<E: Event, Ctx: 'static>(
|
fn on<E: Event, Ctx: 'static>(
|
||||||
self,
|
self,
|
||||||
event: E,
|
event: E,
|
||||||
f: impl EventFn<Ctx, E::Data>,
|
f: impl WidgetEventFn<Ctx, E::Data, W>,
|
||||||
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>;
|
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>;
|
||||||
|
|
||||||
fn id_on<E: Event, Ctx: 'static>(
|
|
||||||
self,
|
|
||||||
event: E,
|
|
||||||
f: impl Fn(&WidgetId<W>, &mut Ctx, E::Data) + 'static,
|
|
||||||
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>
|
|
||||||
where
|
|
||||||
W: Widget;
|
|
||||||
|
|
||||||
fn edit_on<E: Event>(
|
|
||||||
self,
|
|
||||||
event: E,
|
|
||||||
f: impl Fn(&mut W, E::Data) + 'static,
|
|
||||||
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>
|
|
||||||
where
|
|
||||||
W: Widget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: WidgetLike<Tag>, Tag> Eventable<W::Widget, Tag> for W {
|
impl<WL: WidgetLike<Tag>, Tag> Eventable<WL::Widget, Tag> for WL {
|
||||||
fn on<E: Event, Ctx: 'static>(
|
fn on<E: Event, Ctx: 'static>(
|
||||||
self,
|
self,
|
||||||
event: E,
|
event: E,
|
||||||
f: impl EventFn<Ctx, E::Data>,
|
f: impl WidgetEventFn<Ctx, E::Data, WL::Widget>,
|
||||||
) -> impl WidgetIdFn<W::Widget> {
|
) -> impl WidgetIdFn<WL::Widget> {
|
||||||
move |ui| {
|
move |ui| {
|
||||||
let id = self.add(ui);
|
let id = self.add(ui);
|
||||||
ui.register_event(&id, event, f);
|
let id_ = id.weak();
|
||||||
|
ui.register_event(&id, event, move |ctx| {
|
||||||
|
f(EventIdCtx {
|
||||||
|
id: &id_.strong(),
|
||||||
|
state: ctx.state,
|
||||||
|
data: ctx.data,
|
||||||
|
ui: ctx.ui,
|
||||||
|
});
|
||||||
|
});
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id_on<E: Event, Ctx: 'static>(
|
|
||||||
self,
|
|
||||||
event: E,
|
|
||||||
f: impl Fn(&WidgetId<W::Widget>, &mut Ctx, E::Data) + 'static,
|
|
||||||
) -> impl WidgetIdFn<W::Widget>
|
|
||||||
where
|
|
||||||
W::Widget: Widget,
|
|
||||||
{
|
|
||||||
self.with_id(move |ui, id| {
|
|
||||||
// needed so that this widget can actually be dropped
|
|
||||||
let id2 = id.weak();
|
|
||||||
id.on(event, move |ctx, pos| f(&id2.strong(), ctx, pos))
|
|
||||||
.add(ui)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn edit_on<E: Event>(
|
|
||||||
self,
|
|
||||||
event: E,
|
|
||||||
f: impl Fn(&mut W::Widget, E::Data) + 'static,
|
|
||||||
) -> impl WidgetIdFn<W::Widget>
|
|
||||||
where
|
|
||||||
W::Widget: Widget,
|
|
||||||
{
|
|
||||||
self.id_on(event, move |id, ui: &mut Ui, pos| f(&mut ui[id], pos))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +110,7 @@ pub trait EventModule<E: Event, Ctx>: UiModule + Default {
|
|||||||
&self,
|
&self,
|
||||||
id: &Id,
|
id: &Id,
|
||||||
event: E,
|
event: E,
|
||||||
) -> Option<impl Fn(&mut 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<Id, Vec<Rc<dyn EventFn<Ctx, Data>>>>;
|
||||||
@@ -132,14 +139,22 @@ impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<
|
|||||||
.push(Rc::new(f));
|
.push(Rc::new(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run<'a>(&self, id: &Id, event: E) -> Option<impl Fn(&mut Ctx, E::Data) + use<'a, E, Ctx>> {
|
fn run<'a>(
|
||||||
|
&self,
|
||||||
|
id: &Id,
|
||||||
|
event: E,
|
||||||
|
) -> 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: &mut Ctx, data: E::Data| {
|
Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| {
|
||||||
for f in &fs {
|
for f in &fs {
|
||||||
f(ctx, data.clone())
|
f(EventCtx {
|
||||||
|
ui: ctx.ui,
|
||||||
|
state: ctx.state,
|
||||||
|
data: ctx.data.clone(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -149,14 +164,18 @@ impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<E: HashableEvent, Ctx: 'static> DefaultEventModule<E, Ctx> {
|
impl<E: HashableEvent, Ctx: 'static> DefaultEventModule<E, Ctx> {
|
||||||
pub fn run_all(&self, ctx: &mut Ctx, event: E, data: E::Data)
|
pub fn run_all(&self, event: E, ctx: EventCtx<Ctx, E::Data>)
|
||||||
where
|
where
|
||||||
E::Data: Clone,
|
E::Data: Clone,
|
||||||
{
|
{
|
||||||
if let Some(map) = self.map.get(&event) {
|
if let Some(map) = self.map.get(&event) {
|
||||||
for fs in map.values() {
|
for fs in map.values() {
|
||||||
for f in fs {
|
for f in fs {
|
||||||
f(ctx, data.clone())
|
f(EventCtx {
|
||||||
|
ui: ctx.ui,
|
||||||
|
state: ctx.state,
|
||||||
|
data: ctx.data.clone(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,30 +191,24 @@ impl<E: Event + 'static, Ctx: 'static> Default for DefaultEventModule<E, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ui {
|
impl Ui {
|
||||||
pub fn run_event<E: Event, Ctx: UiCtx + 'static, W>(
|
pub fn run_event<E: Event, Ctx: 'static, W>(
|
||||||
|
&mut self,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
id: &WidgetId<W>,
|
id: &WidgetId<W>,
|
||||||
event: E,
|
event: E,
|
||||||
data: E::Data,
|
data: E::Data,
|
||||||
) {
|
) {
|
||||||
if let Some(f) = ctx
|
if let Some(f) = self
|
||||||
.ui()
|
|
||||||
.data
|
.data
|
||||||
.modules
|
.modules
|
||||||
.get_mut::<E::Module<Ctx>>()
|
.get_mut::<E::Module<Ctx>>()
|
||||||
.run(&id.id, event)
|
.run(&id.id, event)
|
||||||
{
|
{
|
||||||
f(ctx, data);
|
f(EventCtx {
|
||||||
|
ui: self,
|
||||||
|
state: ctx,
|
||||||
|
data,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EventCtx: UiCtx {
|
|
||||||
fn run_event<E: Event + Clone, W>(&mut self, id: &WidgetId<W>, event: E, data: E::Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: UiCtx + 'static> EventCtx for Ctx {
|
|
||||||
fn run_event<E: Event + Clone, W>(&mut self, id: &WidgetId<W>, event: E, data: E::Data) {
|
|
||||||
Ui::run_event(self, id, event.clone(), data.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use winit::{
|
|||||||
window::WindowId,
|
window::WindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait UiState {
|
pub trait AppState {
|
||||||
type Event: 'static;
|
type Event: 'static;
|
||||||
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self;
|
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self;
|
||||||
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop);
|
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop);
|
||||||
@@ -13,22 +13,22 @@ pub trait UiState {
|
|||||||
fn exit(&mut self);
|
fn exit(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UiApp<State: UiState> {
|
pub struct App<State: AppState> {
|
||||||
state: Option<State>,
|
state: Option<State>,
|
||||||
proxy: EventLoopProxy<State::Event>,
|
proxy: EventLoopProxy<State::Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State: UiState> UiApp<State> {
|
impl<State: AppState> App<State> {
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
let event_loop = EventLoop::with_user_event().build().unwrap();
|
let event_loop = EventLoop::with_user_event().build().unwrap();
|
||||||
let proxy = event_loop.create_proxy();
|
let proxy = event_loop.create_proxy();
|
||||||
event_loop
|
event_loop
|
||||||
.run_app(&mut UiApp::<State> { state: None, proxy })
|
.run_app(&mut App::<State> { state: None, proxy })
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State: UiState> ApplicationHandler<State::Event> for UiApp<State> {
|
impl<State: AppState> ApplicationHandler<State::Event> for App<State> {
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
if self.state.is_none() {
|
if self.state.is_none() {
|
||||||
let state = State::new(event_loop, self.proxy.clone());
|
let state = State::new(event_loop, self.proxy.clone());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{prelude::*, winit::DefaultUi};
|
use crate::{prelude::*, winit::UiState};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use winit::dpi::{LogicalPosition, LogicalSize};
|
use winit::dpi::{LogicalPosition, LogicalSize};
|
||||||
|
|
||||||
@@ -12,13 +12,14 @@ impl<W: 'static> WidgetAttr<W> for Selector {
|
|||||||
ui.register_event(
|
ui.register_event(
|
||||||
&container.clone(),
|
&container.clone(),
|
||||||
CursorSense::click_or_drag(),
|
CursorSense::click_or_drag(),
|
||||||
move |ui: &mut DefaultUi, mut data| {
|
move |mut ctx| {
|
||||||
|
let ui = ctx.ui;
|
||||||
let region = ui.window_region(&id).unwrap();
|
let region = ui.window_region(&id).unwrap();
|
||||||
let id_pos = region.top_left;
|
let id_pos = region.top_left;
|
||||||
let container_pos = ui.window_region(&container).unwrap().top_left;
|
let container_pos = ui.window_region(&container).unwrap().top_left;
|
||||||
data.cursor += container_pos - id_pos;
|
ctx.data.cursor += container_pos - id_pos;
|
||||||
data.size = region.size();
|
ctx.data.size = region.size();
|
||||||
select(id.clone(), ui, data);
|
select(ui, id.clone(), ctx.state, ctx.data);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -31,29 +32,24 @@ impl WidgetAttr<TextEdit> for Selectable {
|
|||||||
|
|
||||||
fn run(ui: &mut Ui, id: &WidgetId<TextEdit>, _: Self::Input) {
|
fn run(ui: &mut Ui, id: &WidgetId<TextEdit>, _: Self::Input) {
|
||||||
let id = id.clone();
|
let id = id.clone();
|
||||||
ui.register_event(
|
ui.register_event(&id.clone(), CursorSense::click_or_drag(), move |ctx| {
|
||||||
&id.clone(),
|
select(ctx.ui, id.clone(), ctx.state, ctx.data);
|
||||||
CursorSense::click_or_drag(),
|
});
|
||||||
move |ui: &mut DefaultUi, data| {
|
|
||||||
select(id.clone(), ui, data);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(id: WidgetId<TextEdit>, ui: &mut DefaultUi, data: CursorData) {
|
fn select(ui: &mut Ui, id: WidgetId<TextEdit>, state: &mut UiState, data: CursorData) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let recent = (now - ui.last_click) < Duration::from_millis(300);
|
let recent = (now - state.last_click) < Duration::from_millis(300);
|
||||||
ui.last_click = now;
|
state.last_click = now;
|
||||||
ui.ui
|
ui.text(&id)
|
||||||
.text(&id)
|
|
||||||
.select(data.cursor, data.size, data.sense.is_dragging(), recent);
|
.select(data.cursor, data.size, data.sense.is_dragging(), recent);
|
||||||
if let Some(region) = ui.ui.window_region(&id) {
|
if let Some(region) = ui.window_region(&id) {
|
||||||
ui.window.set_ime_allowed(true);
|
state.window.set_ime_allowed(true);
|
||||||
ui.window.set_ime_cursor_area(
|
state.window.set_ime_cursor_area(
|
||||||
LogicalPosition::<f32>::from(region.top_left.tuple()),
|
LogicalPosition::<f32>::from(region.top_left.tuple()),
|
||||||
LogicalSize::<f32>::from(region.size().tuple()),
|
LogicalSize::<f32>::from(region.size().tuple()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ui.focus = Some(id);
|
state.focus = Some(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{CursorState, Modifiers},
|
core::{CursorState, Modifiers},
|
||||||
layout::Vec2,
|
layout::Vec2,
|
||||||
winit::DefaultUi,
|
winit::UiState,
|
||||||
};
|
};
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{MouseButton, MouseScrollDelta, WindowEvent},
|
event::{MouseButton, MouseScrollDelta, WindowEvent},
|
||||||
@@ -70,7 +70,7 @@ impl Input {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultUi {
|
impl UiState {
|
||||||
pub fn window_size(&self) -> Vec2 {
|
pub fn window_size(&self) -> Vec2 {
|
||||||
let size = self.renderer.window().inner_size();
|
let size = self.renderer.window().inner_size();
|
||||||
(size.width, size.height).into()
|
(size.width, size.height).into()
|
||||||
|
|||||||
139
src/winit/mod.rs
139
src/winit/mod.rs
@@ -2,7 +2,6 @@ use crate::prelude::*;
|
|||||||
use crate::winit::event::{Edited, Submit};
|
use crate::winit::event::{Edited, Submit};
|
||||||
use crate::winit::{input::Input, render::UiRenderer};
|
use crate::winit::{input::Input, render::UiRenderer};
|
||||||
use arboard::Clipboard;
|
use arboard::Clipboard;
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use winit::event::{Ime, WindowEvent};
|
use winit::event::{Ime, WindowEvent};
|
||||||
@@ -18,11 +17,17 @@ pub mod render;
|
|||||||
pub use app::*;
|
pub use app::*;
|
||||||
|
|
||||||
pub type Proxy<Event> = EventLoopProxy<Event>;
|
pub type Proxy<Event> = EventLoopProxy<Event>;
|
||||||
|
pub type DefaultApp<Data> = App<DefaultState<Data>>;
|
||||||
|
|
||||||
pub struct DefaultUi {
|
pub struct DefaultState<AppState> {
|
||||||
|
ui: Ui,
|
||||||
|
ui_state: UiState,
|
||||||
|
app_state: AppState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UiState {
|
||||||
pub renderer: UiRenderer,
|
pub renderer: UiRenderer,
|
||||||
pub input: Input,
|
pub input: Input,
|
||||||
pub ui: Ui,
|
|
||||||
pub focus: Option<WidgetId<TextEdit>>,
|
pub focus: Option<WidgetId<TextEdit>>,
|
||||||
pub clipboard: Clipboard,
|
pub clipboard: Clipboard,
|
||||||
pub window: Arc<Window>,
|
pub window: Arc<Window>,
|
||||||
@@ -30,62 +35,71 @@ pub struct DefaultUi {
|
|||||||
pub last_click: Instant,
|
pub last_click: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DefaultUiState: 'static {
|
pub trait DefaultAppState: 'static {
|
||||||
type Event: 'static = ();
|
type Event: 'static = ();
|
||||||
fn new(ui: DefaultUi, proxy: Proxy<Self::Event>) -> Self;
|
fn new(ui: &mut Ui, state: &UiState, proxy: Proxy<Self::Event>) -> Self;
|
||||||
fn ui(&mut self) -> &mut DefaultUi;
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn event(&mut self, event: Self::Event) {}
|
fn event(&mut self, event: Self::Event, ui: &mut Ui, state: &UiState) {}
|
||||||
fn exit(&mut self) {}
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn window_event(&mut self, event: WindowEvent) {}
|
fn exit(&mut self, ui: &mut Ui, state: &UiState) {}
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn window_event(&mut self, event: WindowEvent, ui: &mut Ui, state: &UiState) {}
|
||||||
fn window_attrs() -> WindowAttributes {
|
fn window_attrs() -> WindowAttributes {
|
||||||
WindowAttributes::default()
|
WindowAttributes::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State: DefaultUiState> UiState for State {
|
impl<State: DefaultAppState> AppState for DefaultState<State> {
|
||||||
type Event = State::Event;
|
type Event = State::Event;
|
||||||
|
|
||||||
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self {
|
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self {
|
||||||
let window = Arc::new(
|
let window = Arc::new(
|
||||||
event_loop
|
event_loop
|
||||||
.create_window(Self::window_attrs())
|
.create_window(State::window_attrs())
|
||||||
.expect("failed to create window "),
|
.expect("failed to create window "),
|
||||||
);
|
);
|
||||||
let ui = DefaultUi {
|
let mut ui = Ui::new();
|
||||||
|
let ui_state = UiState {
|
||||||
renderer: UiRenderer::new(window.clone()),
|
renderer: UiRenderer::new(window.clone()),
|
||||||
window,
|
window,
|
||||||
input: Input::default(),
|
input: Input::default(),
|
||||||
ui: Ui::new(),
|
|
||||||
clipboard: Clipboard::new().unwrap(),
|
clipboard: Clipboard::new().unwrap(),
|
||||||
ime: 0,
|
ime: 0,
|
||||||
last_click: Instant::now(),
|
last_click: Instant::now(),
|
||||||
focus: None,
|
focus: None,
|
||||||
};
|
};
|
||||||
State::new(ui, proxy)
|
let app_state = State::new(&mut ui, &ui_state, proxy);
|
||||||
|
Self {
|
||||||
|
ui,
|
||||||
|
ui_state,
|
||||||
|
app_state,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) {
|
fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) {
|
||||||
self.event(event);
|
self.app_state.event(event, &mut self.ui, &self.ui_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) {
|
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) {
|
||||||
let ui = self.ui();
|
let Self {
|
||||||
let input_changed = ui.input.event(&event);
|
ui,
|
||||||
let cursor_state = ui.cursor_state().clone();
|
ui_state: ui_data,
|
||||||
let old = ui.focus.clone();
|
app_state: app_data,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let input_changed = ui_data.input.event(&event);
|
||||||
|
let cursor_state = ui_data.cursor_state().clone();
|
||||||
|
let old = ui_data.focus.clone();
|
||||||
if cursor_state.buttons.left.is_start() {
|
if cursor_state.buttons.left.is_start() {
|
||||||
ui.focus = None;
|
ui_data.focus = None;
|
||||||
}
|
}
|
||||||
if input_changed {
|
if input_changed {
|
||||||
let window_size = ui.window_size();
|
let window_size = ui_data.window_size();
|
||||||
ui.run_sensors(&cursor_state, window_size);
|
ui.run_sensors(&mut (), &cursor_state, window_size);
|
||||||
ui.ui.run_sensors(&cursor_state, window_size);
|
ui.run_sensors(ui_data, &cursor_state, window_size);
|
||||||
self.run_sensors(&cursor_state, window_size);
|
ui.run_sensors(app_data, &cursor_state, window_size);
|
||||||
}
|
}
|
||||||
let ui = self.ui();
|
if old != ui_data.focus
|
||||||
if old != ui.focus
|
|
||||||
&& let Some(old) = old
|
&& let Some(old) = old
|
||||||
{
|
{
|
||||||
ui.text(&old).deselect();
|
ui.text(&old).deselect();
|
||||||
@@ -93,55 +107,55 @@ impl<State: DefaultUiState> UiState for State {
|
|||||||
match &event {
|
match &event {
|
||||||
WindowEvent::CloseRequested => event_loop.exit(),
|
WindowEvent::CloseRequested => event_loop.exit(),
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
ui.ui.update();
|
ui.update();
|
||||||
ui.renderer.update(&mut ui.ui);
|
ui_data.renderer.update(ui);
|
||||||
ui.renderer.draw();
|
ui_data.renderer.draw();
|
||||||
}
|
}
|
||||||
WindowEvent::Resized(size) => {
|
WindowEvent::Resized(size) => {
|
||||||
ui.ui.resize((size.width, size.height));
|
ui.resize((size.width, size.height));
|
||||||
ui.renderer.resize(size)
|
ui_data.renderer.resize(size)
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput { event, .. } => {
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
if let Some(sel) = &ui.focus
|
if let Some(sel) = &ui_data.focus
|
||||||
&& event.state.is_pressed()
|
&& event.state.is_pressed()
|
||||||
{
|
{
|
||||||
let sel = &sel.clone();
|
let sel = &sel.clone();
|
||||||
let mut text = ui.ui.text(sel);
|
let mut text = ui.text(sel);
|
||||||
match text.apply_event(event, &ui.input.modifiers) {
|
match text.apply_event(event, &ui_data.input.modifiers) {
|
||||||
TextInputResult::Unfocus => {
|
TextInputResult::Unfocus => {
|
||||||
ui.focus = None;
|
ui_data.focus = None;
|
||||||
ui.window.set_ime_allowed(false);
|
ui_data.window.set_ime_allowed(false);
|
||||||
}
|
}
|
||||||
TextInputResult::Submit => {
|
TextInputResult::Submit => {
|
||||||
self.run_event(sel, Submit, ());
|
ui.run_event(app_data, sel, Submit, ());
|
||||||
}
|
}
|
||||||
TextInputResult::Paste => {
|
TextInputResult::Paste => {
|
||||||
if let Ok(t) = ui.clipboard.get_text() {
|
if let Ok(t) = ui_data.clipboard.get_text() {
|
||||||
text.insert(&t);
|
text.insert(&t);
|
||||||
}
|
}
|
||||||
self.run_event(sel, Edited, ());
|
ui.run_event(app_data, sel, Edited, ());
|
||||||
}
|
}
|
||||||
TextInputResult::Copy(text) => {
|
TextInputResult::Copy(text) => {
|
||||||
if let Err(err) = ui.clipboard.set_text(text) {
|
if let Err(err) = ui_data.clipboard.set_text(text) {
|
||||||
eprintln!("failed to copy text to clipboard: {err}")
|
eprintln!("failed to copy text to clipboard: {err}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextInputResult::Used => {
|
TextInputResult::Used => {
|
||||||
self.run_event(sel, Edited, ());
|
ui.run_event(app_data, sel, Edited, ());
|
||||||
}
|
}
|
||||||
TextInputResult::Unused => {}
|
TextInputResult::Unused => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::Ime(ime) => {
|
WindowEvent::Ime(ime) => {
|
||||||
if let Some(sel) = &ui.focus {
|
if let Some(sel) = &ui_data.focus {
|
||||||
let mut text = ui.ui.text(sel);
|
let mut text = ui.text(sel);
|
||||||
match ime {
|
match ime {
|
||||||
Ime::Enabled | Ime::Disabled => (),
|
Ime::Enabled | Ime::Disabled => (),
|
||||||
Ime::Preedit(content, _pos) => {
|
Ime::Preedit(content, _pos) => {
|
||||||
// TODO: highlight once that's real
|
// TODO: highlight once that's real
|
||||||
text.replace(ui.ime, content);
|
text.replace(ui_data.ime, content);
|
||||||
ui.ime = content.chars().count();
|
ui_data.ime = content.chars().count();
|
||||||
}
|
}
|
||||||
Ime::Commit(content) => {
|
Ime::Commit(content) => {
|
||||||
text.insert(content);
|
text.insert(content);
|
||||||
@@ -151,41 +165,14 @@ impl<State: DefaultUiState> UiState for State {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
self.window_event(event);
|
app_data.window_event(event, ui, ui_data);
|
||||||
let ui = self.ui();
|
|
||||||
if ui.needs_redraw() {
|
if ui.needs_redraw() {
|
||||||
ui.renderer.window().request_redraw();
|
ui_data.renderer.window().request_redraw();
|
||||||
}
|
}
|
||||||
ui.input.end_frame();
|
ui_data.input.end_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit(&mut self) {
|
fn exit(&mut self) {
|
||||||
self.exit();
|
self.app_state.exit(&mut self.ui, &self.ui_state);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: DefaultUiState> UiCtx for T {
|
|
||||||
fn ui(&mut self) -> &mut Ui {
|
|
||||||
&mut self.ui().ui
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UiCtx for DefaultUi {
|
|
||||||
fn ui(&mut self) -> &mut Ui {
|
|
||||||
&mut self.ui
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for DefaultUi {
|
|
||||||
type Target = Ui;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.ui
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for DefaultUi {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.ui
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user