Simplify ListState
API (#35685)
Follow up to: https://github.com/zed-industries/zed/pull/35670, simplifies the List state APIs so you no longer have to worry about strong vs. weak pointers when rendering list items. Release Notes: - N/A --------- Co-authored-by: Agus Zubiaga <agus@zed.dev>
This commit is contained in:
parent
d0de81b0b4
commit
53175263a1
15 changed files with 322 additions and 403 deletions
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue