Detailed changes
@@ -592,11 +592,11 @@ impl ContextMenu {
&self,
cursor_position: DisplayPoint,
style: EditorStyle,
- cx: &AppContext,
+ cx: &mut RenderContext<Editor>,
) -> (DisplayPoint, ElementBox) {
match self {
ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
- ContextMenu::CodeActions(menu) => menu.render(cursor_position, style),
+ ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
}
}
}
@@ -633,54 +633,62 @@ impl CompletionsMenu {
!self.matches.is_empty()
}
- fn render(&self, style: EditorStyle, _: &AppContext) -> ElementBox {
+ fn render(&self, style: EditorStyle, cx: &mut RenderContext<Editor>) -> ElementBox {
enum CompletionTag {}
let completions = self.completions.clone();
let matches = self.matches.clone();
let selected_item = self.selected_item;
let container_style = style.autocomplete.container;
- UniformList::new(self.list.clone(), matches.len(), move |range, items, cx| {
- let start_ix = range.start;
- for (ix, mat) in matches[range].iter().enumerate() {
- let completion = &completions[mat.candidate_id];
- let item_ix = start_ix + ix;
- items.push(
- MouseEventHandler::new::<CompletionTag, _, _>(
- mat.candidate_id,
- cx,
- |state, _| {
- let item_style = if item_ix == selected_item {
- style.autocomplete.selected_item
- } else if state.hovered {
- style.autocomplete.hovered_item
- } else {
- style.autocomplete.item
- };
-
- Text::new(completion.label.text.clone(), style.text.clone())
- .with_soft_wrap(false)
- .with_highlights(combine_syntax_and_fuzzy_match_highlights(
- &completion.label.text,
- style.text.color.into(),
- styled_runs_for_code_label(&completion.label, &style.syntax),
- &mat.positions,
- ))
- .contained()
- .with_style(item_style)
- .boxed()
- },
- )
- .with_cursor_style(CursorStyle::PointingHand)
- .on_mouse_down(move |cx| {
- cx.dispatch_action(ConfirmCompletion {
- item_ix: Some(item_ix),
- });
- })
- .boxed(),
- );
- }
- })
+ UniformList::new(
+ self.list.clone(),
+ matches.len(),
+ cx,
+ move |_, range, items, cx| {
+ let start_ix = range.start;
+ for (ix, mat) in matches[range].iter().enumerate() {
+ let completion = &completions[mat.candidate_id];
+ let item_ix = start_ix + ix;
+ items.push(
+ MouseEventHandler::new::<CompletionTag, _, _>(
+ mat.candidate_id,
+ cx,
+ |state, _| {
+ let item_style = if item_ix == selected_item {
+ style.autocomplete.selected_item
+ } else if state.hovered {
+ style.autocomplete.hovered_item
+ } else {
+ style.autocomplete.item
+ };
+
+ Text::new(completion.label.text.clone(), style.text.clone())
+ .with_soft_wrap(false)
+ .with_highlights(combine_syntax_and_fuzzy_match_highlights(
+ &completion.label.text,
+ style.text.color.into(),
+ styled_runs_for_code_label(
+ &completion.label,
+ &style.syntax,
+ ),
+ &mat.positions,
+ ))
+ .contained()
+ .with_style(item_style)
+ .boxed()
+ },
+ )
+ .with_cursor_style(CursorStyle::PointingHand)
+ .on_mouse_down(move |cx| {
+ cx.dispatch_action(ConfirmCompletion {
+ item_ix: Some(item_ix),
+ });
+ })
+ .boxed(),
+ );
+ }
+ },
+ )
.with_width_from_item(
self.matches
.iter()
@@ -772,14 +780,18 @@ impl CodeActionsMenu {
&self,
mut cursor_position: DisplayPoint,
style: EditorStyle,
+ cx: &mut RenderContext<Editor>,
) -> (DisplayPoint, ElementBox) {
enum ActionTag {}
let container_style = style.autocomplete.container;
let actions = self.actions.clone();
let selected_item = self.selected_item;
- let element =
- UniformList::new(self.list.clone(), actions.len(), move |range, items, cx| {
+ let element = UniformList::new(
+ self.list.clone(),
+ actions.len(),
+ cx,
+ move |_, range, items, cx| {
let start_ix = range.start;
for (ix, action) in actions[range].iter().enumerate() {
let item_ix = start_ix + ix;
@@ -808,17 +820,18 @@ impl CodeActionsMenu {
.boxed(),
);
}
- })
- .with_width_from_item(
- self.actions
- .iter()
- .enumerate()
- .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
- .map(|(ix, _)| ix),
- )
- .contained()
- .with_style(container_style)
- .boxed();
+ },
+ )
+ .with_width_from_item(
+ self.actions
+ .iter()
+ .enumerate()
+ .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
+ .map(|(ix, _)| ix),
+ )
+ .contained()
+ .with_style(container_style)
+ .boxed();
if self.deployed_from_indicator {
*cursor_position.column_mut() = 0;
@@ -2578,7 +2591,7 @@ impl Editor {
pub fn render_code_actions_indicator(
&self,
style: &EditorStyle,
- cx: &mut ViewContext<Self>,
+ cx: &mut RenderContext<Self>,
) -> Option<ElementBox> {
if self.available_code_actions.is_some() {
enum Tag {}
@@ -2612,7 +2625,7 @@ impl Editor {
&self,
cursor_position: DisplayPoint,
style: EditorStyle,
- cx: &AppContext,
+ cx: &mut RenderContext<Editor>,
) -> Option<(DisplayPoint, ElementBox)> {
self.context_menu
.as_ref()
@@ -1024,8 +1024,6 @@ impl Element for EditorElement {
max_row.saturating_sub(1) as f32,
);
- let mut context_menu = None;
- let mut code_actions_indicator = None;
self.update_view(cx.app, |view, cx| {
let clamped = view.clamp_scroll_left(scroll_max.x());
let autoscrolled;
@@ -1045,7 +1043,11 @@ impl Element for EditorElement {
if clamped || autoscrolled {
snapshot = view.snapshot(cx);
}
+ });
+ let mut context_menu = None;
+ let mut code_actions_indicator = None;
+ cx.render(&self.view.upgrade(cx).unwrap(), |view, cx| {
let newest_selection_head = view
.selections
.newest::<usize>(cx)
@@ -468,6 +468,26 @@ impl TestAppContext {
result
}
+ pub fn render<F, V, T>(&mut self, handle: &ViewHandle<V>, f: F) -> T
+ where
+ F: FnOnce(&mut V, &mut RenderContext<V>) -> T,
+ V: View,
+ {
+ handle.update(&mut *self.cx.borrow_mut(), |view, cx| {
+ let mut render_cx = RenderContext {
+ app: cx,
+ window_id: handle.window_id(),
+ view_id: handle.id(),
+ view_type: PhantomData,
+ titlebar_height: 0.,
+ hovered_region_id: None,
+ clicked_region_id: None,
+ refreshing: false,
+ };
+ f(view, &mut render_cx)
+ })
+ }
+
pub fn to_async(&self) -> AsyncAppContext {
AsyncAppContext(self.cx.clone())
}
@@ -1756,27 +1776,6 @@ impl MutableAppContext {
)
}
- pub fn build_render_context<V: View>(
- &mut self,
- window_id: usize,
- view_id: usize,
- titlebar_height: f32,
- hovered_region_id: Option<MouseRegionId>,
- clicked_region_id: Option<MouseRegionId>,
- refreshing: bool,
- ) -> RenderContext<V> {
- RenderContext {
- app: self,
- window_id,
- view_id,
- view_type: PhantomData,
- titlebar_height,
- hovered_region_id,
- clicked_region_id,
- refreshing,
- }
- }
-
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
where
T: View,
@@ -3429,13 +3428,13 @@ pub struct RenderParams {
}
pub struct RenderContext<'a, T: View> {
+ pub(crate) window_id: usize,
+ pub(crate) view_id: usize,
+ pub(crate) view_type: PhantomData<T>,
+ pub(crate) hovered_region_id: Option<MouseRegionId>,
+ pub(crate) clicked_region_id: Option<MouseRegionId>,
pub app: &'a mut MutableAppContext,
- window_id: usize,
- view_id: usize,
- view_type: PhantomData<T>,
pub titlebar_height: f32,
- hovered_region_id: Option<MouseRegionId>,
- clicked_region_id: Option<MouseRegionId>,
pub refreshing: bool,
}
@@ -3587,6 +3586,16 @@ impl<V> UpgradeViewHandle for ViewContext<'_, V> {
}
}
+impl<V: View> UpgradeViewHandle for RenderContext<'_, V> {
+ fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
+ self.cx.upgrade_view_handle(handle)
+ }
+
+ fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
+ self.cx.upgrade_any_view_handle(handle)
+ }
+}
+
impl<V: View> UpdateModel for ViewContext<'_, V> {
fn update_model<T: Entity, O>(
&mut self,
@@ -5,7 +5,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{self, json},
- ElementBox,
+ ElementBox, RenderContext, View,
};
use json::ToJson;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -41,27 +41,40 @@ pub struct LayoutState {
items: Vec<ElementBox>,
}
-pub struct UniformList<F>
-where
- F: Fn(Range<usize>, &mut Vec<ElementBox>, &mut LayoutContext),
-{
+pub struct UniformList {
state: UniformListState,
item_count: usize,
- append_items: F,
+ append_items: Box<dyn Fn(Range<usize>, &mut Vec<ElementBox>, &mut LayoutContext) -> bool>,
padding_top: f32,
padding_bottom: f32,
get_width_from_item: Option<usize>,
}
-impl<F> UniformList<F>
-where
- F: Fn(Range<usize>, &mut Vec<ElementBox>, &mut LayoutContext),
-{
- pub fn new(state: UniformListState, item_count: usize, append_items: F) -> Self {
+impl UniformList {
+ pub fn new<F, V>(
+ state: UniformListState,
+ item_count: usize,
+ cx: &mut RenderContext<V>,
+ append_items: F,
+ ) -> Self
+ where
+ V: View,
+ F: 'static + Fn(&mut V, Range<usize>, &mut Vec<ElementBox>, &mut RenderContext<V>),
+ {
+ let handle = cx.handle();
Self {
state,
item_count,
- append_items,
+ append_items: Box::new(move |range, items, cx| {
+ if let Some(handle) = handle.upgrade(cx) {
+ cx.render(&handle, |view, cx| {
+ append_items(view, range, items, cx);
+ });
+ true
+ } else {
+ false
+ }
+ }),
padding_top: 0.,
padding_bottom: 0.,
get_width_from_item: None,
@@ -144,10 +157,7 @@ where
}
}
-impl<F> Element for UniformList<F>
-where
- F: Fn(Range<usize>, &mut Vec<ElementBox>, &mut LayoutContext),
-{
+impl Element for UniformList {
type LayoutState = LayoutState;
type PaintState = ();
@@ -9,13 +9,14 @@ use crate::{
text_layout::TextLayoutCache,
Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox,
ElementStateContext, Entity, FontSystem, ModelHandle, MouseRegion, MouseRegionId, ReadModel,
- ReadView, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle,
- WeakModelHandle, WeakViewHandle,
+ ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle, View,
+ ViewHandle, WeakModelHandle, WeakViewHandle,
};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use serde_json::json;
use std::{
collections::{HashMap, HashSet},
+ marker::PhantomData,
ops::{Deref, DerefMut},
sync::Arc,
};
@@ -172,12 +173,15 @@ impl Presenter {
LayoutContext {
rendered_views: &mut self.rendered_views,
parents: &mut self.parents,
- refreshing,
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_id: self.hovered_region_id,
+ clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id),
+ titlebar_height: self.titlebar_height,
app: cx,
}
}
@@ -342,12 +346,15 @@ pub struct LayoutContext<'a> {
rendered_views: &'a mut HashMap<usize, ElementBox>,
parents: &'a mut HashMap<usize, usize>,
view_stack: Vec<usize>,
- pub refreshing: bool,
pub font_cache: &'a Arc<FontCache>,
pub font_system: Arc<dyn FontSystem>,
pub text_layout_cache: &'a TextLayoutCache,
pub asset_cache: &'a AssetCache,
pub app: &'a mut MutableAppContext,
+ pub refreshing: bool,
+ titlebar_height: f32,
+ hovered_region_id: Option<MouseRegionId>,
+ clicked_region_id: Option<MouseRegionId>,
}
impl<'a> LayoutContext<'a> {
@@ -362,6 +369,26 @@ impl<'a> LayoutContext<'a> {
self.view_stack.pop();
size
}
+
+ pub fn render<F, V, T>(&mut self, handle: &ViewHandle<V>, f: F) -> T
+ where
+ F: FnOnce(&mut V, &mut RenderContext<V>) -> T,
+ V: View,
+ {
+ handle.update(self.app, |view, cx| {
+ let mut render_cx = RenderContext {
+ app: cx,
+ window_id: handle.window_id(),
+ view_id: handle.id(),
+ view_type: PhantomData,
+ titlebar_height: self.titlebar_height,
+ hovered_region_id: self.hovered_region_id,
+ clicked_region_id: self.clicked_region_id,
+ refreshing: self.refreshing,
+ };
+ f(view, &mut render_cx)
+ })
+ }
}
impl<'a> Deref for LayoutContext<'a> {
@@ -123,7 +123,6 @@ impl View for Select {
.boxed(),
);
if self.is_open {
- let handle = self.handle.clone();
result.add_child(
Overlay::new(
Container::new(
@@ -131,9 +130,8 @@ impl View for Select {
UniformList::new(
self.list_state.clone(),
self.item_count,
- move |mut range, items, cx| {
- let handle = handle.upgrade(cx).unwrap();
- let this = handle.read(cx);
+ cx,
+ move |this, mut range, items, cx| {
let selected_item_ix = this.selected_item_ix;
range.end = range.end.min(this.item_count);
items.extend(range.map(|ix| {
@@ -141,7 +139,7 @@ impl View for Select {
ix,
cx,
|mouse_state, cx| {
- (handle.read(cx).render_item)(
+ (this.render_item)(
ix,
if ix == selected_item_ix {
ItemType::Selected
@@ -54,6 +54,7 @@ impl<D: PickerDelegate> View for Picker<D> {
fn render(&mut self, cx: &mut RenderContext<Self>) -> gpui::ElementBox {
let settings = cx.global::<Settings>();
+ let container_style = settings.theme.picker.container;
let delegate = self.delegate.clone();
let match_count = if let Some(delegate) = delegate.upgrade(cx.app) {
delegate.read(cx).match_count()
@@ -80,8 +81,9 @@ impl<D: PickerDelegate> View for Picker<D> {
UniformList::new(
self.list_state.clone(),
match_count,
- move |mut range, items, cx| {
- let delegate = delegate.upgrade(cx).unwrap();
+ cx,
+ move |this, mut range, items, cx| {
+ let delegate = this.delegate.upgrade(cx).unwrap();
let selected_ix = delegate.read(cx).selected_index();
range.end = cmp::min(range.end, delegate.read(cx).match_count());
items.extend(range.map(move |ix| {
@@ -103,7 +105,7 @@ impl<D: PickerDelegate> View for Picker<D> {
.boxed(),
)
.contained()
- .with_style(settings.theme.picker.container)
+ .with_style(container_style)
.constrained()
.with_max_width(self.max_size.x())
.with_max_height(self.max_size.y())
@@ -9,8 +9,8 @@ use gpui::{
},
impl_internal_actions, keymap,
platform::CursorStyle,
- AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, PromptLevel, Task,
- View, ViewContext, ViewHandle, WeakViewHandle,
+ AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, PromptLevel,
+ RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use project::{Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
use settings::Settings;
@@ -706,8 +706,8 @@ impl ProjectPanel {
fn for_each_visible_entry(
&self,
range: Range<usize>,
- cx: &mut ViewContext<ProjectPanel>,
- mut callback: impl FnMut(ProjectEntryId, EntryDetails, &mut ViewContext<ProjectPanel>),
+ cx: &mut RenderContext<ProjectPanel>,
+ mut callback: impl FnMut(ProjectEntryId, EntryDetails, &mut RenderContext<ProjectPanel>),
) {
let mut ix = 0;
for (worktree_id, visible_worktree_entries) in &self.visible_entries {
@@ -780,7 +780,7 @@ impl ProjectPanel {
details: EntryDetails,
editor: &ViewHandle<Editor>,
theme: &theme::ProjectPanel,
- cx: &mut ViewContext<Self>,
+ cx: &mut RenderContext<Self>,
) -> ElementBox {
let kind = details.kind;
let show_editor = details.is_editing && !details.is_processing;
@@ -861,31 +861,28 @@ impl View for ProjectPanel {
"ProjectPanel"
}
- fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox {
+ fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> gpui::ElementBox {
let theme = &cx.global::<Settings>().theme.project_panel;
let mut container_style = theme.container;
let padding = std::mem::take(&mut container_style.padding);
- let handle = self.handle.clone();
UniformList::new(
self.list.clone(),
self.visible_entries
.iter()
.map(|(_, worktree_entries)| worktree_entries.len())
.sum(),
- move |range, items, cx| {
+ cx,
+ move |this, range, items, cx| {
let theme = cx.global::<Settings>().theme.clone();
- let this = handle.upgrade(cx).unwrap();
- this.update(cx.app, |this, cx| {
- this.for_each_visible_entry(range.clone(), cx, |id, details, cx| {
- items.push(Self::render_entry(
- id,
- details,
- &this.filename_editor,
- &theme.project_panel,
- cx,
- ));
- });
- })
+ this.for_each_visible_entry(range.clone(), cx, |id, details, cx| {
+ items.push(Self::render_entry(
+ id,
+ details,
+ &this.filename_editor,
+ &theme.project_panel,
+ cx,
+ ));
+ });
},
)
.with_padding_top(padding.top)
@@ -1343,7 +1340,7 @@ mod tests {
let mut result = Vec::new();
let mut project_entries = HashSet::new();
let mut has_editor = false;
- panel.update(cx, |panel, cx| {
+ cx.render(panel, |panel, cx| {
panel.for_each_visible_entry(range, cx, |project_entry, details, _| {
if details.is_editing {
assert!(!has_editor, "duplicate editor entry");