diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 8dbc34b893..b65e631e96 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -310,6 +310,7 @@ pub struct MutableAppContext { window_invalidations: HashMap, invalidation_callbacks: HashMap>, + debug_elements_callbacks: HashMap crate::json::Value>>, foreground: Rc, future_handlers: Rc>>, stream_handlers: Rc>>, @@ -347,6 +348,7 @@ impl MutableAppContext { observations: HashMap::new(), window_invalidations: HashMap::new(), invalidation_callbacks: HashMap::new(), + debug_elements_callbacks: HashMap::new(), foreground, future_handlers: Default::default(), stream_handlers: Default::default(), @@ -373,16 +375,29 @@ impl MutableAppContext { &self.ctx.background } - pub fn on_window_invalidated( - &mut self, - window_id: usize, - callback: F, - ) { + pub fn on_window_invalidated(&mut self, window_id: usize, callback: F) + where + F: 'static + FnMut(WindowInvalidation, &mut MutableAppContext), + { self.invalidation_callbacks .insert(window_id, Box::new(callback)); self.update_windows(); } + pub fn on_debug_elements(&mut self, window_id: usize, callback: F) + where + F: 'static + Fn(&AppContext) -> crate::json::Value, + { + self.debug_elements_callbacks + .insert(window_id, Box::new(callback)); + } + + pub fn debug_elements(&self, window_id: usize) -> Option { + self.debug_elements_callbacks + .get(&window_id) + .map(|debug_elements| debug_elements(&self.ctx)) + } + pub fn add_action(&mut self, name: S, mut handler: F) where S: Into, @@ -692,11 +707,19 @@ impl MutableAppContext { })); } - self.on_window_invalidated(window_id, move |invalidation, ctx| { - let mut presenter = presenter.borrow_mut(); - presenter.invalidate(invalidation, ctx.downgrade()); - let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx); - window.present_scene(scene); + { + let presenter = presenter.clone(); + self.on_window_invalidated(window_id, move |invalidation, ctx| { + let mut presenter = presenter.borrow_mut(); + presenter.invalidate(invalidation, ctx.downgrade()); + let scene = + presenter.build_scene(window.size(), window.scale_factor(), ctx); + window.present_scene(scene); + }); + } + + self.on_debug_elements(window_id, move |ctx| { + presenter.borrow().debug_elements(ctx).unwrap() }); } } @@ -1573,6 +1596,10 @@ impl<'a, T: View> ViewContext<'a, T> { &self.app.ctx.background } + pub fn debug_elements(&self) -> crate::json::Value { + self.app.debug_elements(self.window_id).unwrap() + } + pub fn focus(&mut self, handle: S) where S: Into, diff --git a/gpui/src/presenter.rs b/gpui/src/presenter.rs index 293e528aba..7c3be96a72 100644 --- a/gpui/src/presenter.rs +++ b/gpui/src/presenter.rs @@ -2,7 +2,7 @@ use crate::{ app::{AppContext, MutableAppContext, WindowInvalidation}, elements::Element, font_cache::FontCache, - json::ToJson, + json::{self, ToJson}, platform::Event, text_layout::TextLayoutCache, AssetCache, ElementBox, Scene, @@ -130,6 +130,18 @@ impl Presenter { Vec::new() } } + + pub fn debug_elements(&self, ctx: &AppContext) -> Option { + ctx.root_view_id(self.window_id) + .and_then(|root_view_id| self.rendered_views.get(&root_view_id)) + .map(|root_element| { + root_element.debug(&DebugContext { + rendered_views: &self.rendered_views, + font_cache: &self.font_cache, + app: ctx, + }) + }) + } } pub struct ActionToDispatch { @@ -227,7 +239,7 @@ impl<'a> EventContext<'a> { } pub struct DebugContext<'a> { - rendered_views: &'a mut HashMap, + rendered_views: &'a HashMap, pub font_cache: &'a FontCache, pub app: &'a AppContext, } diff --git a/zed/src/workspace/workspace_view.rs b/zed/src/workspace/workspace_view.rs index 46cbdb0f28..7cb9fd8b64 100644 --- a/zed/src/workspace/workspace_view.rs +++ b/zed/src/workspace/workspace_view.rs @@ -2,15 +2,19 @@ use super::{pane, Pane, PaneGroup, SplitDirection, Workspace}; use crate::{settings::Settings, watch}; use futures_core::future::LocalBoxFuture; use gpui::{ - color::rgbu, elements::*, keymap::Binding, AnyViewHandle, App, AppContext, Entity, ModelHandle, - MutableAppContext, View, ViewContext, ViewHandle, + color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, App, + AppContext, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, }; use log::{error, info}; use std::{collections::HashSet, path::PathBuf}; pub fn init(app: &mut App) { app.add_action("workspace:save", WorkspaceView::save_active_item); - app.add_bindings(vec![Binding::new("cmd-s", "workspace:save", None)]); + app.add_action("workspace:debug_elements", WorkspaceView::debug_elements); + app.add_bindings(vec![ + Binding::new("cmd-s", "workspace:save", None), + Binding::new("cmd-alt-i", "workspace:debug_elements", None), + ]); } pub trait ItemView: View { @@ -251,6 +255,17 @@ impl WorkspaceView { }); } + pub fn debug_elements(&mut self, _: &(), ctx: &mut ViewContext) { + match to_string_pretty(&ctx.debug_elements()) { + Ok(json) => { + log::info!("{}", json); + } + Err(error) => { + log::error!("error debugging elements: {}", error); + } + }; + } + fn workspace_updated(&mut self, _: ModelHandle, ctx: &mut ViewContext) { ctx.notify(); }