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:
Generated
+59
-1
@@ -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",
|
||||
|
||||
@@ -80,6 +80,7 @@ members = [
|
||||
"crates/project",
|
||||
"crates/project2",
|
||||
"crates/project_panel",
|
||||
"crates/project_panel2",
|
||||
"crates/project_symbols",
|
||||
"crates/recent_projects",
|
||||
"crates/rope",
|
||||
|
||||
@@ -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,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
@@ -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);
|
||||
});
|
||||
});
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
)
|
||||
})),
|
||||
|
||||
@@ -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
@@ -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)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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.,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+1334
-270
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+14
-982
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ impl KeystrokeMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum KeyMatch {
|
||||
None,
|
||||
Pending,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
pub use crate::{Context, ParentElement, Refineable};
|
||||
pub use crate::{
|
||||
BorrowAppContext, BorrowWindow, Component, Context, FocusableComponent, InteractiveComponent,
|
||||
ParentComponent, Refineable, Render, StatefulInteractiveComponent, Styled, VisualContext,
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
|
||||
fn shadow_md(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn shadow_md(mut self) -> Self {
|
||||
self.style().box_shadow = Some(smallvec
|
||||
fn shadow_lg(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn shadow_lg(mut self) -> Self {
|
||||
self.style().box_shadow = Some(smallvec
|
||||
fn shadow_xl(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn shadow_xl(mut self) -> Self {
|
||||
self.style().box_shadow = Some(smallvec
|
||||
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());
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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> {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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"))??;
|
||||
|
||||
@@ -167,7 +167,7 @@ fn main() {
|
||||
panic!("unexpected message");
|
||||
}
|
||||
|
||||
cx.update(|cx| cx.quit()).ok();
|
||||
cx.update(|cx| cx.shutdown()).ok();
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
+1525
-1513
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)
|
||||
}
|
||||
}
|
||||
@@ -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,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::*;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
use gpui::{div, Component, Div, ParentElement, Styled, ViewContext};
|
||||
use gpui::{div, Component, Div, ParentComponent, Styled, ViewContext};
|
||||
|
||||
use crate::ActiveTheme;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"))),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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>);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}),
|
||||
|
||||
@@ -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())))
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user