mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
WIP
This commit is contained in:
parent
9d23a98157
commit
7536645eea
@ -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);
|
||||
|
@ -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.)),
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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(®ion.id()) {
|
||||
if self.window.hovered_region_ids.remove(®ion.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> {
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::Element;
|
||||
use crate::{
|
||||
json::{self, json},
|
||||
presenter::MeasurementContext,
|
||||
window::MeasurementContext,
|
||||
DebugContext, PaintContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
presenter::MeasurementContext,
|
||||
window::MeasurementContext,
|
||||
DebugContext,
|
||||
};
|
||||
use crate::{Element, LayoutContext, PaintContext, SizeConstraint};
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -6,8 +6,8 @@ use crate::{
|
||||
},
|
||||
json::{self, json},
|
||||
platform::ScrollWheelEvent,
|
||||
presenter::MeasurementContext,
|
||||
scene::MouseScrollWheel,
|
||||
window::MeasurementContext,
|
||||
ElementBox, MouseRegion, RenderContext, View,
|
||||
};
|
||||
use json::ToJson;
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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(),
|
||||
|
Loading…
Reference in New Issue
Block a user