Merge branch 'main' into callback-handles
This commit is contained in:
commit
d0dd44faad
117 changed files with 3170 additions and 2027 deletions
|
@ -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,7 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use gpui::{
|
||||
CallbackHandle, DefiniteLength, Hsla, MouseButton, StatefulInteractiveComponent, WindowContext,
|
||||
DefiniteLength, DefiniteLength, Div, Hsla, MouseButton, RenderOnce, Stateful,
|
||||
StatefulInteractiveElement, WindowContext,
|
||||
};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
@ -63,7 +64,6 @@ impl ButtonVariant {
|
|||
}
|
||||
}
|
||||
|
||||
// #[derive(Component)] <- todo
|
||||
pub struct Button {
|
||||
disabled: bool,
|
||||
click_handler: Option<CallbackHandle<()>>,
|
||||
|
@ -75,6 +75,57 @@ pub struct Button {
|
|||
color: Option<TextColor>,
|
||||
}
|
||||
|
||||
impl RenderOnce for Button {
|
||||
type Element = Stateful<Div>;
|
||||
|
||||
fn render(self) -> Self::Rendered {
|
||||
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 Button {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
|
@ -150,72 +201,30 @@ impl Button {
|
|||
fn render_icon(&self, icon_color: TextColor) -> Option<IconElement> {
|
||||
self.icon.map(|i| IconElement::new(i).color(icon_color))
|
||||
}
|
||||
|
||||
pub fn render(self, cx: &mut WindowContext) -> impl Component {
|
||||
let (icon_color, label_color) = match (self.disabled, self.color) {
|
||||
(true, _) => (TextColor::Disabled, TextColor::Disabled),
|
||||
(_, None) => (TextColor::Default, TextColor::Default),
|
||||
(_, Some(color)) => (TextColor::from(color), color),
|
||||
};
|
||||
|
||||
let mut button = h_stack()
|
||||
.id(SharedString::from(format!("{}", self.label)))
|
||||
.relative()
|
||||
.p_1()
|
||||
.text_ui()
|
||||
.rounded_md()
|
||||
.bg(self.variant.bg_color(cx))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.bg(self.variant.bg_color_hover(cx)))
|
||||
.active(|style| style.bg(self.variant.bg_color_active(cx)));
|
||||
|
||||
match (self.icon, self.icon_position) {
|
||||
(Some(_), Some(IconPosition::Left)) => {
|
||||
button = button
|
||||
.gap_1()
|
||||
.child(self.render_label(label_color))
|
||||
.children(self.render_icon(icon_color))
|
||||
}
|
||||
(Some(_), Some(IconPosition::Right)) => {
|
||||
button = button
|
||||
.gap_1()
|
||||
.children(self.render_icon(icon_color))
|
||||
.child(self.render_label(label_color))
|
||||
}
|
||||
(_, _) => button = button.child(self.render_label(label_color)),
|
||||
}
|
||||
|
||||
if let Some(width) = self.width {
|
||||
button = button.w(width).justify_center();
|
||||
}
|
||||
|
||||
if let Some(click_handler) = self.handlers.click.clone() {
|
||||
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
|
||||
click_handler(state, cx);
|
||||
});
|
||||
}
|
||||
|
||||
button
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RenderOnce)]
|
||||
pub struct ButtonGroup {
|
||||
buttons: Vec<Button>,
|
||||
}
|
||||
|
||||
impl ButtonGroup {
|
||||
pub fn new(buttons: Vec<Button>) -> Self {
|
||||
Self { buttons }
|
||||
}
|
||||
impl Component for ButtonGroup {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> impl Component {
|
||||
let mut el = h_stack().text_ui();
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let mut group = h_stack();
|
||||
|
||||
for button in self.buttons {
|
||||
el = el.child(button.render(cx));
|
||||
for button in self.buttons.into_iter() {
|
||||
group = group.child(button.render(view, cx));
|
||||
}
|
||||
|
||||
el
|
||||
group
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ButtonGroup<V> {
|
||||
pub fn new(buttons: Vec<Button<V>>) -> Self {
|
||||
Self { buttons }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue