Merge remote-tracking branch 'origin/main' into editor2-rename

# Conflicts:
#	crates/editor2/src/editor.rs
#	crates/editor2/src/element.rs
#	crates/gpui2/src/style.rs
This commit is contained in:
Antonio Scandurra
2023-11-15 09:51:33 +01:00
103 changed files with 7489 additions and 4864 deletions
Generated
+59 -1
View File
@@ -3061,6 +3061,31 @@ dependencies = [
"workspace",
]
[[package]]
name = "file_finder2"
version = "0.1.0"
dependencies = [
"collections",
"ctor",
"editor2",
"env_logger 0.9.3",
"fuzzy2",
"gpui2",
"language2",
"menu2",
"picker2",
"postage",
"project2",
"serde",
"serde_json",
"settings2",
"text2",
"theme2",
"ui2",
"util",
"workspace2",
]
[[package]]
name = "filetime"
version = "0.2.22"
@@ -4198,6 +4223,7 @@ dependencies = [
"anyhow",
"gpui2",
"log",
"serde",
"smol",
"util",
]
@@ -6608,6 +6634,36 @@ dependencies = [
"workspace",
]
[[package]]
name = "project_panel2"
version = "0.1.0"
dependencies = [
"anyhow",
"client2",
"collections",
"context_menu",
"db2",
"editor2",
"futures 0.3.28",
"gpui2",
"language2",
"menu2",
"postage",
"pretty_assertions",
"project2",
"schemars",
"serde",
"serde_derive",
"serde_json",
"settings2",
"smallvec",
"theme2",
"ui2",
"unicase",
"util",
"workspace2",
]
[[package]]
name = "project_symbols"
version = "0.1.0"
@@ -11393,6 +11449,7 @@ dependencies = [
"editor2",
"env_logger 0.9.3",
"feature_flags2",
"file_finder2",
"fs2",
"fsevent",
"futures 0.3.28",
@@ -11402,7 +11459,7 @@ dependencies = [
"ignore",
"image",
"indexmap 1.9.3",
"install_cli",
"install_cli2",
"isahc",
"journal2",
"language2",
@@ -11417,6 +11474,7 @@ dependencies = [
"parking_lot 0.11.2",
"postage",
"project2",
"project_panel2",
"rand 0.8.5",
"regex",
"rope2",
+1
View File
@@ -80,6 +80,7 @@ members = [
"crates/project",
"crates/project2",
"crates/project_panel",
"crates/project_panel2",
"crates/project_symbols",
"crates/recent_projects",
"crates/rope",
-38
View File
@@ -1,38 +0,0 @@
[package]
name = "ai"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/ai.rs"
doctest = false
[features]
test-support = []
[dependencies]
gpui = { path = "../gpui" }
util = { path = "../util" }
language = { path = "../language" }
async-trait.workspace = true
anyhow.workspace = true
futures.workspace = true
lazy_static.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
isahc.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
postage.workspace = true
rand.workspace = true
log.workspace = true
parse_duration = "2.1.1"
tiktoken-rs.workspace = true
matrixmultiply = "0.3.7"
rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
bincode = "1.3.3"
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
@@ -1,9 +1,9 @@
use collections::{CommandPaletteFilter, HashMap};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
actions, div, Action, AppContext, Component, Div, EventEmitter, FocusHandle, Keystroke,
ParentElement, Render, StatelessInteractive, Styled, View, ViewContext, VisualContext,
WeakView, WindowContext,
actions, div, prelude::*, Action, AppContext, Component, Div, EventEmitter, FocusHandle,
Keystroke, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView,
WindowContext,
};
use picker::{Picker, PickerDelegate};
use std::{
+39 -27
View File
@@ -39,12 +39,12 @@ use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display;
use gpui::{
action, actions, div, point, px, relative, rems, render_view, size, uniform_list, AnyElement,
AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
Entity, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla,
InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render,
StatefulInteractive, StatelessInteractive, Styled, Subscription, Task, TextStyle,
UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext,
action, actions, div, point, prelude::*, px, relative, rems, render_view, size, uniform_list,
AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem,
Component, Context, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight,
HighlightStyle, Hsla, InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels,
Render, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext,
VisualContext, WeakView, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@@ -7808,17 +7808,22 @@ impl Editor {
.pl(cx.anchor_x)
.child(render_view(
&rename_editor,
EditorElement::new(EditorStyle {
background: cx.theme().system().transparent,
local_player: cx.editor_style.local_player,
text: text_style,
scrollbar_width: cx.editor_style.scrollbar_width,
syntax: cx.editor_style.syntax.clone(),
diagnostic_style: cx
.editor_style
.diagnostic_style
.clone(),
}),
EditorElement::new(
&rename_editor,
EditorStyle {
background: cx.theme().system().transparent,
local_player: cx.editor_style.local_player,
text: text_style,
scrollbar_width: cx
.editor_style
.scrollbar_width,
syntax: cx.editor_style.syntax.clone(),
diagnostic_style: cx
.editor_style
.diagnostic_style
.clone(),
},
),
))
.render()
}
@@ -9192,6 +9197,10 @@ impl Editor {
cx.focus(&self.focus_handle)
}
pub fn is_focused(&self, cx: &WindowContext) -> bool {
self.focus_handle.is_focused(cx)
}
fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
if self.focus_handle.is_focused(cx) {
// todo!()
@@ -9403,8 +9412,8 @@ impl Render for Editor {
EditorMode::SingleLine => {
TextStyle {
color: cx.theme().colors().text,
font_family: "Zed Sans".into(), // todo!()
font_features: FontFeatures::default(),
font_family: settings.ui_font.family.clone(), // todo!()
font_features: settings.ui_font.features,
font_size: rems(0.875).into(),
font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal,
@@ -9433,14 +9442,17 @@ impl Render for Editor {
EditorMode::Full => cx.theme().colors().editor_background,
};
EditorElement::new(EditorStyle {
background,
local_player: cx.theme().players().local(),
text: text_style,
scrollbar_width: px(12.),
syntax: cx.theme().syntax().clone(),
diagnostic_style: cx.theme().diagnostic_style(),
})
EditorElement::new(
cx.view(),
EditorStyle {
background,
local_player: cx.theme().players().local(),
text: text_style,
scrollbar_width: px(12.),
syntax: cx.theme().syntax().clone(),
diagnostic_style: cx.theme().diagnostic_style(),
},
)
}
}
+212 -211
View File
@@ -20,9 +20,9 @@ use collections::{BTreeMap, HashMap};
use gpui::{
point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, BorrowWindow,
Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, ElementId,
ElementInputHandler, Entity, Hsla, Line, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, ParentElement, Pixels, ScrollWheelEvent, Size, Style, Styled, TextRun, TextStyle,
ViewContext, WindowContext,
ElementInputHandler, Entity, EntityId, Hsla, Line, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, ParentComponent, Pixels, ScrollWheelEvent, Size, Style, Styled, TextRun,
TextStyle, View, ViewContext, WindowContext,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@@ -111,12 +111,16 @@ impl SelectionLayout {
}
pub struct EditorElement {
editor_id: EntityId,
style: EditorStyle,
}
impl EditorElement {
pub fn new(style: EditorStyle) -> Self {
Self { style }
pub fn new(editor: &View<Editor>, style: EditorStyle) -> Self {
Self {
editor_id: editor.entity_id(),
style,
}
}
fn mouse_down(
@@ -622,7 +626,7 @@ impl EditorElement {
let line_end_overshoot = 0.15 * layout.position_map.line_height;
let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
cx.with_content_mask(ContentMask { bounds }, |cx| {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
// todo!("cursor region")
// cx.scene().push_cursor_region(CursorRegion {
// bounds,
@@ -1448,6 +1452,7 @@ impl EditorElement {
let snapshot = editor.snapshot(cx);
let style = self.style.clone();
let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
let font_size = style.text.font_size.to_pixels(cx.rem_size());
let line_height = style.text.line_height_in_pixels(cx.rem_size());
@@ -2399,8 +2404,8 @@ enum Invisible {
impl Element<Editor> for EditorElement {
type ElementState = ();
fn id(&self) -> Option<gpui::ElementId> {
None
fn element_id(&self) -> Option<gpui::ElementId> {
Some(self.editor_id.into())
}
fn initialize(
@@ -2410,186 +2415,6 @@ impl Element<Editor> for EditorElement {
cx: &mut gpui::ViewContext<Editor>,
) -> Self::ElementState {
editor.style = Some(self.style.clone()); // Long-term, we'd like to eliminate this.
let dispatch_context = editor.dispatch_context(cx);
cx.with_element_id(cx.view().entity_id(), |global_id, cx| {
cx.with_key_dispatch(
dispatch_context,
Some(editor.focus_handle.clone()),
|_, cx| {
register_action(cx, Editor::move_left);
register_action(cx, Editor::move_right);
register_action(cx, Editor::move_down);
register_action(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| {
editor.scroll_screen(&ScrollAmount::Line(1.), cx)
});
register_action(cx, |editor, _: &LineUp, cx| {
editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
});
register_action(cx, |editor, _: &HalfPageDown, cx| {
editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
});
register_action(cx, |editor, _: &HalfPageUp, cx| {
editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
});
register_action(cx, |editor, _: &PageDown, cx| {
editor.scroll_screen(&ScrollAmount::Page(1.), cx)
});
register_action(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| {
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| {
editor.select_next(action, cx).log_err();
});
register_action(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);
// 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| {
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);
// on_action(cx, Editor::confirm_completion); todo!()
register_action(cx, |editor, action, cx| {
editor
.confirm_code_action(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, |editor, action, cx| {
editor
.rename(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, |editor, action, cx| {
editor
.confirm_rename(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(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);
},
)
});
}
fn layout(
@@ -2626,32 +2451,208 @@ impl Element<Editor> for EditorElement {
size: layout.text_size,
};
// We call with_z_index to establish a new stacking context.
cx.with_z_index(0, |cx| {
cx.with_content_mask(ContentMask { bounds }, |cx| {
self.paint_mouse_listeners(
bounds,
gutter_bounds,
text_bounds,
&layout.position_map,
cx,
);
let dispatch_context = editor.dispatch_context(cx);
cx.with_key_dispatch(
dispatch_context,
Some(editor.focus_handle.clone()),
|_, cx| {
register_action(cx, Editor::move_left);
register_action(cx, Editor::move_right);
register_action(cx, Editor::move_down);
register_action(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| {
editor.scroll_screen(&ScrollAmount::Line(1.), cx)
});
register_action(cx, |editor, _: &LineUp, cx| {
editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
});
register_action(cx, |editor, _: &HalfPageDown, cx| {
editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
});
register_action(cx, |editor, _: &HalfPageUp, cx| {
editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
});
register_action(cx, |editor, _: &PageDown, cx| {
editor.scroll_screen(&ScrollAmount::Page(1.), cx)
});
register_action(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| {
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| {
editor.select_next(action, cx).log_err();
});
register_action(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);
// 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| {
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);
// on_action(cx, Editor::confirm_completion); todo!()
register_action(cx, |editor, action, cx| {
editor
.confirm_code_action(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, |editor, action, cx| {
editor
.rename(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(cx, |editor, action, cx| {
editor
.confirm_rename(action, cx)
.map(|task| task.detach_and_log_err(cx));
});
register_action(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);
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);
}
// 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| {
self.paint_mouse_listeners(
bounds,
gutter_bounds,
text_bounds,
&layout.position_map,
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);
self.paint_text(text_bounds, &mut layout, editor, cx);
if !layout.blocks.is_empty() {
self.paint_blocks(bounds, &mut layout, editor, cx);
}
if !layout.blocks.is_empty() {
self.paint_blocks(bounds, &mut layout, editor, cx);
}
let input_handler = ElementInputHandler::new(bounds, cx);
cx.handle_input(&editor.focus_handle, input_handler);
});
});
let input_handler = ElementInputHandler::new(bounds, cx);
cx.handle_input(&editor.focus_handle, input_handler);
});
});
},
)
}
}
+3 -3
View File
@@ -9,7 +9,7 @@ use collections::HashSet;
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,
FocusHandle, Model, ParentComponent, Pixels, SharedString, Styled, Subscription, Task, View,
ViewContext, VisualContext, WeakView,
};
use language::{
@@ -30,7 +30,7 @@ use std::{
};
use text::Selection;
use theme::{ActiveTheme, Theme};
use ui::{Label, LabelColor};
use ui::{Label, TextColor};
use util::{paths::PathExt, ResultExt, TryFutureExt};
use workspace::item::{BreadcrumbText, FollowEvent, FollowableEvents, FollowableItemHandle};
use workspace::{
@@ -607,7 +607,7 @@ impl Item for Editor {
&description,
MAX_TAB_TITLE_LEN,
))
.color(LabelColor::Muted),
.color(TextColor::Muted),
),
)
})),
+37
View File
@@ -0,0 +1,37 @@
[package]
name = "file_finder2"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/file_finder.rs"
doctest = false
[dependencies]
editor = { package = "editor2", path = "../editor2" }
collections = { path = "../collections" }
fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
gpui = { package = "gpui2", path = "../gpui2" }
menu = { package = "menu2", path = "../menu2" }
picker = { package = "picker2", path = "../picker2" }
project = { package = "project2", path = "../project2" }
settings = { package = "settings2", path = "../settings2" }
text = { package = "text2", path = "../text2" }
util = { path = "../util" }
theme = { package = "theme2", path = "../theme2" }
ui = { package = "ui2", path = "../ui2" }
workspace = { package = "workspace2", path = "../workspace2" }
postage.workspace = true
serde.workspace = true
[dev-dependencies]
editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
language = { package = "language2", path = "../language2", features = ["test-support"] }
workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
theme = { package = "theme2", path = "../theme2", features = ["test-support"] }
serde_json.workspace = true
ctor.workspace = true
env_logger.workspace = true
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,11 +1,11 @@
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
use gpui::{
actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString,
StatelessInteractive, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
actions, div, prelude::*, AppContext, Div, EventEmitter, ParentComponent, Render, SharedString,
Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
};
use text::{Bias, Point};
use theme::ActiveTheme;
use ui::{h_stack, v_stack, Label, LabelColor, StyledExt};
use ui::{h_stack, v_stack, Label, StyledExt, TextColor};
use util::paths::FILE_ROW_COLUMN_DELIMITER;
use workspace::{Modal, ModalEvent, Workspace};
@@ -150,7 +150,7 @@ impl Render for GoToLine {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div()
.elevation_2(cx)
.context("GoToLine")
.key_context("GoToLine")
.on_action(Self::cancel)
.on_action(Self::confirm)
.w_96()
@@ -176,7 +176,7 @@ impl Render for GoToLine {
.justify_between()
.px_2()
.py_1()
.child(Label::new(self.current_text.clone()).color(LabelColor::Muted)),
.child(Label::new(self.current_text.clone()).color(TextColor::Muted)),
),
)
}
+4
View File
@@ -2112,6 +2112,10 @@ impl AppContext {
AsyncAppContext(self.weak_self.as_ref().unwrap().upgrade().unwrap())
}
pub fn open_url(&self, url: &str) {
self.platform.open_url(url)
}
pub fn write_to_clipboard(&self, item: ClipboardItem) {
self.platform.write_to_clipboard(item);
}
+41
View File
@@ -0,0 +1,41 @@
# Contexts
GPUI makes extensive use of *context parameters*, typically named `cx` and positioned at the end of the parameter list, unless they're before a final function parameter. A context reference provides access to application state and services.
There are multiple kinds of contexts, and contexts implement the `Deref` trait so that a function taking `&mut AppContext` could be passed a `&mut WindowContext` or `&mut ViewContext` instead.
```
AppContext
/ \
ModelContext WindowContext
/
ViewContext
```
- The `AppContext` forms the root of the hierarchy
- `ModelContext` and `WindowContext` both dereference to `AppContext`
- `ViewContext` dereferences to `WindowContext`
## `AppContext`
Provides access to the global application state. All other kinds of contexts ultimately deref to an `AppContext`. You can update a `Model<T>` by passing an `AppContext`, but you can't update a view. For that you need a `WindowContext`...
## `WindowContext`
Provides access to the state of an application window, and also derefs to an `AppContext`, so you can pass a window context reference to any method taking an app context. Obtain this context by calling `WindowHandle::update`.
## `ModelContext<T>`
Available when you create or update a `Model<T>`. It derefs to an `AppContext`, but also contains methods specific to the particular model, such as the ability to notify change observers or emit events.
## `ViewContext<V>`
Available when you create or update a `View<V>`. It derefs to a `WindowContext`, but also contains methods specific to the particular view, such as the ability to notify change observers or emit events.
## `AsyncAppContext` and `AsyncWindowContext`
Whereas the above contexts are always passed to your code as references, you can call `to_async` on the reference to create an async context, which has a static lifetime and can be held across `await` points in async code. When you interact with `Model`s or `View`s with an async context, the calls become fallible, because the context may outlive the window or even the app itself.
## `TestAppContext` and `TestVisualContext`
These are similar to the async contexts above, but they panic if you attempt to access a non-existent app or window, and they also contain other features specific to tests.
+101
View File
@@ -0,0 +1,101 @@
# Key Dispatch
GPUI is designed for keyboard-first interactivity.
To expose functionality to the mouse, you render a button with a click handler.
To expose functionality to the keyboard, you bind an *action* in a *key context*.
Actions are similar to framework-level events like `MouseDown`, `KeyDown`, etc, but you can define them yourself:
```rust
mod menu {
#[gpui::action]
struct MoveUp;
#[gpui::action]
struct MoveDown;
}
```
Actions are frequently unit structs, for which we have a macro. The above could also be written:
```rust
mod menu {
actions!(MoveUp, MoveDown);
}
```
Actions can also be more complex types:
```rust
mod menu {
#[gpui::action]
struct Move {
direction: Direction,
select: bool,
}
}
```
To bind actions, chain `on_action` on to your element:
```rust
impl Render for Menu {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
div()
.on_action(|this: &mut Menu, move: &MoveUp, cx: &mut ViewContext<Menu>| {
// ...
})
.on_action(|this, move: &MoveDown, cx| {
// ...
})
.children(todo!())
}
}
```
In order to bind keys to actions, you need to declare a *key context* for part of the element tree by calling `key_context`.
```rust
impl Render for Menu {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
div()
.key_context("menu")
.on_action(|this: &mut Menu, move: &MoveUp, cx: &mut ViewContext<Menu>| {
// ...
})
.on_action(|this, move: &MoveDown, cx| {
// ...
})
.children(todo!())
}
}
```
Now you can target your context in the keymap. Note how actions are identified in the keymap by their fully-qualified type name.
```json
{
"context": "menu",
"bindings": {
"up": "menu::MoveUp",
"down": "menu::MoveDown"
}
}
```
If you had opted for the more complex type definition, you'd provide the serialized representation of the action alongside the name:
```json
{
"context": "menu",
"bindings": {
"up": ["menu::Move", {direction: "up", select: false}]
"down": ["menu::Move", {direction: "down", select: false}]
"shift-up": ["menu::Move", {direction: "up", select: true}]
"shift-down": ["menu::Move", {direction: "down", select: true}]
}
}
```
+29 -6
View File
@@ -234,10 +234,10 @@ impl AppContext {
app_version: platform.app_version().ok(),
};
Rc::new_cyclic(|this| AppCell {
let app = Rc::new_cyclic(|this| AppCell {
app: RefCell::new(AppContext {
this: this.clone(),
platform,
platform: platform.clone(),
app_metadata,
text_system,
flushing_effects: false,
@@ -269,12 +269,21 @@ impl AppContext {
layout_id_buffer: Default::default(),
propagate_event: true,
}),
})
});
platform.on_quit(Box::new({
let cx = app.clone();
move || {
cx.borrow_mut().shutdown();
}
}));
app
}
/// Quit the application gracefully. Handlers registered with `ModelContext::on_app_quit`
/// will be given 100ms to complete before exiting.
pub fn quit(&mut self) {
pub fn shutdown(&mut self) {
let mut futures = Vec::new();
for observer in self.quit_observers.remove(&()) {
@@ -292,8 +301,10 @@ impl AppContext {
{
log::error!("timed out waiting on app_will_quit");
}
}
self.globals_by_type.clear();
pub fn quit(&mut self) {
self.platform.quit();
}
pub fn app_metadata(&self) -> AppMetadata {
@@ -431,6 +442,18 @@ impl AppContext {
self.platform.activate(ignoring_other_apps);
}
pub fn hide(&self) {
self.platform.hide();
}
pub fn hide_other_apps(&self) {
self.platform.hide_other_apps();
}
pub fn unhide_other_apps(&self) {
self.platform.unhide_other_apps();
}
/// Returns the list of currently active displays.
pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
self.platform.displays()
@@ -1091,7 +1114,7 @@ impl<G: 'static> DerefMut for GlobalLease<G> {
/// Contains state associated with an active drag operation, started by dragging an element
/// within the window or by dragging into the app from the underlying platform.
pub(crate) struct AnyDrag {
pub struct AnyDrag {
pub view: AnyView,
pub cursor_offset: Point<Pixels>,
}
+1 -1
View File
@@ -26,7 +26,7 @@ impl EntityId {
impl Display for EntityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{}", self.as_u64())
}
}
+41 -10
View File
@@ -1,8 +1,8 @@
use crate::{
div, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, BackgroundExecutor,
Context, Div, EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model,
ModelContext, Render, Result, Task, TestDispatcher, TestPlatform, View, ViewContext,
VisualContext, WindowContext, WindowHandle, WindowOptions,
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
BackgroundExecutor, Context, Div, EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent,
Keystroke, Model, ModelContext, Render, Result, Task, TestDispatcher, TestPlatform, View,
ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
};
use anyhow::{anyhow, bail};
use futures::{Stream, StreamExt};
@@ -14,6 +14,7 @@ pub struct TestAppContext {
pub background_executor: BackgroundExecutor,
pub foreground_executor: ForegroundExecutor,
pub dispatcher: TestDispatcher,
pub test_platform: Rc<TestPlatform>,
}
impl Context for TestAppContext {
@@ -77,17 +78,16 @@ impl TestAppContext {
let arc_dispatcher = Arc::new(dispatcher.clone());
let background_executor = BackgroundExecutor::new(arc_dispatcher.clone());
let foreground_executor = ForegroundExecutor::new(arc_dispatcher);
let platform = Rc::new(TestPlatform::new(
background_executor.clone(),
foreground_executor.clone(),
));
let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone());
let asset_source = Arc::new(());
let http_client = util::http::FakeHttpClient::with_404_response();
Self {
app: AppContext::new(platform, asset_source, http_client),
app: AppContext::new(platform.clone(), asset_source, http_client),
background_executor,
foreground_executor,
dispatcher: dispatcher.clone(),
test_platform: platform,
}
}
@@ -96,7 +96,7 @@ impl TestAppContext {
}
pub fn quit(&self) {
self.app.borrow_mut().quit();
self.app.borrow_mut().shutdown();
}
pub fn refresh(&mut self) -> Result<()> {
@@ -152,6 +152,21 @@ impl TestAppContext {
(view, VisualTestContext::from_window(*window.deref(), self))
}
pub fn simulate_new_path_selection(
&self,
select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
) {
self.test_platform.simulate_new_path_selection(select_path);
}
pub fn simulate_prompt_answer(&self, button_ix: usize) {
self.test_platform.simulate_prompt_answer(button_ix);
}
pub fn has_pending_prompt(&self) -> bool {
self.test_platform.has_pending_prompt()
}
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
where
Fut: Future<Output = R> + 'static,
@@ -199,6 +214,15 @@ impl TestAppContext {
}
}
pub fn dispatch_action<A>(&mut self, window: AnyWindowHandle, action: A)
where
A: Action,
{
window
.update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
.unwrap()
}
pub fn dispatch_keystroke(
&mut self,
window: AnyWindowHandle,
@@ -376,6 +400,13 @@ impl<'a> VisualTestContext<'a> {
pub fn from_window(window: AnyWindowHandle, cx: &'a mut TestAppContext) -> Self {
Self { cx, window }
}
pub fn dispatch_action<A>(&mut self, action: A)
where
A: Action,
{
self.cx.dispatch_action(self.window, action)
}
}
impl<'a> Context for VisualTestContext<'a> {
+10 -1
View File
@@ -293,7 +293,16 @@ pub fn blue() -> Hsla {
pub fn green() -> Hsla {
Hsla {
h: 0.3,
h: 0.33,
s: 1.,
l: 0.5,
a: 1.,
}
}
pub fn yellow() -> Hsla {
Hsla {
h: 0.16,
s: 1.,
l: 0.5,
a: 1.,
+7 -7
View File
@@ -8,7 +8,7 @@ use std::{any::Any, mem};
pub trait Element<V: 'static> {
type ElementState: 'static;
fn id(&self) -> Option<ElementId>;
fn element_id(&self) -> Option<ElementId>;
/// Called to initialize this element for the current frame. If this
/// element had state in a previous frame, it will be passed in for the 3rd argument.
@@ -38,7 +38,7 @@ pub trait Element<V: 'static> {
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
pub trait ParentElement<V: 'static> {
pub trait ParentComponent<V: 'static> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
fn child(mut self, child: impl Component<V>) -> Self
@@ -120,7 +120,7 @@ where
E::ElementState: 'static,
{
fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
let frame_state = if let Some(id) = self.element.id() {
let frame_state = if let Some(id) = self.element.element_id() {
cx.with_element_state(id, |element_state, cx| {
let element_state = self.element.initialize(view_state, element_state, cx);
((), element_state)
@@ -142,7 +142,7 @@ where
frame_state: initial_frame_state,
} => {
frame_state = initial_frame_state;
if let Some(id) = self.element.id() {
if let Some(id) = self.element.element_id() {
layout_id = cx.with_element_state(id, |element_state, cx| {
let mut element_state = element_state.unwrap();
let layout_id = self.element.layout(state, &mut element_state, cx);
@@ -181,7 +181,7 @@ where
..
} => {
let bounds = cx.layout_bounds(layout_id);
if let Some(id) = self.element.id() {
if let Some(id) = self.element.element_id() {
cx.with_element_state(id, |element_state, cx| {
let mut element_state = element_state.unwrap();
self.element
@@ -255,7 +255,7 @@ where
// Ignore the element offset when drawing this element, as the origin is already specified
// in absolute terms.
origin -= cx.element_offset();
cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx))
cx.with_element_offset(origin, |cx| self.paint(view_state, cx))
}
}
@@ -351,7 +351,7 @@ where
{
type ElementState = AnyElement<V>;
fn id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<ElementId> {
None
}
File diff suppressed because it is too large Load Diff
+56 -118
View File
@@ -1,35 +1,28 @@
use crate::{
div, AnyElement, BorrowWindow, Bounds, Component, Div, DivState, Element, ElementId,
ElementInteractivity, FocusListeners, Focusable, FocusableKeyDispatch, KeyDispatch, LayoutId,
NonFocusableKeyDispatch, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
AnyElement, BorrowWindow, Bounds, Component, Element, InteractiveComponent,
InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
Styled, ViewContext,
};
use futures::FutureExt;
use util::ResultExt;
pub struct Img<
V: 'static,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: KeyDispatch<V> = NonFocusableKeyDispatch,
> {
base: Div<V, I, F>,
pub struct Img<V: 'static> {
interactivity: Interactivity<V>,
uri: Option<SharedString>,
grayscale: bool,
}
pub fn img<V: 'static>() -> Img<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
pub fn img<V: 'static>() -> Img<V> {
Img {
base: div(),
interactivity: Interactivity::default(),
uri: None,
grayscale: false,
}
}
impl<V, I, F> Img<V, I, F>
impl<V> Img<V>
where
V: 'static,
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
self.uri = Some(uri.into());
@@ -42,145 +35,90 @@ where
}
}
impl<V, F> Img<V, StatelessInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Img<V, StatefulInteractivity<V>, F> {
Img {
base: self.base.id(id),
uri: self.uri,
grayscale: self.grayscale,
}
}
}
impl<V, I, F> Component<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Component<V> for Img<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
impl<V, I, F> Element<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
type ElementState = DivState;
impl<V> Element<V> for Img<V> {
type ElementState = InteractiveElementState;
fn id(&self) -> Option<crate::ElementId> {
self.base.id()
fn element_id(&self) -> Option<crate::ElementId> {
self.interactivity.element_id.clone()
}
fn initialize(
&mut self,
view_state: &mut V,
_view_state: &mut V,
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
self.base.initialize(view_state, element_state, cx)
self.interactivity.initialize(element_state, cx)
}
fn layout(
&mut self,
view_state: &mut V,
_view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> LayoutId {
self.base.layout(view_state, element_state, cx)
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
view: &mut V,
_view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
cx.with_z_index(0, |cx| {
self.base.paint(bounds, view, element_state, cx);
});
self.interactivity.paint(
bounds,
bounds.size,
element_state,
cx,
|style, _scroll_offset, cx| {
let corner_radii = style.corner_radii;
let style = self.base.compute_style(bounds, element_state, cx);
let corner_radii = style.corner_radii;
if let Some(uri) = self.uri.clone() {
// eprintln!(">>> image_cache.get({uri}");
let image_future = cx.image_cache.get(uri.clone());
// eprintln!("<<< image_cache.get({uri}");
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
} else {
cx.spawn(|_, mut cx| async move {
if image_future.await.log_err().is_some() {
cx.on_next_frame(|cx| cx.notify());
if let Some(uri) = self.uri.clone() {
// eprintln!(">>> image_cache.get({uri}");
let image_future = cx.image_cache.get(uri.clone());
// eprintln!("<<< image_cache.get({uri}");
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
} else {
cx.spawn(|_, mut cx| async move {
if image_future.await.log_err().is_some() {
cx.on_next_frame(|cx| cx.notify());
}
})
.detach()
}
})
.detach()
}
}
}
},
)
}
}
impl<V, I, F> Styled for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Styled for Img<V> {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
&mut self.interactivity.base_style
}
}
impl<V, I, F> StatelessInteractive<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Img<V, StatefulInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V, I> Focusable<V> for Img<V, I, FocusableKeyDispatch<V>>
where
V: 'static,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()
}
fn set_focus_style(&mut self, style: StyleRefinement) {
self.base.set_focus_style(style)
}
fn set_focus_in_style(&mut self, style: StyleRefinement) {
self.base.set_focus_in_style(style)
}
fn set_in_focus_style(&mut self, style: StyleRefinement) {
self.base.set_in_focus_style(style)
impl<V> InteractiveComponent<V> for Img<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
}
+31 -100
View File
@@ -1,157 +1,88 @@
use crate::{
div, AnyElement, Bounds, Component, Div, DivState, Element, ElementId, ElementInteractivity,
FocusListeners, Focusable, FocusableKeyDispatch, KeyDispatch, LayoutId,
NonFocusableKeyDispatch, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
AnyElement, Bounds, Component, Element, ElementId, InteractiveComponent,
InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
Styled, ViewContext,
};
use util::ResultExt;
pub struct Svg<
V: 'static,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: KeyDispatch<V> = NonFocusableKeyDispatch,
> {
base: Div<V, I, F>,
pub struct Svg<V: 'static> {
interactivity: Interactivity<V>,
path: Option<SharedString>,
}
pub fn svg<V: 'static>() -> Svg<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
pub fn svg<V: 'static>() -> Svg<V> {
Svg {
base: div(),
interactivity: Interactivity::default(),
path: None,
}
}
impl<V, I, F> Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Svg<V> {
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
self.path = Some(path.into());
self
}
}
impl<V, F> Svg<V, StatelessInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Svg<V, StatefulInteractivity<V>, F> {
Svg {
base: self.base.id(id),
path: self.path,
}
}
}
impl<V, I, F> Component<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Component<V> for Svg<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
impl<V, I, F> Element<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
type ElementState = DivState;
impl<V> Element<V> for Svg<V> {
type ElementState = InteractiveElementState;
fn id(&self) -> Option<crate::ElementId> {
self.base.id()
fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone()
}
fn initialize(
&mut self,
view_state: &mut V,
_view_state: &mut V,
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
self.base.initialize(view_state, element_state, cx)
self.interactivity.initialize(element_state, cx)
}
fn layout(
&mut self,
view_state: &mut V,
_view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> LayoutId {
self.base.layout(view_state, element_state, cx)
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
view: &mut V,
_view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) where
Self: Sized,
{
self.base.paint(bounds, view, element_state, cx);
let color = self
.base
.compute_style(bounds, element_state, cx)
.text
.color;
if let Some((path, color)) = self.path.as_ref().zip(color) {
cx.paint_svg(bounds, path.clone(), color).log_err();
}
self.interactivity
.paint(bounds, bounds.size, element_state, cx, |style, _, cx| {
if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
cx.paint_svg(bounds, path.clone(), color).log_err();
}
})
}
}
impl<V, I, F> Styled for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Styled for Svg<V> {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
&mut self.interactivity.base_style
}
}
impl<V, I, F> StatelessInteractive<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Svg<V, StatefulInteractivity<V>, F>
where
V: 'static,
F: KeyDispatch<V>,
{
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V: 'static, I> Focusable<V> for Svg<V, I, FocusableKeyDispatch<V>>
where
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()
}
fn set_focus_style(&mut self, style: StyleRefinement) {
self.base.set_focus_style(style)
}
fn set_focus_in_style(&mut self, style: StyleRefinement) {
self.base.set_focus_in_style(style)
}
fn set_in_focus_style(&mut self, style: StyleRefinement) {
self.base.set_in_focus_style(style)
impl<V> InteractiveComponent<V> for Svg<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
}
+1 -1
View File
@@ -72,7 +72,7 @@ impl<V: 'static> Component<V> for Text<V> {
impl<V: 'static> Element<V> for Text<V> {
type ElementState = Arc<Mutex<Option<TextElementState>>>;
fn id(&self) -> Option<crate::ElementId> {
fn element_id(&self) -> Option<crate::ElementId> {
None
}
+122 -107
View File
@@ -1,24 +1,23 @@
use crate::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element,
ElementId, ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
StyleRefinement, Styled, ViewContext,
ElementId, InteractiveComponent, InteractiveElementState, Interactivity, LayoutId, Pixels,
Point, Size, StyleRefinement, Styled, ViewContext,
};
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{cmp, ops::Range, sync::Arc};
use std::{cmp, mem, ops::Range, sync::Arc};
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<Id, V, C>(
id: Id,
pub fn uniform_list<I, V, C>(
id: I,
item_count: usize,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[C; 64]>,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<C>,
) -> UniformList<V>
where
Id: Into<ElementId>,
I: Into<ElementId>,
V: 'static,
C: Component<V>,
{
@@ -37,7 +36,10 @@ where
.map(|component| component.render())
.collect()
}),
interactivity: StatefulInteractivity::new(id, StatelessInteractivity::default()),
interactivity: Interactivity {
element_id: Some(id.into()),
..Default::default()
},
scroll_handle: None,
}
}
@@ -54,7 +56,7 @@ pub struct UniformList<V: 'static> {
&'a mut ViewContext<V>,
) -> SmallVec<[AnyElement<V>; 64]>,
>,
interactivity: StatefulInteractivity<V>,
interactivity: Interactivity<V>,
scroll_handle: Option<UniformListScrollHandle>,
}
@@ -103,7 +105,7 @@ pub struct UniformListState {
impl<V: 'static> Element<V> for UniformList<V> {
type ElementState = UniformListState;
fn id(&self) -> Option<crate::ElementId> {
fn element_id(&self) -> Option<crate::ElementId> {
Some(self.id.clone())
}
@@ -113,13 +115,18 @@ impl<V: 'static> Element<V> for UniformList<V> {
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
element_state.unwrap_or_else(|| {
if let Some(mut element_state) = element_state {
element_state.interactive = self
.interactivity
.initialize(Some(element_state.interactive), cx);
element_state
} else {
let item_size = self.measure_item(view_state, None, cx);
UniformListState {
interactive: InteractiveElementState::default(),
interactive: self.interactivity.initialize(None, cx),
item_size,
}
})
}
}
fn layout(
@@ -132,35 +139,44 @@ impl<V: 'static> Element<V> for UniformList<V> {
let item_size = element_state.item_size;
let rem_size = cx.rem_size();
cx.request_measured_layout(
self.computed_style(),
rem_size,
move |known_dimensions: Size<Option<Pixels>>, available_space: Size<AvailableSpace>| {
let desired_height = item_size.height * max_items;
let width = known_dimensions
.width
.unwrap_or(match available_space.width {
AvailableSpace::Definite(x) => x,
AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
});
let height = match available_space.height {
AvailableSpace::Definite(x) => desired_height.min(x),
AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
};
size(width, height)
},
)
self.interactivity
.layout(&mut element_state.interactive, cx, |style, cx| {
cx.request_measured_layout(
style,
rem_size,
move |known_dimensions: Size<Option<Pixels>>,
available_space: Size<AvailableSpace>| {
let desired_height = item_size.height * max_items;
let width = known_dimensions
.width
.unwrap_or(match available_space.width {
AvailableSpace::Definite(x) => x,
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
item_size.width
}
});
let height = match available_space.height {
AvailableSpace::Definite(x) => desired_height.min(x),
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
desired_height
}
};
size(width, height)
},
)
})
}
fn paint(
&mut self,
bounds: crate::Bounds<crate::Pixels>,
bounds: Bounds<crate::Pixels>,
view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
let style = self.computed_style();
let style =
self.interactivity
.compute_style(Some(bounds), &mut element_state.interactive, cx);
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
@@ -170,74 +186,79 @@ impl<V: 'static> Element<V> for UniformList<V> {
- point(border.right + padding.right, border.bottom + padding.bottom),
);
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
style.paint(bounds, cx);
let item_size = element_state.item_size;
let content_size = Size {
width: padded_bounds.size.width,
height: item_size.height * self.item_count,
};
let content_size;
if self.item_count > 0 {
let item_height = self
.measure_item(view_state, Some(padded_bounds.size.width), cx)
.height;
if let Some(scroll_handle) = self.scroll_handle.clone() {
scroll_handle.0.lock().replace(ScrollHandleState {
item_height,
list_height: padded_bounds.size.height,
scroll_offset: element_state.interactive.track_scroll_offset(),
});
}
let visible_item_count = if item_height > px(0.) {
(padded_bounds.size.height / item_height).ceil() as usize + 1
} else {
0
};
let scroll_offset = element_state
.interactive
.scroll_offset()
.map_or((0.0).into(), |offset| offset.y);
let first_visible_element_ix = (-scroll_offset / item_height).floor() as usize;
let visible_range = first_visible_element_ix
..cmp::min(
first_visible_element_ix + visible_item_count,
self.item_count,
);
let mut interactivity = mem::take(&mut self.interactivity);
let shared_scroll_offset = element_state
.interactive
.scroll_offset
.get_or_insert_with(Arc::default)
.clone();
let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
interactivity.paint(
bounds,
content_size,
&mut element_state.interactive,
cx,
|style, scroll_offset, cx| {
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
content_size = Size {
width: padded_bounds.size.width,
height: item_height * self.item_count,
};
cx.with_z_index(1, |cx| {
for (item, ix) in items.iter_mut().zip(visible_range) {
let item_origin =
padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, view_state, cx);
}
});
} else {
content_size = Size {
width: bounds.size.width,
height: px(0.),
};
}
let overflow = point(style.overflow.x, Overflow::Scroll);
cx.with_z_index(0, |cx| {
self.interactivity.paint(
bounds,
content_size,
overflow,
&mut element_state.interactive,
cx,
let padded_bounds = Bounds::from_corners(
bounds.origin + point(border.left + padding.left, border.top + padding.top),
bounds.lower_right()
- point(border.right + padding.right, border.bottom + padding.bottom),
);
});
})
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
style.paint(bounds, cx);
if self.item_count > 0 {
let item_height = self
.measure_item(view_state, Some(padded_bounds.size.width), cx)
.height;
if let Some(scroll_handle) = self.scroll_handle.clone() {
scroll_handle.0.lock().replace(ScrollHandleState {
item_height,
list_height: padded_bounds.size.height,
scroll_offset: shared_scroll_offset,
});
}
let visible_item_count = if item_height > px(0.) {
(padded_bounds.size.height / item_height).ceil() as usize + 1
} else {
0
};
let first_visible_element_ix =
(-scroll_offset.y / item_height).floor() as usize;
let visible_range = first_visible_element_ix
..cmp::min(
first_visible_element_ix + visible_item_count,
self.item_count,
);
let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
cx.with_z_index(1, |cx| {
for (item, ix) in items.iter_mut().zip(visible_range) {
let item_origin = padded_bounds.origin
+ point(px(0.), item_height * ix + scroll_offset.y);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, view_state, cx);
}
});
}
})
},
);
self.interactivity = interactivity;
}
}
@@ -275,14 +296,8 @@ impl<V> UniformList<V> {
}
}
impl<V: 'static> StatelessInteractive<V> for UniformList<V> {
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.interactivity.as_stateless_mut()
}
}
impl<V: 'static> StatefulInteractive<V> for UniformList<V> {
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
impl<V> InteractiveComponent<V> for UniformList<V> {
fn interactivity(&mut self) -> &mut crate::Interactivity<V> {
&mut self.interactivity
}
}
+10 -6
View File
@@ -156,7 +156,7 @@ pub enum GlobalKey {
}
pub trait BorrowAppContext {
fn with_text_style<F, R>(&mut self, style: TextStyleRefinement, f: F) -> R
fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
where
F: FnOnce(&mut Self) -> R;
@@ -167,14 +167,18 @@ impl<C> BorrowAppContext for C
where
C: BorrowMut<AppContext>,
{
fn with_text_style<F, R>(&mut self, style: TextStyleRefinement, f: F) -> R
fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
self.borrow_mut().push_text_style(style);
let result = f(self);
self.borrow_mut().pop_text_style();
result
if let Some(style) = style {
self.borrow_mut().push_text_style(style);
let result = f(self);
self.borrow_mut().pop_text_style();
result
} else {
f(self)
}
}
fn set_global<G: 'static>(&mut self, global: G) {
File diff suppressed because it is too large Load Diff
+2 -263
View File
@@ -1,11 +1,9 @@
use crate::{
build_action_from_type, Action, Bounds, DispatchPhase, Element, FocusEvent, FocusHandle,
FocusId, KeyBinding, KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent,
Pixels, Style, StyleRefinement, ViewContext, WindowContext,
build_action_from_type, Action, DispatchPhase, FocusId, KeyBinding, KeyContext, KeyMatch,
Keymap, Keystroke, KeystrokeMatcher, WindowContext,
};
use collections::HashMap;
use parking_lot::Mutex;
use refineable::Refineable;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
@@ -14,10 +12,6 @@ use std::{
};
use util::ResultExt;
pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
pub type FocusListener<V> =
Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct DispatchNodeId(usize);
@@ -208,258 +202,3 @@ impl DispatchTree {
*self.node_stack.last().unwrap()
}
}
pub trait KeyDispatch<V: 'static>: 'static {
fn as_focusable(&self) -> Option<&FocusableKeyDispatch<V>>;
fn as_focusable_mut(&mut self) -> Option<&mut FocusableKeyDispatch<V>>;
fn key_context(&self) -> &KeyContext;
fn key_context_mut(&mut self) -> &mut KeyContext;
fn initialize<R>(
&mut self,
focus_handle: Option<FocusHandle>,
cx: &mut ViewContext<V>,
f: impl FnOnce(Option<FocusHandle>, &mut ViewContext<V>) -> R,
) -> R {
let focus_handle = if let Some(focusable) = self.as_focusable_mut() {
let focus_handle = focusable
.focus_handle
.get_or_insert_with(|| focus_handle.unwrap_or_else(|| cx.focus_handle()))
.clone();
for listener in focusable.focus_listeners.drain(..) {
let focus_handle = focus_handle.clone();
cx.on_focus_changed(move |view, event, cx| {
listener(view, &focus_handle, event, cx)
});
}
Some(focus_handle)
} else {
None
};
cx.with_key_dispatch(self.key_context().clone(), focus_handle, f)
}
fn refine_style(&self, style: &mut Style, cx: &WindowContext) {
if let Some(focusable) = self.as_focusable() {
let focus_handle = focusable
.focus_handle
.as_ref()
.expect("must call initialize before refine_style");
if focus_handle.contains_focused(cx) {
style.refine(&focusable.focus_in_style);
}
if focus_handle.within_focused(cx) {
style.refine(&focusable.in_focus_style);
}
if focus_handle.is_focused(cx) {
style.refine(&focusable.focus_style);
}
}
}
fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
if let Some(focusable) = self.as_focusable() {
let focus_handle = focusable
.focus_handle
.clone()
.expect("must call initialize before paint");
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
if !cx.default_prevented() {
cx.focus(&focus_handle);
cx.prevent_default();
}
}
})
}
}
}
pub struct FocusableKeyDispatch<V> {
pub non_focusable: NonFocusableKeyDispatch,
pub focus_handle: Option<FocusHandle>,
pub focus_listeners: FocusListeners<V>,
pub focus_style: StyleRefinement,
pub focus_in_style: StyleRefinement,
pub in_focus_style: StyleRefinement,
}
impl<V> FocusableKeyDispatch<V> {
pub fn new(non_focusable: NonFocusableKeyDispatch) -> Self {
Self {
non_focusable,
focus_handle: None,
focus_listeners: FocusListeners::default(),
focus_style: StyleRefinement::default(),
focus_in_style: StyleRefinement::default(),
in_focus_style: StyleRefinement::default(),
}
}
pub fn tracked(non_focusable: NonFocusableKeyDispatch, handle: &FocusHandle) -> Self {
Self {
non_focusable,
focus_handle: Some(handle.clone()),
focus_listeners: FocusListeners::default(),
focus_style: StyleRefinement::default(),
focus_in_style: StyleRefinement::default(),
in_focus_style: StyleRefinement::default(),
}
}
}
impl<V: 'static> KeyDispatch<V> for FocusableKeyDispatch<V> {
fn as_focusable(&self) -> Option<&FocusableKeyDispatch<V>> {
Some(self)
}
fn as_focusable_mut(&mut self) -> Option<&mut FocusableKeyDispatch<V>> {
Some(self)
}
fn key_context(&self) -> &KeyContext {
&self.non_focusable.key_context
}
fn key_context_mut(&mut self) -> &mut KeyContext {
&mut self.non_focusable.key_context
}
}
#[derive(Default)]
pub struct NonFocusableKeyDispatch {
pub(crate) key_context: KeyContext,
}
impl<V: 'static> KeyDispatch<V> for NonFocusableKeyDispatch {
fn as_focusable(&self) -> Option<&FocusableKeyDispatch<V>> {
None
}
fn as_focusable_mut(&mut self) -> Option<&mut FocusableKeyDispatch<V>> {
None
}
fn key_context(&self) -> &KeyContext {
&self.key_context
}
fn key_context_mut(&mut self) -> &mut KeyContext {
&mut self.key_context
}
}
pub trait Focusable<V: 'static>: Element<V> {
fn focus_listeners(&mut self) -> &mut FocusListeners<V>;
fn set_focus_style(&mut self, style: StyleRefinement);
fn set_focus_in_style(&mut self, style: StyleRefinement);
fn set_in_focus_style(&mut self, style: StyleRefinement);
fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_focus_style(f(StyleRefinement::default()));
self
}
fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_focus_in_style(f(StyleRefinement::default()));
self
}
fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_in_focus_style(f(StyleRefinement::default()));
self
}
fn on_focus(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focus_listeners()
.push(Box::new(move |view, focus_handle, event, cx| {
if event.focused.as_ref() == Some(focus_handle) {
listener(view, event, cx)
}
}));
self
}
fn on_blur(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focus_listeners()
.push(Box::new(move |view, focus_handle, event, cx| {
if event.blurred.as_ref() == Some(focus_handle) {
listener(view, event, cx)
}
}));
self
}
fn on_focus_in(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focus_listeners()
.push(Box::new(move |view, focus_handle, event, cx| {
let descendant_blurred = event
.blurred
.as_ref()
.map_or(false, |blurred| focus_handle.contains(blurred, cx));
let descendant_focused = event
.focused
.as_ref()
.map_or(false, |focused| focus_handle.contains(focused, cx));
if !descendant_blurred && descendant_focused {
listener(view, event, cx)
}
}));
self
}
fn on_focus_out(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focus_listeners()
.push(Box::new(move |view, focus_handle, event, cx| {
let descendant_blurred = event
.blurred
.as_ref()
.map_or(false, |blurred| focus_handle.contains(blurred, cx));
let descendant_focused = event
.focused
.as_ref()
.map_or(false, |focused| focus_handle.contains(focused, cx));
if descendant_blurred && !descendant_focused {
listener(view, event, cx)
}
}));
self
}
}
+1
View File
@@ -97,6 +97,7 @@ impl KeystrokeMatcher {
}
}
#[derive(Debug)]
pub enum KeyMatch {
None,
Pending,
+5 -1
View File
@@ -184,7 +184,11 @@ pub trait PlatformTextSystem: Send + Sync {
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>>;
fn rasterize_glyph(&self, params: &RenderGlyphParams) -> Result<(Size<DevicePixels>, Vec<u8>)>;
fn rasterize_glyph(
&self,
params: &RenderGlyphParams,
raster_bounds: Bounds<DevicePixels>,
) -> Result<(Size<DevicePixels>, Vec<u8>)>;
fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout;
fn wrap_line(
&self,
+11 -4
View File
@@ -116,7 +116,9 @@ impl PlatformTextSystem for MacTextSystem {
},
)?;
Ok(candidates[ix])
let font_id = candidates[ix];
lock.font_selections.insert(font.clone(), font_id);
Ok(font_id)
}
}
@@ -145,8 +147,9 @@ impl PlatformTextSystem for MacTextSystem {
fn rasterize_glyph(
&self,
glyph_id: &RenderGlyphParams,
raster_bounds: Bounds<DevicePixels>,
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
self.0.read().rasterize_glyph(glyph_id)
self.0.read().rasterize_glyph(glyph_id, raster_bounds)
}
fn layout_line(&self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout {
@@ -247,8 +250,11 @@ impl MacTextSystemState {
.into())
}
fn rasterize_glyph(&self, params: &RenderGlyphParams) -> Result<(Size<DevicePixels>, Vec<u8>)> {
let glyph_bounds = self.raster_bounds(params)?;
fn rasterize_glyph(
&self,
params: &RenderGlyphParams,
glyph_bounds: Bounds<DevicePixels>,
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 {
Err(anyhow!("glyph bounds are empty"))
} else {
@@ -260,6 +266,7 @@ impl MacTextSystemState {
if params.subpixel_variant.y > 0 {
bitmap_size.height += DevicePixels(1);
}
let bitmap_size = bitmap_size;
let mut bytes;
let cx;
+70 -16
View File
@@ -3,8 +3,15 @@ use crate::{
PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
};
use anyhow::{anyhow, Result};
use collections::VecDeque;
use futures::channel::oneshot;
use parking_lot::Mutex;
use std::{rc::Rc, sync::Arc};
use std::{
cell::RefCell,
path::PathBuf,
rc::{Rc, Weak},
sync::Arc,
};
pub struct TestPlatform {
background_executor: BackgroundExecutor,
@@ -13,18 +20,60 @@ pub struct TestPlatform {
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
active_display: Rc<dyn PlatformDisplay>,
active_cursor: Mutex<CursorStyle>,
pub(crate) prompts: RefCell<TestPrompts>,
weak: Weak<Self>,
}
#[derive(Default)]
pub(crate) struct TestPrompts {
multiple_choice: VecDeque<oneshot::Sender<usize>>,
new_path: VecDeque<(PathBuf, oneshot::Sender<Option<PathBuf>>)>,
}
impl TestPlatform {
pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Self {
TestPlatform {
pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Rc<Self> {
Rc::new_cyclic(|weak| TestPlatform {
background_executor: executor,
foreground_executor,
prompts: Default::default(),
active_cursor: Default::default(),
active_display: Rc::new(TestDisplay::new()),
active_window: Default::default(),
}
weak: weak.clone(),
})
}
pub(crate) fn simulate_new_path_selection(
&self,
select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
) {
let (path, tx) = self
.prompts
.borrow_mut()
.new_path
.pop_front()
.expect("no pending new path prompt");
tx.send(select_path(&path)).ok();
}
pub(crate) fn simulate_prompt_answer(&self, response_ix: usize) {
let tx = self
.prompts
.borrow_mut()
.multiple_choice
.pop_front()
.expect("no pending multiple choice prompt");
tx.send(response_ix).ok();
}
pub(crate) fn has_pending_prompt(&self) -> bool {
!self.prompts.borrow().multiple_choice.is_empty()
}
pub(crate) fn prompt(&self) -> oneshot::Receiver<usize> {
let (tx, rx) = oneshot::channel();
self.prompts.borrow_mut().multiple_choice.push_back(tx);
rx
}
}
@@ -46,9 +95,7 @@ impl Platform for TestPlatform {
unimplemented!()
}
fn quit(&self) {
unimplemented!()
}
fn quit(&self) {}
fn restart(&self) {
unimplemented!()
@@ -88,7 +135,11 @@ impl Platform for TestPlatform {
options: WindowOptions,
) -> Box<dyn crate::PlatformWindow> {
*self.active_window.lock() = Some(handle);
Box::new(TestWindow::new(options, self.active_display.clone()))
Box::new(TestWindow::new(
options,
self.weak.clone(),
self.active_display.clone(),
))
}
fn set_display_link_output_callback(
@@ -118,15 +169,20 @@ impl Platform for TestPlatform {
fn prompt_for_paths(
&self,
_options: crate::PathPromptOptions,
) -> futures::channel::oneshot::Receiver<Option<Vec<std::path::PathBuf>>> {
) -> oneshot::Receiver<Option<Vec<std::path::PathBuf>>> {
unimplemented!()
}
fn prompt_for_new_path(
&self,
_directory: &std::path::Path,
) -> futures::channel::oneshot::Receiver<Option<std::path::PathBuf>> {
unimplemented!()
directory: &std::path::Path,
) -> oneshot::Receiver<Option<std::path::PathBuf>> {
let (tx, rx) = oneshot::channel();
self.prompts
.borrow_mut()
.new_path
.push_back((directory.to_path_buf(), tx));
rx
}
fn reveal_path(&self, _path: &std::path::Path) {
@@ -141,9 +197,7 @@ impl Platform for TestPlatform {
unimplemented!()
}
fn on_quit(&self, _callback: Box<dyn FnMut()>) {
unimplemented!()
}
fn on_quit(&self, _callback: Box<dyn FnMut()>) {}
fn on_reopen(&self, _callback: Box<dyn FnMut()>) {
unimplemented!()
+17 -13
View File
@@ -1,15 +1,13 @@
use std::{
rc::Rc,
sync::{self, Arc},
};
use collections::HashMap;
use parking_lot::Mutex;
use crate::{
px, AtlasKey, AtlasTextureId, AtlasTile, Pixels, PlatformAtlas, PlatformDisplay,
PlatformInputHandler, PlatformWindow, Point, Scene, Size, TileId, WindowAppearance,
WindowBounds, WindowOptions,
PlatformInputHandler, PlatformWindow, Point, Scene, Size, TestPlatform, TileId,
WindowAppearance, WindowBounds, WindowOptions,
};
use collections::HashMap;
use parking_lot::Mutex;
use std::{
rc::{Rc, Weak},
sync::{self, Arc},
};
#[derive(Default)]
@@ -25,16 +23,22 @@ pub struct TestWindow {
current_scene: Mutex<Option<Scene>>,
display: Rc<dyn PlatformDisplay>,
input_handler: Option<Box<dyn PlatformInputHandler>>,
handlers: Mutex<Handlers>,
platform: Weak<TestPlatform>,
sprite_atlas: Arc<dyn PlatformAtlas>,
}
impl TestWindow {
pub fn new(options: WindowOptions, display: Rc<dyn PlatformDisplay>) -> Self {
pub fn new(
options: WindowOptions,
platform: Weak<TestPlatform>,
display: Rc<dyn PlatformDisplay>,
) -> Self {
Self {
bounds: options.bounds,
current_scene: Default::default(),
display,
platform,
input_handler: None,
sprite_atlas: Arc::new(TestAtlas::new()),
handlers: Default::default(),
@@ -89,7 +93,7 @@ impl PlatformWindow for TestWindow {
_msg: &str,
_answers: &[&str],
) -> futures::channel::oneshot::Receiver<usize> {
todo!()
self.platform.upgrade().expect("platform dropped").prompt()
}
fn activate(&self) {
+4 -1
View File
@@ -1 +1,4 @@
pub use crate::{Context, ParentElement, Refineable};
pub use crate::{
BorrowAppContext, BorrowWindow, Component, Context, FocusableComponent, InteractiveComponent,
ParentComponent, Refineable, Render, StatefulInteractiveComponent, Styled, VisualContext,
};
+39 -5
View File
@@ -1,8 +1,8 @@
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, WindowContext,
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement,
Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext,
};
use refineable::{Cascade, Refineable};
use smallvec::SmallVec;
@@ -220,7 +220,7 @@ pub struct HighlightStyle {
impl Eq for HighlightStyle {}
impl Style {
pub fn text_style(&self, _cx: &WindowContext) -> Option<&TextStyleRefinement> {
pub fn text_style(&self) -> Option<&TextStyleRefinement> {
if self.text.is_some() {
Some(&self.text)
} else {
@@ -228,13 +228,47 @@ impl Style {
}
}
pub fn overflow_mask(&self, bounds: Bounds<Pixels>) -> Option<ContentMask<Pixels>> {
match self.overflow {
Point {
x: Overflow::Visible,
y: Overflow::Visible,
} => None,
_ => {
let current_mask = bounds;
let min = current_mask.origin;
let max = current_mask.lower_right();
let bounds = match (
self.overflow.x == Overflow::Visible,
self.overflow.y == Overflow::Visible,
) {
// x and y both visible
(true, true) => return None,
// x visible, y hidden
(true, false) => Bounds::from_corners(
point(min.x, bounds.origin.y),
point(max.x, bounds.lower_right().y),
),
// x hidden, y visible
(false, true) => Bounds::from_corners(
point(bounds.origin.x, min.y),
point(bounds.lower_right().x, max.y),
),
// both hidden
(false, false) => bounds,
};
Some(ContentMask { bounds })
}
}
}
pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
where
C: BorrowAppContext,
F: FnOnce(&mut C) -> R,
{
if self.text.is_some() {
cx.with_text_style(self.text.clone(), f)
cx.with_text_style(Some(self.text.clone()), f)
} else {
f(cx)
}
@@ -274,7 +308,7 @@ impl Style {
bounds: mask_bounds,
};
cx.with_content_mask(mask, f)
cx.with_content_mask(Some(mask), f)
}
/// Paints the background of an element styled with this style.
+76 -215
View File
@@ -1,26 +1,24 @@
use crate::{
self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position,
SharedString, Style, StyleRefinement, Visibility,
SharedString, StyleRefinement, Visibility,
};
use crate::{BoxShadow, TextStyleRefinement};
use refineable::Refineable;
use smallvec::{smallvec, SmallVec};
use taffy::style::Overflow;
pub trait Styled {
pub trait Styled: Sized {
fn style(&mut self) -> &mut StyleRefinement;
fn computed_style(&mut self) -> Style {
Style::default().refined(self.style().clone())
}
gpui2_macros::style_helpers!();
fn z_index(mut self, z_index: u32) -> Self {
self.style().z_index = Some(z_index);
self
}
/// Sets the size of the element to the full width and height.
fn full(mut self) -> Self
where
Self: Sized,
{
fn full(mut self) -> Self {
self.style().size.width = Some(relative(1.).into());
self.style().size.height = Some(relative(1.).into());
self
@@ -28,118 +26,98 @@ pub trait Styled {
/// Sets the position of the element to `relative`.
/// [Docs](https://tailwindcss.com/docs/position)
fn relative(mut self) -> Self
where
Self: Sized,
{
fn relative(mut self) -> Self {
self.style().position = Some(Position::Relative);
self
}
/// Sets the position of the element to `absolute`.
/// [Docs](https://tailwindcss.com/docs/position)
fn absolute(mut self) -> Self
where
Self: Sized,
{
fn absolute(mut self) -> Self {
self.style().position = Some(Position::Absolute);
self
}
/// Sets the display type of the element to `block`.
/// [Docs](https://tailwindcss.com/docs/display)
fn block(mut self) -> Self
where
Self: Sized,
{
fn block(mut self) -> Self {
self.style().display = Some(Display::Block);
self
}
/// Sets the display type of the element to `flex`.
/// [Docs](https://tailwindcss.com/docs/display)
fn flex(mut self) -> Self
where
Self: Sized,
{
fn flex(mut self) -> Self {
self.style().display = Some(Display::Flex);
self
}
/// Sets the visibility of the element to `visible`.
/// [Docs](https://tailwindcss.com/docs/visibility)
fn visible(mut self) -> Self
where
Self: Sized,
{
fn visible(mut self) -> Self {
self.style().visibility = Some(Visibility::Visible);
self
}
/// Sets the visibility of the element to `hidden`.
/// [Docs](https://tailwindcss.com/docs/visibility)
fn invisible(mut self) -> Self
where
Self: Sized,
{
fn invisible(mut self) -> Self {
self.style().visibility = Some(Visibility::Hidden);
self
}
fn cursor(mut self, cursor: CursorStyle) -> Self
where
Self: Sized,
{
fn overflow_hidden(mut self) -> Self {
self.style().overflow.x = Some(Overflow::Hidden);
self.style().overflow.y = Some(Overflow::Hidden);
self
}
fn overflow_hidden_x(mut self) -> Self {
self.style().overflow.x = Some(Overflow::Hidden);
self
}
fn overflow_hidden_y(mut self) -> Self {
self.style().overflow.y = Some(Overflow::Hidden);
self
}
fn cursor(mut self, cursor: CursorStyle) -> Self {
self.style().mouse_cursor = Some(cursor);
self
}
/// Sets the cursor style when hovering an element to `default`.
/// [Docs](https://tailwindcss.com/docs/cursor)
fn cursor_default(mut self) -> Self
where
Self: Sized,
{
fn cursor_default(mut self) -> Self {
self.style().mouse_cursor = Some(CursorStyle::Arrow);
self
}
/// Sets the cursor style when hovering an element to `pointer`.
/// [Docs](https://tailwindcss.com/docs/cursor)
fn cursor_pointer(mut self) -> Self
where
Self: Sized,
{
fn cursor_pointer(mut self) -> Self {
self.style().mouse_cursor = Some(CursorStyle::PointingHand);
self
}
/// Sets the flex direction of the element to `column`.
/// [Docs](https://tailwindcss.com/docs/flex-direction#column)
fn flex_col(mut self) -> Self
where
Self: Sized,
{
fn flex_col(mut self) -> Self {
self.style().flex_direction = Some(FlexDirection::Column);
self
}
/// Sets the flex direction of the element to `row`.
/// [Docs](https://tailwindcss.com/docs/flex-direction#row)
fn flex_row(mut self) -> Self
where
Self: Sized,
{
fn flex_row(mut self) -> Self {
self.style().flex_direction = Some(FlexDirection::Row);
self
}
/// Sets the element to allow a flex item to grow and shrink as needed, ignoring its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#flex-1)
fn flex_1(mut self) -> Self
where
Self: Sized,
{
fn flex_1(mut self) -> Self {
self.style().flex_grow = Some(1.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(relative(0.).into());
@@ -148,10 +126,7 @@ pub trait Styled {
/// Sets the element to allow a flex item to grow and shrink, taking into account its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#auto)
fn flex_auto(mut self) -> Self
where
Self: Sized,
{
fn flex_auto(mut self) -> Self {
self.style().flex_grow = Some(1.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(Length::Auto);
@@ -160,10 +135,7 @@ pub trait Styled {
/// Sets the element to allow a flex item to shrink but not grow, taking into account its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#initial)
fn flex_initial(mut self) -> Self
where
Self: Sized,
{
fn flex_initial(mut self) -> Self {
self.style().flex_grow = Some(0.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(Length::Auto);
@@ -172,10 +144,7 @@ pub trait Styled {
/// Sets the element to prevent a flex item from growing or shrinking.
/// [Docs](https://tailwindcss.com/docs/flex#none)
fn flex_none(mut self) -> Self
where
Self: Sized,
{
fn flex_none(mut self) -> Self {
self.style().flex_grow = Some(0.);
self.style().flex_shrink = Some(0.);
self
@@ -183,40 +152,28 @@ pub trait Styled {
/// Sets the element to allow a flex item to grow to fill any available space.
/// [Docs](https://tailwindcss.com/docs/flex-grow)
fn grow(mut self) -> Self
where
Self: Sized,
{
fn grow(mut self) -> Self {
self.style().flex_grow = Some(1.);
self
}
/// Sets the element to align flex items to the start of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#start)
fn items_start(mut self) -> Self
where
Self: Sized,
{
fn items_start(mut self) -> Self {
self.style().align_items = Some(AlignItems::FlexStart);
self
}
/// Sets the element to align flex items to the end of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#end)
fn items_end(mut self) -> Self
where
Self: Sized,
{
fn items_end(mut self) -> Self {
self.style().align_items = Some(AlignItems::FlexEnd);
self
}
/// Sets the element to align flex items along the center of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#center)
fn items_center(mut self) -> Self
where
Self: Sized,
{
fn items_center(mut self) -> Self {
self.style().align_items = Some(AlignItems::Center);
self
}
@@ -224,40 +181,28 @@ pub trait Styled {
/// Sets the element to justify flex items along the container's main axis
/// such that there is an equal amount of space between each item.
/// [Docs](https://tailwindcss.com/docs/justify-content#space-between)
fn justify_between(mut self) -> Self
where
Self: Sized,
{
fn justify_between(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::SpaceBetween);
self
}
/// Sets the element to justify flex items along the center of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#center)
fn justify_center(mut self) -> Self
where
Self: Sized,
{
fn justify_center(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::Center);
self
}
/// Sets the element to justify flex items against the start of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#start)
fn justify_start(mut self) -> Self
where
Self: Sized,
{
fn justify_start(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::Start);
self
}
/// Sets the element to justify flex items against the end of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#end)
fn justify_end(mut self) -> Self
where
Self: Sized,
{
fn justify_end(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::End);
self
}
@@ -265,10 +210,7 @@ pub trait Styled {
/// Sets the element to justify items along the container's main axis such
/// that there is an equal amount of space on each side of each item.
/// [Docs](https://tailwindcss.com/docs/justify-content#space-around)
fn justify_around(mut self) -> Self
where
Self: Sized,
{
fn justify_around(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::SpaceAround);
self
}
@@ -295,30 +237,21 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self
where
Self: Sized,
{
fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self {
self.style().box_shadow = Some(shadows);
self
}
/// Clears the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_none(mut self) -> Self
where
Self: Sized,
{
fn shadow_none(mut self) -> Self {
self.style().box_shadow = Some(Default::default());
self
}
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_sm(mut self) -> Self
where
Self: Sized,
{
fn shadow_sm(mut self) -> Self {
self.style().box_shadow = Some(smallvec::smallvec![BoxShadow {
color: hsla(0., 0., 0., 0.05),
offset: point(px(0.), px(1.)),
@@ -330,10 +263,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_md(mut self) -> Self
where
Self: Sized,
{
fn shadow_md(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0.5, 0., 0., 0.1),
@@ -353,10 +283,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_lg(mut self) -> Self
where
Self: Sized,
{
fn shadow_lg(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0., 0., 0., 0.1),
@@ -376,10 +303,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_xl(mut self) -> Self
where
Self: Sized,
{
fn shadow_xl(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0., 0., 0., 0.1),
@@ -399,10 +323,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_2xl(mut self) -> Self
where
Self: Sized,
{
fn shadow_2xl(mut self) -> Self {
self.style().box_shadow = Some(smallvec![BoxShadow {
color: hsla(0., 0., 0., 0.25),
offset: point(px(0.), px(25.)),
@@ -417,198 +338,138 @@ pub trait Styled {
&mut style.text
}
fn text_color(mut self, color: impl Into<Hsla>) -> Self
where
Self: Sized,
{
fn text_color(mut self, color: impl Into<Hsla>) -> Self {
self.text_style().get_or_insert_with(Default::default).color = Some(color.into());
self
}
fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self
where
Self: Sized,
{
fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(size.into());
self
}
fn text_xs(mut self) -> Self
where
Self: Sized,
{
fn text_xs(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(0.75).into());
self
}
fn text_sm(mut self) -> Self
where
Self: Sized,
{
fn text_sm(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(0.875).into());
self
}
fn text_base(mut self) -> Self
where
Self: Sized,
{
fn text_base(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.0).into());
self
}
fn text_lg(mut self) -> Self
where
Self: Sized,
{
fn text_lg(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.125).into());
self
}
fn text_xl(mut self) -> Self
where
Self: Sized,
{
fn text_xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.25).into());
self
}
fn text_2xl(mut self) -> Self
where
Self: Sized,
{
fn text_2xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.5).into());
self
}
fn text_3xl(mut self) -> Self
where
Self: Sized,
{
fn text_3xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.875).into());
self
}
fn text_decoration_none(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_none(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.underline = None;
self
}
fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self
where
Self: Sized,
{
fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.color = Some(color.into());
self
}
fn text_decoration_solid(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_solid(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.wavy = false;
self
}
fn text_decoration_wavy(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_wavy(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.wavy = true;
self
}
fn text_decoration_0(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_0(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(0.);
self
}
fn text_decoration_1(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_1(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(1.);
self
}
fn text_decoration_2(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_2(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(2.);
self
}
fn text_decoration_4(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_4(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(4.);
self
}
fn text_decoration_8(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_8(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(8.);
self
}
fn font(mut self, family_name: impl Into<SharedString>) -> Self
where
Self: Sized,
{
fn font(mut self, family_name: impl Into<SharedString>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_family = Some(family_name.into());
self
}
fn line_height(mut self, line_height: impl Into<DefiniteLength>) -> Self
where
Self: Sized,
{
fn line_height(mut self, line_height: impl Into<DefiniteLength>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.line_height = Some(line_height.into());
+19 -7
View File
@@ -39,6 +39,7 @@ pub struct TextSystem {
platform_text_system: Arc<dyn PlatformTextSystem>,
font_ids_by_font: RwLock<HashMap<Font, FontId>>,
font_metrics: RwLock<HashMap<FontId, FontMetrics>>,
raster_bounds: RwLock<HashMap<RenderGlyphParams, Bounds<DevicePixels>>>,
wrapper_pool: Mutex<HashMap<FontIdWithSize, Vec<LineWrapper>>>,
font_runs_pool: Mutex<Vec<Vec<FontRun>>>,
}
@@ -48,10 +49,11 @@ impl TextSystem {
TextSystem {
line_layout_cache: Arc::new(LineLayoutCache::new(platform_text_system.clone())),
platform_text_system,
font_metrics: RwLock::new(HashMap::default()),
font_ids_by_font: RwLock::new(HashMap::default()),
wrapper_pool: Mutex::new(HashMap::default()),
font_runs_pool: Default::default(),
font_metrics: RwLock::default(),
raster_bounds: RwLock::default(),
font_ids_by_font: RwLock::default(),
wrapper_pool: Mutex::default(),
font_runs_pool: Mutex::default(),
}
}
@@ -252,14 +254,24 @@ impl TextSystem {
}
pub fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
self.platform_text_system.glyph_raster_bounds(params)
let raster_bounds = self.raster_bounds.upgradable_read();
if let Some(bounds) = raster_bounds.get(params) {
Ok(bounds.clone())
} else {
let mut raster_bounds = RwLockUpgradableReadGuard::upgrade(raster_bounds);
let bounds = self.platform_text_system.glyph_raster_bounds(params)?;
raster_bounds.insert(params.clone(), bounds);
Ok(bounds)
}
}
pub fn rasterize_glyph(
&self,
glyph_id: &RenderGlyphParams,
params: &RenderGlyphParams,
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
self.platform_text_system.rasterize_glyph(glyph_id)
let raster_bounds = self.raster_bounds(params)?;
self.platform_text_system
.rasterize_glyph(params, raster_bounds)
}
}
+8 -8
View File
@@ -206,7 +206,7 @@ impl<V: Render> From<View<V>> for AnyView {
impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
type ElementState = Box<dyn Any>;
fn id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<ElementId> {
Some(self.model.entity_id.into())
}
@@ -305,7 +305,7 @@ where
{
type ElementState = AnyElement<ViewState>;
fn id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<ElementId> {
Some(self.view.entity_id().into())
}
@@ -315,7 +315,7 @@ where
_: Option<Self::ElementState>,
cx: &mut ViewContext<ParentViewState>,
) -> Self::ElementState {
cx.with_element_id(self.view.entity_id(), |_, cx| {
cx.with_element_id(Some(self.view.entity_id()), |cx| {
self.view.update(cx, |view, cx| {
let mut element = self.component.take().unwrap().render();
element.initialize(view, cx);
@@ -330,7 +330,7 @@ where
element: &mut Self::ElementState,
cx: &mut ViewContext<ParentViewState>,
) -> LayoutId {
cx.with_element_id(self.view.entity_id(), |_, cx| {
cx.with_element_id(Some(self.view.entity_id()), |cx| {
self.view.update(cx, |view, cx| element.layout(view, cx))
})
}
@@ -342,7 +342,7 @@ where
element: &mut Self::ElementState,
cx: &mut ViewContext<ParentViewState>,
) {
cx.with_element_id(self.view.entity_id(), |_, cx| {
cx.with_element_id(Some(self.view.entity_id()), |cx| {
self.view.update(cx, |view, cx| element.paint(view, cx))
})
}
@@ -364,7 +364,7 @@ mod any_view {
use std::any::Any;
pub(crate) fn initialize<V: Render>(view: &AnyView, cx: &mut WindowContext) -> Box<dyn Any> {
cx.with_element_id(view.model.entity_id, |_, cx| {
cx.with_element_id(Some(view.model.entity_id), |cx| {
let view = view.clone().downcast::<V>().unwrap();
let element = view.update(cx, |view, cx| {
let mut element = AnyElement::new(view.render(cx));
@@ -380,7 +380,7 @@ mod any_view {
element: &mut Box<dyn Any>,
cx: &mut WindowContext,
) -> LayoutId {
cx.with_element_id(view.model.entity_id, |_, cx| {
cx.with_element_id(Some(view.model.entity_id), |cx| {
let view = view.clone().downcast::<V>().unwrap();
let element = element.downcast_mut::<AnyElement<V>>().unwrap();
view.update(cx, |view, cx| element.layout(view, cx))
@@ -392,7 +392,7 @@ mod any_view {
element: &mut Box<dyn Any>,
cx: &mut WindowContext,
) {
cx.with_element_id(view.model.entity_id, |_, cx| {
cx.with_element_id(Some(view.model.entity_id), |cx| {
let view = view.clone().downcast::<V>().unwrap();
let element = element.downcast_mut::<AnyElement<V>>().unwrap();
view.update(cx, |view, cx| element.paint(view, cx))
+47 -25
View File
@@ -1074,7 +1074,7 @@ impl<'a> WindowContext<'a> {
if let Some(active_drag) = self.app.active_drag.take() {
self.with_z_index(1, |cx| {
let offset = cx.mouse_position() - active_drag.cursor_offset;
cx.with_element_offset(Some(offset), |cx| {
cx.with_element_offset(offset, |cx| {
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
active_drag.view.draw(available_space, cx);
@@ -1083,7 +1083,7 @@ impl<'a> WindowContext<'a> {
});
} else if let Some(active_tooltip) = self.app.active_tooltip.take() {
self.with_z_index(1, |cx| {
cx.with_element_offset(Some(active_tooltip.cursor_offset), |cx| {
cx.with_element_offset(active_tooltip.cursor_offset, |cx| {
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
active_tooltip.view.draw(available_space, cx);
@@ -1365,6 +1365,14 @@ impl<'a> WindowContext<'a> {
self.window.platform_window.activate();
}
pub fn minimize_window(&self) {
self.window.platform_window.minimize();
}
pub fn toggle_full_screen(&self) {
self.window.platform_window.toggle_full_screen();
}
pub fn prompt(
&self,
level: PromptLevel,
@@ -1573,43 +1581,50 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
/// used to associate state with identified elements across separate frames.
fn with_element_id<R>(
&mut self,
id: impl Into<ElementId>,
f: impl FnOnce(GlobalElementId, &mut Self) -> R,
id: Option<impl Into<ElementId>>,
f: impl FnOnce(&mut Self) -> R,
) -> R {
let window = self.window_mut();
window.element_id_stack.push(id.into());
let global_id = window.element_id_stack.clone();
let result = f(global_id, self);
let window: &mut Window = self.borrow_mut();
window.element_id_stack.pop();
result
if let Some(id) = id.map(Into::into) {
let window = self.window_mut();
window.element_id_stack.push(id.into());
let result = f(self);
let window: &mut Window = self.borrow_mut();
window.element_id_stack.pop();
result
} else {
f(self)
}
}
/// Invoke the given function with the given content mask after intersecting it
/// with the current mask.
fn with_content_mask<R>(
&mut self,
mask: ContentMask<Pixels>,
mask: Option<ContentMask<Pixels>>,
f: impl FnOnce(&mut Self) -> R,
) -> R {
let mask = mask.intersect(&self.content_mask());
self.window_mut()
.current_frame
.content_mask_stack
.push(mask);
let result = f(self);
self.window_mut().current_frame.content_mask_stack.pop();
result
if let Some(mask) = mask {
let mask = mask.intersect(&self.content_mask());
self.window_mut()
.current_frame
.content_mask_stack
.push(mask);
let result = f(self);
self.window_mut().current_frame.content_mask_stack.pop();
result
} else {
f(self)
}
}
/// Update the global element offset based on the given offset. This is used to implement
/// scrolling and position drag handles.
fn with_element_offset<R>(
&mut self,
offset: Option<Point<Pixels>>,
offset: Point<Pixels>,
f: impl FnOnce(&mut Self) -> R,
) -> R {
let Some(offset) = offset else {
if offset.is_zero() {
return f(self);
};
@@ -1645,7 +1660,9 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
where
S: 'static,
{
self.with_element_id(id, |global_id, cx| {
self.with_element_id(Some(id), |cx| {
let global_id = cx.window().element_id_stack.clone();
if let Some(any) = cx
.window_mut()
.current_frame
@@ -2073,11 +2090,10 @@ impl<'a, V: 'static> ViewContext<'a, V> {
f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
) -> R {
let window = &mut self.window;
window
.current_frame
.dispatch_tree
.push_node(context, &mut window.previous_frame.dispatch_tree);
.push_node(context.clone(), &mut window.previous_frame.dispatch_tree);
if let Some(focus_handle) = focus_handle.as_ref() {
window
.current_frame
@@ -2360,6 +2376,12 @@ impl<V: 'static + Render> WindowHandle<V> {
{
cx.read_window(self, |root_view, _cx| root_view.clone())
}
pub fn is_active(&self, cx: &WindowContext) -> Option<bool> {
cx.windows
.get(self.id)
.and_then(|window| window.as_ref().map(|window| window.active))
}
}
impl<V> Copy for WindowHandle<V> {}
+2 -2
View File
@@ -130,7 +130,7 @@ fn generate_predefined_setter(
let method = quote! {
#[doc = #doc_string]
fn #method_name(mut self) -> Self where Self: std::marker::Sized {
fn #method_name(mut self) -> Self {
let style = self.style();
#(#field_assignments)*
self
@@ -163,7 +163,7 @@ fn generate_custom_value_setter(
let method = quote! {
#[doc = #doc_string]
fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self where Self: std::marker::Sized {
fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self {
let style = self.style();
#(#field_assignments)*
self
+1
View File
@@ -14,5 +14,6 @@ test-support = []
smol.workspace = true
anyhow.workspace = true
log.workspace = true
serde.workspace = true
gpui = { package = "gpui2", path = "../gpui2" }
util = { path = "../util" }
+2 -3
View File
@@ -1,10 +1,9 @@
use anyhow::{anyhow, Result};
use gpui::AsyncAppContext;
use gpui::{actions, AsyncAppContext};
use std::path::Path;
use util::ResultExt;
// todo!()
// actions!(cli, [Install]);
actions!(Install);
pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
let cli_path = cx.update(|cx| cx.path_for_auxiliary_executable("cli"))??;
+1 -1
View File
@@ -167,7 +167,7 @@ fn main() {
panic!("unexpected message");
}
cx.update(|cx| cx.quit()).ok();
cx.update(|cx| cx.shutdown()).ok();
})
.detach();
});
+26 -3
View File
@@ -2,7 +2,7 @@ use anyhow::{anyhow, bail, Context, Result};
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use serde::Deserialize;
use smol::{fs, io::BufReader, process::Command};
use smol::{fs, io::BufReader, lock::Mutex, process::Command};
use std::process::{Output, Stdio};
use std::{
env::consts,
@@ -45,14 +45,19 @@ pub trait NodeRuntime: Send + Sync {
pub struct RealNodeRuntime {
http: Arc<dyn HttpClient>,
installation_lock: Mutex<()>,
}
impl RealNodeRuntime {
pub fn new(http: Arc<dyn HttpClient>) -> Arc<dyn NodeRuntime> {
Arc::new(RealNodeRuntime { http })
Arc::new(RealNodeRuntime {
http,
installation_lock: Mutex::new(()),
})
}
async fn install_if_needed(&self) -> Result<PathBuf> {
let _lock = self.installation_lock.lock().await;
log::info!("Node runtime install_if_needed");
let arch = match consts::ARCH {
@@ -73,6 +78,9 @@ impl RealNodeRuntime {
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(["--cache".into(), node_dir.join("cache")])
.args(["--userconfig".into(), node_dir.join("blank_user_npmrc")])
.args(["--globalconfig".into(), node_dir.join("blank_global_npmrc")])
.status()
.await;
let valid = matches!(result, Ok(status) if status.success());
@@ -96,6 +104,11 @@ impl RealNodeRuntime {
archive.unpack(&node_containing_dir).await?;
}
// Note: Not in the `if !valid {}` so we can populate these for existing installations
_ = fs::create_dir(node_dir.join("cache")).await;
_ = fs::write(node_dir.join("blank_user_npmrc"), []).await;
_ = fs::write(node_dir.join("blank_global_npmrc"), []).await;
anyhow::Ok(node_dir)
}
}
@@ -137,7 +150,17 @@ impl NodeRuntime for RealNodeRuntime {
let mut command = Command::new(node_binary);
command.env("PATH", env_path);
command.arg(npm_file).arg(subcommand).args(args);
command.arg(npm_file).arg(subcommand);
command.args(["--cache".into(), installation_path.join("cache")]);
command.args([
"--userconfig".into(),
installation_path.join("blank_user_npmrc"),
]);
command.args([
"--globalconfig".into(),
installation_path.join("blank_global_npmrc"),
]);
command.args(args);
if let Some(directory) = directory {
command.current_dir(directory);
+20 -6
View File
@@ -1,10 +1,10 @@
use editor::Editor;
use gpui::{
div, uniform_list, Component, Div, MouseButton, ParentElement, Render, StatelessInteractive,
Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WindowContext,
div, prelude::*, uniform_list, Component, Div, MouseButton, Render, Task,
UniformListScrollHandle, View, ViewContext, WindowContext,
};
use std::{cmp, sync::Arc};
use ui::{prelude::*, v_stack, Divider, Label, LabelColor};
use ui::{prelude::*, v_stack, Divider, Label, TextColor};
pub struct Picker<D: PickerDelegate> {
pub delegate: D,
@@ -58,7 +58,7 @@ impl<D: PickerDelegate> Picker<D> {
self.editor.update(cx, |editor, cx| editor.focus(cx));
}
fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
pub fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
let count = self.delegate.match_count();
if count > 0 {
let index = self.delegate.selected_index();
@@ -98,6 +98,15 @@ impl<D: PickerDelegate> Picker<D> {
}
}
pub fn cycle_selection(&mut self, cx: &mut ViewContext<Self>) {
let count = self.delegate.match_count();
let index = self.delegate.selected_index();
let new_index = if index + 1 == count { 0 } else { index + 1 };
self.delegate.set_selected_index(new_index, cx);
self.scroll_handle.scroll_to_item(new_index);
cx.notify();
}
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
self.delegate.dismissed(cx);
}
@@ -137,6 +146,11 @@ impl<D: PickerDelegate> Picker<D> {
}
}
pub fn refresh(&mut self, cx: &mut ViewContext<Self>) {
let query = self.editor.read(cx).text(cx);
self.update_matches(query, cx);
}
pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
let update = self.delegate.update_matches(query, cx);
self.matches_updated(cx);
@@ -165,7 +179,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div()
.context("picker")
.key_context("picker")
.size_full()
.elevation_2(cx)
.on_action(Self::select_next)
@@ -224,7 +238,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
v_stack().p_1().grow().child(
div()
.px_1()
.child(Label::new("No matches").color(LabelColor::Muted)),
.child(Label::new("No matches").color(TextColor::Muted)),
),
)
})
+41
View File
@@ -0,0 +1,41 @@
[package]
name = "project_panel2"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
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" }
gpui = { path = "../gpui2", package = "gpui2" }
menu = { path = "../menu2", package = "menu2" }
project = { path = "../project2", package = "project2" }
settings = { path = "../settings2", package = "settings2" }
theme = { path = "../theme2", package = "theme2" }
ui = { path = "../ui2", package = "ui2" }
util = { path = "../util" }
workspace = { path = "../workspace2", package = "workspace2" }
anyhow.workspace = true
postage.workspace = true
futures.workspace = true
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
schemars.workspace = true
smallvec.workspace = true
pretty_assertions.workspace = true
unicase = "2.6"
[dev-dependencies]
client = { path = "../client2", package = "client2", features = ["test-support"] }
language = { path = "../language2", package = "language2", features = ["test-support"] }
editor = { path = "../editor2", package = "editor2", features = ["test-support"] }
gpui = { path = "../gpui2", package = "gpui2", features = ["test-support"] }
workspace = { path = "../workspace2", package = "workspace2", features = ["test-support"] }
serde_json.workspace = true
@@ -0,0 +1,96 @@
use std::{path::Path, str, sync::Arc};
use collections::HashMap;
use gpui::{AppContext, AssetSource};
use serde_derive::Deserialize;
use util::{maybe, paths::PathExt};
#[derive(Deserialize, Debug)]
struct TypeConfig {
icon: Arc<str>,
}
#[derive(Deserialize, Debug)]
pub struct FileAssociations {
suffixes: HashMap<String, String>,
types: HashMap<String, TypeConfig>,
}
const COLLAPSED_DIRECTORY_TYPE: &'static str = "collapsed_folder";
const EXPANDED_DIRECTORY_TYPE: &'static str = "expanded_folder";
const COLLAPSED_CHEVRON_TYPE: &'static str = "collapsed_chevron";
const EXPANDED_CHEVRON_TYPE: &'static str = "expanded_chevron";
pub const FILE_TYPES_ASSET: &'static str = "icons/file_icons/file_types.json";
pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
cx.set_global(FileAssociations::new(assets))
}
impl FileAssociations {
pub fn new(assets: impl AssetSource) -> Self {
assets
.load("icons/file_icons/file_types.json")
.and_then(|file| {
serde_json::from_str::<FileAssociations>(str::from_utf8(&file).unwrap())
.map_err(Into::into)
})
.unwrap_or_else(|_| FileAssociations {
suffixes: HashMap::default(),
types: HashMap::default(),
})
}
pub fn get_icon(path: &Path, cx: &AppContext) -> Arc<str> {
maybe!({
let this = cx.has_global::<Self>().then(|| cx.global::<Self>())?;
// FIXME: Associate a type with the languages and have the file's langauge
// override these associations
maybe!({
let suffix = path.icon_suffix()?;
this.suffixes
.get(suffix)
.and_then(|type_str| this.types.get(type_str))
.map(|type_config| type_config.icon.clone())
})
.or_else(|| this.types.get("default").map(|config| config.icon.clone()))
})
.unwrap_or_else(|| Arc::from("".to_string()))
}
pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Arc<str> {
maybe!({
let this = cx.has_global::<Self>().then(|| cx.global::<Self>())?;
let key = if expanded {
EXPANDED_DIRECTORY_TYPE
} else {
COLLAPSED_DIRECTORY_TYPE
};
this.types
.get(key)
.map(|type_config| type_config.icon.clone())
})
.unwrap_or_else(|| Arc::from("".to_string()))
}
pub fn get_chevron_icon(expanded: bool, cx: &AppContext) -> Arc<str> {
maybe!({
let this = cx.has_global::<Self>().then(|| cx.global::<Self>())?;
let key = if expanded {
EXPANDED_CHEVRON_TYPE
} else {
COLLAPSED_CHEVRON_TYPE
};
this.types
.get(key)
.map(|type_config| type_config.icon.clone())
})
.unwrap_or_else(|| Arc::from("".to_string()))
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
use anyhow;
use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
use settings::Settings;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ProjectPanelDockPosition {
Left,
Right,
}
#[derive(Deserialize, Debug)]
pub struct ProjectPanelSettings {
pub default_width: f32,
pub dock: ProjectPanelDockPosition,
pub file_icons: bool,
pub folder_icons: bool,
pub git_status: bool,
pub indent_size: f32,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct ProjectPanelSettingsContent {
pub default_width: Option<f32>,
pub dock: Option<ProjectPanelDockPosition>,
pub file_icons: Option<bool>,
pub folder_icons: Option<bool>,
pub git_status: Option<bool>,
pub indent_size: Option<f32>,
}
impl Settings for ProjectPanelSettings {
const KEY: Option<&'static str> = Some("project_panel");
type FileContent = ProjectPanelSettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &mut gpui::AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}
}
+4 -2
View File
@@ -9,7 +9,7 @@ use schemars::{
};
use serde::Deserialize;
use serde_json::Value;
use util::{asset_str, ResultExt};
use util::asset_str;
#[derive(Debug, Deserialize, Default, Clone, JsonSchema)]
#[serde(transparent)]
@@ -86,7 +86,9 @@ impl KeymapFile {
"invalid binding value for keystroke {keystroke}, context {context:?}"
)
})
.log_err()
// todo!()
.ok()
// .log_err()
.map(|action| KeyBinding::load(&keystroke, action, context.as_deref()))
})
.collect::<Result<Vec<_>>>()?;
+1 -1
View File
@@ -1,5 +1,5 @@
use crate::story::Story;
use gpui::{px, Div, Render};
use gpui::{prelude::*, px, Div, Render};
use theme2::{default_color_scales, ColorScaleStep};
use ui::prelude::*;
+5 -6
View File
@@ -1,6 +1,5 @@
use gpui::{
actions, div, Div, FocusHandle, Focusable, FocusableKeyDispatch, KeyBinding, ParentElement,
Render, StatefulInteractivity, StatelessInteractive, Styled, View, VisualContext,
actions, div, prelude::*, Div, FocusHandle, Focusable, KeyBinding, Render, Stateful, View,
WindowContext,
};
use theme2::ActiveTheme;
@@ -28,7 +27,7 @@ impl FocusStory {
}
impl Render for FocusStory {
type Element = Div<Self, StatefulInteractivity<Self>, FocusableKeyDispatch<Self>>;
type Element = Focusable<Self, Stateful<Self, Div<Self>>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
@@ -42,7 +41,7 @@ impl Render for FocusStory {
div()
.id("parent")
.focusable()
.context("parent")
.key_context("parent")
.on_action(|_, action: &ActionA, cx| {
println!("Action A dispatched on parent");
})
@@ -62,7 +61,7 @@ impl Render for FocusStory {
.child(
div()
.track_focus(&self.child_1_focus)
.context("child-1")
.key_context("child-1")
.on_action(|_, action: &ActionB, cx| {
println!("Action B dispatched on child 1 during");
})
@@ -82,7 +81,7 @@ impl Render for FocusStory {
.child(
div()
.track_focus(&self.child_2_focus)
.context("child-2")
.key_context("child-2")
.on_action(|_, action: &ActionC, cx| {
println!("Action C dispatched on child 2");
})
@@ -1,5 +1,5 @@
use crate::{story::Story, story_selector::ComponentStory};
use gpui::{Div, Render, StatefulInteractivity, View, VisualContext};
use gpui::{prelude::*, Div, Render, Stateful, View};
use strum::IntoEnumIterator;
use ui::prelude::*;
@@ -12,7 +12,7 @@ impl KitchenSinkStory {
}
impl Render for KitchenSinkStory {
type Element = Div<Self, StatefulInteractivity<Self>>;
type Element = Stateful<Self, Div<Self>>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let component_stories = ComponentStory::iter()
+2 -6
View File
@@ -1,11 +1,7 @@
use std::sync::Arc;
use fuzzy::StringMatchCandidate;
use gpui::{
div, Component, Div, KeyBinding, ParentElement, Render, StatelessInteractive, Styled, Task,
View, VisualContext, WindowContext,
};
use gpui::{div, prelude::*, Div, KeyBinding, Render, Styled, Task, View, WindowContext};
use picker::{Picker, PickerDelegate};
use std::sync::Arc;
use theme2::ActiveTheme;
pub struct PickerStory {
+2 -5
View File
@@ -1,7 +1,4 @@
use gpui::{
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteractivity, Styled,
View, VisualContext, WindowContext,
};
use gpui::{div, prelude::*, px, Div, Render, SharedString, Stateful, Styled, View, WindowContext};
use theme2::ActiveTheme;
pub struct ScrollStory;
@@ -13,7 +10,7 @@ impl ScrollStory {
}
impl Render for ScrollStory {
type Element = Div<Self, StatefulInteractivity<Self>>;
type Element = Stateful<Self, Div<Self>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
+1 -1
View File
@@ -1,4 +1,4 @@
use gpui::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext};
use gpui::{div, white, Div, ParentComponent, Render, Styled, View, VisualContext, WindowContext};
pub struct TextStory;
+1
View File
@@ -35,6 +35,7 @@ pub(crate) fn one_dark() -> Theme {
id: "one_dark".to_string(),
name: "One Dark".into(),
appearance: Appearance::Dark,
styles: ThemeStyles {
system: SystemColors::default(),
colors: ThemeColors {
+7
View File
@@ -19,6 +19,7 @@ const MIN_LINE_HEIGHT: f32 = 1.0;
#[derive(Clone)]
pub struct ThemeSettings {
pub ui_font_size: Pixels,
pub ui_font: Font,
pub buffer_font: Font,
pub buffer_font_size: Pixels,
pub buffer_line_height: BufferLineHeight,
@@ -120,6 +121,12 @@ impl settings::Settings for ThemeSettings {
let mut this = Self {
ui_font_size: defaults.ui_font_size.unwrap_or(16.).into(),
ui_font: Font {
family: "Helvetica".into(),
features: Default::default(),
weight: Default::default(),
style: Default::default(),
},
buffer_font: Font {
family: defaults.buffer_font_family.clone().unwrap().into(),
features: defaults.buffer_font_features.clone().unwrap(),
+1 -1
View File
@@ -1,4 +1,4 @@
use gpui::{div, Component, Div, ParentElement, Styled, ViewContext};
use gpui::{div, Component, Div, ParentComponent, Styled, ViewContext};
use crate::ActiveTheme;
+1 -1
View File
@@ -143,7 +143,7 @@ use crate::{amber, blue, jade, lime, orange, pink, purple, red};
mod stories {
use super::*;
use crate::{ActiveTheme, Story};
use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
use gpui::{div, img, px, Div, ParentComponent, Render, Styled, ViewContext};
pub struct PlayerStory;
+22 -24
View File
@@ -1,11 +1,9 @@
use std::sync::Arc;
use gpui::{div, DefiniteLength, Hsla, MouseButton, WindowContext};
use gpui::{div, DefiniteLength, Hsla, MouseButton, StatefulInteractiveComponent, WindowContext};
use crate::{
h_stack, prelude::*, Icon, IconButton, IconColor, IconElement, Label, LabelColor,
LineHeightStyle,
};
use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, IconElement, Label, LineHeightStyle, TextColor};
/// Provides the flexibility to use either a standard
/// button or an icon button in a given context.
@@ -87,7 +85,7 @@ pub struct Button<V: 'static> {
label: SharedString,
variant: ButtonVariant,
width: Option<DefiniteLength>,
color: Option<LabelColor>,
color: Option<TextColor>,
}
impl<V: 'static> Button<V> {
@@ -141,14 +139,14 @@ impl<V: 'static> Button<V> {
self
}
pub fn color(mut self, color: Option<LabelColor>) -> Self {
pub fn color(mut self, color: Option<TextColor>) -> Self {
self.color = color;
self
}
pub fn label_color(&self, color: Option<LabelColor>) -> LabelColor {
pub fn label_color(&self, color: Option<TextColor>) -> TextColor {
if self.disabled {
LabelColor::Disabled
TextColor::Disabled
} else if let Some(color) = color {
color
} else {
@@ -156,21 +154,21 @@ impl<V: 'static> Button<V> {
}
}
fn render_label(&self, color: LabelColor) -> Label {
fn render_label(&self, color: TextColor) -> Label {
Label::new(self.label.clone())
.color(color)
.line_height_style(LineHeightStyle::UILabel)
}
fn render_icon(&self, icon_color: IconColor) -> Option<IconElement> {
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 Component<V> {
let (icon_color, label_color) = match (self.disabled, self.color) {
(true, _) => (IconColor::Disabled, LabelColor::Disabled),
(_, None) => (IconColor::Default, LabelColor::Default),
(_, Some(color)) => (IconColor::from(color), color),
(true, _) => (TextColor::Disabled, TextColor::Disabled),
(_, None) => (TextColor::Default, TextColor::Default),
(_, Some(color)) => (TextColor::from(color), color),
};
let mut button = h_stack()
@@ -240,7 +238,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::{h_stack, v_stack, LabelColor, Story};
use crate::{h_stack, v_stack, Story, TextColor};
use gpui::{rems, Div, Render};
use strum::IntoEnumIterator;
@@ -265,7 +263,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label").variant(ButtonVariant::Ghost), // .state(state),
@@ -276,7 +274,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label")
@@ -290,7 +288,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label")
@@ -307,7 +305,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label").variant(ButtonVariant::Filled), // .state(state),
@@ -318,7 +316,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label")
@@ -332,7 +330,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label")
@@ -349,7 +347,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label")
@@ -363,7 +361,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label")
@@ -379,7 +377,7 @@ mod stories {
v_stack()
.gap_1()
.child(
Label::new(state.to_string()).color(LabelColor::Muted),
Label::new(state.to_string()).color(TextColor::Muted),
)
.child(
Button::new("Label")
+6 -10
View File
@@ -1,12 +1,8 @@
use gpui::{div, prelude::*, Component, ElementId, Styled, ViewContext};
use std::sync::Arc;
use gpui::{
div, Component, ElementId, ParentElement, StatefulInteractive, StatelessInteractive, Styled,
ViewContext,
};
use theme2::ActiveTheme;
use crate::{Icon, IconColor, IconElement, Selection};
use crate::{Icon, IconElement, Selection, TextColor};
pub type CheckHandler<V> = Arc<dyn Fn(Selection, &mut V, &mut ViewContext<V>) + Send + Sync>;
@@ -58,9 +54,9 @@ impl<V: 'static> Checkbox<V> {
.color(
// If the checkbox is disabled we change the color of the icon.
if self.disabled {
IconColor::Disabled
TextColor::Disabled
} else {
IconColor::Selected
TextColor::Selected
},
),
)
@@ -73,9 +69,9 @@ impl<V: 'static> Checkbox<V> {
.color(
// If the checkbox is disabled we change the color of the icon.
if self.disabled {
IconColor::Disabled
TextColor::Disabled
} else {
IconColor::Selected
TextColor::Selected
},
),
)
@@ -23,6 +23,6 @@ pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<
.shadow(level.shadow())
}
pub fn modal<V>(cx: &mut ViewContext<V>) -> Div<V> {
pub fn modal<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
elevated_surface(ElevationIndex::ModalSurface, cx)
}
+16 -72
View File
@@ -1,7 +1,7 @@
use gpui::{rems, svg, Hsla};
use gpui::{rems, svg};
use strum::EnumIter;
use crate::{prelude::*, LabelColor};
use crate::prelude::*;
#[derive(Default, PartialEq, Copy, Clone)]
pub enum IconSize {
@@ -10,70 +10,6 @@ pub enum IconSize {
Medium,
}
#[derive(Default, PartialEq, Copy, Clone)]
pub enum IconColor {
#[default]
Default,
Accent,
Created,
Deleted,
Disabled,
Error,
Hidden,
Info,
Modified,
Muted,
Placeholder,
Player(u32),
Selected,
Success,
Warning,
}
impl IconColor {
pub fn color(self, cx: &WindowContext) -> Hsla {
match self {
IconColor::Default => cx.theme().colors().icon,
IconColor::Muted => cx.theme().colors().icon_muted,
IconColor::Disabled => cx.theme().colors().icon_disabled,
IconColor::Placeholder => cx.theme().colors().icon_placeholder,
IconColor::Accent => cx.theme().colors().icon_accent,
IconColor::Error => cx.theme().status().error,
IconColor::Warning => cx.theme().status().warning,
IconColor::Success => cx.theme().status().success,
IconColor::Info => cx.theme().status().info,
IconColor::Selected => cx.theme().colors().icon_accent,
IconColor::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor,
IconColor::Created => cx.theme().status().created,
IconColor::Modified => cx.theme().status().modified,
IconColor::Deleted => cx.theme().status().deleted,
IconColor::Hidden => cx.theme().status().hidden,
}
}
}
impl From<LabelColor> for IconColor {
fn from(label: LabelColor) -> Self {
match label {
LabelColor::Default => IconColor::Default,
LabelColor::Muted => IconColor::Muted,
LabelColor::Disabled => IconColor::Disabled,
LabelColor::Placeholder => IconColor::Placeholder,
LabelColor::Accent => IconColor::Accent,
LabelColor::Error => IconColor::Error,
LabelColor::Warning => IconColor::Warning,
LabelColor::Success => IconColor::Success,
LabelColor::Info => IconColor::Info,
LabelColor::Selected => IconColor::Selected,
LabelColor::Player(i) => IconColor::Player(i),
LabelColor::Created => IconColor::Created,
LabelColor::Modified => IconColor::Modified,
LabelColor::Deleted => IconColor::Deleted,
LabelColor::Hidden => IconColor::Hidden,
}
}
}
#[derive(Debug, PartialEq, Copy, Clone, EnumIter)]
pub enum Icon {
Ai,
@@ -193,21 +129,29 @@ impl Icon {
#[derive(Component)]
pub struct IconElement {
icon: Icon,
color: IconColor,
path: SharedString,
color: TextColor,
size: IconSize,
}
impl IconElement {
pub fn new(icon: Icon) -> Self {
Self {
icon,
color: IconColor::default(),
path: icon.path().into(),
color: TextColor::default(),
size: IconSize::default(),
}
}
pub fn color(mut self, color: IconColor) -> Self {
pub fn from_path(path: impl Into<SharedString>) -> Self {
Self {
path: path.into(),
color: TextColor::default(),
size: IconSize::default(),
}
}
pub fn color(mut self, color: TextColor) -> Self {
self.color = color;
self
}
@@ -226,7 +170,7 @@ impl IconElement {
svg()
.size(svg_size)
.flex_none()
.path(self.icon.path())
.path(self.path)
.text_color(self.color.color(cx))
}
}
+6 -6
View File
@@ -1,5 +1,5 @@
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconColor, IconElement, TextTooltip};
use gpui::{MouseButton, VisualContext};
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement, TextTooltip};
use gpui::{prelude::*, MouseButton, VisualContext};
use std::sync::Arc;
struct IconButtonHandlers<V: 'static> {
@@ -16,7 +16,7 @@ impl<V: 'static> Default for IconButtonHandlers<V> {
pub struct IconButton<V: 'static> {
id: ElementId,
icon: Icon,
color: IconColor,
color: TextColor,
variant: ButtonVariant,
state: InteractionState,
tooltip: Option<SharedString>,
@@ -28,7 +28,7 @@ impl<V: 'static> IconButton<V> {
Self {
id: id.into(),
icon,
color: IconColor::default(),
color: TextColor::default(),
variant: ButtonVariant::default(),
state: InteractionState::default(),
tooltip: None,
@@ -41,7 +41,7 @@ impl<V: 'static> IconButton<V> {
self
}
pub fn color(mut self, color: IconColor) -> Self {
pub fn color(mut self, color: TextColor) -> Self {
self.color = color;
self
}
@@ -71,7 +71,7 @@ impl<V: 'static> IconButton<V> {
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
let icon_color = match (self.state, self.color) {
(InteractionState::Disabled, _) => IconColor::Disabled,
(InteractionState::Disabled, _) => TextColor::Disabled,
_ => self.color,
};
+6 -7
View File
@@ -1,6 +1,5 @@
use crate::prelude::*;
use crate::Label;
use crate::LabelColor;
use crate::{prelude::*, Label};
use gpui::prelude::*;
#[derive(Default, PartialEq)]
pub enum InputVariant {
@@ -71,15 +70,15 @@ impl Input {
};
let placeholder_label = Label::new(self.placeholder.clone()).color(if self.disabled {
LabelColor::Disabled
TextColor::Disabled
} else {
LabelColor::Placeholder
TextColor::Placeholder
});
let label = Label::new(self.value.clone()).color(if self.disabled {
LabelColor::Disabled
TextColor::Disabled
} else {
LabelColor::Default
TextColor::Default
});
div()
+1 -1
View File
@@ -3,7 +3,7 @@ use strum::EnumIter;
use crate::prelude::*;
#[derive(Component)]
#[derive(Component, Clone)]
pub struct KeyBinding {
/// A keybinding consists of a key and a set of modifier keys.
/// More then one keybinding produces a chord.
+57 -29
View File
@@ -3,8 +3,15 @@ use gpui::{relative, Hsla, Text, TextRun, WindowContext};
use crate::prelude::*;
use crate::styled_ext::StyledExt;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum LabelSize {
#[default]
Default,
Small,
}
#[derive(Default, PartialEq, Copy, Clone)]
pub enum LabelColor {
pub enum TextColor {
#[default]
Default,
Accent,
@@ -23,24 +30,24 @@ pub enum LabelColor {
Warning,
}
impl LabelColor {
pub fn hsla(&self, cx: &WindowContext) -> Hsla {
impl TextColor {
pub fn color(&self, cx: &WindowContext) -> Hsla {
match self {
LabelColor::Default => cx.theme().colors().text,
LabelColor::Muted => cx.theme().colors().text_muted,
LabelColor::Created => cx.theme().status().created,
LabelColor::Modified => cx.theme().status().modified,
LabelColor::Deleted => cx.theme().status().deleted,
LabelColor::Disabled => cx.theme().colors().text_disabled,
LabelColor::Hidden => cx.theme().status().hidden,
LabelColor::Info => cx.theme().status().info,
LabelColor::Placeholder => cx.theme().colors().text_placeholder,
LabelColor::Accent => cx.theme().colors().text_accent,
LabelColor::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor,
LabelColor::Error => cx.theme().status().error,
LabelColor::Selected => cx.theme().colors().text_accent,
LabelColor::Success => cx.theme().status().success,
LabelColor::Warning => cx.theme().status().warning,
TextColor::Default => cx.theme().colors().text,
TextColor::Muted => cx.theme().colors().text_muted,
TextColor::Created => cx.theme().status().created,
TextColor::Modified => cx.theme().status().modified,
TextColor::Deleted => cx.theme().status().deleted,
TextColor::Disabled => cx.theme().colors().text_disabled,
TextColor::Hidden => cx.theme().status().hidden,
TextColor::Info => cx.theme().status().info,
TextColor::Placeholder => cx.theme().colors().text_placeholder,
TextColor::Accent => cx.theme().colors().text_accent,
TextColor::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor,
TextColor::Error => cx.theme().status().error,
TextColor::Selected => cx.theme().colors().text_accent,
TextColor::Success => cx.theme().status().success,
TextColor::Warning => cx.theme().status().warning,
}
}
}
@@ -56,8 +63,9 @@ pub enum LineHeightStyle {
#[derive(Component)]
pub struct Label {
label: SharedString,
size: LabelSize,
line_height_style: LineHeightStyle,
color: LabelColor,
color: TextColor,
strikethrough: bool,
}
@@ -65,13 +73,19 @@ impl Label {
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
label: label.into(),
size: LabelSize::Default,
line_height_style: LineHeightStyle::default(),
color: LabelColor::Default,
color: TextColor::Default,
strikethrough: false,
}
}
pub fn color(mut self, color: LabelColor) -> Self {
pub fn size(mut self, size: LabelSize) -> Self {
self.size = size;
self
}
pub fn color(mut self, color: TextColor) -> Self {
self.color = color;
self
}
@@ -95,14 +109,17 @@ impl Label {
.top_1_2()
.w_full()
.h_px()
.bg(LabelColor::Hidden.hsla(cx)),
.bg(TextColor::Hidden.color(cx)),
)
})
.text_ui()
.map(|this| match self.size {
LabelSize::Default => this.text_ui(),
LabelSize::Small => this.text_ui_sm(),
})
.when(self.line_height_style == LineHeightStyle::UILabel, |this| {
this.line_height(relative(1.))
})
.text_color(self.color.hsla(cx))
.text_color(self.color.color(cx))
.child(self.label.clone())
}
}
@@ -110,7 +127,8 @@ impl Label {
#[derive(Component)]
pub struct HighlightedLabel {
label: SharedString,
color: LabelColor,
size: LabelSize,
color: TextColor,
highlight_indices: Vec<usize>,
strikethrough: bool,
}
@@ -121,13 +139,19 @@ impl HighlightedLabel {
pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
Self {
label: label.into(),
color: LabelColor::Default,
size: LabelSize::Default,
color: TextColor::Default,
highlight_indices,
strikethrough: false,
}
}
pub fn color(mut self, color: LabelColor) -> Self {
pub fn size(mut self, size: LabelSize) -> Self {
self.size = size;
self
}
pub fn color(mut self, color: TextColor) -> Self {
self.color = color;
self
}
@@ -146,7 +170,7 @@ impl HighlightedLabel {
let mut runs: Vec<TextRun> = Vec::new();
for (char_ix, char) in self.label.char_indices() {
let mut color = self.color.hsla(cx);
let mut color = self.color.color(cx);
if let Some(highlight_ix) = highlight_indices.peek() {
if char_ix == *highlight_ix {
@@ -183,9 +207,13 @@ impl HighlightedLabel {
.my_auto()
.w_full()
.h_px()
.bg(LabelColor::Hidden.hsla(cx)),
.bg(TextColor::Hidden.color(cx)),
)
})
.map(|this| match self.size {
LabelSize::Default => this.text_ui(),
LabelSize::Small => this.text_ui_sm(),
})
.child(Text::styled(self.label, runs))
}
}
+10 -10
View File
@@ -1,11 +1,11 @@
use gpui::div;
use crate::prelude::*;
use crate::settings::user_settings;
use crate::{
disclosure_control, h_stack, v_stack, Avatar, Icon, IconColor, IconElement, IconSize, Label,
LabelColor, Toggle,
disclosure_control, h_stack, v_stack, Avatar, GraphicSlot, Icon, IconElement, IconSize, Label,
TextColor, Toggle,
};
use crate::{prelude::*, GraphicSlot};
#[derive(Clone, Copy, Default, Debug, PartialEq)]
pub enum ListItemVariant {
@@ -68,7 +68,7 @@ impl ListHeader {
.items_center()
.children(icons.into_iter().map(|i| {
IconElement::new(i)
.color(IconColor::Muted)
.color(TextColor::Muted)
.size(IconSize::Small)
})),
),
@@ -106,10 +106,10 @@ impl ListHeader {
.items_center()
.children(self.left_icon.map(|i| {
IconElement::new(i)
.color(IconColor::Muted)
.color(TextColor::Muted)
.size(IconSize::Small)
}))
.child(Label::new(self.label.clone()).color(LabelColor::Muted)),
.child(Label::new(self.label.clone()).color(TextColor::Muted)),
)
.child(disclosure_control),
)
@@ -157,10 +157,10 @@ impl ListSubHeader {
.items_center()
.children(self.left_icon.map(|i| {
IconElement::new(i)
.color(IconColor::Muted)
.color(TextColor::Muted)
.size(IconSize::Small)
}))
.child(Label::new(self.label.clone()).color(LabelColor::Muted)),
.child(Label::new(self.label.clone()).color(TextColor::Muted)),
),
)
}
@@ -291,7 +291,7 @@ impl ListEntry {
h_stack().child(
IconElement::new(i)
.size(IconSize::Small)
.color(IconColor::Muted),
.color(TextColor::Muted),
),
),
Some(GraphicSlot::Avatar(src)) => Some(h_stack().child(Avatar::new(src))),
@@ -394,7 +394,7 @@ impl List {
(false, _) => div().children(self.items),
(true, Toggle::Toggled(false)) => div(),
(true, _) => {
div().child(Label::new(self.empty_message.clone()).color(LabelColor::Muted))
div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted))
}
};
+1 -1
View File
@@ -74,7 +74,7 @@ impl<V: 'static> Modal<V> {
}
}
impl<V: 'static> ParentElement<V> for Modal<V> {
impl<V: 'static> ParentComponent<V> for Modal<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
+15 -18
View File
@@ -1,5 +1,5 @@
use crate::prelude::*;
use crate::{h_stack, v_stack, KeyBinding, Label, LabelColor};
use crate::{h_stack, prelude::*, v_stack, KeyBinding, Label};
use gpui::prelude::*;
#[derive(Component)]
pub struct Palette {
@@ -54,7 +54,7 @@ impl Palette {
v_stack()
.gap_px()
.child(v_stack().py_0p5().px_1().child(div().px_2().py_0p5().child(
Label::new(self.input_placeholder.clone()).color(LabelColor::Placeholder),
Label::new(self.input_placeholder.clone()).color(TextColor::Placeholder),
)))
.child(
div()
@@ -75,7 +75,7 @@ impl Palette {
Some(
h_stack().justify_between().px_2().py_1().child(
Label::new(self.empty_string.clone())
.color(LabelColor::Muted),
.color(TextColor::Muted),
),
)
} else {
@@ -108,7 +108,7 @@ impl Palette {
pub struct PaletteItem {
pub label: SharedString,
pub sublabel: Option<SharedString>,
pub keybinding: Option<KeyBinding>,
pub key_binding: Option<KeyBinding>,
}
impl PaletteItem {
@@ -116,7 +116,7 @@ impl PaletteItem {
Self {
label: label.into(),
sublabel: None,
keybinding: None,
key_binding: None,
}
}
@@ -130,11 +130,8 @@ impl PaletteItem {
self
}
pub fn keybinding<K>(mut self, keybinding: K) -> Self
where
K: Into<Option<KeyBinding>>,
{
self.keybinding = keybinding.into();
pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
self.key_binding = key_binding.into();
self
}
@@ -149,7 +146,7 @@ impl PaletteItem {
.child(Label::new(self.label.clone()))
.children(self.sublabel.clone().map(|sublabel| Label::new(sublabel))),
)
.children(self.keybinding)
.children(self.key_binding)
}
}
@@ -182,23 +179,23 @@ mod stories {
.placeholder("Execute a command...")
.items(vec![
PaletteItem::new("theme selector: toggle")
.keybinding(KeyBinding::new(binding("cmd-k cmd-t"))),
.key_binding(KeyBinding::new(binding("cmd-k cmd-t"))),
PaletteItem::new("assistant: inline assist")
.keybinding(KeyBinding::new(binding("cmd-enter"))),
.key_binding(KeyBinding::new(binding("cmd-enter"))),
PaletteItem::new("assistant: quote selection")
.keybinding(KeyBinding::new(binding("cmd-<"))),
.key_binding(KeyBinding::new(binding("cmd-<"))),
PaletteItem::new("assistant: toggle focus")
.keybinding(KeyBinding::new(binding("cmd-?"))),
.key_binding(KeyBinding::new(binding("cmd-?"))),
PaletteItem::new("auto update: check"),
PaletteItem::new("auto update: view release notes"),
PaletteItem::new("branches: open recent")
.keybinding(KeyBinding::new(binding("cmd-alt-b"))),
.key_binding(KeyBinding::new(binding("cmd-alt-b"))),
PaletteItem::new("chat panel: toggle focus"),
PaletteItem::new("cli: install"),
PaletteItem::new("client: sign in"),
PaletteItem::new("client: sign out"),
PaletteItem::new("editor: cancel")
.keybinding(KeyBinding::new(binding("escape"))),
.key_binding(KeyBinding::new(binding("escape"))),
]),
)
}
+3 -3
View File
@@ -1,4 +1,4 @@
use gpui::{AbsoluteLength, AnyElement};
use gpui::{prelude::*, AbsoluteLength, AnyElement};
use smallvec::SmallVec;
use crate::prelude::*;
@@ -113,7 +113,7 @@ impl<V: 'static> Panel<V> {
}
}
impl<V: 'static> ParentElement<V> for Panel<V> {
impl<V: 'static> ParentComponent<V> for Panel<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
@@ -126,7 +126,7 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{Label, Story};
use gpui::{Div, Render};
use gpui::{Div, InteractiveComponent, Render};
pub struct PanelStory;
+8 -10
View File
@@ -1,6 +1,6 @@
use crate::prelude::*;
use crate::{Icon, IconColor, IconElement, Label, LabelColor};
use gpui::{red, Div, ElementId, Render, View, VisualContext};
use crate::{Icon, IconElement, Label, TextColor};
use gpui::{prelude::*, red, Div, ElementId, Render, View};
#[derive(Component, Clone)]
pub struct Tab {
@@ -92,20 +92,18 @@ impl Tab {
let label = match (self.git_status, is_deleted) {
(_, true) | (GitStatus::Deleted, false) => Label::new(self.title.clone())
.color(LabelColor::Hidden)
.color(TextColor::Hidden)
.set_strikethrough(true),
(GitStatus::None, false) => Label::new(self.title.clone()),
(GitStatus::Created, false) => {
Label::new(self.title.clone()).color(LabelColor::Created)
}
(GitStatus::Created, false) => Label::new(self.title.clone()).color(TextColor::Created),
(GitStatus::Modified, false) => {
Label::new(self.title.clone()).color(LabelColor::Modified)
Label::new(self.title.clone()).color(TextColor::Modified)
}
(GitStatus::Renamed, false) => Label::new(self.title.clone()).color(LabelColor::Accent),
(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(IconColor::Muted);
let close_icon = || IconElement::new(Icon::Close).color(TextColor::Muted);
let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current {
false => (
@@ -148,7 +146,7 @@ impl Tab {
.children(has_fs_conflict.then(|| {
IconElement::new(Icon::ExclamationTriangle)
.size(crate::IconSize::Small)
.color(IconColor::Warning)
.color(TextColor::Warning)
}))
.children(self.icon.map(IconElement::new))
.children(if self.close_side == IconSide::Left {
+3 -4
View File
@@ -1,7 +1,6 @@
use gpui::AnyElement;
use smallvec::SmallVec;
use crate::prelude::*;
use gpui::{prelude::*, AnyElement};
use smallvec::SmallVec;
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
pub enum ToastOrigin {
@@ -59,7 +58,7 @@ impl<V: 'static> Toast<V> {
}
}
impl<V: 'static> ParentElement<V> for Toast<V> {
impl<V: 'static> ParentComponent<V> for Toast<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
+4 -4
View File
@@ -1,6 +1,6 @@
use gpui::{div, Component, ParentElement};
use gpui::{div, Component, ParentComponent};
use crate::{Icon, IconColor, IconElement, IconSize};
use crate::{Icon, IconElement, IconSize, TextColor};
/// Whether the entry is toggleable, and if so, whether it is currently toggled.
///
@@ -49,12 +49,12 @@ pub fn disclosure_control<V: 'static>(toggle: Toggle) -> impl Component<V> {
(false, _) => div(),
(_, true) => div().child(
IconElement::new(Icon::ChevronDown)
.color(IconColor::Muted)
.color(TextColor::Muted)
.size(IconSize::Small),
),
(_, false) => div().child(
IconElement::new(Icon::ChevronRight)
.color(IconColor::Muted)
.color(TextColor::Muted)
.size(IconSize::Small),
),
}
+37 -8
View File
@@ -1,32 +1,61 @@
use gpui::{div, Div, ParentElement, Render, SharedString, Styled, ViewContext};
use theme2::ActiveTheme;
use gpui::{Div, Render};
use settings2::Settings;
use theme2::{ActiveTheme, ThemeSettings};
use crate::StyledExt;
use crate::prelude::*;
use crate::{h_stack, v_stack, KeyBinding, Label, LabelSize, StyledExt, TextColor};
#[derive(Clone, Debug)]
pub struct TextTooltip {
title: SharedString,
meta: Option<SharedString>,
key_binding: Option<KeyBinding>,
}
impl TextTooltip {
pub fn new(title: impl Into<SharedString>) -> Self {
Self {
title: title.into(),
meta: None,
key_binding: None,
}
}
pub fn meta(mut self, meta: impl Into<SharedString>) -> Self {
self.meta = Some(meta.into());
self
}
pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
self.key_binding = key_binding.into();
self
}
}
impl Render for TextTooltip {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div()
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
v_stack()
.elevation_2(cx)
.font("Zed Sans")
.text_ui()
.font(ui_font)
.text_ui_sm()
.text_color(cx.theme().colors().text)
.py_1()
.px_2()
.child(self.title.clone())
.child(
h_stack()
.child(self.title.clone())
.when_some(self.key_binding.clone(), |this, key_binding| {
this.justify_between().child(key_binding)
}),
)
.when_some(self.meta.clone(), |this, meta| {
this.child(
Label::new(meta)
.size(LabelSize::Small)
.color(TextColor::Muted),
)
})
}
}
+3 -3
View File
@@ -1,13 +1,13 @@
use gpui::rems;
use gpui::Rems;
pub use gpui::{
div, Component, Element, ElementId, ParentElement, SharedString, StatefulInteractive,
StatelessInteractive, Styled, ViewContext, WindowContext,
div, Component, Element, ElementId, InteractiveComponent, ParentComponent, SharedString,
Styled, ViewContext, WindowContext,
};
pub use crate::elevation::*;
pub use crate::ButtonVariant;
pub use crate::StyledExt;
pub use crate::{ButtonVariant, TextColor};
pub use theme2::ActiveTheme;
use gpui::Hsla;
+28 -28
View File
@@ -10,9 +10,9 @@ use theme2::ActiveTheme;
use crate::{binding, HighlightedText};
use crate::{
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
HighlightedLine, Icon, KeyBinding, Label, LabelColor, ListEntry, ListEntrySize, Livestream,
MicStatus, Notification, PaletteItem, Player, PlayerCallStatus, PlayerWithCallStatus,
PublicPlayer, ScreenShareStatus, Symbol, Tab, Toggle, VideoStatus,
HighlightedLine, Icon, KeyBinding, Label, ListEntry, ListEntrySize, Livestream, MicStatus,
Notification, PaletteItem, Player, PlayerCallStatus, PlayerWithCallStatus, PublicPlayer,
ScreenShareStatus, Symbol, Tab, TextColor, Toggle, VideoStatus,
};
use crate::{ListItem, NotificationAction};
@@ -490,20 +490,20 @@ pub fn static_project_panel_project_items() -> Vec<ListItem> {
ListEntry::new(Label::new(".config"))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".git").color(LabelColor::Hidden))
ListEntry::new(Label::new(".git").color(TextColor::Hidden))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".cargo"))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".idea").color(LabelColor::Hidden))
ListEntry::new(Label::new(".idea").color(TextColor::Hidden))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new("assets"))
.left_icon(Icon::Folder.into())
.indent_level(1)
.toggle(Toggle::Toggled(true)),
ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden))
ListEntry::new(Label::new("cargo-target").color(TextColor::Hidden))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new("crates"))
@@ -528,7 +528,7 @@ pub fn static_project_panel_project_items() -> Vec<ListItem> {
ListEntry::new(Label::new("call"))
.left_icon(Icon::Folder.into())
.indent_level(2),
ListEntry::new(Label::new("sqlez").color(LabelColor::Modified))
ListEntry::new(Label::new("sqlez").color(TextColor::Modified))
.left_icon(Icon::Folder.into())
.indent_level(2)
.toggle(Toggle::Toggled(false)),
@@ -543,45 +543,45 @@ pub fn static_project_panel_project_items() -> Vec<ListItem> {
ListEntry::new(Label::new("derive_element.rs"))
.left_icon(Icon::FileRust.into())
.indent_level(4),
ListEntry::new(Label::new("storybook").color(LabelColor::Modified))
ListEntry::new(Label::new("storybook").color(TextColor::Modified))
.left_icon(Icon::FolderOpen.into())
.indent_level(1)
.toggle(Toggle::Toggled(true)),
ListEntry::new(Label::new("docs").color(LabelColor::Default))
ListEntry::new(Label::new("docs").color(TextColor::Default))
.left_icon(Icon::Folder.into())
.indent_level(2)
.toggle(Toggle::Toggled(true)),
ListEntry::new(Label::new("src").color(LabelColor::Modified))
ListEntry::new(Label::new("src").color(TextColor::Modified))
.left_icon(Icon::FolderOpen.into())
.indent_level(3)
.toggle(Toggle::Toggled(true)),
ListEntry::new(Label::new("ui").color(LabelColor::Modified))
ListEntry::new(Label::new("ui").color(TextColor::Modified))
.left_icon(Icon::FolderOpen.into())
.indent_level(4)
.toggle(Toggle::Toggled(true)),
ListEntry::new(Label::new("component").color(LabelColor::Created))
ListEntry::new(Label::new("component").color(TextColor::Created))
.left_icon(Icon::FolderOpen.into())
.indent_level(5)
.toggle(Toggle::Toggled(true)),
ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default))
ListEntry::new(Label::new("facepile.rs").color(TextColor::Default))
.left_icon(Icon::FileRust.into())
.indent_level(6),
ListEntry::new(Label::new("follow_group.rs").color(LabelColor::Default))
ListEntry::new(Label::new("follow_group.rs").color(TextColor::Default))
.left_icon(Icon::FileRust.into())
.indent_level(6),
ListEntry::new(Label::new("list_item.rs").color(LabelColor::Created))
ListEntry::new(Label::new("list_item.rs").color(TextColor::Created))
.left_icon(Icon::FileRust.into())
.indent_level(6),
ListEntry::new(Label::new("tab.rs").color(LabelColor::Default))
ListEntry::new(Label::new("tab.rs").color(TextColor::Default))
.left_icon(Icon::FileRust.into())
.indent_level(6),
ListEntry::new(Label::new("target").color(LabelColor::Hidden))
ListEntry::new(Label::new("target").color(TextColor::Hidden))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".dockerignore"))
.left_icon(Icon::FileGeneric.into())
.indent_level(1),
ListEntry::new(Label::new(".DS_Store").color(LabelColor::Hidden))
ListEntry::new(Label::new(".DS_Store").color(TextColor::Hidden))
.left_icon(Icon::FileGeneric.into())
.indent_level(1),
ListEntry::new(Label::new("Cargo.lock"))
@@ -701,16 +701,16 @@ pub fn static_collab_panel_channels() -> Vec<ListItem> {
pub fn example_editor_actions() -> Vec<PaletteItem> {
vec![
PaletteItem::new("New File").keybinding(KeyBinding::new(binding("cmd-n"))),
PaletteItem::new("Open File").keybinding(KeyBinding::new(binding("cmd-o"))),
PaletteItem::new("Save File").keybinding(KeyBinding::new(binding("cmd-s"))),
PaletteItem::new("Cut").keybinding(KeyBinding::new(binding("cmd-x"))),
PaletteItem::new("Copy").keybinding(KeyBinding::new(binding("cmd-c"))),
PaletteItem::new("Paste").keybinding(KeyBinding::new(binding("cmd-v"))),
PaletteItem::new("Undo").keybinding(KeyBinding::new(binding("cmd-z"))),
PaletteItem::new("Redo").keybinding(KeyBinding::new(binding("cmd-shift-z"))),
PaletteItem::new("Find").keybinding(KeyBinding::new(binding("cmd-f"))),
PaletteItem::new("Replace").keybinding(KeyBinding::new(binding("cmd-r"))),
PaletteItem::new("New File").key_binding(KeyBinding::new(binding("cmd-n"))),
PaletteItem::new("Open File").key_binding(KeyBinding::new(binding("cmd-o"))),
PaletteItem::new("Save File").key_binding(KeyBinding::new(binding("cmd-s"))),
PaletteItem::new("Cut").key_binding(KeyBinding::new(binding("cmd-x"))),
PaletteItem::new("Copy").key_binding(KeyBinding::new(binding("cmd-c"))),
PaletteItem::new("Paste").key_binding(KeyBinding::new(binding("cmd-v"))),
PaletteItem::new("Undo").key_binding(KeyBinding::new(binding("cmd-z"))),
PaletteItem::new("Redo").key_binding(KeyBinding::new(binding("cmd-shift-z"))),
PaletteItem::new("Find").key_binding(KeyBinding::new(binding("cmd-f"))),
PaletteItem::new("Replace").key_binding(KeyBinding::new(binding("cmd-r"))),
PaletteItem::new("Jump to Line"),
PaletteItem::new("Select All"),
PaletteItem::new("Deselect All"),
+2 -9
View File
@@ -1,4 +1,4 @@
use gpui::{Div, ElementInteractivity, KeyDispatch, Styled, UniformList, ViewContext};
use gpui::{Styled, ViewContext};
use theme2::ActiveTheme;
use crate::{ElevationIndex, UITextSize};
@@ -93,11 +93,4 @@ pub trait StyledExt: Styled + Sized {
}
}
impl<V, I, F> StyledExt for Div<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
}
impl<V> StyledExt for UniformList<V> {}
impl<E: Styled> StyledExt for E {}
+1 -1
View File
@@ -1,6 +1,6 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Label, Panel, PanelSide};
use gpui::{rems, AbsoluteLength};
use gpui::{prelude::*, rems, AbsoluteLength};
#[derive(Component)]
pub struct AssistantPanel {
+2 -4
View File
@@ -1,9 +1,7 @@
use crate::{h_stack, prelude::*, HighlightedText};
use gpui::{prelude::*, Div};
use std::path::PathBuf;
use crate::prelude::*;
use crate::{h_stack, HighlightedText};
use gpui::Div;
#[derive(Clone)]
pub struct Symbol(pub Vec<HighlightedText>);
+2 -2
View File
@@ -1,7 +1,7 @@
use gpui::{Div, Render, View, VisualContext};
use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, IconColor, Input};
use crate::{h_stack, Icon, IconButton, Input, TextColor};
#[derive(Clone)]
pub struct BufferSearch {
@@ -36,7 +36,7 @@ impl Render for BufferSearch {
.child(
h_stack().child(Input::new("Search")).child(
IconButton::<Self>::new("replace", Icon::Replace)
.when(self.is_replace_open, |this| this.color(IconColor::Accent))
.when(self.is_replace_open, |this| this.color(TextColor::Accent))
.on_click(|buffer_search, cx| {
buffer_search.toggle_replace(cx);
}),
+3 -4
View File
@@ -1,7 +1,6 @@
use crate::{prelude::*, Icon, IconButton, Input, Label};
use chrono::NaiveDateTime;
use crate::prelude::*;
use crate::{Icon, IconButton, Input, Label, LabelColor};
use gpui::prelude::*;
#[derive(Component)]
pub struct ChatPanel {
@@ -95,7 +94,7 @@ impl ChatMessage {
.child(Label::new(self.author.clone()))
.child(
Label::new(self.sent_at.format("%m/%d/%Y").to_string())
.color(LabelColor::Muted),
.color(TextColor::Muted),
),
)
.child(div().child(Label::new(self.text.clone())))
+3 -2
View File
@@ -1,7 +1,8 @@
use crate::{prelude::*, Toggle};
use crate::{
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, ListHeader,
prelude::*, static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon,
List, ListHeader, Toggle,
};
use gpui::prelude::*;
#[derive(Component)]
pub struct CollabPanel {
+2 -2
View File
@@ -1,4 +1,4 @@
use crate::{prelude::*, Button, Label, LabelColor, Modal};
use crate::{prelude::*, Button, Label, Modal, TextColor};
#[derive(Component)]
pub struct CopilotModal {
@@ -14,7 +14,7 @@ impl CopilotModal {
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(LabelColor::Muted))
.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)),
)
}
+4 -4
View File
@@ -5,7 +5,7 @@ use gpui::{Div, Render, View, VisualContext};
use crate::prelude::*;
use crate::{
hello_world_rust_editor_with_status_example, v_stack, Breadcrumb, Buffer, BufferSearch, Icon,
IconButton, IconColor, Symbol, Tab, TabBar, Toolbar,
IconButton, Symbol, Tab, TabBar, TextColor, Toolbar,
};
#[derive(Clone)]
@@ -60,12 +60,12 @@ impl Render for EditorPane {
Toolbar::new()
.left_item(Breadcrumb::new(self.path.clone(), self.symbols.clone()))
.right_items(vec![
IconButton::new("toggle_inlay_hints", Icon::InlayHint),
IconButton::<Self>::new("toggle_inlay_hints", Icon::InlayHint),
IconButton::<Self>::new("buffer_search", Icon::MagnifyingGlass)
.when(self.is_buffer_search_open, |this| {
this.color(IconColor::Accent)
this.color(TextColor::Accent)
})
.on_click(|editor, cx| {
.on_click(|editor: &mut Self, cx| {
editor.toggle_buffer_search(cx);
}),
IconButton::new("inline_assist", Icon::MagicWand),
@@ -1,10 +1,9 @@
use crate::utils::naive_format_distance_from_now;
use crate::{
h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, ButtonOrIconButton,
Icon, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator,
PublicPlayer, UnreadIndicator,
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 crate::{ClickHandler, ListHeader};
use gpui::prelude::*;
#[derive(Component)]
pub struct NotificationsPanel {
@@ -48,7 +47,7 @@ impl NotificationsPanel {
.border_color(cx.theme().colors().border_variant)
.child(
Label::new("Search...")
.color(LabelColor::Placeholder)
.color(TextColor::Placeholder)
.line_height_style(LineHeightStyle::UILabel),
),
)
@@ -252,7 +251,7 @@ impl<V> Notification<V> {
if let Some(icon) = icon {
meta_el = meta_el.child(IconElement::new(icon.clone()));
}
meta_el.child(Label::new(text.clone()).color(LabelColor::Muted))
meta_el.child(Label::new(text.clone()).color(TextColor::Muted))
})
.collect::<Vec<_>>(),
)
@@ -311,7 +310,7 @@ impl<V> Notification<V> {
true,
true,
))
.color(LabelColor::Muted),
.color(TextColor::Muted),
)
.child(self.render_meta_items(cx)),
)
@@ -321,11 +320,11 @@ impl<V> Notification<V> {
// Show the taken_message
(Some(_), Some(action_taken)) => h_stack()
.children(action_taken.taken_message.0.map(|icon| {
IconElement::new(icon).color(crate::IconColor::Muted)
IconElement::new(icon).color(crate::TextColor::Muted)
}))
.child(
Label::new(action_taken.taken_message.1.clone())
.color(LabelColor::Muted),
.color(TextColor::Muted),
),
// Show the actions
(Some(actions), None) => {
+1 -1
View File
@@ -59,7 +59,7 @@ impl<V: 'static> Pane<V> {
}
}
impl<V: 'static> ParentElement<V> for Pane<V> {
impl<V: 'static> ParentComponent<V> for Pane<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
+3 -2
View File
@@ -1,7 +1,8 @@
use crate::prelude::*;
use crate::{
static_project_panel_project_items, static_project_panel_single_items, Input, List, ListHeader,
prelude::*, static_project_panel_project_items, static_project_panel_single_items, Input, List,
ListHeader,
};
use gpui::prelude::*;
#[derive(Component)]
pub struct ProjectPanel {
+11 -11
View File
@@ -1,7 +1,7 @@
use std::sync::Arc;
use crate::prelude::*;
use crate::{Button, Icon, IconButton, IconColor, ToolDivider, Workspace};
use crate::{Button, Icon, IconButton, TextColor, ToolDivider, Workspace};
#[derive(Default, PartialEq)]
pub enum Tool {
@@ -110,18 +110,18 @@ impl StatusBar {
.child(
IconButton::<Workspace>::new("project_panel", Icon::FileTree)
.when(workspace.is_project_panel_open(), |this| {
this.color(IconColor::Accent)
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.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(IconColor::Accent)
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_collab_panel();
}),
)
@@ -174,27 +174,27 @@ impl StatusBar {
.child(
IconButton::<Workspace>::new("terminal", Icon::Terminal)
.when(workspace.is_terminal_open(), |this| {
this.color(IconColor::Accent)
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.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(IconColor::Accent)
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.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(IconColor::Accent)
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_assistant_panel(cx);
}),
),
+2 -2
View File
@@ -1,5 +1,5 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Tab};
use crate::{prelude::*, Icon, IconButton, Tab};
use gpui::prelude::*;
#[derive(Component)]
pub struct TabBar {
+12 -8
View File
@@ -6,8 +6,8 @@ use gpui::{Div, Render, View, VisualContext};
use crate::prelude::*;
use crate::settings::user_settings;
use crate::{
Avatar, Button, Icon, IconButton, IconColor, MicStatus, PlayerStack, PlayerWithCallStatus,
ScreenShareStatus, ToolDivider, TrafficLights,
Avatar, Button, Icon, IconButton, MicStatus, PlayerStack, PlayerWithCallStatus,
ScreenShareStatus, TextColor, ToolDivider, TrafficLights,
};
#[derive(Clone)]
@@ -152,21 +152,25 @@ impl Render for TitleBar {
.gap_1()
.child(
IconButton::<TitleBar>::new("toggle_mic_status", Icon::Mic)
.when(self.is_mic_muted(), |this| this.color(IconColor::Error))
.on_click(|title_bar, cx| title_bar.toggle_mic_status(cx)),
.when(self.is_mic_muted(), |this| this.color(TextColor::Error))
.on_click(|title_bar: &mut TitleBar, cx| {
title_bar.toggle_mic_status(cx)
}),
)
.child(
IconButton::<TitleBar>::new("toggle_deafened", Icon::AudioOn)
.when(self.is_deafened, |this| this.color(IconColor::Error))
.on_click(|title_bar, cx| title_bar.toggle_deafened(cx)),
.when(self.is_deafened, |this| this.color(TextColor::Error))
.on_click(|title_bar: &mut TitleBar, cx| {
title_bar.toggle_deafened(cx)
}),
)
.child(
IconButton::<TitleBar>::new("toggle_screen_share", Icon::Screen)
.when(
self.screen_share_status == ScreenShareStatus::Shared,
|this| this.color(IconColor::Accent),
|this| this.color(TextColor::Accent),
)
.on_click(|title_bar, cx| {
.on_click(|title_bar: &mut TitleBar, cx| {
title_bar.toggle_screen_share_status(cx)
}),
),
+2 -1
View File
@@ -206,13 +206,14 @@ impl Render for Workspace {
.child(self.editor_1.clone())],
SplitDirection::Horizontal,
);
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
div()
.relative()
.size_full()
.flex()
.flex_col()
.font("Zed Sans")
.font(ui_font)
.gap_0()
.justify_start()
.items_start()
+25 -3
View File
@@ -1,7 +1,8 @@
use crate::{status_bar::StatusItemView, Axis, Workspace};
use gpui::{
div, Action, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, ParentElement, Render,
Subscription, View, ViewContext, WeakView, WindowContext,
div, px, Action, AnyView, AppContext, Component, Div, Entity, EntityId, EventEmitter,
FocusHandle, ParentComponent, Render, Styled, Subscription, View, ViewContext, WeakView,
WindowContext,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -34,6 +35,7 @@ pub trait Panel: Render + EventEmitter<PanelEvent> {
fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {}
fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
fn has_focus(&self, cx: &WindowContext) -> bool;
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
}
pub trait PanelHandle: Send + Sync {
@@ -51,6 +53,7 @@ pub trait PanelHandle: Send + Sync {
fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>);
fn icon_label(&self, cx: &WindowContext) -> Option<String>;
fn has_focus(&self, cx: &WindowContext) -> bool;
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
fn to_any(&self) -> AnyView;
}
@@ -117,6 +120,10 @@ where
fn to_any(&self) -> AnyView {
self.clone().into()
}
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle {
self.read(cx).focus_handle(cx).clone()
}
}
impl From<&dyn PanelHandle> for AnyView {
@@ -422,7 +429,18 @@ impl Render for Dock {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
todo!()
if let Some(entry) = self.visible_entry() {
let size = entry.panel.size(cx);
div()
.map(|this| match self.position().axis() {
Axis::Horizontal => this.w(px(size)).h_full(),
Axis::Vertical => this.h(px(size)).w_full(),
})
.child(entry.panel.to_any())
} else {
div()
}
}
}
@@ -728,5 +746,9 @@ pub mod test {
fn has_focus(&self, _cx: &WindowContext) -> bool {
self.has_focus
}
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle {
unimplemented!()
}
}
}
+10 -2
View File
@@ -1,6 +1,6 @@
use gpui::{
div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
div, prelude::*, px, AnyView, Div, EventEmitter, FocusHandle, Render, Subscription, View,
ViewContext, WindowContext,
};
use ui::{h_stack, v_stack};
@@ -71,6 +71,14 @@ impl ModalLayer {
cx.notify();
}
pub fn current_modal<V>(&self) -> Option<View<V>>
where
V: 'static,
{
let active_modal = self.active_modal.as_ref()?;
active_modal.modal.clone().downcast::<V>().ok()
}
}
impl Render for ModalLayer {
+5 -7
View File
@@ -1,5 +1,3 @@
// mod dragged_item_receiver;
use crate::{
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
toolbar::Toolbar,
@@ -9,7 +7,7 @@ use crate::{
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
use gpui::{
actions, register_action, AppContext, AsyncWindowContext, Component, Div, EntityId,
actions, prelude::*, register_action, AppContext, AsyncWindowContext, Component, Div, EntityId,
EventEmitter, FocusHandle, Model, PromptLevel, Render, Task, View, ViewContext, VisualContext,
WeakView, WindowContext,
};
@@ -27,7 +25,7 @@ use std::{
},
};
use ui::v_stack;
use ui::{prelude::*, Icon, IconButton, IconColor, IconElement, TextTooltip};
use ui::{prelude::*, Icon, IconButton, IconElement, TextColor, TextTooltip};
use util::truncate_and_remove_front;
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
@@ -1432,13 +1430,13 @@ impl Pane {
Some(
IconElement::new(Icon::ExclamationTriangle)
.size(ui::IconSize::Small)
.color(IconColor::Warning),
.color(TextColor::Warning),
)
} else if item.is_dirty(cx) {
Some(
IconElement::new(Icon::ExclamationTriangle)
.size(ui::IconSize::Small)
.color(IconColor::Info),
.color(TextColor::Info),
)
} else {
None
@@ -1919,7 +1917,7 @@ impl Render for Pane {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
v_stack()
.context("Pane")
.key_context("Pane")
.size_full()
.on_action(|pane: &mut Self, action, cx| {
pane.close_active_item(action, cx)
@@ -2,7 +2,7 @@ use super::DraggedItem;
use crate::{Pane, SplitDirection, Workspace};
use gpui::{
color::Color,
elements::{Canvas, MouseEventHandler, ParentElement, Stack},
elements::{Canvas, MouseEventHandler, ParentComponent, Stack},
geometry::{rect::RectF, vector::Vector2F},
platform::MouseButton,
scene::MouseUp,
+1 -1
View File
@@ -2,7 +2,7 @@ use std::any::TypeId;
use crate::{ItemHandle, Pane};
use gpui::{
div, AnyView, Component, Div, ParentElement, Render, Styled, Subscription, View, ViewContext,
div, AnyView, Component, Div, ParentComponent, Render, Styled, Subscription, View, ViewContext,
WindowContext,
};
use theme2::ActiveTheme;
+365 -463
View File
@@ -29,23 +29,22 @@ use client2::{
Client, TypedEnvelope, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use dock::{Dock, DockPosition, PanelButtons};
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle as _};
use futures::{
channel::{mpsc, oneshot},
future::try_join_all,
Future, FutureExt, StreamExt,
};
use gpui::{
actions, div, point, rems, size, Action, AnyModel, AnyView, AnyWeakView, AppContext,
AsyncAppContext, AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter,
FocusHandle, GlobalPixels, KeyContext, Model, ModelContext, ParentElement, Point, Render, Size,
StatefulInteractive, StatelessInteractive, StatelessInteractivity, Styled, Subscription, Task,
View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle,
WindowOptions,
actions, div, point, prelude::*, rems, size, Action, AnyModel, AnyView, AnyWeakView,
AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Component, Div, Entity, EntityId,
EventEmitter, FocusHandle, GlobalPixels, KeyContext, Model, ModelContext, ParentComponent,
Point, Render, Size, Styled, Subscription, Task, View, ViewContext, WeakView, WindowBounds,
WindowContext, WindowHandle, WindowOptions,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
use itertools::Itertools;
use language2::LanguageRegistry;
use language2::{LanguageRegistry, Rope};
use lazy_static::lazy_static;
pub use modal_layer::*;
use node_runtime::NodeRuntime;
@@ -67,12 +66,13 @@ use std::{
sync::{atomic::AtomicUsize, Arc},
time::Duration,
};
use theme2::ActiveTheme;
use theme2::{ActiveTheme, ThemeSettings};
pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
use ui::{h_stack, Button, ButtonVariant, Label, LabelColor};
use ui::TextColor;
use ui::{h_stack, Button, ButtonVariant, KeyBinding, Label, TextTooltip};
use util::ResultExt;
use uuid::Uuid;
use workspace_settings::{AutosaveSetting, WorkspaceSettings};
pub use workspace_settings::{AutosaveSetting, WorkspaceSettings};
lazy_static! {
static ref ZED_WINDOW_SIZE: Option<Size<GlobalPixels>> = env::var("ZED_WINDOW_SIZE")
@@ -248,102 +248,6 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
// }
// }
// });
// cx.add_async_action(Workspace::open);
// cx.add_async_action(Workspace::follow_next_collaborator);
// cx.add_async_action(Workspace::close);
// cx.add_async_action(Workspace::close_inactive_items_and_panes);
// cx.add_async_action(Workspace::close_all_items_and_panes);
// cx.add_global_action(Workspace::close_global);
// cx.add_global_action(restart);
// cx.add_async_action(Workspace::save_all);
// cx.add_action(Workspace::add_folder_to_project);
// cx.add_action(
// |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext<Workspace>| {
// let pane = workspace.active_pane().clone();
// workspace.unfollow(&pane, cx);
// },
// );
// cx.add_action(
// |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext<Workspace>| {
// workspace
// .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx)
// .detach_and_log_err(cx);
// },
// );
// cx.add_action(
// |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext<Workspace>| {
// workspace
// .save_active_item(SaveIntent::SaveAs, cx)
// .detach_and_log_err(cx);
// },
// );
// cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
// workspace.activate_previous_pane(cx)
// });
// cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
// workspace.activate_next_pane(cx)
// });
// cx.add_action(
// |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| {
// workspace.activate_pane_in_direction(action.0, cx)
// },
// );
// cx.add_action(
// |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| {
// workspace.swap_pane_in_direction(action.0, cx)
// },
// );
// cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| {
// workspace.toggle_dock(DockPosition::Left, cx);
// });
// cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| {
// workspace.toggle_dock(DockPosition::Right, cx);
// });
// cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| {
// workspace.toggle_dock(DockPosition::Bottom, cx);
// });
// cx.add_action(|workspace: &mut Workspace, _: &CloseAllDocks, cx| {
// workspace.close_all_docks(cx);
// });
// cx.add_action(Workspace::activate_pane_at_index);
// cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
// workspace.reopen_closed_item(cx).detach();
// });
// cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| {
// workspace
// .go_back(workspace.active_pane().downgrade(), cx)
// .detach();
// });
// cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| {
// workspace
// .go_forward(workspace.active_pane().downgrade(), cx)
// .detach();
// });
// cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| {
// cx.spawn(|workspace, mut cx| async move {
// let err = install_cli::install_cli(&cx)
// .await
// .context("Failed to create CLI symlink");
// workspace.update(&mut cx, |workspace, cx| {
// if matches!(err, Err(_)) {
// err.notify_err(workspace, cx);
// } else {
// workspace.show_notification(1, cx, |cx| {
// cx.build_view(|_| {
// MessageNotification::new("Successfully installed the `zed` binary")
// })
// });
// }
// })
// })
// .detach();
// });
}
type ProjectItemBuilders =
@@ -443,7 +347,6 @@ struct Follower {
impl AppState {
#[cfg(any(test, feature = "test-support"))]
pub fn test(cx: &mut AppContext) -> Arc<Self> {
use gpui::Context;
use node_runtime::FakeNodeRuntime;
use settings2::SettingsStore;
@@ -531,13 +434,7 @@ pub enum Event {
pub struct Workspace {
weak_self: WeakView<Self>,
focus_handle: FocusHandle,
workspace_actions: Vec<
Box<
dyn Fn(
Div<Workspace, StatelessInteractivity<Workspace>>,
) -> Div<Workspace, StatelessInteractivity<Workspace>>,
>,
>,
workspace_actions: Vec<Box<dyn Fn(Div<Workspace>) -> Div<Workspace>>>,
zoomed: Option<AnyWeakView>,
zoomed_position: Option<DockPosition>,
center: PaneGroup,
@@ -942,108 +839,15 @@ impl Workspace {
&self.right_dock
}
// pub fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>)
// where
// T::Event: std::fmt::Debug,
// {
// self.add_panel_with_extra_event_handler(panel, cx, |_, _, _, _| {})
// }
pub fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>) {
let dock = match panel.position(cx) {
DockPosition::Left => &self.left_dock,
DockPosition::Bottom => &self.bottom_dock,
DockPosition::Right => &self.right_dock,
};
// pub fn add_panel_with_extra_event_handler<T: Panel, F>(
// &mut self,
// panel: View<T>,
// cx: &mut ViewContext<Self>,
// handler: F,
// ) where
// T::Event: std::fmt::Debug,
// F: Fn(&mut Self, &View<T>, &T::Event, &mut ViewContext<Self>) + 'static,
// {
// let dock = match panel.position(cx) {
// DockPosition::Left => &self.left_dock,
// DockPosition::Bottom => &self.bottom_dock,
// DockPosition::Right => &self.right_dock,
// };
// self.subscriptions.push(cx.subscribe(&panel, {
// let mut dock = dock.clone();
// let mut prev_position = panel.position(cx);
// move |this, panel, event, cx| {
// if T::should_change_position_on_event(event) {
// THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION
// See: Dock::add_panel
//
// let new_position = panel.read(cx).position(cx);
// let mut was_visible = false;
// dock.update(cx, |dock, cx| {
// prev_position = new_position;
// was_visible = dock.is_open()
// && dock
// .visible_panel()
// .map_or(false, |active_panel| active_panel.id() == panel.id());
// dock.remove_panel(&panel, cx);
// });
// if panel.is_zoomed(cx) {
// this.zoomed_position = Some(new_position);
// }
// dock = match panel.read(cx).position(cx) {
// DockPosition::Left => &this.left_dock,
// DockPosition::Bottom => &this.bottom_dock,
// DockPosition::Right => &this.right_dock,
// }
// .clone();
// dock.update(cx, |dock, cx| {
// dock.add_panel(panel.clone(), cx);
// if was_visible {
// dock.set_open(true, cx);
// dock.activate_panel(dock.panels_len() - 1, cx);
// }
// });
// } else if T::should_zoom_in_on_event(event) {
// THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION
// See: Dock::add_panel
//
// dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
// if !panel.has_focus(cx) {
// cx.focus(&panel);
// }
// this.zoomed = Some(panel.downgrade().into_any());
// this.zoomed_position = Some(panel.read(cx).position(cx));
// } else if T::should_zoom_out_on_event(event) {
// THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION
// See: Dock::add_panel
//
// dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
// if this.zoomed_position == Some(prev_position) {
// this.zoomed = None;
// this.zoomed_position = None;
// }
// cx.notify();
// } else if T::is_focus_event(event) {
// THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION
// See: Dock::add_panel
//
// let position = panel.read(cx).position(cx);
// this.dismiss_zoomed_items_to_reveal(Some(position), cx);
// if panel.is_zoomed(cx) {
// this.zoomed = Some(panel.downgrade().into_any());
// this.zoomed_position = Some(position);
// } else {
// this.zoomed = None;
// this.zoomed_position = None;
// }
// this.update_active_view_for_followers(cx);
// cx.notify();
// } else {
// handler(this, &panel, event, cx)
// }
// }
// }));
// dock.update(cx, |dock, cx| dock.add_panel(panel, cx));
// }
dock.update(cx, |dock, cx| dock.add_panel(panel, cx));
}
pub fn status_bar(&self) -> &View<StatusBar> {
&self.status_bar
@@ -1241,29 +1045,29 @@ impl Workspace {
// self.titlebar_item.clone()
// }
// /// Call the given callback with a workspace whose project is local.
// ///
// /// If the given workspace has a local project, then it will be passed
// /// to the callback. Otherwise, a new empty window will be created.
// pub fn with_local_workspace<T, F>(
// &mut self,
// cx: &mut ViewContext<Self>,
// callback: F,
// ) -> Task<Result<T>>
// where
// T: 'static,
// F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
// {
// if self.project.read(cx).is_local() {
// Task::Ready(Some(Ok(callback(self, cx))))
// } else {
// let task = Self::new_local(Vec::new(), self.app_state.clone(), None, cx);
// cx.spawn(|_vh, mut cx| async move {
// let (workspace, _) = task.await;
// workspace.update(&mut cx, callback)
// })
// }
// }
/// Call the given callback with a workspace whose project is local.
///
/// If the given workspace has a local project, then it will be passed
/// to the callback. Otherwise, a new empty window will be created.
pub fn with_local_workspace<T, F>(
&mut self,
cx: &mut ViewContext<Self>,
callback: F,
) -> Task<Result<T>>
where
T: 'static,
F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
{
if self.project.read(cx).is_local() {
Task::Ready(Some(Ok(callback(self, cx))))
} else {
let task = Self::new_local(Vec::new(), self.app_state.clone(), None, cx);
cx.spawn(|_vh, mut cx| async move {
let (workspace, _) = task.await?;
workspace.update(&mut cx, callback)
})
}
}
pub fn worktrees<'a>(&self, cx: &'a AppContext) -> impl 'a + Iterator<Item = Model<Worktree>> {
self.project.read(cx).worktrees()
@@ -1735,42 +1539,43 @@ impl Workspace {
// }
// }
// pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext<Self>) {
// let dock = match dock_side {
// DockPosition::Left => &self.left_dock,
// DockPosition::Bottom => &self.bottom_dock,
// DockPosition::Right => &self.right_dock,
// };
// let mut focus_center = false;
// let mut reveal_dock = false;
// dock.update(cx, |dock, cx| {
// let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side);
// let was_visible = dock.is_open() && !other_is_zoomed;
// dock.set_open(!was_visible, cx);
pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext<Self>) {
let dock = match dock_side {
DockPosition::Left => &self.left_dock,
DockPosition::Bottom => &self.bottom_dock,
DockPosition::Right => &self.right_dock,
};
let mut focus_center = false;
let mut reveal_dock = false;
dock.update(cx, |dock, cx| {
let other_is_zoomed = self.zoomed.is_some() && self.zoomed_position != Some(dock_side);
let was_visible = dock.is_open() && !other_is_zoomed;
dock.set_open(!was_visible, cx);
// if let Some(active_panel) = dock.active_panel() {
// if was_visible {
// if active_panel.has_focus(cx) {
// focus_center = true;
// }
// } else {
// cx.focus(active_panel.as_any());
// reveal_dock = true;
// }
// }
// });
if let Some(active_panel) = dock.active_panel() {
if was_visible {
if active_panel.has_focus(cx) {
focus_center = true;
}
} else {
let focus_handle = &active_panel.focus_handle(cx);
cx.focus(focus_handle);
reveal_dock = true;
}
}
});
// if reveal_dock {
// self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx);
// }
if reveal_dock {
self.dismiss_zoomed_items_to_reveal(Some(dock_side), cx);
}
// if focus_center {
// cx.focus_self();
// }
if focus_center {
cx.focus(&self.focus_handle);
}
// cx.notify();
// self.serialize_workspace(cx);
// }
cx.notify();
self.serialize_workspace(cx);
}
pub fn close_all_docks(&mut self, cx: &mut ViewContext<Self>) {
let docks = [&self.left_dock, &self.bottom_dock, &self.right_dock];
@@ -1961,50 +1766,50 @@ impl Workspace {
})
}
// pub fn open_abs_path(
// &mut self,
// abs_path: PathBuf,
// visible: bool,
// cx: &mut ViewContext<Self>,
// ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
// cx.spawn(|workspace, mut cx| async move {
// let open_paths_task_result = workspace
// .update(&mut cx, |workspace, cx| {
// workspace.open_paths(vec![abs_path.clone()], visible, cx)
// })
// .with_context(|| format!("open abs path {abs_path:?} task spawn"))?
// .await;
// anyhow::ensure!(
// open_paths_task_result.len() == 1,
// "open abs path {abs_path:?} task returned incorrect number of results"
// );
// match open_paths_task_result
// .into_iter()
// .next()
// .expect("ensured single task result")
// {
// Some(open_result) => {
// open_result.with_context(|| format!("open abs path {abs_path:?} task join"))
// }
// None => anyhow::bail!("open abs path {abs_path:?} task returned None"),
// }
// })
// }
pub fn open_abs_path(
&mut self,
abs_path: PathBuf,
visible: bool,
cx: &mut ViewContext<Self>,
) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
cx.spawn(|workspace, mut cx| async move {
let open_paths_task_result = workspace
.update(&mut cx, |workspace, cx| {
workspace.open_paths(vec![abs_path.clone()], visible, cx)
})
.with_context(|| format!("open abs path {abs_path:?} task spawn"))?
.await;
anyhow::ensure!(
open_paths_task_result.len() == 1,
"open abs path {abs_path:?} task returned incorrect number of results"
);
match open_paths_task_result
.into_iter()
.next()
.expect("ensured single task result")
{
Some(open_result) => {
open_result.with_context(|| format!("open abs path {abs_path:?} task join"))
}
None => anyhow::bail!("open abs path {abs_path:?} task returned None"),
}
})
}
// pub fn split_abs_path(
// &mut self,
// abs_path: PathBuf,
// visible: bool,
// cx: &mut ViewContext<Self>,
// ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
// let project_path_task =
// Workspace::project_path_for_path(self.project.clone(), &abs_path, visible, cx);
// cx.spawn(|this, mut cx| async move {
// let (_, path) = project_path_task.await?;
// this.update(&mut cx, |this, cx| this.split_path(path, cx))?
// .await
// })
// }
pub fn split_abs_path(
&mut self,
abs_path: PathBuf,
visible: bool,
cx: &mut ViewContext<Self>,
) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
let project_path_task =
Workspace::project_path_for_path(self.project.clone(), &abs_path, visible, cx);
cx.spawn(|this, mut cx| async move {
let (_, path) = project_path_task.await?;
this.update(&mut cx, |this, cx| this.split_path(path, cx))?
.await
})
}
pub fn open_path(
&mut self,
@@ -2031,37 +1836,37 @@ impl Workspace {
})
}
// pub fn split_path(
// &mut self,
// path: impl Into<ProjectPath>,
// cx: &mut ViewContext<Self>,
// ) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
// let pane = self.last_active_center_pane.clone().unwrap_or_else(|| {
// self.panes
// .first()
// .expect("There must be an active pane")
// .downgrade()
// });
pub fn split_path(
&mut self,
path: impl Into<ProjectPath>,
cx: &mut ViewContext<Self>,
) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
let pane = self.last_active_center_pane.clone().unwrap_or_else(|| {
self.panes
.first()
.expect("There must be an active pane")
.downgrade()
});
// if let Member::Pane(center_pane) = &self.center.root {
// if center_pane.read(cx).items_len() == 0 {
// return self.open_path(path, Some(pane), true, cx);
// }
// }
if let Member::Pane(center_pane) = &self.center.root {
if center_pane.read(cx).items_len() == 0 {
return self.open_path(path, Some(pane), true, cx);
}
}
// let task = self.load_path(path.into(), cx);
// cx.spawn(|this, mut cx| async move {
// let (project_entry_id, build_item) = task.await?;
// this.update(&mut cx, move |this, cx| -> Option<_> {
// let pane = pane.upgrade(cx)?;
// let new_pane = this.split_pane(pane, SplitDirection::Right, cx);
// new_pane.update(cx, |new_pane, cx| {
// Some(new_pane.open_item(project_entry_id, true, cx, build_item))
// })
// })
// .map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))?
// })
// }
let task = self.load_path(path.into(), cx);
cx.spawn(|this, mut cx| async move {
let (project_entry_id, build_item) = task.await?;
this.update(&mut cx, move |this, cx| -> Option<_> {
let pane = pane.upgrade()?;
let new_pane = this.split_pane(pane, SplitDirection::Right, cx);
new_pane.update(cx, |new_pane, cx| {
Some(new_pane.open_item(project_entry_id, true, cx, build_item))
})
})
.map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))?
})
}
pub(crate) fn load_path(
&mut self,
@@ -2660,17 +2465,50 @@ impl Workspace {
h_stack()
// TODO - Add player menu
.child(
Button::new("player")
.variant(ButtonVariant::Ghost)
.color(Some(LabelColor::Player(0))),
div()
.id("project_owner_indicator")
.child(
Button::new("player")
.variant(ButtonVariant::Ghost)
.color(Some(TextColor::Player(0))),
)
.tooltip(move |_, cx| {
cx.build_view(|cx| TextTooltip::new("Toggle following"))
}),
)
// TODO - Add project menu
.child(Button::new("project_name").variant(ButtonVariant::Ghost))
.child(
div()
.id("titlebar_project_menu_button")
.child(Button::new("project_name").variant(ButtonVariant::Ghost))
.tooltip(move |_, cx| {
cx.build_view(|cx| TextTooltip::new("Recent Projects"))
}),
)
// TODO - Add git menu
.child(
Button::new("branch_name")
.variant(ButtonVariant::Ghost)
.color(Some(LabelColor::Muted)),
div()
.id("titlebar_git_menu_button")
.child(
Button::new("branch_name")
.variant(ButtonVariant::Ghost)
.color(Some(TextColor::Muted)),
)
.tooltip(move |_, cx| {
// todo!() Replace with real action.
#[gpui::action]
struct NoAction {}
cx.build_view(|cx| {
TextTooltip::new("Recent Branches")
.key_binding(KeyBinding::new(gpui::KeyBinding::new(
"cmd-b",
NoAction {},
None,
)))
.meta("Only local branches shown")
})
}),
),
) // self.titlebar_item
.child(h_stack().child(Label::new("Right side titlebar item")))
@@ -3192,10 +3030,10 @@ impl Workspace {
fn force_remove_pane(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Workspace>) {
self.panes.retain(|p| p != pane);
if true {
todo!()
// cx.focus(self.panes.last().unwrap());
}
self.panes
.last()
.unwrap()
.update(cx, |pane, cx| pane.focus(cx));
if self.last_active_center_pane == Some(pane.downgrade()) {
self.last_active_center_pane = None;
}
@@ -3467,9 +3305,105 @@ impl Workspace {
})
}
fn actions(div: Div<Self>) -> Div<Self> {
div
// cx.add_async_action(Workspace::open);
// cx.add_async_action(Workspace::follow_next_collaborator);
// cx.add_async_action(Workspace::close);
// cx.add_async_action(Workspace::close_inactive_items_and_panes);
// cx.add_async_action(Workspace::close_all_items_and_panes);
// cx.add_global_action(Workspace::close_global);
// cx.add_global_action(restart);
// cx.add_async_action(Workspace::save_all);
// cx.add_action(Workspace::add_folder_to_project);
// cx.add_action(
// |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext<Workspace>| {
// let pane = workspace.active_pane().clone();
// workspace.unfollow(&pane, cx);
// },
// );
// cx.add_action(
// |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext<Workspace>| {
// workspace
// .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx)
// .detach_and_log_err(cx);
// },
// );
// cx.add_action(
// |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext<Workspace>| {
// workspace
// .save_active_item(SaveIntent::SaveAs, cx)
// .detach_and_log_err(cx);
// },
// );
// cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
// workspace.activate_previous_pane(cx)
// });
// cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
// workspace.activate_next_pane(cx)
// });
// cx.add_action(
// |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| {
// workspace.activate_pane_in_direction(action.0, cx)
// },
// );
// cx.add_action(
// |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| {
// workspace.swap_pane_in_direction(action.0, cx)
// },
// );
.on_action(|this, e: &ToggleLeftDock, cx| {
println!("TOGGLING DOCK");
this.toggle_dock(DockPosition::Left, cx);
})
// cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| {
// workspace.toggle_dock(DockPosition::Right, cx);
// });
// cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| {
// workspace.toggle_dock(DockPosition::Bottom, cx);
// });
// cx.add_action(|workspace: &mut Workspace, _: &CloseAllDocks, cx| {
// workspace.close_all_docks(cx);
// });
// cx.add_action(Workspace::activate_pane_at_index);
// cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
// workspace.reopen_closed_item(cx).detach();
// });
// cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| {
// workspace
// .go_back(workspace.active_pane().downgrade(), cx)
// .detach();
// });
// cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| {
// workspace
// .go_forward(workspace.active_pane().downgrade(), cx)
// .detach();
// });
// cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| {
// cx.spawn(|workspace, mut cx| async move {
// let err = install_cli::install_cli(&cx)
// .await
// .context("Failed to create CLI symlink");
// workspace.update(&mut cx, |workspace, cx| {
// if matches!(err, Err(_)) {
// err.notify_err(workspace, cx);
// } else {
// workspace.show_notification(1, cx, |cx| {
// cx.build_view(|_| {
// MessageNotification::new("Successfully installed the `zed` binary")
// })
// });
// }
// })
// })
// .detach();
// });
}
#[cfg(any(test, feature = "test-support"))]
pub fn test_new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self {
use gpui::Context;
use node_runtime::FakeNodeRuntime;
let client = project.read(cx).client();
@@ -3486,7 +3420,9 @@ impl Workspace {
initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
node_runtime: FakeNodeRuntime::new(),
});
Self::new(0, project, app_state, cx)
let workspace = Self::new(0, project, app_state, cx);
workspace.active_pane.update(cx, |pane, cx| pane.focus(cx));
workspace
}
// fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> {
@@ -3522,25 +3458,27 @@ impl Workspace {
pub fn register_action<A: Action>(
&mut self,
callback: impl Fn(&mut Self, &A, &mut ViewContext<Self>) + 'static,
) {
) -> &mut Self {
let callback = Arc::new(callback);
self.workspace_actions.push(Box::new(move |div| {
let callback = callback.clone();
div.on_action(move |workspace, event, cx| (callback.clone())(workspace, event, cx))
}));
self
}
fn add_workspace_actions_listeners(
&self,
mut div: Div<Workspace, StatelessInteractivity<Workspace>>,
) -> Div<Workspace, StatelessInteractivity<Workspace>> {
fn add_workspace_actions_listeners(&self, mut div: Div<Workspace>) -> Div<Workspace> {
for action in self.workspace_actions.iter() {
div = (action)(div)
}
div
}
pub fn current_modal<V: Modal + 'static>(&mut self, cx: &ViewContext<Self>) -> Option<View<V>> {
self.modal_layer.read(cx).current_modal()
}
pub fn toggle_modal<V: Modal, B>(&mut self, cx: &mut ViewContext<Self>, build: B)
where
B: FnOnce(&mut ViewContext<V>) -> V,
@@ -3764,14 +3702,15 @@ impl Render for Workspace {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let mut context = KeyContext::default();
context.add("Workspace");
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
self.add_workspace_actions_listeners(div())
.context(context)
.key_context(context)
.relative()
.size_full()
.flex()
.flex_col()
.font("Zed Sans")
.font(ui_font)
.gap_0()
.justify_start()
.items_start()
@@ -3791,83 +3730,46 @@ impl Render for Workspace {
.border_b()
.border_color(cx.theme().colors().border)
.child(self.modal_layer.clone())
// .children(
// Some(
// Panel::new("project-panel-outer", cx)
// .side(PanelSide::Left)
// .child(ProjectPanel::new("project-panel-inner")),
// )
// .filter(|_| self.is_project_panel_open()),
// )
// .children(
// Some(
// Panel::new("collab-panel-outer", cx)
// .child(CollabPanel::new("collab-panel-inner"))
// .side(PanelSide::Left),
// )
// .filter(|_| self.is_collab_panel_open()),
// )
// .child(NotificationToast::new(
// "maxbrunsfeld has requested to add you as a contact.".into(),
// ))
.child(
div().flex().flex_col().flex_1().h_full().child(
div().flex().flex_1().child(self.center.render(
&self.project,
&self.follower_states,
self.active_call(),
&self.active_pane,
self.zoomed.as_ref(),
&self.app_state,
cx,
)),
), // .children(
// Some(
// Panel::new("terminal-panel", cx)
// .child(Terminal::new())
// .allowed_sides(PanelAllowedSides::BottomOnly)
// .side(PanelSide::Bottom),
// )
// .filter(|_| self.is_terminal_open()),
// ),
), // .children(
// Some(
// Panel::new("chat-panel-outer", cx)
// .side(PanelSide::Right)
// .child(ChatPanel::new("chat-panel-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(),
// ),
// ])),
// )
// .filter(|_| self.is_chat_panel_open()),
// )
// .children(
// Some(
// Panel::new("notifications-panel-outer", cx)
// .side(PanelSide::Right)
// .child(NotificationsPanel::new("notifications-panel-inner")),
// )
// .filter(|_| self.is_notifications_panel_open()),
// )
// .children(
// Some(
// Panel::new("assistant-panel-outer", cx)
// .child(AssistantPanel::new("assistant-panel-inner")),
// )
// .filter(|_| self.is_assistant_panel_open()),
// ),
div()
.flex()
.flex_row()
.flex_1()
.h_full()
// Left Dock
.child(
div()
.flex()
.flex_none()
.overflow_hidden()
.child(self.left_dock.clone()),
)
// Panes
.child(
div()
.flex()
.flex_col()
.flex_1()
.child(self.center.render(
&self.project,
&self.follower_states,
self.active_call(),
&self.active_pane,
self.zoomed.as_ref(),
&self.app_state,
cx,
))
.child(div().flex().flex_1().child(self.bottom_dock.clone())),
)
// Right Dock
.child(
div()
.flex()
.flex_none()
.overflow_hidden()
.child(self.right_dock.clone()),
),
),
)
.child(self.status_bar.clone())
// .when(self.debug.show_toast, |this| {
@@ -4522,32 +4424,32 @@ pub fn open_new(
})
}
// pub fn create_and_open_local_file(
// path: &'static Path,
// cx: &mut ViewContext<Workspace>,
// default_content: impl 'static + Send + FnOnce() -> Rope,
// ) -> Task<Result<Box<dyn ItemHandle>>> {
// cx.spawn(|workspace, mut cx| async move {
// let fs = workspace.read_with(&cx, |workspace, _| workspace.app_state().fs.clone())?;
// if !fs.is_file(path).await {
// fs.create_file(path, Default::default()).await?;
// fs.save(path, &default_content(), Default::default())
// .await?;
// }
pub fn create_and_open_local_file(
path: &'static Path,
cx: &mut ViewContext<Workspace>,
default_content: impl 'static + Send + FnOnce() -> Rope,
) -> Task<Result<Box<dyn ItemHandle>>> {
cx.spawn(|workspace, mut cx| async move {
let fs = workspace.update(&mut cx, |workspace, _| workspace.app_state().fs.clone())?;
if !fs.is_file(path).await {
fs.create_file(path, Default::default()).await?;
fs.save(path, &default_content(), Default::default())
.await?;
}
// let mut items = workspace
// .update(&mut cx, |workspace, cx| {
// workspace.with_local_workspace(cx, |workspace, cx| {
// workspace.open_paths(vec![path.to_path_buf()], false, cx)
// })
// })?
// .await?
// .await;
let mut items = workspace
.update(&mut cx, |workspace, cx| {
workspace.with_local_workspace(cx, |workspace, cx| {
workspace.open_paths(vec![path.to_path_buf()], false, cx)
})
})?
.await?
.await;
// let item = items.pop().flatten();
// item.ok_or_else(|| anyhow!("path {path:?} is not a file"))?
// })
// }
let item = items.pop().flatten();
item.ok_or_else(|| anyhow!("path {path:?} is not a file"))?
})
}
// pub fn join_remote_project(
// project_id: u64,
+3 -3
View File
@@ -36,14 +36,14 @@ copilot = { package = "copilot2", path = "../copilot2" }
db = { package = "db2", path = "../db2" }
editor = { package="editor2", path = "../editor2" }
# feedback = { path = "../feedback" }
# file_finder = { path = "../file_finder" }
file_finder = { package="file_finder2", path = "../file_finder2" }
# search = { path = "../search" }
fs = { package = "fs2", path = "../fs2" }
fsevent = { path = "../fsevent" }
fuzzy = { path = "../fuzzy" }
go_to_line = { package = "go_to_line2", path = "../go_to_line2" }
gpui = { package = "gpui2", path = "../gpui2" }
install_cli = { path = "../install_cli" }
install_cli = { package = "install_cli2", path = "../install_cli2" }
journal = { package = "journal2", path = "../journal2" }
language = { package = "language2", path = "../language2" }
# language_selector = { path = "../language_selector" }
@@ -55,7 +55,7 @@ node_runtime = { path = "../node_runtime" }
# outline = { path = "../outline" }
# plugin_runtime = { path = "../plugin_runtime",optional = true }
project = { package = "project2", path = "../project2" }
# project_panel = { path = "../project_panel" }
project_panel = { package = "project_panel2", path = "../project_panel2" }
# project_symbols = { path = "../project_symbols" }
# quick_action_bar = { path = "../quick_action_bar" }
# recent_projects = { path = "../recent_projects" }
+12 -7
View File
@@ -50,14 +50,16 @@ use util::{
use uuid::Uuid;
use workspace::{AppState, WorkspaceStore};
use zed2::{
build_window_options, ensure_only_instance, handle_cli_connection, initialize_workspace,
languages, Assets, IsOnlyInstance, OpenListener, OpenRequest,
build_window_options, ensure_only_instance, handle_cli_connection, init_zed_actions,
initialize_workspace, languages, Assets, IsOnlyInstance, OpenListener, OpenRequest,
};
mod open_listener;
fn main() {
menu::init();
zed_actions::init();
let http = http::client();
init_paths();
init_logger();
@@ -96,7 +98,7 @@ fn main() {
let (listener, mut open_rx) = OpenListener::new();
let listener = Arc::new(listener);
let open_listener = listener.clone();
app.on_open_urls(move |urls, _| open_listener.open_urls(urls));
app.on_open_urls(move |urls, _| open_listener.open_urls(&urls));
app.on_reopen(move |_cx| {
// todo!("workspace")
// if cx.has_global::<Weak<AppState>>() {
@@ -111,6 +113,8 @@ fn main() {
app.run(move |cx| {
cx.set_global(*RELEASE_CHANNEL);
cx.set_global(listener.clone());
load_embedded_fonts(cx);
let mut store = SettingsStore::default();
@@ -186,10 +190,10 @@ fn main() {
// recent_projects::init(cx);
go_to_line::init(cx);
// file_finder::init(cx);
file_finder::init(cx);
// outline::init(cx);
// project_symbols::init(cx);
// project_panel::init(Assets, cx);
project_panel::init(Assets, cx);
// channel::init(&client, user_store.clone(), cx);
// diagnostics::init(cx);
// search::init(cx);
@@ -209,12 +213,13 @@ fn main() {
// zed::init(&app_state, cx);
// cx.set_menus(menus::menus());
init_zed_actions(app_state.clone(), cx);
if stdout_is_a_pty() {
cx.activate(true);
let urls = collect_url_args();
if !urls.is_empty() {
listener.open_urls(urls)
listener.open_urls(&urls)
}
} else {
upload_previous_panics(http.clone(), cx);
@@ -224,7 +229,7 @@ fn main() {
if std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_some()
&& !listener.triggered.load(Ordering::Acquire)
{
listener.open_urls(collect_url_args())
listener.open_urls(&collect_url_args())
}
}

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