Merge branch 'main' into editor-tests
This commit is contained in:
commit
4c5d5105f3
100 changed files with 13615 additions and 5795 deletions
|
@ -1,14 +1,15 @@
|
|||
use crate::{
|
||||
px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
|
||||
Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DispatchContext, DisplayId,
|
||||
Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId,
|
||||
GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
|
||||
KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
|
||||
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay,
|
||||
PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render,
|
||||
RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow,
|
||||
SharedString, Size, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline,
|
||||
UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
|
||||
build_action_from_type, px, size, Action, AnyBox, AnyDrag, AnyView, AppContext,
|
||||
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
|
||||
DevicePixels, DispatchContext, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
|
||||
FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, ImageData, InputEvent,
|
||||
IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers,
|
||||
MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite,
|
||||
PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
|
||||
SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription,
|
||||
TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView,
|
||||
WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::HashMap;
|
||||
|
@ -145,6 +146,11 @@ impl FocusHandle {
|
|||
}
|
||||
}
|
||||
|
||||
/// Moves the focus to the element associated with this handle.
|
||||
pub fn focus(&self, cx: &mut WindowContext) {
|
||||
cx.focus(self)
|
||||
}
|
||||
|
||||
/// Obtains whether the element associated with this handle is currently focused.
|
||||
pub fn is_focused(&self, cx: &WindowContext) -> bool {
|
||||
self.id.is_focused(cx)
|
||||
|
@ -200,7 +206,7 @@ pub struct Window {
|
|||
display_id: DisplayId,
|
||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||
rem_size: Pixels,
|
||||
content_size: Size<Pixels>,
|
||||
viewport_size: Size<Pixels>,
|
||||
pub(crate) layout_engine: TaffyLayoutEngine,
|
||||
pub(crate) root_view: Option<AnyView>,
|
||||
pub(crate) element_id_stack: GlobalElementId,
|
||||
|
@ -227,7 +233,7 @@ pub(crate) struct Frame {
|
|||
key_matchers: HashMap<GlobalElementId, KeyMatcher>,
|
||||
mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyListener)>>,
|
||||
pub(crate) focus_listeners: Vec<AnyFocusListener>,
|
||||
key_dispatch_stack: Vec<KeyDispatchStackFrame>,
|
||||
pub(crate) key_dispatch_stack: Vec<KeyDispatchStackFrame>,
|
||||
freeze_key_dispatch_stack: bool,
|
||||
focus_parents_by_child: HashMap<FocusId, FocusId>,
|
||||
pub(crate) scene_builder: SceneBuilder,
|
||||
|
@ -299,7 +305,7 @@ impl Window {
|
|||
display_id,
|
||||
sprite_atlas,
|
||||
rem_size: px(16.),
|
||||
content_size,
|
||||
viewport_size: content_size,
|
||||
layout_engine: TaffyLayoutEngine::new(),
|
||||
root_view: None,
|
||||
element_id_stack: GlobalElementId::default(),
|
||||
|
@ -326,7 +332,7 @@ impl Window {
|
|||
/// find the focused element. We interleave key listeners with dispatch contexts so we can use the
|
||||
/// contexts when matching key events against the keymap. A key listener can be either an action
|
||||
/// handler or a [KeyDown] / [KeyUp] event listener.
|
||||
enum KeyDispatchStackFrame {
|
||||
pub(crate) enum KeyDispatchStackFrame {
|
||||
Listener {
|
||||
event_type: TypeId,
|
||||
listener: AnyKeyListener,
|
||||
|
@ -401,11 +407,18 @@ impl<'a> WindowContext<'a> {
|
|||
|
||||
/// Move focus to the element associated with the given `FocusHandle`.
|
||||
pub fn focus(&mut self, handle: &FocusHandle) {
|
||||
if self.window.focus == Some(handle.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.window.last_blur.is_none() {
|
||||
self.window.last_blur = Some(self.window.focus);
|
||||
}
|
||||
|
||||
self.window.focus = Some(handle.id);
|
||||
|
||||
// self.window.current_frame.key_dispatch_stack.clear()
|
||||
// self.window.root_view.initialize()
|
||||
self.app.push_effect(Effect::FocusChanged {
|
||||
window_handle: self.window.handle,
|
||||
focused: Some(handle.id),
|
||||
|
@ -427,6 +440,14 @@ impl<'a> WindowContext<'a> {
|
|||
self.notify();
|
||||
}
|
||||
|
||||
pub fn dispatch_action(&mut self, action: Box<dyn Action>) {
|
||||
self.defer(|cx| {
|
||||
cx.app.propagate_event = true;
|
||||
let stack = cx.dispatch_stack();
|
||||
cx.dispatch_action_internal(action, &stack[..])
|
||||
})
|
||||
}
|
||||
|
||||
/// Schedules the given function to be run at the end of the current effect cycle, allowing entities
|
||||
/// that are currently on the stack to be returned to the app.
|
||||
pub fn defer(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
|
||||
|
@ -609,7 +630,7 @@ impl<'a> WindowContext<'a> {
|
|||
|
||||
fn window_bounds_changed(&mut self) {
|
||||
self.window.scale_factor = self.window.platform_window.scale_factor();
|
||||
self.window.content_size = self.window.platform_window.content_size();
|
||||
self.window.viewport_size = self.window.platform_window.content_size();
|
||||
self.window.bounds = self.window.platform_window.bounds();
|
||||
self.window.display_id = self.window.platform_window.display().id();
|
||||
self.window.dirty = true;
|
||||
|
@ -624,6 +645,10 @@ impl<'a> WindowContext<'a> {
|
|||
self.window.bounds
|
||||
}
|
||||
|
||||
pub fn viewport_size(&self) -> Size<Pixels> {
|
||||
self.window.viewport_size
|
||||
}
|
||||
|
||||
pub fn is_window_active(&self) -> bool {
|
||||
self.window.active
|
||||
}
|
||||
|
@ -717,7 +742,7 @@ impl<'a> WindowContext<'a> {
|
|||
|
||||
/// Called during painting to invoke the given closure in a new stacking context. The given
|
||||
/// z-index is interpreted relative to the previous call to `stack`.
|
||||
pub fn stack<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
pub fn with_z_index<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
self.window.current_frame.z_index_stack.push(z_index);
|
||||
let result = f(self);
|
||||
self.window.current_frame.z_index_stack.pop();
|
||||
|
@ -1015,13 +1040,13 @@ impl<'a> WindowContext<'a> {
|
|||
|
||||
self.start_frame();
|
||||
|
||||
self.stack(0, |cx| {
|
||||
let available_space = cx.window.content_size.map(Into::into);
|
||||
self.with_z_index(0, |cx| {
|
||||
let available_space = cx.window.viewport_size.map(Into::into);
|
||||
root_view.draw(available_space, cx);
|
||||
});
|
||||
|
||||
if let Some(active_drag) = self.app.active_drag.take() {
|
||||
self.stack(1, |cx| {
|
||||
self.with_z_index(1, |cx| {
|
||||
let offset = cx.mouse_position() - active_drag.cursor_offset;
|
||||
cx.with_element_offset(Some(offset), |cx| {
|
||||
let available_space =
|
||||
|
@ -1031,7 +1056,7 @@ impl<'a> WindowContext<'a> {
|
|||
});
|
||||
});
|
||||
} else if let Some(active_tooltip) = self.app.active_tooltip.take() {
|
||||
self.stack(1, |cx| {
|
||||
self.with_z_index(1, |cx| {
|
||||
cx.with_element_offset(Some(active_tooltip.cursor_offset), |cx| {
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
|
@ -1054,12 +1079,34 @@ impl<'a> WindowContext<'a> {
|
|||
self.window.dirty = false;
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_stack(&mut self) -> Vec<KeyDispatchStackFrame> {
|
||||
let root_view = self.window.root_view.take().unwrap();
|
||||
let window = &mut *self.window;
|
||||
let mut spare_frame = Frame::default();
|
||||
mem::swap(&mut spare_frame, &mut window.previous_frame);
|
||||
|
||||
self.start_frame();
|
||||
|
||||
root_view.draw_dispatch_stack(self);
|
||||
|
||||
let window = &mut *self.window;
|
||||
// restore the old values of current and previous frame,
|
||||
// putting the new frame into spare_frame.
|
||||
mem::swap(&mut window.current_frame, &mut window.previous_frame);
|
||||
mem::swap(&mut spare_frame, &mut window.previous_frame);
|
||||
self.window.root_view = Some(root_view);
|
||||
|
||||
spare_frame.key_dispatch_stack
|
||||
}
|
||||
|
||||
/// Rotate the current frame and the previous frame, then clear the current frame.
|
||||
/// We repopulate all state in the current frame during each paint.
|
||||
fn start_frame(&mut self) {
|
||||
self.text_system().start_frame();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.layout_engine.clear();
|
||||
|
||||
mem::swap(&mut window.previous_frame, &mut window.current_frame);
|
||||
let frame = &mut window.current_frame;
|
||||
frame.element_states.clear();
|
||||
|
@ -1196,7 +1243,7 @@ impl<'a> WindowContext<'a> {
|
|||
DispatchPhase::Capture,
|
||||
self,
|
||||
) {
|
||||
self.dispatch_action(action, &key_dispatch_stack[..ix]);
|
||||
self.dispatch_action_internal(action, &key_dispatch_stack[..ix]);
|
||||
}
|
||||
if !self.app.propagate_event {
|
||||
break;
|
||||
|
@ -1223,7 +1270,10 @@ impl<'a> WindowContext<'a> {
|
|||
DispatchPhase::Bubble,
|
||||
self,
|
||||
) {
|
||||
self.dispatch_action(action, &key_dispatch_stack[..ix]);
|
||||
self.dispatch_action_internal(
|
||||
action,
|
||||
&key_dispatch_stack[..ix],
|
||||
);
|
||||
}
|
||||
|
||||
if !self.app.propagate_event {
|
||||
|
@ -1295,7 +1345,29 @@ impl<'a> WindowContext<'a> {
|
|||
self.window.platform_window.prompt(level, msg, answers)
|
||||
}
|
||||
|
||||
fn dispatch_action(
|
||||
pub fn available_actions(&self) -> impl Iterator<Item = Box<dyn Action>> + '_ {
|
||||
let key_dispatch_stack = &self.window.previous_frame.key_dispatch_stack;
|
||||
key_dispatch_stack.iter().filter_map(|frame| {
|
||||
match frame {
|
||||
// todo!factor out a KeyDispatchStackFrame::Action
|
||||
KeyDispatchStackFrame::Listener {
|
||||
event_type,
|
||||
listener: _,
|
||||
} => {
|
||||
match build_action_from_type(event_type) {
|
||||
Ok(action) => Some(action),
|
||||
Err(err) => {
|
||||
dbg!(err);
|
||||
None
|
||||
} // we'll hit his if TypeId == KeyDown
|
||||
}
|
||||
}
|
||||
KeyDispatchStackFrame::Context(_) => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_action_internal(
|
||||
&mut self,
|
||||
action: Box<dyn Action>,
|
||||
dispatch_stack: &[KeyDispatchStackFrame],
|
||||
|
@ -1706,7 +1778,7 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
|
|||
.unwrap_or_else(|| ContentMask {
|
||||
bounds: Bounds {
|
||||
origin: Point::default(),
|
||||
size: self.window().content_size,
|
||||
size: self.window().viewport_size,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue