Detailed changes
@@ -173,23 +173,7 @@ impl AcpThreadView {
let mention_set = mention_set.clone();
- let list_state = ListState::new(0, gpui::ListAlignment::Bottom, px(2048.0), {
- let this = cx.entity().downgrade();
- move |index: usize, window, cx| {
- let Some(this) = this.upgrade() else {
- return Empty.into_any();
- };
- this.update(cx, |this, cx| {
- let Some((entry, len)) = this.thread().and_then(|thread| {
- let entries = &thread.read(cx).entries();
- Some((entries.get(index)?, entries.len()))
- }) else {
- return Empty.into_any();
- };
- this.render_entry(index, len, entry, window, cx)
- })
- }
- });
+ let list_state = ListState::new(0, gpui::ListAlignment::Bottom, px(2048.0));
Self {
agent: agent.clone(),
@@ -2552,10 +2536,21 @@ impl Render for AcpThreadView {
v_flex().flex_1().map(|this| {
if self.list_state.item_count() > 0 {
this.child(
- list(self.list_state.clone())
- .with_sizing_behavior(gpui::ListSizingBehavior::Auto)
- .flex_grow()
- .into_any(),
+ list(
+ self.list_state.clone(),
+ cx.processor(|this, index: usize, window, cx| {
+ let Some((entry, len)) = this.thread().and_then(|thread| {
+ let entries = &thread.read(cx).entries();
+ Some((entries.get(index)?, entries.len()))
+ }) else {
+ return Empty.into_any();
+ };
+ this.render_entry(index, len, entry, window, cx)
+ }),
+ )
+ .with_sizing_behavior(gpui::ListSizingBehavior::Auto)
+ .flex_grow()
+ .into_any(),
)
.children(match thread_clone.read(cx).status() {
ThreadStatus::Idle | ThreadStatus::WaitingForToolConfirmation => {
@@ -780,13 +780,7 @@ impl ActiveThread {
cx.observe_global::<SettingsStore>(|_, cx| cx.notify()),
];
- let list_state = ListState::new(0, ListAlignment::Bottom, px(2048.), {
- let this = cx.entity().downgrade();
- move |ix, window: &mut Window, cx: &mut App| {
- this.update(cx, |this, cx| this.render_message(ix, window, cx))
- .unwrap()
- }
- });
+ let list_state = ListState::new(0, ListAlignment::Bottom, px(2048.));
let workspace_subscription = if let Some(workspace) = workspace.upgrade() {
Some(cx.observe_release(&workspace, |this, _, cx| {
@@ -1846,7 +1840,12 @@ impl ActiveThread {
)))
}
- fn render_message(&self, ix: usize, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
+ fn render_message(
+ &mut self,
+ ix: usize,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> AnyElement {
let message_id = self.messages[ix];
let workspace = self.workspace.clone();
let thread = self.thread.read(cx);
@@ -3613,7 +3612,7 @@ impl Render for ActiveThread {
this.hide_scrollbar_later(cx);
}),
)
- .child(list(self.list_state.clone()).flex_grow())
+ .child(list(self.list_state.clone(), cx.processor(Self::render_message)).flex_grow())
.when_some(self.render_vertical_scrollbar(cx), |this, scrollbar| {
this.child(scrollbar)
})
@@ -1471,7 +1471,6 @@ impl AgentPanel {
let current_is_special = current_is_history || current_is_config;
let new_is_special = new_is_history || new_is_config;
- let mut old_acp_thread = None;
match &self.active_view {
ActiveView::Thread { thread, .. } => {
@@ -1483,9 +1482,6 @@ impl AgentPanel {
});
}
}
- ActiveView::ExternalAgentThread { thread_view } => {
- old_acp_thread.replace(thread_view.downgrade());
- }
_ => {}
}
@@ -1516,11 +1512,6 @@ impl AgentPanel {
self.active_view = new_view;
}
- debug_assert!(
- old_acp_thread.map_or(true, |thread| !thread.is_upgradable()),
- "AcpThreadView leaked"
- );
-
self.acp_message_history.borrow_mut().reset_position();
self.focus_handle(cx).focus(window);
@@ -103,28 +103,16 @@ impl ChatPanel {
});
cx.new(|cx| {
- let entity = cx.entity().downgrade();
- let message_list = ListState::new(
- 0,
- gpui::ListAlignment::Bottom,
- px(1000.),
- move |ix, window, cx| {
- if let Some(entity) = entity.upgrade() {
- entity.update(cx, |this: &mut Self, cx| {
- this.render_message(ix, window, cx).into_any_element()
- })
- } else {
- div().into_any()
+ let message_list = ListState::new(0, gpui::ListAlignment::Bottom, px(1000.));
+
+ message_list.set_scroll_handler(cx.listener(
+ |this: &mut Self, event: &ListScrollEvent, _, cx| {
+ if event.visible_range.start < MESSAGE_LOADING_THRESHOLD {
+ this.load_more_messages(cx);
}
+ this.is_scrolled_to_bottom = !event.is_scrolled;
},
- );
-
- message_list.set_scroll_handler(cx.listener(|this, event: &ListScrollEvent, _, cx| {
- if event.visible_range.start < MESSAGE_LOADING_THRESHOLD {
- this.load_more_messages(cx);
- }
- this.is_scrolled_to_bottom = !event.is_scrolled;
- }));
+ ));
let local_offset = chrono::Local::now().offset().local_minus_utc();
let mut this = Self {
@@ -399,7 +387,7 @@ impl ChatPanel {
ix: usize,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> impl IntoElement {
+ ) -> AnyElement {
let active_chat = &self.active_chat.as_ref().unwrap().0;
let (message, is_continuation_from_previous, is_admin) =
active_chat.update(cx, |active_chat, cx| {
@@ -582,6 +570,7 @@ impl ChatPanel {
self.render_popover_buttons(message_id, can_delete_message, can_edit_message, cx)
.mt_neg_2p5(),
)
+ .into_any_element()
}
fn has_open_menu(&self, message_id: Option<u64>) -> bool {
@@ -979,7 +968,13 @@ impl Render for ChatPanel {
)
.child(div().flex_grow().px_2().map(|this| {
if self.active_chat.is_some() {
- this.child(list(self.message_list.clone()).size_full())
+ this.child(
+ list(
+ self.message_list.clone(),
+ cx.processor(Self::render_message),
+ )
+ .size_full(),
+ )
} else {
this.child(
div()
@@ -324,20 +324,6 @@ impl CollabPanel {
)
.detach();
- let entity = cx.entity().downgrade();
- let list_state = ListState::new(
- 0,
- gpui::ListAlignment::Top,
- px(1000.),
- move |ix, window, cx| {
- if let Some(entity) = entity.upgrade() {
- entity.update(cx, |this, cx| this.render_list_entry(ix, window, cx))
- } else {
- div().into_any()
- }
- },
- );
-
let mut this = Self {
width: None,
focus_handle: cx.focus_handle(),
@@ -345,7 +331,7 @@ impl CollabPanel {
fs: workspace.app_state().fs.clone(),
pending_serialization: Task::ready(None),
context_menu: None,
- list_state,
+ list_state: ListState::new(0, gpui::ListAlignment::Top, px(1000.)),
channel_name_editor,
filter_editor,
entries: Vec::default(),
@@ -2431,7 +2417,13 @@ impl CollabPanel {
});
v_flex()
.size_full()
- .child(list(self.list_state.clone()).size_full())
+ .child(
+ list(
+ self.list_state.clone(),
+ cx.processor(Self::render_list_entry),
+ )
+ .size_full(),
+ )
.child(
v_flex()
.child(div().mx_2().border_primary(cx).border_t_1())
@@ -118,16 +118,7 @@ impl NotificationPanel {
})
.detach();
- let entity = cx.entity().downgrade();
- let notification_list =
- ListState::new(0, ListAlignment::Top, px(1000.), move |ix, window, cx| {
- entity
- .upgrade()
- .and_then(|entity| {
- entity.update(cx, |this, cx| this.render_notification(ix, window, cx))
- })
- .unwrap_or_else(|| div().into_any())
- });
+ let notification_list = ListState::new(0, ListAlignment::Top, px(1000.));
notification_list.set_scroll_handler(cx.listener(
|this, event: &ListScrollEvent, _, cx| {
if event.count.saturating_sub(event.visible_range.end) < LOADING_THRESHOLD {
@@ -687,7 +678,16 @@ impl Render for NotificationPanel {
),
)
} else {
- this.child(list(self.notification_list.clone()).size_full())
+ this.child(
+ list(
+ self.notification_list.clone(),
+ cx.processor(|this, ix, window, cx| {
+ this.render_notification(ix, window, cx)
+ .unwrap_or_else(|| div().into_any())
+ }),
+ )
+ .size_full(),
+ )
}
})
}
@@ -13,22 +13,8 @@ pub(crate) struct LoadedSourceList {
impl LoadedSourceList {
pub fn new(session: Entity<Session>, cx: &mut Context<Self>) -> Self {
- let weak_entity = cx.weak_entity();
let focus_handle = cx.focus_handle();
-
- let list = ListState::new(
- 0,
- gpui::ListAlignment::Top,
- px(1000.),
- move |ix, _window, cx| {
- weak_entity
- .upgrade()
- .map(|loaded_sources| {
- loaded_sources.update(cx, |this, cx| this.render_entry(ix, cx))
- })
- .unwrap_or(div().into_any())
- },
- );
+ let list = ListState::new(0, gpui::ListAlignment::Top, px(1000.));
let _subscription = cx.subscribe(&session, |this, _, event, cx| match event {
SessionEvent::Stopped(_) | SessionEvent::LoadedSources => {
@@ -98,6 +84,12 @@ impl Render for LoadedSourceList {
.track_focus(&self.focus_handle)
.size_full()
.p_1()
- .child(list(self.list.clone()).size_full())
+ .child(
+ list(
+ self.list.clone(),
+ cx.processor(|this, ix, _window, cx| this.render_entry(ix, cx)),
+ )
+ .size_full(),
+ )
}
}
@@ -70,13 +70,7 @@ impl StackFrameList {
_ => {}
});
- let list_state = ListState::new(0, gpui::ListAlignment::Top, px(1000.), {
- let this = cx.weak_entity();
- move |ix, _window, cx| {
- this.update(cx, |this, cx| this.render_entry(ix, cx))
- .unwrap_or(div().into_any())
- }
- });
+ let list_state = ListState::new(0, gpui::ListAlignment::Top, px(1000.));
let scrollbar_state = ScrollbarState::new(list_state.clone());
let mut this = Self {
@@ -708,11 +702,14 @@ impl StackFrameList {
self.activate_selected_entry(window, cx);
}
- fn render_list(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
- div()
- .p_1()
- .size_full()
- .child(list(self.list_state.clone()).size_full())
+ fn render_list(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ div().p_1().size_full().child(
+ list(
+ self.list_state.clone(),
+ cx.processor(|this, ix, _window, cx| this.render_entry(ix, cx)),
+ )
+ .size_full(),
+ )
}
}
@@ -18,10 +18,16 @@ use refineable::Refineable as _;
use std::{cell::RefCell, ops::Range, rc::Rc};
use sum_tree::{Bias, Dimensions, SumTree};
+type RenderItemFn = dyn FnMut(usize, &mut Window, &mut App) -> AnyElement + 'static;
+
/// Construct a new list element
-pub fn list(state: ListState) -> List {
+pub fn list(
+ state: ListState,
+ render_item: impl FnMut(usize, &mut Window, &mut App) -> AnyElement + 'static,
+) -> List {
List {
state,
+ render_item: Box::new(render_item),
style: StyleRefinement::default(),
sizing_behavior: ListSizingBehavior::default(),
}
@@ -30,6 +36,7 @@ pub fn list(state: ListState) -> List {
/// A list element
pub struct List {
state: ListState,
+ render_item: Box<RenderItemFn>,
style: StyleRefinement,
sizing_behavior: ListSizingBehavior,
}
@@ -55,7 +62,6 @@ impl std::fmt::Debug for ListState {
struct StateInner {
last_layout_bounds: Option<Bounds<Pixels>>,
last_padding: Option<Edges<Pixels>>,
- render_item: Box<dyn FnMut(usize, &mut Window, &mut App) -> AnyElement>,
items: SumTree<ListItem>,
logical_scroll_top: Option<ListOffset>,
alignment: ListAlignment,
@@ -186,19 +192,10 @@ impl ListState {
/// above and below the visible area. Elements within this area will
/// be measured even though they are not visible. This can help ensure
/// that the list doesn't flicker or pop in when scrolling.
- pub fn new<R>(
- item_count: usize,
- alignment: ListAlignment,
- overdraw: Pixels,
- render_item: R,
- ) -> Self
- where
- R: 'static + FnMut(usize, &mut Window, &mut App) -> AnyElement,
- {
+ pub fn new(item_count: usize, alignment: ListAlignment, overdraw: Pixels) -> Self {
let this = Self(Rc::new(RefCell::new(StateInner {
last_layout_bounds: None,
last_padding: None,
- render_item: Box::new(render_item),
items: SumTree::default(),
logical_scroll_top: None,
alignment,
@@ -532,6 +529,7 @@ impl StateInner {
available_width: Option<Pixels>,
available_height: Pixels,
padding: &Edges<Pixels>,
+ render_item: &mut RenderItemFn,
window: &mut Window,
cx: &mut App,
) -> LayoutItemsResponse {
@@ -566,7 +564,7 @@ impl StateInner {
// If we're within the visible area or the height wasn't cached, render and measure the item's element
if visible_height < available_height || size.is_none() {
let item_index = scroll_top.item_ix + ix;
- let mut element = (self.render_item)(item_index, window, cx);
+ let mut element = render_item(item_index, window, cx);
let element_size = element.layout_as_root(available_item_space, window, cx);
size = Some(element_size);
if visible_height < available_height {
@@ -601,7 +599,7 @@ impl StateInner {
cursor.prev();
if let Some(item) = cursor.item() {
let item_index = cursor.start().0;
- let mut element = (self.render_item)(item_index, window, cx);
+ let mut element = render_item(item_index, window, cx);
let element_size = element.layout_as_root(available_item_space, window, cx);
let focus_handle = item.focus_handle();
rendered_height += element_size.height;
@@ -650,7 +648,7 @@ impl StateInner {
let size = if let ListItem::Measured { size, .. } = item {
*size
} else {
- let mut element = (self.render_item)(cursor.start().0, window, cx);
+ let mut element = render_item(cursor.start().0, window, cx);
element.layout_as_root(available_item_space, window, cx)
};
@@ -683,7 +681,7 @@ impl StateInner {
while let Some(item) = cursor.item() {
if item.contains_focused(window, cx) {
let item_index = cursor.start().0;
- let mut element = (self.render_item)(cursor.start().0, window, cx);
+ let mut element = render_item(cursor.start().0, window, cx);
let size = element.layout_as_root(available_item_space, window, cx);
item_layouts.push_back(ItemLayout {
index: item_index,
@@ -708,6 +706,7 @@ impl StateInner {
bounds: Bounds<Pixels>,
padding: Edges<Pixels>,
autoscroll: bool,
+ render_item: &mut RenderItemFn,
window: &mut Window,
cx: &mut App,
) -> Result<LayoutItemsResponse, ListOffset> {
@@ -716,6 +715,7 @@ impl StateInner {
Some(bounds.size.width),
bounds.size.height,
&padding,
+ render_item,
window,
cx,
);
@@ -753,8 +753,7 @@ impl StateInner {
let Some(item) = cursor.item() else { break };
let size = item.size().unwrap_or_else(|| {
- let mut item =
- (self.render_item)(cursor.start().0, window, cx);
+ let mut item = render_item(cursor.start().0, window, cx);
let item_available_size = size(
bounds.size.width.into(),
AvailableSpace::MinContent,
@@ -876,8 +875,14 @@ impl Element for List {
window.rem_size(),
);
- let layout_response =
- state.layout_items(None, available_height, &padding, window, cx);
+ let layout_response = state.layout_items(
+ None,
+ available_height,
+ &padding,
+ &mut self.render_item,
+ window,
+ cx,
+ );
let max_element_width = layout_response.max_item_width;
let summary = state.items.summary();
@@ -951,15 +956,16 @@ impl Element for List {
let padding = style
.padding
.to_pixels(bounds.size.into(), window.rem_size());
- let layout = match state.prepaint_items(bounds, padding, true, window, cx) {
- Ok(layout) => layout,
- Err(autoscroll_request) => {
- state.logical_scroll_top = Some(autoscroll_request);
- state
- .prepaint_items(bounds, padding, false, window, cx)
- .unwrap()
- }
- };
+ let layout =
+ match state.prepaint_items(bounds, padding, true, &mut self.render_item, window, cx) {
+ Ok(layout) => layout,
+ Err(autoscroll_request) => {
+ state.logical_scroll_top = Some(autoscroll_request);
+ state
+ .prepaint_items(bounds, padding, false, &mut self.render_item, window, cx)
+ .unwrap()
+ }
+ };
state.last_layout_bounds = Some(bounds);
state.last_padding = Some(padding);
@@ -1108,9 +1114,7 @@ mod test {
let cx = cx.add_empty_window();
- let state = ListState::new(5, crate::ListAlignment::Top, px(10.), |_, _, _| {
- div().h(px(10.)).w_full().into_any()
- });
+ let state = ListState::new(5, crate::ListAlignment::Top, px(10.));
// Ensure that the list is scrolled to the top
state.scroll_to(gpui::ListOffset {
@@ -1121,7 +1125,11 @@ mod test {
struct TestView(ListState);
impl Render for TestView {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
- list(self.0.clone()).w_full().h_full()
+ list(self.0.clone(), |_, _, _| {
+ div().h(px(10.)).w_full().into_any()
+ })
+ .w_full()
+ .h_full()
}
}
@@ -1154,14 +1162,16 @@ mod test {
let cx = cx.add_empty_window();
- let state = ListState::new(5, crate::ListAlignment::Top, px(10.), |_, _, _| {
- div().h(px(20.)).w_full().into_any()
- });
+ let state = ListState::new(5, crate::ListAlignment::Top, px(10.));
struct TestView(ListState);
impl Render for TestView {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
- list(self.0.clone()).w_full().h_full()
+ list(self.0.clone(), |_, _, _| {
+ div().h(px(20.)).w_full().into_any()
+ })
+ .w_full()
+ .h_full()
}
}
@@ -18,6 +18,7 @@ use workspace::item::{Item, ItemHandle};
use workspace::{Pane, Workspace};
use crate::markdown_elements::ParsedMarkdownElement;
+use crate::markdown_renderer::CheckboxClickedEvent;
use crate::{
MovePageDown, MovePageUp, OpenFollowingPreview, OpenPreview, OpenPreviewToTheSide,
markdown_elements::ParsedMarkdown,
@@ -203,114 +204,7 @@ impl MarkdownPreviewView {
cx: &mut Context<Workspace>,
) -> Entity<Self> {
cx.new(|cx| {
- let view = cx.entity().downgrade();
-
- let list_state = ListState::new(
- 0,
- gpui::ListAlignment::Top,
- px(1000.),
- move |ix, window, cx| {
- if let Some(view) = view.upgrade() {
- view.update(cx, |this: &mut Self, cx| {
- let Some(contents) = &this.contents else {
- return div().into_any();
- };
-
- let mut render_cx =
- RenderContext::new(Some(this.workspace.clone()), window, cx)
- .with_checkbox_clicked_callback({
- let view = view.clone();
- move |checked, source_range, window, cx| {
- view.update(cx, |view, cx| {
- if let Some(editor) = view
- .active_editor
- .as_ref()
- .map(|s| s.editor.clone())
- {
- editor.update(cx, |editor, cx| {
- let task_marker =
- if checked { "[x]" } else { "[ ]" };
-
- editor.edit(
- vec![(source_range, task_marker)],
- cx,
- );
- });
- view.parse_markdown_from_active_editor(
- false, window, cx,
- );
- cx.notify();
- }
- })
- }
- });
-
- let block = contents.children.get(ix).unwrap();
- let rendered_block = render_markdown_block(block, &mut render_cx);
-
- let should_apply_padding = Self::should_apply_padding_between(
- block,
- contents.children.get(ix + 1),
- );
-
- div()
- .id(ix)
- .when(should_apply_padding, |this| {
- this.pb(render_cx.scaled_rems(0.75))
- })
- .group("markdown-block")
- .on_click(cx.listener(
- move |this, event: &ClickEvent, window, cx| {
- if event.click_count() == 2 {
- if let Some(source_range) = this
- .contents
- .as_ref()
- .and_then(|c| c.children.get(ix))
- .and_then(|block| block.source_range())
- {
- this.move_cursor_to_block(
- window,
- cx,
- source_range.start..source_range.start,
- );
- }
- }
- },
- ))
- .map(move |container| {
- let indicator = div()
- .h_full()
- .w(px(4.0))
- .when(ix == this.selected_block, |this| {
- this.bg(cx.theme().colors().border)
- })
- .group_hover("markdown-block", |s| {
- if ix == this.selected_block {
- s
- } else {
- s.bg(cx.theme().colors().border_variant)
- }
- })
- .rounded_xs();
-
- container.child(
- div()
- .relative()
- .child(
- div()
- .pl(render_cx.scaled_rems(1.0))
- .child(rendered_block),
- )
- .child(indicator.absolute().left_0().top_0()),
- )
- })
- .into_any()
- })
- } else {
- div().into_any()
- }
- },
- );
+ let list_state = ListState::new(0, gpui::ListAlignment::Top, px(1000.));
let mut this = Self {
selected_block: 0,
@@ -607,10 +501,107 @@ impl Render for MarkdownPreviewView {
.p_4()
.text_size(buffer_size)
.line_height(relative(buffer_line_height.value()))
- .child(
- div()
- .flex_grow()
- .map(|this| this.child(list(self.list_state.clone()).size_full())),
- )
+ .child(div().flex_grow().map(|this| {
+ this.child(
+ list(
+ self.list_state.clone(),
+ cx.processor(|this, ix, window, cx| {
+ let Some(contents) = &this.contents else {
+ return div().into_any();
+ };
+
+ let mut render_cx =
+ RenderContext::new(Some(this.workspace.clone()), window, cx)
+ .with_checkbox_clicked_callback(cx.listener(
+ move |this, e: &CheckboxClickedEvent, window, cx| {
+ if let Some(editor) = this
+ .active_editor
+ .as_ref()
+ .map(|s| s.editor.clone())
+ {
+ editor.update(cx, |editor, cx| {
+ let task_marker =
+ if e.checked() { "[x]" } else { "[ ]" };
+
+ editor.edit(
+ vec![(e.source_range(), task_marker)],
+ cx,
+ );
+ });
+ this.parse_markdown_from_active_editor(
+ false, window, cx,
+ );
+ cx.notify();
+ }
+ },
+ ));
+
+ let block = contents.children.get(ix).unwrap();
+ let rendered_block = render_markdown_block(block, &mut render_cx);
+
+ let should_apply_padding = Self::should_apply_padding_between(
+ block,
+ contents.children.get(ix + 1),
+ );
+
+ div()
+ .id(ix)
+ .when(should_apply_padding, |this| {
+ this.pb(render_cx.scaled_rems(0.75))
+ })
+ .group("markdown-block")
+ .on_click(cx.listener(
+ move |this, event: &ClickEvent, window, cx| {
+ if event.click_count() == 2 {
+ if let Some(source_range) = this
+ .contents
+ .as_ref()
+ .and_then(|c| c.children.get(ix))
+ .and_then(|block: &ParsedMarkdownElement| {
+ block.source_range()
+ })
+ {
+ this.move_cursor_to_block(
+ window,
+ cx,
+ source_range.start..source_range.start,
+ );
+ }
+ }
+ },
+ ))
+ .map(move |container| {
+ let indicator = div()
+ .h_full()
+ .w(px(4.0))
+ .when(ix == this.selected_block, |this| {
+ this.bg(cx.theme().colors().border)
+ })
+ .group_hover("markdown-block", |s| {
+ if ix == this.selected_block {
+ s
+ } else {
+ s.bg(cx.theme().colors().border_variant)
+ }
+ })
+ .rounded_xs();
+
+ container.child(
+ div()
+ .relative()
+ .child(
+ div()
+ .pl(render_cx.scaled_rems(1.0))
+ .child(rendered_block),
+ )
+ .child(indicator.absolute().left_0().top_0()),
+ )
+ })
+ .into_any()
+ }),
+ )
+ .size_full(),
+ )
+ }))
}
}
@@ -26,7 +26,22 @@ use ui::{
};
use workspace::{OpenOptions, OpenVisible, Workspace};
-type CheckboxClickedCallback = Arc<Box<dyn Fn(bool, Range<usize>, &mut Window, &mut App)>>;
+pub struct CheckboxClickedEvent {
+ pub checked: bool,
+ pub source_range: Range<usize>,
+}
+
+impl CheckboxClickedEvent {
+ pub fn source_range(&self) -> Range<usize> {
+ self.source_range.clone()
+ }
+
+ pub fn checked(&self) -> bool {
+ self.checked
+ }
+}
+
+type CheckboxClickedCallback = Arc<Box<dyn Fn(&CheckboxClickedEvent, &mut Window, &mut App)>>;
#[derive(Clone)]
pub struct RenderContext {
@@ -80,7 +95,7 @@ impl RenderContext {
pub fn with_checkbox_clicked_callback(
mut self,
- callback: impl Fn(bool, Range<usize>, &mut Window, &mut App) + 'static,
+ callback: impl Fn(&CheckboxClickedEvent, &mut Window, &mut App) + 'static,
) -> Self {
self.checkbox_clicked_callback = Some(Arc::new(Box::new(callback)));
self
@@ -229,7 +244,14 @@ fn render_markdown_list_item(
};
if window.modifiers().secondary() {
- callback(checked, range.clone(), window, cx);
+ callback(
+ &CheckboxClickedEvent {
+ checked,
+ source_range: range.clone(),
+ },
+ window,
+ cx,
+ );
}
}
})
@@ -292,7 +292,7 @@ impl<D: PickerDelegate> Picker<D> {
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
- let element_container = Self::create_element_container(container, cx);
+ let element_container = Self::create_element_container(container);
let scrollbar_state = match &element_container {
ElementContainer::UniformList(scroll_handle) => {
ScrollbarState::new(scroll_handle.clone())
@@ -323,31 +323,13 @@ impl<D: PickerDelegate> Picker<D> {
this
}
- fn create_element_container(
- container: ContainerKind,
- cx: &mut Context<Self>,
- ) -> ElementContainer {
+ fn create_element_container(container: ContainerKind) -> ElementContainer {
match container {
ContainerKind::UniformList => {
ElementContainer::UniformList(UniformListScrollHandle::new())
}
ContainerKind::List => {
- let entity = cx.entity().downgrade();
- ElementContainer::List(ListState::new(
- 0,
- gpui::ListAlignment::Top,
- px(1000.),
- move |ix, window, cx| {
- entity
- .upgrade()
- .map(|entity| {
- entity.update(cx, |this, cx| {
- this.render_element(window, cx, ix).into_any_element()
- })
- })
- .unwrap_or_else(|| div().into_any_element())
- },
- ))
+ ElementContainer::List(ListState::new(0, gpui::ListAlignment::Top, px(1000.)))
}
}
}
@@ -786,11 +768,16 @@ impl<D: PickerDelegate> Picker<D> {
.py_1()
.track_scroll(scroll_handle.clone())
.into_any_element(),
- ElementContainer::List(state) => list(state.clone())
- .with_sizing_behavior(sizing_behavior)
- .flex_grow()
- .py_2()
- .into_any_element(),
+ ElementContainer::List(state) => list(
+ state.clone(),
+ cx.processor(|this, ix, window, cx| {
+ this.render_element(window, cx, ix).into_any_element()
+ }),
+ )
+ .with_sizing_behavior(sizing_behavior)
+ .flex_grow()
+ .py_2()
+ .into_any_element(),
}
}
@@ -126,29 +126,7 @@ impl NotebookEditor {
let cell_count = cell_order.len();
let this = cx.entity();
- let cell_list = ListState::new(
- cell_count,
- gpui::ListAlignment::Top,
- px(1000.),
- move |ix, window, cx| {
- notebook_handle
- .upgrade()
- .and_then(|notebook_handle| {
- notebook_handle.update(cx, |notebook, cx| {
- notebook
- .cell_order
- .get(ix)
- .and_then(|cell_id| notebook.cell_map.get(cell_id))
- .map(|cell| {
- notebook
- .render_cell(ix, cell, window, cx)
- .into_any_element()
- })
- })
- })
- .unwrap_or_else(|| div().into_any())
- },
- );
+ let cell_list = ListState::new(cell_count, gpui::ListAlignment::Top, px(1000.));
Self {
project,
@@ -544,7 +522,19 @@ impl Render for NotebookEditor {
.flex_1()
.size_full()
.overflow_y_scroll()
- .child(list(self.cell_list.clone()).size_full()),
+ .child(list(
+ self.cell_list.clone(),
+ cx.processor(|this, ix, window, cx| {
+ this.cell_order
+ .get(ix)
+ .and_then(|cell_id| this.cell_map.get(cell_id))
+ .map(|cell| {
+ this.render_cell(ix, cell, window, cx).into_any_element()
+ })
+ .unwrap_or_else(|| div().into_any())
+ }),
+ ))
+ .size_full(),
)
.child(self.render_notebook_controls(window, cx))
}
@@ -115,21 +115,9 @@ impl ProjectIndexDebugView {
.collect::<Vec<_>>();
this.update(cx, |this, cx| {
- let view = cx.entity().downgrade();
this.selected_path = Some(PathState {
path: file_path,
- list_state: ListState::new(
- chunks.len(),
- gpui::ListAlignment::Top,
- px(100.),
- move |ix, _, cx| {
- if let Some(view) = view.upgrade() {
- view.update(cx, |view, cx| view.render_chunk(ix, cx))
- } else {
- div().into_any()
- }
- },
- ),
+ list_state: ListState::new(chunks.len(), gpui::ListAlignment::Top, px(100.)),
chunks,
});
cx.notify();
@@ -219,7 +207,13 @@ impl Render for ProjectIndexDebugView {
cx.notify();
})),
)
- .child(list(selected_path.list_state.clone()).size_full())
+ .child(
+ list(
+ selected_path.list_state.clone(),
+ cx.processor(|this, ix, _, cx| this.render_chunk(ix, cx)),
+ )
+ .size_full(),
+ )
.size_full()
.into_any_element()
} else {
@@ -107,6 +107,7 @@ struct ComponentPreview {
active_thread: Option<Entity<ActiveThread>>,
reset_key: usize,
component_list: ListState,
+ entries: Vec<PreviewEntry>,
component_map: HashMap<ComponentId, ComponentMetadata>,
components: Vec<ComponentMetadata>,
cursor_index: usize,
@@ -172,17 +173,6 @@ impl ComponentPreview {
sorted_components.len(),
gpui::ListAlignment::Top,
px(1500.0),
- {
- let this = cx.entity().downgrade();
- move |ix, window: &mut Window, cx: &mut App| {
- this.update(cx, |this, cx| {
- let component = this.get_component(ix);
- this.render_preview(&component, window, cx)
- .into_any_element()
- })
- .unwrap()
- }
- },
);
let mut component_preview = Self {
@@ -190,6 +180,7 @@ impl ComponentPreview {
active_thread: None,
reset_key: 0,
component_list,
+ entries: Vec::new(),
component_map: component_registry.component_map(),
components: sorted_components,
cursor_index: selected_index,
@@ -276,10 +267,6 @@ impl ComponentPreview {
cx.notify();
}
- fn get_component(&self, ix: usize) -> ComponentMetadata {
- self.components[ix].clone()
- }
-
fn filtered_components(&self) -> Vec<ComponentMetadata> {
if self.filter_text.is_empty() {
return self.components.clone();
@@ -420,7 +407,6 @@ impl ComponentPreview {
fn update_component_list(&mut self, cx: &mut Context<Self>) {
let entries = self.scope_ordered_entries();
let new_len = entries.len();
- let weak_entity = cx.entity().downgrade();
if new_len > 0 {
self.nav_scroll_handle
@@ -446,56 +432,9 @@ impl ComponentPreview {
}
}
- self.component_list = ListState::new(
- filtered_components.len(),
- gpui::ListAlignment::Top,
- px(1500.0),
- {
- let components = filtered_components.clone();
- let this = cx.entity().downgrade();
- move |ix, window: &mut Window, cx: &mut App| {
- if ix >= components.len() {
- return div().w_full().h_0().into_any_element();
- }
-
- this.update(cx, |this, cx| {
- let component = &components[ix];
- this.render_preview(component, window, cx)
- .into_any_element()
- })
- .unwrap()
- }
- },
- );
+ self.component_list = ListState::new(new_len, gpui::ListAlignment::Top, px(1500.0));
+ self.entries = entries;
- let new_list = ListState::new(
- new_len,
- gpui::ListAlignment::Top,
- px(1500.0),
- move |ix, window, cx| {
- if ix >= entries.len() {
- return div().w_full().h_0().into_any_element();
- }
-
- let entry = &entries[ix];
-
- weak_entity
- .update(cx, |this, cx| match entry {
- PreviewEntry::Component(component, _) => this
- .render_preview(component, window, cx)
- .into_any_element(),
- PreviewEntry::SectionHeader(shared_string) => this
- .render_scope_header(ix, shared_string.clone(), window, cx)
- .into_any_element(),
- PreviewEntry::AllComponents => div().w_full().h_0().into_any_element(),
- PreviewEntry::ActiveThread => div().w_full().h_0().into_any_element(),
- PreviewEntry::Separator => div().w_full().h_0().into_any_element(),
- })
- .unwrap()
- },
- );
-
- self.component_list = new_list;
cx.emit(ItemEvent::UpdateTab);
}
@@ -672,10 +611,35 @@ impl ComponentPreview {
.child(format!("No components matching '{}'.", self.filter_text))
.into_any_element()
} else {
- list(self.component_list.clone())
- .flex_grow()
- .with_sizing_behavior(gpui::ListSizingBehavior::Auto)
- .into_any_element()
+ list(
+ self.component_list.clone(),
+ cx.processor(|this, ix, window, cx| {
+ if ix >= this.entries.len() {
+ return div().w_full().h_0().into_any_element();
+ }
+
+ let entry = &this.entries[ix];
+
+ match entry {
+ PreviewEntry::Component(component, _) => this
+ .render_preview(component, window, cx)
+ .into_any_element(),
+ PreviewEntry::SectionHeader(shared_string) => this
+ .render_scope_header(ix, shared_string.clone(), window, cx)
+ .into_any_element(),
+ PreviewEntry::AllComponents => {
+ div().w_full().h_0().into_any_element()
+ }
+ PreviewEntry::ActiveThread => {
+ div().w_full().h_0().into_any_element()
+ }
+ PreviewEntry::Separator => div().w_full().h_0().into_any_element(),
+ }
+ }),
+ )
+ .flex_grow()
+ .with_sizing_behavior(gpui::ListSizingBehavior::Auto)
+ .into_any_element()
},
)
}