mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 02:44:50 +03:00
Refine element and component traits (#3367)
This PR changes `Element::paint` to move self and introduces a new `RenderOnce` trait, which renders into an element by moving self. Elements are required to be `RenderOnce`, and `element_id` is now on `RenderOnce` so we can get the id without moving self. The `child` and `children` methods now expect `impl RenderOnce`. ```rust pub trait Element<V: 'static>: 'static + RenderOnce<V> { type State: 'static; fn layout( &mut self, view_state: &mut V, element_state: Option<Self::State>, cx: &mut ViewContext<V>, ) -> (LayoutId, Self::State); fn paint( self, bounds: Bounds<Pixels>, view_state: &mut V, element_state: &mut Self::State, cx: &mut ViewContext<V>, ); fn into_any(self) -> AnyElement<V> { AnyElement::new(self) } } pub trait RenderOnce<V: 'static>: Sized { type Element: Element<V> + 'static; fn element_id(&self) -> Option<ElementId>; fn render_once(self) -> Self::Element; // default helpers ... } ``` To make a type a component, you can add `#[derive(RenderOnce)]`, which will require your type to implement the `Component` trait: ```rust pub trait Component<V: 'static>: 'static { type Rendered: RenderOnce<V>; fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered; } ``` I'm satisfied with this being what we open source for elements, aside from maybe adding a `StatefulComponent` trait that uses element state. Things finally feel like they slot into a coherent and simple narrative. Release Notes: - N/A
This commit is contained in:
commit
6f0cdc35f3
@ -1,4 +1,4 @@
|
||||
use gpui::{div, Div, EventEmitter, ParentComponent, Render, SemanticVersion, ViewContext};
|
||||
use gpui::{div, Div, EventEmitter, ParentElement, Render, SemanticVersion, ViewContext};
|
||||
use menu::Cancel;
|
||||
use workspace::notifications::NotificationEvent;
|
||||
|
||||
@ -8,7 +8,7 @@ pub struct UpdateNotification {
|
||||
|
||||
impl EventEmitter<NotificationEvent> for UpdateNotification {}
|
||||
|
||||
impl Render for UpdateNotification {
|
||||
impl Render<Self> for UpdateNotification {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
|
@ -160,7 +160,7 @@ use std::sync::Arc;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{
|
||||
actions, div, serde_json, AppContext, AsyncWindowContext, Div, EventEmitter, FocusHandle,
|
||||
Focusable, FocusableView, InteractiveComponent, ParentComponent, Render, View, ViewContext,
|
||||
Focusable, FocusableView, InteractiveElement, ParentElement, Render, View, ViewContext,
|
||||
VisualContext, WeakView,
|
||||
};
|
||||
use project::Fs;
|
||||
@ -3294,7 +3294,7 @@ impl CollabPanel {
|
||||
// .with_width(size.x())
|
||||
// }
|
||||
|
||||
impl Render for CollabPanel {
|
||||
impl Render<Self> for CollabPanel {
|
||||
type Element = Focusable<Self, Div<Self>>;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -31,9 +31,9 @@ use std::sync::Arc;
|
||||
use call::ActiveCall;
|
||||
use client::{Client, UserStore};
|
||||
use gpui::{
|
||||
div, px, rems, AppContext, Component, Div, InteractiveComponent, Model, ParentComponent,
|
||||
Render, Stateful, StatefulInteractiveComponent, Styled, Subscription, ViewContext,
|
||||
VisualContext, WeakView, WindowBounds,
|
||||
div, px, rems, AppContext, Div, InteractiveElement, Model, ParentElement, Render, RenderOnce,
|
||||
Stateful, StatefulInteractiveElement, Styled, Subscription, ViewContext, VisualContext,
|
||||
WeakView, WindowBounds,
|
||||
};
|
||||
use project::Project;
|
||||
use theme::ActiveTheme;
|
||||
@ -81,7 +81,7 @@ pub struct CollabTitlebarItem {
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
impl Render for CollabTitlebarItem {
|
||||
impl Render<Self> for CollabTitlebarItem {
|
||||
type Element = Stateful<Self, Div<Self>>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,9 +1,8 @@
|
||||
use collections::{CommandPaletteFilter, HashMap};
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, div, prelude::*, Action, AppContext, Component, Div, EventEmitter, FocusHandle,
|
||||
FocusableView, Keystroke, Manager, ParentComponent, Render, Styled, View, ViewContext,
|
||||
VisualContext, WeakView,
|
||||
actions, div, prelude::*, Action, AppContext, Div, EventEmitter, FocusHandle, FocusableView,
|
||||
Keystroke, Manager, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use std::{
|
||||
@ -77,7 +76,7 @@ impl FocusableView for CommandPalette {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for CommandPalette {
|
||||
impl Render<Self> for CommandPalette {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -13,9 +13,9 @@ use editor::{
|
||||
};
|
||||
use futures::future::try_join_all;
|
||||
use gpui::{
|
||||
actions, div, AnyElement, AnyView, AppContext, Component, Context, Div, EventEmitter,
|
||||
FocusEvent, FocusHandle, Focusable, FocusableComponent, FocusableView, InteractiveComponent,
|
||||
Model, ParentComponent, Render, SharedString, Styled, Subscription, Task, View, ViewContext,
|
||||
actions, div, AnyElement, AnyView, AppContext, Context, Div, EventEmitter, FocusEvent,
|
||||
FocusHandle, Focusable, FocusableElement, FocusableView, InteractiveElement, Model,
|
||||
ParentElement, Render, RenderOnce, SharedString, Styled, Subscription, Task, View, ViewContext,
|
||||
VisualContext, WeakView,
|
||||
};
|
||||
use language::{
|
||||
@ -90,7 +90,7 @@ struct DiagnosticGroupState {
|
||||
|
||||
impl EventEmitter<ItemEvent> for ProjectDiagnosticsEditor {}
|
||||
|
||||
impl Render for ProjectDiagnosticsEditor {
|
||||
impl Render<Self> for ProjectDiagnosticsEditor {
|
||||
type Element = Focusable<Self, Div<Self>>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -792,13 +792,15 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
||||
.when_some(diagnostic.code.as_ref(), |stack, code| {
|
||||
stack.child(Label::new(code.clone()))
|
||||
})
|
||||
.render()
|
||||
.render_into_any()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_summary<T: 'static>(summary: &DiagnosticSummary) -> AnyElement<T> {
|
||||
if summary.error_count == 0 && summary.warning_count == 0 {
|
||||
Label::new("No problems").render()
|
||||
let label = Label::new("No problems");
|
||||
label.render_into_any()
|
||||
//.render()
|
||||
} else {
|
||||
h_stack()
|
||||
.bg(gpui::red())
|
||||
@ -806,7 +808,7 @@ pub(crate) fn render_summary<T: 'static>(summary: &DiagnosticSummary) -> AnyElem
|
||||
.child(Label::new(summary.error_count.to_string()))
|
||||
.child(IconElement::new(Icon::ExclamationTriangle))
|
||||
.child(Label::new(summary.warning_count.to_string()))
|
||||
.render()
|
||||
.render_into_any()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use collections::HashSet;
|
||||
use editor::{Editor, GoToDiagnostic};
|
||||
use gpui::{
|
||||
rems, Div, EventEmitter, InteractiveComponent, ParentComponent, Render, Stateful,
|
||||
StatefulInteractiveComponent, Styled, Subscription, View, ViewContext, WeakView,
|
||||
rems, Div, EventEmitter, InteractiveElement, ParentElement, Render, Stateful,
|
||||
StatefulInteractiveElement, Styled, Subscription, View, ViewContext, WeakView,
|
||||
};
|
||||
use language::Diagnostic;
|
||||
use lsp::LanguageServerId;
|
||||
@ -21,7 +21,7 @@ pub struct DiagnosticIndicator {
|
||||
_observe_active_editor: Option<Subscription>,
|
||||
}
|
||||
|
||||
impl Render for DiagnosticIndicator {
|
||||
impl Render<Self> for DiagnosticIndicator {
|
||||
type Element = Stateful<Self, Div<Self>>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ProjectDiagnosticsEditor;
|
||||
use gpui::{div, Div, EventEmitter, ParentComponent, Render, ViewContext, WeakView};
|
||||
use gpui::{div, Div, EventEmitter, ParentElement, Render, ViewContext, WeakView};
|
||||
use ui::{Icon, IconButton, Tooltip};
|
||||
use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
|
||||
|
||||
@ -7,7 +7,7 @@ pub struct ToolbarControls {
|
||||
editor: Option<WeakView<ProjectDiagnosticsEditor>>,
|
||||
}
|
||||
|
||||
impl Render for ToolbarControls {
|
||||
impl Render<Self> for ToolbarControls {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -42,9 +42,9 @@ use gpui::{
|
||||
actions, div, point, prelude::*, px, relative, rems, size, uniform_list, Action, AnyElement,
|
||||
AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
|
||||
EventEmitter, FocusHandle, FocusableView, FontFeatures, FontStyle, FontWeight, HighlightStyle,
|
||||
Hsla, InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels, Render, Styled,
|
||||
Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext,
|
||||
WeakView, WindowContext,
|
||||
Hsla, InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render,
|
||||
SharedString, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View,
|
||||
ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||
use hover_popover::{hide_hover, HoverState};
|
||||
@ -1580,7 +1580,8 @@ impl CodeActionsMenu {
|
||||
)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
})
|
||||
.child(action.lsp_action.title.clone())
|
||||
// TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
|
||||
.child(SharedString::from(action.lsp_action.title.clone()))
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
@ -1595,7 +1596,7 @@ impl CodeActionsMenu {
|
||||
.max_by_key(|(_, action)| action.lsp_action.title.chars().count())
|
||||
.map(|(ix, _)| ix),
|
||||
)
|
||||
.render();
|
||||
.render_into_any();
|
||||
|
||||
if self.deployed_from_indicator {
|
||||
*cursor_position.column_mut() = 0;
|
||||
@ -4353,19 +4354,19 @@ impl Editor {
|
||||
style: &EditorStyle,
|
||||
is_active: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<AnyElement<Self>> {
|
||||
) -> Option<IconButton<Self>> {
|
||||
if self.available_code_actions.is_some() {
|
||||
Some(
|
||||
IconButton::new("code_actions_indicator", ui::Icon::Bolt)
|
||||
.on_click(|editor: &mut Editor, cx| {
|
||||
IconButton::new("code_actions_indicator", ui::Icon::Bolt).on_click(
|
||||
|editor: &mut Editor, cx| {
|
||||
editor.toggle_code_actions(
|
||||
&ToggleCodeActions {
|
||||
deployed_from_indicator: true,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.render(),
|
||||
},
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
@ -4380,7 +4381,7 @@ impl Editor {
|
||||
line_height: Pixels,
|
||||
gutter_margin: Pixels,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Vec<Option<AnyElement<Self>>> {
|
||||
) -> Vec<Option<IconButton<Self>>> {
|
||||
fold_data
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -4402,7 +4403,6 @@ impl Editor {
|
||||
}
|
||||
})
|
||||
.color(ui::TextColor::Muted)
|
||||
.render()
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
@ -7793,7 +7793,7 @@ impl Editor {
|
||||
cx.editor_style.diagnostic_style.clone(),
|
||||
},
|
||||
)))
|
||||
.render()
|
||||
.render_into_any()
|
||||
}
|
||||
}),
|
||||
disposition: BlockDisposition::Below,
|
||||
@ -9382,7 +9382,7 @@ impl FocusableView for Editor {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Editor {
|
||||
impl Render<Self> for Editor {
|
||||
type Element = EditorElement;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -10000,7 +10000,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
|
||||
cx.write_to_clipboard(ClipboardItem::new(message.clone()));
|
||||
})
|
||||
.tooltip(|_, cx| Tooltip::text("Copy diagnostic message", cx))
|
||||
.render()
|
||||
.render_into_any()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -3048,7 +3048,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
||||
position: snapshot.anchor_after(Point::new(2, 0)),
|
||||
disposition: BlockDisposition::Below,
|
||||
height: 1,
|
||||
render: Arc::new(|_| div().render()),
|
||||
render: Arc::new(|_| div().into_any()),
|
||||
}],
|
||||
Some(Autoscroll::fit()),
|
||||
cx,
|
||||
|
@ -20,9 +20,9 @@ use collections::{BTreeMap, HashMap};
|
||||
use gpui::{
|
||||
div, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace,
|
||||
BorrowWindow, Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element,
|
||||
ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveComponent, LineLayout,
|
||||
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels,
|
||||
ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveComponent, Style, Styled,
|
||||
ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveElement, LineLayout,
|
||||
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, RenderOnce,
|
||||
ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
|
||||
TextRun, TextStyle, View, ViewContext, WindowContext, WrappedLine,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
@ -488,8 +488,9 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() {
|
||||
if let Some(fold_indicator) = fold_indicator.as_mut() {
|
||||
for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
|
||||
if let Some(mut fold_indicator) = fold_indicator {
|
||||
let mut fold_indicator = fold_indicator.render_into_any();
|
||||
let available_space = size(
|
||||
AvailableSpace::MinContent,
|
||||
AvailableSpace::Definite(line_height * 0.55),
|
||||
@ -509,20 +510,21 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(indicator) = layout.code_actions_indicator.as_mut() {
|
||||
if let Some(indicator) = layout.code_actions_indicator.take() {
|
||||
let mut button = indicator.button.render_into_any();
|
||||
let available_space = size(
|
||||
AvailableSpace::MinContent,
|
||||
AvailableSpace::Definite(line_height),
|
||||
);
|
||||
let indicator_size = indicator.element.measure(available_space, editor, cx);
|
||||
let indicator_size = button.measure(available_space, editor, cx);
|
||||
|
||||
let mut x = Pixels::ZERO;
|
||||
let mut y = indicator.row as f32 * line_height - scroll_top;
|
||||
// Center indicator.
|
||||
x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.;
|
||||
y += (line_height - indicator_size.height) / 2.;
|
||||
indicator
|
||||
.element
|
||||
.draw(bounds.origin + point(x, y), available_space, editor, cx);
|
||||
|
||||
button.draw(bounds.origin + point(x, y), available_space, editor, cx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -840,7 +842,7 @@ impl EditorElement {
|
||||
}
|
||||
});
|
||||
|
||||
if let Some((position, context_menu)) = layout.context_menu.as_mut() {
|
||||
if let Some((position, mut context_menu)) = layout.context_menu.take() {
|
||||
cx.with_z_index(1, |cx| {
|
||||
let line_height = self.style.text.line_height_in_pixels(cx.rem_size());
|
||||
let available_space = size(
|
||||
@ -1224,7 +1226,7 @@ impl EditorElement {
|
||||
let scroll_left = scroll_position.x * layout.position_map.em_width;
|
||||
let scroll_top = scroll_position.y * layout.position_map.line_height;
|
||||
|
||||
for block in &mut layout.blocks {
|
||||
for block in layout.blocks.drain(..) {
|
||||
let mut origin = bounds.origin
|
||||
+ point(
|
||||
Pixels::ZERO,
|
||||
@ -1810,7 +1812,7 @@ impl EditorElement {
|
||||
.render_code_actions_indicator(&style, active, cx)
|
||||
.map(|element| CodeActionsIndicator {
|
||||
row: newest_selection_head.row(),
|
||||
element,
|
||||
button: element,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -2043,15 +2045,20 @@ impl EditorElement {
|
||||
// Can't use .and_then() because `.file_name()` and `.parent()` return references :(
|
||||
if let Some(path) = path {
|
||||
filename = path.file_name().map(|f| f.to_string_lossy().to_string());
|
||||
parent_path =
|
||||
path.parent().map(|p| p.to_string_lossy().to_string() + "/");
|
||||
parent_path = path
|
||||
.parent()
|
||||
.map(|p| SharedString::from(p.to_string_lossy().to_string() + "/"));
|
||||
}
|
||||
|
||||
h_stack()
|
||||
.id("path header block")
|
||||
.size_full()
|
||||
.bg(gpui::red())
|
||||
.child(filename.unwrap_or_else(|| "untitled".to_string()))
|
||||
.child(
|
||||
filename
|
||||
.map(SharedString::from)
|
||||
.unwrap_or_else(|| "untitled".into()),
|
||||
)
|
||||
.children(parent_path)
|
||||
.children(jump_icon) // .p_x(gutter_padding)
|
||||
} else {
|
||||
@ -2063,7 +2070,7 @@ impl EditorElement {
|
||||
.child("⋯")
|
||||
.children(jump_icon) // .p_x(gutter_padding)
|
||||
};
|
||||
element.render()
|
||||
element.into_any()
|
||||
}
|
||||
};
|
||||
|
||||
@ -2393,18 +2400,14 @@ enum Invisible {
|
||||
}
|
||||
|
||||
impl Element<Editor> for EditorElement {
|
||||
type ElementState = ();
|
||||
|
||||
fn element_id(&self) -> Option<gpui::ElementId> {
|
||||
Some(self.editor_id.into())
|
||||
}
|
||||
type State = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
editor: &mut Editor,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut gpui::ViewContext<Editor>,
|
||||
) -> (gpui::LayoutId, Self::ElementState) {
|
||||
) -> (gpui::LayoutId, Self::State) {
|
||||
editor.style = Some(self.style.clone()); // Long-term, we'd like to eliminate this.
|
||||
|
||||
let rem_size = cx.rem_size();
|
||||
@ -2420,10 +2423,10 @@ impl Element<Editor> for EditorElement {
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
mut self,
|
||||
bounds: Bounds<gpui::Pixels>,
|
||||
editor: &mut Editor,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut gpui::ViewContext<Editor>,
|
||||
) {
|
||||
let mut layout = self.compute_layout(editor, cx, bounds);
|
||||
@ -2470,9 +2473,15 @@ impl Element<Editor> for EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Editor> for EditorElement {
|
||||
fn render(self) -> AnyElement<Editor> {
|
||||
AnyElement::new(self)
|
||||
impl RenderOnce<Editor> for EditorElement {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<gpui::ElementId> {
|
||||
Some(self.editor_id.into())
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -3100,14 +3109,14 @@ pub struct LayoutState {
|
||||
context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
|
||||
code_actions_indicator: Option<CodeActionsIndicator>,
|
||||
// hover_popovers: Option<(DisplayPoint, Vec<AnyElement<Editor>>)>,
|
||||
fold_indicators: Vec<Option<AnyElement<Editor>>>,
|
||||
fold_indicators: Vec<Option<IconButton<Editor>>>,
|
||||
tab_invisible: ShapedLine,
|
||||
space_invisible: ShapedLine,
|
||||
}
|
||||
|
||||
struct CodeActionsIndicator {
|
||||
row: u32,
|
||||
element: AnyElement<Editor>,
|
||||
button: IconButton<Editor>,
|
||||
}
|
||||
|
||||
struct PositionMap {
|
||||
|
@ -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, ParentComponent, Pixels, SharedString, Styled, Subscription, Task, View,
|
||||
FocusHandle, Model, ParentElement, Pixels, SharedString, Styled, Subscription, Task, View,
|
||||
ViewContext, VisualContext, WeakView,
|
||||
};
|
||||
use language::{
|
||||
|
@ -2,8 +2,8 @@ use collections::HashMap;
|
||||
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
|
||||
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
|
||||
use gpui::{
|
||||
actions, div, AppContext, Component, Div, EventEmitter, FocusHandle, FocusableView,
|
||||
InteractiveComponent, Manager, Model, ParentComponent, Render, Styled, Task, View, ViewContext,
|
||||
actions, div, AppContext, Div, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
|
||||
Manager, Model, ParentElement, Render, RenderOnce, Styled, Task, View, ViewContext,
|
||||
VisualContext, WeakView,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
@ -117,7 +117,7 @@ impl FocusableView for FileFinder {
|
||||
self.picker.focus_handle(cx)
|
||||
}
|
||||
}
|
||||
impl Render for FileFinder {
|
||||
impl Render<Self> for FileFinder {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,8 +1,7 @@
|
||||
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
|
||||
use gpui::{
|
||||
actions, div, prelude::*, AppContext, Div, EventEmitter, FocusHandle, FocusableView, Manager,
|
||||
ParentComponent, Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
|
||||
WindowContext,
|
||||
Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
|
||||
};
|
||||
use text::{Bias, Point};
|
||||
use theme::ActiveTheme;
|
||||
@ -145,7 +144,7 @@ impl GoToLine {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for GoToLine {
|
||||
impl Render<Self> for GoToLine {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -14,7 +14,7 @@ use smallvec::SmallVec;
|
||||
pub use test_context::*;
|
||||
|
||||
use crate::{
|
||||
current_platform, image_cache::ImageCache, Action, ActionRegistry, AnyBox, AnyView,
|
||||
current_platform, image_cache::ImageCache, Action, ActionRegistry, Any, AnyView,
|
||||
AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
|
||||
DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId,
|
||||
ForegroundExecutor, KeyBinding, Keymap, LayoutId, PathPromptOptions, Pixels, Platform,
|
||||
@ -28,7 +28,7 @@ use futures::{channel::oneshot, future::LocalBoxFuture, Future};
|
||||
use parking_lot::Mutex;
|
||||
use slotmap::SlotMap;
|
||||
use std::{
|
||||
any::{type_name, Any, TypeId},
|
||||
any::{type_name, TypeId},
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
@ -194,7 +194,7 @@ pub struct AppContext {
|
||||
asset_source: Arc<dyn AssetSource>,
|
||||
pub(crate) image_cache: ImageCache,
|
||||
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
||||
pub(crate) globals_by_type: HashMap<TypeId, AnyBox>,
|
||||
pub(crate) globals_by_type: HashMap<TypeId, Box<dyn Any>>,
|
||||
pub(crate) entities: EntityMap,
|
||||
pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
|
||||
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
|
||||
@ -424,7 +424,7 @@ impl AppContext {
|
||||
/// Opens a new window with the given option and the root view returned by the given function.
|
||||
/// The function is invoked with a `WindowContext`, which can be used to interact with window-specific
|
||||
/// functionality.
|
||||
pub fn open_window<V: Render>(
|
||||
pub fn open_window<V: 'static + Render<V>>(
|
||||
&mut self,
|
||||
options: crate::WindowOptions,
|
||||
build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
|
||||
@ -1104,12 +1104,12 @@ pub(crate) enum Effect {
|
||||
|
||||
/// Wraps a global variable value during `update_global` while the value has been moved to the stack.
|
||||
pub(crate) struct GlobalLease<G: 'static> {
|
||||
global: AnyBox,
|
||||
global: Box<dyn Any>,
|
||||
global_type: PhantomData<G>,
|
||||
}
|
||||
|
||||
impl<G: 'static> GlobalLease<G> {
|
||||
fn new(global: AnyBox) -> Self {
|
||||
fn new(global: Box<dyn Any>) -> Self {
|
||||
GlobalLease {
|
||||
global,
|
||||
global_type: PhantomData,
|
||||
|
@ -115,7 +115,7 @@ impl AsyncAppContext {
|
||||
build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
|
||||
) -> Result<WindowHandle<V>>
|
||||
where
|
||||
V: Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
let app = self
|
||||
.app
|
||||
@ -286,7 +286,7 @@ impl VisualContext for AsyncWindowContext {
|
||||
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: 'static + Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
self.window
|
||||
.update(self, |_, cx| cx.build_view(build_view_state))
|
||||
@ -306,7 +306,7 @@ impl VisualContext for AsyncWindowContext {
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
self.window
|
||||
.update(self, |_, cx| cx.replace_root_view(build_view))
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::{private::Sealed, AnyBox, AppContext, Context, Entity, ModelContext};
|
||||
use crate::{private::Sealed, AppContext, Context, Entity, ModelContext};
|
||||
use anyhow::{anyhow, Result};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
||||
use slotmap::{SecondaryMap, SlotMap};
|
||||
use std::{
|
||||
any::{type_name, TypeId},
|
||||
any::{type_name, Any, TypeId},
|
||||
fmt::{self, Display},
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
@ -31,7 +31,7 @@ impl Display for EntityId {
|
||||
}
|
||||
|
||||
pub(crate) struct EntityMap {
|
||||
entities: SecondaryMap<EntityId, AnyBox>,
|
||||
entities: SecondaryMap<EntityId, Box<dyn Any>>,
|
||||
ref_counts: Arc<RwLock<EntityRefCounts>>,
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ impl EntityMap {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn take_dropped(&mut self) -> Vec<(EntityId, AnyBox)> {
|
||||
pub fn take_dropped(&mut self) -> Vec<(EntityId, Box<dyn Any>)> {
|
||||
let mut ref_counts = self.ref_counts.write();
|
||||
let dropped_entity_ids = mem::take(&mut ref_counts.dropped_entity_ids);
|
||||
|
||||
@ -122,7 +122,7 @@ impl EntityMap {
|
||||
}
|
||||
|
||||
pub struct Lease<'a, T> {
|
||||
entity: Option<AnyBox>,
|
||||
entity: Option<Box<dyn Any>>,
|
||||
pub model: &'a Model<T>,
|
||||
entity_type: PhantomData<T>,
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ impl TestAppContext {
|
||||
pub fn add_window<F, V>(&mut self, build_window: F) -> WindowHandle<V>
|
||||
where
|
||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||
V: Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
let mut cx = self.app.borrow_mut();
|
||||
cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window))
|
||||
@ -143,7 +143,7 @@ impl TestAppContext {
|
||||
pub fn add_window_view<F, V>(&mut self, build_window: F) -> (View<V>, &mut VisualTestContext)
|
||||
where
|
||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||
V: Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
let mut cx = self.app.borrow_mut();
|
||||
let window = cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window));
|
||||
@ -569,7 +569,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: 'static + Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
self.window
|
||||
.update(self.cx, |_, cx| cx.build_view(build_view))
|
||||
@ -591,7 +591,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
self.window
|
||||
.update(self.cx, |_, cx| cx.replace_root_view(build_view))
|
||||
@ -619,7 +619,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
|
||||
}
|
||||
|
||||
impl AnyWindowHandle {
|
||||
pub fn build_view<V: Render + 'static>(
|
||||
pub fn build_view<V: Render<V> + 'static>(
|
||||
&self,
|
||||
cx: &mut TestAppContext,
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
@ -630,7 +630,7 @@ impl AnyWindowHandle {
|
||||
|
||||
pub struct EmptyView {}
|
||||
|
||||
impl Render for EmptyView {
|
||||
impl Render<Self> for EmptyView {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _cx: &mut crate::ViewContext<Self>) -> Self::Element {
|
||||
|
@ -3,27 +3,24 @@ use crate::{
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub(crate) use smallvec::SmallVec;
|
||||
use std::{any::Any, fmt::Debug, mem};
|
||||
use std::{any::Any, fmt::Debug, marker::PhantomData};
|
||||
|
||||
pub trait Element<V: 'static> {
|
||||
type ElementState: 'static;
|
||||
pub trait Render<V: 'static>: 'static + Sized {
|
||||
type Element: Element<V> + 'static;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<V>) -> Self::Element;
|
||||
}
|
||||
|
||||
pub trait RenderOnce<V: 'static>: Sized {
|
||||
type Element: Element<V> + 'static;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState);
|
||||
fn render_once(self) -> Self::Element;
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
cx: &mut ViewContext<V>,
|
||||
);
|
||||
fn render_into_any(self) -> AnyElement<V> {
|
||||
self.render_once().into_any()
|
||||
}
|
||||
|
||||
fn draw<T, R>(
|
||||
self,
|
||||
@ -31,287 +28,40 @@ pub trait Element<V: 'static> {
|
||||
available_space: Size<T>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
f: impl FnOnce(&Self::ElementState, &mut ViewContext<V>) -> R,
|
||||
f: impl FnOnce(&mut <Self::Element as Element<V>>::State, &mut ViewContext<V>) -> R,
|
||||
) -> R
|
||||
where
|
||||
Self: Sized,
|
||||
T: Clone + Default + Debug + Into<AvailableSpace>,
|
||||
{
|
||||
let mut element = RenderedElement {
|
||||
element: self,
|
||||
phase: ElementRenderPhase::Start,
|
||||
let element = self.render_once();
|
||||
let element_id = element.element_id();
|
||||
let element = DrawableElement {
|
||||
element: Some(element),
|
||||
phase: ElementDrawPhase::Start,
|
||||
};
|
||||
element.draw(origin, available_space.map(Into::into), view_state, cx);
|
||||
if let ElementRenderPhase::Painted { frame_state } = &element.phase {
|
||||
if let Some(frame_state) = frame_state.as_ref() {
|
||||
f(&frame_state, cx)
|
||||
} else {
|
||||
let element_id = element
|
||||
.element
|
||||
.element_id()
|
||||
.expect("we either have some frame_state or some element_id");
|
||||
cx.with_element_state(element_id, |element_state, cx| {
|
||||
let element_state = element_state.unwrap();
|
||||
let result = f(&element_state, cx);
|
||||
(result, element_state)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
|
||||
|
||||
pub trait ParentComponent<V: 'static> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
|
||||
|
||||
fn child(mut self, child: impl Component<V>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.children_mut().push(child.render());
|
||||
self
|
||||
}
|
||||
|
||||
fn children(mut self, iter: impl IntoIterator<Item = impl Component<V>>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.children_mut()
|
||||
.extend(iter.into_iter().map(|item| item.render()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
trait ElementObject<V> {
|
||||
fn element_id(&self) -> Option<ElementId>;
|
||||
fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
|
||||
fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels>;
|
||||
fn draw(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
);
|
||||
}
|
||||
|
||||
struct RenderedElement<V: 'static, E: Element<V>> {
|
||||
element: E,
|
||||
phase: ElementRenderPhase<E::ElementState>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum ElementRenderPhase<V> {
|
||||
#[default]
|
||||
Start,
|
||||
LayoutRequested {
|
||||
layout_id: LayoutId,
|
||||
frame_state: Option<V>,
|
||||
},
|
||||
LayoutComputed {
|
||||
layout_id: LayoutId,
|
||||
available_space: Size<AvailableSpace>,
|
||||
frame_state: Option<V>,
|
||||
},
|
||||
Painted {
|
||||
frame_state: Option<V>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Internal struct that wraps an element to store Layout and ElementState after the element is rendered.
|
||||
/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
|
||||
/// improved usability.
|
||||
impl<V, E: Element<V>> RenderedElement<V, E> {
|
||||
fn new(element: E) -> Self {
|
||||
RenderedElement {
|
||||
let frame_state = DrawableElement::draw(
|
||||
element,
|
||||
phase: ElementRenderPhase::Start,
|
||||
origin,
|
||||
available_space.map(Into::into),
|
||||
view_state,
|
||||
cx,
|
||||
);
|
||||
|
||||
if let Some(mut frame_state) = frame_state {
|
||||
f(&mut frame_state, cx)
|
||||
} else {
|
||||
cx.with_element_state(element_id.unwrap(), |element_state, cx| {
|
||||
let mut element_state = element_state.unwrap();
|
||||
let result = f(&mut element_state, cx);
|
||||
(result, element_state)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E> ElementObject<V> for RenderedElement<V, E>
|
||||
where
|
||||
E: Element<V>,
|
||||
E::ElementState: 'static,
|
||||
{
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.element.element_id()
|
||||
}
|
||||
|
||||
fn layout(&mut self, state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
|
||||
let (layout_id, frame_state) = match mem::take(&mut self.phase) {
|
||||
ElementRenderPhase::Start => {
|
||||
if let Some(id) = self.element.element_id() {
|
||||
let layout_id = cx.with_element_state(id, |element_state, cx| {
|
||||
self.element.layout(state, element_state, cx)
|
||||
});
|
||||
(layout_id, None)
|
||||
} else {
|
||||
let (layout_id, frame_state) = self.element.layout(state, None, cx);
|
||||
(layout_id, Some(frame_state))
|
||||
}
|
||||
}
|
||||
ElementRenderPhase::LayoutRequested { .. }
|
||||
| ElementRenderPhase::LayoutComputed { .. }
|
||||
| ElementRenderPhase::Painted { .. } => {
|
||||
panic!("element rendered twice")
|
||||
}
|
||||
};
|
||||
|
||||
self.phase = ElementRenderPhase::LayoutRequested {
|
||||
layout_id,
|
||||
frame_state,
|
||||
};
|
||||
layout_id
|
||||
}
|
||||
|
||||
fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
|
||||
self.phase = match mem::take(&mut self.phase) {
|
||||
ElementRenderPhase::LayoutRequested {
|
||||
layout_id,
|
||||
mut frame_state,
|
||||
}
|
||||
| ElementRenderPhase::LayoutComputed {
|
||||
layout_id,
|
||||
mut frame_state,
|
||||
..
|
||||
} => {
|
||||
let bounds = cx.layout_bounds(layout_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
|
||||
.paint(bounds, view_state, &mut element_state, cx);
|
||||
((), element_state)
|
||||
});
|
||||
} else {
|
||||
self.element
|
||||
.paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
|
||||
}
|
||||
ElementRenderPhase::Painted { frame_state }
|
||||
}
|
||||
|
||||
_ => panic!("must call layout before paint"),
|
||||
};
|
||||
}
|
||||
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels> {
|
||||
if matches!(&self.phase, ElementRenderPhase::Start) {
|
||||
self.layout(view_state, cx);
|
||||
}
|
||||
|
||||
let layout_id = match &mut self.phase {
|
||||
ElementRenderPhase::LayoutRequested {
|
||||
layout_id,
|
||||
frame_state,
|
||||
} => {
|
||||
cx.compute_layout(*layout_id, available_space);
|
||||
let layout_id = *layout_id;
|
||||
self.phase = ElementRenderPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space,
|
||||
frame_state: frame_state.take(),
|
||||
};
|
||||
layout_id
|
||||
}
|
||||
ElementRenderPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space: prev_available_space,
|
||||
..
|
||||
} => {
|
||||
if available_space != *prev_available_space {
|
||||
cx.compute_layout(*layout_id, available_space);
|
||||
*prev_available_space = available_space;
|
||||
}
|
||||
*layout_id
|
||||
}
|
||||
_ => panic!("cannot measure after painting"),
|
||||
};
|
||||
|
||||
cx.layout_bounds(layout_id).size
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.measure(available_space, view_state, cx);
|
||||
cx.with_absolute_element_offset(origin, |cx| self.paint(view_state, cx))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
|
||||
|
||||
impl<V> AnyElement<V> {
|
||||
pub fn new<E>(element: E) -> Self
|
||||
where
|
||||
V: 'static,
|
||||
E: 'static + Element<V>,
|
||||
E::ElementState: Any,
|
||||
{
|
||||
AnyElement(Box::new(RenderedElement::new(element)))
|
||||
}
|
||||
|
||||
pub fn element_id(&self) -> Option<ElementId> {
|
||||
self.0.element_id()
|
||||
}
|
||||
|
||||
pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
|
||||
self.0.layout(view_state, cx)
|
||||
}
|
||||
|
||||
pub fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
|
||||
self.0.paint(view_state, cx)
|
||||
}
|
||||
|
||||
/// Initializes this element and performs layout within the given available space to determine its size.
|
||||
pub fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels> {
|
||||
self.0.measure(available_space, view_state, cx)
|
||||
}
|
||||
|
||||
/// Initializes this element and performs layout in the available space, then paints it at the given origin.
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.0.draw(origin, available_space, view_state, cx)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Component<V> {
|
||||
fn render(self) -> AnyElement<V>;
|
||||
|
||||
fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
|
||||
where
|
||||
Self: Sized,
|
||||
U: Component<V>,
|
||||
U: RenderOnce<V>,
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
@ -337,65 +87,512 @@ pub trait Component<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Component<V> for AnyElement<V> {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
self
|
||||
pub trait Element<V: 'static>: 'static + RenderOnce<V> {
|
||||
type State: 'static;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::State);
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
);
|
||||
|
||||
fn into_any(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E, F> Element<V> for Option<F>
|
||||
where
|
||||
V: 'static,
|
||||
E: 'static + Component<V>,
|
||||
F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
|
||||
{
|
||||
type ElementState = AnyElement<V>;
|
||||
pub trait Component<V: 'static>: 'static {
|
||||
type Rendered: RenderOnce<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered;
|
||||
}
|
||||
|
||||
pub struct CompositeElement<V, C> {
|
||||
component: Option<C>,
|
||||
view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
pub struct CompositeElementState<V: 'static, C: Component<V>> {
|
||||
rendered_element: Option<<C::Rendered as RenderOnce<V>>::Element>,
|
||||
rendered_element_state: <<C::Rendered as RenderOnce<V>>::Element as Element<V>>::State,
|
||||
}
|
||||
|
||||
impl<V, C> CompositeElement<V, C> {
|
||||
pub fn new(component: C) -> Self {
|
||||
CompositeElement {
|
||||
component: Some(component),
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, C: Component<V>> Element<V> for CompositeElement<V, C> {
|
||||
type State = CompositeElementState<V, C>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let mut element = self
|
||||
.component
|
||||
.take()
|
||||
.unwrap()
|
||||
.render(view, cx)
|
||||
.render_once();
|
||||
let (layout_id, state) = element.layout(view, state.map(|s| s.rendered_element_state), cx);
|
||||
let state = CompositeElementState {
|
||||
rendered_element: Some(element),
|
||||
rendered_element_state: state,
|
||||
};
|
||||
(layout_id, state)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
view: &mut V,
|
||||
state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
state.rendered_element.take().unwrap().paint(
|
||||
bounds,
|
||||
view,
|
||||
&mut state.rendered_element_state,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, C: Component<V>> RenderOnce<V> for CompositeElement<V, C> {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
_: Option<Self::ElementState>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
let render = self.take().unwrap();
|
||||
let mut rendered_element = (render)(view_state, cx).render();
|
||||
let layout_id = rendered_element.layout(view_state, cx);
|
||||
(layout_id, rendered_element)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
rendered_element: &mut Self::ElementState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
rendered_element.paint(view_state, cx)
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E, F> Component<V> for Option<F>
|
||||
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
|
||||
|
||||
pub trait ParentElement<V: 'static> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
|
||||
|
||||
fn child(mut self, child: impl RenderOnce<V>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.children_mut().push(child.render_once().into_any());
|
||||
self
|
||||
}
|
||||
|
||||
fn children(mut self, children: impl IntoIterator<Item = impl RenderOnce<V>>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.children_mut().extend(
|
||||
children
|
||||
.into_iter()
|
||||
.map(|child| child.render_once().into_any()),
|
||||
);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
trait ElementObject<V> {
|
||||
fn element_id(&self) -> Option<ElementId>;
|
||||
fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
|
||||
fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels>;
|
||||
fn draw(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
);
|
||||
}
|
||||
|
||||
pub struct DrawableElement<V: 'static, E: Element<V>> {
|
||||
element: Option<E>,
|
||||
phase: ElementDrawPhase<E::State>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum ElementDrawPhase<S> {
|
||||
#[default]
|
||||
Start,
|
||||
LayoutRequested {
|
||||
layout_id: LayoutId,
|
||||
frame_state: Option<S>,
|
||||
},
|
||||
LayoutComputed {
|
||||
layout_id: LayoutId,
|
||||
available_space: Size<AvailableSpace>,
|
||||
frame_state: Option<S>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A wrapper around an implementer of [Element] that allows it to be drawn in a window.
|
||||
impl<V, E: Element<V>> DrawableElement<V, E> {
|
||||
fn new(element: E) -> Self {
|
||||
DrawableElement {
|
||||
element: Some(element),
|
||||
phase: ElementDrawPhase::Start,
|
||||
}
|
||||
}
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.element.as_ref()?.element_id()
|
||||
}
|
||||
|
||||
fn layout(&mut self, state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
|
||||
let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
|
||||
{
|
||||
let layout_id = cx.with_element_state(id, |element_state, cx| {
|
||||
self.element
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.layout(state, element_state, cx)
|
||||
});
|
||||
(layout_id, None)
|
||||
} else {
|
||||
let (layout_id, frame_state) = self.element.as_mut().unwrap().layout(state, None, cx);
|
||||
(layout_id, Some(frame_state))
|
||||
};
|
||||
|
||||
self.phase = ElementDrawPhase::LayoutRequested {
|
||||
layout_id,
|
||||
frame_state,
|
||||
};
|
||||
layout_id
|
||||
}
|
||||
|
||||
fn paint(mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> Option<E::State> {
|
||||
match self.phase {
|
||||
ElementDrawPhase::LayoutRequested {
|
||||
layout_id,
|
||||
frame_state,
|
||||
}
|
||||
| ElementDrawPhase::LayoutComputed {
|
||||
layout_id,
|
||||
frame_state,
|
||||
..
|
||||
} => {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
|
||||
if let Some(mut frame_state) = frame_state {
|
||||
self.element
|
||||
.take()
|
||||
.unwrap()
|
||||
.paint(bounds, view_state, &mut frame_state, cx);
|
||||
Some(frame_state)
|
||||
} else {
|
||||
let element_id = self
|
||||
.element
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.element_id()
|
||||
.expect("if we don't have frame state, we should have element state");
|
||||
cx.with_element_state(element_id, |element_state, cx| {
|
||||
let mut element_state = element_state.unwrap();
|
||||
self.element.take().unwrap().paint(
|
||||
bounds,
|
||||
view_state,
|
||||
&mut element_state,
|
||||
cx,
|
||||
);
|
||||
((), element_state)
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
_ => panic!("must call layout before paint"),
|
||||
}
|
||||
}
|
||||
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels> {
|
||||
if matches!(&self.phase, ElementDrawPhase::Start) {
|
||||
self.layout(view_state, cx);
|
||||
}
|
||||
|
||||
let layout_id = match &mut self.phase {
|
||||
ElementDrawPhase::LayoutRequested {
|
||||
layout_id,
|
||||
frame_state,
|
||||
} => {
|
||||
cx.compute_layout(*layout_id, available_space);
|
||||
let layout_id = *layout_id;
|
||||
self.phase = ElementDrawPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space,
|
||||
frame_state: frame_state.take(),
|
||||
};
|
||||
layout_id
|
||||
}
|
||||
ElementDrawPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space: prev_available_space,
|
||||
..
|
||||
} => {
|
||||
if available_space != *prev_available_space {
|
||||
cx.compute_layout(*layout_id, available_space);
|
||||
*prev_available_space = available_space;
|
||||
}
|
||||
*layout_id
|
||||
}
|
||||
_ => panic!("cannot measure after painting"),
|
||||
};
|
||||
|
||||
cx.layout_bounds(layout_id).size
|
||||
}
|
||||
|
||||
fn draw(
|
||||
mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Option<E::State> {
|
||||
self.measure(available_space, view_state, cx);
|
||||
cx.with_absolute_element_offset(origin, |cx| self.paint(view_state, cx))
|
||||
}
|
||||
}
|
||||
|
||||
// impl<V: 'static, E: Element<V>> Element<V> for DrawableElement<V, E> {
|
||||
// type State = <E::Element as Element<V>>::State;
|
||||
|
||||
// fn layout(
|
||||
// &mut self,
|
||||
// view_state: &mut V,
|
||||
// element_state: Option<Self::State>,
|
||||
// cx: &mut ViewContext<V>,
|
||||
// ) -> (LayoutId, Self::State) {
|
||||
|
||||
// }
|
||||
|
||||
// fn paint(
|
||||
// self,
|
||||
// bounds: Bounds<Pixels>,
|
||||
// view_state: &mut V,
|
||||
// element_state: &mut Self::State,
|
||||
// cx: &mut ViewContext<V>,
|
||||
// ) {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<V: 'static, E: 'static + Element<V>> RenderOnce<V> for DrawableElement<V, E> {
|
||||
// type Element = Self;
|
||||
|
||||
// fn element_id(&self) -> Option<ElementId> {
|
||||
// self.element.as_ref()?.element_id()
|
||||
// }
|
||||
|
||||
// fn render_once(self) -> Self::Element {
|
||||
// self
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<V, E> ElementObject<V> for Option<DrawableElement<V, E>>
|
||||
where
|
||||
V: 'static,
|
||||
E: 'static + Component<V>,
|
||||
F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
|
||||
E: Element<V>,
|
||||
E::State: 'static,
|
||||
{
|
||||
fn render(self) -> AnyElement<V> {
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.as_ref().unwrap().element_id()
|
||||
}
|
||||
|
||||
fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
|
||||
DrawableElement::layout(self.as_mut().unwrap(), view_state, cx)
|
||||
}
|
||||
|
||||
fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
|
||||
DrawableElement::paint(self.take().unwrap(), view_state, cx);
|
||||
}
|
||||
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels> {
|
||||
DrawableElement::measure(self.as_mut().unwrap(), available_space, view_state, cx)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
DrawableElement::draw(
|
||||
self.take().unwrap(),
|
||||
origin,
|
||||
available_space,
|
||||
view_state,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
|
||||
|
||||
impl<V: 'static> AnyElement<V> {
|
||||
pub fn new<E>(element: E) -> Self
|
||||
where
|
||||
V: 'static,
|
||||
E: 'static + Element<V>,
|
||||
E::State: Any,
|
||||
{
|
||||
AnyElement(Box::new(Some(DrawableElement::new(element))) as Box<dyn ElementObject<V>>)
|
||||
}
|
||||
|
||||
pub fn element_id(&self) -> Option<ElementId> {
|
||||
self.0.element_id()
|
||||
}
|
||||
|
||||
pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
|
||||
self.0.layout(view_state, cx)
|
||||
}
|
||||
|
||||
pub fn paint(mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
|
||||
self.0.paint(view_state, cx)
|
||||
}
|
||||
|
||||
/// Initializes this element and performs layout within the given available space to determine its size.
|
||||
pub fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels> {
|
||||
self.0.measure(available_space, view_state, cx)
|
||||
}
|
||||
|
||||
/// Initializes this element and performs layout in the available space, then paints it at the given origin.
|
||||
pub fn draw(
|
||||
mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.0.draw(origin, available_space, view_state, cx)
|
||||
}
|
||||
|
||||
/// Converts this `AnyElement` into a trait object that can be stored and manipulated.
|
||||
pub fn into_any(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E, F> Component<V> for F
|
||||
where
|
||||
V: 'static,
|
||||
E: 'static + Component<V>,
|
||||
F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
|
||||
{
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(Some(self))
|
||||
impl<V: 'static> Element<V> for AnyElement<V> {
|
||||
type State = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let layout_id = self.layout(view_state, cx);
|
||||
(layout_id, ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
_: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.paint(view_state, cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> RenderOnce<V> for AnyElement<V> {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
AnyElement::element_id(self)
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// impl<V, E, F> Element<V> for Option<F>
|
||||
// where
|
||||
// V: 'static,
|
||||
// E: Element<V>,
|
||||
// F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
|
||||
// {
|
||||
// type State = Option<AnyElement<V>>;
|
||||
|
||||
// fn element_id(&self) -> Option<ElementId> {
|
||||
// None
|
||||
// }
|
||||
|
||||
// fn layout(
|
||||
// &mut self,
|
||||
// view_state: &mut V,
|
||||
// _: Option<Self::State>,
|
||||
// cx: &mut ViewContext<V>,
|
||||
// ) -> (LayoutId, Self::State) {
|
||||
// let render = self.take().unwrap();
|
||||
// let mut element = (render)(view_state, cx).into_any();
|
||||
// let layout_id = element.layout(view_state, cx);
|
||||
// (layout_id, Some(element))
|
||||
// }
|
||||
|
||||
// fn paint(
|
||||
// self,
|
||||
// _bounds: Bounds<Pixels>,
|
||||
// view_state: &mut V,
|
||||
// rendered_element: &mut Self::State,
|
||||
// cx: &mut ViewContext<V>,
|
||||
// ) {
|
||||
// rendered_element.take().unwrap().paint(view_state, cx);
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<V, E, F> RenderOnce<V> for Option<F>
|
||||
// where
|
||||
// V: 'static,
|
||||
// E: Element<V>,
|
||||
// F: FnOnce(&mut V, &mut ViewContext<V>) -> E + 'static,
|
||||
// {
|
||||
// type Element = Self;
|
||||
|
||||
// fn render(self) -> Self::Element {
|
||||
// self
|
||||
// }
|
||||
// }
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
|
||||
BorrowWindow, Bounds, ClickEvent, Component, DispatchPhase, Element, ElementId, FocusEvent,
|
||||
FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
|
||||
MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels, Point, Render, ScrollWheelEvent,
|
||||
SharedString, Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
|
||||
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusEvent, FocusHandle,
|
||||
KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||
MouseUpEvent, ParentElement, Pixels, Point, Render, RenderOnce, ScrollWheelEvent, SharedString,
|
||||
Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use refineable::Refineable;
|
||||
@ -28,7 +28,7 @@ pub struct GroupStyle {
|
||||
pub style: StyleRefinement,
|
||||
}
|
||||
|
||||
pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
|
||||
pub trait InteractiveElement<V: 'static>: Sized + Element<V> {
|
||||
fn interactivity(&mut self) -> &mut Interactivity<V>;
|
||||
|
||||
fn group(mut self, group: impl Into<SharedString>) -> Self {
|
||||
@ -314,7 +314,7 @@ pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveComponent<V> {
|
||||
pub trait StatefulInteractiveElement<V: 'static, E: Element<V>>: InteractiveElement<V> {
|
||||
fn focusable(mut self) -> Focusable<V, Self> {
|
||||
self.interactivity().focusable = true;
|
||||
Focusable {
|
||||
@ -381,7 +381,7 @@ pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveCo
|
||||
) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
W: 'static + Render,
|
||||
W: 'static + Render<W>,
|
||||
{
|
||||
debug_assert!(
|
||||
self.interactivity().drag_listener.is_none(),
|
||||
@ -425,7 +425,7 @@ pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveCo
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FocusableComponent<V: 'static>: InteractiveComponent<V> {
|
||||
pub trait FocusableElement<V: 'static>: InteractiveElement<V> {
|
||||
fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
@ -587,31 +587,27 @@ impl<V> Styled for Div<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> InteractiveComponent<V> for Div<V> {
|
||||
impl<V: 'static> InteractiveElement<V> for Div<V> {
|
||||
fn interactivity(&mut self) -> &mut Interactivity<V> {
|
||||
&mut self.interactivity
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentComponent<V> for Div<V> {
|
||||
impl<V: 'static> ParentElement<V> for Div<V> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for Div<V> {
|
||||
type ElementState = DivState;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.interactivity.element_id.clone()
|
||||
}
|
||||
type State = DivState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
) -> (LayoutId, Self::State) {
|
||||
let mut child_layout_ids = SmallVec::new();
|
||||
let mut interactivity = mem::take(&mut self.interactivity);
|
||||
let (layout_id, interactive_state) = interactivity.layout(
|
||||
@ -639,10 +635,10 @@ impl<V: 'static> Element<V> for Div<V> {
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||
@ -658,8 +654,7 @@ impl<V: 'static> Element<V> for Div<V> {
|
||||
(child_max - child_min).into()
|
||||
};
|
||||
|
||||
let mut interactivity = mem::take(&mut self.interactivity);
|
||||
interactivity.paint(
|
||||
self.interactivity.paint(
|
||||
bounds,
|
||||
content_size,
|
||||
&mut element_state.interactive_state,
|
||||
@ -679,7 +674,7 @@ impl<V: 'static> Element<V> for Div<V> {
|
||||
cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||
cx.with_content_mask(style.overflow_mask(bounds), |cx| {
|
||||
cx.with_element_offset(scroll_offset, |cx| {
|
||||
for child in &mut self.children {
|
||||
for child in self.children {
|
||||
child.paint(view_state, cx);
|
||||
}
|
||||
})
|
||||
@ -689,13 +684,18 @@ impl<V: 'static> Element<V> for Div<V> {
|
||||
})
|
||||
},
|
||||
);
|
||||
self.interactivity = interactivity;
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Div<V> {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
impl<V: 'static> RenderOnce<V> for Div<V> {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.interactivity.element_id.clone()
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,7 +770,7 @@ where
|
||||
}
|
||||
|
||||
pub fn paint(
|
||||
&mut self,
|
||||
mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
content_size: Size<Pixels>,
|
||||
element_state: &mut InteractiveElementState,
|
||||
@ -786,25 +786,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
for listener in self.mouse_down_listeners.drain(..) {
|
||||
for listener in self.mouse_down_listeners {
|
||||
cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
|
||||
listener(state, event, &bounds, phase, cx);
|
||||
})
|
||||
}
|
||||
|
||||
for listener in self.mouse_up_listeners.drain(..) {
|
||||
for listener in self.mouse_up_listeners {
|
||||
cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
|
||||
listener(state, event, &bounds, phase, cx);
|
||||
})
|
||||
}
|
||||
|
||||
for listener in self.mouse_move_listeners.drain(..) {
|
||||
for listener in self.mouse_move_listeners {
|
||||
cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
|
||||
listener(state, event, &bounds, phase, cx);
|
||||
})
|
||||
}
|
||||
|
||||
for listener in self.scroll_wheel_listeners.drain(..) {
|
||||
for listener in self.scroll_wheel_listeners {
|
||||
cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
|
||||
listener(state, event, &bounds, phase, cx);
|
||||
})
|
||||
@ -840,7 +840,7 @@ where
|
||||
}
|
||||
|
||||
if cx.active_drag.is_some() {
|
||||
let drop_listeners = mem::take(&mut self.drop_listeners);
|
||||
let drop_listeners = self.drop_listeners;
|
||||
cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
|
||||
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
|
||||
if let Some(drag_state_type) =
|
||||
@ -1062,24 +1062,24 @@ where
|
||||
self.key_context.clone(),
|
||||
element_state.focus_handle.clone(),
|
||||
|_, cx| {
|
||||
for listener in self.key_down_listeners.drain(..) {
|
||||
for listener in self.key_down_listeners {
|
||||
cx.on_key_event(move |state, event: &KeyDownEvent, phase, cx| {
|
||||
listener(state, event, phase, cx);
|
||||
})
|
||||
}
|
||||
|
||||
for listener in self.key_up_listeners.drain(..) {
|
||||
for listener in self.key_up_listeners {
|
||||
cx.on_key_event(move |state, event: &KeyUpEvent, phase, cx| {
|
||||
listener(state, event, phase, cx);
|
||||
})
|
||||
}
|
||||
|
||||
for (action_type, listener) in self.action_listeners.drain(..) {
|
||||
for (action_type, listener) in self.action_listeners {
|
||||
cx.on_action(action_type, listener)
|
||||
}
|
||||
|
||||
if let Some(focus_handle) = element_state.focus_handle.as_ref() {
|
||||
for listener in self.focus_listeners.drain(..) {
|
||||
for listener in self.focus_listeners {
|
||||
let focus_handle = focus_handle.clone();
|
||||
cx.on_focus_changed(move |view, event, cx| {
|
||||
listener(view, &focus_handle, event, cx)
|
||||
@ -1264,19 +1264,19 @@ pub struct Focusable<V, E> {
|
||||
view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static, E: InteractiveComponent<V>> FocusableComponent<V> for Focusable<V, E> {}
|
||||
impl<V: 'static + Render<V>, E: InteractiveElement<V>> FocusableElement<V> for Focusable<V, E> {}
|
||||
|
||||
impl<V, E> InteractiveComponent<V> for Focusable<V, E>
|
||||
impl<V, E> InteractiveElement<V> for Focusable<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
E: InteractiveComponent<V>,
|
||||
V: 'static + Render<V>,
|
||||
E: InteractiveElement<V>,
|
||||
{
|
||||
fn interactivity(&mut self) -> &mut Interactivity<V> {
|
||||
self.element.interactivity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: StatefulInteractiveComponent<V, E>> StatefulInteractiveComponent<V, E>
|
||||
impl<V: 'static + Render<V>, E: StatefulInteractiveElement<V, E>> StatefulInteractiveElement<V, E>
|
||||
for Focusable<V, E>
|
||||
{
|
||||
}
|
||||
@ -1293,49 +1293,51 @@ where
|
||||
|
||||
impl<V, E> Element<V> for Focusable<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
V: 'static + Render<V>,
|
||||
E: Element<V>,
|
||||
{
|
||||
type ElementState = E::ElementState;
|
||||
type State = E::State;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.element.layout(view_state, element_state, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.element.paint(bounds, view_state, element_state, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E> RenderOnce<V> for Focusable<V, E>
|
||||
where
|
||||
V: 'static + Render<V>,
|
||||
E: Element<V>,
|
||||
{
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.element.element_id()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
self.element.layout(view_state, element_state, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.element.paint(bounds, view_state, element_state, cx);
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E> Component<V> for Focusable<V, E>
|
||||
impl<V, E> ParentElement<V> for Focusable<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
E: 'static + Element<V>,
|
||||
{
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E> ParentComponent<V> for Focusable<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
E: ParentComponent<V>,
|
||||
E: ParentElement<V>,
|
||||
{
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
self.element.children_mut()
|
||||
@ -1357,71 +1359,73 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E> StatefulInteractiveComponent<V, E> for Stateful<V, E>
|
||||
impl<V, E> StatefulInteractiveElement<V, E> for Stateful<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
E: Element<V>,
|
||||
Self: InteractiveComponent<V>,
|
||||
Self: InteractiveElement<V>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<V, E> InteractiveComponent<V> for Stateful<V, E>
|
||||
impl<V, E> InteractiveElement<V> for Stateful<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
E: InteractiveComponent<V>,
|
||||
E: InteractiveElement<V>,
|
||||
{
|
||||
fn interactivity(&mut self) -> &mut Interactivity<V> {
|
||||
self.element.interactivity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {}
|
||||
impl<V: 'static, E: FocusableElement<V>> FocusableElement<V> for Stateful<V, E> {}
|
||||
|
||||
impl<V, E> Element<V> for Stateful<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
E: Element<V>,
|
||||
{
|
||||
type ElementState = E::ElementState;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.element.element_id()
|
||||
}
|
||||
type State = E::State;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.element.layout(view_state, element_state, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.element.paint(bounds, view_state, element_state, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E> Component<V> for Stateful<V, E>
|
||||
impl<V, E> RenderOnce<V> for Stateful<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
E: 'static + Element<V>,
|
||||
E: Element<V>,
|
||||
{
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.element.element_id()
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E> ParentComponent<V> for Stateful<V, E>
|
||||
impl<V, E> ParentElement<V> for Stateful<V, E>
|
||||
where
|
||||
V: 'static,
|
||||
E: ParentComponent<V>,
|
||||
E: ParentElement<V>,
|
||||
{
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
self.element.children_mut()
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
AnyElement, BorrowWindow, Bounds, Component, Element, InteractiveComponent,
|
||||
InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
|
||||
Styled, ViewContext,
|
||||
BorrowWindow, Bounds, Element, InteractiveElement, InteractiveElementState, Interactivity,
|
||||
LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, ViewContext,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use util::ResultExt;
|
||||
@ -35,35 +34,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Component<V> for Img<V> {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Element<V> for Img<V> {
|
||||
type ElementState = InteractiveElementState;
|
||||
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
self.interactivity.element_id.clone()
|
||||
}
|
||||
type State = InteractiveElementState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.interactivity.layout(element_state, cx, |style, cx| {
|
||||
cx.request_layout(&style, None)
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.interactivity.paint(
|
||||
@ -102,13 +91,25 @@ impl<V> Element<V> for Img<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> RenderOnce<V> for Img<V> {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
self.interactivity.element_id.clone()
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Styled for Img<V> {
|
||||
fn style(&mut self) -> &mut StyleRefinement {
|
||||
&mut self.interactivity.base_style
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> InteractiveComponent<V> for Img<V> {
|
||||
impl<V> InteractiveElement<V> for Img<V> {
|
||||
fn interactivity(&mut self) -> &mut Interactivity<V> {
|
||||
&mut self.interactivity
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use smallvec::SmallVec;
|
||||
use taffy::style::{Display, Position};
|
||||
|
||||
use crate::{
|
||||
point, AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, ParentComponent, Pixels,
|
||||
Point, Size, Style,
|
||||
point, AnyElement, BorrowWindow, Bounds, Element, LayoutId, ParentElement, Pixels, Point,
|
||||
RenderOnce, Size, Style,
|
||||
};
|
||||
|
||||
pub struct OverlayState {
|
||||
@ -51,31 +51,21 @@ impl<V> Overlay<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentComponent<V> for Overlay<V> {
|
||||
impl<V: 'static> ParentElement<V> for Overlay<V> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Overlay<V> {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for Overlay<V> {
|
||||
type ElementState = OverlayState;
|
||||
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
None
|
||||
}
|
||||
type State = OverlayState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
_: Option<Self::ElementState>,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut crate::ViewContext<V>,
|
||||
) -> (crate::LayoutId, Self::ElementState) {
|
||||
) -> (crate::LayoutId, Self::State) {
|
||||
let child_layout_ids = self
|
||||
.children
|
||||
.iter_mut()
|
||||
@ -92,10 +82,10 @@ impl<V: 'static> Element<V> for Overlay<V> {
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: crate::Bounds<crate::Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut crate::ViewContext<V>,
|
||||
) {
|
||||
if element_state.child_layout_ids.is_empty() {
|
||||
@ -156,13 +146,25 @@ impl<V: 'static> Element<V> for Overlay<V> {
|
||||
}
|
||||
|
||||
cx.with_element_offset(desired.origin - bounds.origin, |cx| {
|
||||
for child in &mut self.children {
|
||||
for child in self.children {
|
||||
child.paint(view_state, cx);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> RenderOnce<V> for Overlay<V> {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
enum Axis {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
AnyElement, Bounds, Component, Element, ElementId, InteractiveComponent,
|
||||
InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
|
||||
Styled, ViewContext,
|
||||
Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
|
||||
LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, ViewContext,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
@ -24,35 +23,25 @@ impl<V> Svg<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Component<V> for Svg<V> {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Element<V> for Svg<V> {
|
||||
type ElementState = InteractiveElementState;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.interactivity.element_id.clone()
|
||||
}
|
||||
type State = InteractiveElementState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.interactivity.layout(element_state, cx, |style, cx| {
|
||||
cx.request_layout(&style, None)
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) where
|
||||
Self: Sized,
|
||||
@ -66,13 +55,25 @@ impl<V> Element<V> for Svg<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> RenderOnce<V> for Svg<V> {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.interactivity.element_id.clone()
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Styled for Svg<V> {
|
||||
fn style(&mut self) -> &mut StyleRefinement {
|
||||
&mut self.interactivity.base_style
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> InteractiveComponent<V> for Svg<V> {
|
||||
impl<V> InteractiveElement<V> for Svg<V> {
|
||||
fn interactivity(&mut self) -> &mut Interactivity<V> {
|
||||
&mut self.interactivity
|
||||
}
|
||||
|
@ -1,50 +1,116 @@
|
||||
use crate::{
|
||||
AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels,
|
||||
SharedString, Size, TextRun, ViewContext, WrappedLine,
|
||||
BorrowWindow, Bounds, Element, ElementId, LayoutId, Pixels, RenderOnce, SharedString, Size,
|
||||
TextRun, ViewContext, WindowContext, WrappedLine,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::Cell, rc::Rc, sync::Arc};
|
||||
use util::ResultExt;
|
||||
|
||||
pub struct Text {
|
||||
impl<V: 'static> Element<V> for &'static str {
|
||||
type State = TextState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_: &mut V,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let mut state = TextState::default();
|
||||
let layout_id = state.layout(SharedString::from(*self), None, cx);
|
||||
(layout_id, state)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut V,
|
||||
state: &mut TextState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
state.paint(bounds, self, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> RenderOnce<V> for &'static str {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for SharedString {
|
||||
type State = TextState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_: &mut V,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let mut state = TextState::default();
|
||||
let layout_id = state.layout(self.clone(), None, cx);
|
||||
(layout_id, state)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut V,
|
||||
state: &mut TextState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let text_str: &str = self.as_ref();
|
||||
state.paint(bounds, text_str, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> RenderOnce<V> for SharedString {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
Some(self.clone().into())
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StyledText {
|
||||
text: SharedString,
|
||||
runs: Option<Vec<TextRun>>,
|
||||
}
|
||||
|
||||
impl Text {
|
||||
impl StyledText {
|
||||
/// Renders text with runs of different styles.
|
||||
///
|
||||
/// Callers are responsible for setting the correct style for each run.
|
||||
/// For text with a uniform style, you can usually avoid calling this constructor
|
||||
/// and just pass text directly.
|
||||
pub fn styled(text: SharedString, runs: Vec<TextRun>) -> Self {
|
||||
Text {
|
||||
pub fn new(text: SharedString, runs: Vec<TextRun>) -> Self {
|
||||
StyledText {
|
||||
text,
|
||||
runs: Some(runs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Text {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for Text {
|
||||
type ElementState = TextState;
|
||||
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
None
|
||||
}
|
||||
impl<V: 'static> Element<V> for StyledText {
|
||||
type State = TextState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_view: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
) -> (LayoutId, Self::State) {
|
||||
let element_state = element_state.unwrap_or_default();
|
||||
let text_system = cx.text_system().clone();
|
||||
let text_style = cx.text_style();
|
||||
@ -118,16 +184,16 @@ impl<V: 'static> Element<V> for Text {
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let element_state = element_state.lock();
|
||||
let element_state = element_state
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("measurement has not been performed on {}", &self.text))
|
||||
.ok_or_else(|| anyhow!("measurement has not been performed on {}", &self.text))
|
||||
.unwrap();
|
||||
|
||||
let line_height = element_state.line_height;
|
||||
@ -139,15 +205,21 @@ impl<V: 'static> Element<V> for Text {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
|
||||
impl<V: 'static> RenderOnce<V> for StyledText {
|
||||
type Element = Self;
|
||||
|
||||
impl TextState {
|
||||
fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
|
||||
self.0.lock()
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
|
||||
|
||||
struct TextStateInner {
|
||||
lines: SmallVec<[WrappedLine; 1]>,
|
||||
line_height: Pixels,
|
||||
@ -155,9 +227,108 @@ struct TextStateInner {
|
||||
size: Option<Size<Pixels>>,
|
||||
}
|
||||
|
||||
impl TextState {
|
||||
fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
|
||||
self.0.lock()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
text: SharedString,
|
||||
runs: Option<Vec<TextRun>>,
|
||||
cx: &mut WindowContext,
|
||||
) -> LayoutId {
|
||||
let text_system = cx.text_system().clone();
|
||||
let text_style = cx.text_style();
|
||||
let font_size = text_style.font_size.to_pixels(cx.rem_size());
|
||||
let line_height = text_style
|
||||
.line_height
|
||||
.to_pixels(font_size.into(), cx.rem_size());
|
||||
let text = SharedString::from(text);
|
||||
|
||||
let rem_size = cx.rem_size();
|
||||
|
||||
let runs = if let Some(runs) = runs {
|
||||
runs
|
||||
} else {
|
||||
vec![text_style.to_run(text.len())]
|
||||
};
|
||||
|
||||
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
|
||||
let element_state = self.clone();
|
||||
|
||||
move |known_dimensions, available_space| {
|
||||
let wrap_width = known_dimensions.width.or(match available_space.width {
|
||||
crate::AvailableSpace::Definite(x) => Some(x),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if let Some(text_state) = element_state.0.lock().as_ref() {
|
||||
if text_state.size.is_some()
|
||||
&& (wrap_width.is_none() || wrap_width == text_state.wrap_width)
|
||||
{
|
||||
return text_state.size.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let Some(lines) = text_system
|
||||
.shape_text(
|
||||
&text,
|
||||
font_size,
|
||||
&runs[..],
|
||||
wrap_width, // Wrap if we know the width.
|
||||
)
|
||||
.log_err()
|
||||
else {
|
||||
element_state.lock().replace(TextStateInner {
|
||||
lines: Default::default(),
|
||||
line_height,
|
||||
wrap_width,
|
||||
size: Some(Size::default()),
|
||||
});
|
||||
return Size::default();
|
||||
};
|
||||
|
||||
let mut size: Size<Pixels> = Size::default();
|
||||
for line in &lines {
|
||||
let line_size = line.size(line_height);
|
||||
size.height += line_size.height;
|
||||
size.width = size.width.max(line_size.width);
|
||||
}
|
||||
|
||||
element_state.lock().replace(TextStateInner {
|
||||
lines,
|
||||
line_height,
|
||||
wrap_width,
|
||||
size: Some(size),
|
||||
});
|
||||
|
||||
size
|
||||
}
|
||||
});
|
||||
|
||||
layout_id
|
||||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
|
||||
let element_state = self.lock();
|
||||
let element_state = element_state
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
|
||||
.unwrap();
|
||||
|
||||
let line_height = element_state.line_height;
|
||||
let mut line_origin = bounds.origin;
|
||||
for line in &element_state.lines {
|
||||
line.paint(line_origin, line_height, cx).log_err();
|
||||
line_origin.y += line.size(line_height).height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InteractiveText {
|
||||
id: ElementId,
|
||||
text: Text,
|
||||
text: StyledText,
|
||||
}
|
||||
|
||||
struct InteractiveTextState {
|
||||
@ -166,18 +337,14 @@ struct InteractiveTextState {
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for InteractiveText {
|
||||
type ElementState = InteractiveTextState;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
type State = InteractiveTextState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
) -> (LayoutId, Self::State) {
|
||||
if let Some(InteractiveTextState {
|
||||
text_state,
|
||||
clicked_range_ixs,
|
||||
@ -200,10 +367,10 @@ impl<V: 'static> Element<V> for InteractiveText {
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: Bounds<Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.text
|
||||
@ -211,34 +378,14 @@ impl<V: 'static> Element<V> for InteractiveText {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for SharedString {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
Text {
|
||||
text: self,
|
||||
runs: None,
|
||||
}
|
||||
.render()
|
||||
}
|
||||
}
|
||||
impl<V: 'static> RenderOnce<V> for InteractiveText {
|
||||
type Element = Self;
|
||||
|
||||
impl<V: 'static> Component<V> for &'static str {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
Text {
|
||||
text: self.into(),
|
||||
runs: None,
|
||||
}
|
||||
.render()
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Figure out how to pass `String` to `child` without this.
|
||||
// This impl doesn't exist in the `gpui2` crate.
|
||||
impl<V: 'static> Component<V> for String {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
Text {
|
||||
text: self.into(),
|
||||
runs: None,
|
||||
}
|
||||
.render()
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
use crate::{
|
||||
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element,
|
||||
ElementId, InteractiveComponent, InteractiveElementState, Interactivity, LayoutId, Pixels,
|
||||
Point, Size, StyleRefinement, Styled, ViewContext,
|
||||
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Element, ElementId,
|
||||
InteractiveElement, InteractiveElementState, Interactivity, LayoutId, Pixels, Point,
|
||||
RenderOnce, Size, StyleRefinement, Styled, ViewContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::RefCell, cmp, mem, ops::Range, rc::Rc};
|
||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||
use taffy::style::Overflow;
|
||||
|
||||
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
|
||||
/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
|
||||
/// uniform_list will only render the visibile subset of items.
|
||||
pub fn uniform_list<I, V, C>(
|
||||
pub fn uniform_list<I, V, E>(
|
||||
id: I,
|
||||
item_count: usize,
|
||||
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<C>,
|
||||
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<E>,
|
||||
) -> UniformList<V>
|
||||
where
|
||||
I: Into<ElementId>,
|
||||
V: 'static,
|
||||
C: Component<V>,
|
||||
E: Element<V>,
|
||||
{
|
||||
let id = id.into();
|
||||
let mut style = StyleRefinement::default();
|
||||
@ -32,7 +32,7 @@ where
|
||||
render_items: Box::new(move |view, visible_range, cx| {
|
||||
f(view, visible_range, cx)
|
||||
.into_iter()
|
||||
.map(|component| component.render())
|
||||
.map(|component| component.into_any())
|
||||
.collect()
|
||||
}),
|
||||
interactivity: Interactivity {
|
||||
@ -102,18 +102,14 @@ pub struct UniformListState {
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for UniformList<V> {
|
||||
type ElementState = UniformListState;
|
||||
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
type State = UniformListState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
) -> (LayoutId, Self::State) {
|
||||
let max_items = self.item_count;
|
||||
let rem_size = cx.rem_size();
|
||||
let item_size = element_state
|
||||
@ -159,10 +155,10 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: Bounds<crate::Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let style =
|
||||
@ -183,14 +179,17 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
height: item_size.height * self.item_count,
|
||||
};
|
||||
|
||||
let mut interactivity = mem::take(&mut self.interactivity);
|
||||
let shared_scroll_offset = element_state
|
||||
.interactive
|
||||
.scroll_offset
|
||||
.get_or_insert_with(Rc::default)
|
||||
.clone();
|
||||
|
||||
interactivity.paint(
|
||||
let item_height = self
|
||||
.measure_item(view_state, Some(padded_bounds.size.width), cx)
|
||||
.height;
|
||||
|
||||
self.interactivity.paint(
|
||||
bounds,
|
||||
content_size,
|
||||
&mut element_state.interactive,
|
||||
@ -209,9 +208,6 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
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.borrow_mut().replace(ScrollHandleState {
|
||||
item_height,
|
||||
@ -233,9 +229,9 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
self.item_count,
|
||||
);
|
||||
|
||||
let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
|
||||
let 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) {
|
||||
for (item, ix) in items.into_iter().zip(visible_range) {
|
||||
let item_origin = padded_bounds.origin
|
||||
+ point(px(0.), item_height * ix + scroll_offset.y);
|
||||
let available_space = size(
|
||||
@ -249,7 +245,18 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
})
|
||||
},
|
||||
);
|
||||
self.interactivity = interactivity;
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> RenderOnce<V> for UniformList<V> {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,14 +294,8 @@ impl<V> UniformList<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> InteractiveComponent<V> for UniformList<V> {
|
||||
impl<V> InteractiveElement<V> for UniformList<V> {
|
||||
fn interactivity(&mut self) -> &mut crate::Interactivity<V> {
|
||||
&mut self.interactivity
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for UniformList<V> {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
@ -78,8 +78,6 @@ use std::{
|
||||
};
|
||||
use taffy::TaffyLayoutEngine;
|
||||
|
||||
type AnyBox = Box<dyn Any>;
|
||||
|
||||
pub trait Context {
|
||||
type Result<T>;
|
||||
|
||||
@ -123,7 +121,7 @@ pub trait VisualContext: Context {
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: 'static + Render;
|
||||
V: 'static + Render<V>;
|
||||
|
||||
fn update_view<V: 'static, R>(
|
||||
&mut self,
|
||||
@ -136,7 +134,7 @@ pub trait VisualContext: Context {
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: Render;
|
||||
V: 'static + Render<V>;
|
||||
|
||||
fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
|
||||
where
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
div, point, Component, Div, FocusHandle, Keystroke, Modifiers, Pixels, Point, Render,
|
||||
ViewContext,
|
||||
div, point, Div, Element, FocusHandle, Keystroke, Modifiers, Pixels, Point, Render, ViewContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf};
|
||||
@ -64,7 +63,7 @@ pub struct Drag<S, R, V, E>
|
||||
where
|
||||
R: Fn(&mut V, &mut ViewContext<V>) -> E,
|
||||
V: 'static,
|
||||
E: Component<()>,
|
||||
E: Element<()>,
|
||||
{
|
||||
pub state: S,
|
||||
pub render_drag_handle: R,
|
||||
@ -75,7 +74,7 @@ impl<S, R, V, E> Drag<S, R, V, E>
|
||||
where
|
||||
R: Fn(&mut V, &mut ViewContext<V>) -> E,
|
||||
V: 'static,
|
||||
E: Component<()>,
|
||||
E: Element<()>,
|
||||
{
|
||||
pub fn new(state: S, render_drag_handle: R) -> Self {
|
||||
Drag {
|
||||
@ -193,7 +192,7 @@ impl Deref for MouseExitEvent {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
|
||||
|
||||
impl Render for ExternalPaths {
|
||||
impl Render<Self> for ExternalPaths {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -286,8 +285,8 @@ pub struct FocusEvent {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
self as gpui, div, Component, Div, FocusHandle, InteractiveComponent, KeyBinding,
|
||||
Keystroke, ParentComponent, Render, Stateful, TestAppContext, ViewContext, VisualContext,
|
||||
self as gpui, div, Div, FocusHandle, InteractiveElement, KeyBinding, Keystroke,
|
||||
ParentElement, Render, Stateful, TestAppContext, VisualContext,
|
||||
};
|
||||
|
||||
struct TestView {
|
||||
@ -298,7 +297,7 @@ mod test {
|
||||
|
||||
actions!(TestAction);
|
||||
|
||||
impl Render for TestView {
|
||||
impl Render<Self> for TestView {
|
||||
type Element = Stateful<Self, Div<Self>>;
|
||||
|
||||
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
@ -307,12 +306,7 @@ mod test {
|
||||
.key_context("parent")
|
||||
.on_key_down(|this: &mut TestView, _, _, _| this.saw_key_down = true)
|
||||
.on_action(|this: &mut TestView, _: &TestAction, _| this.saw_action = true)
|
||||
.child(|this: &mut Self, _cx: &mut ViewContext<Self>| {
|
||||
div()
|
||||
.key_context("nested")
|
||||
.track_focus(&this.focus_handle)
|
||||
.render()
|
||||
}),
|
||||
.child(div().key_context("nested").track_focus(&self.focus_handle)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub use crate::{
|
||||
BorrowAppContext, BorrowWindow, Component, Context, FocusableComponent, InteractiveComponent,
|
||||
ParentComponent, Refineable, Render, StatefulInteractiveComponent, Styled, VisualContext,
|
||||
BorrowAppContext, BorrowWindow, Component, Context, Element, FocusableElement,
|
||||
InteractiveElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement,
|
||||
Styled, VisualContext,
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
private::Sealed, AnyBox, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace,
|
||||
BorrowWindow, Bounds, Component, Element, ElementId, Entity, EntityId, Flatten, FocusHandle,
|
||||
FocusableView, LayoutId, Model, Pixels, Point, Size, ViewContext, VisualContext, WeakModel,
|
||||
private::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
|
||||
Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, LayoutId,
|
||||
Model, Pixels, Point, Render, RenderOnce, Size, ViewContext, VisualContext, WeakModel,
|
||||
WindowContext,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
@ -10,14 +10,8 @@ use std::{
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
pub trait Render: 'static + Sized {
|
||||
type Element: Element<Self> + 'static;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element;
|
||||
}
|
||||
|
||||
pub struct View<V> {
|
||||
pub(crate) model: Model<V>,
|
||||
pub model: Model<V>,
|
||||
}
|
||||
|
||||
impl<V> Sealed for View<V> {}
|
||||
@ -65,13 +59,13 @@ impl<V: 'static> View<V> {
|
||||
self.model.read(cx)
|
||||
}
|
||||
|
||||
pub fn render_with<C>(&self, component: C) -> RenderViewWith<C, V>
|
||||
pub fn render_with<E>(&self, component: E) -> RenderViewWith<E, V>
|
||||
where
|
||||
C: 'static + Component<V>,
|
||||
E: 'static + Element<V>,
|
||||
{
|
||||
RenderViewWith {
|
||||
view: self.clone(),
|
||||
component: Some(component),
|
||||
element: Some(component),
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,12 +99,6 @@ impl<V> PartialEq for View<V> {
|
||||
|
||||
impl<V> Eq for View<V> {}
|
||||
|
||||
impl<V: Render, ParentViewState: 'static> Component<ParentViewState> for View<V> {
|
||||
fn render(self) -> AnyElement<ParentViewState> {
|
||||
AnyElement::new(AnyView::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WeakView<V> {
|
||||
pub(crate) model: WeakModel<V>,
|
||||
}
|
||||
@ -164,7 +152,7 @@ impl<V> Eq for WeakView<V> {}
|
||||
pub struct AnyView {
|
||||
model: AnyModel,
|
||||
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, Box<dyn Any>),
|
||||
paint: fn(&AnyView, &mut AnyBox, &mut WindowContext),
|
||||
paint: fn(&AnyView, Box<dyn Any>, &mut WindowContext),
|
||||
}
|
||||
|
||||
impl AnyView {
|
||||
@ -202,22 +190,16 @@ impl AnyView {
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
cx.with_absolute_element_offset(origin, |cx| {
|
||||
let (layout_id, mut rendered_element) = (self.layout)(self, cx);
|
||||
let (layout_id, rendered_element) = (self.layout)(self, cx);
|
||||
cx.window
|
||||
.layout_engine
|
||||
.compute_layout(layout_id, available_space);
|
||||
(self.paint)(self, &mut rendered_element, cx);
|
||||
(self.paint)(self, rendered_element, cx);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for AnyView {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Render> From<View<V>> for AnyView {
|
||||
impl<V: 'static + Render<V>> From<View<V>> for AnyView {
|
||||
fn from(value: View<V>) -> Self {
|
||||
AnyView {
|
||||
model: value.model.into_any(),
|
||||
@ -227,37 +209,87 @@ impl<V: Render> From<View<V>> for AnyView {
|
||||
}
|
||||
}
|
||||
|
||||
impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
|
||||
type ElementState = Box<dyn Any>;
|
||||
impl<V: 'static + Render<V>, ParentV: 'static> Element<ParentV> for View<V> {
|
||||
type State = Option<AnyElement<V>>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_parent_view: &mut ParentV,
|
||||
_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<ParentV>,
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.update(cx, |view, cx| {
|
||||
let mut element = view.render(cx).into_any();
|
||||
let layout_id = element.layout(view, cx);
|
||||
(layout_id, Some(element))
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
_: Bounds<Pixels>,
|
||||
_parent: &mut ParentV,
|
||||
element: &mut Self::State,
|
||||
cx: &mut ViewContext<ParentV>,
|
||||
) {
|
||||
self.update(cx, |view, cx| {
|
||||
element.take().unwrap().paint(view, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static + Render<V>, ParentV: 'static> RenderOnce<ParentV> for View<V> {
|
||||
type Element = View<V>;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
Some(self.model.entity_id.into())
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for AnyView {
|
||||
type State = Option<Box<dyn Any>>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_view_state: &mut ParentViewState,
|
||||
_element_state: Option<Self::ElementState>,
|
||||
cx: &mut ViewContext<ParentViewState>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
(self.layout)(self, cx)
|
||||
_view_state: &mut V,
|
||||
_element_state: Option<Self::State>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (LayoutId, Self::State) {
|
||||
let (layout_id, rendered_element) = (self.layout)(self, cx);
|
||||
(layout_id, Some(rendered_element))
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_view_state: &mut ParentViewState,
|
||||
rendered_element: &mut Self::ElementState,
|
||||
cx: &mut ViewContext<ParentViewState>,
|
||||
_view_state: &mut V,
|
||||
rendered_element: &mut Self::State,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
(self.paint)(self, rendered_element, cx)
|
||||
(self.paint)(&mut self, rendered_element.take().unwrap(), cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<ParentV: 'static> RenderOnce<ParentV> for AnyView {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
Some(self.model.entity_id.into())
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyWeakView {
|
||||
model: AnyWeakModel,
|
||||
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, Box<dyn Any>),
|
||||
paint: fn(&AnyView, &mut AnyBox, &mut WindowContext),
|
||||
paint: fn(&AnyView, Box<dyn Any>, &mut WindowContext),
|
||||
}
|
||||
|
||||
impl AnyWeakView {
|
||||
@ -271,7 +303,7 @@ impl AnyWeakView {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Render> From<WeakView<V>> for AnyWeakView {
|
||||
impl<V: 'static + Render<V>> From<WeakView<V>> for AnyWeakView {
|
||||
fn from(view: WeakView<V>) -> Self {
|
||||
Self {
|
||||
model: view.model.into(),
|
||||
@ -281,67 +313,58 @@ impl<V: Render> From<WeakView<V>> for AnyWeakView {
|
||||
}
|
||||
}
|
||||
|
||||
// impl<T, E> Render for T
|
||||
// where
|
||||
// T: 'static + FnMut(&mut WindowContext) -> E,
|
||||
// E: 'static + Send + Element<T>,
|
||||
// {
|
||||
// type Element = E;
|
||||
|
||||
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
// (self)(cx)
|
||||
// }
|
||||
// }
|
||||
|
||||
pub struct RenderViewWith<C, V> {
|
||||
pub struct RenderViewWith<E, V> {
|
||||
view: View<V>,
|
||||
component: Option<C>,
|
||||
element: Option<E>,
|
||||
}
|
||||
|
||||
impl<C, ParentViewState, ViewState> Component<ParentViewState> for RenderViewWith<C, ViewState>
|
||||
impl<E, ParentV, V> Element<ParentV> for RenderViewWith<E, V>
|
||||
where
|
||||
C: 'static + Component<ViewState>,
|
||||
ParentViewState: 'static,
|
||||
ViewState: 'static,
|
||||
E: 'static + Element<V>,
|
||||
ParentV: 'static,
|
||||
V: 'static,
|
||||
{
|
||||
fn render(self) -> AnyElement<ParentViewState> {
|
||||
AnyElement::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, ParentViewState, ViewState> Element<ParentViewState> for RenderViewWith<C, ViewState>
|
||||
where
|
||||
C: 'static + Component<ViewState>,
|
||||
ParentViewState: 'static,
|
||||
ViewState: 'static,
|
||||
{
|
||||
type ElementState = AnyElement<ViewState>;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
Some(self.view.entity_id().into())
|
||||
}
|
||||
type State = Option<AnyElement<V>>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_: &mut ParentViewState,
|
||||
_: Option<Self::ElementState>,
|
||||
cx: &mut ViewContext<ParentViewState>,
|
||||
) -> (LayoutId, Self::ElementState) {
|
||||
_: &mut ParentV,
|
||||
_: Option<Self::State>,
|
||||
cx: &mut ViewContext<ParentV>,
|
||||
) -> (LayoutId, Self::State) {
|
||||
self.view.update(cx, |view, cx| {
|
||||
let mut element = self.component.take().unwrap().render();
|
||||
let mut element = self.element.take().unwrap().into_any();
|
||||
let layout_id = element.layout(view, cx);
|
||||
(layout_id, element)
|
||||
(layout_id, Some(element))
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
_: Bounds<Pixels>,
|
||||
_: &mut ParentViewState,
|
||||
element: &mut Self::ElementState,
|
||||
cx: &mut ViewContext<ParentViewState>,
|
||||
_: &mut ParentV,
|
||||
element: &mut Self::State,
|
||||
cx: &mut ViewContext<ParentV>,
|
||||
) {
|
||||
self.view.update(cx, |view, cx| element.paint(view, cx))
|
||||
self.view
|
||||
.update(cx, |view, cx| element.take().unwrap().paint(view, cx))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, V, ParentV> RenderOnce<ParentV> for RenderViewWith<E, V>
|
||||
where
|
||||
E: 'static + Element<V>,
|
||||
V: 'static,
|
||||
ParentV: 'static,
|
||||
{
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.element.as_ref().unwrap().element_id()
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,7 +372,7 @@ mod any_view {
|
||||
use crate::{AnyElement, AnyView, BorrowWindow, LayoutId, Render, WindowContext};
|
||||
use std::any::Any;
|
||||
|
||||
pub(crate) fn layout<V: Render>(
|
||||
pub(crate) fn layout<V: 'static + Render<V>>(
|
||||
view: &AnyView,
|
||||
cx: &mut WindowContext,
|
||||
) -> (LayoutId, Box<dyn Any>) {
|
||||
@ -363,14 +386,14 @@ mod any_view {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn paint<V: Render>(
|
||||
pub(crate) fn paint<V: 'static + Render<V>>(
|
||||
view: &AnyView,
|
||||
element: &mut Box<dyn Any>,
|
||||
element: Box<dyn Any>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
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();
|
||||
let element = element.downcast::<AnyElement<V>>().unwrap();
|
||||
view.update(cx, |view, cx| element.paint(view, cx))
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
key_dispatch::DispatchActionListener, px, size, Action, AnyBox, AnyDrag, AnyView, AppContext,
|
||||
key_dispatch::DispatchActionListener, px, size, Action, AnyDrag, AnyView, AppContext,
|
||||
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
|
||||
DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId,
|
||||
EventEmitter, FileDropEvent, Flatten, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla,
|
||||
@ -187,7 +187,7 @@ impl Drop for FocusHandle {
|
||||
|
||||
/// FocusableView allows users of your view to easily
|
||||
/// focus it (using cx.focus_view(view))
|
||||
pub trait FocusableView: Render {
|
||||
pub trait FocusableView: 'static + Render<Self> {
|
||||
fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ pub struct Window {
|
||||
|
||||
// #[derive(Default)]
|
||||
pub(crate) struct Frame {
|
||||
pub(crate) element_states: HashMap<GlobalElementId, AnyBox>,
|
||||
pub(crate) element_states: HashMap<GlobalElementId, Box<dyn Any>>,
|
||||
mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
|
||||
pub(crate) dispatch_tree: DispatchTree,
|
||||
pub(crate) focus_listeners: Vec<AnyFocusListener>,
|
||||
@ -1520,7 +1520,7 @@ impl VisualContext for WindowContext<'_> {
|
||||
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: 'static + Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
let slot = self.app.entities.reserve();
|
||||
let view = View {
|
||||
@ -1559,7 +1559,7 @@ impl VisualContext for WindowContext<'_> {
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: Render,
|
||||
V: 'static + Render<V>,
|
||||
{
|
||||
let slot = self.app.entities.reserve();
|
||||
let view = View {
|
||||
@ -2335,7 +2335,7 @@ impl<V> Context for ViewContext<'_, V> {
|
||||
}
|
||||
|
||||
impl<V: 'static> VisualContext for ViewContext<'_, V> {
|
||||
fn build_view<W: Render + 'static>(
|
||||
fn build_view<W: Render<W> + 'static>(
|
||||
&mut self,
|
||||
build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W,
|
||||
) -> Self::Result<View<W>> {
|
||||
@ -2355,7 +2355,7 @@ impl<V: 'static> VisualContext for ViewContext<'_, V> {
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, W>) -> W,
|
||||
) -> Self::Result<View<W>>
|
||||
where
|
||||
W: Render,
|
||||
W: 'static + Render<W>,
|
||||
{
|
||||
self.window_cx.replace_root_view(build_view)
|
||||
}
|
||||
@ -2400,7 +2400,7 @@ pub struct WindowHandle<V> {
|
||||
state_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static + Render> WindowHandle<V> {
|
||||
impl<V: 'static + Render<V>> WindowHandle<V> {
|
||||
pub fn new(id: WindowId) -> Self {
|
||||
WindowHandle {
|
||||
any_handle: AnyWindowHandle {
|
||||
|
64
crates/gpui2_macros/src/derive_render_once.rs
Normal file
64
crates/gpui2_macros/src/derive_render_once.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, parse_quote, DeriveInput};
|
||||
|
||||
pub fn derive_render_once(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let type_name = &ast.ident;
|
||||
|
||||
let mut trait_generics = ast.generics.clone();
|
||||
let view_type = if let Some(view_type) = specified_view_type(&ast) {
|
||||
quote! { #view_type }
|
||||
} else {
|
||||
if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
|
||||
if let syn::GenericParam::Type(type_param) = param {
|
||||
Some(type_param.ident.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
quote! { #first_type_param }
|
||||
} else {
|
||||
trait_generics.params.push(parse_quote! { V: 'static });
|
||||
quote! { V }
|
||||
}
|
||||
};
|
||||
|
||||
let (impl_generics, _, where_clause) = trait_generics.split_for_impl();
|
||||
let (_, type_generics, _) = ast.generics.split_for_impl();
|
||||
|
||||
let gen = quote! {
|
||||
impl #impl_generics gpui::RenderOnce<#view_type> for #type_name #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Element = gpui::CompositeElement<#view_type, Self>;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
gpui::CompositeElement::new(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
gen.into()
|
||||
}
|
||||
|
||||
fn specified_view_type(ast: &DeriveInput) -> Option<proc_macro2::Ident> {
|
||||
ast.attrs.iter().find_map(|attr| {
|
||||
if attr.path.is_ident("view") {
|
||||
if let Ok(syn::Meta::NameValue(meta_name_value)) = attr.parse_meta() {
|
||||
if let syn::Lit::Str(lit_str) = meta_name_value.lit {
|
||||
return Some(
|
||||
lit_str
|
||||
.parse::<syn::Ident>()
|
||||
.expect("Failed to parse view_type"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
@ -1,16 +1,12 @@
|
||||
mod action;
|
||||
mod derive_component;
|
||||
mod derive_render_once;
|
||||
mod register_action;
|
||||
mod style_helpers;
|
||||
mod test;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn style_helpers(args: TokenStream) -> TokenStream {
|
||||
style_helpers::style_helpers(args)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Action)]
|
||||
pub fn action(input: TokenStream) -> TokenStream {
|
||||
action::action(input)
|
||||
@ -26,6 +22,16 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
derive_component::derive_component(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(RenderOnce, attributes(view))]
|
||||
pub fn derive_render_once(input: TokenStream) -> TokenStream {
|
||||
derive_render_once::derive_render_once(input)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn style_helpers(input: TokenStream) -> TokenStream {
|
||||
style_helpers::style_helpers(input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
|
||||
test::test(args, function)
|
||||
|
@ -1,7 +1,7 @@
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
div, prelude::*, uniform_list, AppContext, Component, Div, FocusHandle, FocusableView,
|
||||
MouseButton, Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext,
|
||||
div, prelude::*, uniform_list, AppContext, Div, FocusHandle, FocusableView, MouseButton,
|
||||
Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext,
|
||||
};
|
||||
use std::{cmp, sync::Arc};
|
||||
use ui::{prelude::*, v_stack, Divider, Label, TextColor};
|
||||
@ -15,7 +15,7 @@ pub struct Picker<D: PickerDelegate> {
|
||||
}
|
||||
|
||||
pub trait PickerDelegate: Sized + 'static {
|
||||
type ListItem: Component<Picker<Self>>;
|
||||
type ListItem: RenderOnce<Picker<Self>>;
|
||||
|
||||
fn match_count(&self) -> usize;
|
||||
fn selected_index(&self) -> usize;
|
||||
@ -180,7 +180,7 @@ impl<D: PickerDelegate> Picker<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: PickerDelegate> Render for Picker<D> {
|
||||
impl<D: PickerDelegate> Render<Self> for Picker<D> {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -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, Focusable, FocusableView,
|
||||
InteractiveComponent, Model, MouseButton, ParentComponent, Pixels, Point, PromptLevel, Render,
|
||||
Stateful, StatefulInteractiveComponent, Styled, Task, UniformListScrollHandle, View,
|
||||
ViewContext, VisualContext as _, WeakView, WindowContext,
|
||||
ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement,
|
||||
Model, MouseButton, ParentElement, Pixels, Point, PromptLevel, Render, RenderOnce, Stateful,
|
||||
StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, ViewContext,
|
||||
VisualContext as _, WeakView, WindowContext,
|
||||
};
|
||||
use menu::{Confirm, SelectNext, SelectPrev};
|
||||
use project::{
|
||||
@ -247,7 +247,6 @@ impl ProjectPanel {
|
||||
let mut old_dock_position = this.position(cx);
|
||||
ProjectPanelSettings::register(cx);
|
||||
cx.observe_global::<SettingsStore>(move |this, cx| {
|
||||
dbg!("OLA!");
|
||||
let new_dock_position = this.position(cx);
|
||||
if new_dock_position != old_dock_position {
|
||||
old_dock_position = new_dock_position;
|
||||
@ -1424,7 +1423,7 @@ impl ProjectPanel {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ProjectPanel {
|
||||
impl Render<Self> for ProjectPanel {
|
||||
type Element = Focusable<Self, Stateful<Self, Div<Self>>>;
|
||||
|
||||
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
|
@ -5,7 +5,7 @@ use ui::prelude::*;
|
||||
|
||||
pub struct ColorsStory;
|
||||
|
||||
impl Render for ColorsStory {
|
||||
impl Render<Self> for ColorsStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -28,7 +28,7 @@ impl Render for ColorsStory {
|
||||
div()
|
||||
.w(px(75.))
|
||||
.line_height(px(24.))
|
||||
.child(scale.name().to_string()),
|
||||
.child(scale.name().clone()),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
|
@ -26,7 +26,7 @@ impl FocusStory {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for FocusStory {
|
||||
impl Render<Self> for FocusStory {
|
||||
type Element = Focusable<Self, Stateful<Self, Div<Self>>>;
|
||||
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
|
@ -11,7 +11,7 @@ impl KitchenSinkStory {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for KitchenSinkStory {
|
||||
impl Render<Self> for KitchenSinkStory {
|
||||
type Element = Stateful<Self, Div<Self>>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,5 +1,7 @@
|
||||
use fuzzy::StringMatchCandidate;
|
||||
use gpui::{div, prelude::*, Div, KeyBinding, Render, Styled, Task, View, WindowContext};
|
||||
use gpui::{
|
||||
div, prelude::*, Div, KeyBinding, Render, SharedString, Styled, Task, View, WindowContext,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use std::sync::Arc;
|
||||
use theme2::ActiveTheme;
|
||||
@ -54,7 +56,8 @@ impl PickerDelegate for Delegate {
|
||||
let Some(candidate_ix) = self.matches.get(ix) else {
|
||||
return div();
|
||||
};
|
||||
let candidate = self.candidates[*candidate_ix].string.clone();
|
||||
// TASK: Make StringMatchCandidate::string a SharedString
|
||||
let candidate = SharedString::from(self.candidates[*candidate_ix].string.clone());
|
||||
|
||||
div()
|
||||
.text_color(colors.text)
|
||||
@ -202,7 +205,7 @@ impl PickerStory {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for PickerStory {
|
||||
impl Render<Self> for PickerStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
|
@ -10,7 +10,7 @@ impl ScrollStory {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ScrollStory {
|
||||
impl Render<Self> for ScrollStory {
|
||||
type Element = Stateful<Self, Div<Self>>;
|
||||
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use gpui::{
|
||||
blue, div, red, white, Div, ParentComponent, Render, Styled, View, VisualContext, WindowContext,
|
||||
blue, div, red, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext,
|
||||
};
|
||||
use ui::v_stack;
|
||||
|
||||
@ -11,7 +11,7 @@ impl TextStory {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for TextStory {
|
||||
impl Render<Self> for TextStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::{px, rgb, Div, Hsla, Render};
|
||||
use gpui::{px, rgb, Div, Hsla, Render, RenderOnce};
|
||||
use ui::prelude::*;
|
||||
|
||||
use crate::story::Story;
|
||||
@ -7,7 +7,7 @@ use crate::story::Story;
|
||||
/// [https://developer.mozilla.org/en-US/docs/Web/CSS/z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index).
|
||||
pub struct ZIndexStory;
|
||||
|
||||
impl Render for ZIndexStory {
|
||||
impl Render<Self> for ZIndexStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -79,17 +79,15 @@ trait Styles: Styled + Sized {
|
||||
|
||||
impl<V: 'static> Styles for Div<V> {}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
struct ZIndexExample {
|
||||
z_index: u32,
|
||||
}
|
||||
|
||||
impl ZIndexExample {
|
||||
pub fn new(z_index: u32) -> Self {
|
||||
Self { z_index }
|
||||
}
|
||||
impl<V: 'static> Component<V> for ZIndexExample {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.relative()
|
||||
.size_full()
|
||||
@ -109,14 +107,14 @@ impl ZIndexExample {
|
||||
// HACK: Simulate `text-align: center`.
|
||||
.pl(px(24.))
|
||||
.z_index(self.z_index)
|
||||
.child(format!(
|
||||
.child(SharedString::from(format!(
|
||||
"z-index: {}",
|
||||
if self.z_index == 0 {
|
||||
"auto".to_string()
|
||||
} else {
|
||||
self.z_index.to_string()
|
||||
}
|
||||
)),
|
||||
))),
|
||||
)
|
||||
// Blue blocks.
|
||||
.child(
|
||||
@ -173,3 +171,9 @@ impl ZIndexExample {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ZIndexExample {
|
||||
pub fn new(z_index: u32) -> Self {
|
||||
Self { z_index }
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ impl StoryWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for StoryWrapper {
|
||||
impl Render<Self> for StoryWrapper {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -60,7 +60,7 @@ struct TestView {
|
||||
story: AnyView,
|
||||
}
|
||||
|
||||
impl Render for TestView {
|
||||
impl Render<Self> for TestView {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -4,7 +4,7 @@ use crate::TerminalView;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{
|
||||
actions, div, serde_json, AppContext, AsyncWindowContext, Div, Entity, EventEmitter,
|
||||
FocusHandle, FocusableView, ParentComponent, Render, Subscription, Task, View, ViewContext,
|
||||
FocusHandle, FocusableView, ParentElement, Render, Subscription, Task, View, ViewContext,
|
||||
VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use project::Fs;
|
||||
@ -335,7 +335,7 @@ impl TerminalPanel {
|
||||
|
||||
impl EventEmitter<PanelEvent> for TerminalPanel {}
|
||||
|
||||
impl Render for TerminalPanel {
|
||||
impl Render<Self> for TerminalPanel {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -9,10 +9,10 @@ pub mod terminal_panel;
|
||||
// use crate::terminal_element::TerminalElement;
|
||||
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
||||
use gpui::{
|
||||
actions, div, Action, AnyElement, AppContext, Component, DispatchPhase, Div, EventEmitter,
|
||||
FocusEvent, FocusHandle, Focusable, FocusableComponent, FocusableView, InputHandler,
|
||||
InteractiveComponent, KeyDownEvent, Keystroke, Model, MouseButton, ParentComponent, Pixels,
|
||||
Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WeakView,
|
||||
actions, div, Action, AnyElement, AppContext, DispatchPhase, Div, Element, EventEmitter,
|
||||
FocusEvent, FocusHandle, Focusable, FocusableElement, FocusableView, InputHandler,
|
||||
InteractiveElement, KeyDownEvent, Keystroke, Model, MouseButton, ParentElement, Pixels, Render,
|
||||
SharedString, Styled, Task, View, ViewContext, VisualContext, WeakView,
|
||||
};
|
||||
use language::Bias;
|
||||
use persistence::TERMINAL_DB;
|
||||
@ -537,7 +537,7 @@ impl TerminalView {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for TerminalView {
|
||||
impl Render<Self> for TerminalView {
|
||||
type Element = Focusable<Self, Div<Self>>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -577,7 +577,7 @@ impl Render for TerminalView {
|
||||
.children(
|
||||
self.context_menu
|
||||
.clone()
|
||||
.map(|context_menu| div().z_index(1).absolute().child(context_menu.render())),
|
||||
.map(|context_menu| div().z_index(1).absolute().child(context_menu)),
|
||||
)
|
||||
.track_focus(&self.focus_handle)
|
||||
.on_focus_in(Self::focus_in)
|
||||
@ -755,8 +755,8 @@ impl Item for TerminalView {
|
||||
|
||||
div()
|
||||
.child(IconElement::new(Icon::Terminal))
|
||||
.child(title)
|
||||
.render()
|
||||
.child(Label::new(title))
|
||||
.into_any()
|
||||
}
|
||||
|
||||
fn clone_on_split(
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::{div, Component, Div, ParentComponent, Styled, ViewContext};
|
||||
use gpui::{div, Div, Element, ParentElement, SharedString, Styled, ViewContext};
|
||||
|
||||
use crate::ActiveTheme;
|
||||
|
||||
@ -16,23 +16,26 @@ impl Story {
|
||||
.bg(cx.theme().colors().background)
|
||||
}
|
||||
|
||||
pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: &str) -> impl Component<V> {
|
||||
pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: SharedString) -> impl Element<V> {
|
||||
div()
|
||||
.text_xl()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.child(title.to_owned())
|
||||
.child(title)
|
||||
}
|
||||
|
||||
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
Self::title(cx, std::any::type_name::<T>())
|
||||
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
Self::title(cx, std::any::type_name::<T>().into())
|
||||
}
|
||||
|
||||
pub fn label<V: 'static>(cx: &mut ViewContext<V>, label: &str) -> impl Component<V> {
|
||||
pub fn label<V: 'static>(
|
||||
cx: &mut ViewContext<V>,
|
||||
label: impl Into<SharedString>,
|
||||
) -> impl Element<V> {
|
||||
div()
|
||||
.mt_4()
|
||||
.mb_2()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.child(label.to_owned())
|
||||
.child(label.into())
|
||||
}
|
||||
}
|
||||
|
@ -143,11 +143,11 @@ use crate::{amber, blue, jade, lime, orange, pink, purple, red};
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{ActiveTheme, Story};
|
||||
use gpui::{div, img, px, Div, ParentComponent, Render, Styled, ViewContext};
|
||||
use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
|
||||
|
||||
pub struct PlayerStory;
|
||||
|
||||
impl Render for PlayerStory {
|
||||
impl Render<Self> for PlayerStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -49,13 +49,13 @@ use gpui::hsla
|
||||
|
||||
impl<V: 'static> TodoList<V> {
|
||||
// ...
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
div().size_4().bg(hsla(50.0/360.0, 1.0, 0.5, 1.0))
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Every component needs a render method, and it should return `impl Component<V>`. This basic component will render a 16x16px yellow square on the screen.
|
||||
Every component needs a render method, and it should return `impl Element<V>`. This basic component will render a 16x16px yellow square on the screen.
|
||||
|
||||
A couple of questions might come to mind:
|
||||
|
||||
@ -87,7 +87,7 @@ We can access the current theme's colors like this:
|
||||
~~~rust
|
||||
impl<V: 'static> TodoList<V> {
|
||||
// ...
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let color = cx.theme().colors()
|
||||
|
||||
div().size_4().hsla(50.0/360.0, 1.0, 0.5, 1.0)
|
||||
@ -102,7 +102,7 @@ use gpui::hsla
|
||||
|
||||
impl<V: 'static> TodoList<V> {
|
||||
// ...
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let color = cx.theme().colors()
|
||||
|
||||
div().size_4().bg(color.surface)
|
||||
@ -117,7 +117,7 @@ use gpui::hsla
|
||||
|
||||
impl<V: 'static> TodoList<V> {
|
||||
// ...
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let color = cx.theme().colors()
|
||||
|
||||
div()
|
||||
|
@ -1,27 +1,16 @@
|
||||
use gpui::img;
|
||||
|
||||
use crate::prelude::*;
|
||||
use gpui::{img, Img, RenderOnce};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Avatar {
|
||||
src: SharedString,
|
||||
shape: Shape,
|
||||
}
|
||||
|
||||
impl Avatar {
|
||||
pub fn new(src: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
src: src.into(),
|
||||
shape: Shape::Circle,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for Avatar {
|
||||
type Rendered = Img<V>;
|
||||
|
||||
pub fn shape(mut self, shape: Shape) -> Self {
|
||||
self.shape = shape;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let mut img = img();
|
||||
|
||||
if self.shape == Shape::Circle {
|
||||
@ -37,6 +26,20 @@ impl Avatar {
|
||||
}
|
||||
}
|
||||
|
||||
impl Avatar {
|
||||
pub fn new(src: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
src: src.into(),
|
||||
shape: Shape::Circle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shape(mut self, shape: Shape) -> Self {
|
||||
self.shape = shape;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -48,7 +51,7 @@ mod stories {
|
||||
|
||||
pub struct AvatarStory;
|
||||
|
||||
impl Render for AvatarStory {
|
||||
impl Render<Self> for AvatarStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,6 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{DefiniteLength, Hsla, MouseButton, StatefulInteractiveComponent, WindowContext};
|
||||
use gpui::{
|
||||
DefiniteLength, Div, Hsla, MouseButton, RenderOnce, Stateful, StatefulInteractiveElement,
|
||||
WindowContext,
|
||||
};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, Icon, IconButton, IconElement, Label, LineHeightStyle, TextColor};
|
||||
@ -76,7 +79,7 @@ impl<V: 'static> Default for ButtonHandlers<V> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Button<V: 'static> {
|
||||
disabled: bool,
|
||||
handlers: ButtonHandlers<V>,
|
||||
@ -88,6 +91,58 @@ pub struct Button<V: 'static> {
|
||||
color: Option<TextColor>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Button<V> {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let _view: &mut V = view;
|
||||
let (icon_color, label_color) = match (self.disabled, self.color) {
|
||||
(true, _) => (TextColor::Disabled, TextColor::Disabled),
|
||||
(_, None) => (TextColor::Default, TextColor::Default),
|
||||
(_, Some(color)) => (TextColor::from(color), color),
|
||||
};
|
||||
|
||||
let mut button = h_stack()
|
||||
.id(SharedString::from(format!("{}", self.label)))
|
||||
.relative()
|
||||
.p_1()
|
||||
.text_ui()
|
||||
.rounded_md()
|
||||
.bg(self.variant.bg_color(cx))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.bg(self.variant.bg_color_hover(cx)))
|
||||
.active(|style| style.bg(self.variant.bg_color_active(cx)));
|
||||
|
||||
match (self.icon, self.icon_position) {
|
||||
(Some(_), Some(IconPosition::Left)) => {
|
||||
button = button
|
||||
.gap_1()
|
||||
.child(self.render_label(label_color))
|
||||
.children(self.render_icon(icon_color))
|
||||
}
|
||||
(Some(_), Some(IconPosition::Right)) => {
|
||||
button = button
|
||||
.gap_1()
|
||||
.children(self.render_icon(icon_color))
|
||||
.child(self.render_label(label_color))
|
||||
}
|
||||
(_, _) => button = button.child(self.render_label(label_color)),
|
||||
}
|
||||
|
||||
if let Some(width) = self.width {
|
||||
button = button.w(width).justify_center();
|
||||
}
|
||||
|
||||
if let Some(click_handler) = self.handlers.click.clone() {
|
||||
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
|
||||
click_handler(state, cx);
|
||||
});
|
||||
}
|
||||
|
||||
button
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Button<V> {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
@ -164,7 +219,7 @@ impl<V: 'static> Button<V> {
|
||||
self.icon.map(|i| IconElement::new(i).color(icon_color))
|
||||
}
|
||||
|
||||
pub fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
pub fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let (icon_color, label_color) = match (self.disabled, self.color) {
|
||||
(true, _) => (TextColor::Disabled, TextColor::Disabled),
|
||||
(_, None) => (TextColor::Default, TextColor::Default),
|
||||
@ -212,25 +267,29 @@ impl<V: 'static> Button<V> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ButtonGroup<V: 'static> {
|
||||
buttons: Vec<Button<V>>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for ButtonGroup<V> {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let mut group = h_stack();
|
||||
|
||||
for button in self.buttons.into_iter() {
|
||||
group = group.child(button.render(view, cx));
|
||||
}
|
||||
|
||||
group
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ButtonGroup<V> {
|
||||
pub fn new(buttons: Vec<Button<V>>) -> Self {
|
||||
Self { buttons }
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let mut el = h_stack().text_ui();
|
||||
|
||||
for button in self.buttons {
|
||||
el = el.child(button.render(_view, cx));
|
||||
}
|
||||
|
||||
el
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
@ -245,7 +304,7 @@ mod stories {
|
||||
|
||||
pub struct ButtonStory;
|
||||
|
||||
impl Render for ButtonStory {
|
||||
impl Render<Self> for ButtonStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::{div, prelude::*, Component, ElementId, Styled, ViewContext};
|
||||
use gpui::{div, prelude::*, Div, Element, ElementId, RenderOnce, Stateful, Styled, ViewContext};
|
||||
use std::sync::Arc;
|
||||
use theme2::ActiveTheme;
|
||||
|
||||
@ -11,7 +11,7 @@ pub type CheckHandler<V> = Arc<dyn Fn(Selection, &mut V, &mut ViewContext<V>) +
|
||||
/// Checkboxes are used for multiple choices, not for mutually exclusive choices.
|
||||
/// Each checkbox works independently from other checkboxes in the list,
|
||||
/// therefore checking an additional box does not affect any other selections.
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Checkbox<V: 'static> {
|
||||
id: ElementId,
|
||||
checked: Selection,
|
||||
@ -19,6 +19,130 @@ pub struct Checkbox<V: 'static> {
|
||||
on_click: Option<CheckHandler<V>>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Checkbox<V> {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let group_id = format!("checkbox_group_{:?}", self.id);
|
||||
|
||||
let icon = match self.checked {
|
||||
// When selected, we show a checkmark.
|
||||
Selection::Selected => {
|
||||
Some(
|
||||
IconElement::new(Icon::Check)
|
||||
.size(crate::IconSize::Small)
|
||||
.color(
|
||||
// If the checkbox is disabled we change the color of the icon.
|
||||
if self.disabled {
|
||||
TextColor::Disabled
|
||||
} else {
|
||||
TextColor::Selected
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
// In an indeterminate state, we show a dash.
|
||||
Selection::Indeterminate => {
|
||||
Some(
|
||||
IconElement::new(Icon::Dash)
|
||||
.size(crate::IconSize::Small)
|
||||
.color(
|
||||
// If the checkbox is disabled we change the color of the icon.
|
||||
if self.disabled {
|
||||
TextColor::Disabled
|
||||
} else {
|
||||
TextColor::Selected
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
// When unselected, we show nothing.
|
||||
Selection::Unselected => None,
|
||||
};
|
||||
|
||||
// A checkbox could be in an indeterminate state,
|
||||
// for example the indeterminate state could represent:
|
||||
// - a group of options of which only some are selected
|
||||
// - an enabled option that is no longer available
|
||||
// - a previously agreed to license that has been updated
|
||||
//
|
||||
// For the sake of styles we treat the indeterminate state as selected,
|
||||
// but it's icon will be different.
|
||||
let selected =
|
||||
self.checked == Selection::Selected || self.checked == Selection::Indeterminate;
|
||||
|
||||
// We could use something like this to make the checkbox background when selected:
|
||||
//
|
||||
// ~~~rust
|
||||
// ...
|
||||
// .when(selected, |this| {
|
||||
// this.bg(cx.theme().colors().element_selected)
|
||||
// })
|
||||
// ~~~
|
||||
//
|
||||
// But we use a match instead here because the checkbox might be disabled,
|
||||
// and it could be disabled _while_ it is selected, as well as while it is not selected.
|
||||
let (bg_color, border_color) = match (self.disabled, selected) {
|
||||
(true, _) => (
|
||||
cx.theme().colors().ghost_element_disabled,
|
||||
cx.theme().colors().border_disabled,
|
||||
),
|
||||
(false, true) => (
|
||||
cx.theme().colors().element_selected,
|
||||
cx.theme().colors().border,
|
||||
),
|
||||
(false, false) => (
|
||||
cx.theme().colors().element_background,
|
||||
cx.theme().colors().border,
|
||||
),
|
||||
};
|
||||
|
||||
div()
|
||||
.id(self.id)
|
||||
// Rather than adding `px_1()` to add some space around the checkbox,
|
||||
// we use a larger parent element to create a slightly larger
|
||||
// click area for the checkbox.
|
||||
.size_5()
|
||||
// Because we've enlarged the click area, we need to create a
|
||||
// `group` to pass down interactivity events to the checkbox.
|
||||
.group(group_id.clone())
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
// This prevent the flex element from growing
|
||||
// or shrinking in response to any size changes
|
||||
.flex_none()
|
||||
// The combo of `justify_center()` and `items_center()`
|
||||
// is used frequently to center elements in a flex container.
|
||||
//
|
||||
// We use this to center the icon in the checkbox.
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.m_1()
|
||||
.size_4()
|
||||
.rounded_sm()
|
||||
.bg(bg_color)
|
||||
.border()
|
||||
.border_color(border_color)
|
||||
// We only want the interactivity states to fire when we
|
||||
// are in a checkbox that isn't disabled.
|
||||
.when(!self.disabled, |this| {
|
||||
// Here instead of `hover()` we use `group_hover()`
|
||||
// to pass it the group id.
|
||||
this.group_hover(group_id.clone(), |el| {
|
||||
el.bg(cx.theme().colors().element_hover)
|
||||
})
|
||||
})
|
||||
.children(icon),
|
||||
)
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|this, on_click| {
|
||||
this.on_click(move |view, _, cx| on_click(self.checked.inverse(), view, cx))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Checkbox<V> {
|
||||
pub fn new(id: impl Into<ElementId>, checked: Selection) -> Self {
|
||||
Self {
|
||||
@ -42,7 +166,7 @@ impl<V: 'static> Checkbox<V> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
pub fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let group_id = format!("checkbox_group_{:?}", self.id);
|
||||
|
||||
let icon = match self.checked {
|
||||
@ -175,7 +299,7 @@ mod stories {
|
||||
|
||||
pub struct CheckboxStory;
|
||||
|
||||
impl Render for CheckboxStory {
|
||||
impl Render<Self> for CheckboxStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,12 +1,12 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
|
||||
use crate::{prelude::*, v_stack, List, ListItem};
|
||||
use crate::{ListEntry, ListSeparator, ListSubHeader};
|
||||
use gpui::{
|
||||
overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, DispatchPhase, Div,
|
||||
EventEmitter, FocusHandle, FocusableView, LayoutId, ManagedView, Manager, MouseButton,
|
||||
MouseDownEvent, Pixels, Point, Render, View, VisualContext, WeakView,
|
||||
MouseDownEvent, Pixels, Point, Render, RenderOnce, View, VisualContext, WeakView,
|
||||
};
|
||||
|
||||
pub enum ContextMenuItem<V> {
|
||||
@ -24,15 +24,15 @@ pub struct ContextMenu<V> {
|
||||
handle: WeakView<V>,
|
||||
}
|
||||
|
||||
impl<V: Render> FocusableView for ContextMenu<V> {
|
||||
impl<V: 'static> FocusableView for ContextMenu<V> {
|
||||
fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Render> EventEmitter<Manager> for ContextMenu<V> {}
|
||||
impl<V: 'static> EventEmitter<Manager> for ContextMenu<V> {}
|
||||
|
||||
impl<V: Render> ContextMenu<V> {
|
||||
impl<V: 'static> ContextMenu<V> {
|
||||
pub fn build(
|
||||
cx: &mut ViewContext<V>,
|
||||
f: impl FnOnce(Self, &mut ViewContext<Self>) -> Self,
|
||||
@ -86,7 +86,7 @@ impl<V: Render> ContextMenu<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Render> Render for ContextMenu<V> {
|
||||
impl<V: 'static> Render<Self> for ContextMenu<V> {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -129,7 +129,7 @@ impl<V: Render> Render for ContextMenu<V> {
|
||||
}
|
||||
|
||||
pub struct MenuHandle<V: 'static, M: ManagedView> {
|
||||
id: Option<ElementId>,
|
||||
id: ElementId,
|
||||
child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement<V> + 'static>>,
|
||||
menu_builder: Option<Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> View<M> + 'static>>,
|
||||
|
||||
@ -138,18 +138,13 @@ pub struct MenuHandle<V: 'static, M: ManagedView> {
|
||||
}
|
||||
|
||||
impl<V: 'static, M: ManagedView> MenuHandle<V, M> {
|
||||
pub fn id(mut self, id: impl Into<ElementId>) -> Self {
|
||||
self.id = Some(id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn menu(mut self, f: impl Fn(&mut V, &mut ViewContext<V>) -> View<M> + 'static) -> Self {
|
||||
self.menu_builder = Some(Rc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn child<R: Component<V>>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self {
|
||||
self.child_builder = Some(Box::new(|b| f(b).render()));
|
||||
pub fn child<R: RenderOnce<V>>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self {
|
||||
self.child_builder = Some(Box::new(|b| f(b).render_once().into_any()));
|
||||
self
|
||||
}
|
||||
|
||||
@ -167,9 +162,9 @@ impl<V: 'static, M: ManagedView> MenuHandle<V, M> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn menu_handle<V: 'static, M: ManagedView>() -> MenuHandle<V, M> {
|
||||
pub fn menu_handle<V: 'static, M: ManagedView>(id: impl Into<ElementId>) -> MenuHandle<V, M> {
|
||||
MenuHandle {
|
||||
id: None,
|
||||
id: id.into(),
|
||||
child_builder: None,
|
||||
menu_builder: None,
|
||||
anchor: None,
|
||||
@ -185,18 +180,14 @@ pub struct MenuHandleState<V, M> {
|
||||
menu_element: Option<AnyElement<V>>,
|
||||
}
|
||||
impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
|
||||
type ElementState = MenuHandleState<V, M>;
|
||||
|
||||
fn element_id(&self) -> Option<gpui::ElementId> {
|
||||
Some(self.id.clone().expect("menu_handle must have an id()"))
|
||||
}
|
||||
type State = MenuHandleState<V, M>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view_state: &mut V,
|
||||
element_state: Option<Self::ElementState>,
|
||||
element_state: Option<Self::State>,
|
||||
cx: &mut crate::ViewContext<V>,
|
||||
) -> (gpui::LayoutId, Self::ElementState) {
|
||||
) -> (gpui::LayoutId, Self::State) {
|
||||
let (menu, position) = if let Some(element_state) = element_state {
|
||||
(element_state.menu, element_state.position)
|
||||
} else {
|
||||
@ -212,9 +203,9 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
|
||||
}
|
||||
overlay = overlay.position(*position.borrow());
|
||||
|
||||
let mut view = overlay.child(menu.clone()).render();
|
||||
menu_layout_id = Some(view.layout(view_state, cx));
|
||||
view
|
||||
let mut element = overlay.child(menu.clone()).into_any();
|
||||
menu_layout_id = Some(element.layout(view_state, cx));
|
||||
element
|
||||
});
|
||||
|
||||
let mut child_element = self
|
||||
@ -244,22 +235,22 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
self,
|
||||
bounds: Bounds<gpui::Pixels>,
|
||||
view_state: &mut V,
|
||||
element_state: &mut Self::ElementState,
|
||||
element_state: &mut Self::State,
|
||||
cx: &mut crate::ViewContext<V>,
|
||||
) {
|
||||
if let Some(child) = element_state.child_element.as_mut() {
|
||||
if let Some(child) = element_state.child_element.take() {
|
||||
child.paint(view_state, cx);
|
||||
}
|
||||
|
||||
if let Some(menu) = element_state.menu_element.as_mut() {
|
||||
if let Some(menu) = element_state.menu_element.take() {
|
||||
menu.paint(view_state, cx);
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(builder) = self.menu_builder.clone() else {
|
||||
let Some(builder) = self.menu_builder else {
|
||||
return;
|
||||
};
|
||||
let menu = element_state.menu.clone();
|
||||
@ -300,9 +291,15 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, M: ManagedView> Component<V> for MenuHandle<V, M> {
|
||||
fn render(self) -> AnyElement<V> {
|
||||
AnyElement::new(self)
|
||||
impl<V: 'static, M: ManagedView> RenderOnce<V> for MenuHandle<V, M> {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<gpui::ElementId> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
|
||||
fn render_once(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,12 +309,12 @@ pub use stories::*;
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::story::Story;
|
||||
use crate::{story::Story, Label};
|
||||
use gpui::{actions, Div, Render};
|
||||
|
||||
actions!(PrintCurrentDate, PrintBestFood);
|
||||
|
||||
fn build_menu<V: Render>(
|
||||
fn build_menu<V: Render<V>>(
|
||||
cx: &mut ViewContext<V>,
|
||||
header: impl Into<SharedString>,
|
||||
) -> View<ContextMenu<V>> {
|
||||
@ -337,7 +334,7 @@ mod stories {
|
||||
|
||||
pub struct ContextMenuStory;
|
||||
|
||||
impl Render for ContextMenuStory {
|
||||
impl Render<Self> for ContextMenuStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -360,28 +357,24 @@ mod stories {
|
||||
.flex_col()
|
||||
.justify_between()
|
||||
.child(
|
||||
menu_handle()
|
||||
.id("test2")
|
||||
menu_handle("test2")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"TOP LEFT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
.render()
|
||||
})
|
||||
.menu(move |_, cx| build_menu(cx, "top left")),
|
||||
)
|
||||
.child(
|
||||
menu_handle()
|
||||
.id("test1")
|
||||
menu_handle("test1")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"BOTTOM LEFT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
.render()
|
||||
})
|
||||
.anchor(AnchorCorner::BottomLeft)
|
||||
.attach(AnchorCorner::TopLeft)
|
||||
@ -394,29 +387,25 @@ mod stories {
|
||||
.flex_col()
|
||||
.justify_between()
|
||||
.child(
|
||||
menu_handle()
|
||||
.id("test3")
|
||||
menu_handle("test3")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"TOP RIGHT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
.render()
|
||||
})
|
||||
.anchor(AnchorCorner::TopRight)
|
||||
.menu(move |_, cx| build_menu(cx, "top right")),
|
||||
)
|
||||
.child(
|
||||
menu_handle()
|
||||
.id("test4")
|
||||
menu_handle("test4")
|
||||
.child(|is_open| {
|
||||
Label::new(if is_open {
|
||||
"BOTTOM RIGHT"
|
||||
} else {
|
||||
"RIGHT CLICK ME"
|
||||
})
|
||||
.render()
|
||||
})
|
||||
.anchor(AnchorCorner::BottomRight)
|
||||
.attach(AnchorCorner::TopRight)
|
||||
|
@ -1,13 +1,29 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{v_stack, ButtonGroup};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Details<V: 'static> {
|
||||
text: &'static str,
|
||||
meta: Option<&'static str>,
|
||||
actions: Option<ButtonGroup<V>>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Details<V> {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
v_stack()
|
||||
.p_1()
|
||||
.gap_0p5()
|
||||
.text_ui_sm()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.size_full()
|
||||
.child(self.text)
|
||||
.children(self.meta.map(|m| m))
|
||||
.children(self.actions.map(|a| a))
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Details<V> {
|
||||
pub fn new(text: &'static str) -> Self {
|
||||
Self {
|
||||
@ -26,20 +42,9 @@ impl<V: 'static> Details<V> {
|
||||
self.actions = Some(actions);
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
v_stack()
|
||||
.p_1()
|
||||
.gap_0p5()
|
||||
.text_ui_sm()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.size_full()
|
||||
.child(self.text)
|
||||
.children(self.meta.map(|m| m))
|
||||
.children(self.actions.map(|a| a))
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -51,7 +56,7 @@ mod stories {
|
||||
|
||||
pub struct DetailsStory;
|
||||
|
||||
impl Render for DetailsStory {
|
||||
impl Render<Self> for DetailsStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use gpui::{Div, RenderOnce};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
enum DividerDirection {
|
||||
@ -5,12 +7,29 @@ enum DividerDirection {
|
||||
Vertical,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Divider {
|
||||
direction: DividerDirection,
|
||||
inset: bool,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Divider {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.map(|this| match self.direction {
|
||||
DividerDirection::Horizontal => {
|
||||
this.h_px().w_full().when(self.inset, |this| this.mx_1p5())
|
||||
}
|
||||
DividerDirection::Vertical => {
|
||||
this.w_px().h_full().when(self.inset, |this| this.my_1p5())
|
||||
}
|
||||
})
|
||||
.bg(cx.theme().colors().border_variant)
|
||||
}
|
||||
}
|
||||
|
||||
impl Divider {
|
||||
pub fn horizontal() -> Self {
|
||||
Self {
|
||||
@ -31,7 +50,7 @@ impl Divider {
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
div()
|
||||
.map(|this| match self.direction {
|
||||
DividerDirection::Horizontal => {
|
||||
|
@ -1,19 +1,15 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{Avatar, Player};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Facepile {
|
||||
players: Vec<Player>,
|
||||
}
|
||||
|
||||
impl Facepile {
|
||||
pub fn new<P: Iterator<Item = Player>>(players: P) -> Self {
|
||||
Self {
|
||||
players: players.collect(),
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for Facepile {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let player_count = self.players.len();
|
||||
let player_list = self.players.iter().enumerate().map(|(ix, player)| {
|
||||
let isnt_last = ix < player_count - 1;
|
||||
@ -26,6 +22,15 @@ impl Facepile {
|
||||
}
|
||||
}
|
||||
|
||||
impl Facepile {
|
||||
pub fn new<P: Iterator<Item = Player>>(players: P) -> Self {
|
||||
Self {
|
||||
players: players.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -37,7 +42,7 @@ mod stories {
|
||||
|
||||
pub struct FacepileStory;
|
||||
|
||||
impl Render for FacepileStory {
|
||||
impl Render<Self> for FacepileStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::{rems, svg};
|
||||
use gpui::{rems, svg, RenderOnce, Svg};
|
||||
use strum::EnumIter;
|
||||
|
||||
use crate::prelude::*;
|
||||
@ -129,13 +129,30 @@ impl Icon {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct IconElement {
|
||||
path: SharedString,
|
||||
color: TextColor,
|
||||
size: IconSize,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for IconElement {
|
||||
type Rendered = Svg<V>;
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let svg_size = match self.size {
|
||||
IconSize::Small => rems(0.75),
|
||||
IconSize::Medium => rems(0.9375),
|
||||
};
|
||||
|
||||
svg()
|
||||
.size(svg_size)
|
||||
.flex_none()
|
||||
.path(self.path)
|
||||
.text_color(self.color.color(cx))
|
||||
}
|
||||
}
|
||||
|
||||
impl IconElement {
|
||||
pub fn new(icon: Icon) -> Self {
|
||||
Self {
|
||||
@ -163,7 +180,7 @@ impl IconElement {
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let svg_size = match self.size {
|
||||
IconSize::Small => rems(0.75),
|
||||
IconSize::Medium => rems(0.9375),
|
||||
@ -191,7 +208,7 @@ mod stories {
|
||||
|
||||
pub struct IconStory;
|
||||
|
||||
impl Render for IconStory {
|
||||
impl Render<Self> for IconStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement};
|
||||
use gpui::{prelude::*, Action, AnyView, MouseButton};
|
||||
use gpui::{prelude::*, Action, AnyView, Div, MouseButton, Stateful};
|
||||
use std::sync::Arc;
|
||||
|
||||
struct IconButtonHandlers<V: 'static> {
|
||||
@ -12,7 +12,7 @@ impl<V: 'static> Default for IconButtonHandlers<V> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct IconButton<V: 'static> {
|
||||
id: ElementId,
|
||||
icon: Icon,
|
||||
@ -24,6 +24,64 @@ pub struct IconButton<V: 'static> {
|
||||
handlers: IconButtonHandlers<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for IconButton<V> {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let icon_color = match (self.state, self.color) {
|
||||
(InteractionState::Disabled, _) => TextColor::Disabled,
|
||||
(InteractionState::Active, _) => TextColor::Selected,
|
||||
_ => self.color,
|
||||
};
|
||||
|
||||
let (mut bg_color, bg_hover_color, bg_active_color) = match self.variant {
|
||||
ButtonVariant::Filled => (
|
||||
cx.theme().colors().element_background,
|
||||
cx.theme().colors().element_hover,
|
||||
cx.theme().colors().element_active,
|
||||
),
|
||||
ButtonVariant::Ghost => (
|
||||
cx.theme().colors().ghost_element_background,
|
||||
cx.theme().colors().ghost_element_hover,
|
||||
cx.theme().colors().ghost_element_active,
|
||||
),
|
||||
};
|
||||
|
||||
if self.selected {
|
||||
bg_color = bg_hover_color;
|
||||
}
|
||||
|
||||
let mut button = h_stack()
|
||||
.id(self.id.clone())
|
||||
.justify_center()
|
||||
.rounded_md()
|
||||
.p_1()
|
||||
.bg(bg_color)
|
||||
.cursor_pointer()
|
||||
// Nate: Trying to figure out the right places we want to show a
|
||||
// hover state here. I think it is a bit heavy to have it on every
|
||||
// place we use an icon button.
|
||||
// .hover(|style| style.bg(bg_hover_color))
|
||||
.active(|style| style.bg(bg_active_color))
|
||||
.child(IconElement::new(self.icon).color(icon_color));
|
||||
|
||||
if let Some(click_handler) = self.handlers.click.clone() {
|
||||
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
|
||||
cx.stop_propagation();
|
||||
click_handler(state, cx);
|
||||
})
|
||||
}
|
||||
|
||||
if let Some(tooltip) = self.tooltip {
|
||||
if !self.selected {
|
||||
button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
|
||||
}
|
||||
}
|
||||
|
||||
button
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> IconButton<V> {
|
||||
pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
|
||||
Self {
|
||||
@ -79,58 +137,4 @@ impl<V: 'static> IconButton<V> {
|
||||
pub fn action(self, action: Box<dyn Action>) -> Self {
|
||||
self.on_click(move |this, cx| cx.dispatch_action(action.boxed_clone()))
|
||||
}
|
||||
|
||||
fn render(mut self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let icon_color = match (self.state, self.color) {
|
||||
(InteractionState::Disabled, _) => TextColor::Disabled,
|
||||
(InteractionState::Active, _) => TextColor::Selected,
|
||||
_ => self.color,
|
||||
};
|
||||
|
||||
let (mut bg_color, bg_hover_color, bg_active_color) = match self.variant {
|
||||
ButtonVariant::Filled => (
|
||||
cx.theme().colors().element_background,
|
||||
cx.theme().colors().element_hover,
|
||||
cx.theme().colors().element_active,
|
||||
),
|
||||
ButtonVariant::Ghost => (
|
||||
cx.theme().colors().ghost_element_background,
|
||||
cx.theme().colors().ghost_element_hover,
|
||||
cx.theme().colors().ghost_element_active,
|
||||
),
|
||||
};
|
||||
|
||||
if self.selected {
|
||||
bg_color = bg_hover_color;
|
||||
}
|
||||
|
||||
let mut button = h_stack()
|
||||
.id(self.id.clone())
|
||||
.justify_center()
|
||||
.rounded_md()
|
||||
.p_1()
|
||||
.bg(bg_color)
|
||||
.cursor_pointer()
|
||||
// Nate: Trying to figure out the right places we want to show a
|
||||
// hover state here. I think it is a bit heavy to have it on every
|
||||
// place we use an icon button.
|
||||
// .hover(|style| style.bg(bg_hover_color))
|
||||
.active(|style| style.bg(bg_active_color))
|
||||
.child(IconElement::new(self.icon).color(icon_color));
|
||||
|
||||
if let Some(click_handler) = self.handlers.click.clone() {
|
||||
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
|
||||
cx.stop_propagation();
|
||||
click_handler(state, cx);
|
||||
})
|
||||
}
|
||||
|
||||
if let Some(tooltip) = self.tooltip.take() {
|
||||
if !self.selected {
|
||||
button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
|
||||
}
|
||||
}
|
||||
|
||||
button
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,30 @@
|
||||
use gpui::px;
|
||||
|
||||
use crate::prelude::*;
|
||||
use gpui::{px, Div, RenderOnce};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct UnreadIndicator;
|
||||
|
||||
impl UnreadIndicator {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
impl<V: 'static> Component<V> for UnreadIndicator {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.rounded_full()
|
||||
.border_2()
|
||||
.border_color(cx.theme().colors().surface_background)
|
||||
.w(px(9.0))
|
||||
.h(px(9.0))
|
||||
.z_index(2)
|
||||
.bg(cx.theme().status().info)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnreadIndicator {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
div()
|
||||
.rounded_full()
|
||||
.border_2()
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{prelude::*, Label};
|
||||
use gpui::prelude::*;
|
||||
use gpui::{prelude::*, Div, RenderOnce, Stateful};
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub enum InputVariant {
|
||||
@ -8,7 +8,7 @@ pub enum InputVariant {
|
||||
Filled,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Input {
|
||||
placeholder: SharedString,
|
||||
value: String,
|
||||
@ -18,6 +18,57 @@ pub struct Input {
|
||||
is_active: bool,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Input {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
|
||||
InputVariant::Ghost => (
|
||||
cx.theme().colors().ghost_element_background,
|
||||
cx.theme().colors().ghost_element_hover,
|
||||
cx.theme().colors().ghost_element_active,
|
||||
),
|
||||
InputVariant::Filled => (
|
||||
cx.theme().colors().element_background,
|
||||
cx.theme().colors().element_hover,
|
||||
cx.theme().colors().element_active,
|
||||
),
|
||||
};
|
||||
|
||||
let placeholder_label = Label::new(self.placeholder.clone()).color(if self.disabled {
|
||||
TextColor::Disabled
|
||||
} else {
|
||||
TextColor::Placeholder
|
||||
});
|
||||
|
||||
let label = Label::new(self.value.clone()).color(if self.disabled {
|
||||
TextColor::Disabled
|
||||
} else {
|
||||
TextColor::Default
|
||||
});
|
||||
|
||||
div()
|
||||
.id("input")
|
||||
.h_7()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.border()
|
||||
.border_color(cx.theme().styles.system.transparent)
|
||||
.bg(input_bg)
|
||||
.hover(|style| style.bg(input_hover_bg))
|
||||
.active(|style| style.bg(input_active_bg))
|
||||
.flex()
|
||||
.items_center()
|
||||
.child(div().flex().items_center().text_ui_sm().map(move |this| {
|
||||
if self.value.is_empty() {
|
||||
this.child(placeholder_label)
|
||||
} else {
|
||||
this.child(label)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn new(placeholder: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
@ -54,53 +105,6 @@ impl Input {
|
||||
self.is_active = is_active;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
|
||||
InputVariant::Ghost => (
|
||||
cx.theme().colors().ghost_element_background,
|
||||
cx.theme().colors().ghost_element_hover,
|
||||
cx.theme().colors().ghost_element_active,
|
||||
),
|
||||
InputVariant::Filled => (
|
||||
cx.theme().colors().element_background,
|
||||
cx.theme().colors().element_hover,
|
||||
cx.theme().colors().element_active,
|
||||
),
|
||||
};
|
||||
|
||||
let placeholder_label = Label::new(self.placeholder.clone()).color(if self.disabled {
|
||||
TextColor::Disabled
|
||||
} else {
|
||||
TextColor::Placeholder
|
||||
});
|
||||
|
||||
let label = Label::new(self.value.clone()).color(if self.disabled {
|
||||
TextColor::Disabled
|
||||
} else {
|
||||
TextColor::Default
|
||||
});
|
||||
|
||||
div()
|
||||
.id("input")
|
||||
.h_7()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.border()
|
||||
.border_color(cx.theme().styles.system.transparent)
|
||||
.bg(input_bg)
|
||||
.hover(|style| style.bg(input_hover_bg))
|
||||
.active(|style| style.bg(input_active_bg))
|
||||
.flex()
|
||||
.items_center()
|
||||
.child(div().flex().items_center().text_ui_sm().map(|this| {
|
||||
if self.value.is_empty() {
|
||||
this.child(placeholder_label)
|
||||
} else {
|
||||
this.child(label)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
@ -114,7 +118,7 @@ mod stories {
|
||||
|
||||
pub struct InputStory;
|
||||
|
||||
impl Render for InputStory {
|
||||
impl Render<Self> for InputStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,9 +1,7 @@
|
||||
use gpui::{actions, Action};
|
||||
use strum::EnumIter;
|
||||
|
||||
use crate::prelude::*;
|
||||
use gpui::{Action, Div, RenderOnce};
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
#[derive(RenderOnce, Clone)]
|
||||
pub struct KeyBinding {
|
||||
/// A keybinding consists of a key and a set of modifier keys.
|
||||
/// More then one keybinding produces a chord.
|
||||
@ -12,19 +10,10 @@ pub struct KeyBinding {
|
||||
key_binding: gpui::KeyBinding,
|
||||
}
|
||||
|
||||
impl KeyBinding {
|
||||
pub fn for_action(action: &dyn Action, cx: &mut WindowContext) -> Option<Self> {
|
||||
// todo! this last is arbitrary, we want to prefer users key bindings over defaults,
|
||||
// and vim over normal (in vim mode), etc.
|
||||
let key_binding = cx.bindings_for_action(action).last().cloned()?;
|
||||
Some(Self::new(key_binding))
|
||||
}
|
||||
impl<V: 'static> Component<V> for KeyBinding {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
pub fn new(key_binding: gpui::KeyBinding) -> Self {
|
||||
Self { key_binding }
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.flex()
|
||||
.gap_2()
|
||||
@ -42,17 +31,29 @@ impl KeyBinding {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
impl KeyBinding {
|
||||
pub fn for_action(action: &dyn Action, cx: &mut WindowContext) -> Option<Self> {
|
||||
// todo! this last is arbitrary, we want to prefer users key bindings over defaults,
|
||||
// and vim over normal (in vim mode), etc.
|
||||
let key_binding = cx.bindings_for_action(action).last().cloned()?;
|
||||
Some(Self::new(key_binding))
|
||||
}
|
||||
|
||||
pub fn new(key_binding: gpui::KeyBinding) -> Self {
|
||||
Self { key_binding }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Key {
|
||||
key: SharedString,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn new(key: impl Into<SharedString>) -> Self {
|
||||
Self { key: key.into() }
|
||||
}
|
||||
impl<V: 'static> Component<V> for Key {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let _view: &mut V = view;
|
||||
div()
|
||||
.px_2()
|
||||
.py_0()
|
||||
@ -64,20 +65,10 @@ impl Key {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The order the modifier keys appear in this enum impacts the order in
|
||||
// which they are rendered in the UI.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
|
||||
pub enum ModifierKey {
|
||||
Control,
|
||||
Alt,
|
||||
Command,
|
||||
Shift,
|
||||
}
|
||||
|
||||
actions!(NoAction);
|
||||
|
||||
pub fn binding(key: &str) -> gpui::KeyBinding {
|
||||
gpui::KeyBinding::new(key, NoAction {}, None)
|
||||
impl Key {
|
||||
pub fn new(key: impl Into<SharedString>) -> Self {
|
||||
Self { key: key.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
@ -87,12 +78,18 @@ pub use stories::*;
|
||||
mod stories {
|
||||
use super::*;
|
||||
pub use crate::KeyBinding;
|
||||
use crate::{binding, Story};
|
||||
use gpui::{Div, Render};
|
||||
use crate::Story;
|
||||
use gpui::{actions, Div, Render};
|
||||
use itertools::Itertools;
|
||||
pub struct KeybindingStory;
|
||||
|
||||
impl Render for KeybindingStory {
|
||||
actions!(NoAction);
|
||||
|
||||
pub fn binding(key: &str) -> gpui::KeyBinding {
|
||||
gpui::KeyBinding::new(key, NoAction {}, None)
|
||||
}
|
||||
|
||||
impl Render<Self> for KeybindingStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use gpui::{relative, Hsla, Text, TextRun, WindowContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::styled_ext::StyledExt;
|
||||
use gpui::{relative, Div, Hsla, RenderOnce, StyledText, TextRun, WindowContext};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
|
||||
pub enum LabelSize {
|
||||
@ -60,7 +59,7 @@ pub enum LineHeightStyle {
|
||||
UILabel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Component)]
|
||||
#[derive(Clone, RenderOnce)]
|
||||
pub struct Label {
|
||||
label: SharedString,
|
||||
size: LabelSize,
|
||||
@ -69,6 +68,33 @@ pub struct Label {
|
||||
strikethrough: bool,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Label {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.when(self.strikethrough, |this| {
|
||||
this.relative().child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_1_2()
|
||||
.w_full()
|
||||
.h_px()
|
||||
.bg(TextColor::Hidden.color(cx)),
|
||||
)
|
||||
})
|
||||
.map(|this| match self.size {
|
||||
LabelSize::Default => this.text_ui(),
|
||||
LabelSize::Small => this.text_ui_sm(),
|
||||
})
|
||||
.when(self.line_height_style == LineHeightStyle::UILabel, |this| {
|
||||
this.line_height(relative(1.))
|
||||
})
|
||||
.text_color(self.color.color(cx))
|
||||
.child(self.label.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
@ -99,32 +125,9 @@ impl Label {
|
||||
self.strikethrough = strikethrough;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
div()
|
||||
.when(self.strikethrough, |this| {
|
||||
this.relative().child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_1_2()
|
||||
.w_full()
|
||||
.h_px()
|
||||
.bg(TextColor::Hidden.color(cx)),
|
||||
)
|
||||
})
|
||||
.map(|this| match self.size {
|
||||
LabelSize::Default => this.text_ui(),
|
||||
LabelSize::Small => this.text_ui_sm(),
|
||||
})
|
||||
.when(self.line_height_style == LineHeightStyle::UILabel, |this| {
|
||||
this.line_height(relative(1.))
|
||||
})
|
||||
.text_color(self.color.color(cx))
|
||||
.child(self.label.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct HighlightedLabel {
|
||||
label: SharedString,
|
||||
size: LabelSize,
|
||||
@ -133,35 +136,10 @@ pub struct HighlightedLabel {
|
||||
strikethrough: bool,
|
||||
}
|
||||
|
||||
impl HighlightedLabel {
|
||||
/// shows a label with the given characters highlighted.
|
||||
/// characters are identified by utf8 byte position.
|
||||
pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
|
||||
Self {
|
||||
label: label.into(),
|
||||
size: LabelSize::Default,
|
||||
color: TextColor::Default,
|
||||
highlight_indices,
|
||||
strikethrough: false,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for HighlightedLabel {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
pub fn size(mut self, size: LabelSize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: TextColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
|
||||
self.strikethrough = strikethrough;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let highlight_color = cx.theme().colors().text_accent;
|
||||
let mut text_style = cx.text_style().clone();
|
||||
|
||||
@ -214,7 +192,36 @@ impl HighlightedLabel {
|
||||
LabelSize::Default => this.text_ui(),
|
||||
LabelSize::Small => this.text_ui_sm(),
|
||||
})
|
||||
.child(Text::styled(self.label, runs))
|
||||
.child(StyledText::new(self.label, runs))
|
||||
}
|
||||
}
|
||||
|
||||
impl HighlightedLabel {
|
||||
/// shows a label with the given characters highlighted.
|
||||
/// characters are identified by utf8 byte position.
|
||||
pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
|
||||
Self {
|
||||
label: label.into(),
|
||||
size: LabelSize::Default,
|
||||
color: TextColor::Default,
|
||||
highlight_indices,
|
||||
strikethrough: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: LabelSize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: TextColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
|
||||
self.strikethrough = strikethrough;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,7 +242,7 @@ mod stories {
|
||||
|
||||
pub struct LabelStory;
|
||||
|
||||
impl Render for LabelStory {
|
||||
impl Render<Self> for LabelStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use gpui::{div, Div, RenderOnce, Stateful, StatefulInteractiveElement};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{div, Div, Stateful, StatefulInteractiveComponent};
|
||||
|
||||
use crate::settings::user_settings;
|
||||
use crate::{
|
||||
disclosure_control, h_stack, v_stack, Avatar, Icon, IconElement, IconSize, Label, Toggle,
|
||||
@ -24,7 +23,7 @@ pub enum ListHeaderMeta {
|
||||
Text(Label),
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ListHeader {
|
||||
label: SharedString,
|
||||
left_icon: Option<Icon>,
|
||||
@ -33,33 +32,10 @@ pub struct ListHeader {
|
||||
toggle: Toggle,
|
||||
}
|
||||
|
||||
impl ListHeader {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
label: label.into(),
|
||||
left_icon: None,
|
||||
meta: None,
|
||||
variant: ListItemVariant::default(),
|
||||
toggle: Toggle::NotToggleable,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for ListHeader {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
pub fn toggle(mut self, toggle: Toggle) -> Self {
|
||||
self.toggle = toggle;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
|
||||
self.left_icon = left_icon;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn meta(mut self, meta: Option<ListHeaderMeta>) -> Self {
|
||||
self.meta = meta;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let disclosure_control = disclosure_control(self.toggle);
|
||||
|
||||
let meta = match self.meta {
|
||||
@ -81,11 +57,6 @@ impl ListHeader {
|
||||
h_stack()
|
||||
.w_full()
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
// TODO: Add focus state
|
||||
// .when(self.state == InteractionState::Focused, |this| {
|
||||
// this.border()
|
||||
// .border_color(cx.theme().colors().border_focused)
|
||||
// })
|
||||
.relative()
|
||||
.child(
|
||||
div()
|
||||
@ -119,7 +90,94 @@ impl ListHeader {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
impl ListHeader {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
label: label.into(),
|
||||
left_icon: None,
|
||||
meta: None,
|
||||
variant: ListItemVariant::default(),
|
||||
toggle: Toggle::NotToggleable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle(mut self, toggle: Toggle) -> Self {
|
||||
self.toggle = toggle;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
|
||||
self.left_icon = left_icon;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn meta(mut self, meta: Option<ListHeaderMeta>) -> Self {
|
||||
self.meta = meta;
|
||||
self
|
||||
}
|
||||
|
||||
// before_ship!("delete")
|
||||
// fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
// let disclosure_control = disclosure_control(self.toggle);
|
||||
|
||||
// let meta = match self.meta {
|
||||
// Some(ListHeaderMeta::Tools(icons)) => div().child(
|
||||
// h_stack()
|
||||
// .gap_2()
|
||||
// .items_center()
|
||||
// .children(icons.into_iter().map(|i| {
|
||||
// IconElement::new(i)
|
||||
// .color(TextColor::Muted)
|
||||
// .size(IconSize::Small)
|
||||
// })),
|
||||
// ),
|
||||
// Some(ListHeaderMeta::Button(label)) => div().child(label),
|
||||
// Some(ListHeaderMeta::Text(label)) => div().child(label),
|
||||
// None => div(),
|
||||
// };
|
||||
|
||||
// h_stack()
|
||||
// .w_full()
|
||||
// .bg(cx.theme().colors().surface_background)
|
||||
// // TODO: Add focus state
|
||||
// // .when(self.state == InteractionState::Focused, |this| {
|
||||
// // this.border()
|
||||
// // .border_color(cx.theme().colors().border_focused)
|
||||
// // })
|
||||
// .relative()
|
||||
// .child(
|
||||
// div()
|
||||
// .h_5()
|
||||
// .when(self.variant == ListItemVariant::Inset, |this| this.px_2())
|
||||
// .flex()
|
||||
// .flex_1()
|
||||
// .items_center()
|
||||
// .justify_between()
|
||||
// .w_full()
|
||||
// .gap_1()
|
||||
// .child(
|
||||
// h_stack()
|
||||
// .gap_1()
|
||||
// .child(
|
||||
// div()
|
||||
// .flex()
|
||||
// .gap_1()
|
||||
// .items_center()
|
||||
// .children(self.left_icon.map(|i| {
|
||||
// IconElement::new(i)
|
||||
// .color(TextColor::Muted)
|
||||
// .size(IconSize::Small)
|
||||
// }))
|
||||
// .child(Label::new(self.label.clone()).color(TextColor::Muted)),
|
||||
// )
|
||||
// .child(disclosure_control),
|
||||
// )
|
||||
// .child(meta),
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ListSubHeader {
|
||||
label: SharedString,
|
||||
left_icon: Option<Icon>,
|
||||
@ -140,7 +198,7 @@ impl ListSubHeader {
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
h_stack().flex_1().w_full().relative().py_1().child(
|
||||
div()
|
||||
.h_6()
|
||||
@ -200,14 +258,6 @@ impl<V: 'static> From<ListSubHeader> for ListItem<V> {
|
||||
}
|
||||
|
||||
impl<V: 'static> ListItem<V> {
|
||||
fn render(self, view: &mut V, ix: usize, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
match self {
|
||||
ListItem::Entry(entry) => div().child(entry.render(ix, cx)),
|
||||
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
||||
ListItem::Header(header) => div().child(header.render(view, cx)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(label: Label) -> Self {
|
||||
Self::Entry(ListEntry::new(label))
|
||||
}
|
||||
@ -219,8 +269,17 @@ impl<V: 'static> ListItem<V> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn render(self, view: &mut V, ix: usize, cx: &mut ViewContext<V>) -> Div<V> {
|
||||
match self {
|
||||
ListItem::Entry(entry) => div().child(entry.render(ix, cx)),
|
||||
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
|
||||
ListItem::Header(header) => div().child(header.render(view, cx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(RenderOnce)]
|
||||
pub struct ListEntry<V> {
|
||||
disabled: bool,
|
||||
// TODO: Reintroduce this
|
||||
@ -376,20 +435,24 @@ impl<V: 'static> ListEntry<V> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Component)]
|
||||
#[derive(RenderOnce, Clone)]
|
||||
pub struct ListSeparator;
|
||||
|
||||
impl ListSeparator {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
impl<V: 'static> Component<V> for ListSeparator {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div().h_px().w_full().bg(cx.theme().colors().border_variant)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct List<V: 'static> {
|
||||
items: Vec<ListItem<V>>,
|
||||
/// Message to display when the list is empty
|
||||
@ -399,6 +462,31 @@ pub struct List<V: 'static> {
|
||||
toggle: Toggle,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for List<V> {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let list_content = match (self.items.is_empty(), self.toggle) {
|
||||
(false, _) => div().children(
|
||||
self.items
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(ix, item)| item.render(view, ix, cx)),
|
||||
),
|
||||
(true, Toggle::Toggled(false)) => div(),
|
||||
(true, _) => {
|
||||
div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted))
|
||||
}
|
||||
};
|
||||
|
||||
v_stack()
|
||||
.w_full()
|
||||
.py_1()
|
||||
.children(self.header.map(|header| header))
|
||||
.child(list_content)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> List<V> {
|
||||
pub fn new(items: Vec<ListItem<V>>) -> Self {
|
||||
Self {
|
||||
@ -424,7 +512,7 @@ impl<V: 'static> List<V> {
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let list_content = match (self.items.is_empty(), self.toggle) {
|
||||
(false, _) => div().children(
|
||||
self.items
|
||||
|
@ -1,9 +1,9 @@
|
||||
use gpui::AnyElement;
|
||||
use gpui::{AnyElement, Div, RenderOnce, Stateful};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{h_stack, prelude::*, v_stack, Button, Icon, IconButton, Label};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Modal<V: 'static> {
|
||||
id: ElementId,
|
||||
title: Option<SharedString>,
|
||||
@ -12,33 +12,11 @@ pub struct Modal<V: 'static> {
|
||||
children: SmallVec<[AnyElement<V>; 2]>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Modal<V> {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
title: None,
|
||||
primary_action: None,
|
||||
secondary_action: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for Modal<V> {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
pub fn title(mut self, title: impl Into<SharedString>) -> Self {
|
||||
self.title = Some(title.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn primary_action(mut self, action: Button<V>) -> Self {
|
||||
self.primary_action = Some(action);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn secondary_action(mut self, action: Button<V>) -> Self {
|
||||
self.secondary_action = Some(action);
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let _view: &mut V = view;
|
||||
v_stack()
|
||||
.id(self.id.clone())
|
||||
.w_96()
|
||||
@ -74,7 +52,34 @@ impl<V: 'static> Modal<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentComponent<V> for Modal<V> {
|
||||
impl<V: 'static> Modal<V> {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
title: None,
|
||||
primary_action: None,
|
||||
secondary_action: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title(mut self, title: impl Into<SharedString>) -> Self {
|
||||
self.title = Some(title.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn primary_action(mut self, action: Button<V>) -> Self {
|
||||
self.primary_action = Some(action);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn secondary_action(mut self, action: Button<V>) -> Self {
|
||||
self.secondary_action = Some(action);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentElement<V> for Modal<V> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use gpui::rems;
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, Icon};
|
||||
|
||||
#[derive(Component)]
|
||||
// #[derive(RenderOnce)]
|
||||
pub struct NotificationToast {
|
||||
label: SharedString,
|
||||
icon: Option<Icon>,
|
||||
@ -22,7 +22,7 @@ impl NotificationToast {
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
h_stack()
|
||||
.z_index(5)
|
||||
.absolute()
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::{h_stack, prelude::*, v_stack, KeyBinding, Label};
|
||||
use gpui::prelude::*;
|
||||
use gpui::Div;
|
||||
use gpui::Stateful;
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Palette {
|
||||
id: ElementId,
|
||||
input_placeholder: SharedString,
|
||||
@ -10,6 +12,70 @@ pub struct Palette {
|
||||
default_order: OrderMethod,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Palette {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
v_stack()
|
||||
.id(self.id)
|
||||
.w_96()
|
||||
.rounded_lg()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(
|
||||
v_stack()
|
||||
.gap_px()
|
||||
.child(v_stack().py_0p5().px_1().child(
|
||||
div().px_2().py_0p5().child(
|
||||
Label::new(self.input_placeholder).color(TextColor::Placeholder),
|
||||
),
|
||||
))
|
||||
.child(
|
||||
div()
|
||||
.h_px()
|
||||
.w_full()
|
||||
.bg(cx.theme().colors().element_background),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.id("items")
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.grow()
|
||||
.max_h_96()
|
||||
.overflow_y_scroll()
|
||||
.children(
|
||||
vec![if self.items.is_empty() {
|
||||
Some(h_stack().justify_between().px_2().py_1().child(
|
||||
Label::new(self.empty_string).color(TextColor::Muted),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}]
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.children(self.items.into_iter().enumerate().map(|(index, item)| {
|
||||
h_stack()
|
||||
.id(index)
|
||||
.justify_between()
|
||||
.px_2()
|
||||
.py_0p5()
|
||||
.rounded_lg()
|
||||
.hover(|style| {
|
||||
style.bg(cx.theme().colors().ghost_element_hover)
|
||||
})
|
||||
.active(|style| {
|
||||
style.bg(cx.theme().colors().ghost_element_active)
|
||||
})
|
||||
.child(item)
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
@ -41,76 +107,33 @@ impl Palette {
|
||||
self.default_order = default_order;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
v_stack()
|
||||
.id(self.id.clone())
|
||||
.w_96()
|
||||
.rounded_lg()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(
|
||||
v_stack()
|
||||
.gap_px()
|
||||
.child(v_stack().py_0p5().px_1().child(div().px_2().py_0p5().child(
|
||||
Label::new(self.input_placeholder.clone()).color(TextColor::Placeholder),
|
||||
)))
|
||||
.child(
|
||||
div()
|
||||
.h_px()
|
||||
.w_full()
|
||||
.bg(cx.theme().colors().element_background),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.id("items")
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.grow()
|
||||
.max_h_96()
|
||||
.overflow_y_scroll()
|
||||
.children(
|
||||
vec![if self.items.is_empty() {
|
||||
Some(
|
||||
h_stack().justify_between().px_2().py_1().child(
|
||||
Label::new(self.empty_string.clone())
|
||||
.color(TextColor::Muted),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}]
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.children(self.items.into_iter().enumerate().map(|(index, item)| {
|
||||
h_stack()
|
||||
.id(index)
|
||||
.justify_between()
|
||||
.px_2()
|
||||
.py_0p5()
|
||||
.rounded_lg()
|
||||
.hover(|style| {
|
||||
style.bg(cx.theme().colors().ghost_element_hover)
|
||||
})
|
||||
.active(|style| {
|
||||
style.bg(cx.theme().colors().ghost_element_active)
|
||||
})
|
||||
.child(item)
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct PaletteItem {
|
||||
pub label: SharedString,
|
||||
pub sublabel: Option<SharedString>,
|
||||
pub key_binding: Option<KeyBinding>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for PaletteItem {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.grow()
|
||||
.justify_between()
|
||||
.child(
|
||||
v_stack()
|
||||
.child(Label::new(self.label))
|
||||
.children(self.sublabel.map(|sublabel| Label::new(sublabel))),
|
||||
)
|
||||
.children(self.key_binding)
|
||||
}
|
||||
}
|
||||
|
||||
impl PaletteItem {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
@ -134,20 +157,6 @@ impl PaletteItem {
|
||||
self.key_binding = key_binding.into();
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.grow()
|
||||
.justify_between()
|
||||
.child(
|
||||
v_stack()
|
||||
.child(Label::new(self.label.clone()))
|
||||
.children(self.sublabel.clone().map(|sublabel| Label::new(sublabel))),
|
||||
)
|
||||
.children(self.key_binding)
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::ElementId;
|
||||
@ -164,7 +173,7 @@ mod stories {
|
||||
|
||||
pub struct PaletteStory;
|
||||
|
||||
impl Render for PaletteStory {
|
||||
impl Render<Self> for PaletteStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::{prelude::*, AbsoluteLength, AnyElement};
|
||||
use gpui::{prelude::*, AbsoluteLength, AnyElement, Div, RenderOnce, Stateful};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
@ -38,7 +38,7 @@ pub enum PanelSide {
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Panel<V: 'static> {
|
||||
id: ElementId,
|
||||
current_side: PanelSide,
|
||||
@ -49,6 +49,30 @@ pub struct Panel<V: 'static> {
|
||||
children: SmallVec<[AnyElement<V>; 2]>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Panel<V> {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let current_size = self.width.unwrap_or(self.initial_width);
|
||||
|
||||
v_stack()
|
||||
.id(self.id.clone())
|
||||
.flex_initial()
|
||||
.map(|this| match self.current_side {
|
||||
PanelSide::Left | PanelSide::Right => this.h_full().w(current_size),
|
||||
PanelSide::Bottom => this,
|
||||
})
|
||||
.map(|this| match self.current_side {
|
||||
PanelSide::Left => this.border_r(),
|
||||
PanelSide::Right => this.border_l(),
|
||||
PanelSide::Bottom => this.border_b().w_full().h(current_size),
|
||||
})
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.border_color(cx.theme().colors().border)
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Panel<V> {
|
||||
pub fn new(id: impl Into<ElementId>, cx: &mut WindowContext) -> Self {
|
||||
let settings = user_settings(cx);
|
||||
@ -91,29 +115,9 @@ impl<V: 'static> Panel<V> {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let current_size = self.width.unwrap_or(self.initial_width);
|
||||
|
||||
v_stack()
|
||||
.id(self.id.clone())
|
||||
.flex_initial()
|
||||
.map(|this| match self.current_side {
|
||||
PanelSide::Left | PanelSide::Right => this.h_full().w(current_size),
|
||||
PanelSide::Bottom => this,
|
||||
})
|
||||
.map(|this| match self.current_side {
|
||||
PanelSide::Left => this.border_r(),
|
||||
PanelSide::Right => this.border_l(),
|
||||
PanelSide::Bottom => this.border_b().w_full().h(current_size),
|
||||
})
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.border_color(cx.theme().colors().border)
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentComponent<V> for Panel<V> {
|
||||
impl<V: 'static> ParentElement<V> for Panel<V> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
@ -126,11 +130,11 @@ pub use stories::*;
|
||||
mod stories {
|
||||
use super::*;
|
||||
use crate::{Label, Story};
|
||||
use gpui::{Div, InteractiveComponent, Render};
|
||||
use gpui::{Div, InteractiveElement, Render};
|
||||
|
||||
pub struct PanelStory;
|
||||
|
||||
impl Render for PanelStory {
|
||||
impl Render<Self> for PanelStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,19 +1,17 @@
|
||||
use gpui::{Div, RenderOnce};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{Avatar, Facepile, PlayerWithCallStatus};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct PlayerStack {
|
||||
player_with_call_status: PlayerWithCallStatus,
|
||||
}
|
||||
|
||||
impl PlayerStack {
|
||||
pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self {
|
||||
Self {
|
||||
player_with_call_status,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for PlayerStack {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let player = self.player_with_call_status.get_player();
|
||||
|
||||
let followers = self
|
||||
@ -59,3 +57,11 @@ impl PlayerStack {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerStack {
|
||||
pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self {
|
||||
Self {
|
||||
player_with_call_status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{Icon, IconElement, Label, TextColor};
|
||||
use gpui::{prelude::*, red, Div, ElementId, Render, View};
|
||||
use gpui::{prelude::*, red, Div, ElementId, Render, RenderOnce, Stateful, View};
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
#[derive(RenderOnce, Clone)]
|
||||
pub struct Tab {
|
||||
id: ElementId,
|
||||
title: String,
|
||||
@ -20,7 +20,7 @@ struct TabDragState {
|
||||
title: String,
|
||||
}
|
||||
|
||||
impl Render for TabDragState {
|
||||
impl Render<Self> for TabDragState {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -28,65 +28,10 @@ impl Render for TabDragState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
title: "untitled".to_string(),
|
||||
icon: None,
|
||||
current: false,
|
||||
dirty: false,
|
||||
fs_status: FileSystemStatus::None,
|
||||
git_status: GitStatus::None,
|
||||
diagnostic_status: DiagnosticStatus::None,
|
||||
close_side: IconSide::Right,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for Tab {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
pub fn current(mut self, current: bool) -> Self {
|
||||
self.current = current;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn title(mut self, title: String) -> Self {
|
||||
self.title = title;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon<I>(mut self, icon: I) -> Self
|
||||
where
|
||||
I: Into<Option<Icon>>,
|
||||
{
|
||||
self.icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dirty(mut self, dirty: bool) -> Self {
|
||||
self.dirty = dirty;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
|
||||
self.fs_status = fs_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn git_status(mut self, git_status: GitStatus) -> Self {
|
||||
self.git_status = git_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
|
||||
self.diagnostic_status = diagnostic_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn close_side(mut self, close_side: IconSide) -> Self {
|
||||
self.close_side = close_side;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
|
||||
let is_deleted = self.fs_status == FileSystemStatus::Deleted;
|
||||
|
||||
@ -164,6 +109,65 @@ impl Tab {
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
title: "untitled".to_string(),
|
||||
icon: None,
|
||||
current: false,
|
||||
dirty: false,
|
||||
fs_status: FileSystemStatus::None,
|
||||
git_status: GitStatus::None,
|
||||
diagnostic_status: DiagnosticStatus::None,
|
||||
close_side: IconSide::Right,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current(mut self, current: bool) -> Self {
|
||||
self.current = current;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn title(mut self, title: String) -> Self {
|
||||
self.title = title;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon<I>(mut self, icon: I) -> Self
|
||||
where
|
||||
I: Into<Option<Icon>>,
|
||||
{
|
||||
self.icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dirty(mut self, dirty: bool) -> Self {
|
||||
self.dirty = dirty;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
|
||||
self.fs_status = fs_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn git_status(mut self, git_status: GitStatus) -> Self {
|
||||
self.git_status = git_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
|
||||
self.diagnostic_status = diagnostic_status;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn close_side(mut self, close_side: IconSide) -> Self {
|
||||
self.close_side = close_side;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -175,7 +179,7 @@ mod stories {
|
||||
|
||||
pub struct TabStory;
|
||||
|
||||
impl Render for TabStory {
|
||||
impl Render<Self> for TabStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::prelude::*;
|
||||
use gpui::{prelude::*, AnyElement};
|
||||
use gpui::{prelude::*, AnyElement, RenderOnce};
|
||||
use gpui::{Div, Element};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
@ -21,21 +22,16 @@ pub enum ToastOrigin {
|
||||
/// they are actively showing the a process in progress.
|
||||
///
|
||||
/// Only one toast may be visible at a time.
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Toast<V: 'static> {
|
||||
origin: ToastOrigin,
|
||||
children: SmallVec<[AnyElement<V>; 2]>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Toast<V> {
|
||||
pub fn new(origin: ToastOrigin) -> Self {
|
||||
Self {
|
||||
origin,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for Toast<V> {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let mut div = div();
|
||||
|
||||
if self.origin == ToastOrigin::Bottom {
|
||||
@ -58,7 +54,38 @@ impl<V: 'static> Toast<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentComponent<V> for Toast<V> {
|
||||
impl<V: 'static> Toast<V> {
|
||||
pub fn new(origin: ToastOrigin) -> Self {
|
||||
Self {
|
||||
origin,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let mut div = div();
|
||||
|
||||
if self.origin == ToastOrigin::Bottom {
|
||||
div = div.right_1_2();
|
||||
} else {
|
||||
div = div.right_2();
|
||||
}
|
||||
|
||||
div.z_index(5)
|
||||
.absolute()
|
||||
.bottom_9()
|
||||
.flex()
|
||||
.py_1()
|
||||
.px_1p5()
|
||||
.rounded_lg()
|
||||
.shadow_md()
|
||||
.overflow_hidden()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentElement<V> for Toast<V> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
@ -77,7 +104,7 @@ mod stories {
|
||||
|
||||
pub struct ToastStory;
|
||||
|
||||
impl Render for ToastStory {
|
||||
impl Render<Self> for ToastStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::{div, Component, ParentComponent};
|
||||
use gpui::{div, Element, ParentElement};
|
||||
|
||||
use crate::{Icon, IconElement, IconSize, TextColor};
|
||||
|
||||
@ -44,7 +44,7 @@ impl From<bool> for Toggle {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disclosure_control<V: 'static>(toggle: Toggle) -> impl Component<V> {
|
||||
pub fn disclosure_control<V: 'static>(toggle: Toggle) -> impl Element<V> {
|
||||
match (toggle.is_toggleable(), toggle.is_toggled()) {
|
||||
(false, _) => div(),
|
||||
(_, true) => div().child(
|
||||
|
@ -1,14 +1,23 @@
|
||||
use crate::prelude::*;
|
||||
use gpui::{Div, RenderOnce};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ToolDivider;
|
||||
|
||||
impl<V: 'static> Component<V> for ToolDivider {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div().w_px().h_3().bg(cx.theme().colors().border)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolDivider {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
div().w_px().h_3().bg(cx.theme().colors().border)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::{overlay, Action, AnyView, Overlay, Render, VisualContext};
|
||||
use gpui::{overlay, Action, AnyView, Overlay, Render, RenderOnce, VisualContext};
|
||||
use settings2::Settings;
|
||||
use theme2::{ActiveTheme, ThemeSettings};
|
||||
|
||||
@ -67,7 +67,7 @@ impl Tooltip {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Tooltip {
|
||||
impl Render<Self> for Tooltip {
|
||||
type Element = Overlay<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use gpui::rems;
|
||||
use gpui::Rems;
|
||||
pub use gpui::{
|
||||
div, Component, Element, ElementId, InteractiveComponent, ParentComponent, SharedString,
|
||||
Styled, ViewContext, WindowContext,
|
||||
div, Component, Element, ElementId, InteractiveElement, ParentElement, SharedString, Styled,
|
||||
ViewContext, WindowContext,
|
||||
};
|
||||
|
||||
pub use crate::elevation::*;
|
||||
|
@ -745,11 +745,11 @@ pub fn hello_world_rust_editor_example(cx: &mut ViewContext<EditorPane>) -> Edit
|
||||
PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(),
|
||||
vec![Symbol(vec![
|
||||
HighlightedText {
|
||||
text: "fn ".to_string(),
|
||||
text: "fn ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "main".to_string(),
|
||||
text: "main".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
])],
|
||||
@ -779,15 +779,15 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![
|
||||
HighlightedText {
|
||||
text: "fn ".to_string(),
|
||||
text: "fn ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "main".to_string(),
|
||||
text: "main".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "() {".to_string(),
|
||||
text: "() {".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
],
|
||||
@ -803,7 +803,7 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: " // Statements here are executed when the compiled binary is called."
|
||||
.to_string(),
|
||||
.into(),
|
||||
color: cx.theme().syntax_color("comment"),
|
||||
}],
|
||||
}),
|
||||
@ -826,7 +826,7 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
|
||||
current: false,
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: " // Print text to the console.".to_string(),
|
||||
text: " // Print text to the console.".into(),
|
||||
color: cx.theme().syntax_color("comment"),
|
||||
}],
|
||||
}),
|
||||
@ -841,15 +841,15 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![
|
||||
HighlightedText {
|
||||
text: " println!(".to_string(),
|
||||
text: " println!(".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
HighlightedText {
|
||||
text: "\"Hello, world!\"".to_string(),
|
||||
text: "\"Hello, world!\"".into(),
|
||||
color: cx.theme().syntax_color("string"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: ");".to_string(),
|
||||
text: ");".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
],
|
||||
@ -864,7 +864,7 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
|
||||
current: false,
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: "}".to_string(),
|
||||
text: "}".into(),
|
||||
color: cx.theme().colors().text,
|
||||
}],
|
||||
}),
|
||||
@ -882,11 +882,11 @@ pub fn hello_world_rust_editor_with_status_example(cx: &mut ViewContext<EditorPa
|
||||
PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(),
|
||||
vec![Symbol(vec![
|
||||
HighlightedText {
|
||||
text: "fn ".to_string(),
|
||||
text: "fn ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "main".to_string(),
|
||||
text: "main".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
])],
|
||||
@ -916,15 +916,15 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![
|
||||
HighlightedText {
|
||||
text: "fn ".to_string(),
|
||||
text: "fn ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "main".to_string(),
|
||||
text: "main".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "() {".to_string(),
|
||||
text: "() {".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
],
|
||||
@ -940,7 +940,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: "// Statements here are executed when the compiled binary is called."
|
||||
.to_string(),
|
||||
.into(),
|
||||
color: cx.theme().syntax_color("comment"),
|
||||
}],
|
||||
}),
|
||||
@ -963,7 +963,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
|
||||
current: false,
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: " // Print text to the console.".to_string(),
|
||||
text: " // Print text to the console.".into(),
|
||||
color: cx.theme().syntax_color("comment"),
|
||||
}],
|
||||
}),
|
||||
@ -978,15 +978,15 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![
|
||||
HighlightedText {
|
||||
text: " println!(".to_string(),
|
||||
text: " println!(".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
HighlightedText {
|
||||
text: "\"Hello, world!\"".to_string(),
|
||||
text: "\"Hello, world!\"".into(),
|
||||
color: cx.theme().syntax_color("string"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: ");".to_string(),
|
||||
text: ");".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
],
|
||||
@ -1001,7 +1001,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
|
||||
current: false,
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: "}".to_string(),
|
||||
text: "}".into(),
|
||||
color: cx.theme().colors().text,
|
||||
}],
|
||||
}),
|
||||
@ -1015,7 +1015,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
|
||||
current: false,
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: "".to_string(),
|
||||
text: "".into(),
|
||||
color: cx.theme().colors().text,
|
||||
}],
|
||||
}),
|
||||
@ -1029,7 +1029,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
|
||||
current: false,
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: "// Marshall and Nate were here".to_string(),
|
||||
text: "// Marshall and Nate were here".into(),
|
||||
color: cx.theme().syntax_color("comment"),
|
||||
}],
|
||||
}),
|
||||
@ -1042,7 +1042,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
|
||||
|
||||
pub fn terminal_buffer(cx: &AppContext) -> Buffer {
|
||||
Buffer::new("terminal")
|
||||
.set_title("zed — fish".to_string())
|
||||
.set_title(Some("zed — fish".into()))
|
||||
.set_rows(Some(BufferRows {
|
||||
show_line_numbers: false,
|
||||
rows: terminal_buffer_rows(cx),
|
||||
@ -1060,31 +1060,31 @@ pub fn terminal_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![
|
||||
HighlightedText {
|
||||
text: "maxdeviant ".to_string(),
|
||||
text: "maxdeviant ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "in ".to_string(),
|
||||
text: "in ".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
HighlightedText {
|
||||
text: "profaned-capital ".to_string(),
|
||||
text: "profaned-capital ".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "in ".to_string(),
|
||||
text: "in ".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
HighlightedText {
|
||||
text: "~/p/zed ".to_string(),
|
||||
text: "~/p/zed ".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "on ".to_string(),
|
||||
text: "on ".into(),
|
||||
color: cx.theme().colors().text,
|
||||
},
|
||||
HighlightedText {
|
||||
text: " gpui2-ui ".to_string(),
|
||||
text: " gpui2-ui ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
],
|
||||
@ -1099,7 +1099,7 @@ pub fn terminal_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
|
||||
current: false,
|
||||
line: Some(HighlightedLine {
|
||||
highlighted_texts: vec![HighlightedText {
|
||||
text: "λ ".to_string(),
|
||||
text: "λ ".into(),
|
||||
color: cx.theme().syntax_color("string"),
|
||||
}],
|
||||
}),
|
||||
|
@ -15,23 +15,29 @@ impl Story {
|
||||
.bg(cx.theme().colors().background)
|
||||
}
|
||||
|
||||
pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: &str) -> impl Component<V> {
|
||||
pub fn title<V: 'static>(
|
||||
cx: &mut ViewContext<V>,
|
||||
title: impl Into<SharedString>,
|
||||
) -> impl Element<V> {
|
||||
div()
|
||||
.text_xl()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.child(title.to_owned())
|
||||
.child(title.into())
|
||||
}
|
||||
|
||||
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
Self::title(cx, std::any::type_name::<T>())
|
||||
}
|
||||
|
||||
pub fn label<V: 'static>(cx: &mut ViewContext<V>, label: &str) -> impl Component<V> {
|
||||
pub fn label<V: 'static>(
|
||||
cx: &mut ViewContext<V>,
|
||||
label: impl Into<SharedString>,
|
||||
) -> impl Element<V> {
|
||||
div()
|
||||
.mt_4()
|
||||
.mb_2()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.child(label.to_owned())
|
||||
.child(label.into())
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,17 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{Icon, IconButton, Label, Panel, PanelSide};
|
||||
use gpui::{prelude::*, rems, AbsoluteLength};
|
||||
use gpui::{prelude::*, rems, AbsoluteLength, RenderOnce};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct AssistantPanel {
|
||||
id: ElementId,
|
||||
current_side: PanelSide,
|
||||
}
|
||||
|
||||
impl AssistantPanel {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
current_side: PanelSide::default(),
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for AssistantPanel {
|
||||
type Rendered = Panel<V>;
|
||||
|
||||
pub fn side(mut self, side: PanelSide) -> Self {
|
||||
self.current_side = side;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
Panel::new(self.id.clone(), cx)
|
||||
.children(vec![div()
|
||||
.flex()
|
||||
@ -64,12 +54,26 @@ impl AssistantPanel {
|
||||
.overflow_y_scroll()
|
||||
.child(Label::new("Is this thing on?")),
|
||||
)
|
||||
.render()])
|
||||
.into_any()])
|
||||
.side(self.current_side)
|
||||
.width(AbsoluteLength::Rems(rems(32.)))
|
||||
}
|
||||
}
|
||||
|
||||
impl AssistantPanel {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
current_side: PanelSide::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn side(mut self, side: PanelSide) -> Self {
|
||||
self.current_side = side;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -80,7 +84,7 @@ mod stories {
|
||||
use gpui::{Div, Render};
|
||||
pub struct AssistantPanelStory;
|
||||
|
||||
impl Render for AssistantPanelStory {
|
||||
impl Render<Self> for AssistantPanelStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,30 +1,21 @@
|
||||
use crate::{h_stack, prelude::*, HighlightedText};
|
||||
use gpui::{prelude::*, Div};
|
||||
use gpui::{prelude::*, Div, Stateful};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Symbol(pub Vec<HighlightedText>);
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Breadcrumb {
|
||||
path: PathBuf,
|
||||
symbols: Vec<Symbol>,
|
||||
}
|
||||
|
||||
impl Breadcrumb {
|
||||
pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
|
||||
Self { path, symbols }
|
||||
}
|
||||
impl<V: 'static> Component<V> for Breadcrumb {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Div<V> {
|
||||
div()
|
||||
.child(" › ")
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, view_state: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view_state: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let symbols_len = self.symbols.len();
|
||||
|
||||
h_stack()
|
||||
.id("breadcrumb")
|
||||
.px_1()
|
||||
@ -33,7 +24,9 @@ impl Breadcrumb {
|
||||
.rounded_md()
|
||||
.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
|
||||
.active(|style| style.bg(cx.theme().colors().ghost_element_active))
|
||||
.child(self.path.clone().to_str().unwrap().to_string())
|
||||
.child(SharedString::from(
|
||||
self.path.clone().to_str().unwrap().to_string(),
|
||||
))
|
||||
.child(if !self.symbols.is_empty() {
|
||||
self.render_separator(cx)
|
||||
} else {
|
||||
@ -64,6 +57,18 @@ impl Breadcrumb {
|
||||
}
|
||||
}
|
||||
|
||||
impl Breadcrumb {
|
||||
pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
|
||||
Self { path, symbols }
|
||||
}
|
||||
|
||||
fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Div<V> {
|
||||
div()
|
||||
.child(" › ")
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -76,7 +81,7 @@ mod stories {
|
||||
|
||||
pub struct BreadcrumbStory;
|
||||
|
||||
impl Render for BreadcrumbStory {
|
||||
impl Render<Self> for BreadcrumbStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -88,21 +93,21 @@ mod stories {
|
||||
vec![
|
||||
Symbol(vec![
|
||||
HighlightedText {
|
||||
text: "impl ".to_string(),
|
||||
text: "impl ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "BreadcrumbStory".to_string(),
|
||||
text: "BreadcrumbStory".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
]),
|
||||
Symbol(vec![
|
||||
HighlightedText {
|
||||
text: "fn ".to_string(),
|
||||
text: "fn ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "render".to_string(),
|
||||
text: "render".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
]),
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::{Hsla, WindowContext};
|
||||
use gpui::{Div, Hsla, RenderOnce, WindowContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, v_stack, Icon, IconElement};
|
||||
@ -11,7 +11,7 @@ pub struct PlayerCursor {
|
||||
|
||||
#[derive(Default, PartialEq, Clone)]
|
||||
pub struct HighlightedText {
|
||||
pub text: String,
|
||||
pub text: SharedString,
|
||||
pub color: Hsla,
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ impl BufferRow {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
#[derive(RenderOnce, Clone)]
|
||||
pub struct Buffer {
|
||||
id: ElementId,
|
||||
rows: Option<BufferRows>,
|
||||
@ -117,6 +117,21 @@ pub struct Buffer {
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Buffer {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let rows = self.render_rows(cx);
|
||||
|
||||
v_stack()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.children(rows)
|
||||
}
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
@ -154,7 +169,7 @@ impl Buffer {
|
||||
self
|
||||
}
|
||||
|
||||
fn render_row<V: 'static>(row: BufferRow, cx: &WindowContext) -> impl Component<V> {
|
||||
fn render_row<V: 'static>(row: BufferRow, cx: &WindowContext) -> impl Element<V> {
|
||||
let line_background = if row.current {
|
||||
cx.theme().colors().editor_active_line_background
|
||||
} else {
|
||||
@ -186,7 +201,7 @@ impl Buffer {
|
||||
h_stack().justify_end().px_0p5().w_3().child(
|
||||
div()
|
||||
.text_color(line_number_color)
|
||||
.child(row.line_number.to_string()),
|
||||
.child(SharedString::from(row.line_number.to_string())),
|
||||
),
|
||||
)
|
||||
})
|
||||
@ -202,7 +217,7 @@ impl Buffer {
|
||||
}))
|
||||
}
|
||||
|
||||
fn render_rows<V: 'static>(&self, cx: &WindowContext) -> Vec<impl Component<V>> {
|
||||
fn render_rows<V: 'static>(&self, cx: &WindowContext) -> Vec<impl Element<V>> {
|
||||
match &self.rows {
|
||||
Some(rows) => rows
|
||||
.rows
|
||||
@ -213,7 +228,7 @@ impl Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let rows = self.render_rows(cx);
|
||||
|
||||
v_stack()
|
||||
@ -239,7 +254,7 @@ mod stories {
|
||||
|
||||
pub struct BufferStory;
|
||||
|
||||
impl Render for BufferStory {
|
||||
impl Render<Self> for BufferStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use gpui::{Div, Render, View, VisualContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_stack, Icon, IconButton, Input, TextColor};
|
||||
use gpui::{Div, Render, RenderOnce, View, VisualContext};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BufferSearch {
|
||||
@ -26,7 +25,7 @@ impl BufferSearch {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for BufferSearch {
|
||||
impl Render<Self> for BufferSearch {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
|
||||
|
@ -1,27 +1,17 @@
|
||||
use crate::{prelude::*, Icon, IconButton, Input, Label};
|
||||
use chrono::NaiveDateTime;
|
||||
use gpui::prelude::*;
|
||||
use gpui::{prelude::*, Div, Stateful};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ChatPanel {
|
||||
element_id: ElementId,
|
||||
messages: Vec<ChatMessage>,
|
||||
}
|
||||
|
||||
impl ChatPanel {
|
||||
pub fn new(element_id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
element_id: element_id.into(),
|
||||
messages: Vec::new(),
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for ChatPanel {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
pub fn messages(mut self, messages: Vec<ChatMessage>) -> Self {
|
||||
self.messages = messages;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.id(self.element_id.clone())
|
||||
.flex()
|
||||
@ -67,23 +57,31 @@ impl ChatPanel {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
impl ChatPanel {
|
||||
pub fn new(element_id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
element_id: element_id.into(),
|
||||
messages: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn messages(mut self, messages: Vec<ChatMessage>) -> Self {
|
||||
self.messages = messages;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ChatMessage {
|
||||
author: String,
|
||||
text: String,
|
||||
sent_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
impl ChatMessage {
|
||||
pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
|
||||
Self {
|
||||
author,
|
||||
text,
|
||||
sent_at,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for ChatMessage {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
@ -101,6 +99,16 @@ impl ChatMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl ChatMessage {
|
||||
pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
|
||||
Self {
|
||||
author,
|
||||
text,
|
||||
sent_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -115,7 +123,7 @@ mod stories {
|
||||
|
||||
pub struct ChatPanelStory;
|
||||
|
||||
impl Render for ChatPanelStory {
|
||||
impl Render<Self> for ChatPanelStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -2,19 +2,17 @@ use crate::{
|
||||
prelude::*, static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon,
|
||||
List, ListHeader, Toggle,
|
||||
};
|
||||
use gpui::prelude::*;
|
||||
use gpui::{prelude::*, Div, Stateful};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct CollabPanel {
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl CollabPanel {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
impl<V: 'static> Component<V> for CollabPanel {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
v_stack()
|
||||
.id(self.id.clone())
|
||||
.h_full()
|
||||
@ -86,6 +84,12 @@ impl CollabPanel {
|
||||
}
|
||||
}
|
||||
|
||||
impl CollabPanel {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -97,7 +101,7 @@ mod stories {
|
||||
|
||||
pub struct CollabPanelStory;
|
||||
|
||||
impl Render for CollabPanelStory {
|
||||
impl Render<Self> for CollabPanelStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,17 +1,15 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{example_editor_actions, OrderMethod, Palette};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct CommandPalette {
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl CommandPalette {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
impl<V: 'static> Component<V> for CommandPalette {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div().id(self.id.clone()).child(
|
||||
Palette::new("palette")
|
||||
.items(example_editor_actions())
|
||||
@ -22,6 +20,13 @@ impl CommandPalette {
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandPalette {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce, Stateful};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -35,7 +40,7 @@ mod stories {
|
||||
|
||||
pub struct CommandPaletteStory;
|
||||
|
||||
impl Render for CommandPaletteStory {
|
||||
impl Render<Self> for CommandPaletteStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,39 +1,42 @@
|
||||
use crate::{prelude::*, Button, Label, Modal, TextColor};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct CopilotModal {
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for CopilotModal {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div().id(self.id.clone()).child(
|
||||
Modal::new("some-id")
|
||||
.title("Connect Copilot to Zed")
|
||||
.child(Label::new("You can update your settings or sign out from the Copilot menu in the status bar.").color(TextColor::Muted))
|
||||
.primary_action(Button::new("Connect to Github").variant(ButtonVariant::Filled)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CopilotModal {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
div().id(self.id.clone()).child(
|
||||
Modal::new("some-id")
|
||||
.title("Connect Copilot to Zed")
|
||||
.child(Label::new("You can update your settings or sign out from the Copilot menu in the status bar.").color(TextColor::Muted))
|
||||
.primary_action(Button::new("Connect to Github").variant(ButtonVariant::Filled)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce, Stateful};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use gpui::{Div, Render};
|
||||
|
||||
use crate::Story;
|
||||
|
||||
use super::*;
|
||||
use crate::Story;
|
||||
use gpui::{Div, Render};
|
||||
|
||||
pub struct CopilotModalStory;
|
||||
|
||||
impl Render for CopilotModalStory {
|
||||
impl Render<Self> for CopilotModalStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use gpui::{Div, Render, View, VisualContext};
|
||||
use gpui::{Div, Render, RenderOnce, View, VisualContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{
|
||||
@ -47,7 +47,7 @@ impl EditorPane {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for EditorPane {
|
||||
impl Render<Self> for EditorPane {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
|
||||
|
@ -1,17 +1,15 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{OrderMethod, Palette, PaletteItem};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct LanguageSelector {
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl LanguageSelector {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
impl<V: 'static> Component<V> for LanguageSelector {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div().id(self.id.clone()).child(
|
||||
Palette::new("palette")
|
||||
.items(vec![
|
||||
@ -33,6 +31,34 @@ impl LanguageSelector {
|
||||
}
|
||||
}
|
||||
|
||||
impl LanguageSelector {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
div().id(self.id.clone()).child(
|
||||
Palette::new("palette")
|
||||
.items(vec![
|
||||
PaletteItem::new("C"),
|
||||
PaletteItem::new("C++"),
|
||||
PaletteItem::new("CSS"),
|
||||
PaletteItem::new("Elixir"),
|
||||
PaletteItem::new("Elm"),
|
||||
PaletteItem::new("ERB"),
|
||||
PaletteItem::new("Rust (current)"),
|
||||
PaletteItem::new("Scheme"),
|
||||
PaletteItem::new("TOML"),
|
||||
PaletteItem::new("TypeScript"),
|
||||
])
|
||||
.placeholder("Select a language...")
|
||||
.empty_string("No matches")
|
||||
.default_order(OrderMethod::Ascending),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce, Stateful};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -44,7 +70,7 @@ mod stories {
|
||||
|
||||
pub struct LanguageSelectorStory;
|
||||
|
||||
impl Render for LanguageSelectorStory {
|
||||
impl Render<Self> for LanguageSelectorStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,17 +1,15 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{v_stack, Buffer, Icon, IconButton, Label};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct MultiBuffer {
|
||||
buffers: Vec<Buffer>,
|
||||
}
|
||||
|
||||
impl MultiBuffer {
|
||||
pub fn new(buffers: Vec<Buffer>) -> Self {
|
||||
Self { buffers }
|
||||
}
|
||||
impl<V: 'static> Component<V> for MultiBuffer {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
v_stack()
|
||||
.w_full()
|
||||
.h_full()
|
||||
@ -33,6 +31,13 @@ impl MultiBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiBuffer {
|
||||
pub fn new(buffers: Vec<Buffer>) -> Self {
|
||||
Self { buffers }
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -44,7 +49,7 @@ mod stories {
|
||||
|
||||
pub struct MultiBufferStory;
|
||||
|
||||
impl Render for MultiBufferStory {
|
||||
impl Render<Self> for MultiBufferStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -3,19 +3,17 @@ use crate::{
|
||||
v_stack, Avatar, ButtonOrIconButton, ClickHandler, Icon, IconElement, Label, LineHeightStyle,
|
||||
ListHeader, ListHeaderMeta, ListSeparator, PublicPlayer, TextColor, UnreadIndicator,
|
||||
};
|
||||
use gpui::prelude::*;
|
||||
use gpui::{prelude::*, Div, Stateful};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct NotificationsPanel {
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl NotificationsPanel {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
impl<V: 'static> Component<V> for NotificationsPanel {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.flex()
|
||||
@ -56,6 +54,12 @@ impl NotificationsPanel {
|
||||
}
|
||||
}
|
||||
|
||||
impl NotificationsPanel {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NotificationAction<V: 'static> {
|
||||
button: ButtonOrIconButton<V>,
|
||||
tooltip: SharedString,
|
||||
@ -102,7 +106,7 @@ impl<V: 'static> Default for NotificationHandlers<V> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Notification<V: 'static> {
|
||||
id: ElementId,
|
||||
slot: ActorOrIcon,
|
||||
@ -116,6 +120,85 @@ pub struct Notification<V: 'static> {
|
||||
handlers: NotificationHandlers<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Notification<V> {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.relative()
|
||||
.id(self.id.clone())
|
||||
.p_1()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_full()
|
||||
.children(
|
||||
Some(
|
||||
div()
|
||||
.absolute()
|
||||
.left(px(3.0))
|
||||
.top_3()
|
||||
.z_index(2)
|
||||
.child(UnreadIndicator::new()),
|
||||
)
|
||||
.filter(|_| self.unread),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.z_index(1)
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
h_stack()
|
||||
.w_full()
|
||||
.gap_2()
|
||||
.child(self.render_slot(cx))
|
||||
.child(div().flex_1().child(Label::new(self.message.clone()))),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(naive_format_distance_from_now(
|
||||
self.date_received,
|
||||
true,
|
||||
true,
|
||||
))
|
||||
.color(TextColor::Muted),
|
||||
)
|
||||
.child(self.render_meta_items(cx)),
|
||||
)
|
||||
.child(match (self.actions, self.action_taken) {
|
||||
// Show nothing
|
||||
(None, _) => div(),
|
||||
// Show the taken_message
|
||||
(Some(_), Some(action_taken)) => h_stack()
|
||||
.children(action_taken.taken_message.0.map(|icon| {
|
||||
IconElement::new(icon).color(crate::TextColor::Muted)
|
||||
}))
|
||||
.child(
|
||||
Label::new(action_taken.taken_message.1.clone())
|
||||
.color(TextColor::Muted),
|
||||
),
|
||||
// Show the actions
|
||||
(Some(actions), None) => {
|
||||
h_stack().children(actions.map(|action| match action.button {
|
||||
ButtonOrIconButton::Button(button) => {
|
||||
button.render_into_any()
|
||||
}
|
||||
ButtonOrIconButton::IconButton(icon_button) => {
|
||||
icon_button.render_into_any()
|
||||
}
|
||||
}))
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Notification<V> {
|
||||
fn new(
|
||||
id: ElementId,
|
||||
@ -241,7 +324,7 @@ impl<V> Notification<V> {
|
||||
self
|
||||
}
|
||||
|
||||
fn render_meta_items(&self, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render_meta_items(&self, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
if let Some(meta) = &self.meta {
|
||||
h_stack().children(
|
||||
meta.items
|
||||
@ -260,87 +343,12 @@ impl<V> Notification<V> {
|
||||
}
|
||||
}
|
||||
|
||||
fn render_slot(&self, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render_slot(&self, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
match &self.slot {
|
||||
ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render(),
|
||||
ActorOrIcon::Icon(icon) => IconElement::new(icon.clone()).render(),
|
||||
ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render_into_any(),
|
||||
ActorOrIcon::Icon(icon) => IconElement::new(icon.clone()).render_into_any(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
div()
|
||||
.relative()
|
||||
.id(self.id.clone())
|
||||
.p_1()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_full()
|
||||
.children(
|
||||
Some(
|
||||
div()
|
||||
.absolute()
|
||||
.left(px(3.0))
|
||||
.top_3()
|
||||
.z_index(2)
|
||||
.child(UnreadIndicator::new()),
|
||||
)
|
||||
.filter(|_| self.unread),
|
||||
)
|
||||
.child(
|
||||
v_stack()
|
||||
.z_index(1)
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
h_stack()
|
||||
.w_full()
|
||||
.gap_2()
|
||||
.child(self.render_slot(cx))
|
||||
.child(div().flex_1().child(Label::new(self.message.clone()))),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Label::new(naive_format_distance_from_now(
|
||||
self.date_received,
|
||||
true,
|
||||
true,
|
||||
))
|
||||
.color(TextColor::Muted),
|
||||
)
|
||||
.child(self.render_meta_items(cx)),
|
||||
)
|
||||
.child(match (self.actions, self.action_taken) {
|
||||
// Show nothing
|
||||
(None, _) => div(),
|
||||
// Show the taken_message
|
||||
(Some(_), Some(action_taken)) => h_stack()
|
||||
.children(action_taken.taken_message.0.map(|icon| {
|
||||
IconElement::new(icon).color(crate::TextColor::Muted)
|
||||
}))
|
||||
.child(
|
||||
Label::new(action_taken.taken_message.1.clone())
|
||||
.color(TextColor::Muted),
|
||||
),
|
||||
// Show the actions
|
||||
(Some(actions), None) => {
|
||||
h_stack().children(actions.map(|action| match action.button {
|
||||
ButtonOrIconButton::Button(button) => {
|
||||
Component::render(button)
|
||||
}
|
||||
ButtonOrIconButton::IconButton(icon_button) => {
|
||||
Component::render(icon_button)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
use chrono::NaiveDateTime;
|
||||
@ -356,7 +364,7 @@ mod stories {
|
||||
|
||||
pub struct NotificationsPanelStory;
|
||||
|
||||
impl Render for NotificationsPanelStory {
|
||||
impl Render<Self> for NotificationsPanelStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,4 +1,7 @@
|
||||
use gpui::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size, View};
|
||||
use gpui::{
|
||||
hsla, red, AnyElement, Div, ElementId, ExternalPaths, Hsla, Length, RenderOnce, Size, Stateful,
|
||||
View,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
@ -10,7 +13,7 @@ pub enum SplitDirection {
|
||||
Vertical,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Pane<V: 'static> {
|
||||
id: ElementId,
|
||||
size: Size<Length>,
|
||||
@ -18,24 +21,10 @@ pub struct Pane<V: 'static> {
|
||||
children: SmallVec<[AnyElement<V>; 2]>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Pane<V> {
|
||||
pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
|
||||
// Fill is only here for debugging purposes, remove before release
|
||||
impl<V: 'static> Component<V> for Pane<V> {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
Self {
|
||||
id: id.into(),
|
||||
size,
|
||||
fill: hsla(0.3, 0.3, 0.3, 1.),
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill(mut self, fill: Hsla) -> Self {
|
||||
self.fill = fill;
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.flex()
|
||||
@ -59,37 +48,41 @@ impl<V: 'static> Pane<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentComponent<V> for Pane<V> {
|
||||
impl<V: 'static> Pane<V> {
|
||||
pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
|
||||
// Fill is only here for debugging purposes, remove before release
|
||||
|
||||
Self {
|
||||
id: id.into(),
|
||||
size,
|
||||
fill: hsla(0.3, 0.3, 0.3, 1.),
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill(mut self, fill: Hsla) -> Self {
|
||||
self.fill = fill;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentElement<V> for Pane<V> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct PaneGroup<V: 'static> {
|
||||
groups: Vec<PaneGroup<V>>,
|
||||
panes: Vec<Pane<V>>,
|
||||
split_direction: SplitDirection,
|
||||
}
|
||||
|
||||
impl<V: 'static> PaneGroup<V> {
|
||||
pub fn new_groups(groups: Vec<PaneGroup<V>>, split_direction: SplitDirection) -> Self {
|
||||
Self {
|
||||
groups,
|
||||
panes: Vec::new(),
|
||||
split_direction,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for PaneGroup<V> {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
pub fn new_panes(panes: Vec<Pane<V>>, split_direction: SplitDirection) -> Self {
|
||||
Self {
|
||||
groups: Vec::new(),
|
||||
panes,
|
||||
split_direction,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
if !self.panes.is_empty() {
|
||||
let el = div()
|
||||
.flex()
|
||||
@ -126,3 +119,21 @@ impl<V: 'static> PaneGroup<V> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> PaneGroup<V> {
|
||||
pub fn new_groups(groups: Vec<PaneGroup<V>>, split_direction: SplitDirection) -> Self {
|
||||
Self {
|
||||
groups,
|
||||
panes: Vec::new(),
|
||||
split_direction,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_panes(panes: Vec<Pane<V>>, split_direction: SplitDirection) -> Self {
|
||||
Self {
|
||||
groups: Vec::new(),
|
||||
panes,
|
||||
split_direction,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,18 +3,56 @@ use crate::{
|
||||
ListHeader,
|
||||
};
|
||||
use gpui::prelude::*;
|
||||
use gpui::Div;
|
||||
use gpui::Stateful;
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ProjectPanel {
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for ProjectPanel {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.flex()
|
||||
.flex_col()
|
||||
.size_full()
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.child(
|
||||
div()
|
||||
.id("project-panel-contents")
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.overflow_y_scroll()
|
||||
.child(
|
||||
List::new(static_project_panel_single_items())
|
||||
.header(ListHeader::new("FILES"))
|
||||
.empty_message("No files in directory"),
|
||||
)
|
||||
.child(
|
||||
List::new(static_project_panel_project_items())
|
||||
.header(ListHeader::new("PROJECT"))
|
||||
.empty_message("No folders in directory"),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Input::new("Find something...")
|
||||
.value("buffe".to_string())
|
||||
.state(InteractionState::Focused),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProjectPanel {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.flex()
|
||||
@ -59,7 +97,7 @@ mod stories {
|
||||
|
||||
pub struct ProjectPanelStory;
|
||||
|
||||
impl Render for ProjectPanelStory {
|
||||
impl Render<Self> for ProjectPanelStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,17 +1,15 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{OrderMethod, Palette, PaletteItem};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct RecentProjects {
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl RecentProjects {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
impl<V: 'static> Component<V> for RecentProjects {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div().id(self.id.clone()).child(
|
||||
Palette::new("palette")
|
||||
.items(vec![
|
||||
@ -29,6 +27,13 @@ impl RecentProjects {
|
||||
}
|
||||
}
|
||||
|
||||
impl RecentProjects {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce, Stateful};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -40,7 +45,7 @@ mod stories {
|
||||
|
||||
pub struct RecentProjectsStory;
|
||||
|
||||
impl Render for RecentProjectsStory {
|
||||
impl Render<Self> for RecentProjectsStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,5 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{Div, RenderOnce};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{Button, Icon, IconButton, TextColor, ToolDivider, Workspace};
|
||||
|
||||
@ -28,14 +30,31 @@ impl Default for ToolGroup {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[component(view_type = "Workspace")]
|
||||
#[derive(RenderOnce)]
|
||||
#[view = "Workspace"]
|
||||
pub struct StatusBar {
|
||||
left_tools: Option<ToolGroup>,
|
||||
right_tools: Option<ToolGroup>,
|
||||
bottom_tools: Option<ToolGroup>,
|
||||
}
|
||||
|
||||
impl Component<Workspace> for StatusBar {
|
||||
type Rendered = Div<Workspace>;
|
||||
|
||||
fn render(self, view: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Self::Rendered {
|
||||
div()
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.w_full()
|
||||
.bg(cx.theme().colors().status_bar_background)
|
||||
.child(self.left_tools(view, cx))
|
||||
.child(self.right_tools(view, cx))
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusBar {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@ -81,28 +100,7 @@ impl StatusBar {
|
||||
self
|
||||
}
|
||||
|
||||
fn render(
|
||||
self,
|
||||
view: &mut Workspace,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> impl Component<Workspace> {
|
||||
div()
|
||||
.py_0p5()
|
||||
.px_1()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.w_full()
|
||||
.bg(cx.theme().colors().status_bar_background)
|
||||
.child(self.left_tools(view, cx))
|
||||
.child(self.right_tools(view, cx))
|
||||
}
|
||||
|
||||
fn left_tools(
|
||||
&self,
|
||||
workspace: &mut Workspace,
|
||||
cx: &WindowContext,
|
||||
) -> impl Component<Workspace> {
|
||||
fn left_tools(&self, workspace: &mut Workspace, cx: &WindowContext) -> impl Element<Workspace> {
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
@ -133,7 +131,7 @@ impl StatusBar {
|
||||
&self,
|
||||
workspace: &mut Workspace,
|
||||
cx: &WindowContext,
|
||||
) -> impl Component<Workspace> {
|
||||
) -> impl Element<Workspace> {
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::{prelude::*, Icon, IconButton, Tab};
|
||||
use gpui::prelude::*;
|
||||
use gpui::Div;
|
||||
use gpui::Stateful;
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct TabBar {
|
||||
id: ElementId,
|
||||
/// Backwards, Forwards
|
||||
@ -9,21 +11,10 @@ pub struct TabBar {
|
||||
tabs: Vec<Tab>,
|
||||
}
|
||||
|
||||
impl TabBar {
|
||||
pub fn new(id: impl Into<ElementId>, tabs: Vec<Tab>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
can_navigate: (false, false),
|
||||
tabs,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for TabBar {
|
||||
type Rendered = Stateful<V, Div<V>>;
|
||||
|
||||
pub fn can_navigate(mut self, can_navigate: (bool, bool)) -> Self {
|
||||
self.can_navigate = can_navigate;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let (can_navigate_back, can_navigate_forward) = self.can_navigate;
|
||||
|
||||
div()
|
||||
@ -92,6 +83,21 @@ impl TabBar {
|
||||
}
|
||||
}
|
||||
|
||||
impl TabBar {
|
||||
pub fn new(id: impl Into<ElementId>, tabs: Vec<Tab>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
can_navigate: (false, false),
|
||||
tabs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_navigate(mut self, can_navigate: (bool, bool)) -> Self {
|
||||
self.can_navigate = can_navigate;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::ElementId;
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
@ -104,7 +110,7 @@ mod stories {
|
||||
|
||||
pub struct TabBarStory;
|
||||
|
||||
impl Render for TabBarStory {
|
||||
impl Render<Self> for TabBarStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,17 +1,84 @@
|
||||
use gpui::{relative, rems, Size};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{Icon, IconButton, Pane, Tab};
|
||||
use gpui::{relative, rems, Div, RenderOnce, Size};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Terminal;
|
||||
|
||||
impl<V: 'static> Component<V> for Terminal {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let can_navigate_back = true;
|
||||
let can_navigate_forward = false;
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.w_full()
|
||||
.child(
|
||||
// Terminal Tabs.
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.bg(cx.theme().colors().surface_background)
|
||||
.child(
|
||||
div().px_1().flex().flex_none().gap_2().child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_px()
|
||||
.child(
|
||||
IconButton::new("arrow_left", Icon::ArrowLeft).state(
|
||||
InteractionState::Enabled.if_enabled(can_navigate_back),
|
||||
),
|
||||
)
|
||||
.child(IconButton::new("arrow_right", Icon::ArrowRight).state(
|
||||
InteractionState::Enabled.if_enabled(can_navigate_forward),
|
||||
)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div().w_0().flex_1().h_full().child(
|
||||
div()
|
||||
.flex()
|
||||
.child(
|
||||
Tab::new(1)
|
||||
.title("zed — fish".to_string())
|
||||
.icon(Icon::Terminal)
|
||||
.close_side(IconSide::Right)
|
||||
.current(true),
|
||||
)
|
||||
.child(
|
||||
Tab::new(2)
|
||||
.title("zed — fish".to_string())
|
||||
.icon(Icon::Terminal)
|
||||
.close_side(IconSide::Right)
|
||||
.current(false),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
// Terminal Pane.
|
||||
.child(
|
||||
Pane::new(
|
||||
"terminal",
|
||||
Size {
|
||||
width: relative(1.).into(),
|
||||
height: rems(36.).into(),
|
||||
},
|
||||
)
|
||||
.child(crate::static_data::terminal_buffer(cx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Terminal {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let can_navigate_back = true;
|
||||
let can_navigate_forward = false;
|
||||
|
||||
@ -86,7 +153,7 @@ mod stories {
|
||||
use gpui::{Div, Render};
|
||||
pub struct TerminalStory;
|
||||
|
||||
impl Render for TerminalStory {
|
||||
impl Render<Self> for TerminalStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,17 +1,16 @@
|
||||
use crate::prelude::*;
|
||||
use crate::{OrderMethod, Palette, PaletteItem};
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ThemeSelector {
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
impl ThemeSelector {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
impl<V: 'static> Component<V> for ThemeSelector {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let cx: &mut ViewContext<V> = cx;
|
||||
div().child(
|
||||
Palette::new(self.id.clone())
|
||||
.items(vec![
|
||||
@ -34,6 +33,13 @@ impl ThemeSelector {
|
||||
}
|
||||
}
|
||||
|
||||
impl ThemeSelector {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self { id: id.into() }
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -47,7 +53,7 @@ mod stories {
|
||||
|
||||
pub struct ThemeSelectorStory;
|
||||
|
||||
impl Render for ThemeSelectorStory {
|
||||
impl Render<Self> for ThemeSelectorStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{Div, Render, View, VisualContext};
|
||||
use gpui::{Div, Render, RenderOnce, View, VisualContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::settings::user_settings;
|
||||
@ -85,7 +85,7 @@ impl TitleBar {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for TitleBar {
|
||||
impl Render<Self> for TitleBar {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
|
||||
@ -205,7 +205,7 @@ mod stories {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for TitleBarStory {
|
||||
impl Render<Self> for TitleBarStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use gpui::AnyElement;
|
||||
use gpui::{AnyElement, Div, RenderOnce};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
@ -6,12 +6,26 @@ use crate::prelude::*;
|
||||
#[derive(Clone)]
|
||||
pub struct ToolbarItem {}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
pub struct Toolbar<V: 'static> {
|
||||
left_items: SmallVec<[AnyElement<V>; 2]>,
|
||||
right_items: SmallVec<[AnyElement<V>; 2]>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Component<V> for Toolbar<V> {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.bg(cx.theme().colors().toolbar_background)
|
||||
.p_2()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.child(div().flex().children(self.left_items))
|
||||
.child(div().flex().children(self.right_items))
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Toolbar<V> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@ -20,49 +34,39 @@ impl<V: 'static> Toolbar<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_item(mut self, child: impl Component<V>) -> Self
|
||||
pub fn left_item(mut self, child: impl RenderOnce<V>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.left_items.push(child.render());
|
||||
self.left_items.push(child.render_into_any());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_items(mut self, iter: impl IntoIterator<Item = impl Component<V>>) -> Self
|
||||
pub fn left_items(mut self, iter: impl IntoIterator<Item = impl RenderOnce<V>>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.left_items
|
||||
.extend(iter.into_iter().map(|item| item.render()));
|
||||
.extend(iter.into_iter().map(|item| item.render_into_any()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn right_item(mut self, child: impl Component<V>) -> Self
|
||||
pub fn right_item(mut self, child: impl RenderOnce<V>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.right_items.push(child.render());
|
||||
self.right_items.push(child.render_into_any());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn right_items(mut self, iter: impl IntoIterator<Item = impl Component<V>>) -> Self
|
||||
pub fn right_items(mut self, iter: impl IntoIterator<Item = impl RenderOnce<V>>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.right_items
|
||||
.extend(iter.into_iter().map(|item| item.render()));
|
||||
.extend(iter.into_iter().map(|item| item.render_into_any()));
|
||||
self
|
||||
}
|
||||
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
div()
|
||||
.bg(cx.theme().colors().toolbar_background)
|
||||
.p_2()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.child(div().flex().children(self.left_items))
|
||||
.child(div().flex().children(self.right_items))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
@ -81,7 +85,7 @@ mod stories {
|
||||
|
||||
pub struct ToolbarStory;
|
||||
|
||||
impl Render for ToolbarStory {
|
||||
impl Render<Self> for ToolbarStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -95,21 +99,21 @@ mod stories {
|
||||
vec![
|
||||
Symbol(vec![
|
||||
HighlightedText {
|
||||
text: "impl ".to_string(),
|
||||
text: "impl ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "ToolbarStory".to_string(),
|
||||
text: "ToolbarStory".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
]),
|
||||
Symbol(vec![
|
||||
HighlightedText {
|
||||
text: "fn ".to_string(),
|
||||
text: "fn ".into(),
|
||||
color: cx.theme().syntax_color("keyword"),
|
||||
},
|
||||
HighlightedText {
|
||||
text: "render".to_string(),
|
||||
text: "render".into(),
|
||||
color: cx.theme().syntax_color("function"),
|
||||
},
|
||||
]),
|
||||
|
@ -7,21 +7,16 @@ enum TrafficLightColor {
|
||||
Green,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
#[derive(RenderOnce)]
|
||||
struct TrafficLight {
|
||||
color: TrafficLightColor,
|
||||
window_has_focus: bool,
|
||||
}
|
||||
|
||||
impl TrafficLight {
|
||||
fn new(color: TrafficLightColor, window_has_focus: bool) -> Self {
|
||||
Self {
|
||||
color,
|
||||
window_has_focus,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for TrafficLight {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
let system_colors = &cx.theme().styles.system;
|
||||
|
||||
let fill = match (self.window_has_focus, self.color) {
|
||||
@ -35,24 +30,24 @@ impl TrafficLight {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
impl TrafficLight {
|
||||
fn new(color: TrafficLightColor, window_has_focus: bool) -> Self {
|
||||
Self {
|
||||
color,
|
||||
window_has_focus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct TrafficLights {
|
||||
window_has_focus: bool,
|
||||
}
|
||||
|
||||
impl TrafficLights {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
window_has_focus: true,
|
||||
}
|
||||
}
|
||||
impl<V: 'static> Component<V> for TrafficLights {
|
||||
type Rendered = Div<V>;
|
||||
|
||||
pub fn window_has_focus(mut self, window_has_focus: bool) -> Self {
|
||||
self.window_has_focus = window_has_focus;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
@ -72,6 +67,20 @@ impl TrafficLights {
|
||||
}
|
||||
}
|
||||
|
||||
impl TrafficLights {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
window_has_focus: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_has_focus(mut self, window_has_focus: bool) -> Self {
|
||||
self.window_has_focus = window_has_focus;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::{Div, RenderOnce};
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
@ -85,7 +94,7 @@ mod stories {
|
||||
|
||||
pub struct TrafficLightsStory;
|
||||
|
||||
impl Render for TrafficLightsStory {
|
||||
impl Render<Self> for TrafficLightsStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::DateTime;
|
||||
use gpui::{px, relative, Div, Render, Size, View, VisualContext};
|
||||
use gpui::{px, relative, Div, Render, RenderOnce, Size, View, VisualContext};
|
||||
use settings2::Settings;
|
||||
use theme2::ThemeSettings;
|
||||
|
||||
@ -191,7 +191,7 @@ impl Workspace {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Workspace {
|
||||
impl Render<Self> for Workspace {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
|
||||
@ -388,7 +388,7 @@ mod stories {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for WorkspaceStory {
|
||||
impl Render<Self> for WorkspaceStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{status_bar::StatusItemView, Axis, Workspace};
|
||||
use gpui::{
|
||||
div, px, Action, AnchorCorner, AnyView, AppContext, Component, Div, Entity, EntityId,
|
||||
EventEmitter, FocusHandle, FocusableView, ParentComponent, Render, SharedString, Styled,
|
||||
div, px, Action, AnchorCorner, AnyView, AppContext, Div, Entity, EntityId, EventEmitter,
|
||||
FocusHandle, FocusableView, ParentElement, Render, RenderOnce, SharedString, Styled,
|
||||
Subscription, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
@ -476,7 +476,7 @@ impl Dock {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Dock {
|
||||
impl Render<Self> for Dock {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -662,7 +662,7 @@ impl PanelButtons {
|
||||
// }
|
||||
|
||||
// here be kittens
|
||||
impl Render for PanelButtons {
|
||||
impl Render<Self> for PanelButtons {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
@ -705,8 +705,7 @@ impl Render for PanelButtons {
|
||||
};
|
||||
|
||||
Some(
|
||||
menu_handle()
|
||||
.id(name)
|
||||
menu_handle(name)
|
||||
.menu(move |_, cx| {
|
||||
const POSITIONS: [DockPosition; 3] = [
|
||||
DockPosition::Left,
|
||||
@ -781,7 +780,7 @@ pub mod test {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for TestPanel {
|
||||
impl Render<Self> for TestPanel {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -71,7 +71,7 @@ impl ModalLayer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ModalLayer {
|
||||
impl Render<Self> for ModalLayer {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
@ -13,9 +13,9 @@ pub enum NotificationEvent {
|
||||
Dismiss,
|
||||
}
|
||||
|
||||
pub trait Notification: EventEmitter<NotificationEvent> + Render {}
|
||||
pub trait Notification: EventEmitter<NotificationEvent> + Render<Self> {}
|
||||
|
||||
impl<V: EventEmitter<NotificationEvent> + Render> Notification for V {}
|
||||
impl<V: EventEmitter<NotificationEvent> + Render<Self>> Notification for V {}
|
||||
|
||||
pub trait NotificationHandle: Send {
|
||||
fn id(&self) -> EntityId;
|
||||
@ -253,7 +253,7 @@ pub mod simple_message_notification {
|
||||
// }
|
||||
}
|
||||
|
||||
impl Render for MessageNotification {
|
||||
impl Render<Self> for MessageNotification {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user