This commit is contained in:
Nathan Sobo 2023-04-08 08:01:05 -06:00
parent 9d23a98157
commit 7536645eea
26 changed files with 496 additions and 519 deletions

View File

@ -1177,7 +1177,7 @@ mod tests {
}
fn editor_blocks(editor: &ViewHandle<Editor>, cx: &mut AppContext) -> Vec<(u32, String)> {
let mut presenter = cx.build_presenter(editor.id(), 0., Default::default());
let mut presenter = cx.build_window(editor.id(), 0., Default::default());
let mut cx = presenter.build_layout_context(Default::default(), false, cx);
cx.render(editor, |editor, cx| {
let snapshot = editor.snapshot(cx);

View File

@ -2531,7 +2531,7 @@ mod tests {
let layouts = editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
let mut presenter = cx.build_presenter(window_id, 30., Default::default());
let mut presenter = cx.build_window(window_id, 30., Default::default());
let layout_cx = presenter.build_layout_context(Vector2F::zero(), false, cx);
element
.layout_line_numbers(0..6, &Default::default(), false, &snapshot, &layout_cx)
@ -2568,7 +2568,7 @@ mod tests {
let mut element = EditorElement::new(editor.downgrade(), editor.read(cx).style(cx));
let mut scene = SceneBuilder::new(1.0);
let mut presenter = cx.build_presenter(window_id, 30., Default::default());
let mut presenter = cx.build_window(window_id, 30., Default::default());
let mut layout_cx = presenter.build_layout_context(Vector2F::zero(), false, cx);
let (size, mut state) = element.layout(
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),

View File

@ -4,6 +4,7 @@ mod menu;
pub(crate) mod ref_counts;
#[cfg(any(test, feature = "test-support"))]
pub mod test_app_context;
pub(crate) mod window;
mod window_input_handler;
use std::{
@ -23,7 +24,6 @@ use std::{
use anyhow::{anyhow, Context, Result};
use parking_lot::Mutex;
use pathfinder_geometry::vector::Vector2F;
use postage::oneshot;
use smallvec::SmallVec;
use smol::prelude::*;
@ -48,8 +48,8 @@ use crate::{
self, Appearance, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
PathPromptOptions, Platform, PromptLevel, WindowBounds, WindowOptions,
},
presenter::Presenter,
util::post_inc,
window::{Window, WindowContext},
AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId, TextLayoutCache,
};
@ -1622,25 +1622,10 @@ impl AppContext {
let platform_window =
this.platform
.open_window(window_id, window_options, this.foreground.clone());
let presenter = self.build_presenter(
window_id,
platform_window.titlebar_height(),
platform_window.appearance(),
);
this.register_platform_window(window_id, &mut presenter, platform_window.as_mut());
let window =
this.build_window(window_id, root_view.clone().into_any(), platform_window);
this.windows.insert(
window_id,
Window {
root_view: root_view.clone().into_any(),
focused_view_id: Some(root_view.id()),
is_active: false,
invalidation: None,
is_fullscreen: false,
platform_window,
presenter,
},
);
this.windows.insert(window_id, window);
root_view.update(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
(window_id, root_view)
@ -1658,27 +1643,11 @@ impl AppContext {
.build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
.unwrap();
let mut platform_window = this.platform.add_status_item();
let mut presenter = self.build_presenter(
window_id,
platform_window.titlebar_height(),
platform_window.appearance(),
);
this.register_platform_window(window_id, &mut presenter, platform_window.as_mut());
let platform_window = this.platform.add_status_item();
let window =
this.build_window(window_id, root_view.clone().into_any(), platform_window);
let focused_view_id = root_view.id();
this.windows.insert(
window_id,
Window {
root_view: root_view.clone().into_any(),
focused_view_id: Some(focused_view_id),
is_active: false,
invalidation: None,
is_fullscreen: false,
platform_window,
presenter,
},
);
this.windows.insert(window_id, window);
root_view.update(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
(window_id, root_view)
@ -1689,12 +1658,33 @@ impl AppContext {
self.remove_window(id);
}
fn register_platform_window(
pub fn replace_root_view<T, F>(&mut self, window_id: usize, build_root_view: F) -> ViewHandle<T>
where
T: View,
F: FnOnce(&mut ViewContext<T>) -> T,
{
self.update(|this| {
let root_view = this
.build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
.unwrap();
let window = this.windows.get_mut(&window_id).unwrap();
window.root_view = root_view.clone().into_any();
window.focused_view_id = Some(root_view.id());
root_view
})
}
pub fn remove_window(&mut self, window_id: usize) {
self.windows.remove(&window_id);
self.flush_effects();
}
pub fn build_window(
&mut self,
window_id: usize,
presenter: &mut Presenter,
platform_window: &mut dyn platform::Window,
) {
root_view: AnyViewHandle,
mut platform_window: Box<dyn platform::Window>,
) -> Window {
{
let mut app = self.upgrade();
@ -1758,51 +1748,19 @@ impl AppContext {
window_id,
}));
let scene = presenter.build_scene(
platform_window.content_size(),
platform_window.scale_factor(),
false,
self,
);
platform_window.present_scene(scene);
}
pub fn replace_root_view<T, F>(&mut self, window_id: usize, build_root_view: F) -> ViewHandle<T>
where
T: View,
F: FnOnce(&mut ViewContext<T>) -> T,
{
self.update(|this| {
let root_view = this
.build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
.unwrap();
let window = this.windows.get_mut(&window_id).unwrap();
window.root_view = root_view.clone().into_any();
window.focused_view_id = Some(root_view.id());
root_view
})
}
pub fn remove_window(&mut self, window_id: usize) {
self.windows.remove(&window_id);
self.flush_effects();
}
pub fn build_presenter(
&mut self,
window_id: usize,
titlebar_height: f32,
appearance: Appearance,
) -> Presenter {
Presenter::new(
let mut window = Window::new(
window_id,
titlebar_height,
appearance,
root_view,
platform_window,
self.font_cache.clone(),
TextLayoutCache::new(self.platform.fonts()),
self.assets.clone(),
self,
)
);
let scene = WindowContext::new(self, &mut window, window_id).build_scene(false);
window.platform_window.present_scene(scene);
window
}
pub fn add_view<T, F>(&mut self, parent_handle: &AnyViewHandle, build_view: F) -> ViewHandle<T>
@ -2144,21 +2102,16 @@ impl AppContext {
}
fn update_windows(&mut self) {
for (window_id, window) in &mut self.windows {
if let Some(mut invalidation) = window.invalidation.take() {
window.presenter.invalidate(
&mut invalidation,
window.platform_window.appearance(),
self,
);
let scene = window.presenter.build_scene(
window.platform_window.content_size(),
window.platform_window.scale_factor(),
false,
self,
);
window.platform_window.present_scene(scene);
}
let window_ids = self.windows.keys().cloned().collect::<Vec<_>>();
for window_id in window_ids {
self.update_window(window_id, |cx| {
if let Some(mut invalidation) = cx.window.invalidation.take() {
let appearance = cx.window.platform_window.appearance();
cx.invalidate(&mut invalidation, appearance);
let scene = cx.build_scene(false);
cx.window.platform_window.present_scene(scene);
}
});
}
}
@ -2223,20 +2176,14 @@ impl AppContext {
}
fn perform_window_refresh(&mut self) {
for window in self.windows.values_mut() {
let mut invalidation = window.invalidation.take().unwrap_or_default();
window.presenter.invalidate(
&mut invalidation,
window.platform_window.appearance(),
self,
);
let scene = window.presenter.build_scene(
window.platform_window.content_size(),
window.platform_window.scale_factor(),
true,
self,
);
window.platform_window.present_scene(scene);
let window_ids = self.windows.keys().cloned().collect::<Vec<_>>();
for window_id in window_ids {
self.update_window(window_id, |cx| {
let mut invalidation = cx.window.invalidation.take().unwrap_or_default();
cx.invalidate(&mut invalidation, cx.window.platform_window.appearance());
let scene = cx.build_scene(true);
cx.window.platform_window.present_scene(scene);
});
}
}
@ -2478,14 +2425,12 @@ impl AppContext {
self.update_window(window_id, |cx| {
if let Some(display) = cx.window_display_uuid() {
let bounds = cx.window_bounds();
cx.window_bounds_observations.clone().emit(
window_id,
self,
move |callback, this| {
cx.window_bounds_observations
.clone()
.emit(window_id, cx, move |callback, this| {
callback(bounds, display, this);
true
},
);
});
}
});
}
@ -2708,16 +2653,6 @@ pub enum ParentId {
Root,
}
pub struct Window {
root_view: AnyViewHandle,
focused_view_id: Option<usize>,
is_active: bool,
is_fullscreen: bool,
invalidation: Option<WindowInvalidation>,
presenter: Presenter,
platform_window: Box<dyn platform::Window>,
}
#[derive(Default, Clone)]
pub struct WindowInvalidation {
pub updated: HashSet<usize>,
@ -3494,138 +3429,6 @@ impl<M> DerefMut for ModelContext<'_, M> {
}
}
pub struct WindowContext<'a: 'b, 'b> {
app_context: &'a mut AppContext,
window: &'b mut Window,
window_id: usize,
}
impl Deref for WindowContext<'_, '_> {
type Target = AppContext;
fn deref(&self) -> &Self::Target {
self.app_context
}
}
impl DerefMut for WindowContext<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.app_context
}
}
impl WindowContext<'_, '_> {
pub fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
let window_id = self.window_id;
if let Some(focused_view_id) = self.focused_view_id(window_id) {
let dispatch_path = self
.ancestors(window_id, focused_view_id)
.filter_map(|view_id| {
self.views
.get(&(window_id, view_id))
.map(|view| (view_id, view.keymap_context(self)))
})
.collect();
let match_result = self
.keystroke_matcher
.push_keystroke(keystroke.clone(), dispatch_path);
let mut handled_by = None;
let keystroke_handled = match &match_result {
MatchResult::None => false,
MatchResult::Pending => true,
MatchResult::Matches(matches) => {
for (view_id, action) in matches {
if self.handle_dispatch_action_from_effect(
window_id,
Some(*view_id),
action.as_ref(),
) {
self.keystroke_matcher.clear_pending();
handled_by = Some(action.boxed_clone());
break;
}
}
handled_by.is_some()
}
};
self.keystroke(
window_id,
keystroke.clone(),
handled_by,
match_result.clone(),
);
keystroke_handled
} else {
self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
false
}
}
pub fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
self.window
.presenter
.dispatch_event(event, event_reused, self)
}
pub fn set_window_title(&mut self, title: &str) {
self.window.platform_window.set_title(title);
}
pub fn set_window_edited(&mut self, edited: bool) {
self.window.platform_window.set_edited(edited);
}
pub fn is_topmost_window_for_position(&self, position: Vector2F) -> bool {
self.window
.platform_window
.is_topmost_for_position(position)
}
pub fn activate_window(&self) {
self.window.platform_window.activate();
}
pub fn window_bounds(&self) -> WindowBounds {
self.window.platform_window.bounds()
}
pub fn window_display_uuid(&self) -> Option<Uuid> {
self.window.platform_window.screen().display_uuid()
}
pub fn debug_elements(&self) -> Option<crate::json::Value> {
self.window.presenter.debug_elements(self)
}
fn show_character_palette(&self) {
self.window.platform_window.show_character_palette();
}
pub fn minimize_window(&self) {
self.window.platform_window.minimize();
}
pub fn zoom_window(&self) {
self.window.platform_window.zoom();
}
pub fn toggle_window_full_screen(&self) {
self.window.platform_window.toggle_full_screen();
}
pub fn prompt(
&self,
level: PromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize> {
self.window.platform_window.prompt(level, msg, answers)
}
}
pub struct ViewContext<'a, T: ?Sized> {
app: &'a mut AppContext,
window_id: usize,
@ -5139,6 +4942,7 @@ mod tests {
elements::*,
impl_actions,
platform::{MouseButton, MouseButtonEvent},
window::ChildView,
};
use itertools::Itertools;
use postage::{sink::Sink, stream::Stream};
@ -6847,7 +6651,7 @@ mod tests {
let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0));
cx.update_window(window_id, |cx| {
assert_eq!(
cx.window.presenter.rendered_views[&root_view.id()].name(),
cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 0")
);
});
@ -6859,11 +6663,11 @@ mod tests {
cx.update_window(window_id, |cx| {
assert_eq!(
cx.window.presenter.rendered_views[&root_view.id()].name(),
cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 1")
);
assert_eq!(
cx.window.presenter.rendered_views[&view.id()].name(),
cx.window.rendered_views[&view.id()].name(),
Some("render count: 0")
);
});
@ -6872,11 +6676,11 @@ mod tests {
cx.update_window(window_id, |cx| {
assert_eq!(
cx.window.presenter.rendered_views[&root_view.id()].name(),
cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 2")
);
assert_eq!(
cx.window.presenter.rendered_views[&view.id()].name(),
cx.window.rendered_views[&view.id()].name(),
Some("render count: 1")
);
});
@ -6888,10 +6692,10 @@ mod tests {
cx.update_window(window_id, |cx| {
assert_eq!(
cx.window.presenter.rendered_views[&root_view.id()].name(),
cx.window.rendered_views[&root_view.id()].name(),
Some("render count: 3")
);
assert_eq!(cx.window.presenter.rendered_views.len(), 1);
assert_eq!(cx.window.rendered_views.len(), 1);
});
}

View File

@ -92,13 +92,12 @@ impl TestAppContext {
return true;
}
if cx.window.presenter.dispatch_event(
if cx.dispatch_event(
Event::KeyDown(KeyDownEvent {
keystroke: keystroke.clone(),
is_held,
}),
false,
cx,
) {
return true;
}
@ -286,12 +285,15 @@ impl TestAppContext {
pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
self.cx.borrow_mut().update(|cx| {
for window_id in cx
let other_window_ids = cx
.windows
.keys()
.filter(|window_id| Some(**window_id) != to_activate)
{
cx.window_changed_active_status(*window_id, false)
.copied()
.collect::<Vec<_>>();
for window_id in other_window_ids {
cx.window_changed_active_status(window_id, false)
}
if let Some(to_activate) = to_activate {

View File

@ -4,7 +4,11 @@ use crate::{
font_cache::FontCache,
geometry::rect::RectF,
json::{self, ToJson},
platform::{Appearance, CursorStyle, Event, FontSystem, MouseButton, MouseMovedEvent},
keymap_matcher::{Keystroke, MatchResult},
platform::{
self, Appearance, CursorStyle, Event, FontSystem, MouseButton, MouseMovedEvent,
PromptLevel, WindowBounds,
},
scene::{
CursorRegion, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover,
MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene,
@ -18,6 +22,7 @@ use crate::{
use anyhow::bail;
use collections::{HashMap, HashSet};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use postage::oneshot;
use serde_json::json;
use smallvec::SmallVec;
use sqlez::{
@ -29,9 +34,16 @@ use std::{
ops::{Deref, DerefMut, Range},
sync::Arc,
};
use uuid::Uuid;
pub struct Presenter {
pub struct Window {
window_id: usize,
pub(crate) root_view: AnyViewHandle,
pub(crate) focused_view_id: Option<usize>,
pub(crate) is_active: bool,
pub(crate) is_fullscreen: bool,
pub(crate) invalidation: Option<WindowInvalidation>,
pub(crate) platform_window: Box<dyn platform::Window>,
pub(crate) rendered_views: HashMap<usize, ElementBox>,
cursor_regions: Vec<CursorRegion>,
mouse_regions: Vec<(MouseRegion, usize)>,
@ -47,18 +59,27 @@ pub struct Presenter {
appearance: Appearance,
}
impl Presenter {
impl Window {
pub fn new(
window_id: usize,
titlebar_height: f32,
appearance: Appearance,
root_view: AnyViewHandle,
platform_window: Box<dyn platform::Window>,
font_cache: Arc<FontCache>,
text_layout_cache: TextLayoutCache,
asset_cache: Arc<AssetCache>,
cx: &mut AppContext,
) -> Self {
let focused_view_id = Some(root_view.id());
let titlebar_height = platform_window.titlebar_height();
let appearance = platform_window.appearance();
Self {
window_id,
root_view,
focused_view_id,
is_active: false,
invalidation: None,
is_fullscreen: false,
platform_window,
rendered_views: cx.render_views(window_id, titlebar_height, appearance),
cursor_regions: Default::default(),
mouse_regions: Default::default(),
@ -74,180 +95,101 @@ impl Presenter {
appearance,
}
}
}
pub fn invalidate(
&mut self,
invalidation: &mut WindowInvalidation,
appearance: Appearance,
cx: &mut AppContext,
) {
cx.start_frame();
self.appearance = appearance;
for view_id in &invalidation.removed {
invalidation.updated.remove(view_id);
self.rendered_views.remove(view_id);
pub struct WindowContext<'a: 'b, 'b> {
app_context: &'a mut AppContext,
pub(crate) window: &'b mut Window, // TODO: make this private?
window_id: usize,
}
impl Deref for WindowContext<'_, '_> {
type Target = AppContext;
fn deref(&self) -> &Self::Target {
self.app_context
}
}
impl DerefMut for WindowContext<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.app_context
}
}
impl<'a: 'b, 'b> WindowContext<'a, 'b> {
pub fn new(app_context: &'a mut AppContext, window: &'b mut Window, window_id: usize) -> Self {
Self {
app_context,
window,
window_id,
}
for view_id in &invalidation.updated {
self.rendered_views.insert(
*view_id,
cx.render_view(RenderParams {
window_id: self.window_id,
view_id: *view_id,
titlebar_height: self.titlebar_height,
hovered_region_ids: self.hovered_region_ids.clone(),
clicked_region_ids: self
.clicked_button
.map(|button| (self.clicked_region_ids.clone(), button)),
refreshing: false,
appearance,
}
pub fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
let window_id = self.window_id;
if let Some(focused_view_id) = self.focused_view_id(window_id) {
let dispatch_path = self
.ancestors(window_id, focused_view_id)
.filter_map(|view_id| {
self.views
.get(&(window_id, view_id))
.map(|view| (view_id, view.keymap_context(self)))
})
.unwrap(),
);
}
}
.collect();
pub fn refresh(
&mut self,
invalidation: &mut WindowInvalidation,
appearance: Appearance,
cx: &mut AppContext,
) {
self.invalidate(invalidation, appearance, cx);
for (view_id, view) in &mut self.rendered_views {
if !invalidation.updated.contains(view_id) {
*view = cx
.render_view(RenderParams {
window_id: self.window_id,
view_id: *view_id,
titlebar_height: self.titlebar_height,
hovered_region_ids: self.hovered_region_ids.clone(),
clicked_region_ids: self
.clicked_button
.map(|button| (self.clicked_region_ids.clone(), button)),
refreshing: true,
appearance,
})
.unwrap();
}
}
}
let match_result = self
.keystroke_matcher
.push_keystroke(keystroke.clone(), dispatch_path);
let mut handled_by = None;
pub fn build_scene(
&mut self,
window_size: Vector2F,
scale_factor: f32,
refreshing: bool,
cx: &mut AppContext,
) -> Scene {
let mut scene_builder = SceneBuilder::new(scale_factor);
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
self.layout(window_size, refreshing, cx);
let mut paint_cx = self.build_paint_context(&mut scene_builder, window_size, cx);
paint_cx.paint(
root_view_id,
Vector2F::zero(),
RectF::new(Vector2F::zero(), window_size),
);
self.text_layout_cache.finish_frame();
let scene = scene_builder.build();
self.cursor_regions = scene.cursor_regions();
self.mouse_regions = scene.mouse_regions();
// window.is_topmost for the mouse moved event's postion?
if cx.window_is_active(self.window_id) {
if let Some(event) = self.last_mouse_moved_event.clone() {
self.dispatch_event(event, true, cx);
let keystroke_handled = match &match_result {
MatchResult::None => false,
MatchResult::Pending => true,
MatchResult::Matches(matches) => {
for (view_id, action) in matches {
if self.handle_dispatch_action_from_effect(
window_id,
Some(*view_id),
action.as_ref(),
) {
self.keystroke_matcher.clear_pending();
handled_by = Some(action.boxed_clone());
break;
}
}
handled_by.is_some()
}
}
scene
} else {
log::error!("could not find root_view_id for window {}", self.window_id);
scene_builder.build()
}
}
fn layout(&mut self, window_size: Vector2F, refreshing: bool, cx: &mut AppContext) {
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
self.build_layout_context(window_size, refreshing, cx)
.layout(root_view_id, SizeConstraint::strict(window_size));
}
}
pub fn build_layout_context<'a>(
&'a mut self,
window_size: Vector2F,
refreshing: bool,
cx: &'a mut AppContext,
) -> LayoutContext<'a> {
LayoutContext {
window_id: self.window_id,
rendered_views: &mut self.rendered_views,
font_cache: &self.font_cache,
font_system: cx.platform().fonts(),
text_layout_cache: &self.text_layout_cache,
asset_cache: &self.asset_cache,
view_stack: Vec::new(),
refreshing,
hovered_region_ids: self.hovered_region_ids.clone(),
clicked_region_ids: self
.clicked_button
.map(|button| (self.clicked_region_ids.clone(), button)),
titlebar_height: self.titlebar_height,
appearance: self.appearance,
window_size,
app: cx,
}
}
pub fn build_paint_context<'a>(
&'a mut self,
scene: &'a mut SceneBuilder,
window_size: Vector2F,
cx: &'a mut AppContext,
) -> PaintContext {
PaintContext {
scene,
window_size,
font_cache: &self.font_cache,
text_layout_cache: &self.text_layout_cache,
rendered_views: &mut self.rendered_views,
view_stack: Vec::new(),
app: cx,
}
}
pub fn rect_for_text_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<RectF> {
cx.focused_view_id(self.window_id).and_then(|view_id| {
let cx = MeasurementContext {
app: cx,
rendered_views: &self.rendered_views,
window_id: self.window_id,
};
cx.rect_for_text_range(view_id, range_utf16)
})
self.keystroke(
window_id,
keystroke.clone(),
handled_by,
match_result.clone(),
);
keystroke_handled
} else {
self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
false
}
}
pub fn dispatch_event(
&mut self,
event: Event,
event_reused: bool,
cx: &mut AppContext,
) -> bool {
pub fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
let mut mouse_events = SmallVec::<[_; 2]>::new();
let mut notified_views: HashSet<usize> = Default::default();
let window_id = self.window_id;
// 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events
// get mapped into the mouse-specific MouseEvent type.
// -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?]
// -> Also updates mouse-related state
match &event {
Event::KeyDown(e) => return cx.dispatch_key_down(self.window_id, e),
Event::KeyDown(e) => return self.dispatch_key_down(window_id, e),
Event::KeyUp(e) => return cx.dispatch_key_up(self.window_id, e),
Event::KeyUp(e) => return self.dispatch_key_up(window_id, e),
Event::ModifiersChanged(e) => return cx.dispatch_modifiers_changed(self.window_id, e),
Event::ModifiersChanged(e) => return self.dispatch_modifiers_changed(window_id, e),
Event::MouseDown(e) => {
// Click events are weird because they can be fired after a drag event.
@ -256,8 +198,9 @@ impl Presenter {
// So we need to store the overlapping regions on mouse down.
// If there is already clicked_button stored, don't replace it.
if self.clicked_button.is_none() {
self.clicked_region_ids = self
if self.window.clicked_button.is_none() {
self.window.clicked_region_ids = self
.window
.mouse_regions
.iter()
.filter_map(|(region, _)| {
@ -269,7 +212,7 @@ impl Presenter {
})
.collect();
self.clicked_button = Some(e.button);
self.window.clicked_button = Some(e.button);
}
mouse_events.push(MouseEvent::Down(MouseDown {
@ -308,26 +251,29 @@ impl Presenter {
},
) => {
let mut style_to_assign = CursorStyle::Arrow;
for region in self.cursor_regions.iter().rev() {
for region in self.window.cursor_regions.iter().rev() {
if region.bounds.contains_point(*position) {
style_to_assign = region.style;
break;
}
}
// TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// if cx.is_topmost_window_for_position(self.window_id, *position) {
// cx.platform().set_cursor_style(style_to_assign);
// }
if self
.window
.platform_window
.is_topmost_for_position(*position)
{
self.platform().set_cursor_style(style_to_assign);
}
if !event_reused {
if pressed_button.is_some() {
mouse_events.push(MouseEvent::Drag(MouseDrag {
region: Default::default(),
prev_mouse_position: self.mouse_position,
prev_mouse_position: self.window.mouse_position,
platform_event: e.clone(),
}));
} else if let Some(clicked_button) = self.clicked_button {
} else if let Some(clicked_button) = self.window.clicked_button {
// Mouse up event happened outside the current window. Simulate mouse up button event
let button_event = e.to_button_event(clicked_button);
mouse_events.push(MouseEvent::Up(MouseUp {
@ -359,7 +305,7 @@ impl Presenter {
region: Default::default(),
}));
self.last_mouse_moved_event = Some(event.clone());
self.window.last_mouse_moved_event = Some(event.clone());
}
Event::MouseExited(event) => {
@ -373,7 +319,6 @@ impl Presenter {
modifiers: event.modifiers,
}),
event_reused,
cx,
);
}
@ -384,7 +329,7 @@ impl Presenter {
}
if let Some(position) = event.position() {
self.mouse_position = position;
self.window.mouse_position = position;
}
// 2. Dispatch mouse events on regions
@ -398,8 +343,8 @@ impl Presenter {
match &mouse_event {
MouseEvent::Hover(_) => {
let mut highest_z_index = None;
let mouse_position = self.mouse_position.clone();
for (region, z_index) in self.mouse_regions.iter().rev() {
let mouse_position = self.window.mouse_position.clone();
for (region, z_index) in self.window.mouse_regions.iter().rev() {
// Allow mouse regions to appear transparent to hovers
if !region.hoverable {
continue;
@ -417,7 +362,7 @@ impl Presenter {
// highest_z_index is set.
if contains_mouse && z_index == highest_z_index.unwrap() {
//Ensure that hover entrance events aren't sent twice
if self.hovered_region_ids.insert(region.id()) {
if self.window.hovered_region_ids.insert(region.id()) {
valid_regions.push(region.clone());
if region.notify_on_hover {
notified_views.insert(region.id().view_id());
@ -425,7 +370,7 @@ impl Presenter {
}
} else {
// Ensure that hover exit events aren't sent twice
if self.hovered_region_ids.remove(&region.id()) {
if self.window.hovered_region_ids.remove(&region.id()) {
valid_regions.push(region.clone());
if region.notify_on_hover {
notified_views.insert(region.id().view_id());
@ -436,8 +381,8 @@ impl Presenter {
}
MouseEvent::Down(_) | MouseEvent::Up(_) => {
for (region, _) in self.mouse_regions.iter().rev() {
if region.bounds.contains_point(self.mouse_position) {
for (region, _) in self.window.mouse_regions.iter().rev() {
if region.bounds.contains_point(self.window.mouse_position) {
valid_regions.push(region.clone());
if region.notify_on_click {
notified_views.insert(region.id().view_id());
@ -449,19 +394,25 @@ impl Presenter {
MouseEvent::Click(e) => {
// Only raise click events if the released button is the same as the one stored
if self
.window
.clicked_button
.map(|clicked_button| clicked_button == e.button)
.unwrap_or(false)
{
// Clear clicked regions and clicked button
let clicked_region_ids =
std::mem::replace(&mut self.clicked_region_ids, Default::default());
self.clicked_button = None;
let clicked_region_ids = std::mem::replace(
&mut self.window.clicked_region_ids,
Default::default(),
);
self.window.clicked_button = None;
// Find regions which still overlap with the mouse since the last MouseDown happened
for (mouse_region, _) in self.mouse_regions.iter().rev() {
for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
if clicked_region_ids.contains(&mouse_region.id()) {
if mouse_region.bounds.contains_point(self.mouse_position) {
if mouse_region
.bounds
.contains_point(self.window.mouse_position)
{
valid_regions.push(mouse_region.clone());
}
}
@ -470,26 +421,32 @@ impl Presenter {
}
MouseEvent::Drag(_) => {
for (mouse_region, _) in self.mouse_regions.iter().rev() {
if self.clicked_region_ids.contains(&mouse_region.id()) {
for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
if self.window.clicked_region_ids.contains(&mouse_region.id()) {
valid_regions.push(mouse_region.clone());
}
}
}
MouseEvent::MoveOut(_) | MouseEvent::UpOut(_) | MouseEvent::DownOut(_) => {
for (mouse_region, _) in self.mouse_regions.iter().rev() {
for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
// NOT contains
if !mouse_region.bounds.contains_point(self.mouse_position) {
if !mouse_region
.bounds
.contains_point(self.window.mouse_position)
{
valid_regions.push(mouse_region.clone());
}
}
}
_ => {
for (mouse_region, _) in self.mouse_regions.iter().rev() {
for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
// Contains
if mouse_region.bounds.contains_point(self.mouse_position) {
if mouse_region
.bounds
.contains_point(self.window.mouse_position)
{
valid_regions.push(mouse_region.clone());
}
}
@ -497,9 +454,9 @@ impl Presenter {
}
//3. Fire region events
let hovered_region_ids = self.hovered_region_ids.clone();
let hovered_region_ids = self.window.hovered_region_ids.clone();
for valid_region in valid_regions.into_iter() {
let mut event_cx = self.build_event_context(&mut notified_views, cx);
let mut event_cx = self.build_event_context(&mut notified_views);
mouse_event.set_region(valid_region.bounds);
if let MouseEvent::Hover(e) = &mut mouse_event {
@ -548,43 +505,253 @@ impl Presenter {
}
for view_id in notified_views {
cx.notify_view(self.window_id, view_id);
self.notify_view(window_id, view_id);
}
any_event_handled
}
pub fn build_event_context<'a>(
&'a mut self,
notified_views: &'a mut HashSet<usize>,
cx: &'a mut AppContext,
) -> EventContext<'a> {
pub fn build_event_context<'c>(
&'c mut self,
notified_views: &'c mut HashSet<usize>,
) -> EventContext<'c> {
EventContext {
font_cache: &self.font_cache,
text_layout_cache: &self.text_layout_cache,
font_cache: &self.window.font_cache,
text_layout_cache: &self.window.text_layout_cache,
view_stack: Default::default(),
notified_views,
notify_count: 0,
handled: false,
window_id: self.window_id,
app: self,
}
}
pub fn invalidate(&mut self, invalidation: &mut WindowInvalidation, appearance: Appearance) {
self.start_frame();
self.window.appearance = appearance;
for view_id in &invalidation.removed {
invalidation.updated.remove(view_id);
self.window.rendered_views.remove(view_id);
}
for view_id in &invalidation.updated {
let window_id = self.window_id;
let titlebar_height = self.window.titlebar_height;
let hovered_region_ids = self.window.hovered_region_ids.clone();
let clicked_region_ids = self
.window
.clicked_button
.map(|button| (self.window.clicked_region_ids.clone(), button));
let element = self
.render_view(RenderParams {
window_id,
view_id: *view_id,
titlebar_height,
hovered_region_ids,
clicked_region_ids,
refreshing: false,
appearance,
})
.unwrap();
self.window.rendered_views.insert(*view_id, element);
}
}
pub fn refresh(&mut self, invalidation: &mut WindowInvalidation, appearance: Appearance) {
self.invalidate(invalidation, appearance);
let view_ids = self
.window
.rendered_views
.keys()
.copied()
.collect::<Vec<_>>();
for view_id in view_ids {
if !invalidation.updated.contains(&view_id) {
let window_id = self.window_id;
let titlebar_height = self.window.titlebar_height;
let hovered_region_ids = self.window.hovered_region_ids.clone();
let clicked_region_ids = self
.window
.clicked_button
.map(|button| (self.window.clicked_region_ids.clone(), button));
let element = self
.render_view(RenderParams {
window_id,
view_id,
titlebar_height,
hovered_region_ids,
clicked_region_ids,
refreshing: true,
appearance,
})
.unwrap();
self.window.rendered_views.insert(view_id, element);
}
}
}
pub fn build_scene(&mut self, refreshing: bool) -> Scene {
let window_size = self.window.platform_window.content_size();
let scale_factor = self.window.platform_window.scale_factor();
let mut scene_builder = SceneBuilder::new(scale_factor);
if let Some(root_view_id) = self.root_view_id(self.window_id) {
self.layout(window_size, refreshing, self);
let mut paint_cx = self.build_paint_context(&mut scene_builder, window_size);
paint_cx.paint(
root_view_id,
Vector2F::zero(),
RectF::new(Vector2F::zero(), window_size),
);
self.window.text_layout_cache.finish_frame();
let scene = scene_builder.build();
self.window.cursor_regions = scene.cursor_regions();
self.window.mouse_regions = scene.mouse_regions();
// window.is_topmost for the mouse moved event's postion?
if self.window_is_active(self.window_id) {
if let Some(event) = self.window.last_mouse_moved_event.clone() {
self.dispatch_event(event, true);
}
}
scene
} else {
log::error!("could not find root_view_id for window {}", self.window_id);
scene_builder.build()
}
}
fn layout(&mut self, window_size: Vector2F, refreshing: bool, cx: &mut AppContext) {
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
self.build_layout_context(window_size, refreshing, cx)
.layout(root_view_id, SizeConstraint::strict(window_size));
}
}
pub fn build_layout_context<'c>(
&'c mut self,
window_size: Vector2F,
refreshing: bool,
cx: &'c mut AppContext,
) -> LayoutContext<'c> {
LayoutContext {
window_id: self.window_id,
rendered_views: &mut self.window.rendered_views,
font_cache: &self.font_cache,
font_system: cx.platform().fonts(),
text_layout_cache: &self.window.text_layout_cache,
asset_cache: &self.window.asset_cache,
view_stack: Vec::new(),
refreshing,
hovered_region_ids: self.window.hovered_region_ids.clone(),
clicked_region_ids: self
.window
.clicked_button
.map(|button| (self.window.clicked_region_ids.clone(), button)),
titlebar_height: self.window.titlebar_height,
appearance: self.window.appearance,
window_size,
app: cx,
}
}
pub fn debug_elements(&self, cx: &AppContext) -> Option<json::Value> {
let view = cx.root_view(self.window_id)?;
pub fn build_paint_context<'c>(
&'c mut self,
scene: &'c mut SceneBuilder,
window_size: Vector2F,
) -> PaintContext {
PaintContext {
scene,
window_size,
font_cache: &self.font_cache,
text_layout_cache: &self.window.text_layout_cache,
rendered_views: &mut self.window.rendered_views,
view_stack: Vec::new(),
app: self,
}
}
pub fn rect_for_text_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
self.focused_view_id(self.window_id).and_then(|view_id| {
let cx = MeasurementContext {
app: self,
rendered_views: &self.window.rendered_views,
window_id: self.window_id,
};
cx.rect_for_text_range(view_id, range_utf16)
})
}
pub fn debug_elements(&self) -> Option<json::Value> {
let view = self.root_view(self.window_id)?;
Some(json!({
"root_view": view.debug_json(cx),
"root_element": self.rendered_views.get(&view.id())
"root_view": view.debug_json(self),
"root_element": self.window.rendered_views.get(&view.id())
.map(|root_element| {
root_element.debug(&DebugContext {
rendered_views: &self.rendered_views,
font_cache: &self.font_cache,
app: cx,
rendered_views: &self.window.rendered_views,
font_cache: &self.window.font_cache,
app: self,
})
})
}))
}
pub fn set_window_title(&mut self, title: &str) {
self.window.platform_window.set_title(title);
}
pub fn set_window_edited(&mut self, edited: bool) {
self.window.platform_window.set_edited(edited);
}
pub fn is_topmost_window_for_position(&self, position: Vector2F) -> bool {
self.window
.platform_window
.is_topmost_for_position(position)
}
pub fn activate_window(&self) {
self.window.platform_window.activate();
}
pub fn window_bounds(&self) -> WindowBounds {
self.window.platform_window.bounds()
}
pub fn window_display_uuid(&self) -> Option<Uuid> {
self.window.platform_window.screen().display_uuid()
}
fn show_character_palette(&self) {
self.window.platform_window.show_character_palette();
}
pub fn minimize_window(&self) {
self.window.platform_window.minimize();
}
pub fn zoom_window(&self) {
self.window.platform_window.zoom();
}
pub fn toggle_window_full_screen(&self) {
self.window.platform_window.toggle_full_screen();
}
pub fn prompt(
&self,
level: PromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize> {
self.window.platform_window.prompt(level, msg, answers)
}
}
pub struct LayoutContext<'a> {

View File

@ -90,8 +90,9 @@ impl InputHandler for WindowInputHandler {
}
fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
let app = self.app.borrow();
let window = app.windows.get(&self.window_id)?;
window.presenter.rect_for_text_range(range_utf16, &app)
self.app
.borrow_mut()
.update_window(self.window_id, |cx| cx.rect_for_text_range(range_utf16))
.flatten()
}
}

View File

@ -26,16 +26,14 @@ pub use self::{
stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
};
use self::{clipped::Clipped, expanded::Expanded};
pub use crate::presenter::ChildView;
use crate::{
app::window::MeasurementContext,
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
json,
presenter::MeasurementContext,
Action, DebugContext, EventContext, LayoutContext, PaintContext, RenderContext, SizeConstraint,
View,
json, Action, DebugContext, EventContext, LayoutContext, PaintContext, RenderContext,
SizeConstraint, View,
};
use core::panic;
use json::ToJson;

View File

@ -1,7 +1,7 @@
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json,
presenter::MeasurementContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
};
use json::ToJson;

View File

@ -1,7 +1,7 @@
use super::Element;
use crate::{
json::{self, json},
presenter::MeasurementContext,
window::MeasurementContext,
DebugContext, PaintContext,
};
use json::ToJson;

View File

@ -6,7 +6,7 @@ use serde_json::json;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json,
presenter::MeasurementContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
};

View File

@ -9,8 +9,8 @@ use crate::{
},
json::ToJson,
platform::CursorStyle,
presenter::MeasurementContext,
scene::{self, Border, CursorRegion, Quad},
window::MeasurementContext,
Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
};
use serde::Deserialize;

View File

@ -6,7 +6,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{json, ToJson},
presenter::MeasurementContext,
window::MeasurementContext,
DebugContext,
};
use crate::{Element, LayoutContext, PaintContext, SizeConstraint};

View File

@ -3,7 +3,7 @@ use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json,
presenter::MeasurementContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
};
use serde_json::json;

View File

@ -2,7 +2,7 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
use crate::{
json::{self, ToJson, Value},
presenter::MeasurementContext,
window::MeasurementContext,
Axis, DebugContext, Element, ElementBox, ElementStateHandle, LayoutContext, PaintContext,
RenderContext, SizeConstraint, Vector2FExt, View,
};

View File

@ -3,7 +3,7 @@ use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::json,
presenter::MeasurementContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
};

View File

@ -5,8 +5,9 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{json, ToJson},
presenter::MeasurementContext,
scene, Border, DebugContext, Element, ImageData, LayoutContext, PaintContext, SizeConstraint,
scene,
window::MeasurementContext,
Border, DebugContext, Element, ImageData, LayoutContext, PaintContext, SizeConstraint,
};
use serde::Deserialize;
use std::{ops::Range, sync::Arc};

View File

@ -7,8 +7,8 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{ToJson, Value},
presenter::MeasurementContext,
text_layout::{Line, RunStyle},
window::MeasurementContext,
DebugContext, Element, LayoutContext, PaintContext, SizeConstraint,
};
use serde::Deserialize;

View File

@ -4,7 +4,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::json,
presenter::MeasurementContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, ElementRc, EventContext, LayoutContext, MouseRegion,
PaintContext, RenderContext, SizeConstraint, View, ViewContext,
};
@ -631,7 +631,6 @@ mod tests {
#[crate::test(self)]
fn test_layout(cx: &mut crate::AppContext) {
let mut presenter = cx.build_presenter(0, 0., Default::default());
let (_, view) = cx.add_window(Default::default(), |_| TestView);
let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.));
@ -730,8 +729,7 @@ mod tests {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let (_, view) = cx.add_window(Default::default(), |_| TestView);
let mut presenter = cx.build_presenter(0, 0., Default::default());
let (window_id, view) = cx.add_window(Default::default(), |_| TestView);
let mut next_id = 0;
let elements = Rc::new(RefCell::new(
(0..rng.gen_range(0..=20))

View File

@ -3,7 +3,7 @@ use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::ToJson,
presenter::MeasurementContext,
window::MeasurementContext,
Axis, DebugContext, Element, ElementBox, LayoutContext, MouseRegion, PaintContext,
SizeConstraint,
};

View File

@ -3,7 +3,7 @@ use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::{self, json, ToJson},
presenter::MeasurementContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
};

View File

@ -8,8 +8,9 @@ use crate::{
rect::RectF,
vector::{vec2f, Vector2F},
},
presenter::MeasurementContext,
scene, DebugContext, Element, LayoutContext, PaintContext, SizeConstraint,
scene,
window::MeasurementContext,
DebugContext, Element, LayoutContext, PaintContext, SizeConstraint,
};
pub struct Svg {

View File

@ -6,8 +6,8 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{ToJson, Value},
presenter::MeasurementContext,
text_layout::{Line, RunStyle, ShapedBoundary},
window::MeasurementContext,
DebugContext, Element, FontCache, LayoutContext, PaintContext, SizeConstraint, TextLayoutCache,
};
use log::warn;
@ -271,12 +271,18 @@ pub fn layout_highlighted_chunks<'a>(
#[cfg(test)]
mod tests {
use super::*;
use crate::{elements::Empty, fonts, AppContext, ElementBox, Entity, RenderContext, View};
use crate::{
elements::Empty, fonts, platform, AppContext, ElementBox, Entity, RenderContext, View,
};
#[crate::test(self)]
fn test_soft_wrapping_with_carriage_returns(cx: &mut AppContext) {
let (window_id, _) = cx.add_window(Default::default(), |_| TestView);
let mut presenter = cx.build_presenter(window_id, Default::default(), Default::default());
let (window_id, root_view) = cx.add_window(Default::default(), |_| TestView);
let mut presenter = cx.build_window(
window_id,
root_view.into_any(),
Box::new(platform::test::Window::new(Vector2F::new(800., 600.))),
);
fonts::with_font_cache(cx.font_cache().clone(), || {
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
let (_, state) = text.layout(

View File

@ -6,7 +6,7 @@ use crate::{
fonts::TextStyle,
geometry::{rect::RectF, vector::Vector2F},
json::json,
presenter::MeasurementContext,
window::MeasurementContext,
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, RenderContext, SizeConstraint,
Task, View,
};

View File

@ -6,8 +6,8 @@ use crate::{
},
json::{self, json},
platform::ScrollWheelEvent,
presenter::MeasurementContext,
scene::MouseScrollWheel,
window::MeasurementContext,
ElementBox, MouseRegion, RenderContext, View,
};
use json::ToJson;

View File

@ -14,7 +14,6 @@ mod clipboard;
pub use clipboard::ClipboardItem;
pub mod fonts;
pub mod geometry;
mod presenter;
pub mod scene;
pub use scene::{Border, CursorRegion, MouseRegion, MouseRegionId, Quad, Scene, SceneBuilder};
pub mod text_layout;
@ -28,7 +27,7 @@ pub mod json;
pub mod keymap_matcher;
pub mod platform;
pub use gpui_macros::test;
pub use presenter::{
pub use window::{
Axis, DebugContext, EventContext, LayoutContext, MeasurementContext, PaintContext,
SizeConstraint, Vector2FExt,
};

View File

@ -261,7 +261,7 @@ pub struct Window {
}
impl Window {
fn new(size: Vector2F) -> Self {
pub fn new(size: Vector2F) -> Self {
Self {
size,
event_handlers: Default::default(),