Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 64 additions & 46 deletions crates/story/examples/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use gpui_component::{
},
list::ListItem,
resizable::{h_resizable, resizable_panel},
tree::{TreeItem, TreeState, tree},
tree::{TreeDelegate, TreeEntry, TreeItem, TreeState, tree},
v_flex,
};
use gpui_component_assets::Assets;
Expand All @@ -44,9 +44,62 @@ fn init() {
);
}

struct EditorTreeDelegate {
view: Entity<Example>,
}

impl TreeDelegate for EditorTreeDelegate {
fn render_item(
&self,
ix: usize,
entry: &TreeEntry,
selected: bool,
window: &mut Window,
cx: &mut App,
) -> ListItem {
let item = entry.item();

let icon = if !entry.is_folder() {
IconName::File
} else if entry.is_expanded() {
IconName::FolderOpen
} else {
IconName::Folder
};

ListItem::new(ix)
.w_full()
.rounded(cx.theme().radius)
.py_0p5()
.px_2()
.pl(px(16.) * entry.depth() + px(8.))
.child(h_flex().gap_2().child(icon).child(item.label.clone()))
.selected(selected)
.on_click(window.listener_for(&self.view, {
let view_entity = self.view.clone();
let item = item.clone();
move |_this, _, _window, cx| {
if item.is_folder() {
return;
}

Example::open_file(
view_entity.clone(),
PathBuf::from(item.id.as_str()),
_window,
cx,
)
.ok();

cx.notify();
}
}))
}
}

pub struct Example {
editor: Entity<InputState>,
tree_state: Entity<TreeState>,
tree_state: Entity<TreeState<EditorTreeDelegate>>,
go_to_line_state: Entity<InputState>,
language: Language,
line_number: bool,
Expand Down Expand Up @@ -657,7 +710,13 @@ impl Example {
});
let go_to_line_state = cx.new(|cx| InputState::new(window, cx));

let tree_state = cx.new(|cx| TreeState::new(cx));
let view_entity = cx.entity();
let tree_state = cx.new(move |cx| {
TreeState::new(
EditorTreeDelegate { view: view_entity },
cx
)
});
Self::load_files(tree_state.clone(), PathBuf::from("./"), cx);

let _subscriptions = vec![cx.subscribe(&editor, |this, _, _: &InputEvent, cx| {
Expand All @@ -678,7 +737,7 @@ impl Example {
}
}

fn load_files(state: Entity<TreeState>, path: PathBuf, cx: &mut App) {
fn load_files(state: Entity<TreeState<EditorTreeDelegate>>, path: PathBuf, cx: &mut App) {
cx.spawn(async move |cx| {
let ignorer = Ignorer::new(&path.to_string_lossy());
let items = build_file_items(&ignorer, &path, &path);
Expand Down Expand Up @@ -849,48 +908,7 @@ impl Example {
}

fn render_file_tree(&self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let view = cx.entity();
tree(
&self.tree_state,
move |ix, entry, _selected, _window, cx| {
view.update(cx, |_, cx| {
let item = entry.item();
let icon = if !entry.is_folder() {
IconName::File
} else if entry.is_expanded() {
IconName::FolderOpen
} else {
IconName::Folder
};

ListItem::new(ix)
.w_full()
.rounded(cx.theme().radius)
.py_0p5()
.px_2()
.pl(px(16.) * entry.depth() + px(8.))
.child(h_flex().gap_2().child(icon).child(item.label.clone()))
.on_click(cx.listener({
let item = item.clone();
move |_, _, _window, cx| {
if item.is_folder() {
return;
}

Self::open_file(
cx.entity(),
PathBuf::from(item.id.as_str()),
_window,
cx,
)
.ok();

cx.notify();
}
}))
})
},
)
tree(&self.tree_state)
.text_sm()
.p_1()
.bg(cx.theme().sidebar)
Expand Down
114 changes: 72 additions & 42 deletions crates/story/src/tree_story.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,65 @@ use gpui_component::{
h_flex,
label::Label,
list::ListItem,
tree::{TreeItem, TreeState, tree},
tree::{TreeDelegate, TreeEntry, TreeItem, TreeState, tree},
v_flex,
};

use crate::{Story, section};

actions!(story, [Rename, SelectItem]);

struct FileTreeDelegate {
parent: Entity<TreeStory>,
}

impl TreeDelegate for FileTreeDelegate {
fn render_item(
&self,
ix: usize,
entry: &TreeEntry,
selected: bool,
window: &mut Window,
cx: &mut App,
) -> ListItem {
let item = entry.item();
let icon = if !entry.is_folder() {
IconName::File
} else if entry.is_expanded() {
IconName::FolderOpen
} else {
IconName::Folder
};

ListItem::new(ix)
.w_full()
.rounded(cx.theme().radius)
.px_3()
.pl(px(16.) * entry.depth() + px(12.))
.child(h_flex().gap_2().child(icon).child(item.label.clone()))
.selected(selected)
.on_click(window.listener_for(&self.parent, {
let item = item.clone();
move |this, _, _window, cx| {
this.selected_item = Some(item.clone());
cx.notify();
}
}))
}

fn context_menu(
&self,
ix: usize,
menu: gpui_component::menu::PopupMenu,
_window: &mut Window,
_cx: &mut App,
) -> gpui_component::menu::PopupMenu {
menu.label(format!("Selected Index: {}", ix))
.separator()
.menu("Rename", Box::new(Rename))
}
}

const CONTEXT: &str = "TreeStory";
pub(crate) fn init(cx: &mut App) {
cx.bind_keys([
Expand All @@ -29,7 +80,7 @@ pub(crate) fn init(cx: &mut App) {
}

pub struct TreeStory {
tree_state: Entity<TreeState>,
tree_state: Entity<TreeState<FileTreeDelegate>>,
selected_item: Option<TreeItem>,
}

Expand Down Expand Up @@ -71,7 +122,7 @@ impl TreeStory {
cx.new(|cx| Self::new(window, cx))
}

fn load_files(state: Entity<TreeState>, path: PathBuf, cx: &mut App) {
fn load_files(state: Entity<TreeState<FileTreeDelegate>>, path: PathBuf, cx: &mut App) {
cx.spawn(async move |cx| {
let ignorer = Ignorer::new(&path.to_string_lossy());
let items = build_file_items(&ignorer, &path, &path);
Expand All @@ -83,7 +134,15 @@ impl TreeStory {
}

fn new(_: &mut Window, cx: &mut Context<Self>) -> Self {
let tree_state = cx.new(|cx| TreeState::new(cx));
let parent_entity = cx.entity();
let tree_state = cx.new(move |cx| {
TreeState::new(
FileTreeDelegate {
parent: parent_entity,
},
cx,
)
});

Self::load_files(tree_state.clone(), PathBuf::from("./"), cx);

Expand Down Expand Up @@ -134,7 +193,6 @@ impl Render for TreeStory {
_: &mut gpui::Window,
cx: &mut gpui::Context<Self>,
) -> impl gpui::IntoElement {
let view = cx.entity();
v_flex()
.id("tree-story")
.key_context(CONTEXT)
Expand All @@ -144,46 +202,18 @@ impl Render for TreeStory {
.size_full()
.child(
section("File tree")
.sub_title("Press `space` to select, `enter` to rename.")
.sub_title(
"Press `space` to select, `enter` to rename, right-click for context menu.",
)
.v_flex()
.max_w_md()
.child(
tree(
&self.tree_state,
move |ix, entry, _selected, _window, cx| {
view.update(cx, |_, cx| {
let item = entry.item();
let icon = if !entry.is_folder() {
IconName::File
} else if entry.is_expanded() {
IconName::FolderOpen
} else {
IconName::Folder
};

ListItem::new(ix)
.w_full()
.rounded(cx.theme().radius)
.px_3()
.pl(px(16.) * entry.depth() + px(12.))
.child(
h_flex().gap_2().child(icon).child(item.label.clone()),
)
.on_click(cx.listener({
let item = item.clone();
move |this, _, _window, cx| {
this.selected_item = Some(item.clone());
cx.notify();
}
}))
})
},
)
.p_1()
.border_1()
.border_color(cx.theme().border)
.rounded(cx.theme().radius)
.h(px(540.)),
tree(&self.tree_state)
.p_1()
.border_1()
.border_color(cx.theme().border)
.rounded(cx.theme().radius)
.h(px(540.)),
)
.child(
h_flex()
Expand Down
Loading