2 Commits

Author SHA1 Message Date
1102dc7338 work 2026-02-26 19:18:27 -05:00
1aadef0e7e fix Draw (redraw) 2026-02-21 00:19:39 -05:00
9 changed files with 130 additions and 78 deletions

View File

@@ -28,7 +28,7 @@ pub trait UiRsc {
#[allow(unused_variables)]
fn on_remove(&mut self, id: WidgetId) {}
#[allow(unused_variables)]
fn on_draw(&mut self, active: &ActiveData) {}
fn on_draw(&mut self, active: &ActiveData, redrawn: bool) {}
#[allow(unused_variables)]
fn on_undraw(&mut self, active: &ActiveData) {}

View File

@@ -81,10 +81,12 @@ impl UiRenderState {
old_children: Option<Vec<WidgetId>>,
rsc: &mut dyn UiRsc,
) {
let mut redrawn = old_children.is_some();
let mut old_children = old_children.unwrap_or_default();
if let Some(active) = self.active.get_mut(&id)
&& !rsc.widgets().needs_redraw.contains(&id)
{
redrawn = true;
// check to see if we can skip drawing first
if active.region == region {
return;
@@ -149,7 +151,7 @@ impl UiRenderState {
}
}
rsc.on_draw(&active);
rsc.on_draw(&active, redrawn);
self.active.insert(id, active);
}

View File

@@ -10,11 +10,7 @@ struct State {
}
impl DefaultAppState for State {
fn new(
mut ui_state: DefaultUiState,
rsc: &mut DefaultRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
rect(Color::RED).set_root(rsc, &mut ui_state);
Self { ui_state }
}

View File

@@ -16,11 +16,7 @@ pub struct Client {
}
impl DefaultAppState for Client {
fn new(
mut ui_state: DefaultUiState,
rsc: &mut DefaultRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
let rrect = rect(Color::WHITE).radius(20);
let pad_test = (
rrect.color(Color::BLUE),

View File

@@ -11,11 +11,7 @@ struct State {
}
impl DefaultAppState for State {
fn new(
mut ui_state: DefaultUiState,
rsc: &mut DefaultRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
let rect = rect(Color::RED).add(rsc);
rect.task_on(CursorSense::click(), async move |mut ctx| {
tokio::time::sleep(Duration::from_secs(1)).await;

View File

@@ -36,11 +36,7 @@ impl Test {
}
impl DefaultAppState for State {
fn new(
mut ui_state: DefaultUiState,
rsc: &mut DefaultRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
let test = Test::new(rsc);
test.on(CursorSense::click(), move |_, rsc| {

View File

@@ -11,3 +11,7 @@ impl Event for Edited {}
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct Draw;
impl Event for Draw {}
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct Undraw;
impl Event for Undraw {}

View File

@@ -29,7 +29,26 @@ pub use sense::*;
pub use state::*;
pub use task::*;
pub type Proxy<Event> = EventLoopProxy<Event>;
pub struct EventSender<State: DefaultAppState> {
proxy: EventLoopProxy<UiMainEvent<State>>,
}
impl<State: DefaultAppState> Clone for EventSender<State> {
fn clone(&self) -> Self {
Self {
proxy: self.proxy.clone(),
}
}
}
impl<State: DefaultAppState> EventSender<State> {
pub fn send(&self, event: State::Event) {
let _ = self.proxy.send_event(UiMainEvent::App(event));
}
pub fn run(&self, f: impl MainCallback<State>) {
let _ = self.proxy.send_event(UiMainEvent::Callback(Box::new(f)));
}
}
pub struct DefaultUiState {
pub root: Option<StrongWidget>,
@@ -70,9 +89,8 @@ pub trait HasDefaultUiState: Sized + 'static {
}
pub trait DefaultAppState: HasDefaultUiState {
type Event = ();
fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, proxy: Proxy<Self::Event>)
-> Self;
type Event: Send = ();
fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self;
#[allow(unused_variables)]
fn event(
&mut self,
@@ -96,12 +114,13 @@ pub trait DefaultAppState: HasDefaultUiState {
}
}
pub struct DefaultRsc<State: 'static> {
pub struct DefaultRsc<State: 'static + DefaultAppState> {
pub ui: UiData,
pub events: EventManager<Self>,
pub tasks: Tasks<Self>,
pub state: WidgetState,
pub widget_events: Vec<WidgetEvent>,
pub window_event: EventSender<State>,
_state: PhantomData<State>,
}
@@ -116,9 +135,25 @@ pub enum WidgetEventType {
Remove,
}
impl<State> DefaultRsc<State> {
fn init(window: Arc<Window>) -> (Self, TaskMsgReceiver<Self>) {
let (tasks, recv) = Tasks::init(window);
pub trait MainCallback<State>: FnOnce(&mut DefaultRsc<State>) + Sync + Send + 'static {}
impl<F: FnOnce(&mut DefaultRsc<State>) + Sync + Send + 'static, State> MainCallback<State> for F {}
pub enum UiMainEvent<State: DefaultAppState> {
RequestUpdate,
Callback(Box<dyn MainCallback<State>>),
App(State::Event),
}
impl<State: DefaultAppState> DefaultRsc<State> {
fn init(proxy: EventLoopProxy<UiMainEvent<State>>) -> (Self, TaskMsgReceiver<Self>) {
let window_event = EventSender {
proxy: proxy.clone(),
};
let (tasks, recv) = Tasks::init(move || {
if proxy.send_event(UiMainEvent::RequestUpdate).is_err() {
panic!("main thread blew up or smth");
}
});
(
Self {
ui: Default::default(),
@@ -126,6 +161,7 @@ impl<State> DefaultRsc<State> {
tasks,
widget_events: Default::default(),
state: Default::default(),
window_event,
_state: Default::default(),
},
recv,
@@ -137,7 +173,7 @@ impl<State> DefaultRsc<State> {
}
}
impl<State> UiRsc for DefaultRsc<State> {
impl<State: DefaultAppState> UiRsc for DefaultRsc<State> {
fn ui(&self) -> &UiData {
&self.ui
}
@@ -146,13 +182,15 @@ impl<State> UiRsc for DefaultRsc<State> {
&mut self.ui
}
fn on_draw(&mut self, active: &ActiveData) {
fn on_draw(&mut self, active: &ActiveData, redrawn: bool) {
self.events.draw(active);
if !redrawn {
self.widget_events.push(WidgetEvent {
id: active.id,
ty: WidgetEventType::Draw,
});
}
}
fn on_undraw(&mut self, active: &ActiveData) {
self.events.undraw(active);
@@ -172,11 +210,11 @@ impl<State> UiRsc for DefaultRsc<State> {
}
}
impl<State: 'static> HasState for DefaultRsc<State> {
impl<State: 'static + DefaultAppState> HasState for DefaultRsc<State> {
type State = State;
}
impl<State: 'static> HasEvents for DefaultRsc<State> {
impl<State: 'static + DefaultAppState> HasEvents for DefaultRsc<State> {
fn events(&self) -> &EventManager<Self> {
&self.events
}
@@ -186,13 +224,13 @@ impl<State: 'static> HasEvents for DefaultRsc<State> {
}
}
impl<State: 'static> HasTasks for DefaultRsc<State> {
impl<State: 'static + DefaultAppState> HasTasks for DefaultRsc<State> {
fn tasks_mut(&mut self) -> &mut Tasks<Self> {
&mut self.tasks
}
}
impl<State: 'static> HasWidgetState for DefaultRsc<State> {
impl<State: 'static + DefaultAppState> HasWidgetState for DefaultRsc<State> {
fn widget_state(&self) -> &WidgetState {
&self.state
}
@@ -210,15 +248,15 @@ pub struct DefaultApp<State: DefaultAppState> {
}
impl<State: DefaultAppState> AppState for DefaultApp<State> {
type Event = State::Event;
type Event = UiMainEvent<State>;
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self {
let window = event_loop
.create_window(State::window_attributes())
.unwrap();
let default_state = DefaultUiState::new(window);
let (mut rsc, task_recv) = DefaultRsc::init(default_state.window.clone());
let state = State::new(default_state, &mut rsc, proxy);
let (mut rsc, task_recv) = DefaultRsc::init(proxy);
let state = State::new(default_state, &mut rsc);
let render = UiRenderState::new();
Self {
rsc,
@@ -229,38 +267,39 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
}
fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) {
match event {
UiMainEvent::RequestUpdate => {
self.check_updates();
}
UiMainEvent::App(event) => {
self.state.event(event, &mut self.rsc, &mut self.render);
}
UiMainEvent::Callback(f) => f(&mut self.rsc),
}
}
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) {
let Self {
rsc,
render,
state,
task_recv,
rsc, render, state, ..
} = self;
for update in task_recv.try_iter() {
update(state, rsc);
}
// input handling
let ui_state = state.default_state_mut();
let input_changed = ui_state.input.event(&event);
if ui_state.input.event(&event) {
let cursor_state = ui_state.cursor_state().clone();
let old = ui_state.focus;
if cursor_state.buttons.left.is_start() {
ui_state.focus = None;
}
if input_changed {
let window_size = ui_state.window_size();
render.run_sensors(rsc, state, cursor_state, window_size);
}
let ui_state = state.default_state_mut();
if old != ui_state.focus
if old != state.default_state().focus
&& let Some(old) = old
{
old.edit(rsc).deselect();
}
}
let ui_state = state.default_state_mut();
match &event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
@@ -323,26 +362,46 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
}
state.window_event(event, rsc, render);
self.check_updates();
self.state.default_state_mut().input.end_frame();
}
fn exit(&mut self) {
self.state.exit(&mut self.rsc, &mut self.render);
}
}
impl<State: DefaultAppState> DefaultApp<State> {
pub fn check_updates(&mut self) {
let Self {
rsc,
render,
state,
task_recv,
} = self;
for update in task_recv.try_iter() {
update(state, rsc);
}
let mut events = std::mem::take(&mut rsc.widget_events);
for event in events.drain(..) {
match event.ty {
WidgetEventType::Draw => {
rsc.run_event::<Draw>(event.id, (), state);
}
WidgetEventType::Undraw => {
rsc.run_event::<Undraw>(event.id, (), state);
}
_ => (),
}
}
rsc.widget_events = events;
let ui_state = self.state.default_state_mut();
let ui_state = state.default_state();
if render.needs_redraw(&ui_state.root, rsc.widgets()) {
ui_state.renderer.window().request_redraw();
}
ui_state.input.end_frame();
}
fn exit(&mut self) {
self.state.exit(&mut self.rsc, &mut self.render);
}
}
@@ -352,7 +411,9 @@ pub trait RscIdx<Rsc> {
fn get_mut(self, rsc: &mut Rsc) -> &mut Self::Output;
}
impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::Index<I> for DefaultRsc<State> {
impl<State: 'static + DefaultAppState, I: RscIdx<DefaultRsc<State>>> std::ops::Index<I>
for DefaultRsc<State>
{
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
@@ -360,7 +421,9 @@ impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::Index<I> for Defaul
}
}
impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::IndexMut<I> for DefaultRsc<State> {
impl<State: 'static + DefaultAppState, I: RscIdx<DefaultRsc<State>>> std::ops::IndexMut<I>
for DefaultRsc<State>
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
index.get_mut(self)
}

View File

@@ -13,7 +13,6 @@ use tokio::{
unbounded_channel as async_channel,
},
};
use winit::window::Window;
pub type TaskMsgSender<Rsc> = SyncSender<Box<dyn TaskUpdate<Rsc>>>;
pub type TaskMsgReceiver<Rsc> = SyncReceiver<Box<dyn TaskUpdate<Rsc>>>;
@@ -23,7 +22,7 @@ impl<F: FnOnce(&mut Rsc::State, &mut Rsc) + Send, Rsc: HasState> TaskUpdate<Rsc>
pub struct Tasks<Rsc: HasState> {
start: AsyncSender<BoxTask>,
window: Arc<Window>,
request_update: Arc<dyn Fn() + Send + Sync>,
msg_send: SyncSender<Box<dyn TaskUpdate<Rsc>>>,
}
@@ -45,7 +44,7 @@ impl<Rsc: HasState + 'static> TaskCtx<Rsc> {
type BoxTask = Pin<Box<dyn Future<Output = ()> + Send>>;
impl<Rsc: HasState> Tasks<Rsc> {
pub fn init(window: Arc<Window>) -> (Self, TaskMsgReceiver<Rsc>) {
pub fn init(request_update: impl Fn() + 'static + Send + Sync) -> (Self, TaskMsgReceiver<Rsc>) {
let (start, start_recv) = async_channel();
let (msgs, msgs_recv) = sync_channel();
std::thread::spawn(|| {
@@ -56,7 +55,7 @@ impl<Rsc: HasState> Tasks<Rsc> {
Self {
start,
msg_send: msgs,
window,
request_update: Arc::new(request_update),
},
msgs_recv,
)
@@ -67,10 +66,10 @@ impl<Rsc: HasState> Tasks<Rsc> {
F::CallOnceFuture: Send,
{
let send = self.msg_send.clone();
let window = self.window.clone();
let request_update = self.request_update.clone();
let _ = self.start.send(Box::pin(async move {
task(TaskCtx::new(send)).await;
window.request_redraw();
request_update();
}));
}
}