Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/src/settings_view/environments_page_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1311,7 +1311,7 @@ fn test_toolbar_renders_search_editor_view() {
let env_page_id = env_page_handle.id();
let search_editor_id = env_page.search_editor.id();

let chain = presenter.borrow().ancestors(search_editor_id);
let chain = ctx.view_ancestors(window_id, search_editor_id);
assert!(
chain.len() >= 2,
"Expected search editor to be laid out as a child view; got ancestors={chain:?}"
Expand Down
347 changes: 221 additions & 126 deletions crates/warpui_core/src/core/app.rs

Large diffs are not rendered by default.

26 changes: 19 additions & 7 deletions crates/warpui_core/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,17 @@ impl fmt::Display for TaskId {

pub type OptionalPlatformWindow = Option<Rc<dyn platform::Window>>;

// The view callbacks receive the type-erased view state (`dyn Any`) so the
// action registries are shared by GUI views and (with the `tui` feature) TUI
// views; the callbacks downcast to the concrete view type they were
// registered for.
type ActionCallback =
dyn FnMut(&mut dyn AnyView, &dyn Any, &mut AppContext, WindowId, EntityId) -> bool;
dyn FnMut(&mut dyn Any, &dyn Any, &mut AppContext, WindowId, EntityId) -> bool;

type TypedActionCallback =
dyn FnMut(&mut dyn AnyView, &dyn Any, &mut AppContext, WindowId, EntityId);
type TypedActionCallback = dyn FnMut(&mut dyn Any, &dyn Any, &mut AppContext, WindowId, EntityId);

/// Per-view-type action handlers, keyed by action name.
type ActionHandlersByName = HashMap<String, Vec<Box<ActionCallback>>>;

type GlobalActionCallback =
dyn FnMut(&dyn Any, &'static std::panic::Location<'static>, &mut AppContext);
Expand Down Expand Up @@ -230,6 +236,9 @@ pub enum Effect {
},
}

/// The object-safe, type-erased view object stored per window. Carries the
/// render + focus/blur/keymap/a11y hooks the shared core needs without naming
/// the concrete view type.
pub trait AnyView {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
Expand Down Expand Up @@ -294,7 +303,7 @@ where
T::ui_name()
}

fn render<'a>(&self, app: &AppContext) -> Box<dyn Element> {
fn render(&self, app: &AppContext) -> Box<dyn Element> {
View::render(self, app)
}

Expand Down Expand Up @@ -538,13 +547,16 @@ type ModelFromFutureCallback = dyn FnOnce(&mut dyn Any, Box<dyn Any>, &mut AppCo
type ModelFromStreamItemCallback = dyn FnMut(&mut dyn Any, Box<dyn Any>, &mut AppContext, EntityId);
type ModelFromStreamDoneCallback = dyn FnOnce(&mut dyn Any, &mut AppContext, EntityId);

// The view task callbacks receive the type-erased view state (`dyn Any`),
// shared by GUI and TUI views; the callbacks downcast to the concrete view
// type they were registered for.
type ViewFromFutureCallback =
dyn FnOnce(&mut dyn AnyView, Box<dyn Any>, &mut AppContext, WindowId, EntityId);
dyn FnOnce(&mut dyn Any, Box<dyn Any>, &mut AppContext, WindowId, EntityId);

type ViewFromStreamItemCallback =
dyn FnMut(&mut dyn AnyView, Box<dyn Any>, &mut AppContext, WindowId, EntityId);
dyn FnMut(&mut dyn Any, Box<dyn Any>, &mut AppContext, WindowId, EntityId);

type ViewFromStreamDoneCallback = dyn FnOnce(&mut dyn AnyView, &mut AppContext, WindowId, EntityId);
type ViewFromStreamDoneCallback = dyn FnOnce(&mut dyn Any, &mut AppContext, WindowId, EntityId);

enum TaskCallback {
ModelFromFuture {
Expand Down
2 changes: 1 addition & 1 deletion crates/warpui_core/src/core/ref_count_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ fn test_weak_view_handle_fails_after_last_strong_handle_dropped_in_event_callbac
}

impl super::super::View for TargetView {
fn render(&self, _: &AppContext) -> Box<dyn crate::Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down
27 changes: 14 additions & 13 deletions crates/warpui_core/src/core/transfer_view_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::cell::RefCell;
use std::rc::Rc;

use super::*;
use crate::elements::{Element, Empty};

#[test]
fn test_transfer_view_to_window_updates_window_mapping() {
Expand All @@ -15,7 +16,7 @@ fn test_transfer_view_to_window_updates_window_mapping() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -96,7 +97,7 @@ fn test_transfer_view_subscriptions_continue_working() {
}

impl View for EmitterView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand All @@ -119,7 +120,7 @@ fn test_transfer_view_subscriptions_continue_working() {
}

impl View for SubscriberView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -190,7 +191,7 @@ fn test_transfer_view_app_subscriptions_continue_working() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -250,7 +251,7 @@ fn test_transfer_view_observations_continue_working() {
}

impl View for ObserverView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -329,7 +330,7 @@ fn test_on_window_transferred_callback_fires() {
}

impl View for TransferTrackingView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -395,7 +396,7 @@ fn test_weak_view_handle_upgrade_after_transfer() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -459,7 +460,7 @@ fn test_transfer_nonexistent_view_returns_false() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -495,7 +496,7 @@ fn test_transfer_to_same_window_is_noop() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -548,7 +549,7 @@ fn test_transfer_view_drop_and_reference_counting() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -624,7 +625,7 @@ fn test_transfer_structural_children_follows_parent() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -709,7 +710,7 @@ fn test_transfer_structural_grandchildren_follows_transitively() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down Expand Up @@ -780,7 +781,7 @@ fn test_transfer_structural_children_does_not_move_unrelated_views() {
}

impl View for TestView {
fn render(&self, _: &AppContext) -> Box<dyn Element> {
fn render(&self, _: &AppContext) -> Box<dyn crate::elements::Element> {
Empty::new().finish()
}

Expand Down
11 changes: 4 additions & 7 deletions crates/warpui_core/src/core/view/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ impl<'a, T: View> ViewContext<'a, T> {
window_id: self.window_id,
view_id: self.view_id,
callback: Box::new(move |view, output, app, window_id, view_id| {
let view = view.as_any_mut().downcast_mut().expect("this downcast should never fail, as correct typing is statically enforced via the generic parameters on spawn_local");
let view = view.downcast_mut().expect("this downcast should never fail, as correct typing is statically enforced via the generic parameters on spawn_local");
let output = *output.downcast().expect("this downcast should never fail, as correct typing is statically enforced via the generic parameters on spawn_local");
let result =
callback(view, output, &mut ViewContext::new(app, window_id, view_id));
Expand Down Expand Up @@ -657,13 +657,13 @@ impl<'a, T: View> ViewContext<'a, T> {
window_id: self.window_id,
view_id: self.view_id,
on_item: Box::new(move |view, output, app, window_id, view_id| {
let view = view.as_any_mut().downcast_mut().expect("this downcast should never fail, as correct typing is statically enforced via the generic parameters on spawn_local");
let view = view.downcast_mut().expect("this downcast should never fail, as correct typing is statically enforced via the generic parameters on spawn_local");
let output = *output.downcast().expect("this downcast should never fail, as correct typing is statically enforced via the generic parameters on spawn_local");
let mut ctx = ViewContext::new(app, window_id, view_id);
on_item(view, output, &mut ctx);
}),
on_done: Box::new(move |view, app, window_id, view_id| {
let view = view.as_any_mut().downcast_mut().expect("this downcast should never fail, as correct typing is statically enforced via the generic parameters on spawn_local");
let view = view.downcast_mut().expect("this downcast should never fail, as correct typing is statically enforced via the generic parameters on spawn_local");
let mut ctx = ViewContext::new(app, window_id, view_id);
on_done(view, &mut ctx);
}),
Expand Down Expand Up @@ -777,10 +777,7 @@ impl<'a, T: View> ViewContext<'a, T> {
window_id: self.window_id,
view_id: self.view_id,
on_item: Box::new(move |view, task, app, window_id, view_id| {
let view = view
.as_any_mut()
.downcast_mut()
.expect("unexpected view type");
let view = view.downcast_mut().expect("unexpected view type");
let task: ViewTask<T> = *task
.downcast()
.expect("task from spawner should be ViewTask<T>");
Expand Down
6 changes: 5 additions & 1 deletion crates/warpui_core/src/core/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use serde::{Deserialize, Serialize};

use crate::core::view::AnyViewHandle;
use crate::{AnyView, EntityId};
use crate::core::AnyView;
use crate::EntityId;

/// A unique identifier for a window.
///
Expand Down Expand Up @@ -36,6 +37,9 @@ impl fmt::Display for WindowId {

/// A structure holding all application state that is linked to a particular
/// window.
///
/// Backend-neutral since the view-trait unification: `views` stores the single
/// type-erased [`AnyView`] object, whose render output is a `Box<dyn Element>`.
#[derive(Default)]
pub(super) struct Window {
/// The set of views owned by this window, keyed by view ID.
Expand Down
35 changes: 1 addition & 34 deletions crates/warpui_core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,16 @@ use std::ops::Range;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::Vector2F;

use crate::elements::{Point, ZIndex};
use crate::keymap::Keystroke;
use crate::platform::keyboard::KeyCode;
use crate::zoom::{Scale, ZoomFactor};
use crate::EventContext;

#[derive(Debug)]
pub struct DispatchedEvent {
event: Event,
pub(crate) event: Event,
}

impl DispatchedEvent {
/// Filters out event types that most-likely shouldn't be handled if this
/// event is being received by an element at the given z-index
pub fn at_z_index(&self, z_index: ZIndex, ctx: &EventContext) -> Option<&Event> {
match self.event {
Event::KeyDown { .. } => Some(&self.event),
Event::ScrollWheel { position, .. }
| Event::LeftMouseDown { position, .. }
| Event::LeftMouseUp { position, .. }
| Event::LeftMouseDragged { position, .. }
| Event::MiddleMouseDown { position, .. }
| Event::RightMouseDown { position, .. }
| Event::BackMouseDown { position, .. }
| Event::ForwardMouseDown { position, .. } => {
if !ctx.is_covered(Point::from_vec2f(position, z_index)) {
Some(&self.event)
} else {
None
}
}
Event::MouseMoved { .. } => Some(&self.event),
Event::ModifierStateChanged { .. } => Some(&self.event),
Event::ModifierKeyChanged { .. } => Some(&self.event),
Event::TypedCharacters { .. } => Some(&self.event),
Event::DragAndDropFiles { .. } => Some(&self.event),
Event::DragFiles { .. } => Some(&self.event),
Event::DragFileExit => Some(&self.event),
Event::SetMarkedText { .. } => Some(&self.event),
Event::ClearMarkedText => Some(&self.event),
}
}

/// Returns the raw event - note that an element at a higher z-index
/// may already have handled it.
pub fn raw_event(&self) -> &Event {
Expand Down
Loading