Simplify element types (#3318)

This PR does away with the extra type parameters on Div and instead
introduces two wrapper elements, `Stateful` and `Focusable`. All of the
interactivity is stored on `Interactivity` and `InteractiveState`, which
is stored on the base element. The wrappers simply control what methods
are available to call.

Not sure this is fully working, but a smoke test does work.

/cc @as-cii @ConradIrwin 

Release Notes:

- N/A
This commit is contained in:
Mikayla Maki 2023-11-14 16:03:20 -08:00 committed by GitHub
commit 96f0257fb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 2158 additions and 2465 deletions

View File

@ -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::{

View File

@ -39,12 +39,12 @@ use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display;
use gpui::{
action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext,
AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
action, actions, div, point, prelude::*, px, relative, rems, size, uniform_list, AnyElement,
AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
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,
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};
@ -9413,14 +9413,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(),
},
)
}
}

View File

@ -20,9 +20,9 @@ use collections::{BTreeMap, HashMap};
use gpui::{
point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, BorrowWindow,
Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element, ElementId,
ElementInputHandler, Entity, Hsla, Line, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, ParentElement, Pixels, ScrollWheelEvent, Size, Style, Styled, TextRun, TextStyle,
ViewContext, WindowContext,
ElementInputHandler, Entity, EntityId, Hsla, Line, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, ParentComponent, Pixels, ScrollWheelEvent, Size, Style, Styled, TextRun,
TextStyle, View, ViewContext, WindowContext,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@ -111,12 +111,16 @@ impl SelectionLayout {
}
pub struct EditorElement {
editor_id: EntityId,
style: EditorStyle,
}
impl EditorElement {
pub fn new(style: EditorStyle) -> Self {
Self { style }
pub fn new(editor: &View<Editor>, style: EditorStyle) -> Self {
Self {
editor_id: editor.entity_id(),
style,
}
}
fn mouse_down(
@ -622,7 +626,7 @@ impl EditorElement {
let line_end_overshoot = 0.15 * layout.position_map.line_height;
let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
cx.with_content_mask(ContentMask { bounds }, |cx| {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
// todo!("cursor region")
// cx.scene().push_cursor_region(CursorRegion {
// bounds,
@ -2404,8 +2408,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(
@ -2415,178 +2419,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));
});
// on_action(cx, Editor::rename); todo!()
// on_action(cx, Editor::confirm_rename); todo!()
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(
@ -2623,32 +2455,200 @@ 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));
});
// on_action(cx, Editor::rename); todo!()
// on_action(cx, Editor::confirm_rename); todo!()
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);
});
});
},
)
}
}

View File

@ -9,7 +9,7 @@ use collections::HashSet;
use futures::future::try_join_all;
use gpui::{
div, point, AnyElement, AppContext, AsyncAppContext, Entity, EntityId, EventEmitter,
FocusHandle, Model, ParentElement, Pixels, SharedString, Styled, Subscription, Task, View,
FocusHandle, Model, ParentComponent, Pixels, SharedString, Styled, Subscription, Task, View,
ViewContext, VisualContext, WeakView,
};
use language::{

View File

@ -2,8 +2,9 @@ use collections::HashMap;
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
use gpui::{
actions, div, AppContext, Component, Div, EventEmitter, Model, ParentElement, Render,
StatelessInteractive, Styled, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
actions, div, AppContext, Component, Div, EventEmitter, InteractiveComponent, Model,
ParentComponent, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
WindowContext,
};
use picker::{Picker, PickerDelegate};
use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
@ -32,9 +33,7 @@ pub fn init(cx: &mut AppContext) {
impl FileFinder {
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
dbg!("REGISTERING");
workspace.register_action(|workspace, _: &Toggle, cx| {
dbg!("CALLING ACTION");
let Some(file_finder) = workspace.current_modal::<Self>(cx) else {
Self::open(workspace, cx);
return;
@ -593,7 +592,6 @@ impl PickerDelegate for FileFinderDelegate {
}
fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
dbg!("CONFIRMING???");
if let Some(m) = self.matches.get(self.selected_index()) {
if let Some(workspace) = self.workspace.upgrade() {
let open_task = workspace.update(cx, move |workspace, cx| {
@ -691,7 +689,6 @@ impl PickerDelegate for FileFinderDelegate {
.log_err();
}
}
dbg!("DISMISSING");
finder
.update(&mut cx, |_, cx| cx.emit(ModalEvent::Dismissed))
.ok()?;

View File

@ -1,7 +1,7 @@
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;
@ -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()

View File

View File

@ -0,0 +1,101 @@
# Key Dispatch
GPUI is designed for keyboard-first interactivity.
To expose functionality to the mouse, you render a button with a click handler.
To expose functionality to the keyboard, you bind an *action* in a *key context*.
Actions are similar to framework-level events like `MouseDown`, `KeyDown`, etc, but you can define them yourself:
```rust
mod menu {
#[gpui::action]
struct MoveUp;
#[gpui::action]
struct MoveDown;
}
```
Actions are frequently unit structs, for which we have a macro. The above could also be written:
```rust
mod menu {
actions!(MoveUp, MoveDown);
}
```
Actions can also be more complex types:
```rust
mod menu {
#[gpui::action]
struct Move {
direction: Direction,
select: bool,
}
}
```
To bind actions, chain `on_action` on to your element:
```rust
impl Render for Menu {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
div()
.on_action(|this: &mut Menu, move: &MoveUp, cx: &mut ViewContext<Menu>| {
// ...
})
.on_action(|this, move: &MoveDown, cx| {
// ...
})
.children(todo!())
}
}
```
In order to bind keys to actions, you need to declare a *key context* for part of the element tree by calling `key_context`.
```rust
impl Render for Menu {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
div()
.key_context("menu")
.on_action(|this: &mut Menu, move: &MoveUp, cx: &mut ViewContext<Menu>| {
// ...
})
.on_action(|this, move: &MoveDown, cx| {
// ...
})
.children(todo!())
}
}
```
Now you can target your context in the keymap. Note how actions are identified in the keymap by their fully-qualified type name.
```json
{
"context": "menu",
"bindings": {
"up": "menu::MoveUp",
"down": "menu::MoveDown"
}
}
```
If you had opted for the more complex type definition, you'd provide the serialized representation of the action alongside the name:
```json
{
"context": "menu",
"bindings": {
"up": ["menu::Move", {direction: "up", select: false}]
"down": ["menu::Move", {direction: "down", select: false}]
"shift-up": ["menu::Move", {direction: "up", select: true}]
"shift-down": ["menu::Move", {direction: "down", select: true}]
}
}
```

View File

@ -1114,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>,
}

View File

@ -8,7 +8,7 @@ use std::{any::Any, mem};
pub trait Element<V: 'static> {
type ElementState: 'static;
fn id(&self) -> Option<ElementId>;
fn element_id(&self) -> Option<ElementId>;
/// Called to initialize this element for the current frame. If this
/// element had state in a previous frame, it will be passed in for the 3rd argument.
@ -38,7 +38,7 @@ pub trait Element<V: 'static> {
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
pub trait ParentElement<V: 'static> {
pub trait ParentComponent<V: 'static> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
fn child(mut self, child: impl Component<V>) -> Self
@ -120,7 +120,7 @@ where
E::ElementState: 'static,
{
fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
let frame_state = if let Some(id) = self.element.id() {
let frame_state = if let Some(id) = self.element.element_id() {
cx.with_element_state(id, |element_state, cx| {
let element_state = self.element.initialize(view_state, element_state, cx);
((), element_state)
@ -142,7 +142,7 @@ where
frame_state: initial_frame_state,
} => {
frame_state = initial_frame_state;
if let Some(id) = self.element.id() {
if let Some(id) = self.element.element_id() {
layout_id = cx.with_element_state(id, |element_state, cx| {
let mut element_state = element_state.unwrap();
let layout_id = self.element.layout(state, &mut element_state, cx);
@ -181,7 +181,7 @@ where
..
} => {
let bounds = cx.layout_bounds(layout_id);
if let Some(id) = self.element.id() {
if let Some(id) = self.element.element_id() {
cx.with_element_state(id, |element_state, cx| {
let mut element_state = element_state.unwrap();
self.element
@ -255,7 +255,7 @@ where
// Ignore the element offset when drawing this element, as the origin is already specified
// in absolute terms.
origin -= cx.element_offset();
cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx))
cx.with_element_offset(origin, |cx| self.paint(view_state, cx))
}
}
@ -351,7 +351,7 @@ where
{
type ElementState = AnyElement<V>;
fn id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<ElementId> {
None
}

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +1,28 @@
use crate::{
div, AnyElement, BorrowWindow, Bounds, Component, Div, DivState, Element, ElementId,
ElementInteractivity, FocusListeners, Focusable, FocusableKeyDispatch, KeyDispatch, LayoutId,
NonFocusableKeyDispatch, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
AnyElement, BorrowWindow, Bounds, Component, Element, InteractiveComponent,
InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
Styled, ViewContext,
};
use futures::FutureExt;
use util::ResultExt;
pub struct Img<
V: 'static,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: KeyDispatch<V> = NonFocusableKeyDispatch,
> {
base: Div<V, I, F>,
pub struct Img<V: 'static> {
interactivity: Interactivity<V>,
uri: Option<SharedString>,
grayscale: bool,
}
pub fn img<V: 'static>() -> Img<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
pub fn img<V: 'static>() -> Img<V> {
Img {
base: div(),
interactivity: Interactivity::default(),
uri: None,
grayscale: false,
}
}
impl<V, I, F> Img<V, I, F>
impl<V> Img<V>
where
V: 'static,
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
self.uri = Some(uri.into());
@ -42,145 +35,90 @@ where
}
}
impl<V, F> Img<V, StatelessInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Img<V, StatefulInteractivity<V>, F> {
Img {
base: self.base.id(id),
uri: self.uri,
grayscale: self.grayscale,
}
}
}
impl<V, I, F> Component<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Component<V> for Img<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
impl<V, I, F> Element<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
type ElementState = DivState;
impl<V> Element<V> for Img<V> {
type ElementState = InteractiveElementState;
fn id(&self) -> Option<crate::ElementId> {
self.base.id()
fn element_id(&self) -> Option<crate::ElementId> {
self.interactivity.element_id.clone()
}
fn initialize(
&mut self,
view_state: &mut V,
_view_state: &mut V,
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
self.base.initialize(view_state, element_state, cx)
self.interactivity.initialize(element_state, cx)
}
fn layout(
&mut self,
view_state: &mut V,
_view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> LayoutId {
self.base.layout(view_state, element_state, cx)
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
view: &mut V,
_view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
cx.with_z_index(0, |cx| {
self.base.paint(bounds, view, element_state, cx);
});
self.interactivity.paint(
bounds,
bounds.size,
element_state,
cx,
|style, _scroll_offset, cx| {
let corner_radii = style.corner_radii;
let style = self.base.compute_style(bounds, element_state, cx);
let corner_radii = style.corner_radii;
if let Some(uri) = self.uri.clone() {
// eprintln!(">>> image_cache.get({uri}");
let image_future = cx.image_cache.get(uri.clone());
// eprintln!("<<< image_cache.get({uri}");
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
} else {
cx.spawn(|_, mut cx| async move {
if image_future.await.log_err().is_some() {
cx.on_next_frame(|cx| cx.notify());
if let Some(uri) = self.uri.clone() {
// eprintln!(">>> image_cache.get({uri}");
let image_future = cx.image_cache.get(uri.clone());
// eprintln!("<<< image_cache.get({uri}");
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
} else {
cx.spawn(|_, mut cx| async move {
if image_future.await.log_err().is_some() {
cx.on_next_frame(|cx| cx.notify());
}
})
.detach()
}
})
.detach()
}
}
}
},
)
}
}
impl<V, I, F> Styled for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Styled for Img<V> {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
&mut self.interactivity.base_style
}
}
impl<V, I, F> StatelessInteractive<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Img<V, StatefulInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V, I> Focusable<V> for Img<V, I, FocusableKeyDispatch<V>>
where
V: 'static,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()
}
fn set_focus_style(&mut self, style: StyleRefinement) {
self.base.set_focus_style(style)
}
fn set_focus_in_style(&mut self, style: StyleRefinement) {
self.base.set_focus_in_style(style)
}
fn set_in_focus_style(&mut self, style: StyleRefinement) {
self.base.set_in_focus_style(style)
impl<V> InteractiveComponent<V> for Img<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
}

View File

@ -1,157 +1,88 @@
use crate::{
div, AnyElement, Bounds, Component, Div, DivState, Element, ElementId, ElementInteractivity,
FocusListeners, Focusable, FocusableKeyDispatch, KeyDispatch, LayoutId,
NonFocusableKeyDispatch, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
AnyElement, Bounds, Component, Element, ElementId, InteractiveComponent,
InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
Styled, ViewContext,
};
use util::ResultExt;
pub struct Svg<
V: 'static,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: KeyDispatch<V> = NonFocusableKeyDispatch,
> {
base: Div<V, I, F>,
pub struct Svg<V: 'static> {
interactivity: Interactivity<V>,
path: Option<SharedString>,
}
pub fn svg<V: 'static>() -> Svg<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
pub fn svg<V: 'static>() -> Svg<V> {
Svg {
base: div(),
interactivity: Interactivity::default(),
path: None,
}
}
impl<V, I, F> Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Svg<V> {
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
self.path = Some(path.into());
self
}
}
impl<V, F> Svg<V, StatelessInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Svg<V, StatefulInteractivity<V>, F> {
Svg {
base: self.base.id(id),
path: self.path,
}
}
}
impl<V, I, F> Component<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Component<V> for Svg<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
impl<V, I, F> Element<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
type ElementState = DivState;
impl<V> Element<V> for Svg<V> {
type ElementState = InteractiveElementState;
fn id(&self) -> Option<crate::ElementId> {
self.base.id()
fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone()
}
fn initialize(
&mut self,
view_state: &mut V,
_view_state: &mut V,
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
self.base.initialize(view_state, element_state, cx)
self.interactivity.initialize(element_state, cx)
}
fn layout(
&mut self,
view_state: &mut V,
_view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> LayoutId {
self.base.layout(view_state, element_state, cx)
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
view: &mut V,
_view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) where
Self: Sized,
{
self.base.paint(bounds, view, element_state, cx);
let color = self
.base
.compute_style(bounds, element_state, cx)
.text
.color;
if let Some((path, color)) = self.path.as_ref().zip(color) {
cx.paint_svg(bounds, path.clone(), color).log_err();
}
self.interactivity
.paint(bounds, bounds.size, element_state, cx, |style, _, cx| {
if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
cx.paint_svg(bounds, path.clone(), color).log_err();
}
})
}
}
impl<V, I, F> Styled for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Styled for Svg<V> {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
&mut self.interactivity.base_style
}
}
impl<V, I, F> StatelessInteractive<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Svg<V, StatefulInteractivity<V>, F>
where
V: 'static,
F: KeyDispatch<V>,
{
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V: 'static, I> Focusable<V> for Svg<V, I, FocusableKeyDispatch<V>>
where
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()
}
fn set_focus_style(&mut self, style: StyleRefinement) {
self.base.set_focus_style(style)
}
fn set_focus_in_style(&mut self, style: StyleRefinement) {
self.base.set_focus_in_style(style)
}
fn set_in_focus_style(&mut self, style: StyleRefinement) {
self.base.set_in_focus_style(style)
impl<V> InteractiveComponent<V> for Svg<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
}

View File

@ -72,7 +72,7 @@ impl<V: 'static> Component<V> for Text<V> {
impl<V: 'static> Element<V> for Text<V> {
type ElementState = Arc<Mutex<Option<TextElementState>>>;
fn id(&self) -> Option<crate::ElementId> {
fn element_id(&self) -> Option<crate::ElementId> {
None
}

View File

@ -1,24 +1,23 @@
use crate::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element,
ElementId, ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
StyleRefinement, Styled, ViewContext,
ElementId, InteractiveComponent, InteractiveElementState, Interactivity, LayoutId, Pixels,
Point, Size, StyleRefinement, Styled, ViewContext,
};
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{cmp, ops::Range, sync::Arc};
use std::{cmp, mem, ops::Range, sync::Arc};
use taffy::style::Overflow;
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
/// uniform_list will only render the visibile subset of items.
pub fn uniform_list<Id, V, C>(
id: Id,
pub fn uniform_list<I, V, C>(
id: I,
item_count: usize,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> 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
}
}

View File

@ -156,7 +156,7 @@ pub enum GlobalKey {
}
pub trait BorrowAppContext {
fn with_text_style<F, R>(&mut self, style: TextStyleRefinement, f: F) -> R
fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
where
F: FnOnce(&mut Self) -> R;
@ -167,14 +167,18 @@ impl<C> BorrowAppContext for C
where
C: BorrowMut<AppContext>,
{
fn with_text_style<F, R>(&mut self, style: TextStyleRefinement, f: F) -> R
fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
self.borrow_mut().push_text_style(style);
let result = f(self);
self.borrow_mut().pop_text_style();
result
if let Some(style) = style {
self.borrow_mut().push_text_style(style);
let result = f(self);
self.borrow_mut().pop_text_style();
result
} else {
f(self)
}
}
fn set_global<G: 'static>(&mut self, global: G) {

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,9 @@
use crate::{
build_action_from_type, Action, Bounds, DispatchPhase, Element, FocusEvent, FocusHandle,
FocusId, KeyBinding, KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent,
Pixels, Style, StyleRefinement, ViewContext, WindowContext,
build_action_from_type, Action, DispatchPhase, FocusId, KeyBinding, KeyContext, KeyMatch,
Keymap, Keystroke, KeystrokeMatcher, WindowContext,
};
use collections::HashMap;
use parking_lot::Mutex;
use refineable::Refineable;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
@ -14,10 +12,6 @@ use std::{
};
use util::ResultExt;
pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
pub type FocusListener<V> =
Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct DispatchNodeId(usize);
@ -208,258 +202,3 @@ impl DispatchTree {
*self.node_stack.last().unwrap()
}
}
pub trait KeyDispatch<V: 'static>: 'static {
fn as_focusable(&self) -> Option<&FocusableKeyDispatch<V>>;
fn as_focusable_mut(&mut self) -> Option<&mut FocusableKeyDispatch<V>>;
fn key_context(&self) -> &KeyContext;
fn key_context_mut(&mut self) -> &mut KeyContext;
fn initialize<R>(
&mut self,
focus_handle: Option<FocusHandle>,
cx: &mut ViewContext<V>,
f: impl FnOnce(Option<FocusHandle>, &mut ViewContext<V>) -> R,
) -> R {
let focus_handle = if let Some(focusable) = self.as_focusable_mut() {
let focus_handle = focusable
.focus_handle
.get_or_insert_with(|| focus_handle.unwrap_or_else(|| cx.focus_handle()))
.clone();
for listener in focusable.focus_listeners.drain(..) {
let focus_handle = focus_handle.clone();
cx.on_focus_changed(move |view, event, cx| {
listener(view, &focus_handle, event, cx)
});
}
Some(focus_handle)
} else {
None
};
cx.with_key_dispatch(self.key_context().clone(), focus_handle, f)
}
fn refine_style(&self, style: &mut Style, cx: &WindowContext) {
if let Some(focusable) = self.as_focusable() {
let focus_handle = focusable
.focus_handle
.as_ref()
.expect("must call initialize before refine_style");
if focus_handle.contains_focused(cx) {
style.refine(&focusable.focus_in_style);
}
if focus_handle.within_focused(cx) {
style.refine(&focusable.in_focus_style);
}
if focus_handle.is_focused(cx) {
style.refine(&focusable.focus_style);
}
}
}
fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
if let Some(focusable) = self.as_focusable() {
let focus_handle = focusable
.focus_handle
.clone()
.expect("must call initialize before paint");
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
if !cx.default_prevented() {
cx.focus(&focus_handle);
cx.prevent_default();
}
}
})
}
}
}
pub struct FocusableKeyDispatch<V> {
pub non_focusable: NonFocusableKeyDispatch,
pub focus_handle: Option<FocusHandle>,
pub focus_listeners: FocusListeners<V>,
pub focus_style: StyleRefinement,
pub focus_in_style: StyleRefinement,
pub in_focus_style: StyleRefinement,
}
impl<V> FocusableKeyDispatch<V> {
pub fn new(non_focusable: NonFocusableKeyDispatch) -> Self {
Self {
non_focusable,
focus_handle: None,
focus_listeners: FocusListeners::default(),
focus_style: StyleRefinement::default(),
focus_in_style: StyleRefinement::default(),
in_focus_style: StyleRefinement::default(),
}
}
pub fn tracked(non_focusable: NonFocusableKeyDispatch, handle: &FocusHandle) -> Self {
Self {
non_focusable,
focus_handle: Some(handle.clone()),
focus_listeners: FocusListeners::default(),
focus_style: StyleRefinement::default(),
focus_in_style: StyleRefinement::default(),
in_focus_style: StyleRefinement::default(),
}
}
}
impl<V: 'static> KeyDispatch<V> for FocusableKeyDispatch<V> {
fn as_focusable(&self) -> Option<&FocusableKeyDispatch<V>> {
Some(self)
}
fn as_focusable_mut(&mut self) -> Option<&mut FocusableKeyDispatch<V>> {
Some(self)
}
fn key_context(&self) -> &KeyContext {
&self.non_focusable.key_context
}
fn key_context_mut(&mut self) -> &mut KeyContext {
&mut self.non_focusable.key_context
}
}
#[derive(Default)]
pub struct NonFocusableKeyDispatch {
pub(crate) key_context: KeyContext,
}
impl<V: 'static> KeyDispatch<V> for NonFocusableKeyDispatch {
fn as_focusable(&self) -> Option<&FocusableKeyDispatch<V>> {
None
}
fn as_focusable_mut(&mut self) -> Option<&mut FocusableKeyDispatch<V>> {
None
}
fn key_context(&self) -> &KeyContext {
&self.key_context
}
fn key_context_mut(&mut self) -> &mut KeyContext {
&mut self.key_context
}
}
pub trait Focusable<V: 'static>: Element<V> {
fn focus_listeners(&mut self) -> &mut FocusListeners<V>;
fn set_focus_style(&mut self, style: StyleRefinement);
fn set_focus_in_style(&mut self, style: StyleRefinement);
fn set_in_focus_style(&mut self, style: StyleRefinement);
fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_focus_style(f(StyleRefinement::default()));
self
}
fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_focus_in_style(f(StyleRefinement::default()));
self
}
fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_in_focus_style(f(StyleRefinement::default()));
self
}
fn on_focus(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focus_listeners()
.push(Box::new(move |view, focus_handle, event, cx| {
if event.focused.as_ref() == Some(focus_handle) {
listener(view, event, cx)
}
}));
self
}
fn on_blur(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focus_listeners()
.push(Box::new(move |view, focus_handle, event, cx| {
if event.blurred.as_ref() == Some(focus_handle) {
listener(view, event, cx)
}
}));
self
}
fn on_focus_in(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focus_listeners()
.push(Box::new(move |view, focus_handle, event, cx| {
let descendant_blurred = event
.blurred
.as_ref()
.map_or(false, |blurred| focus_handle.contains(blurred, cx));
let descendant_focused = event
.focused
.as_ref()
.map_or(false, |focused| focus_handle.contains(focused, cx));
if !descendant_blurred && descendant_focused {
listener(view, event, cx)
}
}));
self
}
fn on_focus_out(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focus_listeners()
.push(Box::new(move |view, focus_handle, event, cx| {
let descendant_blurred = event
.blurred
.as_ref()
.map_or(false, |blurred| focus_handle.contains(blurred, cx));
let descendant_focused = event
.focused
.as_ref()
.map_or(false, |focused| focus_handle.contains(focused, cx));
if descendant_blurred && !descendant_focused {
listener(view, event, cx)
}
}));
self
}
}

View File

@ -97,6 +97,7 @@ impl KeystrokeMatcher {
}
}
#[derive(Debug)]
pub enum KeyMatch {
None,
Pending,

View File

@ -1 +1,4 @@
pub use crate::{Context, ParentElement, Refineable};
pub use crate::{
BorrowAppContext, BorrowWindow, Component, Context, FocusableComponent, InteractiveComponent,
ParentComponent, Refineable, Render, StatefulInteractiveComponent, Styled, VisualContext,
};

View File

@ -2,7 +2,7 @@ use crate::{
black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Result,
Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext, WindowContext,
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.

View File

@ -1,26 +1,24 @@
use crate::{
self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position,
SharedString, Style, StyleRefinement, Visibility,
SharedString, StyleRefinement, Visibility,
};
use crate::{BoxShadow, TextStyleRefinement};
use refineable::Refineable;
use smallvec::{smallvec, SmallVec};
use taffy::style::Overflow;
pub trait Styled {
pub trait Styled: Sized {
fn style(&mut self) -> &mut StyleRefinement;
fn computed_style(&mut self) -> Style {
Style::default().refined(self.style().clone())
}
gpui2_macros::style_helpers!();
fn z_index(mut self, z_index: u32) -> Self {
self.style().z_index = Some(z_index);
self
}
/// Sets the size of the element to the full width and height.
fn full(mut self) -> Self
where
Self: Sized,
{
fn full(mut self) -> Self {
self.style().size.width = Some(relative(1.).into());
self.style().size.height = Some(relative(1.).into());
self
@ -28,118 +26,98 @@ pub trait Styled {
/// Sets the position of the element to `relative`.
/// [Docs](https://tailwindcss.com/docs/position)
fn relative(mut self) -> Self
where
Self: Sized,
{
fn relative(mut self) -> Self {
self.style().position = Some(Position::Relative);
self
}
/// Sets the position of the element to `absolute`.
/// [Docs](https://tailwindcss.com/docs/position)
fn absolute(mut self) -> Self
where
Self: Sized,
{
fn absolute(mut self) -> Self {
self.style().position = Some(Position::Absolute);
self
}
/// Sets the display type of the element to `block`.
/// [Docs](https://tailwindcss.com/docs/display)
fn block(mut self) -> Self
where
Self: Sized,
{
fn block(mut self) -> Self {
self.style().display = Some(Display::Block);
self
}
/// Sets the display type of the element to `flex`.
/// [Docs](https://tailwindcss.com/docs/display)
fn flex(mut self) -> Self
where
Self: Sized,
{
fn flex(mut self) -> Self {
self.style().display = Some(Display::Flex);
self
}
/// Sets the visibility of the element to `visible`.
/// [Docs](https://tailwindcss.com/docs/visibility)
fn visible(mut self) -> Self
where
Self: Sized,
{
fn visible(mut self) -> Self {
self.style().visibility = Some(Visibility::Visible);
self
}
/// Sets the visibility of the element to `hidden`.
/// [Docs](https://tailwindcss.com/docs/visibility)
fn invisible(mut self) -> Self
where
Self: Sized,
{
fn invisible(mut self) -> Self {
self.style().visibility = Some(Visibility::Hidden);
self
}
fn cursor(mut self, cursor: CursorStyle) -> Self
where
Self: Sized,
{
fn overflow_hidden(mut self) -> Self {
self.style().overflow.x = Some(Overflow::Hidden);
self.style().overflow.y = Some(Overflow::Hidden);
self
}
fn overflow_hidden_x(mut self) -> Self {
self.style().overflow.x = Some(Overflow::Hidden);
self
}
fn overflow_hidden_y(mut self) -> Self {
self.style().overflow.y = Some(Overflow::Hidden);
self
}
fn cursor(mut self, cursor: CursorStyle) -> Self {
self.style().mouse_cursor = Some(cursor);
self
}
/// Sets the cursor style when hovering an element to `default`.
/// [Docs](https://tailwindcss.com/docs/cursor)
fn cursor_default(mut self) -> Self
where
Self: Sized,
{
fn cursor_default(mut self) -> Self {
self.style().mouse_cursor = Some(CursorStyle::Arrow);
self
}
/// Sets the cursor style when hovering an element to `pointer`.
/// [Docs](https://tailwindcss.com/docs/cursor)
fn cursor_pointer(mut self) -> Self
where
Self: Sized,
{
fn cursor_pointer(mut self) -> Self {
self.style().mouse_cursor = Some(CursorStyle::PointingHand);
self
}
/// Sets the flex direction of the element to `column`.
/// [Docs](https://tailwindcss.com/docs/flex-direction#column)
fn flex_col(mut self) -> Self
where
Self: Sized,
{
fn flex_col(mut self) -> Self {
self.style().flex_direction = Some(FlexDirection::Column);
self
}
/// Sets the flex direction of the element to `row`.
/// [Docs](https://tailwindcss.com/docs/flex-direction#row)
fn flex_row(mut self) -> Self
where
Self: Sized,
{
fn flex_row(mut self) -> Self {
self.style().flex_direction = Some(FlexDirection::Row);
self
}
/// Sets the element to allow a flex item to grow and shrink as needed, ignoring its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#flex-1)
fn flex_1(mut self) -> Self
where
Self: Sized,
{
fn flex_1(mut self) -> Self {
self.style().flex_grow = Some(1.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(relative(0.).into());
@ -148,10 +126,7 @@ pub trait Styled {
/// Sets the element to allow a flex item to grow and shrink, taking into account its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#auto)
fn flex_auto(mut self) -> Self
where
Self: Sized,
{
fn flex_auto(mut self) -> Self {
self.style().flex_grow = Some(1.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(Length::Auto);
@ -160,10 +135,7 @@ pub trait Styled {
/// Sets the element to allow a flex item to shrink but not grow, taking into account its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#initial)
fn flex_initial(mut self) -> Self
where
Self: Sized,
{
fn flex_initial(mut self) -> Self {
self.style().flex_grow = Some(0.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(Length::Auto);
@ -172,10 +144,7 @@ pub trait Styled {
/// Sets the element to prevent a flex item from growing or shrinking.
/// [Docs](https://tailwindcss.com/docs/flex#none)
fn flex_none(mut self) -> Self
where
Self: Sized,
{
fn flex_none(mut self) -> Self {
self.style().flex_grow = Some(0.);
self.style().flex_shrink = Some(0.);
self
@ -183,40 +152,28 @@ pub trait Styled {
/// Sets the element to allow a flex item to grow to fill any available space.
/// [Docs](https://tailwindcss.com/docs/flex-grow)
fn grow(mut self) -> Self
where
Self: Sized,
{
fn grow(mut self) -> Self {
self.style().flex_grow = Some(1.);
self
}
/// Sets the element to align flex items to the start of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#start)
fn items_start(mut self) -> Self
where
Self: Sized,
{
fn items_start(mut self) -> Self {
self.style().align_items = Some(AlignItems::FlexStart);
self
}
/// Sets the element to align flex items to the end of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#end)
fn items_end(mut self) -> Self
where
Self: Sized,
{
fn items_end(mut self) -> Self {
self.style().align_items = Some(AlignItems::FlexEnd);
self
}
/// Sets the element to align flex items along the center of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#center)
fn items_center(mut self) -> Self
where
Self: Sized,
{
fn items_center(mut self) -> Self {
self.style().align_items = Some(AlignItems::Center);
self
}
@ -224,40 +181,28 @@ pub trait Styled {
/// Sets the element to justify flex items along the container's main axis
/// such that there is an equal amount of space between each item.
/// [Docs](https://tailwindcss.com/docs/justify-content#space-between)
fn justify_between(mut self) -> Self
where
Self: Sized,
{
fn justify_between(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::SpaceBetween);
self
}
/// Sets the element to justify flex items along the center of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#center)
fn justify_center(mut self) -> Self
where
Self: Sized,
{
fn justify_center(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::Center);
self
}
/// Sets the element to justify flex items against the start of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#start)
fn justify_start(mut self) -> Self
where
Self: Sized,
{
fn justify_start(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::Start);
self
}
/// Sets the element to justify flex items against the end of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#end)
fn justify_end(mut self) -> Self
where
Self: Sized,
{
fn justify_end(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::End);
self
}
@ -265,10 +210,7 @@ pub trait Styled {
/// Sets the element to justify items along the container's main axis such
/// that there is an equal amount of space on each side of each item.
/// [Docs](https://tailwindcss.com/docs/justify-content#space-around)
fn justify_around(mut self) -> Self
where
Self: Sized,
{
fn justify_around(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::SpaceAround);
self
}
@ -295,30 +237,21 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self
where
Self: Sized,
{
fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self {
self.style().box_shadow = Some(shadows);
self
}
/// Clears the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_none(mut self) -> Self
where
Self: Sized,
{
fn shadow_none(mut self) -> Self {
self.style().box_shadow = Some(Default::default());
self
}
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_sm(mut self) -> Self
where
Self: Sized,
{
fn shadow_sm(mut self) -> Self {
self.style().box_shadow = Some(smallvec::smallvec![BoxShadow {
color: hsla(0., 0., 0., 0.05),
offset: point(px(0.), px(1.)),
@ -330,10 +263,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_md(mut self) -> Self
where
Self: Sized,
{
fn shadow_md(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0.5, 0., 0., 0.1),
@ -353,10 +283,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_lg(mut self) -> Self
where
Self: Sized,
{
fn shadow_lg(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0., 0., 0., 0.1),
@ -376,10 +303,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_xl(mut self) -> Self
where
Self: Sized,
{
fn shadow_xl(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0., 0., 0., 0.1),
@ -399,10 +323,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_2xl(mut self) -> Self
where
Self: Sized,
{
fn shadow_2xl(mut self) -> Self {
self.style().box_shadow = Some(smallvec![BoxShadow {
color: hsla(0., 0., 0., 0.25),
offset: point(px(0.), px(25.)),
@ -417,198 +338,138 @@ pub trait Styled {
&mut style.text
}
fn text_color(mut self, color: impl Into<Hsla>) -> Self
where
Self: Sized,
{
fn text_color(mut self, color: impl Into<Hsla>) -> Self {
self.text_style().get_or_insert_with(Default::default).color = Some(color.into());
self
}
fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self
where
Self: Sized,
{
fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(size.into());
self
}
fn text_xs(mut self) -> Self
where
Self: Sized,
{
fn text_xs(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(0.75).into());
self
}
fn text_sm(mut self) -> Self
where
Self: Sized,
{
fn text_sm(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(0.875).into());
self
}
fn text_base(mut self) -> Self
where
Self: Sized,
{
fn text_base(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.0).into());
self
}
fn text_lg(mut self) -> Self
where
Self: Sized,
{
fn text_lg(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.125).into());
self
}
fn text_xl(mut self) -> Self
where
Self: Sized,
{
fn text_xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.25).into());
self
}
fn text_2xl(mut self) -> Self
where
Self: Sized,
{
fn text_2xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.5).into());
self
}
fn text_3xl(mut self) -> Self
where
Self: Sized,
{
fn text_3xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.875).into());
self
}
fn text_decoration_none(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_none(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.underline = None;
self
}
fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self
where
Self: Sized,
{
fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.color = Some(color.into());
self
}
fn text_decoration_solid(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_solid(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.wavy = false;
self
}
fn text_decoration_wavy(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_wavy(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.wavy = true;
self
}
fn text_decoration_0(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_0(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(0.);
self
}
fn text_decoration_1(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_1(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(1.);
self
}
fn text_decoration_2(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_2(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(2.);
self
}
fn text_decoration_4(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_4(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(4.);
self
}
fn text_decoration_8(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_8(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(8.);
self
}
fn font(mut self, family_name: impl Into<SharedString>) -> Self
where
Self: Sized,
{
fn font(mut self, family_name: impl Into<SharedString>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_family = Some(family_name.into());
self
}
fn line_height(mut self, line_height: impl Into<DefiniteLength>) -> Self
where
Self: Sized,
{
fn line_height(mut self, line_height: impl Into<DefiniteLength>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.line_height = Some(line_height.into());

View File

@ -206,7 +206,7 @@ impl<V: Render> From<View<V>> for AnyView {
impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
type ElementState = Box<dyn Any>;
fn id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<ElementId> {
Some(self.model.entity_id.into())
}
@ -286,7 +286,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));
@ -302,7 +302,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))
@ -314,7 +314,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))

View File

@ -422,11 +422,8 @@ impl<'a> WindowContext<'a> {
}
pub fn dispatch_action(&mut self, action: Box<dyn Action>) {
dbg!("BEFORE FOCUS");
if let Some(focus_handle) = self.focused() {
dbg!("BEFORE DEFER", focus_handle.id);
self.defer(move |cx| {
dbg!("AFTER DEFER");
if let Some(node_id) = cx
.window
.current_frame
@ -1077,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);
@ -1086,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);
@ -1584,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);
};
@ -1656,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
@ -2084,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

View File

@ -130,7 +130,7 @@ fn generate_predefined_setter(
let method = quote! {
#[doc = #doc_string]
fn #method_name(mut self) -> Self where Self: std::marker::Sized {
fn #method_name(mut self) -> Self {
let style = self.style();
#(#field_assignments)*
self
@ -163,7 +163,7 @@ fn generate_custom_value_setter(
let method = quote! {
#[doc = #doc_string]
fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self where Self: std::marker::Sized {
fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self {
let style = self.style();
#(#field_assignments)*
self

View File

@ -1,7 +1,7 @@
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, TextColor};
@ -179,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)

View File

@ -9,10 +9,10 @@ use file_associations::FileAssociations;
use anyhow::{anyhow, Result};
use gpui::{
actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
ClipboardItem, Component, Div, EventEmitter, FocusHandle, FocusableKeyDispatch, Model,
MouseButton, ParentElement as _, Pixels, Point, PromptLevel, Render, StatefulInteractive,
StatefulInteractivity, StatelessInteractive, Styled, Task, UniformListScrollHandle, View,
ViewContext, VisualContext as _, WeakView, WindowContext,
ClipboardItem, Component, Div, EventEmitter, FocusHandle, Focusable, InteractiveComponent,
Model, MouseButton, ParentComponent, Pixels, Point, PromptLevel, Render, Stateful,
StatefulInteractiveComponent, Styled, Task, UniformListScrollHandle, View, ViewContext,
VisualContext as _, WeakView, WindowContext,
};
use menu::{Confirm, SelectNext, SelectPrev};
use project::{
@ -1372,7 +1372,7 @@ impl ProjectPanel {
details: EntryDetails,
// dragged_entry_destination: &mut Option<Arc<Path>>,
cx: &mut ViewContext<Self>,
) -> Div<Self, StatefulInteractivity<Self>> {
) -> Stateful<Self, Div<Self>> {
let kind = details.kind;
let settings = ProjectPanelSettings::get_global(cx);
const INDENT_SIZE: Pixels = px(16.0);
@ -1418,7 +1418,7 @@ impl ProjectPanel {
}
impl Render for ProjectPanel {
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 has_worktree = self.visible_entries.len() != 0;
@ -1427,7 +1427,7 @@ impl Render for ProjectPanel {
div()
.id("project-panel")
.size_full()
.context("ProjectPanel")
.key_context("ProjectPanel")
.on_action(Self::select_next)
.on_action(Self::select_prev)
.on_action(Self::expand_selected_entry)

View File

@ -1,5 +1,5 @@
use crate::story::Story;
use gpui::{px, Div, Render};
use gpui::{prelude::*, px, Div, Render};
use theme2::{default_color_scales, ColorScaleStep};
use ui::prelude::*;

View File

@ -1,6 +1,5 @@
use gpui::{
actions, div, Div, FocusHandle, Focusable, FocusableKeyDispatch, KeyBinding, ParentElement,
Render, StatefulInteractivity, StatelessInteractive, Styled, View, VisualContext,
actions, div, prelude::*, Div, FocusHandle, Focusable, KeyBinding, Render, Stateful, View,
WindowContext,
};
use theme2::ActiveTheme;
@ -28,7 +27,7 @@ impl FocusStory {
}
impl Render for FocusStory {
type Element = Div<Self, StatefulInteractivity<Self>, FocusableKeyDispatch<Self>>;
type Element = Focusable<Self, Stateful<Self, Div<Self>>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
@ -42,7 +41,7 @@ impl Render for FocusStory {
div()
.id("parent")
.focusable()
.context("parent")
.key_context("parent")
.on_action(|_, action: &ActionA, cx| {
println!("Action A dispatched on parent");
})
@ -62,7 +61,7 @@ impl Render for FocusStory {
.child(
div()
.track_focus(&self.child_1_focus)
.context("child-1")
.key_context("child-1")
.on_action(|_, action: &ActionB, cx| {
println!("Action B dispatched on child 1 during");
})
@ -82,7 +81,7 @@ impl Render for FocusStory {
.child(
div()
.track_focus(&self.child_2_focus)
.context("child-2")
.key_context("child-2")
.on_action(|_, action: &ActionC, cx| {
println!("Action C dispatched on child 2");
})

View File

@ -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()

View File

@ -1,11 +1,7 @@
use std::sync::Arc;
use fuzzy::StringMatchCandidate;
use gpui::{
div, Component, Div, KeyBinding, ParentElement, Render, StatelessInteractive, Styled, Task,
View, VisualContext, WindowContext,
};
use gpui::{div, prelude::*, Div, KeyBinding, Render, Styled, Task, View, WindowContext};
use picker::{Picker, PickerDelegate};
use std::sync::Arc;
use theme2::ActiveTheme;
pub struct PickerStory {

View File

@ -1,7 +1,4 @@
use gpui::{
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteractivity, Styled,
View, VisualContext, WindowContext,
};
use gpui::{div, prelude::*, px, Div, Render, SharedString, Stateful, Styled, View, WindowContext};
use theme2::ActiveTheme;
pub struct ScrollStory;
@ -13,7 +10,7 @@ impl ScrollStory {
}
impl Render for ScrollStory {
type Element = Div<Self, StatefulInteractivity<Self>>;
type Element = Stateful<Self, Div<Self>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();

View File

@ -1,4 +1,4 @@
use gpui::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext};
use gpui::{div, white, Div, ParentComponent, Render, Styled, View, VisualContext, WindowContext};
pub struct TextStory;

View File

@ -1,4 +1,4 @@
use gpui::{div, Component, Div, ParentElement, Styled, ViewContext};
use gpui::{div, Component, Div, ParentComponent, Styled, ViewContext};
use crate::ActiveTheme;

View File

@ -143,7 +143,7 @@ use crate::{amber, blue, jade, lime, orange, pink, purple, red};
mod stories {
use super::*;
use crate::{ActiveTheme, Story};
use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
use gpui::{div, img, px, Div, ParentComponent, Render, Styled, ViewContext};
pub struct PlayerStory;

View File

@ -1,6 +1,6 @@
use std::sync::Arc;
use gpui::{div, DefiniteLength, Hsla, MouseButton, WindowContext};
use gpui::{div, DefiniteLength, Hsla, MouseButton, StatefulInteractiveComponent, WindowContext};
use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, IconElement, Label, LineHeightStyle, TextColor};

View File

@ -1,9 +1,5 @@
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, IconElement, Selection, TextColor};

View File

@ -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)
}

View File

@ -1,5 +1,5 @@
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement, TextColor, 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> {

View File

@ -1,6 +1,5 @@
use crate::prelude::*;
use crate::Label;
use crate::TextColor;
use crate::{prelude::*, Label};
use gpui::prelude::*;
#[derive(Default, PartialEq)]
pub enum InputVariant {

View File

@ -74,7 +74,7 @@ impl<V: 'static> Modal<V> {
}
}
impl<V: 'static> ParentElement<V> for Modal<V> {
impl<V: 'static> ParentComponent<V> for Modal<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}

View File

@ -1,5 +1,5 @@
use crate::prelude::*;
use crate::{h_stack, v_stack, KeyBinding, Label, TextColor};
use crate::{h_stack, prelude::*, v_stack, KeyBinding, Label};
use gpui::prelude::*;
#[derive(Component)]
pub struct Palette {

View File

@ -1,4 +1,4 @@
use gpui::{AbsoluteLength, AnyElement};
use gpui::{prelude::*, AbsoluteLength, AnyElement};
use smallvec::SmallVec;
use crate::prelude::*;
@ -113,7 +113,7 @@ impl<V: 'static> Panel<V> {
}
}
impl<V: 'static> ParentElement<V> for Panel<V> {
impl<V: 'static> ParentComponent<V> for Panel<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
@ -126,7 +126,7 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{Label, Story};
use gpui::{Div, Render};
use gpui::{Div, InteractiveComponent, Render};
pub struct PanelStory;

View File

@ -1,6 +1,6 @@
use crate::prelude::*;
use crate::{Icon, IconElement, Label, TextColor};
use gpui::{red, Div, ElementId, Render, View, VisualContext};
use gpui::{prelude::*, red, Div, ElementId, Render, View};
#[derive(Component, Clone)]
pub struct Tab {

View File

@ -1,7 +1,6 @@
use gpui::AnyElement;
use smallvec::SmallVec;
use crate::prelude::*;
use gpui::{prelude::*, AnyElement};
use smallvec::SmallVec;
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
pub enum ToastOrigin {
@ -59,7 +58,7 @@ impl<V: 'static> Toast<V> {
}
}
impl<V: 'static> ParentElement<V> for Toast<V> {
impl<V: 'static> ParentComponent<V> for Toast<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}

View File

@ -1,4 +1,4 @@
use gpui::{div, Component, ParentElement};
use gpui::{div, Component, ParentComponent};
use crate::{Icon, IconElement, IconSize, TextColor};

View File

@ -1,8 +1,8 @@
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::*;

View File

@ -1,4 +1,4 @@
use gpui::{Div, ElementInteractivity, KeyDispatch, Styled, UniformList, ViewContext};
use gpui::{Styled, ViewContext};
use theme2::ActiveTheme;
use crate::{ElevationIndex, UITextSize};
@ -93,11 +93,4 @@ pub trait StyledExt: Styled + Sized {
}
}
impl<V, I, F> StyledExt for Div<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
}
impl<V> StyledExt for UniformList<V> {}
impl<E: Styled> StyledExt for E {}

View File

@ -1,6 +1,6 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Label, Panel, PanelSide};
use gpui::{rems, AbsoluteLength};
use gpui::{prelude::*, rems, AbsoluteLength};
#[derive(Component)]
pub struct AssistantPanel {

View File

@ -1,9 +1,7 @@
use crate::{h_stack, prelude::*, HighlightedText};
use gpui::{prelude::*, Div};
use std::path::PathBuf;
use crate::prelude::*;
use crate::{h_stack, HighlightedText};
use gpui::Div;
#[derive(Clone)]
pub struct Symbol(pub Vec<HighlightedText>);

View File

@ -1,7 +1,6 @@
use crate::{prelude::*, Icon, IconButton, Input, Label};
use chrono::NaiveDateTime;
use crate::prelude::*;
use crate::{Icon, IconButton, Input, Label, TextColor};
use gpui::prelude::*;
#[derive(Component)]
pub struct ChatPanel {

View File

@ -1,7 +1,8 @@
use crate::{prelude::*, Toggle};
use crate::{
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, ListHeader,
prelude::*, static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon,
List, ListHeader, Toggle,
};
use gpui::prelude::*;
#[derive(Component)]
pub struct CollabPanel {

View File

@ -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(TextColor::Accent)
})
.on_click(|editor, cx| {
.on_click(|editor: &mut Self, cx| {
editor.toggle_buffer_search(cx);
}),
IconButton::new("inline_assist", Icon::MagicWand),

View File

@ -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, LineHeightStyle, ListHeaderMeta, ListSeparator, PublicPlayer,
TextColor, 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 {

View File

@ -59,7 +59,7 @@ impl<V: 'static> Pane<V> {
}
}
impl<V: 'static> ParentElement<V> for Pane<V> {
impl<V: 'static> ParentComponent<V> for Pane<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}

View File

@ -1,7 +1,8 @@
use crate::prelude::*;
use crate::{
static_project_panel_project_items, static_project_panel_single_items, Input, List, ListHeader,
prelude::*, static_project_panel_project_items, static_project_panel_single_items, Input, List,
ListHeader,
};
use gpui::prelude::*;
#[derive(Component)]
pub struct ProjectPanel {

View File

@ -112,7 +112,7 @@ impl StatusBar {
.when(workspace.is_project_panel_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_project_panel(cx);
}),
)
@ -121,7 +121,7 @@ impl StatusBar {
.when(workspace.is_collab_panel_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_collab_panel();
}),
)
@ -176,7 +176,7 @@ impl StatusBar {
.when(workspace.is_terminal_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_terminal(cx);
}),
)
@ -185,7 +185,7 @@ impl StatusBar {
.when(workspace.is_chat_panel_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_chat_panel(cx);
}),
)
@ -194,7 +194,7 @@ impl StatusBar {
.when(workspace.is_assistant_panel_open(), |this| {
this.color(TextColor::Accent)
})
.on_click(|workspace, cx| {
.on_click(|workspace: &mut Workspace, cx| {
workspace.toggle_assistant_panel(cx);
}),
),

View File

@ -1,5 +1,5 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Tab};
use crate::{prelude::*, Icon, IconButton, Tab};
use gpui::prelude::*;
#[derive(Component)]
pub struct TabBar {

View File

@ -153,12 +153,16 @@ impl Render for TitleBar {
.child(
IconButton::<TitleBar>::new("toggle_mic_status", Icon::Mic)
.when(self.is_mic_muted(), |this| this.color(TextColor::Error))
.on_click(|title_bar, cx| title_bar.toggle_mic_status(cx)),
.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(TextColor::Error))
.on_click(|title_bar, cx| title_bar.toggle_deafened(cx)),
.on_click(|title_bar: &mut TitleBar, cx| {
title_bar.toggle_deafened(cx)
}),
)
.child(
IconButton::<TitleBar>::new("toggle_screen_share", Icon::Screen)
@ -166,7 +170,7 @@ impl Render for TitleBar {
self.screen_share_status == ScreenShareStatus::Shared,
|this| this.color(TextColor::Accent),
)
.on_click(|title_bar, cx| {
.on_click(|title_bar: &mut TitleBar, cx| {
title_bar.toggle_screen_share_status(cx)
}),
),

View File

@ -1,7 +1,7 @@
use crate::{status_bar::StatusItemView, Axis, Workspace};
use gpui::{
div, px, Action, AnyView, AppContext, Component, Div, Entity, EntityId, EventEmitter,
FocusHandle, ParentElement, Render, Styled, Subscription, View, ViewContext, WeakView,
FocusHandle, ParentComponent, Render, Styled, Subscription, View, ViewContext, WeakView,
WindowContext,
};
use schemars::JsonSchema;

View File

@ -1,6 +1,6 @@
use gpui::{
div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
div, prelude::*, px, AnyView, Div, EventEmitter, FocusHandle, Render, Subscription, View,
ViewContext, WindowContext,
};
use ui::{h_stack, v_stack};

View File

@ -1,5 +1,3 @@
// mod dragged_item_receiver;
use crate::{
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
toolbar::Toolbar,
@ -9,7 +7,7 @@ use crate::{
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
use gpui::{
actions, register_action, AppContext, AsyncWindowContext, Component, Div, EntityId,
actions, prelude::*, register_action, AppContext, AsyncWindowContext, Component, Div, EntityId,
EventEmitter, FocusHandle, Model, PromptLevel, Render, Task, View, ViewContext, VisualContext,
WeakView, WindowContext,
};
@ -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)

View File

@ -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,

View File

@ -2,7 +2,7 @@ use std::any::TypeId;
use crate::{ItemHandle, Pane};
use gpui::{
div, AnyView, Component, Div, ParentElement, Render, Styled, Subscription, View, ViewContext,
div, AnyView, Component, Div, ParentComponent, Render, Styled, Subscription, View, ViewContext,
WindowContext,
};
use theme2::ActiveTheme;

View File

@ -36,12 +36,11 @@ use futures::{
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;
@ -348,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;
@ -436,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,
@ -3412,7 +3404,6 @@ impl Workspace {
#[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();
@ -3477,10 +3468,7 @@ impl Workspace {
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)
}
@ -3717,7 +3705,7 @@ impl Render for 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()