mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 12:38:02 +00:00
e946b0a2ec
Document core traits Add start for a component storybook
237 lines
6.8 KiB
Rust
237 lines
6.8 KiB
Rust
use button_component::Button;
|
|
|
|
use gpui::{
|
|
color::Color,
|
|
elements::{ContainerStyle, Flex, Label, ParentElement, StatefulComponent},
|
|
fonts::{self, TextStyle},
|
|
platform::WindowOptions,
|
|
AnyElement, App, Element, Entity, View, ViewContext,
|
|
};
|
|
use log::LevelFilter;
|
|
use pathfinder_geometry::vector::vec2f;
|
|
use simplelog::SimpleLogger;
|
|
use theme::Toggleable;
|
|
use toggleable_button::ToggleableButton;
|
|
|
|
// cargo run -p gpui --example components
|
|
|
|
fn main() {
|
|
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
|
|
|
App::new(()).unwrap().run(|cx| {
|
|
cx.platform().activate(true);
|
|
cx.add_window(WindowOptions::with_bounds(vec2f(300., 200.)), |_| {
|
|
TestView {
|
|
count: 0,
|
|
is_doubling: false,
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
pub struct TestView {
|
|
count: usize,
|
|
is_doubling: bool,
|
|
}
|
|
|
|
impl TestView {
|
|
fn increase_count(&mut self) {
|
|
if self.is_doubling {
|
|
self.count *= 2;
|
|
} else {
|
|
self.count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Entity for TestView {
|
|
type Event = ();
|
|
}
|
|
|
|
type ButtonStyle = ContainerStyle;
|
|
|
|
impl View for TestView {
|
|
fn ui_name() -> &'static str {
|
|
"TestView"
|
|
}
|
|
|
|
fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> AnyElement<Self> {
|
|
fonts::with_font_cache(cx.font_cache.to_owned(), || {
|
|
Flex::column()
|
|
.with_child(Label::new(
|
|
format!("Count: {}", self.count),
|
|
TextStyle::for_color(Color::red()),
|
|
))
|
|
.with_child(
|
|
Button::new(move |_, v: &mut Self, cx| {
|
|
v.increase_count();
|
|
cx.notify();
|
|
})
|
|
.with_text(
|
|
"Hello from a counting BUTTON",
|
|
TextStyle::for_color(Color::blue()),
|
|
)
|
|
.with_style(ButtonStyle::fill(Color::yellow()))
|
|
.element(),
|
|
)
|
|
.with_child(
|
|
ToggleableButton::new(self.is_doubling, move |_, v: &mut Self, cx| {
|
|
v.is_doubling = !v.is_doubling;
|
|
cx.notify();
|
|
})
|
|
.with_text("Double the count?", TextStyle::for_color(Color::black()))
|
|
.with_style(Toggleable {
|
|
inactive: ButtonStyle::fill(Color::red()),
|
|
active: ButtonStyle::fill(Color::green()),
|
|
})
|
|
.element(),
|
|
)
|
|
.expanded()
|
|
.contained()
|
|
.with_background_color(Color::white())
|
|
.into_any()
|
|
})
|
|
}
|
|
}
|
|
|
|
mod theme {
|
|
pub struct Toggleable<T> {
|
|
pub inactive: T,
|
|
pub active: T,
|
|
}
|
|
|
|
impl<T> Toggleable<T> {
|
|
pub fn style_for(&self, active: bool) -> &T {
|
|
if active {
|
|
&self.active
|
|
} else {
|
|
&self.inactive
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Component creation:
|
|
mod toggleable_button {
|
|
use gpui::{
|
|
elements::{ContainerStyle, LabelStyle, StatefulComponent},
|
|
scene::MouseClick,
|
|
EventContext, View,
|
|
};
|
|
|
|
use crate::{button_component::Button, theme::Toggleable};
|
|
|
|
pub struct ToggleableButton<V: View> {
|
|
active: bool,
|
|
style: Option<Toggleable<ContainerStyle>>,
|
|
button: Button<V>,
|
|
}
|
|
|
|
impl<V: View> ToggleableButton<V> {
|
|
pub fn new<F>(active: bool, on_click: F) -> Self
|
|
where
|
|
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
|
|
{
|
|
Self {
|
|
active,
|
|
button: Button::new(on_click),
|
|
style: None,
|
|
}
|
|
}
|
|
|
|
pub fn with_text(self, text: &str, style: impl Into<LabelStyle>) -> ToggleableButton<V> {
|
|
ToggleableButton {
|
|
active: self.active,
|
|
style: self.style,
|
|
button: self.button.with_text(text, style),
|
|
}
|
|
}
|
|
|
|
pub fn with_style(self, style: Toggleable<ContainerStyle>) -> ToggleableButton<V> {
|
|
ToggleableButton {
|
|
active: self.active,
|
|
style: Some(style),
|
|
button: self.button,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<V: View> StatefulComponent<V> for ToggleableButton<V> {
|
|
fn render(self, v: &mut V, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
|
|
let button = if let Some(style) = self.style {
|
|
self.button.with_style(*style.style_for(self.active))
|
|
} else {
|
|
self.button
|
|
};
|
|
button.render(v, cx)
|
|
}
|
|
}
|
|
}
|
|
|
|
mod button_component {
|
|
|
|
use gpui::{
|
|
elements::{ContainerStyle, Label, LabelStyle, MouseEventHandler, StatefulComponent},
|
|
platform::MouseButton,
|
|
scene::MouseClick,
|
|
AnyElement, Element, EventContext, TypeTag, View, ViewContext,
|
|
};
|
|
|
|
type ClickHandler<V> = Box<dyn Fn(MouseClick, &mut V, &mut EventContext<V>)>;
|
|
|
|
pub struct Button<V: View> {
|
|
click_handler: ClickHandler<V>,
|
|
tag: TypeTag,
|
|
contents: Option<AnyElement<V>>,
|
|
style: Option<ContainerStyle>,
|
|
}
|
|
|
|
impl<V: View> Button<V> {
|
|
pub fn new<F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static>(handler: F) -> Self {
|
|
Self {
|
|
click_handler: Box::new(handler),
|
|
tag: TypeTag::new::<F>(),
|
|
style: None,
|
|
contents: None,
|
|
}
|
|
}
|
|
|
|
pub fn with_text(mut self, text: &str, style: impl Into<LabelStyle>) -> Self {
|
|
self.contents = Some(Label::new(text.to_string(), style).into_any());
|
|
self
|
|
}
|
|
|
|
pub fn _with_contents<E: Element<V>>(mut self, contents: E) -> Self {
|
|
self.contents = Some(contents.into_any());
|
|
self
|
|
}
|
|
|
|
pub fn with_style(mut self, style: ContainerStyle) -> Self {
|
|
self.style = Some(style);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<V: View> StatefulComponent<V> for Button<V> {
|
|
fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
|
|
let click_handler = self.click_handler;
|
|
|
|
let result = MouseEventHandler::new_dynamic(self.tag, 0, cx, |_, _| {
|
|
self.contents
|
|
.unwrap_or_else(|| gpui::elements::Empty::new().into_any())
|
|
})
|
|
.on_click(MouseButton::Left, move |click, v, cx| {
|
|
click_handler(click, v, cx);
|
|
})
|
|
.contained();
|
|
|
|
let result = if let Some(style) = self.style {
|
|
result.with_style(style)
|
|
} else {
|
|
result
|
|
};
|
|
|
|
result.into_any()
|
|
}
|
|
}
|
|
}
|