Split playground into gpui2 and storybook
This commit is contained in:
parent
ee11be98e5
commit
3b5ee59273
41 changed files with 175 additions and 2210 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -5194,26 +5194,6 @@ version = "0.3.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "playground"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"derive_more",
|
|
||||||
"gpui",
|
|
||||||
"gpui2_macros",
|
|
||||||
"log",
|
|
||||||
"parking_lot 0.11.2",
|
|
||||||
"refineable",
|
|
||||||
"rust-embed",
|
|
||||||
"serde",
|
|
||||||
"settings",
|
|
||||||
"simplelog",
|
|
||||||
"smallvec",
|
|
||||||
"theme",
|
|
||||||
"util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plist"
|
name = "plist"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -7320,6 +7300,21 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "storybook"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"gpui2",
|
||||||
|
"log",
|
||||||
|
"rust-embed",
|
||||||
|
"serde",
|
||||||
|
"settings",
|
||||||
|
"simplelog",
|
||||||
|
"theme",
|
||||||
|
"util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stringprep"
|
name = "stringprep"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
|
|
@ -32,7 +32,6 @@ members = [
|
||||||
"crates/git",
|
"crates/git",
|
||||||
"crates/go_to_line",
|
"crates/go_to_line",
|
||||||
"crates/gpui",
|
"crates/gpui",
|
||||||
"crates/gpui/playground",
|
|
||||||
"crates/gpui_macros",
|
"crates/gpui_macros",
|
||||||
"crates/gpui2",
|
"crates/gpui2",
|
||||||
"crates/gpui2_macros",
|
"crates/gpui2_macros",
|
||||||
|
@ -64,6 +63,7 @@ members = [
|
||||||
"crates/sqlez",
|
"crates/sqlez",
|
||||||
"crates/sqlez_macros",
|
"crates/sqlez_macros",
|
||||||
"crates/feature_flags",
|
"crates/feature_flags",
|
||||||
|
"crates/storybook",
|
||||||
"crates/sum_tree",
|
"crates/sum_tree",
|
||||||
"crates/terminal",
|
"crates/terminal",
|
||||||
"crates/text",
|
"crates/text",
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "playground"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "playground"
|
|
||||||
path = "src/playground.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow.workspace = true
|
|
||||||
derive_more.workspace = true
|
|
||||||
gpui = { path = ".." }
|
|
||||||
log.workspace = true
|
|
||||||
gpui2_macros = { path = "../../gpui2_macros" }
|
|
||||||
parking_lot.workspace = true
|
|
||||||
refineable.workspace = true
|
|
||||||
rust-embed.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
settings = { path = "../../settings" }
|
|
||||||
simplelog = "0.9"
|
|
||||||
smallvec.workspace = true
|
|
||||||
theme = { path = "../../theme" }
|
|
||||||
util = { path = "../../util" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
gpui = { path = "..", features = ["test-support"] }
|
|
|
@ -1,78 +0,0 @@
|
||||||
use crate::{layout_context::LayoutContext, paint_context::PaintContext};
|
|
||||||
use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId};
|
|
||||||
use util::ResultExt;
|
|
||||||
|
|
||||||
/// Makes a new, playground-style element into a legacy element.
|
|
||||||
pub struct AdapterElement<V>(pub(crate) crate::element::AnyElement<V>);
|
|
||||||
|
|
||||||
impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
|
|
||||||
type LayoutState = Option<(LayoutEngine, LayoutId)>;
|
|
||||||
type PaintState = ();
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
constraint: gpui::SizeConstraint,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut gpui::LayoutContext<V>,
|
|
||||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
|
||||||
cx.push_layout_engine(LayoutEngine::new());
|
|
||||||
|
|
||||||
let size = constraint.max;
|
|
||||||
let mut cx = LayoutContext::new(cx);
|
|
||||||
let layout_id = self.0.layout(view, &mut cx).log_err();
|
|
||||||
if let Some(layout_id) = layout_id {
|
|
||||||
cx.layout_engine()
|
|
||||||
.unwrap()
|
|
||||||
.compute_layout(layout_id, constraint.max)
|
|
||||||
.log_err();
|
|
||||||
}
|
|
||||||
|
|
||||||
let layout_engine = cx.pop_layout_engine();
|
|
||||||
debug_assert!(layout_engine.is_some(),
|
|
||||||
"unexpected layout stack state. is there an unmatched pop_layout_engine in the called code?"
|
|
||||||
);
|
|
||||||
|
|
||||||
(constraint.max, layout_engine.zip(layout_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
scene: &mut gpui::SceneBuilder,
|
|
||||||
bounds: RectF,
|
|
||||||
visible_bounds: RectF,
|
|
||||||
layout_data: &mut Option<(LayoutEngine, LayoutId)>,
|
|
||||||
view: &mut V,
|
|
||||||
legacy_cx: &mut gpui::PaintContext<V>,
|
|
||||||
) -> Self::PaintState {
|
|
||||||
let (layout_engine, layout_id) = layout_data.take().unwrap();
|
|
||||||
legacy_cx.push_layout_engine(layout_engine);
|
|
||||||
let mut cx = PaintContext::new(legacy_cx, scene);
|
|
||||||
self.0.paint(view, bounds.origin(), &mut cx);
|
|
||||||
*layout_data = legacy_cx.pop_layout_engine().zip(Some(layout_id));
|
|
||||||
debug_assert!(layout_data.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rect_for_text_range(
|
|
||||||
&self,
|
|
||||||
range_utf16: std::ops::Range<usize>,
|
|
||||||
bounds: RectF,
|
|
||||||
visible_bounds: RectF,
|
|
||||||
layout: &Self::LayoutState,
|
|
||||||
paint: &Self::PaintState,
|
|
||||||
view: &V,
|
|
||||||
cx: &gpui::ViewContext<V>,
|
|
||||||
) -> Option<RectF> {
|
|
||||||
todo!("implement before merging to main")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug(
|
|
||||||
&self,
|
|
||||||
bounds: RectF,
|
|
||||||
layout: &Self::LayoutState,
|
|
||||||
paint: &Self::PaintState,
|
|
||||||
view: &V,
|
|
||||||
cx: &gpui::ViewContext<V>,
|
|
||||||
) -> gpui::serde_json::Value {
|
|
||||||
todo!("implement before merging to main")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
use crate::{
|
|
||||||
div::div,
|
|
||||||
element::{IntoElement, ParentElement},
|
|
||||||
interactive::Interactive,
|
|
||||||
style::StyleHelpers,
|
|
||||||
text::ArcCow,
|
|
||||||
// themes::Theme,
|
|
||||||
};
|
|
||||||
use gpui::{platform::MouseButton, ViewContext};
|
|
||||||
use gpui2_macros::Element;
|
|
||||||
use std::{marker::PhantomData, rc::Rc};
|
|
||||||
|
|
||||||
struct ButtonHandlers<V, D> {
|
|
||||||
click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V, D> Default for ButtonHandlers<V, D> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { click: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use crate as gpui2;
|
|
||||||
#[derive(Element)]
|
|
||||||
pub struct Button<V: 'static, D: 'static> {
|
|
||||||
handlers: ButtonHandlers<V, D>,
|
|
||||||
label: Option<ArcCow<'static, str>>,
|
|
||||||
icon: Option<ArcCow<'static, str>>,
|
|
||||||
data: Rc<D>,
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Impl block for buttons without data.
|
|
||||||
// See below for an impl block for any button.
|
|
||||||
impl<V: 'static> Button<V, ()> {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
handlers: ButtonHandlers::default(),
|
|
||||||
label: None,
|
|
||||||
icon: None,
|
|
||||||
data: Rc::new(()),
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
|
|
||||||
Button {
|
|
||||||
handlers: ButtonHandlers::default(),
|
|
||||||
label: self.label,
|
|
||||||
icon: self.icon,
|
|
||||||
data: Rc::new(data),
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Impl block for button regardless of its data type.
|
|
||||||
impl<V: 'static, D: 'static> Button<V, D> {
|
|
||||||
pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
|
|
||||||
self.label = Some(label.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
|
|
||||||
self.icon = Some(icon.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_click(mut self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
|
|
||||||
self.handlers.click = Some(Rc::new(handler));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn button<V>() -> Button<V, ()> {
|
|
||||||
Button::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, D: 'static> Button<V, D> {
|
|
||||||
fn render(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) -> impl IntoElement<V> + Interactive<V> {
|
|
||||||
// let colors = &cx.theme::<Theme>().colors;
|
|
||||||
|
|
||||||
let button = div()
|
|
||||||
// .fill(colors.error(0.5))
|
|
||||||
.h_4()
|
|
||||||
.children(self.label.clone());
|
|
||||||
|
|
||||||
if let Some(handler) = self.handlers.click.clone() {
|
|
||||||
let data = self.data.clone();
|
|
||||||
button.on_mouse_down(MouseButton::Left, move |view, event, cx| {
|
|
||||||
handler(view, data.as_ref(), cx)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
button
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
use crate::{
|
|
||||||
element::{AnyElement, Element, IntoElement, Layout, ParentElement},
|
|
||||||
interactive::{InteractionHandlers, Interactive},
|
|
||||||
layout_context::LayoutContext,
|
|
||||||
paint_context::PaintContext,
|
|
||||||
style::{Style, StyleHelpers, Styleable},
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
|
||||||
use gpui::{LayoutId, RenderContext};
|
|
||||||
use refineable::{Refineable, RefinementCascade};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
pub struct Div<V: 'static> {
|
|
||||||
styles: RefinementCascade<Style>,
|
|
||||||
handlers: InteractionHandlers<V>,
|
|
||||||
children: SmallVec<[AnyElement<V>; 2]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn div<V>() -> Div<V> {
|
|
||||||
Div {
|
|
||||||
styles: Default::default(),
|
|
||||||
handlers: Default::default(),
|
|
||||||
children: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Element<V> for Div<V> {
|
|
||||||
type PaintState = ();
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<(LayoutId, Self::PaintState)>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let style = self.computed_style();
|
|
||||||
let pop_text_style = style.text_style().map_or(false, |style| {
|
|
||||||
cx.push_text_style(cx.text_style().clone().refined(&style));
|
|
||||||
true
|
|
||||||
});
|
|
||||||
|
|
||||||
let children = self
|
|
||||||
.children
|
|
||||||
.iter_mut()
|
|
||||||
.map(|child| child.layout(view, cx))
|
|
||||||
.collect::<Result<Vec<LayoutId>>>()?;
|
|
||||||
|
|
||||||
if pop_text_style {
|
|
||||||
cx.pop_text_style();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((cx.add_layout_node(style, children)?, ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
layout: &Layout,
|
|
||||||
paint_state: &mut Self::PaintState,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let style = &self.computed_style();
|
|
||||||
let pop_text_style = style.text_style().map_or(false, |style| {
|
|
||||||
let style = cx.text_style().clone().refined(&style);
|
|
||||||
cx.push_text_style(style);
|
|
||||||
true
|
|
||||||
});
|
|
||||||
style.paint_background(layout.bounds, cx);
|
|
||||||
self.interaction_handlers()
|
|
||||||
.paint(layout.order, layout.bounds, cx);
|
|
||||||
for child in &mut self.children {
|
|
||||||
child.paint(view, layout.bounds.origin(), cx);
|
|
||||||
}
|
|
||||||
if pop_text_style {
|
|
||||||
cx.pop_text_style();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Styleable for Div<V> {
|
|
||||||
type Style = Style;
|
|
||||||
|
|
||||||
fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
|
|
||||||
&mut self.styles
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
|
|
||||||
self.styles.base()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> StyleHelpers for Div<V> {}
|
|
||||||
|
|
||||||
impl<V> Interactive<V> for Div<V> {
|
|
||||||
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
|
|
||||||
&mut self.handlers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> ParentElement<V> for Div<V> {
|
|
||||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
|
||||||
&mut self.children
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> IntoElement<V> for Div<V> {
|
|
||||||
type Element = Self;
|
|
||||||
|
|
||||||
fn into_element(self) -> Self::Element {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,170 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
pub use crate::layout_context::LayoutContext;
|
|
||||||
pub use crate::paint_context::PaintContext;
|
|
||||||
use crate::themes::{Theme, Themed};
|
|
||||||
use anyhow::Result;
|
|
||||||
use gpui::geometry::vector::Vector2F;
|
|
||||||
pub use gpui::{Layout, LayoutId};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
pub trait Element<V: 'static>: 'static {
|
|
||||||
type PaintState;
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<(LayoutId, Self::PaintState)>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
layout: &Layout,
|
|
||||||
state: &mut Self::PaintState,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
fn into_any(self) -> AnyElement<V>
|
|
||||||
where
|
|
||||||
Self: 'static + Sized,
|
|
||||||
{
|
|
||||||
AnyElement(Box::new(StatefulElement {
|
|
||||||
element: self,
|
|
||||||
phase: ElementPhase::Init,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn themed(self, theme: Theme) -> Themed<V, Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
crate::themes::Themed {
|
|
||||||
child: self,
|
|
||||||
theme,
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
|
|
||||||
trait AnyStatefulElement<V> {
|
|
||||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
|
|
||||||
fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper around an element that stores its layout state.
|
|
||||||
struct StatefulElement<V: 'static, E: Element<V>> {
|
|
||||||
element: E,
|
|
||||||
phase: ElementPhase<V, E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ElementPhase<V: 'static, E: Element<V>> {
|
|
||||||
Init,
|
|
||||||
PostLayout {
|
|
||||||
layout_id: LayoutId,
|
|
||||||
paint_state: E::PaintState,
|
|
||||||
},
|
|
||||||
PostPaint {
|
|
||||||
layout: Layout,
|
|
||||||
paint_state: E::PaintState,
|
|
||||||
},
|
|
||||||
Error(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V>> Default for ElementPhase<V, E> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Init
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
|
|
||||||
impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
|
|
||||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
|
|
||||||
let result;
|
|
||||||
self.phase = match self.element.layout(view, cx) {
|
|
||||||
Ok((layout_id, paint_state)) => {
|
|
||||||
result = Ok(layout_id);
|
|
||||||
ElementPhase::PostLayout {
|
|
||||||
layout_id,
|
|
||||||
paint_state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
let message = error.to_string();
|
|
||||||
result = Err(error);
|
|
||||||
ElementPhase::Error(message)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>) {
|
|
||||||
self.phase = match std::mem::take(&mut self.phase) {
|
|
||||||
ElementPhase::PostLayout {
|
|
||||||
layout_id,
|
|
||||||
mut paint_state,
|
|
||||||
} => match cx.computed_layout(layout_id) {
|
|
||||||
Ok(mut layout) => {
|
|
||||||
layout.bounds = layout.bounds + parent_origin;
|
|
||||||
self.element.paint(view, &layout, &mut paint_state, cx);
|
|
||||||
ElementPhase::PostPaint {
|
|
||||||
layout,
|
|
||||||
paint_state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => ElementPhase::Error(error.to_string()),
|
|
||||||
},
|
|
||||||
phase @ ElementPhase::Error(_) => phase,
|
|
||||||
_ => panic!("invalid element phase to call paint"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A dynamic element.
|
|
||||||
pub struct AnyElement<V>(Box<dyn AnyStatefulElement<V>>);
|
|
||||||
|
|
||||||
impl<V> AnyElement<V> {
|
|
||||||
pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
|
|
||||||
self.0.layout(view, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn paint(&mut self, view: &mut V, parent_origin: Vector2F, cx: &mut PaintContext<V>) {
|
|
||||||
self.0.paint(view, parent_origin, cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ParentElement<V: 'static> {
|
|
||||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
|
|
||||||
|
|
||||||
fn child(mut self, child: impl IntoElement<V>) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.children_mut().push(child.into_element().into_any());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn children<I, E>(mut self, children: I) -> Self
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = E>,
|
|
||||||
E: IntoElement<V>,
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.children_mut().extend(
|
|
||||||
children
|
|
||||||
.into_iter()
|
|
||||||
.map(|child| child.into_element().into_any()),
|
|
||||||
);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait IntoElement<V: 'static> {
|
|
||||||
type Element: Element<V>;
|
|
||||||
|
|
||||||
fn into_element(self) -> Self::Element;
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
use crate::{
|
|
||||||
element::{AnyElement, Element, IntoElement, Layout, ParentElement},
|
|
||||||
interactive::{InteractionHandlers, Interactive},
|
|
||||||
layout_context::LayoutContext,
|
|
||||||
paint_context::PaintContext,
|
|
||||||
style::{Style, StyleHelpers, Styleable},
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
|
||||||
use gpui::{platform::MouseMovedEvent, LayoutId};
|
|
||||||
use refineable::{CascadeSlot, Refineable, RefinementCascade};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::{cell::Cell, rc::Rc};
|
|
||||||
|
|
||||||
pub struct Hoverable<E: Styleable> {
|
|
||||||
hovered: Rc<Cell<bool>>,
|
|
||||||
cascade_slot: CascadeSlot,
|
|
||||||
hovered_style: <E::Style as Refineable>::Refinement,
|
|
||||||
child: E,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hoverable<E: Styleable>(mut child: E) -> Hoverable<E> {
|
|
||||||
Hoverable {
|
|
||||||
hovered: Rc::new(Cell::new(false)),
|
|
||||||
cascade_slot: child.style_cascade().reserve(),
|
|
||||||
hovered_style: Default::default(),
|
|
||||||
child,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Styleable> Styleable for Hoverable<E> {
|
|
||||||
type Style = E::Style;
|
|
||||||
|
|
||||||
fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
|
|
||||||
self.child.style_cascade()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
|
|
||||||
&mut self.hovered_style
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
|
|
||||||
type PaintState = E::PaintState;
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<(LayoutId, Self::PaintState)>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Ok(self.child.layout(view, cx)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
layout: &Layout,
|
|
||||||
paint_state: &mut Self::PaintState,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.hovered
|
|
||||||
.set(layout.bounds.contains_point(cx.mouse_position()));
|
|
||||||
|
|
||||||
let slot = self.cascade_slot;
|
|
||||||
let style = self.hovered.get().then_some(self.hovered_style.clone());
|
|
||||||
self.style_cascade().set(slot, style);
|
|
||||||
|
|
||||||
let hovered = self.hovered.clone();
|
|
||||||
let bounds = layout.bounds;
|
|
||||||
cx.on_event(layout.order, move |view, event: &MouseMovedEvent, cx| {
|
|
||||||
if bounds.contains_point(cx.mouse_position()) != hovered.get() {
|
|
||||||
cx.repaint();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.child.paint(view, layout, paint_state, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Styleable<Style = Style>> StyleHelpers for Hoverable<E> {}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Interactive<V> + Styleable> Interactive<V> for Hoverable<E> {
|
|
||||||
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
|
|
||||||
self.child.interaction_handlers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: ParentElement<V> + Styleable> ParentElement<V> for Hoverable<E> {
|
|
||||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
|
||||||
self.child.children_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V> + Styleable> IntoElement<V> for Hoverable<E> {
|
|
||||||
type Element = Self;
|
|
||||||
|
|
||||||
fn into_element(self) -> Self::Element {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
use gpui::{
|
|
||||||
geometry::rect::RectF,
|
|
||||||
platform::{MouseButton, MouseButtonEvent},
|
|
||||||
EventContext,
|
|
||||||
};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::{cell::Cell, rc::Rc};
|
|
||||||
|
|
||||||
use crate::element::PaintContext;
|
|
||||||
|
|
||||||
pub trait Interactive<V: 'static> {
|
|
||||||
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
|
|
||||||
|
|
||||||
fn on_mouse_down(
|
|
||||||
mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.interaction_handlers()
|
|
||||||
.mouse_down
|
|
||||||
.push(Rc::new(handler));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_mouse_up(
|
|
||||||
mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.interaction_handlers().mouse_up.push(Rc::new(handler));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_mouse_down_out(
|
|
||||||
mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.interaction_handlers()
|
|
||||||
.mouse_down_out
|
|
||||||
.push(Rc::new(handler));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_mouse_up_out(
|
|
||||||
mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.interaction_handlers()
|
|
||||||
.mouse_up_out
|
|
||||||
.push(Rc::new(handler));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_click(
|
|
||||||
self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let pressed = Rc::new(Cell::new(false));
|
|
||||||
self.on_mouse_down(button, {
|
|
||||||
let pressed = pressed.clone();
|
|
||||||
move |_, _, _| {
|
|
||||||
pressed.set(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on_mouse_up_out(button, {
|
|
||||||
let pressed = pressed.clone();
|
|
||||||
move |_, _, _| {
|
|
||||||
pressed.set(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on_mouse_up(button, move |view, event, cx| {
|
|
||||||
if pressed.get() {
|
|
||||||
pressed.set(false);
|
|
||||||
handler(view, event, cx);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InteractionHandlers<V: 'static> {
|
|
||||||
mouse_down: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
|
|
||||||
mouse_down_out: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
|
|
||||||
mouse_up: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
|
|
||||||
mouse_up_out: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> InteractionHandlers<V> {
|
|
||||||
pub fn paint(&self, order: u32, bounds: RectF, cx: &mut PaintContext<V>) {
|
|
||||||
for handler in self.mouse_down.iter().cloned() {
|
|
||||||
cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
|
|
||||||
if event.is_down && bounds.contains_point(event.position) {
|
|
||||||
handler(view, event, cx);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for handler in self.mouse_up.iter().cloned() {
|
|
||||||
cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
|
|
||||||
if !event.is_down && bounds.contains_point(event.position) {
|
|
||||||
handler(view, event, cx);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for handler in self.mouse_down_out.iter().cloned() {
|
|
||||||
cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
|
|
||||||
if event.is_down && !bounds.contains_point(event.position) {
|
|
||||||
handler(view, event, cx);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for handler in self.mouse_up_out.iter().cloned() {
|
|
||||||
cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
|
|
||||||
if !event.is_down && !bounds.contains_point(event.position) {
|
|
||||||
handler(view, event, cx);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Default for InteractionHandlers<V> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
mouse_down: Default::default(),
|
|
||||||
mouse_up: Default::default(),
|
|
||||||
mouse_down_out: Default::default(),
|
|
||||||
mouse_up_out: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
use crate::{element::LayoutId, style::Style};
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use derive_more::{Deref, DerefMut};
|
|
||||||
use gpui::{geometry::Size, MeasureParams, RenderContext, ViewContext};
|
|
||||||
pub use gpui::{taffy::tree::NodeId, LayoutContext as LegacyLayoutContext};
|
|
||||||
|
|
||||||
#[derive(Deref, DerefMut)]
|
|
||||||
pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
|
|
||||||
#[deref]
|
|
||||||
#[deref_mut]
|
|
||||||
pub(crate) legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> {
|
|
||||||
fn text_style(&self) -> gpui::fonts::TextStyle {
|
|
||||||
self.legacy_cx.text_style()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
|
|
||||||
self.legacy_cx.push_text_style(style)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_text_style(&mut self) {
|
|
||||||
self.legacy_cx.pop_text_style()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
|
|
||||||
&mut self.view_context
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
|
|
||||||
pub fn new(legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>) -> Self {
|
|
||||||
Self { legacy_cx }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_layout_node(
|
|
||||||
&mut self,
|
|
||||||
style: Style,
|
|
||||||
children: impl IntoIterator<Item = NodeId>,
|
|
||||||
) -> Result<LayoutId> {
|
|
||||||
let rem_size = self.rem_pixels();
|
|
||||||
let id = self
|
|
||||||
.legacy_cx
|
|
||||||
.layout_engine()
|
|
||||||
.ok_or_else(|| anyhow!("no layout engine"))?
|
|
||||||
.add_node(style.to_taffy(rem_size), children)?;
|
|
||||||
|
|
||||||
Ok(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_measured_layout_node<F>(&mut self, style: Style, measure: F) -> Result<LayoutId>
|
|
||||||
where
|
|
||||||
F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
|
|
||||||
{
|
|
||||||
let rem_size = self.rem_pixels();
|
|
||||||
let layout_id = self
|
|
||||||
.layout_engine()
|
|
||||||
.ok_or_else(|| anyhow!("no layout engine"))?
|
|
||||||
.add_measured_node(style.to_taffy(rem_size), measure)?;
|
|
||||||
|
|
||||||
Ok(layout_id)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use derive_more::{Deref, DerefMut};
|
|
||||||
pub use gpui::taffy::tree::NodeId;
|
|
||||||
use gpui::{
|
|
||||||
scene::EventHandler, EventContext, Layout, LayoutId, PaintContext as LegacyPaintContext,
|
|
||||||
};
|
|
||||||
use std::{any::TypeId, rc::Rc};
|
|
||||||
|
|
||||||
#[derive(Deref, DerefMut)]
|
|
||||||
pub struct PaintContext<'a, 'b, 'c, 'd, V> {
|
|
||||||
#[deref]
|
|
||||||
#[deref_mut]
|
|
||||||
pub(crate) legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
|
|
||||||
pub(crate) scene: &'d mut gpui::SceneBuilder,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
|
|
||||||
pub fn new(
|
|
||||||
legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
|
|
||||||
scene: &'d mut gpui::SceneBuilder,
|
|
||||||
) -> Self {
|
|
||||||
Self { legacy_cx, scene }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_event<E: 'static>(
|
|
||||||
&mut self,
|
|
||||||
order: u32,
|
|
||||||
handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
|
|
||||||
) {
|
|
||||||
let view = self.weak_handle();
|
|
||||||
|
|
||||||
self.scene.event_handlers.push(EventHandler {
|
|
||||||
order,
|
|
||||||
handler: Rc::new(move |event, window_cx| {
|
|
||||||
if let Some(view) = view.upgrade(window_cx) {
|
|
||||||
view.update(window_cx, |view, view_cx| {
|
|
||||||
let mut event_cx = EventContext::new(view_cx);
|
|
||||||
handler(view, event.downcast_ref().unwrap(), &mut event_cx);
|
|
||||||
event_cx.bubble
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
event_type: TypeId::of::<E>(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
|
|
||||||
self.layout_engine()
|
|
||||||
.ok_or_else(|| anyhow!("no layout engine present"))?
|
|
||||||
.computed_layout(layout_id)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
#![allow(dead_code, unused_variables)]
|
|
||||||
use crate::element::Element;
|
|
||||||
use gpui::{
|
|
||||||
geometry::{rect::RectF, vector::vec2f},
|
|
||||||
platform::WindowOptions,
|
|
||||||
serde_json, ViewContext,
|
|
||||||
};
|
|
||||||
use log::LevelFilter;
|
|
||||||
use settings::{default_settings, SettingsStore};
|
|
||||||
use simplelog::SimpleLogger;
|
|
||||||
use theme::ThemeSettings;
|
|
||||||
use themes::Theme;
|
|
||||||
use view::view;
|
|
||||||
use workspace::workspace;
|
|
||||||
|
|
||||||
mod adapter;
|
|
||||||
mod color;
|
|
||||||
mod components;
|
|
||||||
mod div;
|
|
||||||
mod element;
|
|
||||||
mod hoverable;
|
|
||||||
mod interactive;
|
|
||||||
mod layout_context;
|
|
||||||
mod paint_context;
|
|
||||||
mod pressable;
|
|
||||||
mod style;
|
|
||||||
mod text;
|
|
||||||
mod themes;
|
|
||||||
mod view;
|
|
||||||
mod workspace;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
|
||||||
|
|
||||||
gpui::App::new(()).unwrap().run(|cx| {
|
|
||||||
let mut store = SettingsStore::default();
|
|
||||||
store
|
|
||||||
.set_default_settings(default_settings().as_ref(), cx)
|
|
||||||
.unwrap();
|
|
||||||
cx.set_global(store);
|
|
||||||
theme::init(Assets, cx);
|
|
||||||
|
|
||||||
cx.add_window(
|
|
||||||
WindowOptions {
|
|
||||||
bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
|
|
||||||
vec2f(0., 0.),
|
|
||||||
vec2f(400., 300.),
|
|
||||||
)),
|
|
||||||
center: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
|_| view(|cx| playground(cx)),
|
|
||||||
);
|
|
||||||
cx.platform().activate(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn playground<V: 'static>(cx: &mut ViewContext<V>) -> impl Element<V> {
|
|
||||||
workspace().themed(current_theme(cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nathan: During the transition, we will include the base theme on the legacy Theme struct.
|
|
||||||
fn current_theme<V: 'static>(cx: &mut ViewContext<V>) -> Theme {
|
|
||||||
settings::get::<ThemeSettings>(cx)
|
|
||||||
.theme
|
|
||||||
.deserialized_base_theme
|
|
||||||
.lock()
|
|
||||||
.get_or_insert_with(|| {
|
|
||||||
let theme: Theme =
|
|
||||||
serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
|
|
||||||
.unwrap();
|
|
||||||
Box::new(theme)
|
|
||||||
})
|
|
||||||
.downcast_ref::<Theme>()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use gpui::AssetSource;
|
|
||||||
use rust_embed::RustEmbed;
|
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
|
||||||
#[folder = "../../../assets"]
|
|
||||||
#[include = "themes/**/*"]
|
|
||||||
#[exclude = "*.DS_Store"]
|
|
||||||
pub struct Assets;
|
|
||||||
|
|
||||||
impl AssetSource for Assets {
|
|
||||||
fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
|
|
||||||
Self::get(path)
|
|
||||||
.map(|f| f.data)
|
|
||||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn list(&self, path: &str) -> Vec<std::borrow::Cow<'static, str>> {
|
|
||||||
Self::iter().filter(|p| p.starts_with(path)).collect()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,379 +0,0 @@
|
||||||
use crate::{
|
|
||||||
color::Hsla,
|
|
||||||
hoverable::{hoverable, Hoverable},
|
|
||||||
paint_context::PaintContext,
|
|
||||||
pressable::{pressable, Pressable},
|
|
||||||
};
|
|
||||||
pub use gpui::taffy::style::{
|
|
||||||
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
|
|
||||||
Overflow, Position,
|
|
||||||
};
|
|
||||||
use gpui::{
|
|
||||||
fonts::TextStyleRefinement,
|
|
||||||
geometry::{
|
|
||||||
rect::RectF, relative, AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length,
|
|
||||||
Point, PointRefinement, Size, SizeRefinement,
|
|
||||||
},
|
|
||||||
taffy,
|
|
||||||
};
|
|
||||||
use gpui2_macros::styleable_helpers;
|
|
||||||
use refineable::{Refineable, RefinementCascade};
|
|
||||||
|
|
||||||
#[derive(Clone, Refineable)]
|
|
||||||
pub struct Style {
|
|
||||||
/// What layout strategy should be used?
|
|
||||||
pub display: Display,
|
|
||||||
|
|
||||||
// Overflow properties
|
|
||||||
/// How children overflowing their container should affect layout
|
|
||||||
#[refineable]
|
|
||||||
pub overflow: Point<Overflow>,
|
|
||||||
/// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
|
|
||||||
pub scrollbar_width: f32,
|
|
||||||
|
|
||||||
// Position properties
|
|
||||||
/// What should the `position` value of this struct use as a base offset?
|
|
||||||
pub position: Position,
|
|
||||||
/// How should the position of this element be tweaked relative to the layout defined?
|
|
||||||
#[refineable]
|
|
||||||
pub inset: Edges<Length>,
|
|
||||||
|
|
||||||
// Size properies
|
|
||||||
/// Sets the initial size of the item
|
|
||||||
#[refineable]
|
|
||||||
pub size: Size<Length>,
|
|
||||||
/// Controls the minimum size of the item
|
|
||||||
#[refineable]
|
|
||||||
pub min_size: Size<Length>,
|
|
||||||
/// Controls the maximum size of the item
|
|
||||||
#[refineable]
|
|
||||||
pub max_size: Size<Length>,
|
|
||||||
/// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.
|
|
||||||
pub aspect_ratio: Option<f32>,
|
|
||||||
|
|
||||||
// Spacing Properties
|
|
||||||
/// How large should the margin be on each side?
|
|
||||||
#[refineable]
|
|
||||||
pub margin: Edges<Length>,
|
|
||||||
/// How large should the padding be on each side?
|
|
||||||
#[refineable]
|
|
||||||
pub padding: Edges<DefiniteLength>,
|
|
||||||
/// How large should the border be on each side?
|
|
||||||
#[refineable]
|
|
||||||
pub border: Edges<DefiniteLength>,
|
|
||||||
|
|
||||||
// Alignment properties
|
|
||||||
/// How this node's children aligned in the cross/block axis?
|
|
||||||
pub align_items: Option<AlignItems>,
|
|
||||||
/// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set
|
|
||||||
pub align_self: Option<AlignSelf>,
|
|
||||||
/// How should content contained within this item be aligned in the cross/block axis
|
|
||||||
pub align_content: Option<AlignContent>,
|
|
||||||
/// How should contained within this item be aligned in the main/inline axis
|
|
||||||
pub justify_content: Option<JustifyContent>,
|
|
||||||
/// How large should the gaps between items in a flex container be?
|
|
||||||
#[refineable]
|
|
||||||
pub gap: Size<DefiniteLength>,
|
|
||||||
|
|
||||||
// Flexbox properies
|
|
||||||
/// Which direction does the main axis flow in?
|
|
||||||
pub flex_direction: FlexDirection,
|
|
||||||
/// Should elements wrap, or stay in a single line?
|
|
||||||
pub flex_wrap: FlexWrap,
|
|
||||||
/// Sets the initial main axis size of the item
|
|
||||||
pub flex_basis: Length,
|
|
||||||
/// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.
|
|
||||||
pub flex_grow: f32,
|
|
||||||
/// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
|
|
||||||
pub flex_shrink: f32,
|
|
||||||
|
|
||||||
/// The fill color of this element
|
|
||||||
pub fill: Option<Fill>,
|
|
||||||
/// The radius of the corners of this element
|
|
||||||
#[refineable]
|
|
||||||
pub corner_radii: CornerRadii,
|
|
||||||
/// The color of text within this element. Cascades to children unless overridden.
|
|
||||||
pub text_color: Option<Hsla>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Style {
|
|
||||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
|
|
||||||
taffy::style::Style {
|
|
||||||
display: self.display,
|
|
||||||
overflow: self.overflow.clone().into(),
|
|
||||||
scrollbar_width: self.scrollbar_width,
|
|
||||||
position: self.position,
|
|
||||||
inset: self.inset.to_taffy(rem_size),
|
|
||||||
size: self.size.to_taffy(rem_size),
|
|
||||||
min_size: self.min_size.to_taffy(rem_size),
|
|
||||||
max_size: self.max_size.to_taffy(rem_size),
|
|
||||||
aspect_ratio: self.aspect_ratio,
|
|
||||||
margin: self.margin.to_taffy(rem_size),
|
|
||||||
padding: self.padding.to_taffy(rem_size),
|
|
||||||
border: self.border.to_taffy(rem_size),
|
|
||||||
align_items: self.align_items,
|
|
||||||
align_self: self.align_self,
|
|
||||||
align_content: self.align_content,
|
|
||||||
justify_content: self.justify_content,
|
|
||||||
gap: self.gap.to_taffy(rem_size),
|
|
||||||
flex_direction: self.flex_direction,
|
|
||||||
flex_wrap: self.flex_wrap,
|
|
||||||
flex_basis: self.flex_basis.to_taffy(rem_size).into(),
|
|
||||||
flex_grow: self.flex_grow,
|
|
||||||
flex_shrink: self.flex_shrink,
|
|
||||||
..Default::default() // Ignore grid properties for now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Paints the background of an element styled with this style.
|
|
||||||
/// Return the bounds in which to paint the content.
|
|
||||||
pub fn paint_background<V: 'static>(&self, bounds: RectF, cx: &mut PaintContext<V>) {
|
|
||||||
let rem_size = cx.rem_pixels();
|
|
||||||
if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
|
|
||||||
cx.scene.push_quad(gpui::Quad {
|
|
||||||
bounds,
|
|
||||||
background: Some(color.into()),
|
|
||||||
corner_radii: self.corner_radii.to_gpui(rem_size),
|
|
||||||
border: Default::default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn text_style(&self) -> Option<TextStyleRefinement> {
|
|
||||||
if let Some(color) = self.text_color {
|
|
||||||
Some(TextStyleRefinement {
|
|
||||||
color: Some(color.into()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Style {
|
|
||||||
fn default() -> Self {
|
|
||||||
Style {
|
|
||||||
display: Display::Block,
|
|
||||||
overflow: Point {
|
|
||||||
x: Overflow::Visible,
|
|
||||||
y: Overflow::Visible,
|
|
||||||
},
|
|
||||||
scrollbar_width: 0.0,
|
|
||||||
position: Position::Relative,
|
|
||||||
inset: Edges::auto(),
|
|
||||||
margin: Edges::<Length>::zero(),
|
|
||||||
padding: Edges::<DefiniteLength>::zero(),
|
|
||||||
border: Edges::<DefiniteLength>::zero(),
|
|
||||||
size: Size::auto(),
|
|
||||||
min_size: Size::auto(),
|
|
||||||
max_size: Size::auto(),
|
|
||||||
aspect_ratio: None,
|
|
||||||
gap: Size::zero(),
|
|
||||||
// Aligment
|
|
||||||
align_items: None,
|
|
||||||
align_self: None,
|
|
||||||
align_content: None,
|
|
||||||
justify_content: None,
|
|
||||||
// Flexbox
|
|
||||||
flex_direction: FlexDirection::Row,
|
|
||||||
flex_wrap: FlexWrap::NoWrap,
|
|
||||||
flex_grow: 0.0,
|
|
||||||
flex_shrink: 1.0,
|
|
||||||
flex_basis: Length::Auto,
|
|
||||||
fill: None,
|
|
||||||
text_color: None,
|
|
||||||
corner_radii: CornerRadii::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StyleRefinement {
|
|
||||||
pub fn text_style(&self) -> Option<TextStyleRefinement> {
|
|
||||||
self.text_color.map(|color| TextStyleRefinement {
|
|
||||||
color: Some(color.into()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OptionalTextStyle {
|
|
||||||
color: Option<Hsla>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OptionalTextStyle {
|
|
||||||
pub fn apply(&self, style: &mut gpui::fonts::TextStyle) {
|
|
||||||
if let Some(color) = self.color {
|
|
||||||
style.color = color.into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Fill {
|
|
||||||
Color(Hsla),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fill {
|
|
||||||
pub fn color(&self) -> Option<Hsla> {
|
|
||||||
match self {
|
|
||||||
Fill::Color(color) => Some(*color),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Fill {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Color(Hsla::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Hsla> for Fill {
|
|
||||||
fn from(color: Hsla) -> Self {
|
|
||||||
Self::Color(color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Refineable, Default)]
|
|
||||||
pub struct CornerRadii {
|
|
||||||
top_left: AbsoluteLength,
|
|
||||||
top_right: AbsoluteLength,
|
|
||||||
bottom_left: AbsoluteLength,
|
|
||||||
bottom_right: AbsoluteLength,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CornerRadii {
|
|
||||||
pub fn to_gpui(&self, rem_size: f32) -> gpui::scene::CornerRadii {
|
|
||||||
gpui::scene::CornerRadii {
|
|
||||||
top_left: self.top_left.to_pixels(rem_size),
|
|
||||||
top_right: self.top_right.to_pixels(rem_size),
|
|
||||||
bottom_left: self.bottom_left.to_pixels(rem_size),
|
|
||||||
bottom_right: self.bottom_right.to_pixels(rem_size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Styleable {
|
|
||||||
type Style: Refineable + Default;
|
|
||||||
|
|
||||||
fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style>;
|
|
||||||
fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement;
|
|
||||||
|
|
||||||
fn computed_style(&mut self) -> Self::Style {
|
|
||||||
Self::Style::from_refinement(&self.style_cascade().merged())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hovered(self) -> Hoverable<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
hoverable(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pressed(self) -> Pressable<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
pressable(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
|
|
||||||
// fn p_2(mut self) -> Self where Self: Sized;
|
|
||||||
pub trait StyleHelpers: Styleable<Style = Style> {
|
|
||||||
styleable_helpers!();
|
|
||||||
|
|
||||||
fn h(mut self, height: Length) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().size.height = Some(height);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn full(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().size.width = Some(relative(1.));
|
|
||||||
self.declared_style().size.height = Some(relative(1.));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn relative(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().position = Some(Position::Relative);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn absolute(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().position = Some(Position::Absolute);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().display = Some(Display::Block);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flex(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().display = Some(Display::Flex);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flex_col(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().flex_direction = Some(FlexDirection::Column);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flex_row(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().flex_direction = Some(FlexDirection::Row);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flex_grow(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().flex_grow = Some(1.);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill<F>(mut self, fill: F) -> Self
|
|
||||||
where
|
|
||||||
F: Into<Fill>,
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().fill = Some(fill.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text_color<C>(mut self, color: C) -> Self
|
|
||||||
where
|
|
||||||
C: Into<Hsla>,
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().text_color = Some(color.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
use crate::{
|
|
||||||
color::Hsla,
|
|
||||||
element::{Element, PaintContext},
|
|
||||||
layout_context::LayoutContext,
|
|
||||||
};
|
|
||||||
use gpui::WindowContext;
|
|
||||||
use serde::{de::Visitor, Deserialize, Deserializer};
|
|
||||||
use std::{collections::HashMap, fmt, marker::PhantomData};
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct Theme {
|
|
||||||
pub name: String,
|
|
||||||
pub is_light: bool,
|
|
||||||
pub lowest: Layer,
|
|
||||||
pub middle: Layer,
|
|
||||||
pub highest: Layer,
|
|
||||||
pub popover_shadow: Shadow,
|
|
||||||
pub modal_shadow: Shadow,
|
|
||||||
#[serde(deserialize_with = "deserialize_player_colors")]
|
|
||||||
pub players: Vec<PlayerColors>,
|
|
||||||
#[serde(deserialize_with = "deserialize_syntax_colors")]
|
|
||||||
pub syntax: HashMap<String, Hsla>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct Layer {
|
|
||||||
pub base: StyleSet,
|
|
||||||
pub variant: StyleSet,
|
|
||||||
pub on: StyleSet,
|
|
||||||
pub accent: StyleSet,
|
|
||||||
pub positive: StyleSet,
|
|
||||||
pub warning: StyleSet,
|
|
||||||
pub negative: StyleSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct StyleSet {
|
|
||||||
#[serde(rename = "default")]
|
|
||||||
pub default: ContainerColors,
|
|
||||||
pub hovered: ContainerColors,
|
|
||||||
pub pressed: ContainerColors,
|
|
||||||
pub active: ContainerColors,
|
|
||||||
pub disabled: ContainerColors,
|
|
||||||
pub inverted: ContainerColors,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct ContainerColors {
|
|
||||||
pub background: Hsla,
|
|
||||||
pub foreground: Hsla,
|
|
||||||
pub border: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct PlayerColors {
|
|
||||||
pub selection: Hsla,
|
|
||||||
pub cursor: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
|
||||||
pub struct Shadow {
|
|
||||||
pub blur: u8,
|
|
||||||
pub color: Hsla,
|
|
||||||
pub offset: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
|
|
||||||
cx.theme::<Theme>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_player_colors<'de, D>(deserializer: D) -> Result<Vec<PlayerColors>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct PlayerArrayVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for PlayerArrayVisitor {
|
|
||||||
type Value = Vec<PlayerColors>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("an object with integer keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_map<A: serde::de::MapAccess<'de>>(
|
|
||||||
self,
|
|
||||||
mut map: A,
|
|
||||||
) -> Result<Self::Value, A::Error> {
|
|
||||||
let mut players = Vec::with_capacity(8);
|
|
||||||
while let Some((key, value)) = map.next_entry::<usize, PlayerColors>()? {
|
|
||||||
if key < 8 {
|
|
||||||
players.push(value);
|
|
||||||
} else {
|
|
||||||
return Err(serde::de::Error::invalid_value(
|
|
||||||
serde::de::Unexpected::Unsigned(key as u64),
|
|
||||||
&"a key in range 0..7",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(players)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_map(PlayerArrayVisitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result<HashMap<String, Hsla>, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ColorWrapper {
|
|
||||||
color: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SyntaxVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for SyntaxVisitor {
|
|
||||||
type Value = HashMap<String, Hsla>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a map with keys and objects with a single color field as values")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_map<M>(self, mut map: M) -> Result<HashMap<String, Hsla>, M::Error>
|
|
||||||
where
|
|
||||||
M: serde::de::MapAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
while let Some(key) = map.next_key()? {
|
|
||||||
let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla
|
|
||||||
result.insert(key, wrapper.color);
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deserializer.deserialize_map(SyntaxVisitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Themed<V: 'static, E> {
|
|
||||||
pub(crate) theme: Theme,
|
|
||||||
pub(crate) child: E,
|
|
||||||
pub(crate) view_type: PhantomData<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V>> Element<V> for Themed<V, E> {
|
|
||||||
type PaintState = E::PaintState;
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> anyhow::Result<(gpui::LayoutId, Self::PaintState)>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
cx.push_theme(self.theme.clone());
|
|
||||||
let result = self.child.layout(view, cx);
|
|
||||||
cx.pop_theme();
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
layout: &gpui::Layout,
|
|
||||||
state: &mut Self::PaintState,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
cx.push_theme(self.theme.clone());
|
|
||||||
self.child.paint(view, layout, state, cx);
|
|
||||||
cx.pop_theme();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
color::{hsla, rgb, Hsla},
|
|
||||||
ThemeColors,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct RosePineThemes {
|
|
||||||
pub default: RosePinePalette,
|
|
||||||
pub dawn: RosePinePalette,
|
|
||||||
pub moon: RosePinePalette,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct RosePinePalette {
|
|
||||||
pub base: Hsla,
|
|
||||||
pub surface: Hsla,
|
|
||||||
pub overlay: Hsla,
|
|
||||||
pub muted: Hsla,
|
|
||||||
pub subtle: Hsla,
|
|
||||||
pub text: Hsla,
|
|
||||||
pub love: Hsla,
|
|
||||||
pub gold: Hsla,
|
|
||||||
pub rose: Hsla,
|
|
||||||
pub pine: Hsla,
|
|
||||||
pub foam: Hsla,
|
|
||||||
pub iris: Hsla,
|
|
||||||
pub highlight_low: Hsla,
|
|
||||||
pub highlight_med: Hsla,
|
|
||||||
pub highlight_high: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RosePinePalette {
|
|
||||||
pub fn default() -> RosePinePalette {
|
|
||||||
RosePinePalette {
|
|
||||||
base: rgb(0x191724),
|
|
||||||
surface: rgb(0x1f1d2e),
|
|
||||||
overlay: rgb(0x26233a),
|
|
||||||
muted: rgb(0x6e6a86),
|
|
||||||
subtle: rgb(0x908caa),
|
|
||||||
text: rgb(0xe0def4),
|
|
||||||
love: rgb(0xeb6f92),
|
|
||||||
gold: rgb(0xf6c177),
|
|
||||||
rose: rgb(0xebbcba),
|
|
||||||
pine: rgb(0x31748f),
|
|
||||||
foam: rgb(0x9ccfd8),
|
|
||||||
iris: rgb(0xc4a7e7),
|
|
||||||
highlight_low: rgb(0x21202e),
|
|
||||||
highlight_med: rgb(0x403d52),
|
|
||||||
highlight_high: rgb(0x524f67),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn moon() -> RosePinePalette {
|
|
||||||
RosePinePalette {
|
|
||||||
base: rgb(0x232136),
|
|
||||||
surface: rgb(0x2a273f),
|
|
||||||
overlay: rgb(0x393552),
|
|
||||||
muted: rgb(0x6e6a86),
|
|
||||||
subtle: rgb(0x908caa),
|
|
||||||
text: rgb(0xe0def4),
|
|
||||||
love: rgb(0xeb6f92),
|
|
||||||
gold: rgb(0xf6c177),
|
|
||||||
rose: rgb(0xea9a97),
|
|
||||||
pine: rgb(0x3e8fb0),
|
|
||||||
foam: rgb(0x9ccfd8),
|
|
||||||
iris: rgb(0xc4a7e7),
|
|
||||||
highlight_low: rgb(0x2a283e),
|
|
||||||
highlight_med: rgb(0x44415a),
|
|
||||||
highlight_high: rgb(0x56526e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dawn() -> RosePinePalette {
|
|
||||||
RosePinePalette {
|
|
||||||
base: rgb(0xfaf4ed),
|
|
||||||
surface: rgb(0xfffaf3),
|
|
||||||
overlay: rgb(0xf2e9e1),
|
|
||||||
muted: rgb(0x9893a5),
|
|
||||||
subtle: rgb(0x797593),
|
|
||||||
text: rgb(0x575279),
|
|
||||||
love: rgb(0xb4637a),
|
|
||||||
gold: rgb(0xea9d34),
|
|
||||||
rose: rgb(0xd7827e),
|
|
||||||
pine: rgb(0x286983),
|
|
||||||
foam: rgb(0x56949f),
|
|
||||||
iris: rgb(0x907aa9),
|
|
||||||
highlight_low: rgb(0xf4ede8),
|
|
||||||
highlight_med: rgb(0xdfdad9),
|
|
||||||
highlight_high: rgb(0xcecacd),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default() -> ThemeColors {
|
|
||||||
theme_colors(&RosePinePalette::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn moon() -> ThemeColors {
|
|
||||||
theme_colors(&RosePinePalette::moon())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dawn() -> ThemeColors {
|
|
||||||
theme_colors(&RosePinePalette::dawn())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn theme_colors(p: &RosePinePalette) -> ThemeColors {
|
|
||||||
ThemeColors {
|
|
||||||
base: scale_sl(p.base, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
surface: scale_sl(p.surface, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
overlay: scale_sl(p.overlay, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
muted: scale_sl(p.muted, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
subtle: scale_sl(p.subtle, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
text: scale_sl(p.text, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
highlight_low: scale_sl(p.highlight_low, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
highlight_med: scale_sl(p.highlight_med, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
highlight_high: scale_sl(p.highlight_high, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
success: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
warning: scale_sl(p.gold, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
error: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
inserted: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
deleted: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
modified: scale_sl(p.rose, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produces a range by multiplying the saturation and lightness of the base color by the given
|
|
||||||
/// start and end factors.
|
|
||||||
fn scale_sl(base: Hsla, (start_s, start_l): (f32, f32), (end_s, end_l): (f32, f32)) -> Range<Hsla> {
|
|
||||||
let start = hsla(base.h, base.s * start_s, base.l * start_l, base.a);
|
|
||||||
let end = hsla(base.h, base.s * end_s, base.l * end_l, base.a);
|
|
||||||
Range { start, end }
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
use crate::{
|
|
||||||
adapter::AdapterElement,
|
|
||||||
element::{AnyElement, Element},
|
|
||||||
};
|
|
||||||
use gpui::ViewContext;
|
|
||||||
|
|
||||||
pub fn view<F, E>(mut render: F) -> ViewFn
|
|
||||||
where
|
|
||||||
F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
|
|
||||||
E: Element<ViewFn>,
|
|
||||||
{
|
|
||||||
ViewFn(Box::new(move |cx| (render)(cx).into_any()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
|
|
||||||
|
|
||||||
impl gpui::Entity for ViewFn {
|
|
||||||
type Event = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl gpui::View for ViewFn {
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
|
|
||||||
use gpui::Element as _;
|
|
||||||
AdapterElement((self.0)(cx)).into_any()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3459,7 +3459,7 @@ pub trait RenderContext<'a, 'b, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LayoutContext<'a, 'b, 'c, V> {
|
pub struct LayoutContext<'a, 'b, 'c, V> {
|
||||||
// Nathan: Making this is public while I work on playground.
|
// Nathan: Making this is public while I work on gpui2.
|
||||||
pub view_context: &'c mut ViewContext<'a, 'b, V>,
|
pub view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||||
pub refreshing: bool,
|
pub refreshing: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct SceneBuilder {
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
stacking_contexts: Vec<StackingContext>,
|
stacking_contexts: Vec<StackingContext>,
|
||||||
active_stacking_context_stack: Vec<usize>,
|
active_stacking_context_stack: Vec<usize>,
|
||||||
/// Used by the playground crate.
|
/// Used by the gpui2 crate.
|
||||||
pub event_handlers: Vec<EventHandler>,
|
pub event_handlers: Vec<EventHandler>,
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
mouse_region_ids: HashSet<MouseRegionId>,
|
mouse_region_ids: HashSet<MouseRegionId>,
|
||||||
|
|
|
@ -8,6 +8,9 @@ publish = false
|
||||||
name = "gpui2"
|
name = "gpui2"
|
||||||
path = "src/gpui2.rs"
|
path = "src/gpui2.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test-support = ["gpui/test-support"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
derive_more.workspace = true
|
derive_more.workspace = true
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{layout_context::LayoutContext, paint_context::PaintContext};
|
||||||
use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId};
|
use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
/// Makes a new, playground-style element into a legacy element.
|
/// Makes a new, gpui2-style element into a legacy element.
|
||||||
pub struct AdapterElement<V>(pub(crate) crate::element::AnyElement<V>);
|
pub struct AdapterElement<V>(pub(crate) crate::element::AnyElement<V>);
|
||||||
|
|
||||||
impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
|
impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
|
||||||
|
@ -17,7 +17,6 @@ impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
|
||||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
||||||
cx.push_layout_engine(LayoutEngine::new());
|
cx.push_layout_engine(LayoutEngine::new());
|
||||||
|
|
||||||
let size = constraint.max;
|
|
||||||
let mut cx = LayoutContext::new(cx);
|
let mut cx = LayoutContext::new(cx);
|
||||||
let layout_id = self.0.layout(view, &mut cx).log_err();
|
let layout_id = self.0.layout(view, &mut cx).log_err();
|
||||||
if let Some(layout_id) = layout_id {
|
if let Some(layout_id) = layout_id {
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::marker::PhantomData;
|
||||||
|
|
||||||
pub use crate::layout_context::LayoutContext;
|
pub use crate::layout_context::LayoutContext;
|
||||||
pub use crate::paint_context::PaintContext;
|
pub use crate::paint_context::PaintContext;
|
||||||
use crate::themes::{Theme, Themed};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpui::geometry::vector::Vector2F;
|
use gpui::geometry::vector::Vector2F;
|
||||||
pub use gpui::{Layout, LayoutId};
|
pub use gpui::{Layout, LayoutId};
|
||||||
|
@ -37,17 +36,6 @@ pub trait Element<V: 'static>: 'static {
|
||||||
phase: ElementPhase::Init,
|
phase: ElementPhase::Init,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn themed(self, theme: Theme) -> Themed<V, Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
crate::themes::Themed {
|
|
||||||
child: self,
|
|
||||||
theme,
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
|
/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
|
||||||
|
|
6
crates/gpui2/src/elements.rs
Normal file
6
crates/gpui2/src/elements.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
pub mod div;
|
||||||
|
pub mod hoverable;
|
||||||
|
pub mod pressable;
|
||||||
|
pub mod text;
|
||||||
|
|
||||||
|
pub use div::div;
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
element::{AnyElement, Element, IntoElement, Layout, ParentElement},
|
element::{AnyElement, Element, IntoElement, Layout, ParentElement},
|
||||||
interactive::{InteractionHandlers, Interactive},
|
|
||||||
layout_context::LayoutContext,
|
layout_context::LayoutContext,
|
||||||
paint_context::PaintContext,
|
paint_context::PaintContext,
|
||||||
style::{Style, StyleHelpers, Styleable},
|
style::{Style, StyleHelpers, Styleable},
|
||||||
|
InteractionHandlers, Interactive,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpui::{LayoutId, RenderContext};
|
use gpui::{LayoutId, RenderContext};
|
||||||
|
@ -58,7 +58,7 @@ impl<V: 'static> Element<V> for Div<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
layout: &Layout,
|
layout: &Layout,
|
||||||
paint_state: &mut Self::PaintState,
|
_: &mut Self::PaintState,
|
||||||
cx: &mut PaintContext<V>,
|
cx: &mut PaintContext<V>,
|
||||||
) where
|
) where
|
||||||
Self: Sized,
|
Self: Sized,
|
|
@ -71,7 +71,7 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
|
||||||
|
|
||||||
let hovered = self.hovered.clone();
|
let hovered = self.hovered.clone();
|
||||||
let bounds = layout.bounds;
|
let bounds = layout.bounds;
|
||||||
cx.on_event(layout.order, move |view, event: &MouseMovedEvent, cx| {
|
cx.on_event(layout.order, move |_view, _: &MouseMovedEvent, cx| {
|
||||||
if bounds.contains_point(cx.mouse_position()) != hovered.get() {
|
if bounds.contains_point(cx.mouse_position()) != hovered.get() {
|
||||||
cx.repaint();
|
cx.repaint();
|
||||||
}
|
}
|
|
@ -68,7 +68,7 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
|
||||||
|
|
||||||
let pressed = self.pressed.clone();
|
let pressed = self.pressed.clone();
|
||||||
let bounds = layout.bounds;
|
let bounds = layout.bounds;
|
||||||
cx.on_event(layout.order, move |view, event: &MouseButtonEvent, cx| {
|
cx.on_event(layout.order, move |_view, event: &MouseButtonEvent, cx| {
|
||||||
if event.is_down {
|
if event.is_down {
|
||||||
if bounds.contains_point(event.position) {
|
if bounds.contains_point(event.position) {
|
||||||
pressed.set(true);
|
pressed.set(true);
|
|
@ -25,10 +25,9 @@ impl<V: 'static> Element<V> for Text {
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
view: &mut V,
|
_view: &mut V,
|
||||||
cx: &mut LayoutContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> Result<(LayoutId, Self::PaintState)> {
|
) -> Result<(LayoutId, Self::PaintState)> {
|
||||||
let rem_size = cx.rem_pixels();
|
|
||||||
let fonts = cx.platform().fonts();
|
let fonts = cx.platform().fonts();
|
||||||
let text_style = cx.text_style();
|
let text_style = cx.text_style();
|
||||||
let line_height = cx.font_cache().line_height(text_style.font_size);
|
let line_height = cx.font_cache().line_height(text_style.font_size);
|
||||||
|
@ -63,7 +62,7 @@ impl<V: 'static> Element<V> for Text {
|
||||||
|
|
||||||
fn paint<'a>(
|
fn paint<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
view: &mut V,
|
_view: &mut V,
|
||||||
layout: &Layout,
|
layout: &Layout,
|
||||||
paint_state: &mut Self::PaintState,
|
paint_state: &mut Self::PaintState,
|
||||||
cx: &mut PaintContext<V>,
|
cx: &mut PaintContext<V>,
|
|
@ -1,36 +1,22 @@
|
||||||
#![allow(dead_code, unused_variables)]
|
pub mod adapter;
|
||||||
use gpui::{serde_json, ViewContext};
|
pub mod color;
|
||||||
use theme::ThemeSettings;
|
pub mod element;
|
||||||
use themes::Theme;
|
pub mod elements;
|
||||||
|
pub mod interactive;
|
||||||
|
pub mod layout_context;
|
||||||
|
pub mod paint_context;
|
||||||
|
pub mod style;
|
||||||
|
pub mod view;
|
||||||
|
|
||||||
mod adapter;
|
pub use color::*;
|
||||||
mod color;
|
pub use element::{AnyElement, Element, IntoElement, Layout, ParentElement};
|
||||||
mod components;
|
pub use geometry::{
|
||||||
mod div;
|
rect::RectF,
|
||||||
mod element;
|
vector::{vec2f, Vector2F},
|
||||||
mod hoverable;
|
};
|
||||||
mod interactive;
|
pub use gpui::*;
|
||||||
mod layout_context;
|
pub use gpui2_macros::{Element, *};
|
||||||
mod paint_context;
|
pub use interactive::*;
|
||||||
mod pressable;
|
pub use layout_context::LayoutContext;
|
||||||
mod style;
|
pub use platform::{WindowBounds, WindowOptions};
|
||||||
mod text;
|
pub use view::*;
|
||||||
mod themes;
|
|
||||||
mod view;
|
|
||||||
|
|
||||||
// Nathan: During the transition, we will include the base theme on the legacy Theme struct.
|
|
||||||
fn current_theme<V: 'static>(cx: &mut ViewContext<V>) -> Theme {
|
|
||||||
settings::get::<ThemeSettings>(cx)
|
|
||||||
.theme
|
|
||||||
.deserialized_base_theme
|
|
||||||
.lock()
|
|
||||||
.get_or_insert_with(|| {
|
|
||||||
let theme: Theme =
|
|
||||||
serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
|
|
||||||
.unwrap();
|
|
||||||
Box::new(theme)
|
|
||||||
})
|
|
||||||
.downcast_ref::<Theme>()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,7 +21,11 @@ pub trait Interactive<V: 'static> {
|
||||||
{
|
{
|
||||||
self.interaction_handlers()
|
self.interaction_handlers()
|
||||||
.mouse_down
|
.mouse_down
|
||||||
.push(Rc::new(handler));
|
.push(Rc::new(move |view, event, cx| {
|
||||||
|
if event.button == button {
|
||||||
|
handler(view, event, cx)
|
||||||
|
}
|
||||||
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +37,13 @@ pub trait Interactive<V: 'static> {
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
self.interaction_handlers().mouse_up.push(Rc::new(handler));
|
self.interaction_handlers()
|
||||||
|
.mouse_up
|
||||||
|
.push(Rc::new(move |view, event, cx| {
|
||||||
|
if event.button == button {
|
||||||
|
handler(view, event, cx)
|
||||||
|
}
|
||||||
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +57,11 @@ pub trait Interactive<V: 'static> {
|
||||||
{
|
{
|
||||||
self.interaction_handlers()
|
self.interaction_handlers()
|
||||||
.mouse_down_out
|
.mouse_down_out
|
||||||
.push(Rc::new(handler));
|
.push(Rc::new(move |view, event, cx| {
|
||||||
|
if event.button == button {
|
||||||
|
handler(view, event, cx)
|
||||||
|
}
|
||||||
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +75,11 @@ pub trait Interactive<V: 'static> {
|
||||||
{
|
{
|
||||||
self.interaction_handlers()
|
self.interaction_handlers()
|
||||||
.mouse_up_out
|
.mouse_up_out
|
||||||
.push(Rc::new(handler));
|
.push(Rc::new(move |view, event, cx| {
|
||||||
|
if event.button == button {
|
||||||
|
handler(view, event, cx)
|
||||||
|
}
|
||||||
|
}));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
use crate::{
|
|
||||||
element::{AnyElement, Element, IntoElement, Layout, ParentElement},
|
|
||||||
interactive::{InteractionHandlers, Interactive},
|
|
||||||
layout_context::LayoutContext,
|
|
||||||
paint_context::PaintContext,
|
|
||||||
style::{Style, StyleHelpers, Styleable},
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
|
||||||
use gpui::{platform::MouseButtonEvent, LayoutId};
|
|
||||||
use refineable::{CascadeSlot, Refineable, RefinementCascade};
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::{cell::Cell, rc::Rc};
|
|
||||||
|
|
||||||
pub struct Pressable<E: Styleable> {
|
|
||||||
pressed: Rc<Cell<bool>>,
|
|
||||||
pressed_style: <E::Style as Refineable>::Refinement,
|
|
||||||
cascade_slot: CascadeSlot,
|
|
||||||
child: E,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pressable<E: Styleable>(mut child: E) -> Pressable<E> {
|
|
||||||
Pressable {
|
|
||||||
pressed: Rc::new(Cell::new(false)),
|
|
||||||
pressed_style: Default::default(),
|
|
||||||
cascade_slot: child.style_cascade().reserve(),
|
|
||||||
child,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Styleable> Styleable for Pressable<E> {
|
|
||||||
type Style = E::Style;
|
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
|
|
||||||
&mut self.pressed_style
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style_cascade(&mut self) -> &mut RefinementCascade<E::Style> {
|
|
||||||
self.child.style_cascade()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
|
|
||||||
type PaintState = E::PaintState;
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<(LayoutId, Self::PaintState)>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.child.layout(view, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
layout: &Layout,
|
|
||||||
paint_state: &mut Self::PaintState,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let slot = self.cascade_slot;
|
|
||||||
let style = self.pressed.get().then_some(self.pressed_style.clone());
|
|
||||||
self.style_cascade().set(slot, style);
|
|
||||||
|
|
||||||
let pressed = self.pressed.clone();
|
|
||||||
let bounds = layout.bounds;
|
|
||||||
cx.on_event(layout.order, move |view, event: &MouseButtonEvent, cx| {
|
|
||||||
if event.is_down {
|
|
||||||
if bounds.contains_point(event.position) {
|
|
||||||
pressed.set(true);
|
|
||||||
cx.repaint();
|
|
||||||
}
|
|
||||||
} else if pressed.get() {
|
|
||||||
pressed.set(false);
|
|
||||||
cx.repaint();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.child.paint(view, layout, paint_state, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Styleable<Style = Style>> StyleHelpers for Pressable<E> {}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Interactive<V> + Styleable> Interactive<V> for Pressable<E> {
|
|
||||||
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
|
|
||||||
self.child.interaction_handlers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: ParentElement<V> + Styleable> ParentElement<V> for Pressable<E> {
|
|
||||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
|
||||||
self.child.children_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V> + Styleable> IntoElement<V> for Pressable<E> {
|
|
||||||
type Element = Self;
|
|
||||||
|
|
||||||
fn into_element(self) -> Self::Element {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Hsla,
|
color::Hsla,
|
||||||
hoverable::{hoverable, Hoverable},
|
elements::hoverable::{hoverable, Hoverable},
|
||||||
|
elements::pressable::{pressable, Pressable},
|
||||||
paint_context::PaintContext,
|
paint_context::PaintContext,
|
||||||
pressable::{pressable, Pressable},
|
|
||||||
};
|
};
|
||||||
pub use gpui::taffy::style::{
|
pub use gpui::taffy::style::{
|
||||||
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
|
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
use crate::{
|
|
||||||
element::{Element, IntoElement, Layout},
|
|
||||||
layout_context::LayoutContext,
|
|
||||||
paint_context::PaintContext,
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
|
||||||
use gpui::{geometry::Size, text_layout::LineLayout, LayoutId, RenderContext};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
|
|
||||||
type Element = Text;
|
|
||||||
|
|
||||||
fn into_element(self) -> Self::Element {
|
|
||||||
Text { text: self.into() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Text {
|
|
||||||
text: ArcCow<'static, str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Element<V> for Text {
|
|
||||||
type PaintState = Arc<Mutex<Option<TextLayout>>>;
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<(LayoutId, Self::PaintState)> {
|
|
||||||
let rem_size = cx.rem_pixels();
|
|
||||||
let fonts = cx.platform().fonts();
|
|
||||||
let text_style = cx.text_style();
|
|
||||||
let line_height = cx.font_cache().line_height(text_style.font_size);
|
|
||||||
let text = self.text.clone();
|
|
||||||
let paint_state = Arc::new(Mutex::new(None));
|
|
||||||
|
|
||||||
let layout_id = cx.add_measured_layout_node(Default::default(), {
|
|
||||||
let paint_state = paint_state.clone();
|
|
||||||
move |params| {
|
|
||||||
let line_layout = fonts.layout_line(
|
|
||||||
text.as_ref(),
|
|
||||||
text_style.font_size,
|
|
||||||
&[(text.len(), text_style.to_run())],
|
|
||||||
);
|
|
||||||
|
|
||||||
let size = Size {
|
|
||||||
width: line_layout.width,
|
|
||||||
height: line_height,
|
|
||||||
};
|
|
||||||
|
|
||||||
paint_state.lock().replace(TextLayout {
|
|
||||||
line_layout: Arc::new(line_layout),
|
|
||||||
line_height,
|
|
||||||
});
|
|
||||||
|
|
||||||
size
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok((layout_id?, paint_state))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint<'a>(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
layout: &Layout,
|
|
||||||
paint_state: &mut Self::PaintState,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) {
|
|
||||||
let line_layout;
|
|
||||||
let line_height;
|
|
||||||
{
|
|
||||||
let paint_state = paint_state.lock();
|
|
||||||
let paint_state = paint_state
|
|
||||||
.as_ref()
|
|
||||||
.expect("measurement has not been performed");
|
|
||||||
line_layout = paint_state.line_layout.clone();
|
|
||||||
line_height = paint_state.line_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
let text_style = cx.text_style();
|
|
||||||
let line =
|
|
||||||
gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
|
|
||||||
|
|
||||||
let origin = layout.bounds.origin();
|
|
||||||
// TODO: We haven't added visible bounds to the new element system yet, so this is a placeholder.
|
|
||||||
let visible_bounds = layout.bounds;
|
|
||||||
line.paint(cx.scene, origin, visible_bounds, line_height, cx.legacy_cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TextLayout {
|
|
||||||
line_layout: Arc<LineLayout>,
|
|
||||||
line_height: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ArcCow<'a, T: ?Sized> {
|
|
||||||
Borrowed(&'a T),
|
|
||||||
Owned(Arc<T>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: ?Sized> Clone for ArcCow<'a, T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Borrowed(borrowed) => Self::Borrowed(borrowed),
|
|
||||||
Self::Owned(owned) => Self::Owned(owned.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> {
|
|
||||||
fn from(s: &'a T) -> Self {
|
|
||||||
Self::Borrowed(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<Arc<T>> for ArcCow<'_, T> {
|
|
||||||
fn from(s: Arc<T>) -> Self {
|
|
||||||
Self::Owned(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for ArcCow<'_, str> {
|
|
||||||
fn from(value: String) -> Self {
|
|
||||||
Self::Owned(value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized> std::ops::Deref for ArcCow<'_, T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
match self {
|
|
||||||
ArcCow::Borrowed(s) => s,
|
|
||||||
ArcCow::Owned(s) => s.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized> AsRef<T> for ArcCow<'_, T> {
|
|
||||||
fn as_ref(&self) -> &T {
|
|
||||||
match self {
|
|
||||||
ArcCow::Borrowed(borrowed) => borrowed,
|
|
||||||
ArcCow::Owned(owned) => owned.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
color::{hsla, rgb, Hsla},
|
|
||||||
ThemeColors,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct RosePineThemes {
|
|
||||||
pub default: RosePinePalette,
|
|
||||||
pub dawn: RosePinePalette,
|
|
||||||
pub moon: RosePinePalette,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct RosePinePalette {
|
|
||||||
pub base: Hsla,
|
|
||||||
pub surface: Hsla,
|
|
||||||
pub overlay: Hsla,
|
|
||||||
pub muted: Hsla,
|
|
||||||
pub subtle: Hsla,
|
|
||||||
pub text: Hsla,
|
|
||||||
pub love: Hsla,
|
|
||||||
pub gold: Hsla,
|
|
||||||
pub rose: Hsla,
|
|
||||||
pub pine: Hsla,
|
|
||||||
pub foam: Hsla,
|
|
||||||
pub iris: Hsla,
|
|
||||||
pub highlight_low: Hsla,
|
|
||||||
pub highlight_med: Hsla,
|
|
||||||
pub highlight_high: Hsla,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RosePinePalette {
|
|
||||||
pub fn default() -> RosePinePalette {
|
|
||||||
RosePinePalette {
|
|
||||||
base: rgb(0x191724),
|
|
||||||
surface: rgb(0x1f1d2e),
|
|
||||||
overlay: rgb(0x26233a),
|
|
||||||
muted: rgb(0x6e6a86),
|
|
||||||
subtle: rgb(0x908caa),
|
|
||||||
text: rgb(0xe0def4),
|
|
||||||
love: rgb(0xeb6f92),
|
|
||||||
gold: rgb(0xf6c177),
|
|
||||||
rose: rgb(0xebbcba),
|
|
||||||
pine: rgb(0x31748f),
|
|
||||||
foam: rgb(0x9ccfd8),
|
|
||||||
iris: rgb(0xc4a7e7),
|
|
||||||
highlight_low: rgb(0x21202e),
|
|
||||||
highlight_med: rgb(0x403d52),
|
|
||||||
highlight_high: rgb(0x524f67),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn moon() -> RosePinePalette {
|
|
||||||
RosePinePalette {
|
|
||||||
base: rgb(0x232136),
|
|
||||||
surface: rgb(0x2a273f),
|
|
||||||
overlay: rgb(0x393552),
|
|
||||||
muted: rgb(0x6e6a86),
|
|
||||||
subtle: rgb(0x908caa),
|
|
||||||
text: rgb(0xe0def4),
|
|
||||||
love: rgb(0xeb6f92),
|
|
||||||
gold: rgb(0xf6c177),
|
|
||||||
rose: rgb(0xea9a97),
|
|
||||||
pine: rgb(0x3e8fb0),
|
|
||||||
foam: rgb(0x9ccfd8),
|
|
||||||
iris: rgb(0xc4a7e7),
|
|
||||||
highlight_low: rgb(0x2a283e),
|
|
||||||
highlight_med: rgb(0x44415a),
|
|
||||||
highlight_high: rgb(0x56526e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dawn() -> RosePinePalette {
|
|
||||||
RosePinePalette {
|
|
||||||
base: rgb(0xfaf4ed),
|
|
||||||
surface: rgb(0xfffaf3),
|
|
||||||
overlay: rgb(0xf2e9e1),
|
|
||||||
muted: rgb(0x9893a5),
|
|
||||||
subtle: rgb(0x797593),
|
|
||||||
text: rgb(0x575279),
|
|
||||||
love: rgb(0xb4637a),
|
|
||||||
gold: rgb(0xea9d34),
|
|
||||||
rose: rgb(0xd7827e),
|
|
||||||
pine: rgb(0x286983),
|
|
||||||
foam: rgb(0x56949f),
|
|
||||||
iris: rgb(0x907aa9),
|
|
||||||
highlight_low: rgb(0xf4ede8),
|
|
||||||
highlight_med: rgb(0xdfdad9),
|
|
||||||
highlight_high: rgb(0xcecacd),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default() -> ThemeColors {
|
|
||||||
theme_colors(&RosePinePalette::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn moon() -> ThemeColors {
|
|
||||||
theme_colors(&RosePinePalette::moon())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dawn() -> ThemeColors {
|
|
||||||
theme_colors(&RosePinePalette::dawn())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn theme_colors(p: &RosePinePalette) -> ThemeColors {
|
|
||||||
ThemeColors {
|
|
||||||
base: scale_sl(p.base, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
surface: scale_sl(p.surface, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
overlay: scale_sl(p.overlay, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
muted: scale_sl(p.muted, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
subtle: scale_sl(p.subtle, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
text: scale_sl(p.text, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
highlight_low: scale_sl(p.highlight_low, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
highlight_med: scale_sl(p.highlight_med, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
highlight_high: scale_sl(p.highlight_high, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
success: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
warning: scale_sl(p.gold, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
error: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
inserted: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
deleted: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
modified: scale_sl(p.rose, (0.8, 0.8), (1.2, 1.2)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produces a range by multiplying the saturation and lightness of the base color by the given
|
|
||||||
/// start and end factors.
|
|
||||||
fn scale_sl(base: Hsla, (start_s, start_l): (f32, f32), (end_s, end_l): (f32, f32)) -> Range<Hsla> {
|
|
||||||
let start = hsla(base.h, base.s * start_s, base.l * start_l, base.a);
|
|
||||||
let end = hsla(base.h, base.s * end_s, base.l * end_l, base.a);
|
|
||||||
Range { start, end }
|
|
||||||
}
|
|
|
@ -1686,7 +1686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "playground"
|
name = "storybook"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui",
|
"gpui",
|
23
crates/storybook/Cargo.toml
Normal file
23
crates/storybook/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "storybook"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "storybook"
|
||||||
|
path = "src/storybook.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gpui2 = { path = "../gpui2" }
|
||||||
|
anyhow.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
rust-embed.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
settings = { path = "../settings" }
|
||||||
|
simplelog = "0.9"
|
||||||
|
theme = { path = "../theme" }
|
||||||
|
util = { path = "../util" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
gpui2 = { path = "../gpui2", features = ["test-support"] }
|
|
@ -1,13 +1,7 @@
|
||||||
use crate::{
|
use gpui2::{
|
||||||
div::div,
|
elements::div, elements::text::ArcCow, interactive::Interactive, platform::MouseButton,
|
||||||
element::{IntoElement, ParentElement},
|
style::StyleHelpers, Element, IntoElement, ParentElement, ViewContext,
|
||||||
interactive::Interactive,
|
|
||||||
style::StyleHelpers,
|
|
||||||
text::ArcCow,
|
|
||||||
// themes::Theme,
|
|
||||||
};
|
};
|
||||||
use gpui::{platform::MouseButton, ViewContext};
|
|
||||||
use gpui2_macros::Element;
|
|
||||||
use std::{marker::PhantomData, rc::Rc};
|
use std::{marker::PhantomData, rc::Rc};
|
||||||
|
|
||||||
struct ButtonHandlers<V, D> {
|
struct ButtonHandlers<V, D> {
|
||||||
|
@ -20,7 +14,6 @@ impl<V, D> Default for ButtonHandlers<V, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate as gpui2;
|
|
||||||
#[derive(Element)]
|
#[derive(Element)]
|
||||||
pub struct Button<V: 'static, D: 'static> {
|
pub struct Button<V: 'static, D: 'static> {
|
||||||
handlers: ButtonHandlers<V, D>,
|
handlers: ButtonHandlers<V, D>,
|
22
crates/storybook/src/element_ext.rs
Normal file
22
crates/storybook/src/element_ext.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use crate::theme::{Theme, Themed};
|
||||||
|
use gpui2::Element;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub trait ElementExt<V: 'static>: Element<V> {
|
||||||
|
fn themed(self, theme: Theme) -> Themed<V, Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static, E: Element<V>> ElementExt<V> for E {
|
||||||
|
fn themed(self, theme: Theme) -> Themed<V, Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Themed {
|
||||||
|
child: self,
|
||||||
|
theme,
|
||||||
|
view_type: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,61 +1,43 @@
|
||||||
#![allow(dead_code, unused_variables)]
|
#![allow(dead_code, unused_variables)]
|
||||||
use crate::element::Element;
|
use crate::theme::Theme;
|
||||||
use gpui::{
|
use ::theme as legacy_theme;
|
||||||
geometry::{rect::RectF, vector::vec2f},
|
use element_ext::ElementExt;
|
||||||
platform::WindowOptions,
|
use gpui2::{serde_json, vec2f, view, Element, RectF, ViewContext, WindowBounds};
|
||||||
serde_json, ViewContext,
|
use legacy_theme::ThemeSettings;
|
||||||
};
|
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use settings::{default_settings, SettingsStore};
|
use settings::{default_settings, SettingsStore};
|
||||||
use simplelog::SimpleLogger;
|
use simplelog::SimpleLogger;
|
||||||
use theme::ThemeSettings;
|
|
||||||
use themes::Theme;
|
|
||||||
use view::view;
|
|
||||||
use workspace::workspace;
|
use workspace::workspace;
|
||||||
|
|
||||||
mod adapter;
|
|
||||||
mod color;
|
|
||||||
mod components;
|
mod components;
|
||||||
mod div;
|
mod element_ext;
|
||||||
mod element;
|
mod theme;
|
||||||
mod hoverable;
|
|
||||||
mod interactive;
|
|
||||||
mod layout_context;
|
|
||||||
mod paint_context;
|
|
||||||
mod pressable;
|
|
||||||
mod style;
|
|
||||||
mod text;
|
|
||||||
mod themes;
|
|
||||||
mod view;
|
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
||||||
|
|
||||||
gpui::App::new(()).unwrap().run(|cx| {
|
gpui2::App::new(()).unwrap().run(|cx| {
|
||||||
let mut store = SettingsStore::default();
|
let mut store = SettingsStore::default();
|
||||||
store
|
store
|
||||||
.set_default_settings(default_settings().as_ref(), cx)
|
.set_default_settings(default_settings().as_ref(), cx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx.set_global(store);
|
cx.set_global(store);
|
||||||
theme::init(Assets, cx);
|
legacy_theme::init(Assets, cx);
|
||||||
|
|
||||||
cx.add_window(
|
cx.add_window(
|
||||||
WindowOptions {
|
gpui2::WindowOptions {
|
||||||
bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
|
bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(400., 300.))),
|
||||||
vec2f(0., 0.),
|
|
||||||
vec2f(400., 300.),
|
|
||||||
)),
|
|
||||||
center: true,
|
center: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|_| view(|cx| playground(cx)),
|
|_| view(|cx| storybook(cx)),
|
||||||
);
|
);
|
||||||
cx.platform().activate(true);
|
cx.platform().activate(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn playground<V: 'static>(cx: &mut ViewContext<V>) -> impl Element<V> {
|
fn storybook<V: 'static>(cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||||
workspace().themed(current_theme(cx))
|
workspace().themed(current_theme(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,11 +59,11 @@ fn current_theme<V: 'static>(cx: &mut ViewContext<V>) -> Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use gpui::AssetSource;
|
use gpui2::AssetSource;
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
#[folder = "../../../assets"]
|
#[folder = "../../assets"]
|
||||||
#[include = "themes/**/*"]
|
#[include = "themes/**/*"]
|
||||||
#[exclude = "*.DS_Store"]
|
#[exclude = "*.DS_Store"]
|
||||||
pub struct Assets;
|
pub struct Assets;
|
|
@ -1,11 +1,12 @@
|
||||||
use crate::{
|
use gpui2::{
|
||||||
color::Hsla,
|
color::Hsla,
|
||||||
element::{Element, PaintContext},
|
element::{Element, PaintContext},
|
||||||
layout_context::LayoutContext,
|
layout_context::LayoutContext,
|
||||||
|
serde_json, AppContext, WindowContext,
|
||||||
};
|
};
|
||||||
use gpui::WindowContext;
|
|
||||||
use serde::{de::Visitor, Deserialize, Deserializer};
|
use serde::{de::Visitor, Deserialize, Deserializer};
|
||||||
use std::{collections::HashMap, fmt, marker::PhantomData};
|
use std::{collections::HashMap, fmt, marker::PhantomData};
|
||||||
|
use theme::ThemeSettings;
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Default, Debug)]
|
#[derive(Deserialize, Clone, Default, Debug)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
|
@ -64,10 +65,6 @@ pub struct Shadow {
|
||||||
pub offset: Vec<u8>,
|
pub offset: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
|
|
||||||
cx.theme::<Theme>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_player_colors<'de, D>(deserializer: D) -> Result<Vec<PlayerColors>, D::Error>
|
fn deserialize_player_colors<'de, D>(deserializer: D) -> Result<Vec<PlayerColors>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
|
@ -149,7 +146,7 @@ impl<V: 'static, E: Element<V>> Element<V> for Themed<V, E> {
|
||||||
&mut self,
|
&mut self,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut LayoutContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> anyhow::Result<(gpui::LayoutId, Self::PaintState)>
|
) -> anyhow::Result<(gpui2::LayoutId, Self::PaintState)>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
|
@ -162,7 +159,7 @@ impl<V: 'static, E: Element<V>> Element<V> for Themed<V, E> {
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
layout: &gpui::Layout,
|
layout: &gpui2::Layout,
|
||||||
state: &mut Self::PaintState,
|
state: &mut Self::PaintState,
|
||||||
cx: &mut PaintContext<V>,
|
cx: &mut PaintContext<V>,
|
||||||
) where
|
) where
|
||||||
|
@ -173,3 +170,23 @@ impl<V: 'static, E: Element<V>> Element<V> for Themed<V, E> {
|
||||||
cx.pop_theme();
|
cx.pop_theme();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn preferred_theme<V: 'static>(cx: &AppContext) -> Theme {
|
||||||
|
settings::get::<ThemeSettings>(cx)
|
||||||
|
.theme
|
||||||
|
.deserialized_base_theme
|
||||||
|
.lock()
|
||||||
|
.get_or_insert_with(|| {
|
||||||
|
let theme: Theme =
|
||||||
|
serde_json::from_value(settings::get::<ThemeSettings>(cx).theme.base_theme.clone())
|
||||||
|
.unwrap();
|
||||||
|
Box::new(theme)
|
||||||
|
})
|
||||||
|
.downcast_ref::<Theme>()
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn theme<'a>(cx: &'a WindowContext) -> &'a Theme {
|
||||||
|
cx.theme::<Theme>()
|
||||||
|
}
|
|
@ -1,13 +1,9 @@
|
||||||
use crate::{
|
use crate::theme::theme;
|
||||||
div::div,
|
use gpui2::{
|
||||||
element::{Element, IntoElement, ParentElement},
|
elements::div, geometry::pixels, style::StyleHelpers, Element, IntoElement, ParentElement,
|
||||||
style::StyleHelpers,
|
ViewContext,
|
||||||
themes::theme,
|
|
||||||
};
|
};
|
||||||
use gpui::{geometry::pixels, ViewContext};
|
|
||||||
use gpui2_macros::Element;
|
|
||||||
|
|
||||||
use crate as playground;
|
|
||||||
#[derive(Element)]
|
#[derive(Element)]
|
||||||
struct WorkspaceElement;
|
struct WorkspaceElement;
|
||||||
|
|
6
test.rs
6
test.rs
|
@ -4484,7 +4484,7 @@ mod element {
|
||||||
.layout_engine()
|
.layout_engine()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.computed_layout(*layout_node_id)
|
.computed_layout(*layout_node_id)
|
||||||
.expect("you can currently only use playground elements within an adapter"),
|
.expect("make sure you're using this within a gpui2 adapter element"),
|
||||||
from_element: element_layout.as_mut(),
|
from_element: element_layout.as_mut(),
|
||||||
};
|
};
|
||||||
let style = self.element.style();
|
let style = self.element.style();
|
||||||
|
@ -5601,12 +5601,12 @@ fn main() {
|
||||||
center: true,
|
center: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|_| view(|_| playground(&rose_pine::moon())),
|
|_| view(|_| storybook(&rose_pine::moon())),
|
||||||
);
|
);
|
||||||
cx.platform().activate(true);
|
cx.platform().activate(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
fn storybook<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||||
frame()
|
frame()
|
||||||
.text_color(black())
|
.text_color(black())
|
||||||
.h_full()
|
.h_full()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue