Merge remote-tracking branch 'origin/callback-handles' into search2

This commit is contained in:
Piotr Osiewicz 2023-11-21 00:40:20 +01:00
commit 54a3b56935
118 changed files with 2175 additions and 6954 deletions

69
Cargo.lock generated
View File

@ -841,17 +841,6 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "backtrace-on-stack-overflow"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd2d70527f3737a1ad17355e260706c1badebabd1fa06a7a053407380df841b"
dependencies = [
"backtrace",
"libc",
"nix 0.23.2",
]
[[package]]
name = "base64"
version = "0.13.1"
@ -1398,7 +1387,7 @@ dependencies = [
"smol",
"sum_tree",
"tempfile",
"text",
"text2",
"thiserror",
"time",
"tiny_http",
@ -1869,7 +1858,7 @@ dependencies = [
"editor2",
"feature_flags2",
"futures 0.3.28",
"fuzzy",
"fuzzy2",
"gpui2",
"language2",
"lazy_static",
@ -5570,19 +5559,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "nix"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
dependencies = [
"bitflags 1.3.2",
"cc",
"cfg-if 1.0.0",
"libc",
"memoffset 0.6.5",
]
[[package]]
name = "nix"
version = "0.24.3"
@ -6763,7 +6739,6 @@ dependencies = [
"anyhow",
"client2",
"collections",
"context_menu",
"db2",
"editor2",
"futures 0.3.28",
@ -8884,45 +8859,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "storybook2"
version = "0.1.0"
dependencies = [
"anyhow",
"backtrace-on-stack-overflow",
"chrono",
"clap 4.4.4",
"editor2",
"fuzzy2",
"gpui2",
"itertools 0.11.0",
"language2",
"log",
"menu2",
"picker2",
"rust-embed",
"serde",
"settings2",
"simplelog",
"smallvec",
"strum",
"theme",
"theme2",
"ui2",
"util",
]
[[package]]
name = "storybook3"
version = "0.1.0"
dependencies = [
"anyhow",
"gpui2",
"settings2",
"theme2",
"ui2",
]
[[package]]
name = "stringprep"
version = "0.1.4"
@ -11663,7 +11599,6 @@ dependencies = [
"fs2",
"fsevent",
"futures 0.3.28",
"fuzzy",
"go_to_line2",
"gpui2",
"ignore",

View File

@ -97,8 +97,8 @@ members = [
"crates/sqlez",
"crates/sqlez_macros",
"crates/rich_text",
"crates/storybook2",
"crates/storybook3",
# "crates/storybook2",
# "crates/storybook3",
"crates/sum_tree",
"crates/terminal",
"crates/terminal2",

View File

@ -8,8 +8,8 @@ pub struct UpdateNotification {
impl EventEmitter<NotificationEvent> for UpdateNotification {}
impl Render<Self> for UpdateNotification {
type Element = Div<Self>;
impl Render for UpdateNotification {
type Element = Div;
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> Self::Element {
div().child("Updated zed!")

View File

@ -18,7 +18,7 @@ db = { package = "db2", path = "../db2" }
gpui = { package = "gpui2", path = "../gpui2" }
util = { path = "../util" }
rpc = { package = "rpc2", path = "../rpc2" }
text = { path = "../text" }
text = { package = "text2", path = "../text2" }
language = { package = "language2", path = "../language2" }
settings = { package = "settings2", path = "../settings2" }
feature_flags = { package = "feature_flags2", path = "../feature_flags2" }

View File

@ -33,7 +33,7 @@ collections = { path = "../collections" }
# drag_and_drop = { path = "../drag_and_drop" }
editor = { package="editor2", path = "../editor2" }
#feedback = { path = "../feedback" }
fuzzy = { path = "../fuzzy" }
fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
gpui = { package = "gpui2", path = "../gpui2" }
language = { package = "language2", path = "../language2" }
menu = { package = "menu2", path = "../menu2" }

View File

@ -3294,8 +3294,8 @@ impl CollabPanel {
// .with_width(size.x())
// }
impl Render<Self> for CollabPanel {
type Element = Focusable<Self, Div<Self>>;
impl Render for CollabPanel {
type Element = Focusable<Div>;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
div()

View File

@ -81,8 +81,8 @@ pub struct CollabTitlebarItem {
_subscriptions: Vec<Subscription>,
}
impl Render<Self> for CollabTitlebarItem {
type Element = Stateful<Self, Div<Self>>;
impl Render for CollabTitlebarItem {
type Element = Stateful<Div>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
h_stack()
@ -100,7 +100,7 @@ impl Render<Self> for CollabTitlebarItem {
|s| s.pl(px(68.)),
)
.bg(cx.theme().colors().title_bar_background)
.on_click(|_, event, cx| {
.on_click(|event, cx| {
if event.up.click_count == 2 {
cx.zoom_window();
}
@ -117,14 +117,14 @@ impl Render<Self> for CollabTitlebarItem {
.variant(ButtonVariant::Ghost)
.color(Some(TextColor::Player(0))),
)
.tooltip(move |_, cx| Tooltip::text("Toggle following", cx)),
.tooltip(move |cx| Tooltip::text("Toggle following", cx)),
)
// TODO - Add project menu
.child(
div()
.id("titlebar_project_menu_button")
.child(Button::new("project_name").variant(ButtonVariant::Ghost))
.tooltip(move |_, cx| Tooltip::text("Recent Projects", cx)),
.tooltip(move |cx| Tooltip::text("Recent Projects", cx)),
)
// TODO - Add git menu
.child(
@ -135,7 +135,7 @@ impl Render<Self> for CollabTitlebarItem {
.variant(ButtonVariant::Ghost)
.color(Some(TextColor::Muted)),
)
.tooltip(move |_, cx| {
.tooltip(move |cx| {
cx.build_view(|_| {
Tooltip::new("Recent Branches")
.key_binding(KeyBinding::new(gpui::KeyBinding::new(

View File

@ -76,8 +76,8 @@ impl FocusableView for CommandPalette {
}
}
impl Render<Self> for CommandPalette {
type Element = Div<Self>;
impl Render for CommandPalette {
type Element = Div;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
v_stack().w_96().child(self.picker.clone())
@ -140,7 +140,7 @@ impl CommandPaletteDelegate {
}
impl PickerDelegate for CommandPaletteDelegate {
type ListItem = Div<Picker<Self>>;
type ListItem = Div;
fn placeholder_text(&self) -> Arc<str> {
"Execute a command...".into()

View File

@ -16,7 +16,7 @@ use gpui::{
actions, div, AnyElement, AnyView, AppContext, Context, Div, EventEmitter, FocusEvent,
FocusHandle, Focusable, FocusableElement, FocusableView, InteractiveElement, Model,
ParentElement, Render, RenderOnce, SharedString, Styled, Subscription, Task, View, ViewContext,
VisualContext, WeakView,
VisualContext, WeakView, WindowContext,
};
use language::{
Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection,
@ -90,8 +90,8 @@ struct DiagnosticGroupState {
impl EventEmitter<ItemEvent> for ProjectDiagnosticsEditor {}
impl Render<Self> for ProjectDiagnosticsEditor {
type Element = Focusable<Self, Div<Self>>;
impl Render for ProjectDiagnosticsEditor {
type Element = Focusable<Div>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let child = if self.path_states.is_empty() {
@ -109,8 +109,8 @@ impl Render<Self> for ProjectDiagnosticsEditor {
div()
.track_focus(&self.focus_handle)
.size_full()
.on_focus_in(Self::focus_in)
.on_action(Self::toggle_warnings)
.on_focus_in(cx.listener(Self::focus_in))
.on_action(cx.listener(Self::toggle_warnings))
.child(child)
}
}
@ -662,7 +662,7 @@ impl Item for ProjectDiagnosticsEditor {
Some("Project Diagnostics".into())
}
fn tab_content<T: 'static>(&self, _detail: Option<usize>, _: &AppContext) -> AnyElement<T> {
fn tab_content(&self, _detail: Option<usize>, _: &WindowContext) -> AnyElement {
render_summary(&self.summary)
}
@ -796,11 +796,10 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
})
}
pub(crate) fn render_summary<T: 'static>(summary: &DiagnosticSummary) -> AnyElement<T> {
pub(crate) fn render_summary(summary: &DiagnosticSummary) -> AnyElement {
if summary.error_count == 0 && summary.warning_count == 0 {
let label = Label::new("No problems");
label.render_into_any()
//.render()
} else {
h_stack()
.bg(gpui::red())

View File

@ -21,8 +21,8 @@ pub struct DiagnosticIndicator {
_observe_active_editor: Option<Subscription>,
}
impl Render<Self> for DiagnosticIndicator {
type Element = Stateful<Self, Div<Self>>;
impl Render for DiagnosticIndicator {
type Element = Stateful<Div>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) {
@ -45,7 +45,7 @@ impl Render<Self> for DiagnosticIndicator {
h_stack()
.id(cx.entity_id())
.on_action(Self::go_to_next_diagnostic)
.on_action(cx.listener(Self::go_to_next_diagnostic))
.rounded_md()
.flex_none()
.h(rems(1.375))
@ -54,14 +54,14 @@ impl Render<Self> for DiagnosticIndicator {
.bg(cx.theme().colors().ghost_element_background)
.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
.active(|style| style.bg(cx.theme().colors().ghost_element_active))
.tooltip(|_, cx| Tooltip::text("Project Diagnostics", cx))
.on_click(|this, _, cx| {
.tooltip(|cx| Tooltip::text("Project Diagnostics", cx))
.on_click(cx.listener(|this, _, cx| {
if let Some(workspace) = this.workspace.upgrade() {
workspace.update(cx, |workspace, cx| {
ProjectDiagnosticsEditor::deploy(workspace, &Default::default(), cx)
})
}
})
}))
.child(diagnostic_indicator)
}
}

View File

@ -7,8 +7,8 @@ pub struct ToolbarControls {
editor: Option<WeakView<ProjectDiagnosticsEditor>>,
}
impl Render<Self> for ToolbarControls {
type Element = Div<Self>;
impl Render for ToolbarControls {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let include_warnings = self
@ -26,14 +26,14 @@ impl Render<Self> for ToolbarControls {
div().child(
IconButton::new("toggle-warnings", Icon::ExclamationTriangle)
.tooltip(move |_, cx| Tooltip::text(tooltip, cx))
.on_click(|this: &mut Self, cx| {
.tooltip(move |cx| Tooltip::text(tooltip, cx))
.on_click(cx.listener(|this, _, cx| {
if let Some(editor) = this.editor.as_ref().and_then(|editor| editor.upgrade()) {
editor.update(cx, |editor, cx| {
editor.toggle_warnings(&Default::default(), cx);
});
}
}),
})),
)
}
}

View File

@ -50,7 +50,7 @@ struct BlockRow(u32);
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
struct WrapRow(u32);
pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement<Editor>>;
pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement>;
pub struct Block {
id: BlockId,
@ -69,7 +69,7 @@ where
pub position: P,
pub height: u8,
pub style: BlockStyle,
pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement<Editor>>,
pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement>,
pub disposition: BlockDisposition,
}
@ -947,7 +947,7 @@ impl DerefMut for BlockContext<'_, '_> {
}
impl Block {
pub fn render(&self, cx: &mut BlockContext) -> AnyElement<Editor> {
pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
self.render.lock()(cx)
}

View File

@ -907,7 +907,7 @@ impl ContextMenu {
style: &EditorStyle,
workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>,
) -> (DisplayPoint, AnyElement<Editor>) {
) -> (DisplayPoint, AnyElement) {
match self {
ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)),
ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
@ -1223,7 +1223,7 @@ impl CompletionsMenu {
style: &EditorStyle,
workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>,
) -> AnyElement<Editor> {
) -> AnyElement {
todo!("old implementation below")
}
@ -1541,13 +1541,15 @@ impl CodeActionsMenu {
mut cursor_position: DisplayPoint,
style: &EditorStyle,
cx: &mut ViewContext<Editor>,
) -> (DisplayPoint, AnyElement<Editor>) {
) -> (DisplayPoint, AnyElement) {
let actions = self.actions.clone();
let selected_item = self.selected_item;
let element = uniform_list(
cx.view().clone(),
"code_actions_menu",
self.actions.len(),
move |editor, range, cx| {
move |this, range, cx| {
actions[range.clone()]
.iter()
.enumerate()
@ -1569,17 +1571,20 @@ impl CodeActionsMenu {
.bg(colors.element_hover)
.text_color(colors.text_accent)
})
.on_mouse_down(MouseButton::Left, move |editor: &mut Editor, _, cx| {
cx.stop_propagation();
editor
.confirm_code_action(
&ConfirmCodeAction {
item_ix: Some(item_ix),
},
cx,
)
.map(|task| task.detach_and_log_err(cx));
})
.on_mouse_down(
MouseButton::Left,
cx.listener(move |editor, _, cx| {
cx.stop_propagation();
editor
.confirm_code_action(
&ConfirmCodeAction {
item_ix: Some(item_ix),
},
cx,
)
.map(|task| task.detach_and_log_err(cx));
}),
)
// TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
.child(SharedString::from(action.lsp_action.title.clone()))
})
@ -4355,11 +4360,11 @@ impl Editor {
style: &EditorStyle,
is_active: bool,
cx: &mut ViewContext<Self>,
) -> Option<IconButton<Self>> {
) -> Option<IconButton> {
if self.available_code_actions.is_some() {
Some(
IconButton::new("code_actions_indicator", ui::Icon::Bolt).on_click(
|editor: &mut Editor, cx| {
IconButton::new("code_actions_indicator", ui::Icon::Bolt).on_click(cx.listener(
|editor, e, cx| {
editor.toggle_code_actions(
&ToggleCodeActions {
deployed_from_indicator: true,
@ -4367,7 +4372,7 @@ impl Editor {
cx,
);
},
),
)),
)
} else {
None
@ -4382,7 +4387,7 @@ impl Editor {
line_height: Pixels,
gutter_margin: Pixels,
cx: &mut ViewContext<Self>,
) -> Vec<Option<IconButton<Self>>> {
) -> Vec<Option<IconButton>> {
fold_data
.iter()
.enumerate()
@ -4395,14 +4400,14 @@ impl Editor {
FoldStatus::Foldable => ui::Icon::ChevronDown,
};
IconButton::new(ix as usize, icon)
.on_click(move |editor: &mut Editor, cx| match fold_status {
.on_click(cx.listener(move |editor, e, cx| match fold_status {
FoldStatus::Folded => {
editor.unfold_at(&UnfoldAt { buffer_row }, cx);
}
FoldStatus::Foldable => {
editor.fold_at(&FoldAt { buffer_row }, cx);
}
})
}))
.color(ui::TextColor::Muted)
})
})
@ -4423,7 +4428,7 @@ impl Editor {
cursor_position: DisplayPoint,
style: &EditorStyle,
cx: &mut ViewContext<Editor>,
) -> Option<(DisplayPoint, AnyElement<Editor>)> {
) -> Option<(DisplayPoint, AnyElement)> {
self.context_menu.read().as_ref().map(|menu| {
menu.render(
cursor_position,
@ -7782,7 +7787,7 @@ impl Editor {
}
div()
.pl(cx.anchor_x)
.child(rename_editor.render_with(EditorElement::new(
.child(EditorElement::new(
&rename_editor,
EditorStyle {
background: cx.theme().system().transparent,
@ -7790,10 +7795,12 @@ impl Editor {
text: text_style,
scrollbar_width: cx.editor_style.scrollbar_width,
syntax: cx.editor_style.syntax.clone(),
diagnostic_style:
cx.editor_style.diagnostic_style.clone(),
diagnostic_style: cx
.editor_style
.diagnostic_style
.clone(),
},
)))
))
.render_into_any()
}
}),
@ -9383,7 +9390,7 @@ impl FocusableView for Editor {
}
}
impl Render<Self> for Editor {
impl Render for Editor {
type Element = EditorElement;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@ -9997,10 +10004,10 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
.ml(cx.anchor_x)
}))
.cursor_pointer()
.on_click(move |_, _, cx| {
.on_click(cx.listener(move |_, _, cx| {
cx.write_to_clipboard(ClipboardItem::new(message.clone()));
})
.tooltip(|_, cx| Tooltip::text("Copy diagnostic message", cx))
}))
.tooltip(|cx| Tooltip::text("Copy diagnostic message", cx))
.render_into_any()
})
}

View File

@ -23,7 +23,7 @@ use gpui::{
ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveElement, LineLayout,
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, RenderOnce,
ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
TextRun, TextStyle, View, ViewContext, WindowContext, WrappedLine,
TextRun, TextStyle, View, ViewContext, WeakView, WindowContext, WrappedLine,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@ -112,14 +112,14 @@ impl SelectionLayout {
}
pub struct EditorElement {
editor_id: EntityId,
editor: View<Editor>,
style: EditorStyle,
}
impl EditorElement {
pub fn new(editor: &View<Editor>, style: EditorStyle) -> Self {
Self {
editor_id: editor.entity_id(),
editor: editor.clone(),
style,
}
}
@ -349,7 +349,7 @@ impl EditorElement {
gutter_bounds: Bounds<Pixels>,
text_bounds: Bounds<Pixels>,
layout: &LayoutState,
cx: &mut ViewContext<Editor>,
cx: &mut WindowContext,
) {
let bounds = gutter_bounds.union(&text_bounds);
let scroll_top =
@ -460,7 +460,7 @@ impl EditorElement {
bounds: Bounds<Pixels>,
layout: &mut LayoutState,
editor: &mut Editor,
cx: &mut ViewContext<Editor>,
cx: &mut WindowContext,
) {
let line_height = layout.position_map.line_height;
@ -495,7 +495,7 @@ impl EditorElement {
AvailableSpace::MinContent,
AvailableSpace::Definite(line_height * 0.55),
);
let fold_indicator_size = fold_indicator.measure(available_space, editor, cx);
let fold_indicator_size = fold_indicator.measure(available_space, cx);
let position = point(
bounds.size.width - layout.gutter_padding,
@ -506,7 +506,7 @@ impl EditorElement {
(line_height - fold_indicator_size.height) / 2.,
);
let origin = bounds.origin + position + centering_offset;
fold_indicator.draw(origin, available_space, editor, cx);
fold_indicator.draw(origin, available_space, cx);
}
}
@ -516,7 +516,7 @@ impl EditorElement {
AvailableSpace::MinContent,
AvailableSpace::Definite(line_height),
);
let indicator_size = button.measure(available_space, editor, cx);
let indicator_size = button.measure(available_space, cx);
let mut x = Pixels::ZERO;
let mut y = indicator.row as f32 * line_height - scroll_top;
@ -524,15 +524,11 @@ impl EditorElement {
x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.;
y += (line_height - indicator_size.height) / 2.;
button.draw(bounds.origin + point(x, y), available_space, editor, cx);
button.draw(bounds.origin + point(x, y), available_space, cx);
}
}
fn paint_diff_hunks(
bounds: Bounds<Pixels>,
layout: &LayoutState,
cx: &mut ViewContext<Editor>,
) {
fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
// todo!()
// let diff_style = &theme::current(cx).editor.diff.clone();
// let line_height = layout.position_map.line_height;
@ -621,7 +617,7 @@ impl EditorElement {
text_bounds: Bounds<Pixels>,
layout: &mut LayoutState,
editor: &mut Editor,
cx: &mut ViewContext<Editor>,
cx: &mut WindowContext,
) {
let scroll_position = layout.position_map.snapshot.scroll_position();
let start_row = layout.visible_display_row_range.start;
@ -676,20 +672,22 @@ impl EditorElement {
div()
.id(fold.id)
.size_full()
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
.on_click(move |editor: &mut Editor, _, cx| {
editor.unfold_ranges(
[fold_range.start..fold_range.end],
true,
false,
cx,
);
cx.stop_propagation();
})
.on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
.on_click(cx.listener_for(
&self.editor,
move |editor: &mut Editor, _, cx| {
editor.unfold_ranges(
[fold_range.start..fold_range.end],
true,
false,
cx,
);
cx.stop_propagation();
},
))
.draw(
fold_bounds.origin,
fold_bounds.size,
editor,
cx,
|fold_element_state, cx| {
if fold_element_state.is_active() {
@ -852,7 +850,7 @@ impl EditorElement {
.min((text_bounds.size.height - line_height) / 2.),
),
);
let context_menu_size = context_menu.measure(available_space, editor, cx);
let context_menu_size = context_menu.measure(available_space, cx);
let cursor_row_layout = &layout.position_map.line_layouts
[(position.row() - start_row) as usize]
@ -876,7 +874,7 @@ impl EditorElement {
list_origin.y -= layout.position_map.line_height - list_height;
}
context_menu.draw(list_origin, available_space, editor, cx);
context_menu.draw(list_origin, available_space, cx);
})
}
@ -1167,7 +1165,7 @@ impl EditorElement {
layout: &LayoutState,
content_origin: gpui::Point<Pixels>,
bounds: Bounds<Pixels>,
cx: &mut ViewContext<Editor>,
cx: &mut WindowContext,
) {
let start_row = layout.visible_display_row_range.start;
let end_row = layout.visible_display_row_range.end;
@ -1220,7 +1218,7 @@ impl EditorElement {
bounds: Bounds<Pixels>,
layout: &mut LayoutState,
editor: &mut Editor,
cx: &mut ViewContext<Editor>,
cx: &mut WindowContext,
) {
let scroll_position = layout.position_map.snapshot.scroll_position();
let scroll_left = scroll_position.x * layout.position_map.em_width;
@ -1235,9 +1233,7 @@ impl EditorElement {
if !matches!(block.style, BlockStyle::Sticky) {
origin += point(-scroll_left, Pixels::ZERO);
}
block
.element
.draw(origin, block.available_space, editor, cx);
block.element.draw(origin, block.available_space, cx);
}
}
@ -2030,12 +2026,10 @@ impl EditorElement {
let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
IconButton::new(block_id, ui::Icon::ArrowUpRight)
.on_click(move |editor: &mut Editor, cx| {
.on_click(cx.listener_for(&self.editor, move |editor, e, cx| {
editor.jump(jump_path.clone(), jump_position, jump_anchor, cx);
})
.tooltip(move |_, cx| {
Tooltip::for_action("Jump to Buffer", &OpenExcerpts, cx)
})
}))
.tooltip(|cx| Tooltip::for_action("Jump to Buffer", &OpenExcerpts, cx))
});
let element = if *starts_new_buffer {
@ -2074,7 +2068,7 @@ impl EditorElement {
}
};
let size = element.measure(available_space, editor, cx);
let size = element.measure(available_space, cx);
(element, size)
};
@ -2133,47 +2127,61 @@ impl EditorElement {
gutter_bounds: Bounds<Pixels>,
text_bounds: Bounds<Pixels>,
layout: &LayoutState,
cx: &mut ViewContext<Editor>,
cx: &mut WindowContext,
) {
let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
cx.on_mouse_event({
let position_map = layout.position_map.clone();
move |editor, event: &ScrollWheelEvent, phase, cx| {
let editor = self.editor.clone();
move |event: &ScrollWheelEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
if Self::scroll(editor, event, &position_map, bounds, cx) {
let should_cancel = editor.update(cx, |editor, cx| {
Self::scroll(editor, event, &position_map, bounds, cx)
});
if should_cancel {
cx.stop_propagation();
}
}
});
cx.on_mouse_event({
let position_map = layout.position_map.clone();
move |editor, event: &MouseDownEvent, phase, cx| {
let editor = self.editor.clone();
move |event: &MouseDownEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
if Self::mouse_down(editor, event, &position_map, text_bounds, gutter_bounds, cx) {
let should_cancel = editor.update(cx, |editor, cx| {
Self::mouse_down(editor, event, &position_map, text_bounds, gutter_bounds, cx)
});
if should_cancel {
cx.stop_propagation()
}
}
});
cx.on_mouse_event({
let position_map = layout.position_map.clone();
move |editor, event: &MouseUpEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
let editor = self.editor.clone();
move |event: &MouseUpEvent, phase, cx| {
let should_cancel = editor.update(cx, |editor, cx| {
Self::mouse_up(editor, event, &position_map, text_bounds, cx)
});
if Self::mouse_up(editor, event, &position_map, text_bounds, cx) {
if should_cancel {
cx.stop_propagation()
}
}
});
// todo!()
//todo!()
// on_down(MouseButton::Right, {
// let position_map = layout.position_map.clone();
// move |event, editor, cx| {
@ -2190,12 +2198,17 @@ impl EditorElement {
// });
cx.on_mouse_event({
let position_map = layout.position_map.clone();
move |editor, event: &MouseMoveEvent, phase, cx| {
let editor = self.editor.clone();
move |event: &MouseMoveEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
if Self::mouse_moved(editor, event, &position_map, text_bounds, gutter_bounds, cx) {
let stop_propogating = editor.update(cx, |editor, cx| {
Self::mouse_moved(editor, event, &position_map, text_bounds, gutter_bounds, cx)
});
if stop_propogating {
cx.stop_propagation()
}
}
@ -2324,7 +2337,7 @@ impl LineWithInvisibles {
content_origin: gpui::Point<Pixels>,
whitespace_setting: ShowWhitespaceSetting,
selection_ranges: &[Range<DisplayPoint>],
cx: &mut ViewContext<Editor>,
cx: &mut WindowContext,
) {
let line_height = layout.position_map.line_height;
let line_y = line_height * row as f32 - layout.position_map.scroll_position.y;
@ -2356,7 +2369,7 @@ impl LineWithInvisibles {
row: u32,
line_height: Pixels,
whitespace_setting: ShowWhitespaceSetting,
cx: &mut ViewContext<Editor>,
cx: &mut WindowContext,
) {
let allowed_invisibles_regions = match whitespace_setting {
ShowWhitespaceSetting::None => return,
@ -2399,85 +2412,98 @@ enum Invisible {
Whitespace { line_offset: usize },
}
impl Element<Editor> for EditorElement {
impl Element for EditorElement {
type State = ();
fn layout(
&mut self,
editor: &mut Editor,
element_state: Option<Self::State>,
cx: &mut gpui::ViewContext<Editor>,
cx: &mut gpui::WindowContext,
) -> (gpui::LayoutId, Self::State) {
editor.style = Some(self.style.clone()); // Long-term, we'd like to eliminate this.
self.editor.update(cx, |editor, cx| {
editor.style = Some(self.style.clone()); // Long-term, we'd like to eliminate this.
let rem_size = cx.rem_size();
let mut style = Style::default();
style.size.width = relative(1.).into();
style.size.height = match editor.mode {
EditorMode::SingleLine => self.style.text.line_height_in_pixels(cx.rem_size()).into(),
EditorMode::AutoHeight { .. } => todo!(),
EditorMode::Full => relative(1.).into(),
};
let layout_id = cx.request_layout(&style, None);
(layout_id, ())
let rem_size = cx.rem_size();
let mut style = Style::default();
style.size.width = relative(1.).into();
style.size.height = match editor.mode {
EditorMode::SingleLine => {
self.style.text.line_height_in_pixels(cx.rem_size()).into()
}
EditorMode::AutoHeight { .. } => todo!(),
EditorMode::Full => relative(1.).into(),
};
let layout_id = cx.request_layout(&style, None);
(layout_id, ())
})
}
fn paint(
mut self,
bounds: Bounds<gpui::Pixels>,
editor: &mut Editor,
element_state: &mut Self::State,
cx: &mut gpui::ViewContext<Editor>,
cx: &mut gpui::WindowContext,
) {
let mut layout = self.compute_layout(editor, cx, bounds);
let gutter_bounds = Bounds {
origin: bounds.origin,
size: layout.gutter_size,
};
let text_bounds = Bounds {
origin: gutter_bounds.upper_right(),
size: layout.text_size,
};
let editor = self.editor.clone();
editor.update(cx, |editor, cx| {
let mut layout = self.compute_layout(editor, cx, bounds);
let gutter_bounds = Bounds {
origin: bounds.origin,
size: layout.gutter_size,
};
let text_bounds = Bounds {
origin: gutter_bounds.upper_right(),
size: layout.text_size,
};
let dispatch_context = editor.dispatch_context(cx);
cx.with_key_dispatch(
dispatch_context,
Some(editor.focus_handle.clone()),
|_, cx| {
register_actions(cx);
let dispatch_context = editor.dispatch_context(cx);
let editor_handle = cx.view().clone();
cx.with_key_dispatch(
dispatch_context,
Some(editor.focus_handle.clone()),
|_, cx| {
register_actions(&editor_handle, cx);
// We call with_z_index to establish a new stacking context.
cx.with_z_index(0, |cx| {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
// Paint mouse listeners first, so any elements we paint on top of the editor
// take precedence.
self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx);
let input_handler = ElementInputHandler::new(bounds, cx);
cx.handle_input(&editor.focus_handle, input_handler);
// We call with_z_index to establish a new stacking context.
cx.with_z_index(0, |cx| {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
// Paint mouse listeners first, so any elements we paint on top of the editor
// take precedence.
self.paint_mouse_listeners(
bounds,
gutter_bounds,
text_bounds,
&layout,
cx,
);
let input_handler = ElementInputHandler::new(bounds, editor_handle, cx);
cx.handle_input(&editor.focus_handle, input_handler);
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
if layout.gutter_size.width > Pixels::ZERO {
self.paint_gutter(gutter_bounds, &mut layout, editor, cx);
}
self.paint_text(text_bounds, &mut layout, editor, cx);
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
if layout.gutter_size.width > Pixels::ZERO {
self.paint_gutter(gutter_bounds, &mut layout, editor, cx);
}
self.paint_text(text_bounds, &mut layout, editor, cx);
if !layout.blocks.is_empty() {
cx.with_element_id(Some("editor_blocks"), |cx| {
self.paint_blocks(bounds, &mut layout, editor, cx);
})
}
if !layout.blocks.is_empty() {
cx.with_element_id(Some("editor_blocks"), |cx| {
self.paint_blocks(bounds, &mut layout, editor, cx);
})
}
});
});
});
},
)
},
)
})
}
}
impl RenderOnce<Editor> for EditorElement {
impl RenderOnce for EditorElement {
type Element = Self;
fn element_id(&self) -> Option<gpui::ElementId> {
Some(self.editor_id.into())
self.editor.element_id()
}
fn render_once(self) -> Self::Element {
@ -3106,17 +3132,17 @@ pub struct LayoutState {
show_scrollbars: bool,
is_singleton: bool,
max_row: u32,
context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
context_menu: Option<(DisplayPoint, AnyElement)>,
code_actions_indicator: Option<CodeActionsIndicator>,
// hover_popovers: Option<(DisplayPoint, Vec<AnyElement<Editor>>)>,
fold_indicators: Vec<Option<IconButton<Editor>>>,
// hover_popovers: Option<(DisplayPoint, Vec<AnyElement>)>,
fold_indicators: Vec<Option<IconButton>>,
tab_invisible: ShapedLine,
space_invisible: ShapedLine,
}
struct CodeActionsIndicator {
row: u32,
button: IconButton<Editor>,
button: IconButton,
}
struct PositionMap {
@ -3201,7 +3227,7 @@ impl PositionMap {
struct BlockLayout {
row: u32,
element: AnyElement<Editor>,
element: AnyElement,
available_space: Size<AvailableSpace>,
style: BlockStyle,
}
@ -3906,187 +3932,191 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
// }
// }
fn register_actions(cx: &mut ViewContext<Editor>) {
register_action(cx, Editor::move_left);
register_action(cx, Editor::move_right);
register_action(cx, Editor::move_down);
register_action(cx, Editor::move_up);
fn register_actions(view: &View<Editor>, cx: &mut WindowContext) {
register_action(view, cx, Editor::move_left);
register_action(view, cx, Editor::move_right);
register_action(view, cx, Editor::move_down);
register_action(view, cx, Editor::move_up);
// on_action(cx, Editor::new_file); todo!()
// on_action(cx, Editor::new_file_in_direction); todo!()
register_action(cx, Editor::cancel);
register_action(cx, Editor::newline);
register_action(cx, Editor::newline_above);
register_action(cx, Editor::newline_below);
register_action(cx, Editor::backspace);
register_action(cx, Editor::delete);
register_action(cx, Editor::tab);
register_action(cx, Editor::tab_prev);
register_action(cx, Editor::indent);
register_action(cx, Editor::outdent);
register_action(cx, Editor::delete_line);
register_action(cx, Editor::join_lines);
register_action(cx, Editor::sort_lines_case_sensitive);
register_action(cx, Editor::sort_lines_case_insensitive);
register_action(cx, Editor::reverse_lines);
register_action(cx, Editor::shuffle_lines);
register_action(cx, Editor::convert_to_upper_case);
register_action(cx, Editor::convert_to_lower_case);
register_action(cx, Editor::convert_to_title_case);
register_action(cx, Editor::convert_to_snake_case);
register_action(cx, Editor::convert_to_kebab_case);
register_action(cx, Editor::convert_to_upper_camel_case);
register_action(cx, Editor::convert_to_lower_camel_case);
register_action(cx, Editor::delete_to_previous_word_start);
register_action(cx, Editor::delete_to_previous_subword_start);
register_action(cx, Editor::delete_to_next_word_end);
register_action(cx, Editor::delete_to_next_subword_end);
register_action(cx, Editor::delete_to_beginning_of_line);
register_action(cx, Editor::delete_to_end_of_line);
register_action(cx, Editor::cut_to_end_of_line);
register_action(cx, Editor::duplicate_line);
register_action(cx, Editor::move_line_up);
register_action(cx, Editor::move_line_down);
register_action(cx, Editor::transpose);
register_action(cx, Editor::cut);
register_action(cx, Editor::copy);
register_action(cx, Editor::paste);
register_action(cx, Editor::undo);
register_action(cx, Editor::redo);
register_action(cx, Editor::move_page_up);
register_action(cx, Editor::move_page_down);
register_action(cx, Editor::next_screen);
register_action(cx, Editor::scroll_cursor_top);
register_action(cx, Editor::scroll_cursor_center);
register_action(cx, Editor::scroll_cursor_bottom);
register_action(cx, |editor, _: &LineDown, cx| {
register_action(view, cx, Editor::cancel);
register_action(view, cx, Editor::newline);
register_action(view, cx, Editor::newline_above);
register_action(view, cx, Editor::newline_below);
register_action(view, cx, Editor::backspace);
register_action(view, cx, Editor::delete);
register_action(view, cx, Editor::tab);
register_action(view, cx, Editor::tab_prev);
register_action(view, cx, Editor::indent);
register_action(view, cx, Editor::outdent);
register_action(view, cx, Editor::delete_line);
register_action(view, cx, Editor::join_lines);
register_action(view, cx, Editor::sort_lines_case_sensitive);
register_action(view, cx, Editor::sort_lines_case_insensitive);
register_action(view, cx, Editor::reverse_lines);
register_action(view, cx, Editor::shuffle_lines);
register_action(view, cx, Editor::convert_to_upper_case);
register_action(view, cx, Editor::convert_to_lower_case);
register_action(view, cx, Editor::convert_to_title_case);
register_action(view, cx, Editor::convert_to_snake_case);
register_action(view, cx, Editor::convert_to_kebab_case);
register_action(view, cx, Editor::convert_to_upper_camel_case);
register_action(view, cx, Editor::convert_to_lower_camel_case);
register_action(view, cx, Editor::delete_to_previous_word_start);
register_action(view, cx, Editor::delete_to_previous_subword_start);
register_action(view, cx, Editor::delete_to_next_word_end);
register_action(view, cx, Editor::delete_to_next_subword_end);
register_action(view, cx, Editor::delete_to_beginning_of_line);
register_action(view, cx, Editor::delete_to_end_of_line);
register_action(view, cx, Editor::cut_to_end_of_line);
register_action(view, cx, Editor::duplicate_line);
register_action(view, cx, Editor::move_line_up);
register_action(view, cx, Editor::move_line_down);
register_action(view, cx, Editor::transpose);
register_action(view, cx, Editor::cut);
register_action(view, cx, Editor::copy);
register_action(view, cx, Editor::paste);
register_action(view, cx, Editor::undo);
register_action(view, cx, Editor::redo);
register_action(view, cx, Editor::move_page_up);
register_action(view, cx, Editor::move_page_down);
register_action(view, cx, Editor::next_screen);
register_action(view, cx, Editor::scroll_cursor_top);
register_action(view, cx, Editor::scroll_cursor_center);
register_action(view, cx, Editor::scroll_cursor_bottom);
register_action(view, cx, |editor, _: &LineDown, cx| {
editor.scroll_screen(&ScrollAmount::Line(1.), cx)
});
register_action(cx, |editor, _: &LineUp, cx| {
register_action(view, cx, |editor, _: &LineUp, cx| {
editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
});
register_action(cx, |editor, _: &HalfPageDown, cx| {
register_action(view, cx, |editor, _: &HalfPageDown, cx| {
editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
});
register_action(cx, |editor, _: &HalfPageUp, cx| {
register_action(view, cx, |editor, _: &HalfPageUp, cx| {
editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
});
register_action(cx, |editor, _: &PageDown, cx| {
register_action(view, cx, |editor, _: &PageDown, cx| {
editor.scroll_screen(&ScrollAmount::Page(1.), cx)
});
register_action(cx, |editor, _: &PageUp, cx| {
register_action(view, cx, |editor, _: &PageUp, cx| {
editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
});
register_action(cx, Editor::move_to_previous_word_start);
register_action(cx, Editor::move_to_previous_subword_start);
register_action(cx, Editor::move_to_next_word_end);
register_action(cx, Editor::move_to_next_subword_end);
register_action(cx, Editor::move_to_beginning_of_line);
register_action(cx, Editor::move_to_end_of_line);
register_action(cx, Editor::move_to_start_of_paragraph);
register_action(cx, Editor::move_to_end_of_paragraph);
register_action(cx, Editor::move_to_beginning);
register_action(cx, Editor::move_to_end);
register_action(cx, Editor::select_up);
register_action(cx, Editor::select_down);
register_action(cx, Editor::select_left);
register_action(cx, Editor::select_right);
register_action(cx, Editor::select_to_previous_word_start);
register_action(cx, Editor::select_to_previous_subword_start);
register_action(cx, Editor::select_to_next_word_end);
register_action(cx, Editor::select_to_next_subword_end);
register_action(cx, Editor::select_to_beginning_of_line);
register_action(cx, Editor::select_to_end_of_line);
register_action(cx, Editor::select_to_start_of_paragraph);
register_action(cx, Editor::select_to_end_of_paragraph);
register_action(cx, Editor::select_to_beginning);
register_action(cx, Editor::select_to_end);
register_action(cx, Editor::select_all);
register_action(cx, |editor, action, cx| {
register_action(view, cx, Editor::move_to_previous_word_start);
register_action(view, cx, Editor::move_to_previous_subword_start);
register_action(view, cx, Editor::move_to_next_word_end);
register_action(view, cx, Editor::move_to_next_subword_end);
register_action(view, cx, Editor::move_to_beginning_of_line);
register_action(view, cx, Editor::move_to_end_of_line);
register_action(view, cx, Editor::move_to_start_of_paragraph);
register_action(view, cx, Editor::move_to_end_of_paragraph);
register_action(view, cx, Editor::move_to_beginning);
register_action(view, cx, Editor::move_to_end);
register_action(view, cx, Editor::select_up);
register_action(view, cx, Editor::select_down);
register_action(view, cx, Editor::select_left);
register_action(view, cx, Editor::select_right);
register_action(view, cx, Editor::select_to_previous_word_start);
register_action(view, cx, Editor::select_to_previous_subword_start);
register_action(view, cx, Editor::select_to_next_word_end);
register_action(view, cx, Editor::select_to_next_subword_end);
register_action(view, cx, Editor::select_to_beginning_of_line);
register_action(view, cx, Editor::select_to_end_of_line);
register_action(view, cx, Editor::select_to_start_of_paragraph);
register_action(view, cx, Editor::select_to_end_of_paragraph);
register_action(view, cx, Editor::select_to_beginning);
register_action(view, cx, Editor::select_to_end);
register_action(view, cx, Editor::select_all);
register_action(view, cx, |editor, action, cx| {
editor.select_all_matches(action, cx).log_err();
});
register_action(cx, Editor::select_line);
register_action(cx, Editor::split_selection_into_lines);
register_action(cx, Editor::add_selection_above);
register_action(cx, Editor::add_selection_below);
register_action(cx, |editor, action, cx| {
register_action(view, cx, Editor::select_line);
register_action(view, cx, Editor::split_selection_into_lines);
register_action(view, cx, Editor::add_selection_above);
register_action(view, cx, Editor::add_selection_below);
register_action(view, cx, |editor, action, cx| {
editor.select_next(action, cx).log_err();
});
register_action(cx, |editor, action, cx| {
register_action(view, cx, |editor, action, cx| {
editor.select_previous(action, cx).log_err();
});
register_action(cx, Editor::toggle_comments);
register_action(cx, Editor::select_larger_syntax_node);
register_action(cx, Editor::select_smaller_syntax_node);
register_action(cx, Editor::move_to_enclosing_bracket);
register_action(cx, Editor::undo_selection);
register_action(cx, Editor::redo_selection);
register_action(cx, Editor::go_to_diagnostic);
register_action(cx, Editor::go_to_prev_diagnostic);
register_action(cx, Editor::go_to_hunk);
register_action(cx, Editor::go_to_prev_hunk);
register_action(cx, Editor::go_to_definition);
register_action(cx, Editor::go_to_definition_split);
register_action(cx, Editor::go_to_type_definition);
register_action(cx, Editor::go_to_type_definition_split);
register_action(cx, Editor::fold);
register_action(cx, Editor::fold_at);
register_action(cx, Editor::unfold_lines);
register_action(cx, Editor::unfold_at);
register_action(cx, Editor::fold_selected_ranges);
register_action(cx, Editor::show_completions);
register_action(cx, Editor::toggle_code_actions);
register_action(view, cx, Editor::toggle_comments);
register_action(view, cx, Editor::select_larger_syntax_node);
register_action(view, cx, Editor::select_smaller_syntax_node);
register_action(view, cx, Editor::move_to_enclosing_bracket);
register_action(view, cx, Editor::undo_selection);
register_action(view, cx, Editor::redo_selection);
register_action(view, cx, Editor::go_to_diagnostic);
register_action(view, cx, Editor::go_to_prev_diagnostic);
register_action(view, cx, Editor::go_to_hunk);
register_action(view, cx, Editor::go_to_prev_hunk);
register_action(view, cx, Editor::go_to_definition);
register_action(view, cx, Editor::go_to_definition_split);
register_action(view, cx, Editor::go_to_type_definition);
register_action(view, cx, Editor::go_to_type_definition_split);
register_action(view, cx, Editor::fold);
register_action(view, cx, Editor::fold_at);
register_action(view, cx, Editor::unfold_lines);
register_action(view, cx, Editor::unfold_at);
register_action(view, cx, Editor::fold_selected_ranges);
register_action(view, cx, Editor::show_completions);
register_action(view, cx, Editor::toggle_code_actions);
// on_action(cx, Editor::open_excerpts); todo!()
register_action(cx, Editor::toggle_soft_wrap);
register_action(cx, Editor::toggle_inlay_hints);
register_action(cx, Editor::reveal_in_finder);
register_action(cx, Editor::copy_path);
register_action(cx, Editor::copy_relative_path);
register_action(cx, Editor::copy_highlight_json);
register_action(cx, |editor, action, cx| {
register_action(view, cx, Editor::toggle_soft_wrap);
register_action(view, cx, Editor::toggle_inlay_hints);
register_action(view, cx, Editor::reveal_in_finder);
register_action(view, cx, Editor::copy_path);
register_action(view, cx, Editor::copy_relative_path);
register_action(view, cx, Editor::copy_highlight_json);
register_action(view, cx, |editor, action, cx| {
editor
.format(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, Editor::restart_language_server);
register_action(cx, Editor::show_character_palette);
register_action(view, cx, Editor::restart_language_server);
register_action(view, cx, Editor::show_character_palette);
// on_action(cx, Editor::confirm_completion); todo!()
register_action(cx, |editor, action, cx| {
register_action(view, cx, |editor, action, cx| {
editor
.confirm_code_action(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, |editor, action, cx| {
register_action(view, cx, |editor, action, cx| {
editor
.rename(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, |editor, action, cx| {
register_action(view, cx, |editor, action, cx| {
editor
.confirm_rename(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, |editor, action, cx| {
register_action(view, cx, |editor, action, cx| {
editor
.find_all_references(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, Editor::next_copilot_suggestion);
register_action(cx, Editor::previous_copilot_suggestion);
register_action(cx, Editor::copilot_suggest);
register_action(cx, Editor::context_menu_first);
register_action(cx, Editor::context_menu_prev);
register_action(cx, Editor::context_menu_next);
register_action(cx, Editor::context_menu_last);
register_action(view, cx, Editor::next_copilot_suggestion);
register_action(view, cx, Editor::previous_copilot_suggestion);
register_action(view, cx, Editor::copilot_suggest);
register_action(view, cx, Editor::context_menu_first);
register_action(view, cx, Editor::context_menu_prev);
register_action(view, cx, Editor::context_menu_next);
register_action(view, cx, Editor::context_menu_last);
}
fn register_action<T: Action>(
cx: &mut ViewContext<Editor>,
view: &View<Editor>,
cx: &mut WindowContext,
listener: impl Fn(&mut Editor, &T, &mut ViewContext<Editor>) + 'static,
) {
cx.on_action(TypeId::of::<T>(), move |editor, action, phase, cx| {
let view = view.clone();
cx.on_action(TypeId::of::<T>(), move |action, phase, cx| {
let action = action.downcast_ref().unwrap();
if phase == DispatchPhase::Bubble {
listener(editor, action, cx);
view.update(cx, |editor, cx| {
listener(editor, action, cx);
})
}
})
}

View File

@ -422,7 +422,7 @@ impl HoverState {
visible_rows: Range<u32>,
workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>,
) -> Option<(DisplayPoint, Vec<AnyElement<Editor>>)> {
) -> Option<(DisplayPoint, Vec<AnyElement>)> {
todo!("old version below")
}
// // If there is a diagnostic, position the popovers based on that.
@ -504,7 +504,7 @@ pub struct DiagnosticPopover {
}
impl DiagnosticPopover {
pub fn render(&self, style: &EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
pub fn render(&self, style: &EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement {
todo!()
// enum PrimaryDiagnostic {}

View File

@ -10,7 +10,7 @@ use futures::future::try_join_all;
use gpui::{
div, point, AnyElement, AppContext, AsyncAppContext, Entity, EntityId, EventEmitter,
FocusHandle, Model, ParentElement, Pixels, SharedString, Styled, Subscription, Task, View,
ViewContext, VisualContext, WeakView,
ViewContext, VisualContext, WeakView, WindowContext,
};
use language::{
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, CharKind, OffsetRangeExt,
@ -584,7 +584,7 @@ impl Item for Editor {
Some(path.to_string_lossy().to_string().into())
}
fn tab_content<T: 'static>(&self, detail: Option<usize>, cx: &AppContext) -> AnyElement<T> {
fn tab_content(&self, detail: Option<usize>, cx: &WindowContext) -> AnyElement {
let theme = cx.theme();
AnyElement::new(

View File

@ -117,8 +117,8 @@ impl FocusableView for FileFinder {
self.picker.focus_handle(cx)
}
}
impl Render<Self> for FileFinder {
type Element = Div<Self>;
impl Render for FileFinder {
type Element = Div;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
v_stack().w_96().child(self.picker.clone())
@ -530,7 +530,7 @@ impl FileFinderDelegate {
}
impl PickerDelegate for FileFinderDelegate {
type ListItem = Div<Picker<Self>>;
type ListItem = Div;
fn placeholder_text(&self) -> Arc<str> {
"Search project files...".into()

View File

@ -144,15 +144,15 @@ impl GoToLine {
}
}
impl Render<Self> for GoToLine {
type Element = Div<Self>;
impl Render for GoToLine {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div()
.elevation_2(cx)
.key_context("GoToLine")
.on_action(Self::cancel)
.on_action(Self::confirm)
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::confirm))
.w_96()
.child(
v_stack()

View File

@ -424,7 +424,7 @@ impl AppContext {
/// Opens a new window with the given option and the root view returned by the given function.
/// The function is invoked with a `WindowContext`, which can be used to interact with window-specific
/// functionality.
pub fn open_window<V: 'static + Render<V>>(
pub fn open_window<V: 'static + Render>(
&mut self,
options: crate::WindowOptions,
build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,

View File

@ -115,7 +115,7 @@ impl AsyncAppContext {
build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
) -> Result<WindowHandle<V>>
where
V: 'static + Render<V>,
V: 'static + Render,
{
let app = self
.app
@ -286,7 +286,7 @@ impl VisualContext for AsyncWindowContext {
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render<V>,
V: 'static + Render,
{
self.window
.update(self, |_, cx| cx.build_view(build_view_state))
@ -306,7 +306,7 @@ impl VisualContext for AsyncWindowContext {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render<V>,
V: 'static + Render,
{
self.window
.update(self, |_, cx| cx.replace_root_view(build_view))

View File

@ -127,7 +127,7 @@ impl TestAppContext {
pub fn add_window<F, V>(&mut self, build_window: F) -> WindowHandle<V>
where
F: FnOnce(&mut ViewContext<V>) -> V,
V: 'static + Render<V>,
V: 'static + Render,
{
let mut cx = self.app.borrow_mut();
cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window))
@ -144,7 +144,7 @@ impl TestAppContext {
pub fn add_window_view<F, V>(&mut self, build_window: F) -> (View<V>, &mut VisualTestContext)
where
F: FnOnce(&mut ViewContext<V>) -> V,
V: 'static + Render<V>,
V: 'static + Render,
{
let mut cx = self.app.borrow_mut();
let window = cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window));
@ -568,7 +568,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render<V>,
V: 'static + Render,
{
self.window
.update(self.cx, |_, cx| cx.build_view(build_view))
@ -590,7 +590,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render<V>,
V: 'static + Render,
{
self.window
.update(self.cx, |_, cx| cx.replace_root_view(build_view))
@ -618,7 +618,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
}
impl AnyWindowHandle {
pub fn build_view<V: Render<V> + 'static>(
pub fn build_view<V: Render + 'static>(
&self,
cx: &mut TestAppContext,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
@ -629,8 +629,8 @@ impl AnyWindowHandle {
pub struct EmptyView {}
impl Render<Self> for EmptyView {
type Element = Div<Self>;
impl Render for EmptyView {
type Element = Div;
fn render(&mut self, _cx: &mut crate::ViewContext<Self>) -> Self::Element {
div()

View File

@ -1,24 +1,25 @@
use crate::{
AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size, ViewContext,
WindowContext,
};
use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec;
use std::{any::Any, fmt::Debug, marker::PhantomData};
use std::{any::Any, fmt::Debug};
pub trait Render<V: 'static>: 'static + Sized {
type Element: Element<V> + 'static;
pub trait Render: 'static + Sized {
type Element: Element + 'static;
fn render(&mut self, cx: &mut ViewContext<V>) -> Self::Element;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element;
}
pub trait RenderOnce<V: 'static>: Sized {
type Element: Element<V> + 'static;
pub trait RenderOnce: Sized {
type Element: Element + 'static;
fn element_id(&self) -> Option<ElementId>;
fn render_once(self) -> Self::Element;
fn render_into_any(self) -> AnyElement<V> {
fn render_into_any(self) -> AnyElement {
self.render_once().into_any()
}
@ -26,9 +27,8 @@ pub trait RenderOnce<V: 'static>: Sized {
self,
origin: Point<Pixels>,
available_space: Size<T>,
view_state: &mut V,
cx: &mut ViewContext<V>,
f: impl FnOnce(&mut <Self::Element as Element<V>>::State, &mut ViewContext<V>) -> R,
cx: &mut WindowContext,
f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R,
) -> R
where
T: Clone + Default + Debug + Into<AvailableSpace>,
@ -39,13 +39,9 @@ pub trait RenderOnce<V: 'static>: Sized {
element: Some(element),
phase: ElementDrawPhase::Start,
};
let frame_state = DrawableElement::draw(
element,
origin,
available_space.map(Into::into),
view_state,
cx,
);
let frame_state =
DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
if let Some(mut frame_state) = frame_state {
f(&mut frame_state, cx)
@ -61,7 +57,7 @@ pub trait RenderOnce<V: 'static>: Sized {
fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
where
Self: Sized,
U: RenderOnce<V>,
U: RenderOnce,
{
f(self)
}
@ -87,70 +83,55 @@ pub trait RenderOnce<V: 'static>: Sized {
}
}
pub trait Element<V: 'static>: 'static + RenderOnce<V> {
pub trait Element: 'static + RenderOnce {
type State: 'static;
fn layout(
&mut self,
view_state: &mut V,
element_state: Option<Self::State>,
cx: &mut ViewContext<V>,
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State);
fn paint(
self,
bounds: Bounds<Pixels>,
view_state: &mut V,
element_state: &mut Self::State,
cx: &mut ViewContext<V>,
);
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
fn into_any(self) -> AnyElement<V> {
fn into_any(self) -> AnyElement {
AnyElement::new(self)
}
}
pub trait Component<V: 'static>: 'static {
type Rendered: RenderOnce<V>;
pub trait Component: 'static {
type Rendered: RenderOnce;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered;
fn render(self, cx: &mut WindowContext) -> Self::Rendered;
}
pub struct CompositeElement<V, C> {
pub struct CompositeElement<C> {
component: Option<C>,
view_type: PhantomData<V>,
}
pub struct CompositeElementState<V: 'static, C: Component<V>> {
rendered_element: Option<<C::Rendered as RenderOnce<V>>::Element>,
rendered_element_state: <<C::Rendered as RenderOnce<V>>::Element as Element<V>>::State,
pub struct CompositeElementState<C: Component> {
rendered_element: Option<<C::Rendered as RenderOnce>::Element>,
rendered_element_state: <<C::Rendered as RenderOnce>::Element as Element>::State,
}
impl<V, C> CompositeElement<V, C> {
impl<C> CompositeElement<C> {
pub fn new(component: C) -> Self {
CompositeElement {
component: Some(component),
view_type: PhantomData,
}
}
}
impl<V: 'static, C: Component<V>> Element<V> for CompositeElement<V, C> {
type State = CompositeElementState<V, C>;
impl<C: Component> Element for CompositeElement<C> {
type State = CompositeElementState<C>;
fn layout(
&mut self,
view: &mut V,
state: Option<Self::State>,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut element = self
.component
.take()
.unwrap()
.render(view, cx)
.render_once();
let (layout_id, state) = element.layout(view, state.map(|s| s.rendered_element_state), cx);
let mut element = self.component.take().unwrap().render(cx).render_once();
let (layout_id, state) = element.layout(state.map(|s| s.rendered_element_state), cx);
let state = CompositeElementState {
rendered_element: Some(element),
rendered_element_state: state,
@ -158,23 +139,16 @@ impl<V: 'static, C: Component<V>> Element<V> for CompositeElement<V, C> {
(layout_id, state)
}
fn paint(
self,
bounds: Bounds<Pixels>,
view: &mut V,
state: &mut Self::State,
cx: &mut ViewContext<V>,
) {
state.rendered_element.take().unwrap().paint(
bounds,
view,
&mut state.rendered_element_state,
cx,
);
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
state
.rendered_element
.take()
.unwrap()
.paint(bounds, &mut state.rendered_element_state, cx);
}
}
impl<V: 'static, C: Component<V>> RenderOnce<V> for CompositeElement<V, C> {
impl<C: Component> RenderOnce for CompositeElement<C> {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
@ -189,10 +163,10 @@ impl<V: 'static, C: Component<V>> RenderOnce<V> for CompositeElement<V, C> {
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
pub trait ParentElement<V: 'static> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
pub trait ParentElement {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
fn child(mut self, child: impl RenderOnce<V>) -> Self
fn child(mut self, child: impl RenderOnce) -> Self
where
Self: Sized,
{
@ -200,7 +174,7 @@ pub trait ParentElement<V: 'static> {
self
}
fn children(mut self, children: impl IntoIterator<Item = impl RenderOnce<V>>) -> Self
fn children(mut self, children: impl IntoIterator<Item = impl RenderOnce>) -> Self
where
Self: Sized,
{
@ -213,26 +187,28 @@ pub trait ParentElement<V: 'static> {
}
}
trait ElementObject<V> {
trait ElementObject {
fn element_id(&self) -> Option<ElementId>;
fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
fn layout(&mut self, cx: &mut WindowContext) -> LayoutId;
fn paint(&mut self, cx: &mut WindowContext);
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> Size<Pixels>;
fn draw(
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
);
}
pub struct DrawableElement<V: 'static, E: Element<V>> {
pub struct DrawableElement<E: Element> {
element: Option<E>,
phase: ElementDrawPhase<E::State>,
}
@ -253,7 +229,7 @@ enum ElementDrawPhase<S> {
}
/// A wrapper around an implementer of [Element] that allows it to be drawn in a window.
impl<V, E: Element<V>> DrawableElement<V, E> {
impl<E: Element> DrawableElement<E> {
fn new(element: E) -> Self {
DrawableElement {
element: Some(element),
@ -265,18 +241,15 @@ impl<V, E: Element<V>> DrawableElement<V, E> {
self.element.as_ref()?.element_id()
}
fn layout(&mut self, state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
{
let layout_id = cx.with_element_state(id, |element_state, cx| {
self.element
.as_mut()
.unwrap()
.layout(state, element_state, cx)
self.element.as_mut().unwrap().layout(element_state, cx)
});
(layout_id, None)
} else {
let (layout_id, frame_state) = self.element.as_mut().unwrap().layout(state, None, cx);
let (layout_id, frame_state) = self.element.as_mut().unwrap().layout(None, cx);
(layout_id, Some(frame_state))
};
@ -287,7 +260,7 @@ impl<V, E: Element<V>> DrawableElement<V, E> {
layout_id
}
fn paint(mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> Option<E::State> {
fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
match self.phase {
ElementDrawPhase::LayoutRequested {
layout_id,
@ -304,7 +277,7 @@ impl<V, E: Element<V>> DrawableElement<V, E> {
self.element
.take()
.unwrap()
.paint(bounds, view_state, &mut frame_state, cx);
.paint(bounds, &mut frame_state, cx);
Some(frame_state)
} else {
let element_id = self
@ -315,12 +288,10 @@ impl<V, E: Element<V>> DrawableElement<V, E> {
.expect("if we don't have frame state, we should have element state");
cx.with_element_state(element_id, |element_state, cx| {
let mut element_state = element_state.unwrap();
self.element.take().unwrap().paint(
bounds,
view_state,
&mut element_state,
cx,
);
self.element
.take()
.unwrap()
.paint(bounds, &mut element_state, cx);
((), element_state)
});
None
@ -334,11 +305,10 @@ impl<V, E: Element<V>> DrawableElement<V, E> {
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> Size<Pixels> {
if matches!(&self.phase, ElementDrawPhase::Start) {
self.layout(view_state, cx);
self.layout(cx);
}
let layout_id = match &mut self.phase {
@ -376,22 +346,20 @@ impl<V, E: Element<V>> DrawableElement<V, E> {
mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> Option<E::State> {
self.measure(available_space, view_state, cx);
cx.with_absolute_element_offset(origin, |cx| self.paint(view_state, cx))
self.measure(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
}
}
// impl<V: 'static, E: Element<V>> Element<V> for DrawableElement<V, E> {
// type State = <E::Element as Element<V>>::State;
// impl<V: 'static, E: Element> Element for DrawableElement<V, E> {
// type State = <E::Element as Element>::State;
// fn layout(
// &mut self,
// view_state: &mut V,
// element_state: Option<Self::State>,
// cx: &mut ViewContext<V>,
// cx: &mut WindowContext,
// ) -> (LayoutId, Self::State) {
// }
@ -399,15 +367,14 @@ impl<V, E: Element<V>> DrawableElement<V, E> {
// fn paint(
// self,
// bounds: Bounds<Pixels>,
// view_state: &mut V,
// element_state: &mut Self::State,
// cx: &mut ViewContext<V>,
// cx: &mut WindowContext,
// ) {
// todo!()
// }
// }
// impl<V: 'static, E: 'static + Element<V>> RenderOnce<V> for DrawableElement<V, E> {
// impl<V: 'static, E: 'static + Element> RenderOnce for DrawableElement<V, E> {
// type Element = Self;
// fn element_id(&self) -> Option<ElementId> {
@ -419,81 +386,71 @@ impl<V, E: Element<V>> DrawableElement<V, E> {
// }
// }
impl<V, E> ElementObject<V> for Option<DrawableElement<V, E>>
impl<E> ElementObject for Option<DrawableElement<E>>
where
E: Element<V>,
E: Element,
E::State: 'static,
{
fn element_id(&self) -> Option<ElementId> {
self.as_ref().unwrap().element_id()
}
fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
DrawableElement::layout(self.as_mut().unwrap(), view_state, cx)
fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
DrawableElement::layout(self.as_mut().unwrap(), cx)
}
fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
DrawableElement::paint(self.take().unwrap(), view_state, cx);
fn paint(&mut self, cx: &mut WindowContext) {
DrawableElement::paint(self.take().unwrap(), cx);
}
fn measure(
&mut self,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> Size<Pixels> {
DrawableElement::measure(self.as_mut().unwrap(), available_space, view_state, cx)
DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
}
fn draw(
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) {
DrawableElement::draw(
self.take().unwrap(),
origin,
available_space,
view_state,
cx,
);
DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
}
}
pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
pub struct AnyElement(Box<dyn ElementObject>);
impl<V: 'static> AnyElement<V> {
impl AnyElement {
pub fn new<E>(element: E) -> Self
where
V: 'static,
E: 'static + Element<V>,
E: 'static + Element,
E::State: Any,
{
AnyElement(Box::new(Some(DrawableElement::new(element))) as Box<dyn ElementObject<V>>)
AnyElement(Box::new(Some(DrawableElement::new(element))) as Box<dyn ElementObject>)
}
pub fn element_id(&self) -> Option<ElementId> {
self.0.element_id()
}
pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
self.0.layout(view_state, cx)
pub fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
self.0.layout(cx)
}
pub fn paint(mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
self.0.paint(view_state, cx)
pub fn paint(mut self, cx: &mut WindowContext) {
self.0.paint(cx)
}
/// Initializes this element and performs layout within the given available space to determine its size.
pub fn measure(
&mut self,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> Size<Pixels> {
self.0.measure(available_space, view_state, cx)
self.0.measure(available_space, cx)
}
/// Initializes this element and performs layout in the available space, then paints it at the given origin.
@ -501,43 +458,35 @@ impl<V: 'static> AnyElement<V> {
mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
view_state: &mut V,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) {
self.0.draw(origin, available_space, view_state, cx)
self.0.draw(origin, available_space, cx)
}
/// Converts this `AnyElement` into a trait object that can be stored and manipulated.
pub fn into_any(self) -> AnyElement<V> {
pub fn into_any(self) -> AnyElement {
AnyElement::new(self)
}
}
impl<V: 'static> Element<V> for AnyElement<V> {
impl Element for AnyElement {
type State = ();
fn layout(
&mut self,
view_state: &mut V,
_: Option<Self::State>,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let layout_id = self.layout(view_state, cx);
let layout_id = self.layout(cx);
(layout_id, ())
}
fn paint(
self,
_bounds: Bounds<Pixels>,
view_state: &mut V,
_: &mut Self::State,
cx: &mut ViewContext<V>,
) {
self.paint(view_state, cx);
fn paint(self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
self.paint(cx);
}
}
impl<V: 'static> RenderOnce<V> for AnyElement<V> {
impl RenderOnce for AnyElement {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
@ -549,13 +498,13 @@ impl<V: 'static> RenderOnce<V> for AnyElement<V> {
}
}
// impl<V, E, F> Element<V> for Option<F>
// impl<V, E, F> Element for Option<F>
// where
// V: 'static,
// E: Element<V>,
// F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
// E: Element,
// F: FnOnce(&mut V, &mut WindowContext<'_, V>) -> E + 'static,
// {
// type State = Option<AnyElement<V>>;
// type State = Option<AnyElement>;
// fn element_id(&self) -> Option<ElementId> {
// None
@ -563,9 +512,8 @@ impl<V: 'static> RenderOnce<V> for AnyElement<V> {
// fn layout(
// &mut self,
// view_state: &mut V,
// _: Option<Self::State>,
// cx: &mut ViewContext<V>,
// cx: &mut WindowContext,
// ) -> (LayoutId, Self::State) {
// let render = self.take().unwrap();
// let mut element = (render)(view_state, cx).into_any();
@ -576,19 +524,18 @@ impl<V: 'static> RenderOnce<V> for AnyElement<V> {
// fn paint(
// self,
// _bounds: Bounds<Pixels>,
// view_state: &mut V,
// rendered_element: &mut Self::State,
// cx: &mut ViewContext<V>,
// cx: &mut WindowContext,
// ) {
// rendered_element.take().unwrap().paint(view_state, cx);
// }
// }
// impl<V, E, F> RenderOnce<V> for Option<F>
// impl<V, E, F> RenderOnce for Option<F>
// where
// V: 'static,
// E: Element<V>,
// F: FnOnce(&mut V, &mut ViewContext<V>) -> E + 'static,
// E: Element,
// F: FnOnce(&mut V, &mut WindowContext) -> E + 'static,
// {
// type Element = Self;

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,17 @@
use crate::{
BorrowWindow, Bounds, Element, InteractiveElement, InteractiveElementState, Interactivity,
LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, ViewContext,
Bounds, Element, InteractiveElement, InteractiveElementState, Interactivity, LayoutId, Pixels,
RenderOnce, SharedString, StyleRefinement, Styled, WindowContext,
};
use futures::FutureExt;
use util::ResultExt;
pub struct Img<V: 'static> {
interactivity: Interactivity<V>,
pub struct Img {
interactivity: Interactivity,
uri: Option<SharedString>,
grayscale: bool,
}
pub fn img<V: 'static>() -> Img<V> {
pub fn img() -> Img {
Img {
interactivity: Interactivity::default(),
uri: None,
@ -19,10 +19,7 @@ pub fn img<V: 'static>() -> Img<V> {
}
}
impl<V> Img<V>
where
V: 'static,
{
impl Img {
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
self.uri = Some(uri.into());
self
@ -34,14 +31,13 @@ where
}
}
impl<V> Element<V> for Img<V> {
impl Element for Img {
type State = InteractiveElementState;
fn layout(
&mut self,
_view_state: &mut V,
element_state: Option<Self::State>,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
@ -51,9 +47,8 @@ impl<V> Element<V> for Img<V> {
fn paint(
self,
bounds: Bounds<Pixels>,
_view_state: &mut V,
element_state: &mut Self::State,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) {
self.interactivity.paint(
bounds,
@ -78,7 +73,7 @@ impl<V> Element<V> for Img<V> {
.log_err()
});
} else {
cx.spawn(|_, mut cx| async move {
cx.spawn(|mut cx| async move {
if image_future.await.ok().is_some() {
cx.on_next_frame(|cx| cx.notify());
}
@ -91,7 +86,7 @@ impl<V> Element<V> for Img<V> {
}
}
impl<V: 'static> RenderOnce<V> for Img<V> {
impl RenderOnce for Img {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
@ -103,14 +98,14 @@ impl<V: 'static> RenderOnce<V> for Img<V> {
}
}
impl<V> Styled for Img<V> {
impl Styled for Img {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.interactivity.base_style
}
}
impl<V> InteractiveElement<V> for Img<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
impl InteractiveElement for Img {
fn interactivity(&mut self) -> &mut Interactivity {
&mut self.interactivity
}
}

View File

@ -3,15 +3,15 @@ use taffy::style::{Display, Position};
use crate::{
point, AnyElement, BorrowWindow, Bounds, Element, LayoutId, ParentElement, Pixels, Point,
RenderOnce, Size, Style,
RenderOnce, Size, Style, WindowContext,
};
pub struct OverlayState {
child_layout_ids: SmallVec<[LayoutId; 4]>,
}
pub struct Overlay<V> {
children: SmallVec<[AnyElement<V>; 2]>,
pub struct Overlay {
children: SmallVec<[AnyElement; 2]>,
anchor_corner: AnchorCorner,
fit_mode: OverlayFitMode,
// todo!();
@ -21,7 +21,7 @@ pub struct Overlay<V> {
/// overlay gives you a floating element that will avoid overflowing the window bounds.
/// Its children should have no margin to avoid measurement issues.
pub fn overlay<V: 'static>() -> Overlay<V> {
pub fn overlay() -> Overlay {
Overlay {
children: SmallVec::new(),
anchor_corner: AnchorCorner::TopLeft,
@ -30,7 +30,7 @@ pub fn overlay<V: 'static>() -> Overlay<V> {
}
}
impl<V> Overlay<V> {
impl Overlay {
/// Sets which corner of the overlay should be anchored to the current position.
pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
self.anchor_corner = anchor;
@ -51,25 +51,24 @@ impl<V> Overlay<V> {
}
}
impl<V: 'static> ParentElement<V> for Overlay<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
impl ParentElement for Overlay {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
&mut self.children
}
}
impl<V: 'static> Element<V> for Overlay<V> {
impl Element for Overlay {
type State = OverlayState;
fn layout(
&mut self,
view_state: &mut V,
_: Option<Self::State>,
cx: &mut crate::ViewContext<V>,
cx: &mut WindowContext,
) -> (crate::LayoutId, Self::State) {
let child_layout_ids = self
.children
.iter_mut()
.map(|child| child.layout(view_state, cx))
.map(|child| child.layout(cx))
.collect::<SmallVec<_>>();
let mut overlay_style = Style::default();
@ -84,9 +83,8 @@ impl<V: 'static> Element<V> for Overlay<V> {
fn paint(
self,
bounds: crate::Bounds<crate::Pixels>,
view_state: &mut V,
element_state: &mut Self::State,
cx: &mut crate::ViewContext<V>,
cx: &mut WindowContext,
) {
if element_state.child_layout_ids.is_empty() {
return;
@ -147,13 +145,13 @@ impl<V: 'static> Element<V> for Overlay<V> {
cx.with_element_offset(desired.origin - bounds.origin, |cx| {
for child in self.children {
child.paint(view_state, cx);
child.paint(cx);
}
})
}
}
impl<V: 'static> RenderOnce<V> for Overlay<V> {
impl RenderOnce for Overlay {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {

View File

@ -1,49 +1,43 @@
use crate::{
Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, ViewContext,
LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, WindowContext,
};
use util::ResultExt;
pub struct Svg<V: 'static> {
interactivity: Interactivity<V>,
pub struct Svg {
interactivity: Interactivity,
path: Option<SharedString>,
}
pub fn svg<V: 'static>() -> Svg<V> {
pub fn svg() -> Svg {
Svg {
interactivity: Interactivity::default(),
path: None,
}
}
impl<V> Svg<V> {
impl Svg {
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
self.path = Some(path.into());
self
}
}
impl<V> Element<V> for Svg<V> {
impl Element for Svg {
type State = InteractiveElementState;
fn layout(
&mut self,
_view_state: &mut V,
element_state: Option<Self::State>,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
}
fn paint(
self,
bounds: Bounds<Pixels>,
_view_state: &mut V,
element_state: &mut Self::State,
cx: &mut ViewContext<V>,
) where
fn paint(self, bounds: Bounds<Pixels>, element_state: &mut Self::State, cx: &mut WindowContext)
where
Self: Sized,
{
self.interactivity
@ -55,7 +49,7 @@ impl<V> Element<V> for Svg<V> {
}
}
impl<V: 'static> RenderOnce<V> for Svg<V> {
impl RenderOnce for Svg {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
@ -67,14 +61,14 @@ impl<V: 'static> RenderOnce<V> for Svg<V> {
}
}
impl<V> Styled for Svg<V> {
impl Styled for Svg {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.interactivity.base_style
}
}
impl<V> InteractiveElement<V> for Svg<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
impl InteractiveElement for Svg {
fn interactivity(&mut self) -> &mut Interactivity {
&mut self.interactivity
}
}

View File

@ -1,6 +1,6 @@
use crate::{
BorrowWindow, Bounds, Element, ElementId, LayoutId, Pixels, RenderOnce, SharedString, Size,
TextRun, ViewContext, WindowContext, WrappedLine,
Bounds, Element, ElementId, LayoutId, Pixels, RenderOnce, SharedString, Size, TextRun,
WindowContext, WrappedLine,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
@ -8,32 +8,25 @@ use smallvec::SmallVec;
use std::{cell::Cell, rc::Rc, sync::Arc};
use util::ResultExt;
impl<V: 'static> Element<V> for &'static str {
impl Element for &'static str {
type State = TextState;
fn layout(
&mut self,
_: &mut V,
_: Option<Self::State>,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut state = TextState::default();
let layout_id = state.layout(SharedString::from(*self), None, cx);
(layout_id, state)
}
fn paint(
self,
bounds: Bounds<Pixels>,
_: &mut V,
state: &mut TextState,
cx: &mut ViewContext<V>,
) {
fn paint(self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
state.paint(bounds, self, cx)
}
}
impl<V: 'static> RenderOnce<V> for &'static str {
impl RenderOnce for &'static str {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
@ -45,37 +38,30 @@ impl<V: 'static> RenderOnce<V> for &'static str {
}
}
impl<V: 'static> Element<V> for SharedString {
impl Element for SharedString {
type State = TextState;
fn layout(
&mut self,
_: &mut V,
_: Option<Self::State>,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut state = TextState::default();
let layout_id = state.layout(self.clone(), None, cx);
(layout_id, state)
}
fn paint(
self,
bounds: Bounds<Pixels>,
_: &mut V,
state: &mut TextState,
cx: &mut ViewContext<V>,
) {
fn paint(self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
let text_str: &str = self.as_ref();
state.paint(bounds, text_str, cx)
}
}
impl<V: 'static> RenderOnce<V> for SharedString {
impl RenderOnce for SharedString {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
Some(self.clone().into())
None
}
fn render_once(self) -> Self::Element {
@ -102,110 +88,25 @@ impl StyledText {
}
}
impl<V: 'static> Element<V> for StyledText {
impl Element for StyledText {
type State = TextState;
fn layout(
&mut self,
_view: &mut V,
element_state: Option<Self::State>,
cx: &mut ViewContext<V>,
_: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let element_state = element_state.unwrap_or_default();
let text_system = cx.text_system().clone();
let text_style = cx.text_style();
let font_size = text_style.font_size.to_pixels(cx.rem_size());
let line_height = text_style
.line_height
.to_pixels(font_size.into(), cx.rem_size());
let text = self.text.clone();
let rem_size = cx.rem_size();
let runs = if let Some(runs) = self.runs.take() {
runs
} else {
vec![text_style.to_run(text.len())]
};
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
let element_state = element_state.clone();
move |known_dimensions, available_space| {
let wrap_width = known_dimensions.width.or(match available_space.width {
crate::AvailableSpace::Definite(x) => Some(x),
_ => None,
});
if let Some(text_state) = element_state.0.lock().as_ref() {
if text_state.size.is_some()
&& (wrap_width.is_none() || wrap_width == text_state.wrap_width)
{
return text_state.size.unwrap();
}
}
let Some(lines) = text_system
.shape_text(
&text,
font_size,
&runs[..],
wrap_width, // Wrap if we know the width.
)
.log_err()
else {
element_state.lock().replace(TextStateInner {
lines: Default::default(),
line_height,
wrap_width,
size: Some(Size::default()),
});
return Size::default();
};
let mut size: Size<Pixels> = Size::default();
for line in &lines {
let line_size = line.size(line_height);
size.height += line_size.height;
size.width = size.width.max(line_size.width);
}
element_state.lock().replace(TextStateInner {
lines,
line_height,
wrap_width,
size: Some(size),
});
size
}
});
(layout_id, element_state)
let mut state = TextState::default();
let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
(layout_id, state)
}
fn paint(
self,
bounds: Bounds<Pixels>,
_: &mut V,
element_state: &mut Self::State,
cx: &mut ViewContext<V>,
) {
let element_state = element_state.lock();
let element_state = element_state
.as_ref()
.ok_or_else(|| anyhow!("measurement has not been performed on {}", &self.text))
.unwrap();
let line_height = element_state.line_height;
let mut line_origin = bounds.origin;
for line in &element_state.lines {
line.paint(line_origin, line_height, cx).log_err();
line_origin.y += line.size(line_height).height;
}
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
state.paint(bounds, &self.text, cx)
}
}
impl<V: 'static> RenderOnce<V> for StyledText {
impl RenderOnce for StyledText {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
@ -327,7 +228,7 @@ impl TextState {
}
struct InteractiveText {
id: ElementId,
element_id: ElementId,
text: StyledText,
}
@ -336,28 +237,27 @@ struct InteractiveTextState {
clicked_range_ixs: Rc<Cell<SmallVec<[usize; 1]>>>,
}
impl<V: 'static> Element<V> for InteractiveText {
impl Element for InteractiveText {
type State = InteractiveTextState;
fn layout(
&mut self,
view_state: &mut V,
element_state: Option<Self::State>,
cx: &mut ViewContext<V>,
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
if let Some(InteractiveTextState {
text_state,
clicked_range_ixs,
}) = element_state
}) = state
{
let (layout_id, text_state) = self.text.layout(view_state, Some(text_state), cx);
let (layout_id, text_state) = self.text.layout(Some(text_state), cx);
let element_state = InteractiveTextState {
text_state,
clicked_range_ixs,
};
(layout_id, element_state)
} else {
let (layout_id, text_state) = self.text.layout(view_state, None, cx);
let (layout_id, text_state) = self.text.layout(None, cx);
let element_state = InteractiveTextState {
text_state,
clicked_range_ixs: Rc::default(),
@ -366,23 +266,16 @@ impl<V: 'static> Element<V> for InteractiveText {
}
}
fn paint(
self,
bounds: Bounds<Pixels>,
view_state: &mut V,
element_state: &mut Self::State,
cx: &mut ViewContext<V>,
) {
self.text
.paint(bounds, view_state, &mut element_state.text_state, cx)
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
self.text.paint(bounds, &mut state.text_state, cx)
}
}
impl<V: 'static> RenderOnce<V> for InteractiveText {
impl RenderOnce for InteractiveText {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
Some(self.id.clone())
Some(self.element_id.clone())
}
fn render_once(self) -> Self::Element {

View File

@ -1,7 +1,7 @@
use crate::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Element, ElementId,
InteractiveElement, InteractiveElementState, Interactivity, LayoutId, Pixels, Point,
RenderOnce, Size, StyleRefinement, Styled, ViewContext,
point, px, size, AnyElement, AvailableSpace, Bounds, Element, ElementId, InteractiveElement,
InteractiveElementState, Interactivity, LayoutId, Pixels, Point, Render, RenderOnce, Size,
StyleRefinement, Styled, View, ViewContext, WindowContext,
};
use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@ -10,31 +10,36 @@ use taffy::style::Overflow;
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
/// uniform_list will only render the visibile subset of items.
pub fn uniform_list<I, V, E>(
pub fn uniform_list<I, R, V>(
view: View<V>,
id: I,
item_count: usize,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<E>,
) -> UniformList<V>
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<R>,
) -> UniformList
where
I: Into<ElementId>,
V: 'static,
E: Element<V>,
R: RenderOnce,
V: Render,
{
let id = id.into();
let mut style = StyleRefinement::default();
style.overflow.y = Some(Overflow::Hidden);
let render_range = move |range, cx: &mut WindowContext| {
view.update(cx, |this, cx| {
f(this, range, cx)
.into_iter()
.map(|component| component.render_into_any())
.collect()
})
};
UniformList {
id: id.clone(),
style,
item_count,
item_to_measure_index: 0,
render_items: Box::new(move |view, visible_range, cx| {
f(view, visible_range, cx)
.into_iter()
.map(|component| component.into_any())
.collect()
}),
render_items: Box::new(render_range),
interactivity: Interactivity {
element_id: Some(id.into()),
..Default::default()
@ -43,19 +48,14 @@ where
}
}
pub struct UniformList<V: 'static> {
pub struct UniformList {
id: ElementId,
style: StyleRefinement,
item_count: usize,
item_to_measure_index: usize,
render_items: Box<
dyn for<'a> Fn(
&'a mut V,
Range<usize>,
&'a mut ViewContext<V>,
) -> SmallVec<[AnyElement<V>; 64]>,
>,
interactivity: Interactivity<V>,
render_items:
Box<dyn for<'a> Fn(Range<usize>, &'a mut WindowContext) -> SmallVec<[AnyElement; 64]>>,
interactivity: Interactivity,
scroll_handle: Option<UniformListScrollHandle>,
}
@ -89,7 +89,7 @@ impl UniformListScrollHandle {
}
}
impl<V: 'static> Styled for UniformList<V> {
impl Styled for UniformList {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
@ -101,25 +101,24 @@ pub struct UniformListState {
item_size: Size<Pixels>,
}
impl<V: 'static> Element<V> for UniformList<V> {
impl Element for UniformList {
type State = UniformListState;
fn layout(
&mut self,
view_state: &mut V,
element_state: Option<Self::State>,
cx: &mut ViewContext<V>,
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let max_items = self.item_count;
let rem_size = cx.rem_size();
let item_size = element_state
let item_size = state
.as_ref()
.map(|s| s.item_size)
.unwrap_or_else(|| self.measure_item(view_state, None, cx));
.unwrap_or_else(|| self.measure_item(None, cx));
let (layout_id, interactive) =
self.interactivity
.layout(element_state.map(|s| s.interactive), cx, |style, cx| {
.layout(state.map(|s| s.interactive), cx, |style, cx| {
cx.request_measured_layout(
style,
rem_size,
@ -157,9 +156,8 @@ impl<V: 'static> Element<V> for UniformList<V> {
fn paint(
self,
bounds: Bounds<crate::Pixels>,
view_state: &mut V,
element_state: &mut Self::State,
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
) {
let style =
self.interactivity
@ -185,9 +183,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
.get_or_insert_with(Rc::default)
.clone();
let item_height = self
.measure_item(view_state, Some(padded_bounds.size.width), cx)
.height;
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
self.interactivity.paint(
bounds,
@ -229,7 +225,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
self.item_count,
);
let items = (self.render_items)(view_state, visible_range.clone(), cx);
let items = (self.render_items)(visible_range.clone(), cx);
cx.with_z_index(1, |cx| {
for (item, ix) in items.into_iter().zip(visible_range) {
let item_origin = padded_bounds.origin
@ -238,7 +234,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, view_state, cx);
item.draw(item_origin, available_space, cx);
}
});
}
@ -248,7 +244,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
}
}
impl<V> RenderOnce<V> for UniformList<V> {
impl RenderOnce for UniformList {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
@ -260,24 +256,19 @@ impl<V> RenderOnce<V> for UniformList<V> {
}
}
impl<V> UniformList<V> {
impl UniformList {
pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
self.item_to_measure_index = item_index.unwrap_or(0);
self
}
fn measure_item(
&self,
view_state: &mut V,
list_width: Option<Pixels>,
cx: &mut ViewContext<V>,
) -> Size<Pixels> {
fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
if self.item_count == 0 {
return Size::default();
}
let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
let mut items = (self.render_items)(view_state, item_ix..item_ix + 1, cx);
let mut items = (self.render_items)(item_ix..item_ix + 1, cx);
let mut item_to_measure = items.pop().unwrap();
let available_space = size(
list_width.map_or(AvailableSpace::MinContent, |width| {
@ -285,7 +276,7 @@ impl<V> UniformList<V> {
}),
AvailableSpace::MinContent,
);
item_to_measure.measure(available_space, view_state, cx)
item_to_measure.measure(available_space, cx)
}
pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
@ -294,8 +285,8 @@ impl<V> UniformList<V> {
}
}
impl<V> InteractiveElement<V> for UniformList<V> {
fn interactivity(&mut self) -> &mut crate::Interactivity<V> {
impl InteractiveElement for UniformList {
fn interactivity(&mut self) -> &mut crate::Interactivity {
&mut self.interactivity
}
}

View File

@ -121,7 +121,7 @@ pub trait VisualContext: Context {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render<V>;
V: 'static + Render;
fn update_view<V: 'static, R>(
&mut self,
@ -134,7 +134,7 @@ pub trait VisualContext: Context {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render<V>;
V: 'static + Render;
fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
where

View File

@ -1,4 +1,6 @@
use crate::{AsyncWindowContext, Bounds, Pixels, PlatformInputHandler, View, ViewContext};
use crate::{
AsyncWindowContext, Bounds, Pixels, PlatformInputHandler, View, ViewContext, WindowContext,
};
use std::ops::Range;
/// Implement this trait to allow views to handle textual input when implementing an editor, field, etc.
@ -43,9 +45,9 @@ pub struct ElementInputHandler<V> {
impl<V: 'static> ElementInputHandler<V> {
/// Used in [Element::paint] with the element's bounds and a view context for its
/// containing view.
pub fn new(element_bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) -> Self {
pub fn new(element_bounds: Bounds<Pixels>, view: View<V>, cx: &mut WindowContext) -> Self {
ElementInputHandler {
view: cx.view().clone(),
view,
element_bounds,
cx: cx.to_async(),
}

View File

@ -1,5 +1,6 @@
use crate::{
div, point, Div, Element, FocusHandle, Keystroke, Modifiers, Pixels, Point, Render, ViewContext,
div, point, Div, Element, FocusHandle, Keystroke, Modifiers, Pixels, Point, Render, RenderOnce,
ViewContext,
};
use smallvec::SmallVec;
use std::{any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf};
@ -63,24 +64,24 @@ pub struct Drag<S, R, V, E>
where
R: Fn(&mut V, &mut ViewContext<V>) -> E,
V: 'static,
E: Element<()>,
E: RenderOnce,
{
pub state: S,
pub render_drag_handle: R,
view_type: PhantomData<V>,
view_element_types: PhantomData<(V, E)>,
}
impl<S, R, V, E> Drag<S, R, V, E>
where
R: Fn(&mut V, &mut ViewContext<V>) -> E,
V: 'static,
E: Element<()>,
E: Element,
{
pub fn new(state: S, render_drag_handle: R) -> Self {
Drag {
state,
render_drag_handle,
view_type: PhantomData,
view_element_types: Default::default(),
}
}
}
@ -192,8 +193,8 @@ impl Deref for MouseExitEvent {
#[derive(Debug, Clone, Default)]
pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
impl Render<Self> for ExternalPaths {
type Element = Div<Self>;
impl Render for ExternalPaths {
type Element = Div;
fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
div() // Intentionally left empty because the platform will render icons for the dragged files
@ -286,7 +287,7 @@ pub struct FocusEvent {
mod test {
use crate::{
self as gpui, div, Div, FocusHandle, InteractiveElement, KeyBinding, Keystroke,
ParentElement, Render, Stateful, TestAppContext, VisualContext,
ParentElement, Render, RenderOnce, Stateful, TestAppContext, VisualContext,
};
struct TestView {
@ -297,16 +298,25 @@ mod test {
actions!(TestAction);
impl Render<Self> for TestView {
type Element = Stateful<Self, Div<Self>>;
impl Render for TestView {
type Element = Stateful<Div>;
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
div().id("testview").child(
div()
.key_context("parent")
.on_key_down(|this: &mut TestView, _, _, _| this.saw_key_down = true)
.on_action(|this: &mut TestView, _: &TestAction, _| this.saw_action = true)
.child(div().key_context("nested").track_focus(&self.focus_handle)),
.on_key_down(cx.listener(|this, _, _| this.saw_key_down = true))
.on_action(
cx.listener(|this: &mut TestView, _: &TestAction, _| {
this.saw_action = true
}),
)
.child(
div()
.key_context("nested")
.track_focus(&self.focus_handle)
.render_once(),
),
)
}
}

View File

@ -2,7 +2,7 @@ use crate::{
black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext,
SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
};
use refineable::{Cascade, Refineable};
use smallvec::SmallVec;
@ -313,7 +313,7 @@ impl Style {
}
/// Paints the background of an element styled with this style.
pub fn paint<V: 'static>(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
let rem_size = cx.rem_size();
cx.with_z_index(0, |cx| {

View File

@ -6,7 +6,7 @@ use crate::{
};
use anyhow::{Context, Result};
use std::{
any::{Any, TypeId},
any::TypeId,
hash::{Hash, Hasher},
};
@ -59,15 +59,15 @@ impl<V: 'static> View<V> {
self.model.read(cx)
}
pub fn render_with<E>(&self, component: E) -> RenderViewWith<E, V>
where
E: 'static + Element<V>,
{
RenderViewWith {
view: self.clone(),
element: Some(component),
}
}
// pub fn render_with<E>(&self, component: E) -> RenderViewWith<E, V>
// where
// E: 'static + Element,
// {
// RenderViewWith {
// view: self.clone(),
// element: Some(component),
// }
// }
pub fn focus_handle(&self, cx: &AppContext) -> FocusHandle
where
@ -77,6 +77,24 @@ impl<V: 'static> View<V> {
}
}
impl<V: Render> Element for View<V> {
type State = Option<AnyElement>;
fn layout(
&mut self,
_state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut element = self.update(cx, |view, cx| view.render(cx).into_any());
let layout_id = element.layout(cx);
(layout_id, Some(element))
}
fn paint(self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
element.take().unwrap().paint(cx);
}
}
impl<V> Clone for View<V> {
fn clone(&self) -> Self {
Self {
@ -151,8 +169,8 @@ impl<V> Eq for WeakView<V> {}
#[derive(Clone, Debug)]
pub struct AnyView {
model: AnyModel,
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, Box<dyn Any>),
paint: fn(&AnyView, Box<dyn Any>, &mut WindowContext),
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
paint: fn(&AnyView, AnyElement, &mut WindowContext),
}
impl AnyView {
@ -199,7 +217,7 @@ impl AnyView {
}
}
impl<V: 'static + Render<V>> From<View<V>> for AnyView {
impl<V: Render> From<View<V>> for AnyView {
fn from(value: View<V>) -> Self {
AnyView {
model: value.model.into_any(),
@ -209,36 +227,24 @@ impl<V: 'static + Render<V>> From<View<V>> for AnyView {
}
}
impl<V: 'static + Render<V>, ParentV: 'static> Element<ParentV> for View<V> {
type State = Option<AnyElement<V>>;
impl Element for AnyView {
type State = Option<AnyElement>;
fn layout(
&mut self,
_parent_view: &mut ParentV,
_state: Option<Self::State>,
cx: &mut ViewContext<ParentV>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
self.update(cx, |view, cx| {
let mut element = view.render(cx).into_any();
let layout_id = element.layout(view, cx);
(layout_id, Some(element))
})
let (layout_id, state) = (self.layout)(self, cx);
(layout_id, Some(state))
}
fn paint(
self,
_: Bounds<Pixels>,
_parent: &mut ParentV,
element: &mut Self::State,
cx: &mut ViewContext<ParentV>,
) {
self.update(cx, |view, cx| {
element.take().unwrap().paint(view, cx);
});
fn paint(self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
(self.paint)(&self, state.take().unwrap(), cx)
}
}
impl<V: 'static + Render<V>, ParentV: 'static> RenderOnce<ParentV> for View<V> {
impl<V: 'static + Render> RenderOnce for View<V> {
type Element = View<V>;
fn element_id(&self) -> Option<ElementId> {
@ -250,31 +256,7 @@ impl<V: 'static + Render<V>, ParentV: 'static> RenderOnce<ParentV> for View<V> {
}
}
impl<V: 'static> Element<V> for AnyView {
type State = Option<Box<dyn Any>>;
fn layout(
&mut self,
_view_state: &mut V,
_element_state: Option<Self::State>,
cx: &mut ViewContext<V>,
) -> (LayoutId, Self::State) {
let (layout_id, rendered_element) = (self.layout)(self, cx);
(layout_id, Some(rendered_element))
}
fn paint(
mut self,
_bounds: Bounds<Pixels>,
_view_state: &mut V,
rendered_element: &mut Self::State,
cx: &mut ViewContext<V>,
) {
(self.paint)(&mut self, rendered_element.take().unwrap(), cx)
}
}
impl<ParentV: 'static> RenderOnce<ParentV> for AnyView {
impl RenderOnce for AnyView {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
@ -288,8 +270,8 @@ impl<ParentV: 'static> RenderOnce<ParentV> for AnyView {
pub struct AnyWeakView {
model: AnyWeakModel,
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, Box<dyn Any>),
paint: fn(&AnyView, Box<dyn Any>, &mut WindowContext),
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
paint: fn(&AnyView, AnyElement, &mut WindowContext),
}
impl AnyWeakView {
@ -303,7 +285,7 @@ impl AnyWeakView {
}
}
impl<V: 'static + Render<V>> From<WeakView<V>> for AnyWeakView {
impl<V: 'static + Render> From<WeakView<V>> for AnyWeakView {
fn from(view: WeakView<V>) -> Self {
Self {
model: view.model.into(),
@ -313,88 +295,40 @@ impl<V: 'static + Render<V>> From<WeakView<V>> for AnyWeakView {
}
}
pub struct RenderViewWith<E, V> {
view: View<V>,
element: Option<E>,
}
impl<E, ParentV, V> Element<ParentV> for RenderViewWith<E, V>
impl<T, E> Render for T
where
E: 'static + Element<V>,
ParentV: 'static,
V: 'static,
T: 'static + FnMut(&mut WindowContext) -> E,
E: 'static + Send + Element,
{
type State = Option<AnyElement<V>>;
type Element = E;
fn layout(
&mut self,
_: &mut ParentV,
_: Option<Self::State>,
cx: &mut ViewContext<ParentV>,
) -> (LayoutId, Self::State) {
self.view.update(cx, |view, cx| {
let mut element = self.element.take().unwrap().into_any();
let layout_id = element.layout(view, cx);
(layout_id, Some(element))
})
}
fn paint(
self,
_: Bounds<Pixels>,
_: &mut ParentV,
element: &mut Self::State,
cx: &mut ViewContext<ParentV>,
) {
self.view
.update(cx, |view, cx| element.take().unwrap().paint(view, cx))
}
}
impl<E, V, ParentV> RenderOnce<ParentV> for RenderViewWith<E, V>
where
E: 'static + Element<V>,
V: 'static,
ParentV: 'static,
{
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
self.element.as_ref().unwrap().element_id()
}
fn render_once(self) -> Self::Element {
self
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
(self)(cx)
}
}
mod any_view {
use crate::{AnyElement, AnyView, BorrowWindow, LayoutId, Render, WindowContext};
use std::any::Any;
use crate::{AnyElement, AnyView, BorrowWindow, Element, LayoutId, Render, WindowContext};
pub(crate) fn layout<V: 'static + Render<V>>(
pub(crate) fn layout<V: 'static + Render>(
view: &AnyView,
cx: &mut WindowContext,
) -> (LayoutId, Box<dyn Any>) {
) -> (LayoutId, AnyElement) {
cx.with_element_id(Some(view.model.entity_id), |cx| {
let view = view.clone().downcast::<V>().unwrap();
view.update(cx, |view, cx| {
let mut element = AnyElement::new(view.render(cx));
let layout_id = element.layout(view, cx);
(layout_id, Box::new(element) as Box<dyn Any>)
})
let mut element = view.update(cx, |view, cx| view.render(cx).into_any());
let layout_id = element.layout(cx);
(layout_id, element)
})
}
pub(crate) fn paint<V: 'static + Render<V>>(
pub(crate) fn paint<V: 'static + Render>(
view: &AnyView,
element: Box<dyn Any>,
element: AnyElement,
cx: &mut WindowContext,
) {
cx.with_element_id(Some(view.model.entity_id), |cx| {
let view = view.clone().downcast::<V>().unwrap();
let element = element.downcast::<AnyElement<V>>().unwrap();
view.update(cx, |view, cx| element.paint(view, cx))
element.paint(cx);
})
}
}

View File

@ -187,7 +187,7 @@ impl Drop for FocusHandle {
/// FocusableView allows users of your view to easily
/// focus it (using cx.focus_view(view))
pub trait FocusableView: 'static + Render<Self> {
pub trait FocusableView: 'static + Render {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
}
@ -1436,6 +1436,82 @@ impl<'a> WindowContext<'a> {
.dispatch_tree
.bindings_for_action(action)
}
pub fn listener_for<V: Render, E>(
&self,
view: &View<V>,
f: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
) -> impl Fn(&E, &mut WindowContext) + 'static {
let view = view.downgrade();
move |e: &E, cx: &mut WindowContext| {
view.update(cx, |view, cx| f(view, e, cx)).ok();
}
}
pub fn constructor_for<V: Render, R>(
&self,
view: &View<V>,
f: impl Fn(&mut V, &mut ViewContext<V>) -> R + 'static,
) -> impl Fn(&mut WindowContext) -> R + 'static {
let view = view.clone();
move |cx: &mut WindowContext| view.update(cx, |view, cx| f(view, cx))
}
//========== ELEMENT RELATED FUNCTIONS ===========
pub fn with_key_dispatch<R>(
&mut self,
context: KeyContext,
focus_handle: Option<FocusHandle>,
f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
) -> R {
let window = &mut self.window;
window
.current_frame
.dispatch_tree
.push_node(context.clone());
if let Some(focus_handle) = focus_handle.as_ref() {
window
.current_frame
.dispatch_tree
.make_focusable(focus_handle.id);
}
let result = f(focus_handle, self);
self.window.current_frame.dispatch_tree.pop_node();
result
}
/// Register a focus listener for the current frame only. It will be cleared
/// on the next frame render. You should use this method only from within elements,
/// and we may want to enforce that better via a different context type.
// todo!() Move this to `FrameContext` to emphasize its individuality?
pub fn on_focus_changed(
&mut self,
listener: impl Fn(&FocusEvent, &mut WindowContext) + 'static,
) {
self.window
.current_frame
.focus_listeners
.push(Box::new(move |event, cx| {
listener(event, cx);
}));
}
/// Set an input handler, such as [ElementInputHandler], which interfaces with the
/// platform to receive textual input with proper integration with concerns such
/// as IME interactions.
pub fn handle_input(
&mut self,
focus_handle: &FocusHandle,
input_handler: impl PlatformInputHandler,
) {
if focus_handle.is_focused(self) {
self.window
.platform_window
.set_input_handler(Box::new(input_handler));
}
}
}
impl Context for WindowContext<'_> {
@ -1520,7 +1596,7 @@ impl VisualContext for WindowContext<'_> {
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render<V>,
V: 'static + Render,
{
let slot = self.app.entities.reserve();
let view = View {
@ -1559,7 +1635,7 @@ impl VisualContext for WindowContext<'_> {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static + Render<V>,
V: 'static + Render,
{
let slot = self.app.entities.reserve();
let view = View {
@ -1617,6 +1693,10 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
self.borrow_mut()
}
fn app(&self) -> &AppContext {
self.borrow()
}
fn window(&self) -> &Window {
self.borrow()
}
@ -2124,49 +2204,6 @@ impl<'a, V: 'static> ViewContext<'a, V> {
)
}
/// Register a focus listener for the current frame only. It will be cleared
/// on the next frame render. You should use this method only from within elements,
/// and we may want to enforce that better via a different context type.
// todo!() Move this to `FrameContext` to emphasize its individuality?
pub fn on_focus_changed(
&mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) {
let handle = self.view().downgrade();
self.window
.current_frame
.focus_listeners
.push(Box::new(move |event, cx| {
handle
.update(cx, |view, cx| listener(view, event, cx))
.log_err();
}));
}
pub fn with_key_dispatch<R>(
&mut self,
context: KeyContext,
focus_handle: Option<FocusHandle>,
f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
) -> R {
let window = &mut self.window;
window
.current_frame
.dispatch_tree
.push_node(context.clone());
if let Some(focus_handle) = focus_handle.as_ref() {
window
.current_frame
.dispatch_tree
.make_focusable(focus_handle.id);
}
let result = f(focus_handle, self);
self.window.current_frame.dispatch_tree.pop_node();
result
}
pub fn spawn<Fut, R>(
&mut self,
f: impl FnOnce(WeakView<V>, AsyncWindowContext) -> Fut,
@ -2243,21 +2280,6 @@ impl<'a, V: 'static> ViewContext<'a, V> {
});
}
/// Set an input handler, such as [ElementInputHandler], which interfaces with the
/// platform to receive textual input with proper integration with concerns such
/// as IME interactions.
pub fn handle_input(
&mut self,
focus_handle: &FocusHandle,
input_handler: impl PlatformInputHandler,
) {
if focus_handle.is_focused(self) {
self.window
.platform_window
.set_input_handler(Box::new(input_handler));
}
}
pub fn emit<Evt>(&mut self, event: Evt)
where
Evt: 'static,
@ -2284,6 +2306,16 @@ impl<'a, V: 'static> ViewContext<'a, V> {
{
self.defer(|_, cx| cx.emit(Manager::Dismiss))
}
pub fn listener<E>(
&self,
f: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
) -> impl Fn(&E, &mut WindowContext) + 'static {
let view = self.view().downgrade();
move |e: &E, cx: &mut WindowContext| {
view.update(cx, |view, cx| f(view, e, cx)).ok();
}
}
}
impl<V> Context for ViewContext<'_, V> {
@ -2335,7 +2367,7 @@ impl<V> Context for ViewContext<'_, V> {
}
impl<V: 'static> VisualContext for ViewContext<'_, V> {
fn build_view<W: Render<W> + 'static>(
fn build_view<W: Render + 'static>(
&mut self,
build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W,
) -> Self::Result<View<W>> {
@ -2355,7 +2387,7 @@ impl<V: 'static> VisualContext for ViewContext<'_, V> {
build_view: impl FnOnce(&mut ViewContext<'_, W>) -> W,
) -> Self::Result<View<W>>
where
W: 'static + Render<W>,
W: 'static + Render,
{
self.window_cx.replace_root_view(build_view)
}
@ -2400,7 +2432,7 @@ pub struct WindowHandle<V> {
state_type: PhantomData<V>,
}
impl<V: 'static + Render<V>> WindowHandle<V> {
impl<V: 'static + Render> WindowHandle<V> {
pub fn new(id: WindowId) -> Self {
WindowHandle {
any_handle: AnyWindowHandle {

View File

@ -1,37 +1,17 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, parse_quote, DeriveInput};
use syn::{parse_macro_input, DeriveInput};
pub fn derive_render_once(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let type_name = &ast.ident;
let mut trait_generics = ast.generics.clone();
let view_type = if let Some(view_type) = specified_view_type(&ast) {
quote! { #view_type }
} else {
if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
if let syn::GenericParam::Type(type_param) = param {
Some(type_param.ident.clone())
} else {
None
}
}) {
quote! { #first_type_param }
} else {
trait_generics.params.push(parse_quote! { V: 'static });
quote! { V }
}
};
let (impl_generics, _, where_clause) = trait_generics.split_for_impl();
let (_, type_generics, _) = ast.generics.split_for_impl();
let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
let gen = quote! {
impl #impl_generics gpui::RenderOnce<#view_type> for #type_name #type_generics
impl #impl_generics gpui::RenderOnce for #type_name #type_generics
#where_clause
{
type Element = gpui::CompositeElement<#view_type, Self>;
type Element = gpui::CompositeElement<Self>;
fn element_id(&self) -> Option<ElementId> {
None
@ -45,20 +25,3 @@ pub fn derive_render_once(input: TokenStream) -> TokenStream {
gen.into()
}
fn specified_view_type(ast: &DeriveInput) -> Option<proc_macro2::Ident> {
ast.attrs.iter().find_map(|attr| {
if attr.path.is_ident("view") {
if let Ok(syn::Meta::NameValue(meta_name_value)) = attr.parse_meta() {
if let syn::Lit::Str(lit_str) = meta_name_value.lit {
return Some(
lit_str
.parse::<syn::Ident>()
.expect("Failed to parse view_type"),
);
}
}
}
None
})
}

View File

@ -61,12 +61,14 @@ fn build_bridge(swift_target: &SwiftTarget) {
let swift_package_root = swift_package_root();
let swift_target_folder = swift_target_folder();
let swift_cache_folder = swift_cache_folder();
if !Command::new("swift")
.arg("build")
.arg("--disable-automatic-resolution")
.args(["--configuration", &env::var("PROFILE").unwrap()])
.args(["--triple", &swift_target.target.triple])
.args(["--build-path".into(), swift_target_folder])
.args(["--cache-path".into(), swift_cache_folder])
.current_dir(&swift_package_root)
.status()
.unwrap()
@ -133,9 +135,17 @@ fn swift_package_root() -> PathBuf {
}
fn swift_target_folder() -> PathBuf {
let target = env::var("TARGET").unwrap();
env::current_dir()
.unwrap()
.join(format!("../../target/{SWIFT_PACKAGE_NAME}"))
.join(format!("../../target/{target}/{SWIFT_PACKAGE_NAME}_target"))
}
fn swift_cache_folder() -> PathBuf {
let target = env::var("TARGET").unwrap();
env::current_dir()
.unwrap()
.join(format!("../../target/{target}/{SWIFT_PACKAGE_NAME}_cache"))
}
fn copy_dir(source: &Path, destination: &Path) {

View File

@ -1,7 +1,7 @@
use editor::Editor;
use gpui::{
div, prelude::*, uniform_list, AppContext, Div, FocusHandle, FocusableView, MouseButton,
Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext,
MouseDownEvent, Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext,
};
use std::{cmp, sync::Arc};
use ui::{prelude::*, v_stack, Divider, Label, TextColor};
@ -15,7 +15,7 @@ pub struct Picker<D: PickerDelegate> {
}
pub trait PickerDelegate: Sized + 'static {
type ListItem: RenderOnce<Picker<Self>>;
type ListItem: RenderOnce;
fn match_count(&self) -> usize;
fn selected_index(&self) -> usize;
@ -180,21 +180,21 @@ impl<D: PickerDelegate> Picker<D> {
}
}
impl<D: PickerDelegate> Render<Self> for Picker<D> {
type Element = Div<Self>;
impl<D: PickerDelegate> Render for Picker<D> {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div()
.key_context("picker")
.size_full()
.elevation_2(cx)
.on_action(Self::select_next)
.on_action(Self::select_prev)
.on_action(Self::select_first)
.on_action(Self::select_last)
.on_action(Self::cancel)
.on_action(Self::confirm)
.on_action(Self::secondary_confirm)
.on_action(cx.listener(Self::select_next))
.on_action(cx.listener(Self::select_prev))
.on_action(cx.listener(Self::select_first))
.on_action(cx.listener(Self::select_last))
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::secondary_confirm))
.child(
v_stack()
.py_0p5()
@ -208,31 +208,37 @@ impl<D: PickerDelegate> Render<Self> for Picker<D> {
.p_1()
.grow()
.child(
uniform_list("candidates", self.delegate.match_count(), {
move |this: &mut Self, visible_range, cx| {
let selected_ix = this.delegate.selected_index();
visible_range
.map(|ix| {
div()
.on_mouse_down(
MouseButton::Left,
move |this: &mut Self, event, cx| {
this.handle_click(
ix,
event.modifiers.command,
cx,
)
},
)
.child(this.delegate.render_match(
ix,
ix == selected_ix,
cx,
))
})
.collect()
}
})
uniform_list(
cx.view().clone(),
"candidates",
self.delegate.match_count(),
{
let selected_index = self.delegate.selected_index();
move |picker, visible_range, cx| {
visible_range
.map(|ix| {
div()
.on_mouse_down(
MouseButton::Left,
cx.listener(move |this, event: &MouseDownEvent, cx| {
this.handle_click(
ix,
event.modifiers.command,
cx,
)
}),
)
.child(picker.delegate.render_match(
ix,
ix == selected_index,
cx,
))
})
.collect()
}
},
)
.track_scroll(self.scroll_handle.clone()),
)
.max_h_72()

View File

@ -9,7 +9,6 @@ path = "src/project_panel.rs"
doctest = false
[dependencies]
context_menu = { path = "../context_menu" }
collections = { path = "../collections" }
db = { path = "../db2", package = "db2" }
editor = { path = "../editor2", package = "editor2" }

View File

@ -10,9 +10,9 @@ use anyhow::{anyhow, Result};
use gpui::{
actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement,
Model, MouseButton, ParentElement, Pixels, Point, PromptLevel, Render, RenderOnce, Stateful,
StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, ViewContext,
VisualContext as _, WeakView, WindowContext,
Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render,
RenderOnce, Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View,
ViewContext, VisualContext as _, WeakView, WindowContext,
};
use menu::{Confirm, SelectNext, SelectPrev};
use project::{
@ -1339,7 +1339,7 @@ impl ProjectPanel {
editor: Option<&View<Editor>>,
padding: Pixels,
cx: &mut ViewContext<Self>,
) -> Div<Self> {
) -> Div {
let show_editor = details.is_editing && !details.is_processing;
let theme = cx.theme();
@ -1378,7 +1378,7 @@ impl ProjectPanel {
details: EntryDetails,
// dragged_entry_destination: &mut Option<Arc<Path>>,
cx: &mut ViewContext<Self>,
) -> Stateful<Self, Div<Self>> {
) -> Stateful<Div> {
let kind = details.kind;
let settings = ProjectPanelSettings::get_global(cx);
const INDENT_SIZE: Pixels = px(16.0);
@ -1396,7 +1396,7 @@ impl ProjectPanel {
this.bg(cx.theme().colors().element_selected)
})
.hover(|style| style.bg(cx.theme().colors().element_hover))
.on_click(move |this, event, cx| {
.on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
if !show_editor {
if kind.is_dir() {
this.toggle_expanded(entry_id, cx);
@ -1408,10 +1408,13 @@ impl ProjectPanel {
}
}
}
})
.on_mouse_down(MouseButton::Right, move |this, event, cx| {
this.deploy_context_menu(event.position, entry_id, cx);
})
}))
.on_mouse_down(
MouseButton::Right,
cx.listener(move |this, event: &MouseDownEvent, cx| {
this.deploy_context_menu(event.position, entry_id, cx);
}),
)
// .on_drop::<ProjectEntryId>(|this, event, cx| {
// this.move_entry(
// *dragged_entry,
@ -1423,10 +1426,10 @@ impl ProjectPanel {
}
}
impl Render<Self> for ProjectPanel {
type Element = Focusable<Self, Stateful<Self, Div<Self>>>;
impl Render for ProjectPanel {
type Element = Focusable<Stateful<Div>>;
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> Self::Element {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let has_worktree = self.visible_entries.len() != 0;
if has_worktree {
@ -1434,40 +1437,43 @@ impl Render<Self> for ProjectPanel {
.id("project-panel")
.size_full()
.key_context("ProjectPanel")
.on_action(Self::select_next)
.on_action(Self::select_prev)
.on_action(Self::expand_selected_entry)
.on_action(Self::collapse_selected_entry)
.on_action(Self::collapse_all_entries)
.on_action(Self::new_file)
.on_action(Self::new_directory)
.on_action(Self::rename)
.on_action(Self::delete)
.on_action(Self::confirm)
.on_action(Self::open_file)
.on_action(Self::cancel)
.on_action(Self::cut)
.on_action(Self::copy)
.on_action(Self::copy_path)
.on_action(Self::copy_relative_path)
.on_action(Self::paste)
.on_action(Self::reveal_in_finder)
.on_action(Self::open_in_terminal)
.on_action(Self::new_search_in_directory)
.on_action(cx.listener(Self::select_next))
.on_action(cx.listener(Self::select_prev))
.on_action(cx.listener(Self::expand_selected_entry))
.on_action(cx.listener(Self::collapse_selected_entry))
.on_action(cx.listener(Self::collapse_all_entries))
.on_action(cx.listener(Self::new_file))
.on_action(cx.listener(Self::new_directory))
.on_action(cx.listener(Self::rename))
.on_action(cx.listener(Self::delete))
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::open_file))
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::cut))
.on_action(cx.listener(Self::copy))
.on_action(cx.listener(Self::copy_path))
.on_action(cx.listener(Self::copy_relative_path))
.on_action(cx.listener(Self::paste))
.on_action(cx.listener(Self::reveal_in_finder))
.on_action(cx.listener(Self::open_in_terminal))
.on_action(cx.listener(Self::new_search_in_directory))
.track_focus(&self.focus_handle)
.child(
uniform_list(
cx.view().clone(),
"entries",
self.visible_entries
.iter()
.map(|(_, worktree_entries)| worktree_entries.len())
.sum(),
|this: &mut Self, range, cx| {
let mut items = Vec::new();
this.for_each_visible_entry(range, cx, |id, details, cx| {
items.push(this.render_entry(id, details, cx));
});
items
{
|this, range, cx| {
let mut items = Vec::new();
this.for_each_visible_entry(range, cx, |id, details, cx| {
items.push(this.render_entry(id, details, cx));
});
items
}
},
)
.size_full()

View File

@ -56,12 +56,12 @@ pub struct Mention {
}
impl RichText {
pub fn element<V: 'static>(
pub fn element(
&self,
// syntax: Arc<SyntaxTheme>,
// style: RichTextStyle,
// cx: &mut ViewContext<V>,
) -> AnyElement<V> {
) -> AnyElement {
todo!();
// let mut region_id = 0;

View File

@ -5,8 +5,8 @@ use ui::prelude::*;
pub struct ColorsStory;
impl Render<Self> for ColorsStory {
type Element = Div<Self>;
impl Render for ColorsStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let color_scales = default_color_scales();

View File

@ -27,7 +27,7 @@ impl FocusStory {
}
impl Render<Self> for FocusStory {
type Element = Focusable<Self, Stateful<Self, Div<Self>>>;
type Element = Focusable<Stateful<Div>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
@ -42,18 +42,20 @@ impl Render<Self> for FocusStory {
.id("parent")
.focusable()
.key_context("parent")
.on_action(|_, action: &ActionA, cx| {
.on_action(cx.listener(|_, action: &ActionA, cx| {
println!("Action A dispatched on parent");
})
.on_action(|_, action: &ActionB, cx| {
}))
.on_action(cx.listener(|_, action: &ActionB, cx| {
println!("Action B dispatched on parent");
})
.on_focus(|_, _, _| println!("Parent focused"))
.on_blur(|_, _, _| println!("Parent blurred"))
.on_focus_in(|_, _, _| println!("Parent focus_in"))
.on_focus_out(|_, _, _| println!("Parent focus_out"))
.on_key_down(|_, event, phase, _| println!("Key down on parent {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on parent {:?}", event))
}))
.on_focus(cx.listener(|_, _, _| println!("Parent focused")))
.on_blur(cx.listener(|_, _, _| println!("Parent blurred")))
.on_focus_in(cx.listener(|_, _, _| println!("Parent focus_in")))
.on_focus_out(cx.listener(|_, _, _| println!("Parent focus_out")))
.on_key_down(
cx.listener(|_, event, phase, _| println!("Key down on parent {:?}", event)),
)
.on_key_up(cx.listener(|_, event, phase, _| println!("Key up on parent {:?}", event)))
.size_full()
.bg(color_1)
.focus(|style| style.bg(color_2))
@ -61,38 +63,42 @@ impl Render<Self> for FocusStory {
div()
.track_focus(&self.child_1_focus)
.key_context("child-1")
.on_action(|_, action: &ActionB, cx| {
.on_action(cx.listener(|_, action: &ActionB, cx| {
println!("Action B dispatched on child 1 during");
})
}))
.w_full()
.h_6()
.bg(color_4)
.focus(|style| style.bg(color_5))
.in_focus(|style| style.bg(color_6))
.on_focus(|_, _, _| println!("Child 1 focused"))
.on_blur(|_, _, _| println!("Child 1 blurred"))
.on_focus_in(|_, _, _| println!("Child 1 focus_in"))
.on_focus_out(|_, _, _| println!("Child 1 focus_out"))
.on_key_down(|_, event, phase, _| println!("Key down on child 1 {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on child 1 {:?}", event))
.on_focus(cx.listener(|_, _, _| println!("Child 1 focused")))
.on_blur(cx.listener(|_, _, _| println!("Child 1 blurred")))
.on_focus_in(cx.listener(|_, _, _| println!("Child 1 focus_in")))
.on_focus_out(cx.listener(|_, _, _| println!("Child 1 focus_out")))
.on_key_down(
cx.listener(|_, event, _| println!("Key down on child 1 {:?}", event)),
)
.on_key_up(cx.listener(|_, event, _| println!("Key up on child 1 {:?}", event)))
.child("Child 1"),
)
.child(
div()
.track_focus(&self.child_2_focus)
.key_context("child-2")
.on_action(|_, action: &ActionC, cx| {
.on_action(cx.listener(|_, action: &ActionC, cx| {
println!("Action C dispatched on child 2");
})
}))
.w_full()
.h_6()
.bg(color_4)
.on_focus(|_, _, _| println!("Child 2 focused"))
.on_blur(|_, _, _| println!("Child 2 blurred"))
.on_focus_in(|_, _, _| println!("Child 2 focus_in"))
.on_focus_out(|_, _, _| println!("Child 2 focus_out"))
.on_key_down(|_, event, phase, _| println!("Key down on child 2 {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on child 2 {:?}", event))
.on_focus(cx.listener(|_, _, _| println!("Child 2 focused")))
.on_blur(cx.listener(|_, _, _| println!("Child 2 blurred")))
.on_focus_in(cx.listener(|_, _, _| println!("Child 2 focus_in")))
.on_focus_out(cx.listener(|_, _, _| println!("Child 2 focus_out")))
.on_key_down(
cx.listener(|_, event, _| println!("Key down on child 2 {:?}", event)),
)
.on_key_up(cx.listener(|_, event, _| println!("Key up on child 2 {:?}", event)))
.child("Child 2"),
)
}

View File

@ -11,8 +11,8 @@ impl KitchenSinkStory {
}
}
impl Render<Self> for KitchenSinkStory {
type Element = Stateful<Self, Div<Self>>;
impl Render for KitchenSinkStory {
type Element = Stateful<Div>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let component_stories = ComponentStory::iter()

View File

@ -11,8 +11,8 @@ impl TextStory {
}
}
impl Render<Self> for TextStory {
type Element = Div<Self>;
impl Render for TextStory {
type Element = Div;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
v_stack()

View File

@ -335,8 +335,8 @@ impl TerminalPanel {
impl EventEmitter<PanelEvent> for TerminalPanel {}
impl Render<Self> for TerminalPanel {
type Element = Div<Self>;
impl Render for TerminalPanel {
type Element = Div;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
div().child(self.pane.clone())

View File

@ -9,10 +9,10 @@ pub mod terminal_panel;
// use crate::terminal_element::TerminalElement;
use editor::{scroll::autoscroll::Autoscroll, Editor};
use gpui::{
actions, div, Action, AnyElement, AppContext, DispatchPhase, Div, Element, EventEmitter,
FocusEvent, FocusHandle, Focusable, FocusableElement, FocusableView, InputHandler,
InteractiveElement, KeyDownEvent, Keystroke, Model, MouseButton, ParentElement, Pixels, Render,
SharedString, Styled, Task, View, ViewContext, VisualContext, WeakView,
actions, div, Action, AnyElement, AppContext, Div, Element, EventEmitter, FocusEvent,
FocusHandle, Focusable, FocusableElement, FocusableView, InputHandler, InteractiveElement,
KeyDownEvent, Keystroke, Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Render,
SharedString, Styled, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
};
use language::Bias;
use persistence::TERMINAL_DB;
@ -31,7 +31,7 @@ use workspace::{
notifications::NotifyResultExt,
register_deserializable_item,
searchable::{SearchEvent, SearchOptions, SearchableItem},
ui::{ContextMenu, Icon, IconElement, Label, ListEntry},
ui::{ContextMenu, Icon, IconElement, Label, ListItem},
CloseActiveItem, NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
};
@ -63,7 +63,6 @@ pub struct SendKeystroke(String);
actions!(Clear, Copy, Paste, ShowCharacterPalette, SearchTest);
pub fn init(cx: &mut AppContext) {
workspace::ui::init(cx);
terminal_panel::init(cx);
terminal::init(cx);
@ -84,7 +83,7 @@ pub struct TerminalView {
has_new_content: bool,
//Currently using iTerm bell, show bell emoji in tab until input is received
has_bell: bool,
context_menu: Option<View<ContextMenu<Self>>>,
context_menu: Option<View<ContextMenu>>,
blink_state: bool,
blinking_on: bool,
blinking_paused: bool,
@ -300,9 +299,9 @@ impl TerminalView {
cx: &mut ViewContext<Self>,
) {
self.context_menu = Some(ContextMenu::build(cx, |menu, _| {
menu.action(ListEntry::new(Label::new("Clear")), Box::new(Clear))
menu.action(ListItem::new("clear", Label::new("Clear")), Box::new(Clear))
.action(
ListEntry::new(Label::new("Close")),
ListItem::new("close", Label::new("Close")),
Box::new(CloseActiveItem { save_intent: None }),
)
}));
@ -505,12 +504,7 @@ pub fn regex_search_for_query(query: &project::search::SearchQuery) -> Option<Re
}
impl TerminalView {
fn key_down(
&mut self,
event: &KeyDownEvent,
_dispatch_phase: DispatchPhase,
cx: &mut ViewContext<Self>,
) {
fn key_down(&mut self, event: &KeyDownEvent, cx: &mut ViewContext<Self>) {
self.clear_bel(cx);
self.pause_cursor_blinking(cx);
@ -537,8 +531,8 @@ impl TerminalView {
}
}
impl Render<Self> for TerminalView {
type Element = Focusable<Self, Div<Self>>;
impl Render for TerminalView {
type Element = Focusable<Div>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let terminal_handle = self.terminal.clone().downgrade();
@ -552,14 +546,14 @@ impl Render<Self> for TerminalView {
div()
.z_index(0)
.absolute()
.on_key_down(Self::key_down)
.on_action(TerminalView::send_text)
.on_action(TerminalView::send_keystroke)
.on_action(TerminalView::copy)
.on_action(TerminalView::paste)
.on_action(TerminalView::clear)
.on_action(TerminalView::show_character_palette)
.on_action(TerminalView::select_all)
.on_key_down(cx.listener(Self::key_down))
.on_action(cx.listener(TerminalView::send_text))
.on_action(cx.listener(TerminalView::send_keystroke))
.on_action(cx.listener(TerminalView::copy))
.on_action(cx.listener(TerminalView::paste))
.on_action(cx.listener(TerminalView::clear))
.on_action(cx.listener(TerminalView::show_character_palette))
.on_action(cx.listener(TerminalView::select_all))
// todo!()
.child(
"TERMINAL HERE", // TerminalElement::new(
@ -569,10 +563,13 @@ impl Render<Self> for TerminalView {
// self.can_navigate_to_selected_word,
// )
)
.on_mouse_down(MouseButton::Right, |this, event, cx| {
this.deploy_context_menu(event.position, cx);
cx.notify();
}),
.on_mouse_down(
MouseButton::Right,
cx.listener(|this, event: &MouseDownEvent, cx| {
this.deploy_context_menu(event.position, cx);
cx.notify();
}),
),
)
.children(
self.context_menu
@ -580,8 +577,8 @@ impl Render<Self> for TerminalView {
.map(|context_menu| div().z_index(1).absolute().child(context_menu)),
)
.track_focus(&self.focus_handle)
.on_focus_in(Self::focus_in)
.on_focus_out(Self::focus_out)
.on_focus_in(cx.listener(Self::focus_in))
.on_focus_out(cx.listener(Self::focus_out))
}
}
@ -746,11 +743,7 @@ impl Item for TerminalView {
Some(self.terminal().read(cx).title().into())
}
fn tab_content<T: 'static>(
&self,
_detail: Option<usize>,
cx: &gpui::AppContext,
) -> AnyElement<T> {
fn tab_content(&self, _detail: Option<usize>, cx: &WindowContext) -> AnyElement {
let title = self.terminal().read(cx).title();
div()

View File

@ -1,11 +1,11 @@
use gpui::{div, Div, Element, ParentElement, SharedString, Styled, ViewContext};
use gpui::{div, Div, Element, ParentElement, SharedString, Styled, WindowContext};
use crate::ActiveTheme;
pub struct Story {}
impl Story {
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
pub fn container(cx: &mut WindowContext) -> Div {
div()
.size_full()
.flex()
@ -16,21 +16,18 @@ impl Story {
.bg(cx.theme().colors().background)
}
pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: SharedString) -> impl Element<V> {
pub fn title(cx: &mut WindowContext, title: SharedString) -> impl Element {
div()
.text_xl()
.text_color(cx.theme().colors().text)
.child(title)
}
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Element<V> {
pub fn title_for<T>(cx: &mut WindowContext) -> impl Element {
Self::title(cx, std::any::type_name::<T>().into())
}
pub fn label<V: 'static>(
cx: &mut ViewContext<V>,
label: impl Into<SharedString>,
) -> impl Element<V> {
pub fn label(cx: &mut WindowContext, label: impl Into<SharedString>) -> impl Element {
div()
.mt_4()
.mb_2()

View File

@ -147,8 +147,8 @@ mod stories {
pub struct PlayerStory;
impl Render<Self> for PlayerStory {
type Element = Div<Self>;
impl Render for PlayerStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx).child(
@ -156,7 +156,7 @@ mod stories {
.flex()
.flex_col()
.gap_4()
.child(Story::title_for::<_, PlayerColors>(cx))
.child(Story::title_for::<PlayerColors>(cx))
.child(Story::label(cx, "Player Colors"))
.child(
div()

View File

@ -63,6 +63,12 @@ impl ActiveTheme for AppContext {
}
}
// impl<'a> ActiveTheme for WindowContext<'a> {
// fn theme(&self) -> &Arc<Theme> {
// &ThemeSettings::get_global(self.app()).active_theme
// }
// }
pub struct ThemeFamily {
pub id: String,
pub name: SharedString,

View File

@ -7,10 +7,10 @@ pub struct Avatar {
shape: Shape,
}
impl<V: 'static> Component<V> for Avatar {
type Rendered = Img<V>;
impl Component for Avatar {
type Rendered = Img;
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, _: &mut WindowContext) -> Self::Rendered {
let mut img = img();
if self.shape == Shape::Circle {
@ -51,12 +51,12 @@ mod stories {
pub struct AvatarStory;
impl Render<Self> for AvatarStory {
type Element = Div<Self>;
impl Render for AvatarStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
fn render(&mut self, cx: &mut WindowContext) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, Avatar>(cx))
.child(Story::title_for::<Avatar>(cx))
.child(Story::label(cx, "Default"))
.child(Avatar::new(
"https://avatars.githubusercontent.com/u/1714999?v=4",

View File

@ -1,7 +1,7 @@
use std::sync::Arc;
use std::rc::Rc;
use gpui::{
DefiniteLength, Div, Hsla, MouseButton, RenderOnce, Stateful, StatefulInteractiveElement,
DefiniteLength, Div, Hsla, MouseButton, MouseDownEvent, RenderOnce, StatefulInteractiveElement,
WindowContext,
};
@ -10,19 +10,19 @@ use crate::{h_stack, Icon, IconButton, IconElement, Label, LineHeightStyle, Text
/// Provides the flexibility to use either a standard
/// button or an icon button in a given context.
pub enum ButtonOrIconButton<V: 'static> {
Button(Button<V>),
IconButton(IconButton<V>),
pub enum ButtonOrIconButton {
Button(Button),
IconButton(IconButton),
}
impl<V: 'static> From<Button<V>> for ButtonOrIconButton<V> {
fn from(value: Button<V>) -> Self {
impl From<Button> for ButtonOrIconButton {
fn from(value: Button) -> Self {
Self::Button(value)
}
}
impl<V: 'static> From<IconButton<V>> for ButtonOrIconButton<V> {
fn from(value: IconButton<V>) -> Self {
impl From<IconButton> for ButtonOrIconButton {
fn from(value: IconButton) -> Self {
Self::IconButton(value)
}
}
@ -64,25 +64,10 @@ impl ButtonVariant {
}
}
pub type ClickHandler<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>)>;
struct ButtonHandlers<V: 'static> {
click: Option<ClickHandler<V>>,
}
unsafe impl<S> Send for ButtonHandlers<S> {}
unsafe impl<S> Sync for ButtonHandlers<S> {}
impl<V: 'static> Default for ButtonHandlers<V> {
fn default() -> Self {
Self { click: None }
}
}
#[derive(RenderOnce)]
pub struct Button<V: 'static> {
pub struct Button {
disabled: bool,
handlers: ButtonHandlers<V>,
click_handler: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext)>>,
icon: Option<Icon>,
icon_position: Option<IconPosition>,
label: SharedString,
@ -91,11 +76,10 @@ pub struct Button<V: 'static> {
color: Option<TextColor>,
}
impl<V: 'static> Component<V> for Button<V> {
type Rendered = Stateful<V, Div<V>>;
impl Component for Button {
type Rendered = gpui::Stateful<Div>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let _view: &mut V = view;
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let (icon_color, label_color) = match (self.disabled, self.color) {
(true, _) => (TextColor::Disabled, TextColor::Disabled),
(_, None) => (TextColor::Default, TextColor::Default),
@ -133,9 +117,9 @@ impl<V: 'static> Component<V> for Button<V> {
button = button.w(width).justify_center();
}
if let Some(click_handler) = self.handlers.click.clone() {
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
click_handler(state, cx);
if let Some(click_handler) = self.click_handler.clone() {
button = button.on_mouse_down(MouseButton::Left, move |event, cx| {
click_handler(event, cx);
});
}
@ -143,11 +127,11 @@ impl<V: 'static> Component<V> for Button<V> {
}
}
impl<V: 'static> Button<V> {
impl Button {
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
disabled: false,
handlers: ButtonHandlers::default(),
click_handler: None,
icon: None,
icon_position: None,
label: label.into(),
@ -184,8 +168,11 @@ impl<V: 'static> Button<V> {
self
}
pub fn on_click(mut self, handler: ClickHandler<V>) -> Self {
self.handlers.click = Some(handler);
pub fn on_click(
mut self,
handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) -> Self {
self.click_handler = Some(Rc::new(handler));
self
}
@ -218,76 +205,29 @@ impl<V: 'static> Button<V> {
fn render_icon(&self, icon_color: TextColor) -> Option<IconElement> {
self.icon.map(|i| IconElement::new(i).color(icon_color))
}
pub fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
let (icon_color, label_color) = match (self.disabled, self.color) {
(true, _) => (TextColor::Disabled, TextColor::Disabled),
(_, None) => (TextColor::Default, TextColor::Default),
(_, Some(color)) => (TextColor::from(color), color),
};
let mut button = h_stack()
.id(SharedString::from(format!("{}", self.label)))
.relative()
.p_1()
.text_ui()
.rounded_md()
.bg(self.variant.bg_color(cx))
.cursor_pointer()
.hover(|style| style.bg(self.variant.bg_color_hover(cx)))
.active(|style| style.bg(self.variant.bg_color_active(cx)));
match (self.icon, self.icon_position) {
(Some(_), Some(IconPosition::Left)) => {
button = button
.gap_1()
.child(self.render_label(label_color))
.children(self.render_icon(icon_color))
}
(Some(_), Some(IconPosition::Right)) => {
button = button
.gap_1()
.children(self.render_icon(icon_color))
.child(self.render_label(label_color))
}
(_, _) => button = button.child(self.render_label(label_color)),
}
if let Some(width) = self.width {
button = button.w(width).justify_center();
}
if let Some(click_handler) = self.handlers.click.clone() {
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
click_handler(state, cx);
});
}
button
}
}
#[derive(RenderOnce)]
pub struct ButtonGroup<V: 'static> {
buttons: Vec<Button<V>>,
pub struct ButtonGroup {
buttons: Vec<Button>,
}
impl<V: 'static> Component<V> for ButtonGroup<V> {
type Rendered = Div<V>;
impl Component for ButtonGroup {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let mut group = h_stack();
for button in self.buttons.into_iter() {
group = group.child(button.render(view, cx));
group = group.child(button.render(cx));
}
group
}
}
impl<V: 'static> ButtonGroup<V> {
pub fn new(buttons: Vec<Button<V>>) -> Self {
impl ButtonGroup {
pub fn new(buttons: Vec<Button>) -> Self {
Self { buttons }
}
}
@ -304,14 +244,14 @@ mod stories {
pub struct ButtonStory;
impl Render<Self> for ButtonStory {
type Element = Div<Self>;
impl Render for ButtonStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let states = InteractionState::iter();
Story::container(cx)
.child(Story::title_for::<_, Button<Self>>(cx))
.child(Story::title_for::<_, Button>(cx))
.child(
div()
.flex()
@ -454,7 +394,7 @@ mod stories {
.child(
Button::new("Label")
.variant(ButtonVariant::Ghost)
.on_click(Arc::new(|_view, _cx| println!("Button clicked."))),
.on_click(cx.callback(|_view, _, cx| println!("Button clicked."))),
)
}
}

View File

@ -1,10 +1,10 @@
use gpui::{div, prelude::*, Div, Element, ElementId, RenderOnce, Stateful, Styled, ViewContext};
use std::sync::Arc;
use gpui::{div, prelude::*, Div, Element, ElementId, RenderOnce, Styled, WindowContext};
use theme2::ActiveTheme;
use crate::{Icon, IconElement, Selection, TextColor};
pub type CheckHandler<V> = Arc<dyn Fn(Selection, &mut V, &mut ViewContext<V>) + Send + Sync>;
pub type CheckHandler = Box<dyn Fn(&Selection, &mut WindowContext) + 'static>;
/// # Checkbox
///
@ -12,17 +12,17 @@ pub type CheckHandler<V> = Arc<dyn Fn(Selection, &mut V, &mut ViewContext<V>) +
/// Each checkbox works independently from other checkboxes in the list,
/// therefore checking an additional box does not affect any other selections.
#[derive(RenderOnce)]
pub struct Checkbox<V: 'static> {
pub struct Checkbox {
id: ElementId,
checked: Selection,
disabled: bool,
on_click: Option<CheckHandler<V>>,
on_click: Option<CheckHandler>,
}
impl<V: 'static> Component<V> for Checkbox<V> {
type Rendered = Stateful<V, Div<V>>;
impl Component for Checkbox {
type Rendered = gpui::Stateful<Div>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let group_id = format!("checkbox_group_{:?}", self.id);
let icon = match self.checked {
@ -137,13 +137,11 @@ impl<V: 'static> Component<V> for Checkbox<V> {
)
.when_some(
self.on_click.filter(|_| !self.disabled),
|this, on_click| {
this.on_click(move |view, _, cx| on_click(self.checked.inverse(), view, cx))
},
|this, on_click| this.on_click(move |_, cx| on_click(&self.checked.inverse(), cx)),
)
}
}
impl<V: 'static> Checkbox<V> {
impl Checkbox {
pub fn new(id: impl Into<ElementId>, checked: Selection) -> Self {
Self {
id: id.into(),
@ -160,13 +158,13 @@ impl<V: 'static> Checkbox<V> {
pub fn on_click(
mut self,
handler: impl 'static + Fn(Selection, &mut V, &mut ViewContext<V>) + Send + Sync,
handler: impl 'static + Fn(&Selection, &mut WindowContext) + Send + Sync,
) -> Self {
self.on_click = Some(Arc::new(handler));
self.on_click = Some(Box::new(handler));
self
}
pub fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
pub fn render(self, cx: &mut WindowContext) -> impl Element {
let group_id = format!("checkbox_group_{:?}", self.id);
let icon = match self.checked {
@ -281,9 +279,7 @@ impl<V: 'static> Checkbox<V> {
)
.when_some(
self.on_click.filter(|_| !self.disabled),
|this, on_click| {
this.on_click(move |view, _, cx| on_click(self.checked.inverse(), view, cx))
},
|this, on_click| this.on_click(move |_, cx| on_click(&self.checked.inverse(), cx)),
)
}
}
@ -299,8 +295,8 @@ mod stories {
pub struct CheckboxStory;
impl Render<Self> for CheckboxStory {
type Element = Div<Self>;
impl Render for CheckboxStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View File

@ -1,47 +1,42 @@
use std::cell::RefCell;
use std::rc::Rc;
use crate::{prelude::*, v_stack, List, ListItem};
use crate::{ListEntry, ListSeparator, ListSubHeader};
use crate::{prelude::*, v_stack, List};
use crate::{ListItem, ListSeparator, ListSubHeader};
use gpui::{
overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, DispatchPhase, Div,
EventEmitter, FocusHandle, FocusableView, LayoutId, ManagedView, Manager, MouseButton,
MouseDownEvent, Pixels, Point, Render, RenderOnce, View, VisualContext, WeakView,
overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DispatchPhase,
Div, EventEmitter, FocusHandle, FocusableView, LayoutId, ManagedView, Manager, MouseButton,
MouseDownEvent, Pixels, Point, Render, RenderOnce, View, VisualContext,
};
pub enum ContextMenuItem<V> {
pub enum ContextMenuItem {
Separator(ListSeparator),
Header(ListSubHeader),
Entry(
ListEntry<ContextMenu<V>>,
Rc<dyn Fn(&mut V, &mut ViewContext<V>)>,
),
Entry(ListItem, Rc<dyn Fn(&ClickEvent, &mut WindowContext)>),
}
pub struct ContextMenu<V> {
items: Vec<ContextMenuItem<V>>,
pub struct ContextMenu {
items: Vec<ContextMenuItem>,
focus_handle: FocusHandle,
handle: WeakView<V>,
}
impl<V: 'static> FocusableView for ContextMenu<V> {
impl FocusableView for ContextMenu {
fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
self.focus_handle.clone()
}
}
impl<V: 'static> EventEmitter<Manager> for ContextMenu<V> {}
impl EventEmitter<Manager> for ContextMenu {}
impl<V: 'static> ContextMenu<V> {
impl ContextMenu {
pub fn build(
cx: &mut ViewContext<V>,
f: impl FnOnce(Self, &mut ViewContext<Self>) -> Self,
cx: &mut WindowContext,
f: impl FnOnce(Self, &mut WindowContext) -> Self,
) -> View<Self> {
let handle = cx.view().downgrade();
// let handle = cx.view().downgrade();
cx.build_view(|cx| {
f(
Self {
handle,
items: Default::default(),
focus_handle: cx.focus_handle(),
},
@ -63,15 +58,15 @@ impl<V: 'static> ContextMenu<V> {
pub fn entry(
mut self,
view: ListEntry<Self>,
on_click: impl Fn(&mut V, &mut ViewContext<V>) + 'static,
view: ListItem,
on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
) -> Self {
self.items
.push(ContextMenuItem::Entry(view, Rc::new(on_click)));
self
}
pub fn action(self, view: ListEntry<Self>, action: Box<dyn Action>) -> Self {
pub fn action(self, view: ListItem, action: Box<dyn Action>) -> Self {
// todo: add the keybindings to the list entry
self.entry(view, move |_, cx| cx.dispatch_action(action.boxed_clone()))
}
@ -86,64 +81,66 @@ impl<V: 'static> ContextMenu<V> {
}
}
impl<V: 'static> Render<Self> for ContextMenu<V> {
type Element = Div<Self>;
impl Render for ContextMenu {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div().elevation_2(cx).flex().flex_row().child(
v_stack()
.min_w(px(200.))
.track_focus(&self.focus_handle)
.on_mouse_down_out(|this: &mut Self, _, cx| this.cancel(&Default::default(), cx))
.on_mouse_down_out(
cx.listener(|this: &mut Self, _, cx| this.cancel(&Default::default(), cx)),
)
// .on_action(ContextMenu::select_first)
// .on_action(ContextMenu::select_last)
// .on_action(ContextMenu::select_next)
// .on_action(ContextMenu::select_prev)
.on_action(ContextMenu::confirm)
.on_action(ContextMenu::cancel)
.on_action(cx.listener(ContextMenu::confirm))
.on_action(cx.listener(ContextMenu::cancel))
.flex_none()
// .bg(cx.theme().colors().elevated_surface_background)
// .border()
// .border_color(cx.theme().colors().border)
.child(List::new(
self.items
.iter()
.map(|item| match item {
ContextMenuItem::Separator(separator) => {
ListItem::Separator(separator.clone())
}
ContextMenuItem::Header(header) => ListItem::Header(header.clone()),
ContextMenuItem::Entry(entry, callback) => {
let callback = callback.clone();
let handle = self.handle.clone();
ListItem::Entry(entry.clone().on_click(move |this, cx| {
handle.update(cx, |view, cx| callback(view, cx)).ok();
cx.emit(Manager::Dismiss);
}))
}
})
.collect(),
)),
.child(
List::new().children(self.items.iter().map(|item| match item {
ContextMenuItem::Separator(separator) => {
separator.clone().render_into_any()
}
ContextMenuItem::Header(header) => header.clone().render_into_any(),
ContextMenuItem::Entry(entry, callback) => {
let callback = callback.clone();
let dismiss = cx.listener(|_, _, cx| cx.emit(Manager::Dismiss));
entry
.clone()
.on_click(move |event, cx| {
callback(event, cx);
dismiss(event, cx)
})
.render_into_any()
}
})),
),
)
}
}
pub struct MenuHandle<V: 'static, M: ManagedView> {
pub struct MenuHandle<M: ManagedView> {
id: ElementId,
child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement<V> + 'static>>,
menu_builder: Option<Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> View<M> + 'static>>,
child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement + 'static>>,
menu_builder: Option<Rc<dyn Fn(&mut WindowContext) -> View<M> + 'static>>,
anchor: Option<AnchorCorner>,
attach: Option<AnchorCorner>,
}
impl<V: 'static, M: ManagedView> MenuHandle<V, M> {
pub fn menu(mut self, f: impl Fn(&mut V, &mut ViewContext<V>) -> View<M> + 'static) -> Self {
impl<M: ManagedView> MenuHandle<M> {
pub fn menu(mut self, f: impl Fn(&mut WindowContext) -> View<M> + 'static) -> Self {
self.menu_builder = Some(Rc::new(f));
self
}
pub fn child<R: RenderOnce<V>>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self {
pub fn child<R: RenderOnce>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self {
self.child_builder = Some(Box::new(|b| f(b).render_once().into_any()));
self
}
@ -162,7 +159,7 @@ impl<V: 'static, M: ManagedView> MenuHandle<V, M> {
}
}
pub fn menu_handle<V: 'static, M: ManagedView>(id: impl Into<ElementId>) -> MenuHandle<V, M> {
pub fn menu_handle<M: ManagedView>(id: impl Into<ElementId>) -> MenuHandle<M> {
MenuHandle {
id: id.into(),
child_builder: None,
@ -172,21 +169,20 @@ pub fn menu_handle<V: 'static, M: ManagedView>(id: impl Into<ElementId>) -> Menu
}
}
pub struct MenuHandleState<V, M> {
pub struct MenuHandleState<M> {
menu: Rc<RefCell<Option<View<M>>>>,
position: Rc<RefCell<Point<Pixels>>>,
child_layout_id: Option<LayoutId>,
child_element: Option<AnyElement<V>>,
menu_element: Option<AnyElement<V>>,
child_element: Option<AnyElement>,
menu_element: Option<AnyElement>,
}
impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
type State = MenuHandleState<V, M>;
impl<M: ManagedView> Element for MenuHandle<M> {
type State = MenuHandleState<M>;
fn layout(
&mut self,
view_state: &mut V,
element_state: Option<Self::State>,
cx: &mut crate::ViewContext<V>,
cx: &mut WindowContext,
) -> (gpui::LayoutId, Self::State) {
let (menu, position) = if let Some(element_state) = element_state {
(element_state.menu, element_state.position)
@ -197,14 +193,14 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
let mut menu_layout_id = None;
let menu_element = menu.borrow_mut().as_mut().map(|menu| {
let mut overlay = overlay::<V>().snap_to_window();
let mut overlay = overlay().snap_to_window();
if let Some(anchor) = self.anchor {
overlay = overlay.anchor(anchor);
}
overlay = overlay.position(*position.borrow());
let mut element = overlay.child(menu.clone()).into_any();
menu_layout_id = Some(element.layout(view_state, cx));
menu_layout_id = Some(element.layout(cx));
element
});
@ -215,7 +211,7 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
let child_layout_id = child_element
.as_mut()
.map(|child_element| child_element.layout(view_state, cx));
.map(|child_element| child_element.layout(cx));
let layout_id = cx.request_layout(
&gpui::Style::default(),
@ -237,16 +233,15 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
fn paint(
self,
bounds: Bounds<gpui::Pixels>,
view_state: &mut V,
element_state: &mut Self::State,
cx: &mut crate::ViewContext<V>,
cx: &mut WindowContext,
) {
if let Some(child) = element_state.child_element.take() {
child.paint(view_state, cx);
child.paint(cx);
}
if let Some(menu) = element_state.menu_element.take() {
menu.paint(view_state, cx);
menu.paint(cx);
return;
}
@ -258,7 +253,7 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
let attach = self.attach.clone();
let child_layout_id = element_state.child_layout_id.clone();
cx.on_mouse_event(move |view_state, event: &MouseDownEvent, phase, cx| {
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == MouseButton::Right
&& bounds.contains_point(&event.position)
@ -266,9 +261,9 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
cx.stop_propagation();
cx.prevent_default();
let new_menu = (builder)(view_state, cx);
let new_menu = (builder)(cx);
let menu2 = menu.clone();
cx.subscribe(&new_menu, move |this, modal, e, cx| match e {
cx.subscribe(&new_menu, move |modal, e, cx| match e {
&Manager::Dismiss => {
*menu2.borrow_mut() = None;
cx.notify();
@ -291,7 +286,7 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
}
}
impl<V: 'static, M: ManagedView> RenderOnce<V> for MenuHandle<V, M> {
impl<M: ManagedView> RenderOnce for MenuHandle<M> {
type Element = Self;
fn element_id(&self) -> Option<gpui::ElementId> {
@ -314,28 +309,31 @@ mod stories {
actions!(PrintCurrentDate, PrintBestFood);
fn build_menu<V: Render<V>>(
cx: &mut ViewContext<V>,
fn build_menu<V: Render>(
cx: &mut WindowContext,
header: impl Into<SharedString>,
) -> View<ContextMenu<V>> {
let handle = cx.view().clone();
) -> View<ContextMenu> {
ContextMenu::build(cx, |menu, _| {
menu.header(header)
.separator()
.entry(ListEntry::new(Label::new("Print current time")), |v, cx| {
println!("dispatching PrintCurrentTime action");
cx.dispatch_action(PrintCurrentDate.boxed_clone())
})
.entry(ListEntry::new(Label::new("Print best food")), |v, cx| {
cx.dispatch_action(PrintBestFood.boxed_clone())
})
.entry(
ListItem::new("Print current time", Label::new("Print current time")),
|v, cx| {
println!("dispatching PrintCurrentTime action");
cx.dispatch_action(PrintCurrentDate.boxed_clone())
},
)
.entry(
ListItem::new("Print best food", Label::new("Print best food")),
|v, cx| cx.dispatch_action(PrintBestFood.boxed_clone()),
)
})
}
pub struct ContextMenuStory;
impl Render<Self> for ContextMenuStory {
type Element = Div<Self>;
impl Render for ContextMenuStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View File

@ -2,16 +2,16 @@ use crate::prelude::*;
use crate::{v_stack, ButtonGroup};
#[derive(RenderOnce)]
pub struct Details<V: 'static> {
pub struct Details {
text: &'static str,
meta: Option<&'static str>,
actions: Option<ButtonGroup<V>>,
actions: Option<ButtonGroup>,
}
impl<V: 'static> Component<V> for Details<V> {
type Rendered = Div<V>;
impl Component for Details {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
v_stack()
.p_1()
.gap_0p5()
@ -24,7 +24,7 @@ impl<V: 'static> Component<V> for Details<V> {
}
}
impl<V: 'static> Details<V> {
impl Details {
pub fn new(text: &'static str) -> Self {
Self {
text,
@ -38,7 +38,7 @@ impl<V: 'static> Details<V> {
self
}
pub fn actions(mut self, actions: ButtonGroup<V>) -> Self {
pub fn actions(mut self, actions: ButtonGroup) -> Self {
self.actions = Some(actions);
self
}
@ -56,8 +56,8 @@ mod stories {
pub struct DetailsStory;
impl Render<Self> for DetailsStory {
type Element = Div<Self>;
impl Render for DetailsStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View File

@ -13,10 +13,10 @@ pub struct Divider {
inset: bool,
}
impl<V: 'static> Component<V> for Divider {
type Rendered = Div<V>;
impl Component for Divider {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
div()
.map(|this| match self.direction {
DividerDirection::Horizontal => {
@ -50,7 +50,7 @@ impl Divider {
self
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
fn render(self, cx: &mut WindowContext) -> impl Element {
div()
.map(|this| match self.direction {
DividerDirection::Horizontal => {

View File

@ -5,7 +5,7 @@ use crate::{prelude::*, v_stack};
/// Create an elevated surface.
///
/// Must be used inside of a relative parent element
pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<V>) -> Div<V> {
pub fn elevated_surface(level: ElevationIndex, cx: &mut WindowContext) -> Div {
let colors = cx.theme().colors();
// let shadow = BoxShadow {
@ -23,6 +23,6 @@ pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<
.shadow(level.shadow())
}
pub fn modal<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
pub fn modal(cx: &mut WindowContext) -> Div {
elevated_surface(ElevationIndex::ModalSurface, cx)
}

View File

@ -6,10 +6,10 @@ pub struct Facepile {
players: Vec<Player>,
}
impl<V: 'static> Component<V> for Facepile {
type Rendered = Div<V>;
impl Component for Facepile {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let player_count = self.players.len();
let player_list = self.players.iter().enumerate().map(|(ix, player)| {
let isnt_last = ix < player_count - 1;
@ -42,8 +42,8 @@ mod stories {
pub struct FacepileStory;
impl Render<Self> for FacepileStory {
type Element = Div<Self>;
impl Render for FacepileStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let players = static_players();

View File

@ -140,10 +140,10 @@ pub struct IconElement {
size: IconSize,
}
impl<V: 'static> Component<V> for IconElement {
type Rendered = Svg<V>;
impl Component for IconElement {
type Rendered = Svg;
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let svg_size = match self.size {
IconSize::Small => rems(0.75),
IconSize::Medium => rems(0.9375),
@ -184,7 +184,7 @@ impl IconElement {
self
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
fn render(self, cx: &mut WindowContext) -> impl Element {
let svg_size = match self.size {
IconSize::Small => rems(0.75),
IconSize::Medium => rems(0.9375),
@ -212,8 +212,8 @@ mod stories {
pub struct IconStory;
impl Render<Self> for IconStory {
type Element = Div<Self>;
impl Render for IconStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let icons = Icon::iter();

View File

@ -1,33 +1,22 @@
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement};
use gpui::{prelude::*, Action, AnyView, Div, MouseButton, Stateful};
use std::sync::Arc;
struct IconButtonHandlers<V: 'static> {
click: Option<ClickHandler<V>>,
}
impl<V: 'static> Default for IconButtonHandlers<V> {
fn default() -> Self {
Self { click: None }
}
}
use crate::{h_stack, prelude::*, Icon, IconElement};
use gpui::{prelude::*, Action, AnyView, Div, MouseButton, MouseDownEvent, Stateful};
#[derive(RenderOnce)]
pub struct IconButton<V: 'static> {
pub struct IconButton {
id: ElementId,
icon: Icon,
color: TextColor,
variant: ButtonVariant,
state: InteractionState,
selected: bool,
tooltip: Option<Box<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>>,
handlers: IconButtonHandlers<V>,
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
on_mouse_down: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
}
impl<V: 'static> Component<V> for IconButton<V> {
type Rendered = Stateful<V, Div<V>>;
impl Component for IconButton {
type Rendered = Stateful<Div>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let icon_color = match (self.state, self.color) {
(InteractionState::Disabled, _) => TextColor::Disabled,
(InteractionState::Active, _) => TextColor::Selected,
@ -65,16 +54,16 @@ impl<V: 'static> Component<V> for IconButton<V> {
.active(|style| style.bg(bg_active_color))
.child(IconElement::new(self.icon).color(icon_color));
if let Some(click_handler) = self.handlers.click.clone() {
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
if let Some(click_handler) = self.on_mouse_down {
button = button.on_mouse_down(MouseButton::Left, move |event, cx| {
cx.stop_propagation();
click_handler(state, cx);
click_handler(event, cx);
})
}
if let Some(tooltip) = self.tooltip {
if !self.selected {
button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
button = button.tooltip(move |cx| tooltip(cx))
}
}
@ -82,7 +71,7 @@ impl<V: 'static> Component<V> for IconButton<V> {
}
}
impl<V: 'static> IconButton<V> {
impl IconButton {
pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
Self {
id: id.into(),
@ -92,7 +81,7 @@ impl<V: 'static> IconButton<V> {
state: InteractionState::default(),
selected: false,
tooltip: None,
handlers: IconButtonHandlers::default(),
on_mouse_down: None,
}
}
@ -121,16 +110,16 @@ impl<V: 'static> IconButton<V> {
self
}
pub fn tooltip(
mut self,
tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static,
) -> Self {
pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
self.tooltip = Some(Box::new(tooltip));
self
}
pub fn on_click(mut self, handler: impl 'static + Fn(&mut V, &mut ViewContext<V>)) -> Self {
self.handlers.click = Some(Arc::new(handler));
pub fn on_click(
mut self,
handler: impl 'static + Fn(&MouseDownEvent, &mut WindowContext),
) -> Self {
self.on_mouse_down = Some(Box::new(handler));
self
}

View File

@ -4,10 +4,10 @@ use gpui::{px, Div, RenderOnce};
#[derive(RenderOnce)]
pub struct UnreadIndicator;
impl<V: 'static> Component<V> for UnreadIndicator {
type Rendered = Div<V>;
impl Component for UnreadIndicator {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
div()
.rounded_full()
.border_2()
@ -24,7 +24,7 @@ impl UnreadIndicator {
Self
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
fn render(self, cx: &mut WindowContext) -> impl Element {
div()
.rounded_full()
.border_2()

View File

@ -18,10 +18,10 @@ pub struct Input {
is_active: bool,
}
impl<V: 'static> Component<V> for Input {
type Rendered = Stateful<V, Div<V>>;
impl Component for Input {
type Rendered = Stateful<Div>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
InputVariant::Ghost => (
cx.theme().colors().ghost_element_background,
@ -118,8 +118,8 @@ mod stories {
pub struct InputStory;
impl Render<Self> for InputStory {
type Element = Div<Self>;
impl Render for InputStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View File

@ -10,10 +10,10 @@ pub struct KeyBinding {
key_binding: gpui::KeyBinding,
}
impl<V: 'static> Component<V> for KeyBinding {
type Rendered = Div<V>;
impl Component for KeyBinding {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
div()
.flex()
.gap_2()
@ -49,11 +49,10 @@ pub struct Key {
key: SharedString,
}
impl<V: 'static> Component<V> for Key {
type Rendered = Div<V>;
impl Component for Key {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let _view: &mut V = view;
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
div()
.px_2()
.py_0()
@ -89,8 +88,8 @@ mod stories {
gpui::KeyBinding::new(key, NoAction {}, None)
}
impl Render<Self> for KeybindingStory {
type Element = Div<Self>;
impl Render for KeybindingStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let all_modifier_permutations =

View File

@ -68,10 +68,10 @@ pub struct Label {
strikethrough: bool,
}
impl<V: 'static> Component<V> for Label {
type Rendered = Div<V>;
impl Component for Label {
type Rendered = Div;
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
div()
.when(self.strikethrough, |this| {
this.relative().child(
@ -136,10 +136,10 @@ pub struct HighlightedLabel {
strikethrough: bool,
}
impl<V: 'static> Component<V> for HighlightedLabel {
type Rendered = Div<V>;
impl Component for HighlightedLabel {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let highlight_color = cx.theme().colors().text_accent;
let mut text_style = cx.text_style().clone();
@ -242,8 +242,8 @@ mod stories {
pub struct LabelStory;
impl Render<Self> for LabelStory {
type Element = Div<Self>;
impl Render for LabelStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View File

@ -1,7 +1,9 @@
use gpui::{div, Div, RenderOnce, Stateful, StatefulInteractiveElement};
use gpui::{
div, px, AnyElement, ClickEvent, Div, RenderOnce, Stateful, StatefulInteractiveElement,
};
use smallvec::SmallVec;
use std::rc::Rc;
use crate::settings::user_settings;
use crate::{
disclosure_control, h_stack, v_stack, Avatar, Icon, IconElement, IconSize, Label, Toggle,
};
@ -32,10 +34,10 @@ pub struct ListHeader {
toggle: Toggle,
}
impl<V: 'static> Component<V> for ListHeader {
type Rendered = Div<V>;
impl Component for ListHeader {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let disclosure_control = disclosure_control(self.toggle);
let meta = match self.meta {
@ -117,7 +119,7 @@ impl ListHeader {
}
// before_ship!("delete")
// fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
// fn render<V: 'static>(self, cx: &mut WindowContext) -> impl Element<V> {
// let disclosure_control = disclosure_control(self.toggle);
// let meta = match self.meta {
@ -177,7 +179,7 @@ impl ListHeader {
// }
}
#[derive(Clone)]
#[derive(RenderOnce, Clone)]
pub struct ListSubHeader {
label: SharedString,
left_icon: Option<Icon>,
@ -197,8 +199,12 @@ impl ListSubHeader {
self.left_icon = left_icon;
self
}
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
impl Component for ListSubHeader {
type Rendered = Div;
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
h_stack().flex_1().w_full().relative().py_1().child(
div()
.h_6()
@ -232,55 +238,9 @@ pub enum ListEntrySize {
Medium,
}
#[derive(Clone)]
pub enum ListItem<V: 'static> {
Entry(ListEntry<V>),
Separator(ListSeparator),
Header(ListSubHeader),
}
impl<V: 'static> From<ListEntry<V>> for ListItem<V> {
fn from(entry: ListEntry<V>) -> Self {
Self::Entry(entry)
}
}
impl<V: 'static> From<ListSeparator> for ListItem<V> {
fn from(entry: ListSeparator) -> Self {
Self::Separator(entry)
}
}
impl<V: 'static> From<ListSubHeader> for ListItem<V> {
fn from(entry: ListSubHeader) -> Self {
Self::Header(entry)
}
}
impl<V: 'static> ListItem<V> {
pub fn new(label: Label) -> Self {
Self::Entry(ListEntry::new(label))
}
pub fn as_entry(&mut self) -> Option<&mut ListEntry<V>> {
if let Self::Entry(entry) = self {
Some(entry)
} else {
None
}
}
fn render(self, view: &mut V, ix: usize, cx: &mut ViewContext<V>) -> Div<V> {
match self {
ListItem::Entry(entry) => div().child(entry.render(ix, cx)),
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
ListItem::Header(header) => div().child(header.render(view, cx)),
}
}
}
// #[derive(RenderOnce)]
pub struct ListEntry<V> {
#[derive(RenderOnce)]
pub struct ListItem {
id: ElementId,
disabled: bool,
// TODO: Reintroduce this
// disclosure_control_style: DisclosureControlVisibility,
@ -291,12 +251,13 @@ pub struct ListEntry<V> {
size: ListEntrySize,
toggle: Toggle,
variant: ListItemVariant,
on_click: Option<Rc<dyn Fn(&mut V, &mut ViewContext<V>) + 'static>>,
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
}
impl<V> Clone for ListEntry<V> {
impl Clone for ListItem {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
disabled: self.disabled,
indent_level: self.indent_level,
label: self.label.clone(),
@ -310,9 +271,10 @@ impl<V> Clone for ListEntry<V> {
}
}
impl<V: 'static> ListEntry<V> {
pub fn new(label: Label) -> Self {
impl ListItem {
pub fn new(id: impl Into<ElementId>, label: Label) -> Self {
Self {
id: id.into(),
disabled: false,
indent_level: 0,
label,
@ -325,7 +287,7 @@ impl<V: 'static> ListEntry<V> {
}
}
pub fn on_click(mut self, handler: impl Fn(&mut V, &mut ViewContext<V>) + 'static) -> Self {
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
self.on_click = Some(Rc::new(handler));
self
}
@ -364,10 +326,12 @@ impl<V: 'static> ListEntry<V> {
self.size = size;
self
}
}
fn render(self, ix: usize, cx: &mut ViewContext<V>) -> Stateful<V, Div<V>> {
let settings = user_settings(cx);
impl Component for ListItem {
type Rendered = Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let left_content = match self.left_slot.clone() {
Some(GraphicSlot::Icon(i)) => Some(
h_stack().child(
@ -386,7 +350,7 @@ impl<V: 'static> ListEntry<V> {
ListEntrySize::Medium => div().h_7(),
};
div()
.id(ix)
.id(self.id)
.relative()
.hover(|mut style| {
style.background = Some(cx.theme().colors().editor_background.into());
@ -394,10 +358,9 @@ impl<V: 'static> ListEntry<V> {
})
.on_click({
let on_click = self.on_click.clone();
move |view: &mut V, event, cx| {
move |event, cx| {
if let Some(on_click) = &on_click {
(on_click)(view, cx)
(on_click)(event, cx)
}
}
})
@ -413,7 +376,7 @@ impl<V: 'static> ListEntry<V> {
// .ml(rems(0.75 * self.indent_level as f32))
.children((0..self.indent_level).map(|_| {
div()
.w(*settings.list_indent_depth)
.w(px(4.))
.h_full()
.flex()
.justify_center()
@ -444,35 +407,30 @@ impl ListSeparator {
}
}
impl<V: 'static> Component<V> for ListSeparator {
type Rendered = Div<V>;
impl Component for ListSeparator {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
div().h_px().w_full().bg(cx.theme().colors().border_variant)
}
}
#[derive(RenderOnce)]
pub struct List<V: 'static> {
items: Vec<ListItem<V>>,
pub struct List {
/// Message to display when the list is empty
/// Defaults to "No items"
empty_message: SharedString,
header: Option<ListHeader>,
toggle: Toggle,
children: SmallVec<[AnyElement; 2]>,
}
impl<V: 'static> Component<V> for List<V> {
type Rendered = Div<V>;
impl Component for List {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let list_content = match (self.items.is_empty(), self.toggle) {
(false, _) => div().children(
self.items
.into_iter()
.enumerate()
.map(|(ix, item)| item.render(view, ix, cx)),
),
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let list_content = match (self.children.is_empty(), self.toggle) {
(false, _) => div().children(self.children),
(true, Toggle::Toggled(false)) => div(),
(true, _) => {
div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted))
@ -487,13 +445,13 @@ impl<V: 'static> Component<V> for List<V> {
}
}
impl<V: 'static> List<V> {
pub fn new(items: Vec<ListItem<V>>) -> Self {
impl List {
pub fn new() -> Self {
Self {
items,
empty_message: "No items".into(),
header: None,
toggle: Toggle::NotToggleable,
children: SmallVec::new(),
}
}
@ -511,25 +469,10 @@ impl<V: 'static> List<V> {
self.toggle = toggle;
self
}
}
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
let list_content = match (self.items.is_empty(), self.toggle) {
(false, _) => div().children(
self.items
.into_iter()
.enumerate()
.map(|(ix, item)| item.render(view, ix, cx)),
),
(true, Toggle::Toggled(false)) => div(),
(true, _) => {
div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted))
}
};
v_stack()
.w_full()
.py_1()
.children(self.header.map(|header| header))
.child(list_content)
impl ParentElement for List {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
&mut self.children
}
}

View File

@ -1,22 +1,21 @@
use gpui::{AnyElement, Div, RenderOnce, Stateful};
use gpui::{AnyElement, Div, RenderOnce};
use smallvec::SmallVec;
use crate::{h_stack, prelude::*, v_stack, Button, Icon, IconButton, Label};
#[derive(RenderOnce)]
pub struct Modal<V: 'static> {
pub struct Modal {
id: ElementId,
title: Option<SharedString>,
primary_action: Option<Button<V>>,
secondary_action: Option<Button<V>>,
children: SmallVec<[AnyElement<V>; 2]>,
primary_action: Option<Button>,
secondary_action: Option<Button>,
children: SmallVec<[AnyElement; 2]>,
}
impl<V: 'static> Component<V> for Modal<V> {
type Rendered = Stateful<V, Div<V>>;
impl Component for Modal {
type Rendered = gpui::Stateful<Div>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let _view: &mut V = view;
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
v_stack()
.id(self.id.clone())
.w_96()
@ -52,7 +51,7 @@ impl<V: 'static> Component<V> for Modal<V> {
}
}
impl<V: 'static> Modal<V> {
impl Modal {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
id: id.into(),
@ -68,19 +67,19 @@ impl<V: 'static> Modal<V> {
self
}
pub fn primary_action(mut self, action: Button<V>) -> Self {
pub fn primary_action(mut self, action: Button) -> Self {
self.primary_action = Some(action);
self
}
pub fn secondary_action(mut self, action: Button<V>) -> Self {
pub fn secondary_action(mut self, action: Button) -> Self {
self.secondary_action = Some(action);
self
}
}
impl<V: 'static> ParentElement<V> for Modal<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
impl ParentElement for Modal {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
&mut self.children
}
}

View File

@ -22,7 +22,7 @@ impl NotificationToast {
self
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
fn render(self, cx: &mut WindowContext) -> impl Element {
h_stack()
.z_index(5)
.absolute()

View File

@ -1,7 +1,6 @@
use crate::{h_stack, prelude::*, v_stack, KeyBinding, Label};
use gpui::prelude::*;
use gpui::Div;
use gpui::Stateful;
#[derive(RenderOnce)]
pub struct Palette {
@ -12,10 +11,10 @@ pub struct Palette {
default_order: OrderMethod,
}
impl<V: 'static> Component<V> for Palette {
type Rendered = Stateful<V, Div<V>>;
impl Component for Palette {
type Rendered = gpui::Stateful<Div>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
v_stack()
.id(self.id)
.w_96()
@ -116,10 +115,10 @@ pub struct PaletteItem {
pub key_binding: Option<KeyBinding>,
}
impl<V: 'static> Component<V> for PaletteItem {
type Rendered = Div<V>;
impl Component for PaletteItem {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
div()
.flex()
.flex_row()
@ -173,8 +172,8 @@ mod stories {
pub struct PaletteStory;
impl Render<Self> for PaletteStory {
type Element = Div<Self>;
impl Render for PaletteStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
{

View File

@ -1,8 +1,8 @@
use gpui::{prelude::*, AbsoluteLength, AnyElement, Div, RenderOnce, Stateful};
use gpui::px;
use gpui::{prelude::*, AbsoluteLength, AnyElement, Div, RenderOnce};
use smallvec::SmallVec;
use crate::prelude::*;
use crate::settings::user_settings;
use crate::v_stack;
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
@ -39,20 +39,20 @@ pub enum PanelSide {
use std::collections::HashSet;
#[derive(RenderOnce)]
pub struct Panel<V: 'static> {
pub struct Panel {
id: ElementId,
current_side: PanelSide,
/// Defaults to PanelAllowedSides::LeftAndRight
allowed_sides: PanelAllowedSides,
initial_width: AbsoluteLength,
width: Option<AbsoluteLength>,
children: SmallVec<[AnyElement<V>; 2]>,
children: SmallVec<[AnyElement; 2]>,
}
impl<V: 'static> Component<V> for Panel<V> {
type Rendered = Stateful<V, Div<V>>;
impl Component for Panel {
type Rendered = gpui::Stateful<Div>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let current_size = self.width.unwrap_or(self.initial_width);
v_stack()
@ -73,15 +73,13 @@ impl<V: 'static> Component<V> for Panel<V> {
}
}
impl<V: 'static> Panel<V> {
impl Panel {
pub fn new(id: impl Into<ElementId>, cx: &mut WindowContext) -> Self {
let settings = user_settings(cx);
Self {
id: id.into(),
current_side: PanelSide::default(),
allowed_sides: PanelAllowedSides::default(),
initial_width: *settings.default_panel_size,
initial_width: px(320.).into(),
width: None,
children: SmallVec::new(),
}
@ -117,8 +115,8 @@ impl<V: 'static> Panel<V> {
}
}
impl<V: 'static> ParentElement<V> for Panel<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
impl ParentElement for Panel {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
&mut self.children
}
}
@ -134,12 +132,12 @@ mod stories {
pub struct PanelStory;
impl Render<Self> for PanelStory {
type Element = Div<Self>;
impl Render for PanelStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, Panel<Self>>(cx))
.child(Story::title_for::<Panel<Self>>(cx))
.child(Story::label(cx, "Default"))
.child(
Panel::new("panel", cx).child(

View File

@ -1,4 +1,4 @@
use gpui::{Hsla, ViewContext};
use gpui::Hsla;
use crate::prelude::*;
@ -156,11 +156,11 @@ impl Player {
self
}
pub fn cursor_color<V: 'static>(&self, cx: &mut ViewContext<V>) -> Hsla {
pub fn cursor_color(&self, cx: &mut WindowContext) -> Hsla {
cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].cursor
}
pub fn selection_color<V: 'static>(&self, cx: &mut ViewContext<V>) -> Hsla {
pub fn selection_color(&self, cx: &mut WindowContext) -> Hsla {
cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].selection
}

View File

@ -8,10 +8,10 @@ pub struct PlayerStack {
player_with_call_status: PlayerWithCallStatus,
}
impl<V: 'static> Component<V> for PlayerStack {
type Rendered = Div<V>;
impl Component for PlayerStack {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let player = self.player_with_call_status.get_player();
let followers = self

View File

@ -5,13 +5,13 @@ use crate::StyledExt;
/// Horizontally stacks elements.
///
/// Sets `flex()`, `flex_row()`, `items_center()`
pub fn h_stack<V: 'static>() -> Div<V> {
pub fn h_stack() -> Div {
div().h_flex()
}
/// Vertically stacks elements.
///
/// Sets `flex()`, `flex_col()`
pub fn v_stack<V: 'static>() -> Div<V> {
pub fn v_stack() -> Div {
div().v_flex()
}

View File

@ -1,276 +1,276 @@
use crate::prelude::*;
use crate::{Icon, IconElement, Label, TextColor};
use gpui::{prelude::*, red, Div, ElementId, Render, RenderOnce, Stateful, View};
// use crate::prelude::*;
// use crate::{Icon, IconElement, Label, TextColor};
// use gpui::{prelude::*, red, Div, ElementId, Render, RenderOnce, View};
#[derive(RenderOnce, Clone)]
pub struct Tab {
id: ElementId,
title: String,
icon: Option<Icon>,
current: bool,
dirty: bool,
fs_status: FileSystemStatus,
git_status: GitStatus,
diagnostic_status: DiagnosticStatus,
close_side: IconSide,
}
// #[derive(RenderOnce, Clone)]
// pub struct Tab {
// id: ElementId,
// title: String,
// icon: Option<Icon>,
// current: bool,
// dirty: bool,
// fs_status: FileSystemStatus,
// git_status: GitStatus,
// diagnostic_status: DiagnosticStatus,
// close_side: IconSide,
// }
#[derive(Clone, Debug)]
struct TabDragState {
title: String,
}
// #[derive(Clone, Debug)]
// struct TabDragState {
// title: String,
// }
impl Render<Self> for TabDragState {
type Element = Div<Self>;
// impl Render for TabDragState {
// type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div().w_8().h_4().bg(red())
}
}
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
// div().w_8().h_4().bg(red())
// }
// }
impl<V: 'static> Component<V> for Tab {
type Rendered = Stateful<V, Div<V>>;
// impl Component for Tab {
// type Rendered = gpui::Stateful<Div>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
let is_deleted = self.fs_status == FileSystemStatus::Deleted;
// fn render(self, cx: &mut WindowContext) -> Self::Rendered {
// let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
// let is_deleted = self.fs_status == FileSystemStatus::Deleted;
let label = match (self.git_status, is_deleted) {
(_, true) | (GitStatus::Deleted, false) => Label::new(self.title.clone())
.color(TextColor::Hidden)
.set_strikethrough(true),
(GitStatus::None, false) => Label::new(self.title.clone()),
(GitStatus::Created, false) => Label::new(self.title.clone()).color(TextColor::Created),
(GitStatus::Modified, false) => {
Label::new(self.title.clone()).color(TextColor::Modified)
}
(GitStatus::Renamed, false) => Label::new(self.title.clone()).color(TextColor::Accent),
(GitStatus::Conflict, false) => Label::new(self.title.clone()),
};
// let label = match (self.git_status, is_deleted) {
// (_, true) | (GitStatus::Deleted, false) => Label::new(self.title.clone())
// .color(TextColor::Hidden)
// .set_strikethrough(true),
// (GitStatus::None, false) => Label::new(self.title.clone()),
// (GitStatus::Created, false) => Label::new(self.title.clone()).color(TextColor::Created),
// (GitStatus::Modified, false) => {
// Label::new(self.title.clone()).color(TextColor::Modified)
// }
// (GitStatus::Renamed, false) => Label::new(self.title.clone()).color(TextColor::Accent),
// (GitStatus::Conflict, false) => Label::new(self.title.clone()),
// };
let close_icon = || IconElement::new(Icon::Close).color(TextColor::Muted);
// let close_icon = || IconElement::new(Icon::Close).color(TextColor::Muted);
let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current {
false => (
cx.theme().colors().tab_inactive_background,
cx.theme().colors().ghost_element_hover,
cx.theme().colors().ghost_element_active,
),
true => (
cx.theme().colors().tab_active_background,
cx.theme().colors().element_hover,
cx.theme().colors().element_active,
),
};
// let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current {
// false => (
// cx.theme().colors().tab_inactive_background,
// cx.theme().colors().ghost_element_hover,
// cx.theme().colors().ghost_element_active,
// ),
// true => (
// cx.theme().colors().tab_active_background,
// cx.theme().colors().element_hover,
// cx.theme().colors().element_active,
// ),
// };
let drag_state = TabDragState {
title: self.title.clone(),
};
// let drag_state = TabDragState {
// title: self.title.clone(),
// };
div()
.id(self.id.clone())
.on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone()))
.drag_over::<TabDragState>(|d| d.bg(cx.theme().colors().drop_target_background))
.on_drop(|_view, state: View<TabDragState>, cx| {
eprintln!("{:?}", state.read(cx));
})
.px_2()
.py_0p5()
.flex()
.items_center()
.justify_center()
.bg(tab_bg)
.hover(|h| h.bg(tab_hover_bg))
.active(|a| a.bg(tab_active_bg))
.child(
div()
.px_1()
.flex()
.items_center()
.gap_1p5()
.children(has_fs_conflict.then(|| {
IconElement::new(Icon::ExclamationTriangle)
.size(crate::IconSize::Small)
.color(TextColor::Warning)
}))
.children(self.icon.map(IconElement::new))
.children(if self.close_side == IconSide::Left {
Some(close_icon())
} else {
None
})
.child(label)
.children(if self.close_side == IconSide::Right {
Some(close_icon())
} else {
None
}),
)
}
}
// div()
// .id(self.id.clone())
// .on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone()))
// .drag_over::<TabDragState>(|d| d.bg(cx.theme().colors().drop_target_background))
// .on_drop(|_view, state: View<TabDragState>, cx| {
// eprintln!("{:?}", state.read(cx));
// })
// .px_2()
// .py_0p5()
// .flex()
// .items_center()
// .justify_center()
// .bg(tab_bg)
// .hover(|h| h.bg(tab_hover_bg))
// .active(|a| a.bg(tab_active_bg))
// .child(
// div()
// .px_1()
// .flex()
// .items_center()
// .gap_1p5()
// .children(has_fs_conflict.then(|| {
// IconElement::new(Icon::ExclamationTriangle)
// .size(crate::IconSize::Small)
// .color(TextColor::Warning)
// }))
// .children(self.icon.map(IconElement::new))
// .children(if self.close_side == IconSide::Left {
// Some(close_icon())
// } else {
// None
// })
// .child(label)
// .children(if self.close_side == IconSide::Right {
// Some(close_icon())
// } else {
// None
// }),
// )
// }
// }
impl Tab {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
id: id.into(),
title: "untitled".to_string(),
icon: None,
current: false,
dirty: false,
fs_status: FileSystemStatus::None,
git_status: GitStatus::None,
diagnostic_status: DiagnosticStatus::None,
close_side: IconSide::Right,
}
}
// impl Tab {
// pub fn new(id: impl Into<ElementId>) -> Self {
// Self {
// id: id.into(),
// title: "untitled".to_string(),
// icon: None,
// current: false,
// dirty: false,
// fs_status: FileSystemStatus::None,
// git_status: GitStatus::None,
// diagnostic_status: DiagnosticStatus::None,
// close_side: IconSide::Right,
// }
// }
pub fn current(mut self, current: bool) -> Self {
self.current = current;
self
}
// pub fn current(mut self, current: bool) -> Self {
// self.current = current;
// self
// }
pub fn title(mut self, title: String) -> Self {
self.title = title;
self
}
// pub fn title(mut self, title: String) -> Self {
// self.title = title;
// self
// }
pub fn icon<I>(mut self, icon: I) -> Self
where
I: Into<Option<Icon>>,
{
self.icon = icon.into();
self
}
// pub fn icon<I>(mut self, icon: I) -> Self
// where
// I: Into<Option<Icon>>,
// {
// self.icon = icon.into();
// self
// }
pub fn dirty(mut self, dirty: bool) -> Self {
self.dirty = dirty;
self
}
// pub fn dirty(mut self, dirty: bool) -> Self {
// self.dirty = dirty;
// self
// }
pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
self.fs_status = fs_status;
self
}
// pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
// self.fs_status = fs_status;
// self
// }
pub fn git_status(mut self, git_status: GitStatus) -> Self {
self.git_status = git_status;
self
}
// pub fn git_status(mut self, git_status: GitStatus) -> Self {
// self.git_status = git_status;
// self
// }
pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
self.diagnostic_status = diagnostic_status;
self
}
// pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
// self.diagnostic_status = diagnostic_status;
// self
// }
pub fn close_side(mut self, close_side: IconSide) -> Self {
self.close_side = close_side;
self
}
}
// pub fn close_side(mut self, close_side: IconSide) -> Self {
// self.close_side = close_side;
// self
// }
// }
#[cfg(feature = "stories")]
pub use stories::*;
// #[cfg(feature = "stories")]
// pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::{h_stack, v_stack, Icon, Story};
use strum::IntoEnumIterator;
// #[cfg(feature = "stories")]
// mod stories {
// use super::*;
// use crate::{h_stack, v_stack, Icon, Story};
// use strum::IntoEnumIterator;
pub struct TabStory;
// pub struct TabStory;
impl Render<Self> for TabStory {
type Element = Div<Self>;
// impl Render for TabStory {
// type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let git_statuses = GitStatus::iter();
let fs_statuses = FileSystemStatus::iter();
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
// let git_statuses = GitStatus::iter();
// let fs_statuses = FileSystemStatus::iter();
Story::container(cx)
.child(Story::title_for::<_, Tab>(cx))
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Default"))
.child(Tab::new("default")),
),
)
.child(
h_stack().child(
v_stack().gap_2().child(Story::label(cx, "Current")).child(
h_stack()
.gap_4()
.child(
Tab::new("current")
.title("Current".to_string())
.current(true),
)
.child(
Tab::new("not_current")
.title("Not Current".to_string())
.current(false),
),
),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Titled"))
.child(Tab::new("titled").title("label".to_string())),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "With Icon"))
.child(
Tab::new("with_icon")
.title("label".to_string())
.icon(Some(Icon::Envelope)),
),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Close Side"))
.child(
h_stack()
.gap_4()
.child(
Tab::new("left")
.title("Left".to_string())
.close_side(IconSide::Left),
)
.child(Tab::new("right").title("Right".to_string())),
),
),
)
.child(
v_stack()
.gap_2()
.child(Story::label(cx, "Git Status"))
.child(h_stack().gap_4().children(git_statuses.map(|git_status| {
Tab::new("git_status")
.title(git_status.to_string())
.git_status(git_status)
}))),
)
.child(
v_stack()
.gap_2()
.child(Story::label(cx, "File System Status"))
.child(h_stack().gap_4().children(fs_statuses.map(|fs_status| {
Tab::new("file_system_status")
.title(fs_status.to_string())
.fs_status(fs_status)
}))),
)
}
}
}
// Story::container(cx)
// .child(Story::title_for::<_, Tab>(cx))
// .child(
// h_stack().child(
// v_stack()
// .gap_2()
// .child(Story::label(cx, "Default"))
// .child(Tab::new("default")),
// ),
// )
// .child(
// h_stack().child(
// v_stack().gap_2().child(Story::label(cx, "Current")).child(
// h_stack()
// .gap_4()
// .child(
// Tab::new("current")
// .title("Current".to_string())
// .current(true),
// )
// .child(
// Tab::new("not_current")
// .title("Not Current".to_string())
// .current(false),
// ),
// ),
// ),
// )
// .child(
// h_stack().child(
// v_stack()
// .gap_2()
// .child(Story::label(cx, "Titled"))
// .child(Tab::new("titled").title("label".to_string())),
// ),
// )
// .child(
// h_stack().child(
// v_stack()
// .gap_2()
// .child(Story::label(cx, "With Icon"))
// .child(
// Tab::new("with_icon")
// .title("label".to_string())
// .icon(Some(Icon::Envelope)),
// ),
// ),
// )
// .child(
// h_stack().child(
// v_stack()
// .gap_2()
// .child(Story::label(cx, "Close Side"))
// .child(
// h_stack()
// .gap_4()
// .child(
// Tab::new("left")
// .title("Left".to_string())
// .close_side(IconSide::Left),
// )
// .child(Tab::new("right").title("Right".to_string())),
// ),
// ),
// )
// .child(
// v_stack()
// .gap_2()
// .child(Story::label(cx, "Git Status"))
// .child(h_stack().gap_4().children(git_statuses.map(|git_status| {
// Tab::new("git_status")
// .title(git_status.to_string())
// .git_status(git_status)
// }))),
// )
// .child(
// v_stack()
// .gap_2()
// .child(Story::label(cx, "File System Status"))
// .child(h_stack().gap_4().children(fs_statuses.map(|fs_status| {
// Tab::new("file_system_status")
// .title(fs_status.to_string())
// .fs_status(fs_status)
// }))),
// )
// }
// }
// }

View File

@ -23,15 +23,15 @@ pub enum ToastOrigin {
///
/// Only one toast may be visible at a time.
#[derive(RenderOnce)]
pub struct Toast<V: 'static> {
pub struct Toast {
origin: ToastOrigin,
children: SmallVec<[AnyElement<V>; 2]>,
children: SmallVec<[AnyElement; 2]>,
}
impl<V: 'static> Component<V> for Toast<V> {
type Rendered = Div<V>;
impl Component for Toast {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let mut div = div();
if self.origin == ToastOrigin::Bottom {
@ -54,7 +54,7 @@ impl<V: 'static> Component<V> for Toast<V> {
}
}
impl<V: 'static> Toast<V> {
impl Toast {
pub fn new(origin: ToastOrigin) -> Self {
Self {
origin,
@ -62,7 +62,7 @@ impl<V: 'static> Toast<V> {
}
}
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
fn render(self, cx: &mut WindowContext) -> impl Element {
let mut div = div();
if self.origin == ToastOrigin::Bottom {
@ -85,8 +85,8 @@ impl<V: 'static> Toast<V> {
}
}
impl<V: 'static> ParentElement<V> for Toast<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
impl ParentElement for Toast {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
&mut self.children
}
}
@ -104,12 +104,12 @@ mod stories {
pub struct ToastStory;
impl Render<Self> for ToastStory {
type Element = Div<Self>;
impl Render for ToastStory {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, Toast<Self>>(cx))
.child(Story::title_for::<Toast<Self>>(cx))
.child(Story::label(cx, "Default"))
.child(Toast::new(ToastOrigin::Bottom).child(Label::new("label")))
}

View File

@ -44,7 +44,7 @@ impl From<bool> for Toggle {
}
}
pub fn disclosure_control<V: 'static>(toggle: Toggle) -> impl Element<V> {
pub fn disclosure_control(toggle: Toggle) -> impl Element {
match (toggle.is_toggleable(), toggle.is_toggled()) {
(false, _) => div(),
(_, true) => div().child(

View File

@ -4,10 +4,10 @@ use gpui::{Div, RenderOnce};
#[derive(RenderOnce)]
pub struct ToolDivider;
impl<V: 'static> Component<V> for ToolDivider {
type Rendered = Div<V>;
impl Component for ToolDivider {
type Rendered = Div;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
div().w_px().h_3().bg(cx.theme().colors().border)
}
}
@ -17,7 +17,7 @@ impl ToolDivider {
Self
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
fn render(self, cx: &mut WindowContext) -> impl Element {
div().w_px().h_3().bg(cx.theme().colors().border)
}
}

View File

@ -67,8 +67,8 @@ impl Tooltip {
}
}
impl Render<Self> for Tooltip {
type Element = Overlay<Self>;
impl Render for Tooltip {
type Element = Overlay;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();

View File

@ -17,34 +17,15 @@
mod components;
mod elevation;
pub mod prelude;
pub mod settings;
mod static_data;
mod styled_ext;
mod to_extract;
pub mod utils;
pub use components::*;
use gpui::actions;
pub use prelude::*;
pub use static_data::*;
// pub use static_data::*;
pub use styled_ext::*;
pub use to_extract::*;
// This needs to be fully qualified with `crate::` otherwise we get a panic
// at:
// thread '<unnamed>' panicked at crates/gpui2/src/platform/mac/platform.rs:66:81:
// called `Option::unwrap()` on a `None` value
//
// AFAICT this is something to do with conflicting names between crates and modules that
// interfaces with declaring the `ClassDecl`.
pub use crate::settings::*;
#[cfg(feature = "stories")]
mod story;
#[cfg(feature = "stories")]
pub use story::*;
actions!(NoAction);
pub fn binding(key: &str) -> gpui::KeyBinding {
gpui::KeyBinding::new(key, NoAction {}, None)
}

View File

@ -1,74 +0,0 @@
use std::ops::Deref;
use gpui::{rems, AbsoluteLength, AppContext, WindowContext};
use crate::prelude::*;
pub fn init(cx: &mut AppContext) {
cx.set_global(FakeSettings::default());
}
/// Returns the user settings.
pub fn user_settings(cx: &WindowContext) -> FakeSettings {
cx.global::<FakeSettings>().clone()
}
pub fn user_settings_mut<'cx>(cx: &'cx mut WindowContext) -> &'cx mut FakeSettings {
cx.global_mut::<FakeSettings>()
}
#[derive(Clone)]
pub enum SettingValue<T> {
UserDefined(T),
Default(T),
}
impl<T> Deref for SettingValue<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::UserDefined(value) => value,
Self::Default(value) => value,
}
}
}
#[derive(Clone)]
pub struct TitlebarSettings {
pub show_project_owner: SettingValue<bool>,
pub show_git_status: SettingValue<bool>,
pub show_git_controls: SettingValue<bool>,
}
impl Default for TitlebarSettings {
fn default() -> Self {
Self {
show_project_owner: SettingValue::Default(true),
show_git_status: SettingValue::Default(true),
show_git_controls: SettingValue::Default(true),
}
}
}
// These should be merged into settings
#[derive(Clone)]
pub struct FakeSettings {
pub default_panel_size: SettingValue<AbsoluteLength>,
pub list_disclosure_style: SettingValue<DisclosureControlStyle>,
pub list_indent_depth: SettingValue<AbsoluteLength>,
pub titlebar: TitlebarSettings,
}
impl Default for FakeSettings {
fn default() -> Self {
Self {
titlebar: TitlebarSettings::default(),
list_disclosure_style: SettingValue::Default(DisclosureControlStyle::ChevronOnHover),
list_indent_depth: SettingValue::Default(rems(0.3).into()),
default_panel_size: SettingValue::Default(rems(16.).into()),
}
}
}
impl FakeSettings {}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ use crate::prelude::*;
pub struct Story {}
impl Story {
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
pub fn container(cx: &mut gpui::WindowContext) -> Div {
div()
.size_full()
.flex()
@ -16,23 +16,23 @@ impl Story {
}
pub fn title<V: 'static>(
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
title: impl Into<SharedString>,
) -> impl Element<V> {
) -> impl Element {
div()
.text_xl()
.text_color(cx.theme().colors().text)
.child(title.into())
}
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Element<V> {
pub fn title_for<T>(cx: &mut WindowContext) -> impl Element {
Self::title(cx, std::any::type_name::<T>())
}
pub fn label<V: 'static>(
cx: &mut ViewContext<V>,
cx: &mut WindowContext,
label: impl Into<SharedString>,
) -> impl Element<V> {
) -> impl Element {
div()
.mt_4()
.mb_2()

View File

@ -1,9 +1,9 @@
use gpui::{Styled, ViewContext};
use gpui::{Styled, WindowContext};
use theme2::ActiveTheme;
use crate::{ElevationIndex, UITextSize};
fn elevated<E: Styled, V: 'static>(this: E, cx: &mut ViewContext<V>, index: ElevationIndex) -> E {
fn elevated<E: Styled>(this: E, cx: &mut WindowContext, index: ElevationIndex) -> E {
this.bg(cx.theme().colors().elevated_surface_background)
.z_index(index.z_index())
.rounded_lg()
@ -65,7 +65,7 @@ pub trait StyledExt: Styled + Sized {
/// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`
///
/// Example Elements: Title Bar, Panel, Tab Bar, Editor
fn elevation_1<V: 'static>(self, cx: &mut ViewContext<V>) -> Self {
fn elevation_1(self, cx: &mut WindowContext) -> Self {
elevated(self, cx, ElevationIndex::Surface)
}
@ -74,7 +74,7 @@ pub trait StyledExt: Styled + Sized {
/// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`
///
/// Examples: Notifications, Palettes, Detached/Floating Windows, Detached/Floating Panels
fn elevation_2<V: 'static>(self, cx: &mut ViewContext<V>) -> Self {
fn elevation_2(self, cx: &mut WindowContext) -> Self {
elevated(self, cx, ElevationIndex::ElevatedSurface)
}
@ -89,7 +89,7 @@ pub trait StyledExt: Styled + Sized {
/// Sets `bg()`, `rounded_lg()`, `border()`, `border_color()`, `shadow()`
///
/// Examples: Settings Modal, Channel Management, Wizards/Setup UI, Dialogs
fn elevation_4<V: 'static>(self, cx: &mut ViewContext<V>) -> Self {
fn elevation_4(self, cx: &mut WindowContext) -> Self {
elevated(self, cx, ElevationIndex::ModalSurface)
}
}

View File

@ -1,47 +0,0 @@
mod assistant_panel;
mod breadcrumb;
mod buffer;
mod buffer_search;
mod chat_panel;
mod collab_panel;
mod command_palette;
mod copilot;
mod editor_pane;
mod language_selector;
mod multi_buffer;
mod notifications_panel;
mod panes;
mod project_panel;
mod recent_projects;
mod status_bar;
mod tab_bar;
mod terminal;
mod theme_selector;
mod title_bar;
mod toolbar;
mod traffic_lights;
mod workspace;
pub use assistant_panel::*;
pub use breadcrumb::*;
pub use buffer::*;
pub use buffer_search::*;
pub use chat_panel::*;
pub use collab_panel::*;
pub use command_palette::*;
pub use copilot::*;
pub use editor_pane::*;
pub use language_selector::*;
pub use multi_buffer::*;
pub use notifications_panel::*;
pub use panes::*;
pub use project_panel::*;
pub use recent_projects::*;
pub use status_bar::*;
pub use tab_bar::*;
pub use terminal::*;
pub use theme_selector::*;
pub use title_bar::*;
pub use toolbar::*;
pub use traffic_lights::*;
pub use workspace::*;

View File

@ -1,97 +0,0 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Label, Panel, PanelSide};
use gpui::{prelude::*, rems, AbsoluteLength, RenderOnce};
#[derive(RenderOnce)]
pub struct AssistantPanel {
id: ElementId,
current_side: PanelSide,
}
impl<V: 'static> Component<V> for AssistantPanel {
type Rendered = Panel<V>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
Panel::new(self.id.clone(), cx)
.children(vec![div()
.flex()
.flex_col()
.h_full()
.px_2()
.gap_2()
// Header
.child(
div()
.flex()
.justify_between()
.gap_2()
.child(
div()
.flex()
.child(IconButton::new("menu", Icon::Menu))
.child(Label::new("New Conversation")),
)
.child(
div()
.flex()
.items_center()
.gap_px()
.child(IconButton::new("split_message", Icon::SplitMessage))
.child(IconButton::new("quote", Icon::Quote))
.child(IconButton::new("magic_wand", Icon::MagicWand))
.child(IconButton::new("plus", Icon::Plus))
.child(IconButton::new("maximize", Icon::Maximize)),
),
)
// Chat Body
.child(
div()
.id("chat-body")
.w_full()
.flex()
.flex_col()
.gap_3()
.overflow_y_scroll()
.child(Label::new("Is this thing on?")),
)
.into_any()])
.side(self.current_side)
.width(AbsoluteLength::Rems(rems(32.)))
}
}
impl AssistantPanel {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
id: id.into(),
current_side: PanelSide::default(),
}
}
pub fn side(mut self, side: PanelSide) -> Self {
self.current_side = side;
self
}
}
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
pub struct AssistantPanelStory;
impl Render<Self> for AssistantPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, AssistantPanel>(cx))
.child(Story::label(cx, "Default"))
.child(AssistantPanel::new("assistant-panel"))
}
}
}

View File

@ -1,118 +0,0 @@
use crate::{h_stack, prelude::*, HighlightedText};
use gpui::{prelude::*, Div, Stateful};
use std::path::PathBuf;
#[derive(Clone)]
pub struct Symbol(pub Vec<HighlightedText>);
#[derive(RenderOnce)]
pub struct Breadcrumb {
path: PathBuf,
symbols: Vec<Symbol>,
}
impl<V: 'static> Component<V> for Breadcrumb {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view_state: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let symbols_len = self.symbols.len();
h_stack()
.id("breadcrumb")
.px_1()
.text_ui_sm()
.text_color(cx.theme().colors().text_muted)
.rounded_md()
.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
.active(|style| style.bg(cx.theme().colors().ghost_element_active))
.child(SharedString::from(
self.path.clone().to_str().unwrap().to_string(),
))
.child(if !self.symbols.is_empty() {
self.render_separator(cx)
} else {
div()
})
.child(
div().flex().children(
self.symbols
.iter()
.enumerate()
// TODO: Could use something like `intersperse` here instead.
.flat_map(|(ix, symbol)| {
let mut items =
vec![div().flex().children(symbol.0.iter().map(|segment| {
div().child(segment.text.clone()).text_color(segment.color)
}))];
let is_last_segment = ix == symbols_len - 1;
if !is_last_segment {
items.push(self.render_separator(cx));
}
items
})
.collect::<Vec<_>>(),
),
)
}
}
impl Breadcrumb {
pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
Self { path, symbols }
}
fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Div<V> {
div()
.child(" ")
.text_color(cx.theme().colors().text_muted)
}
}
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::Story;
use gpui::Render;
use std::str::FromStr;
pub struct BreadcrumbStory;
impl Render<Self> for BreadcrumbStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, Breadcrumb>(cx))
.child(Story::label(cx, "Default"))
.child(Breadcrumb::new(
PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
vec![
Symbol(vec![
HighlightedText {
text: "impl ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
text: "BreadcrumbStory".into(),
color: cx.theme().syntax_color("function"),
},
]),
Symbol(vec![
HighlightedText {
text: "fn ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
text: "render".into(),
color: cx.theme().syntax_color("function"),
},
]),
],
))
}
}
}

View File

@ -1,281 +0,0 @@
use gpui::{Div, Hsla, RenderOnce, WindowContext};
use crate::prelude::*;
use crate::{h_stack, v_stack, Icon, IconElement};
#[derive(Default, PartialEq, Copy, Clone)]
pub struct PlayerCursor {
color: Hsla,
index: usize,
}
#[derive(Default, PartialEq, Clone)]
pub struct HighlightedText {
pub text: SharedString,
pub color: Hsla,
}
#[derive(Default, PartialEq, Clone)]
pub struct HighlightedLine {
pub highlighted_texts: Vec<HighlightedText>,
}
#[derive(Default, PartialEq, Clone)]
pub struct BufferRow {
pub line_number: usize,
pub code_action: bool,
pub current: bool,
pub line: Option<HighlightedLine>,
pub cursors: Option<Vec<PlayerCursor>>,
pub status: GitStatus,
pub show_line_number: bool,
}
#[derive(Clone)]
pub struct BufferRows {
pub show_line_numbers: bool,
pub rows: Vec<BufferRow>,
}
impl Default for BufferRows {
fn default() -> Self {
Self {
show_line_numbers: true,
rows: vec![BufferRow {
line_number: 1,
code_action: false,
current: true,
line: None,
cursors: None,
status: GitStatus::None,
show_line_number: true,
}],
}
}
}
impl BufferRow {
pub fn new(line_number: usize) -> Self {
Self {
line_number,
code_action: false,
current: false,
line: None,
cursors: None,
status: GitStatus::None,
show_line_number: true,
}
}
pub fn set_line(mut self, line: Option<HighlightedLine>) -> Self {
self.line = line;
self
}
pub fn set_cursors(mut self, cursors: Option<Vec<PlayerCursor>>) -> Self {
self.cursors = cursors;
self
}
pub fn add_cursor(mut self, cursor: PlayerCursor) -> Self {
if let Some(cursors) = &mut self.cursors {
cursors.push(cursor);
} else {
self.cursors = Some(vec![cursor]);
}
self
}
pub fn set_status(mut self, status: GitStatus) -> Self {
self.status = status;
self
}
pub fn set_show_line_number(mut self, show_line_number: bool) -> Self {
self.show_line_number = show_line_number;
self
}
pub fn set_code_action(mut self, code_action: bool) -> Self {
self.code_action = code_action;
self
}
pub fn set_current(mut self, current: bool) -> Self {
self.current = current;
self
}
}
#[derive(RenderOnce, Clone)]
pub struct Buffer {
id: ElementId,
rows: Option<BufferRows>,
readonly: bool,
language: Option<String>,
title: Option<String>,
path: Option<String>,
}
impl<V: 'static> Component<V> for Buffer {
type Rendered = Div<V>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let rows = self.render_rows(cx);
v_stack()
.flex_1()
.w_full()
.h_full()
.bg(cx.theme().colors().editor_background)
.children(rows)
}
}
impl Buffer {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
id: id.into(),
rows: Some(BufferRows::default()),
readonly: false,
language: None,
title: Some("untitled".to_string()),
path: None,
}
}
pub fn set_title<T: Into<Option<String>>>(mut self, title: T) -> Self {
self.title = title.into();
self
}
pub fn set_path<P: Into<Option<String>>>(mut self, path: P) -> Self {
self.path = path.into();
self
}
pub fn set_readonly(mut self, readonly: bool) -> Self {
self.readonly = readonly;
self
}
pub fn set_rows<R: Into<Option<BufferRows>>>(mut self, rows: R) -> Self {
self.rows = rows.into();
self
}
pub fn set_language<L: Into<Option<String>>>(mut self, language: L) -> Self {
self.language = language.into();
self
}
fn render_row<V: 'static>(row: BufferRow, cx: &WindowContext) -> impl Element<V> {
let line_background = if row.current {
cx.theme().colors().editor_active_line_background
} else {
cx.theme().styles.system.transparent
};
let line_number_color = if row.current {
cx.theme().colors().text
} else {
cx.theme().syntax_color("comment")
};
h_stack()
.bg(line_background)
.w_full()
.gap_2()
.px_1()
.child(
h_stack()
.w_4()
.h_full()
.px_0p5()
.when(row.code_action, |c| {
div().child(IconElement::new(Icon::Bolt))
}),
)
.when(row.show_line_number, |this| {
this.child(
h_stack().justify_end().px_0p5().w_3().child(
div()
.text_color(line_number_color)
.child(SharedString::from(row.line_number.to_string())),
),
)
})
.child(div().mx_0p5().w_1().h_full().bg(row.status.hsla(cx)))
.children(row.line.map(|line| {
div()
.flex()
.children(line.highlighted_texts.iter().map(|highlighted_text| {
div()
.text_color(highlighted_text.color)
.child(highlighted_text.text.clone())
}))
}))
}
fn render_rows<V: 'static>(&self, cx: &WindowContext) -> Vec<impl Element<V>> {
match &self.rows {
Some(rows) => rows
.rows
.iter()
.map(|row| Self::render_row(row.clone(), cx))
.collect(),
None => vec![],
}
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
let rows = self.render_rows(cx);
v_stack()
.flex_1()
.w_full()
.h_full()
.bg(cx.theme().colors().editor_background)
.children(rows)
}
}
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::{
empty_buffer_example, hello_world_rust_buffer_example,
hello_world_rust_buffer_with_status_example, Story,
};
use gpui::{rems, Div, Render};
pub struct BufferStory;
impl Render<Self> for BufferStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, Buffer>(cx))
.child(Story::label(cx, "Default"))
.child(div().w(rems(64.)).h_96().child(empty_buffer_example()))
.child(Story::label(cx, "Hello World (Rust)"))
.child(
div()
.w(rems(64.))
.h_96()
.child(hello_world_rust_buffer_example(cx)),
)
.child(Story::label(cx, "Hello World (Rust) with Status"))
.child(
div()
.w(rems(64.))
.h_96()
.child(hello_world_rust_buffer_with_status_example(cx)),
)
}
}
}

View File

@ -1,45 +0,0 @@
use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, Input, TextColor};
use gpui::{Div, Render, RenderOnce, View, VisualContext};
#[derive(Clone)]
pub struct BufferSearch {
is_replace_open: bool,
}
impl BufferSearch {
pub fn new() -> Self {
Self {
is_replace_open: false,
}
}
fn toggle_replace(&mut self, cx: &mut ViewContext<Self>) {
self.is_replace_open = !self.is_replace_open;
cx.notify();
}
pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.build_view(|cx| Self::new())
}
}
impl Render<Self> for BufferSearch {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
h_stack()
.bg(cx.theme().colors().toolbar_background)
.p_2()
.child(
h_stack().child(Input::new("Search")).child(
IconButton::<Self>::new("replace", Icon::Replace)
.when(self.is_replace_open, |this| this.color(TextColor::Accent))
.on_click(|buffer_search, cx| {
buffer_search.toggle_replace(cx);
}),
),
)
}
}

View File

@ -1,158 +0,0 @@
use crate::{prelude::*, Icon, IconButton, Input, Label};
use chrono::NaiveDateTime;
use gpui::{prelude::*, Div, Stateful};
#[derive(RenderOnce)]
pub struct ChatPanel {
element_id: ElementId,
messages: Vec<ChatMessage>,
}
impl<V: 'static> Component<V> for ChatPanel {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.id(self.element_id.clone())
.flex()
.flex_col()
.justify_between()
.h_full()
.px_2()
.gap_2()
// Header
.child(
div()
.flex()
.justify_between()
.py_2()
.child(div().flex().child(Label::new("#design")))
.child(
div()
.flex()
.items_center()
.gap_px()
.child(IconButton::new("file", Icon::File))
.child(IconButton::new("audio_on", Icon::AudioOn)),
),
)
.child(
div()
.flex()
.flex_col()
// Chat Body
.child(
div()
.id("chat-body")
.w_full()
.flex()
.flex_col()
.gap_3()
.overflow_y_scroll()
.children(self.messages),
)
// Composer
.child(div().flex().my_2().child(Input::new("Message #design"))),
)
}
}
impl ChatPanel {
pub fn new(element_id: impl Into<ElementId>) -> Self {
Self {
element_id: element_id.into(),
messages: Vec::new(),
}
}
pub fn messages(mut self, messages: Vec<ChatMessage>) -> Self {
self.messages = messages;
self
}
}
#[derive(RenderOnce)]
pub struct ChatMessage {
author: String,
text: String,
sent_at: NaiveDateTime,
}
impl<V: 'static> Component<V> for ChatMessage {
type Rendered = Div<V>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.flex()
.flex_col()
.child(
div()
.flex()
.gap_2()
.child(Label::new(self.author.clone()))
.child(
Label::new(self.sent_at.format("%m/%d/%Y").to_string())
.color(TextColor::Muted),
),
)
.child(div().child(Label::new(self.text.clone())))
}
}
impl ChatMessage {
pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
Self {
author,
text,
sent_at,
}
}
}
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use chrono::DateTime;
use gpui::{Div, Render};
use crate::{Panel, Story};
use super::*;
pub struct ChatPanelStory;
impl Render<Self> for ChatPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, ChatPanel>(cx))
.child(Story::label(cx, "Default"))
.child(
Panel::new("chat-panel-1-outer", cx)
.child(ChatPanel::new("chat-panel-1-inner")),
)
.child(Story::label(cx, "With Mesages"))
.child(Panel::new("chat-panel-2-outer", cx).child(
ChatPanel::new("chat-panel-2-inner").messages(vec![
ChatMessage::new(
"osiewicz".to_string(),
"is this thing on?".to_string(),
DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
.unwrap()
.naive_local(),
),
ChatMessage::new(
"maxdeviant".to_string(),
"Reading you loud and clear!".to_string(),
DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
.unwrap()
.naive_local(),
),
]),
))
}
}
}

View File

@ -1,114 +0,0 @@
use crate::{
prelude::*, static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon,
List, ListHeader, Toggle,
};
use gpui::{prelude::*, Div, Stateful};
#[derive(RenderOnce)]
pub struct CollabPanel {
id: ElementId,
}
impl<V: 'static> Component<V> for CollabPanel {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
v_stack()
.id(self.id.clone())
.h_full()
.bg(cx.theme().colors().surface_background)
.child(
v_stack()
.id("crdb")
.w_full()
.overflow_y_scroll()
.child(
div()
.pb_1()
.border_color(cx.theme().colors().border)
.border_b()
.child(
List::new(static_collab_panel_current_call())
.header(
ListHeader::new("CRDB")
.left_icon(Icon::Hash.into())
.toggle(Toggle::Toggled(true)),
)
.toggle(Toggle::Toggled(true)),
),
)
.child(
v_stack().id("channels").py_1().child(
List::new(static_collab_panel_channels())
.header(ListHeader::new("CHANNELS").toggle(Toggle::Toggled(true)))
.empty_message("No channels yet. Add a channel to get started.")
.toggle(Toggle::Toggled(true)),
),
)
.child(
v_stack().id("contacts-online").py_1().child(
List::new(static_collab_panel_current_call())
.header(
ListHeader::new("CONTACTS ONLINE")
.toggle(Toggle::Toggled(true)),
)
.toggle(Toggle::Toggled(true)),
),
)
.child(
v_stack().id("contacts-offline").py_1().child(
List::new(static_collab_panel_current_call())
.header(
ListHeader::new("CONTACTS OFFLINE")
.toggle(Toggle::Toggled(false)),
)
.toggle(Toggle::Toggled(false)),
),
),
)
.child(
div()
.h_7()
.px_2()
.border_t()
.border_color(cx.theme().colors().border)
.flex()
.items_center()
.child(
div()
.text_ui_sm()
.text_color(cx.theme().colors().text_placeholder)
.child("Find..."),
),
)
}
}
impl CollabPanel {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
}
}
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
pub struct CollabPanelStory;
impl Render<Self> for CollabPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, CollabPanel>(cx))
.child(Story::label(cx, "Default"))
.child(CollabPanel::new("collab-panel"))
}
}
}

View File

@ -1,53 +0,0 @@
use crate::prelude::*;
use crate::{example_editor_actions, OrderMethod, Palette};
#[derive(RenderOnce)]
pub struct CommandPalette {
id: ElementId,
}
impl<V: 'static> Component<V> for CommandPalette {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div().id(self.id.clone()).child(
Palette::new("palette")
.items(example_editor_actions())
.placeholder("Execute a command...")
.empty_string("No items found.")
.default_order(OrderMethod::Ascending),
)
}
}
impl CommandPalette {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
}
}
use gpui::{Div, RenderOnce, Stateful};
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui::{Div, Render};
use crate::Story;
use super::*;
pub struct CommandPaletteStory;
impl Render<Self> for CommandPaletteStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, CommandPalette>(cx))
.child(Story::label(cx, "Default"))
.child(CommandPalette::new("command-palette"))
}
}
}

View File

@ -1,49 +0,0 @@
use crate::{prelude::*, Button, Label, Modal, TextColor};
#[derive(RenderOnce)]
pub struct CopilotModal {
id: ElementId,
}
impl<V: 'static> Component<V> for CopilotModal {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div().id(self.id.clone()).child(
Modal::new("some-id")
.title("Connect Copilot to Zed")
.child(Label::new("You can update your settings or sign out from the Copilot menu in the status bar.").color(TextColor::Muted))
.primary_action(Button::new("Connect to Github").variant(ButtonVariant::Filled)),
)
}
}
impl CopilotModal {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
}
}
use gpui::{Div, RenderOnce, Stateful};
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
pub struct CopilotModalStory;
impl Render<Self> for CopilotModalStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, CopilotModal>(cx))
.child(Story::label(cx, "Default"))
.child(CopilotModal::new("copilot-modal"))
}
}
}

View File

@ -1,77 +0,0 @@
use std::path::PathBuf;
use gpui::{Div, Render, RenderOnce, View, VisualContext};
use crate::prelude::*;
use crate::{
hello_world_rust_editor_with_status_example, v_stack, Breadcrumb, Buffer, BufferSearch, Icon,
IconButton, Symbol, Tab, TabBar, TextColor, Toolbar,
};
#[derive(Clone)]
pub struct EditorPane {
tabs: Vec<Tab>,
path: PathBuf,
symbols: Vec<Symbol>,
buffer: Buffer,
buffer_search: View<BufferSearch>,
is_buffer_search_open: bool,
}
impl EditorPane {
pub fn new(
cx: &mut ViewContext<Self>,
tabs: Vec<Tab>,
path: PathBuf,
symbols: Vec<Symbol>,
buffer: Buffer,
) -> Self {
Self {
tabs,
path,
symbols,
buffer,
buffer_search: BufferSearch::view(cx),
is_buffer_search_open: false,
}
}
pub fn toggle_buffer_search(&mut self, cx: &mut ViewContext<Self>) {
self.is_buffer_search_open = !self.is_buffer_search_open;
cx.notify();
}
pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.build_view(|cx| hello_world_rust_editor_with_status_example(cx))
}
}
impl Render<Self> for EditorPane {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
v_stack()
.w_full()
.h_full()
.flex_1()
.child(TabBar::new("editor-pane-tabs", self.tabs.clone()).can_navigate((false, true)))
.child(
Toolbar::new()
.left_item(Breadcrumb::new(self.path.clone(), self.symbols.clone()))
.right_items(vec![
IconButton::<Self>::new("toggle_inlay_hints", Icon::InlayHint),
IconButton::<Self>::new("buffer_search", Icon::MagnifyingGlass)
.when(self.is_buffer_search_open, |this| {
this.color(TextColor::Accent)
})
.on_click(|editor: &mut Self, cx| {
editor.toggle_buffer_search(cx);
}),
IconButton::new("inline_assist", Icon::MagicWand),
]),
)
.children(Some(self.buffer_search.clone()).filter(|_| self.is_buffer_search_open))
.child(self.buffer.clone())
}
}

View File

@ -1,83 +0,0 @@
use crate::prelude::*;
use crate::{OrderMethod, Palette, PaletteItem};
#[derive(RenderOnce)]
pub struct LanguageSelector {
id: ElementId,
}
impl<V: 'static> Component<V> for LanguageSelector {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div().id(self.id.clone()).child(
Palette::new("palette")
.items(vec![
PaletteItem::new("C"),
PaletteItem::new("C++"),
PaletteItem::new("CSS"),
PaletteItem::new("Elixir"),
PaletteItem::new("Elm"),
PaletteItem::new("ERB"),
PaletteItem::new("Rust (current)"),
PaletteItem::new("Scheme"),
PaletteItem::new("TOML"),
PaletteItem::new("TypeScript"),
])
.placeholder("Select a language...")
.empty_string("No matches")
.default_order(OrderMethod::Ascending),
)
}
}
impl LanguageSelector {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
div().id(self.id.clone()).child(
Palette::new("palette")
.items(vec![
PaletteItem::new("C"),
PaletteItem::new("C++"),
PaletteItem::new("CSS"),
PaletteItem::new("Elixir"),
PaletteItem::new("Elm"),
PaletteItem::new("ERB"),
PaletteItem::new("Rust (current)"),
PaletteItem::new("Scheme"),
PaletteItem::new("TOML"),
PaletteItem::new("TypeScript"),
])
.placeholder("Select a language...")
.empty_string("No matches")
.default_order(OrderMethod::Ascending),
)
}
}
use gpui::{Div, RenderOnce, Stateful};
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
pub struct LanguageSelectorStory;
impl Render<Self> for LanguageSelectorStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, LanguageSelector>(cx))
.child(Story::label(cx, "Default"))
.child(LanguageSelector::new("language-selector"))
}
}
}

View File

@ -1,68 +0,0 @@
use crate::prelude::*;
use crate::{v_stack, Buffer, Icon, IconButton, Label};
#[derive(RenderOnce)]
pub struct MultiBuffer {
buffers: Vec<Buffer>,
}
impl<V: 'static> Component<V> for MultiBuffer {
type Rendered = Div<V>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
v_stack()
.w_full()
.h_full()
.flex_1()
.children(self.buffers.clone().into_iter().map(|buffer| {
v_stack()
.child(
div()
.flex()
.items_center()
.justify_between()
.p_4()
.bg(cx.theme().colors().editor_subheader_background)
.child(Label::new("main.rs"))
.child(IconButton::new("arrow_up_right", Icon::ArrowUpRight)),
)
.child(buffer)
}))
}
}
impl MultiBuffer {
pub fn new(buffers: Vec<Buffer>) -> Self {
Self { buffers }
}
}
use gpui::{Div, RenderOnce};
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::{hello_world_rust_buffer_example, Story};
use gpui::{Div, Render};
pub struct MultiBufferStory;
impl Render<Self> for MultiBufferStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, MultiBuffer>(cx))
.child(Story::label(cx, "Default"))
.child(MultiBuffer::new(vec![
hello_world_rust_buffer_example(cx),
hello_world_rust_buffer_example(cx),
hello_world_rust_buffer_example(cx),
hello_world_rust_buffer_example(cx),
hello_world_rust_buffer_example(cx),
]))
}
}
}

View File

@ -1,379 +0,0 @@
use crate::{
h_stack, prelude::*, static_new_notification_items_2, utils::naive_format_distance_from_now,
v_stack, Avatar, ButtonOrIconButton, ClickHandler, Icon, IconElement, Label, LineHeightStyle,
ListHeader, ListHeaderMeta, ListSeparator, PublicPlayer, TextColor, UnreadIndicator,
};
use gpui::{prelude::*, Div, Stateful};
#[derive(RenderOnce)]
pub struct NotificationsPanel {
id: ElementId,
}
impl<V: 'static> Component<V> for NotificationsPanel {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.id(self.id.clone())
.flex()
.flex_col()
.size_full()
.bg(cx.theme().colors().surface_background)
.child(
ListHeader::new("Notifications").meta(Some(ListHeaderMeta::Tools(vec![
Icon::AtSign,
Icon::BellOff,
Icon::MailOpen,
]))),
)
.child(ListSeparator::new())
.child(
v_stack()
.id("notifications-panel-scroll-view")
.py_1()
.overflow_y_scroll()
.flex_1()
.child(
div()
.mx_2()
.p_1()
// TODO: Add cursor style
// .cursor(Cursor::IBeam)
.bg(cx.theme().colors().element_background)
.border()
.border_color(cx.theme().colors().border_variant)
.child(
Label::new("Search...")
.color(TextColor::Placeholder)
.line_height_style(LineHeightStyle::UILabel),
),
)
.child(v_stack().px_1().children(static_new_notification_items_2())),
)
}
}
impl NotificationsPanel {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
}
}
pub struct NotificationAction<V: 'static> {
button: ButtonOrIconButton<V>,
tooltip: SharedString,
/// Shows after action is chosen
///
/// For example, if the action is "Accept" the taken message could be:
///
/// - `(None,"Accepted")` - "Accepted"
///
/// - `(Some(Icon::Check),"Accepted")` - ✓ "Accepted"
taken_message: (Option<Icon>, SharedString),
}
impl<V: 'static> NotificationAction<V> {
pub fn new(
button: impl Into<ButtonOrIconButton<V>>,
tooltip: impl Into<SharedString>,
(icon, taken_message): (Option<Icon>, impl Into<SharedString>),
) -> Self {
Self {
button: button.into(),
tooltip: tooltip.into(),
taken_message: (icon, taken_message.into()),
}
}
}
pub enum ActorOrIcon {
Actor(PublicPlayer),
Icon(Icon),
}
pub struct NotificationMeta<V: 'static> {
items: Vec<(Option<Icon>, SharedString, Option<ClickHandler<V>>)>,
}
struct NotificationHandlers<V: 'static> {
click: Option<ClickHandler<V>>,
}
impl<V: 'static> Default for NotificationHandlers<V> {
fn default() -> Self {
Self { click: None }
}
}
#[derive(RenderOnce)]
pub struct Notification<V: 'static> {
id: ElementId,
slot: ActorOrIcon,
message: SharedString,
date_received: NaiveDateTime,
meta: Option<NotificationMeta<V>>,
actions: Option<[NotificationAction<V>; 2]>,
unread: bool,
new: bool,
action_taken: Option<NotificationAction<V>>,
handlers: NotificationHandlers<V>,
}
impl<V: 'static> Component<V> for Notification<V> {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.relative()
.id(self.id.clone())
.p_1()
.flex()
.flex_col()
.w_full()
.children(
Some(
div()
.absolute()
.left(px(3.0))
.top_3()
.z_index(2)
.child(UnreadIndicator::new()),
)
.filter(|_| self.unread),
)
.child(
v_stack()
.z_index(1)
.gap_1()
.w_full()
.child(
h_stack()
.w_full()
.gap_2()
.child(self.render_slot(cx))
.child(div().flex_1().child(Label::new(self.message.clone()))),
)
.child(
h_stack()
.justify_between()
.child(
h_stack()
.gap_1()
.child(
Label::new(naive_format_distance_from_now(
self.date_received,
true,
true,
))
.color(TextColor::Muted),
)
.child(self.render_meta_items(cx)),
)
.child(match (self.actions, self.action_taken) {
// Show nothing
(None, _) => div(),
// Show the taken_message
(Some(_), Some(action_taken)) => h_stack()
.children(action_taken.taken_message.0.map(|icon| {
IconElement::new(icon).color(crate::TextColor::Muted)
}))
.child(
Label::new(action_taken.taken_message.1.clone())
.color(TextColor::Muted),
),
// Show the actions
(Some(actions), None) => {
h_stack().children(actions.map(|action| match action.button {
ButtonOrIconButton::Button(button) => {
button.render_into_any()
}
ButtonOrIconButton::IconButton(icon_button) => {
icon_button.render_into_any()
}
}))
}
}),
),
)
}
}
impl<V> Notification<V> {
fn new(
id: ElementId,
message: SharedString,
date_received: NaiveDateTime,
slot: ActorOrIcon,
click_action: Option<ClickHandler<V>>,
) -> Self {
let handlers = if click_action.is_some() {
NotificationHandlers {
click: click_action,
}
} else {
NotificationHandlers::default()
};
Self {
id,
date_received,
message,
meta: None,
slot,
actions: None,
unread: true,
new: false,
action_taken: None,
handlers,
}
}
/// Creates a new notification with an actor slot.
///
/// Requires a click action.
pub fn new_actor_message(
id: impl Into<ElementId>,
message: impl Into<SharedString>,
date_received: NaiveDateTime,
actor: PublicPlayer,
click_action: ClickHandler<V>,
) -> Self {
Self::new(
id.into(),
message.into(),
date_received,
ActorOrIcon::Actor(actor),
Some(click_action),
)
}
/// Creates a new notification with an icon slot.
///
/// Requires a click action.
pub fn new_icon_message(
id: impl Into<ElementId>,
message: impl Into<SharedString>,
date_received: NaiveDateTime,
icon: Icon,
click_action: ClickHandler<V>,
) -> Self {
Self::new(
id.into(),
message.into(),
date_received,
ActorOrIcon::Icon(icon),
Some(click_action),
)
}
/// Creates a new notification with an actor slot
/// and a Call To Action row.
///
/// Cannot take a click action due to required actions.
pub fn new_actor_with_actions(
id: impl Into<ElementId>,
message: impl Into<SharedString>,
date_received: NaiveDateTime,
actor: PublicPlayer,
actions: [NotificationAction<V>; 2],
) -> Self {
Self::new(
id.into(),
message.into(),
date_received,
ActorOrIcon::Actor(actor),
None,
)
.actions(actions)
}
/// Creates a new notification with an icon slot
/// and a Call To Action row.
///
/// Cannot take a click action due to required actions.
pub fn new_icon_with_actions(
id: impl Into<ElementId>,
message: impl Into<SharedString>,
date_received: NaiveDateTime,
icon: Icon,
actions: [NotificationAction<V>; 2],
) -> Self {
Self::new(
id.into(),
message.into(),
date_received,
ActorOrIcon::Icon(icon),
None,
)
.actions(actions)
}
fn on_click(mut self, handler: ClickHandler<V>) -> Self {
self.handlers.click = Some(handler);
self
}
pub fn actions(mut self, actions: [NotificationAction<V>; 2]) -> Self {
self.actions = Some(actions);
self
}
pub fn meta(mut self, meta: NotificationMeta<V>) -> Self {
self.meta = Some(meta);
self
}
fn render_meta_items(&self, cx: &mut ViewContext<V>) -> impl Element<V> {
if let Some(meta) = &self.meta {
h_stack().children(
meta.items
.iter()
.map(|(icon, text, _)| {
let mut meta_el = div();
if let Some(icon) = icon {
meta_el = meta_el.child(IconElement::new(icon.clone()));
}
meta_el.child(Label::new(text.clone()).color(TextColor::Muted))
})
.collect::<Vec<_>>(),
)
} else {
div()
}
}
fn render_slot(&self, cx: &mut ViewContext<V>) -> impl Element<V> {
match &self.slot {
ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render_into_any(),
ActorOrIcon::Icon(icon) => IconElement::new(icon.clone()).render_into_any(),
}
}
}
use chrono::NaiveDateTime;
use gpui::{px, Styled};
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::{Panel, Story};
use gpui::{Div, Render};
pub struct NotificationsPanelStory;
impl Render<Self> for NotificationsPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, NotificationsPanel>(cx))
.child(Story::label(cx, "Default"))
.child(
Panel::new("panel", cx).child(NotificationsPanel::new("notifications_panel")),
)
}
}
}

View File

@ -1,139 +0,0 @@
use gpui::{
hsla, red, AnyElement, Div, ElementId, ExternalPaths, Hsla, Length, RenderOnce, Size, Stateful,
View,
};
use smallvec::SmallVec;
use crate::prelude::*;
#[derive(Default, PartialEq)]
pub enum SplitDirection {
#[default]
Horizontal,
Vertical,
}
#[derive(RenderOnce)]
pub struct Pane<V: 'static> {
id: ElementId,
size: Size<Length>,
fill: Hsla,
children: SmallVec<[AnyElement<V>; 2]>,
}
impl<V: 'static> Component<V> for Pane<V> {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.id(self.id.clone())
.flex()
.flex_initial()
.bg(self.fill)
.w(self.size.width)
.h(self.size.height)
.relative()
.child(div().z_index(0).size_full().children(self.children))
.child(
div()
.z_index(1)
.id("drag-target")
.drag_over::<ExternalPaths>(|d| d.bg(red()))
.on_drop(|_, files: View<ExternalPaths>, cx| {
eprintln!("dropped files! {:?}", files.read(cx));
})
.absolute()
.inset_0(),
)
}
}
impl<V: 'static> Pane<V> {
pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
// Fill is only here for debugging purposes, remove before release
Self {
id: id.into(),
size,
fill: hsla(0.3, 0.3, 0.3, 1.),
children: SmallVec::new(),
}
}
pub fn fill(mut self, fill: Hsla) -> Self {
self.fill = fill;
self
}
}
impl<V: 'static> ParentElement<V> for Pane<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
}
#[derive(RenderOnce)]
pub struct PaneGroup<V: 'static> {
groups: Vec<PaneGroup<V>>,
panes: Vec<Pane<V>>,
split_direction: SplitDirection,
}
impl<V: 'static> Component<V> for PaneGroup<V> {
type Rendered = Div<V>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
if !self.panes.is_empty() {
let el = div()
.flex()
.flex_1()
.gap_px()
.w_full()
.h_full()
.children(self.panes.into_iter().map(|pane| pane.render(view, cx)));
if self.split_direction == SplitDirection::Horizontal {
return el;
} else {
return el.flex_col();
}
}
if !self.groups.is_empty() {
let el = div()
.flex()
.flex_1()
.gap_px()
.w_full()
.h_full()
.bg(cx.theme().colors().editor_background)
.children(self.groups.into_iter().map(|group| group.render(view, cx)));
if self.split_direction == SplitDirection::Horizontal {
return el;
} else {
return el.flex_col();
}
}
unreachable!()
}
}
impl<V: 'static> PaneGroup<V> {
pub fn new_groups(groups: Vec<PaneGroup<V>>, split_direction: SplitDirection) -> Self {
Self {
groups,
panes: Vec::new(),
split_direction,
}
}
pub fn new_panes(panes: Vec<Pane<V>>, split_direction: SplitDirection) -> Self {
Self {
groups: Vec::new(),
panes,
split_direction,
}
}
}

View File

@ -1,113 +0,0 @@
use crate::{
prelude::*, static_project_panel_project_items, static_project_panel_single_items, Input, List,
ListHeader,
};
use gpui::prelude::*;
use gpui::Div;
use gpui::Stateful;
#[derive(RenderOnce)]
pub struct ProjectPanel {
id: ElementId,
}
impl<V: 'static> Component<V> for ProjectPanel {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.id(self.id.clone())
.flex()
.flex_col()
.size_full()
.bg(cx.theme().colors().surface_background)
.child(
div()
.id("project-panel-contents")
.w_full()
.flex()
.flex_col()
.overflow_y_scroll()
.child(
List::new(static_project_panel_single_items())
.header(ListHeader::new("FILES"))
.empty_message("No files in directory"),
)
.child(
List::new(static_project_panel_project_items())
.header(ListHeader::new("PROJECT"))
.empty_message("No folders in directory"),
),
)
.child(
Input::new("Find something...")
.value("buffe".to_string())
.state(InteractionState::Focused),
)
}
}
impl ProjectPanel {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
div()
.id(self.id.clone())
.flex()
.flex_col()
.size_full()
.bg(cx.theme().colors().surface_background)
.child(
div()
.id("project-panel-contents")
.w_full()
.flex()
.flex_col()
.overflow_y_scroll()
.child(
List::new(static_project_panel_single_items())
.header(ListHeader::new("FILES"))
.empty_message("No files in directory"),
)
.child(
List::new(static_project_panel_project_items())
.header(ListHeader::new("PROJECT"))
.empty_message("No folders in directory"),
),
)
.child(
Input::new("Find something...")
.value("buffe".to_string())
.state(InteractionState::Focused),
)
}
}
use gpui::ElementId;
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::{Panel, Story};
use gpui::{Div, Render};
pub struct ProjectPanelStory;
impl Render<Self> for ProjectPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, ProjectPanel>(cx))
.child(Story::label(cx, "Default"))
.child(
Panel::new("project-panel-outer", cx)
.child(ProjectPanel::new("project-panel-inner")),
)
}
}
}

View File

@ -1,58 +0,0 @@
use crate::prelude::*;
use crate::{OrderMethod, Palette, PaletteItem};
#[derive(RenderOnce)]
pub struct RecentProjects {
id: ElementId,
}
impl<V: 'static> Component<V> for RecentProjects {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div().id(self.id.clone()).child(
Palette::new("palette")
.items(vec![
PaletteItem::new("zed").sublabel(SharedString::from("~/projects/zed")),
PaletteItem::new("saga").sublabel(SharedString::from("~/projects/saga")),
PaletteItem::new("journal").sublabel(SharedString::from("~/journal")),
PaletteItem::new("dotfiles").sublabel(SharedString::from("~/dotfiles")),
PaletteItem::new("zed.dev").sublabel(SharedString::from("~/projects/zed.dev")),
PaletteItem::new("laminar").sublabel(SharedString::from("~/projects/laminar")),
])
.placeholder("Recent Projects...")
.empty_string("No matches")
.default_order(OrderMethod::Ascending),
)
}
}
impl RecentProjects {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
}
}
use gpui::{Div, RenderOnce, Stateful};
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
pub struct RecentProjectsStory;
impl Render<Self> for RecentProjectsStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, RecentProjects>(cx))
.child(Story::label(cx, "Default"))
.child(RecentProjects::new("recent-projects"))
}
}
}

View File

@ -1,201 +0,0 @@
use std::sync::Arc;
use gpui::{Div, RenderOnce};
use crate::prelude::*;
use crate::{Button, Icon, IconButton, TextColor, ToolDivider, Workspace};
#[derive(Default, PartialEq)]
pub enum Tool {
#[default]
ProjectPanel,
CollaborationPanel,
Terminal,
Assistant,
Feedback,
Diagnostics,
}
struct ToolGroup {
active_index: Option<usize>,
tools: Vec<Tool>,
}
impl Default for ToolGroup {
fn default() -> Self {
ToolGroup {
active_index: None,
tools: vec![],
}
}
}
#[derive(RenderOnce)]
#[view = "Workspace"]
pub struct StatusBar {
left_tools: Option<ToolGroup>,
right_tools: Option<ToolGroup>,
bottom_tools: Option<ToolGroup>,
}
impl Component<Workspace> for StatusBar {
type Rendered = Div<Workspace>;
fn render(self, view: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Self::Rendered {
div()
.py_0p5()
.px_1()
.flex()
.items_center()
.justify_between()
.w_full()
.bg(cx.theme().colors().status_bar_background)
.child(self.left_tools(view, cx))
.child(self.right_tools(view, cx))
}
}
impl StatusBar {
pub fn new() -> Self {
Self {
left_tools: None,
right_tools: None,
bottom_tools: None,
}
}
pub fn left_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
self.left_tools = {
let mut tools = vec![tool];
tools.extend(self.left_tools.take().unwrap_or_default().tools);
Some(ToolGroup {
active_index,
tools,
})
};
self
}
pub fn right_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
self.right_tools = {
let mut tools = vec![tool];
tools.extend(self.left_tools.take().unwrap_or_default().tools);
Some(ToolGroup {
active_index,
tools,
})
};
self
}
pub fn bottom_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
self.bottom_tools = {
let mut tools = vec![tool];
tools.extend(self.left_tools.take().unwrap_or_default().tools);
Some(ToolGroup {
active_index,
tools,
})
};
self
}
fn left_tools(&self, workspace: &mut Workspace, cx: &WindowContext) -> impl Element<Workspace> {
div()
.flex()
.items_center()
.gap_1()
.child(
IconButton::<Workspace>::new("project_panel", Icon::FileTree)
.when(workspace.is_project_panel_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_project_panel(cx);
}),
)
.child(
IconButton::<Workspace>::new("collab_panel", Icon::Hash)
.when(workspace.is_collab_panel_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_collab_panel();
}),
)
.child(ToolDivider::new())
.child(IconButton::new("diagnostics", Icon::XCircle))
}
fn right_tools(
&self,
workspace: &mut Workspace,
cx: &WindowContext,
) -> impl Element<Workspace> {
div()
.flex()
.items_center()
.gap_2()
.child(
div()
.flex()
.items_center()
.gap_1()
.child(Button::new("116:25"))
.child(
Button::<Workspace>::new("Rust").on_click(Arc::new(|workspace, cx| {
workspace.toggle_language_selector(cx);
})),
),
)
.child(ToolDivider::new())
.child(
div()
.flex()
.items_center()
.gap_1()
.child(
IconButton::new("copilot", Icon::Copilot)
.on_click(|_, _| println!("Copilot clicked.")),
)
.child(
IconButton::new("envelope", Icon::Envelope)
.on_click(|_, _| println!("Send Feedback clicked.")),
),
)
.child(ToolDivider::new())
.child(
div()
.flex()
.items_center()
.gap_1()
.child(
IconButton::<Workspace>::new("terminal", Icon::Terminal)
.when(workspace.is_terminal_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_terminal(cx);
}),
)
.child(
IconButton::<Workspace>::new("chat_panel", Icon::MessageBubbles)
.when(workspace.is_chat_panel_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_chat_panel(cx);
}),
)
.child(
IconButton::<Workspace>::new("assistant_panel", Icon::Ai)
.when(workspace.is_assistant_panel_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_assistant_panel(cx);
}),
),
)
}
}

View File

@ -1,156 +0,0 @@
use crate::{prelude::*, Icon, IconButton, Tab};
use gpui::prelude::*;
use gpui::Div;
use gpui::Stateful;
#[derive(RenderOnce)]
pub struct TabBar {
id: ElementId,
/// Backwards, Forwards
can_navigate: (bool, bool),
tabs: Vec<Tab>,
}
impl<V: 'static> Component<V> for TabBar {
type Rendered = Stateful<V, Div<V>>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let (can_navigate_back, can_navigate_forward) = self.can_navigate;
div()
.group("tab_bar")
.id(self.id.clone())
.w_full()
.flex()
.bg(cx.theme().colors().tab_bar_background)
// Left Side
.child(
div()
.relative()
.px_1()
.flex()
.flex_none()
.gap_2()
// Nav Buttons
.child(
div()
.right_0()
.flex()
.items_center()
.gap_px()
.child(
IconButton::new("arrow_left", Icon::ArrowLeft)
.state(InteractionState::Enabled.if_enabled(can_navigate_back)),
)
.child(
IconButton::new("arrow_right", Icon::ArrowRight).state(
InteractionState::Enabled.if_enabled(can_navigate_forward),
),
),
),
)
.child(
div().w_0().flex_1().h_full().child(
div()
.id("tabs")
.flex()
.overflow_x_scroll()
.children(self.tabs.clone()),
),
)
// Right Side
.child(
div()
// We only use absolute here since we don't
// have opacity or `hidden()` yet
.absolute()
.neg_top_7()
.px_1()
.flex()
.flex_none()
.gap_2()
.group_hover("tab_bar", |this| this.top_0())
// Nav Buttons
.child(
div()
.flex()
.items_center()
.gap_px()
.child(IconButton::new("plus", Icon::Plus))
.child(IconButton::new("split", Icon::Split)),
),
)
}
}
impl TabBar {
pub fn new(id: impl Into<ElementId>, tabs: Vec<Tab>) -> Self {
Self {
id: id.into(),
can_navigate: (false, false),
tabs,
}
}
pub fn can_navigate(mut self, can_navigate: (bool, bool)) -> Self {
self.can_navigate = can_navigate;
self
}
}
use gpui::ElementId;
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
pub struct TabBarStory;
impl Render<Self> for TabBarStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, TabBar>(cx))
.child(Story::label(cx, "Default"))
.child(TabBar::new(
"tab-bar",
vec![
Tab::new(1)
.title("Cargo.toml".to_string())
.current(false)
.git_status(GitStatus::Modified),
Tab::new(2)
.title("Channels Panel".to_string())
.current(false),
Tab::new(3)
.title("channels_panel.rs".to_string())
.current(true)
.git_status(GitStatus::Modified),
Tab::new(4)
.title("workspace.rs".to_string())
.current(false)
.git_status(GitStatus::Modified),
Tab::new(5)
.title("icon_button.rs".to_string())
.current(false),
Tab::new(6)
.title("storybook.rs".to_string())
.current(false)
.git_status(GitStatus::Created),
Tab::new(7).title("theme.rs".to_string()).current(false),
Tab::new(8)
.title("theme_registry.rs".to_string())
.current(false),
Tab::new(9)
.title("styleable_helpers.rs".to_string())
.current(false),
],
))
}
}
}

View File

@ -1,166 +0,0 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Pane, Tab};
use gpui::{relative, rems, Div, RenderOnce, Size};
#[derive(RenderOnce)]
pub struct Terminal;
impl<V: 'static> Component<V> for Terminal {
type Rendered = Div<V>;
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let can_navigate_back = true;
let can_navigate_forward = false;
div()
.flex()
.flex_col()
.w_full()
.child(
// Terminal Tabs.
div()
.w_full()
.flex()
.bg(cx.theme().colors().surface_background)
.child(
div().px_1().flex().flex_none().gap_2().child(
div()
.flex()
.items_center()
.gap_px()
.child(
IconButton::new("arrow_left", Icon::ArrowLeft).state(
InteractionState::Enabled.if_enabled(can_navigate_back),
),
)
.child(IconButton::new("arrow_right", Icon::ArrowRight).state(
InteractionState::Enabled.if_enabled(can_navigate_forward),
)),
),
)
.child(
div().w_0().flex_1().h_full().child(
div()
.flex()
.child(
Tab::new(1)
.title("zed — fish".to_string())
.icon(Icon::Terminal)
.close_side(IconSide::Right)
.current(true),
)
.child(
Tab::new(2)
.title("zed — fish".to_string())
.icon(Icon::Terminal)
.close_side(IconSide::Right)
.current(false),
),
),
),
)
// Terminal Pane.
.child(
Pane::new(
"terminal",
Size {
width: relative(1.).into(),
height: rems(36.).into(),
},
)
.child(crate::static_data::terminal_buffer(cx)),
)
}
}
impl Terminal {
pub fn new() -> Self {
Self
}
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
let can_navigate_back = true;
let can_navigate_forward = false;
div()
.flex()
.flex_col()
.w_full()
.child(
// Terminal Tabs.
div()
.w_full()
.flex()
.bg(cx.theme().colors().surface_background)
.child(
div().px_1().flex().flex_none().gap_2().child(
div()
.flex()
.items_center()
.gap_px()
.child(
IconButton::new("arrow_left", Icon::ArrowLeft).state(
InteractionState::Enabled.if_enabled(can_navigate_back),
),
)
.child(IconButton::new("arrow_right", Icon::ArrowRight).state(
InteractionState::Enabled.if_enabled(can_navigate_forward),
)),
),
)
.child(
div().w_0().flex_1().h_full().child(
div()
.flex()
.child(
Tab::new(1)
.title("zed — fish".to_string())
.icon(Icon::Terminal)
.close_side(IconSide::Right)
.current(true),
)
.child(
Tab::new(2)
.title("zed — fish".to_string())
.icon(Icon::Terminal)
.close_side(IconSide::Right)
.current(false),
),
),
),
)
// Terminal Pane.
.child(
Pane::new(
"terminal",
Size {
width: relative(1.).into(),
height: rems(36.).into(),
},
)
.child(crate::static_data::terminal_buffer(cx)),
)
}
}
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
pub struct TerminalStory;
impl Render<Self> for TerminalStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
.child(Story::title_for::<_, Terminal>(cx))
.child(Story::label(cx, "Default"))
.child(Terminal::new())
}
}
}

Some files were not shown because too many files have changed in this diff Show More