Ditch the hot reloading approach
This commit is contained in:
parent
0bf607cd2d
commit
d6eaa3c6b8
14 changed files with 340 additions and 275 deletions
256
crates/gpui/playground/src/color.rs
Normal file
256
crates/gpui/playground/src/color.rs
Normal file
|
@ -0,0 +1,256 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::{num::ParseIntError, ops::Range};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
|
||||
let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
|
||||
let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
|
||||
let b = (hex & 0xFF) as f32 / 255.0;
|
||||
Rgba { r, g, b, a: 1.0 }.into()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct Rgba {
|
||||
pub r: f32,
|
||||
pub g: f32,
|
||||
pub b: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
pub trait Lerp {
|
||||
fn lerp(&self, level: f32) -> Hsla;
|
||||
}
|
||||
|
||||
impl Lerp for Range<Hsla> {
|
||||
fn lerp(&self, level: f32) -> Hsla {
|
||||
let level = level.clamp(0., 1.);
|
||||
Hsla {
|
||||
h: self.start.h + (level * (self.end.h - self.start.h)),
|
||||
s: self.start.s + (level * (self.end.s - self.start.s)),
|
||||
l: self.start.l + (level * (self.end.l - self.start.l)),
|
||||
a: self.start.a + (level * (self.end.a - self.start.a)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gpui::color::Color> for Rgba {
|
||||
fn from(value: gpui::color::Color) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hsla> for Rgba {
|
||||
fn from(color: Hsla) -> Self {
|
||||
let h = color.h;
|
||||
let s = color.s;
|
||||
let l = color.l;
|
||||
|
||||
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
|
||||
let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
|
||||
let m = l - c / 2.0;
|
||||
let cm = c + m;
|
||||
let xm = x + m;
|
||||
|
||||
let (r, g, b) = match (h * 6.0).floor() as i32 {
|
||||
0 | 6 => (cm, xm, m),
|
||||
1 => (xm, cm, m),
|
||||
2 => (m, cm, xm),
|
||||
3 => (m, xm, cm),
|
||||
4 => (xm, m, cm),
|
||||
_ => (cm, m, xm),
|
||||
};
|
||||
|
||||
Rgba {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
a: color.a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&'_ str> for Rgba {
|
||||
type Error = ParseIntError;
|
||||
|
||||
fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
|
||||
let r = u8::from_str_radix(&value[1..3], 16)? as f32 / 255.0;
|
||||
let g = u8::from_str_radix(&value[3..5], 16)? as f32 / 255.0;
|
||||
let b = u8::from_str_radix(&value[5..7], 16)? as f32 / 255.0;
|
||||
let a = if value.len() > 7 {
|
||||
u8::from_str_radix(&value[7..9], 16)? as f32 / 255.0
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
Ok(Rgba { r, g, b, a })
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<gpui::color::Color> for Rgba {
|
||||
fn into(self) -> gpui::color::Color {
|
||||
gpui::color::rgba(self.r, self.g, self.b, self.a)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Hsla {
|
||||
pub h: f32,
|
||||
pub s: f32,
|
||||
pub l: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
|
||||
Hsla {
|
||||
h: h.clamp(0., 1.),
|
||||
s: s.clamp(0., 1.),
|
||||
l: l.clamp(0., 1.),
|
||||
a: a.clamp(0., 1.),
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rgba> for Hsla {
|
||||
fn from(color: Rgba) -> Self {
|
||||
let r = color.r;
|
||||
let g = color.g;
|
||||
let b = color.b;
|
||||
|
||||
let max = r.max(g.max(b));
|
||||
let min = r.min(g.min(b));
|
||||
let delta = max - min;
|
||||
|
||||
let l = (max + min) / 2.0;
|
||||
let s = if l == 0.0 || l == 1.0 {
|
||||
0.0
|
||||
} else if l < 0.5 {
|
||||
delta / (2.0 * l)
|
||||
} else {
|
||||
delta / (2.0 - 2.0 * l)
|
||||
};
|
||||
|
||||
let h = if delta == 0.0 {
|
||||
0.0
|
||||
} else if max == r {
|
||||
((g - b) / delta).rem_euclid(6.0) / 6.0
|
||||
} else if max == g {
|
||||
((b - r) / delta + 2.0) / 6.0
|
||||
} else {
|
||||
((r - g) / delta + 4.0) / 6.0
|
||||
};
|
||||
|
||||
Hsla {
|
||||
h,
|
||||
s,
|
||||
l,
|
||||
a: color.a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hsla {
|
||||
/// Scales the saturation and lightness by the given values, clamping at 1.0.
|
||||
pub fn scale_sl(mut self, s: f32, l: f32) -> Self {
|
||||
self.s = (self.s * s).clamp(0., 1.);
|
||||
self.l = (self.l * l).clamp(0., 1.);
|
||||
self
|
||||
}
|
||||
|
||||
/// Increases the saturation of the color by a certain amount, with a max
|
||||
/// value of 1.0.
|
||||
pub fn saturate(mut self, amount: f32) -> Self {
|
||||
self.s += amount;
|
||||
self.s = self.s.clamp(0.0, 1.0);
|
||||
self
|
||||
}
|
||||
|
||||
/// Decreases the saturation of the color by a certain amount, with a min
|
||||
/// value of 0.0.
|
||||
pub fn desaturate(mut self, amount: f32) -> Self {
|
||||
self.s -= amount;
|
||||
self.s = self.s.max(0.0);
|
||||
if self.s < 0.0 {
|
||||
self.s = 0.0;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Brightens the color by increasing the lightness by a certain amount,
|
||||
/// with a max value of 1.0.
|
||||
pub fn brighten(mut self, amount: f32) -> Self {
|
||||
self.l += amount;
|
||||
self.l = self.l.clamp(0.0, 1.0);
|
||||
self
|
||||
}
|
||||
|
||||
/// Darkens the color by decreasing the lightness by a certain amount,
|
||||
/// with a max value of 0.0.
|
||||
pub fn darken(mut self, amount: f32) -> Self {
|
||||
self.l -= amount;
|
||||
self.l = self.l.clamp(0.0, 1.0);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gpui::color::Color> for Hsla {
|
||||
fn from(value: gpui::color::Color) -> Self {
|
||||
Rgba::from(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ColorScale {
|
||||
colors: SmallVec<[Hsla; 2]>,
|
||||
positions: SmallVec<[f32; 2]>,
|
||||
}
|
||||
|
||||
pub fn scale<I, C>(colors: I) -> ColorScale
|
||||
where
|
||||
I: IntoIterator<Item = C>,
|
||||
C: Into<Hsla>,
|
||||
{
|
||||
let mut scale = ColorScale {
|
||||
colors: colors.into_iter().map(Into::into).collect(),
|
||||
positions: SmallVec::new(),
|
||||
};
|
||||
let num_colors: f32 = scale.colors.len() as f32 - 1.0;
|
||||
scale.positions = (0..scale.colors.len())
|
||||
.map(|i| i as f32 / num_colors)
|
||||
.collect();
|
||||
scale
|
||||
}
|
||||
|
||||
impl ColorScale {
|
||||
fn at(&self, t: f32) -> Hsla {
|
||||
// Ensure that the input is within [0.0, 1.0]
|
||||
debug_assert!(
|
||||
0.0 <= t && t <= 1.0,
|
||||
"t value {} is out of range. Expected value in range 0.0 to 1.0",
|
||||
t
|
||||
);
|
||||
|
||||
let position = match self
|
||||
.positions
|
||||
.binary_search_by(|a| a.partial_cmp(&t).unwrap())
|
||||
{
|
||||
Ok(index) | Err(index) => index,
|
||||
};
|
||||
let lower_bound = position.saturating_sub(1);
|
||||
let upper_bound = position.min(self.colors.len() - 1);
|
||||
let lower_color = &self.colors[lower_bound];
|
||||
let upper_color = &self.colors[upper_bound];
|
||||
|
||||
match upper_bound.checked_sub(lower_bound) {
|
||||
Some(0) | None => *lower_color,
|
||||
Some(_) => {
|
||||
let interval_t = (t - self.positions[lower_bound])
|
||||
/ (self.positions[upper_bound] - self.positions[lower_bound]);
|
||||
let h = lower_color.h + interval_t * (upper_color.h - lower_color.h);
|
||||
let s = lower_color.s + interval_t * (upper_color.s - lower_color.s);
|
||||
let l = lower_color.l + interval_t * (upper_color.l - lower_color.l);
|
||||
let a = lower_color.a + interval_t * (upper_color.a - lower_color.a);
|
||||
Hsla { h, s, l, a }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
165
crates/gpui/playground/src/editor_layout_demo.rs
Normal file
165
crates/gpui/playground/src/editor_layout_demo.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
use gpui::{AnyElement, Element, LayoutContext, View, ViewContext};
|
||||
|
||||
#[derive(Element, Clone, Default)]
|
||||
pub struct Playground<V>(PhantomData<V>);
|
||||
|
||||
// example layout design here: https://www.figma.com/file/5QLTmxjO0xQpDD3CD4hR6T/Untitled?type=design&node-id=0%3A1&mode=design&t=SoJieVVIvDDDKagv-1
|
||||
|
||||
impl<V> Playground<V> {
|
||||
pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> impl Element<V> {
|
||||
col() // fullscreen container with header and main in it
|
||||
.width(flex(1.))
|
||||
.height(flex(1.))
|
||||
.fill(colors(gray.900))
|
||||
.children([
|
||||
row() // header container
|
||||
.fill(colors(gray.900))
|
||||
.width(flex(1.))
|
||||
.children([
|
||||
row() // tab bar
|
||||
.width(flex(1.))
|
||||
.gap(spacing(2))
|
||||
.padding(spacing(3))
|
||||
.overflow_x(scroll())
|
||||
.chidren([
|
||||
row() // tab
|
||||
.padding_x(spacing(3))
|
||||
.padding_y(spacing(2))
|
||||
.corner_radius(6.)
|
||||
.gap(spacing(3))
|
||||
.align(center())
|
||||
.fill(colors(gray.800))
|
||||
.children([text("Tab title 1"), svg("icon_name")]),
|
||||
row() // tab
|
||||
.padding_x(spacing(3))
|
||||
.padding_y(spacing(2))
|
||||
.corner_radius(6.)
|
||||
.gap(spacing(3))
|
||||
.align(center())
|
||||
.fill(colors(gray.800))
|
||||
.children([text("Tab title 2"), svg("icon_name")]),
|
||||
row() // tab
|
||||
.padding_x(spacing(3))
|
||||
.padding_y(spacing(2))
|
||||
.corner_radius(6.)
|
||||
.gap(spacing(3))
|
||||
.align(center())
|
||||
.fill(colors(gray.800))
|
||||
.children([text("Tab title 3"), svg("icon_name")]),
|
||||
]),
|
||||
row() // tab bar actions
|
||||
.border_left(colors(gray.700))
|
||||
.gap(spacing(2))
|
||||
.padding(spacing(3))
|
||||
.chidren([
|
||||
row()
|
||||
.width(spacing(8))
|
||||
.height(spacing(8))
|
||||
.corner_radius(6.)
|
||||
.justify(center())
|
||||
.align(center())
|
||||
.fill(colors(gray.800))
|
||||
.child(svg(icon_name)),
|
||||
row()
|
||||
.width(spacing(8))
|
||||
.height(spacing(8))
|
||||
.corner_radius(6.)
|
||||
.justify(center())
|
||||
.align(center())
|
||||
.fill(colors(gray.800))
|
||||
.child(svg(icon_name)),
|
||||
row()
|
||||
.width(spacing(8))
|
||||
.height(spacing(8))
|
||||
.corner_radius(6.)
|
||||
.justify(center())
|
||||
.align(center())
|
||||
.fill(colors(gray.800))
|
||||
.child(svg(icon_name)),
|
||||
]),
|
||||
]),
|
||||
row() // main container
|
||||
.width(flex(1.))
|
||||
.height(flex(1.))
|
||||
.children([
|
||||
col() // left sidebar
|
||||
.fill(colors(gray.800))
|
||||
.border_right(colors(gray.700))
|
||||
.height(flex(1.))
|
||||
.width(260.)
|
||||
.children([
|
||||
col() // containter to hold list items and notification alert box
|
||||
.justify(between())
|
||||
.padding_x(spacing(6))
|
||||
.padding_bottom(3)
|
||||
.padding_top(spacing(6))
|
||||
.children([
|
||||
col().gap(spacing(3)).children([ // sidebar list
|
||||
text("Item"),
|
||||
text("Item"),
|
||||
text("Item"),
|
||||
text("Item"),
|
||||
text("Item"),
|
||||
text("Item"),
|
||||
text("Item"),
|
||||
text("Item"),
|
||||
]),
|
||||
col().align(center()).gap(spacing(1)).children([ // notification alert box
|
||||
text("Title text").size("lg"),
|
||||
text("Description text goes here")
|
||||
.text_color(colors(rose.200)),
|
||||
]),
|
||||
]),
|
||||
row()
|
||||
.padding_x(spacing(3))
|
||||
.padding_y(spacing(2))
|
||||
.border_top(1., colors(gray.700))
|
||||
.align(center())
|
||||
.gap(spacing(2))
|
||||
.fill(colors(gray.900))
|
||||
.children([
|
||||
row() // avatar container
|
||||
.width(spacing(8))
|
||||
.height(spacing(8))
|
||||
.corner_radius(spacing(8))
|
||||
.justify(center())
|
||||
.align(center())
|
||||
.child(image(image_url)),
|
||||
text("FirstName Lastname"), // user name
|
||||
]),
|
||||
]),
|
||||
col() // primary content container
|
||||
.align(center())
|
||||
.justify(center())
|
||||
.child(
|
||||
col().justify(center()).gap(spacing(8)).children([ // detail container wrapper for center positioning
|
||||
col() // blue rectangle
|
||||
.width(rem(30.))
|
||||
.height(rem(20.))
|
||||
.corner_radius(16.)
|
||||
.fill(colors(blue.200)),
|
||||
col().gap(spacing(1)).children([ // center content text items
|
||||
text("This is a title").size("lg"),
|
||||
text("This is a description").text_color(colors(gray.500)),
|
||||
]),
|
||||
]),
|
||||
),
|
||||
col(), // right sidebar
|
||||
]),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// row(
|
||||
// padding(),
|
||||
// width(),
|
||||
// fill(),
|
||||
// )
|
||||
|
||||
// .width(flex(1.))
|
||||
// .height(flex(1.))
|
||||
// .justify(end())
|
||||
// .align(start()) // default
|
||||
// .fill(green)
|
||||
// .child(other_tab_bar())
|
||||
// .child(profile_menu())
|
1668
crates/gpui/playground/src/frame.rs
Normal file
1668
crates/gpui/playground/src/frame.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,11 @@
|
|||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
use gpui::{
|
||||
platform::{TitlebarOptions, WindowOptions},
|
||||
AnyElement, Element, Entity, View,
|
||||
AnyElement, Element,
|
||||
};
|
||||
use log::LevelFilter;
|
||||
use simplelog::SimpleLogger;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
// dymod! {
|
||||
// #[path = "../ui/src/playground_ui.rs"]
|
||||
// pub mod ui {
|
||||
// // fn workspace<V>(theme: &ThemeColors) -> impl Element<V>;
|
||||
// }
|
||||
// }
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
||||
|
@ -26,38 +20,213 @@ fn main() {
|
|||
}),
|
||||
..Default::default()
|
||||
},
|
||||
|_| Playground::default(),
|
||||
|_| view(|_| Playground::new()),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct Playground(playground_ui::Playground<Self>);
|
||||
use frame::{length::auto, *};
|
||||
use gpui::{LayoutContext, ViewContext};
|
||||
use std::{borrow::Cow, cell::RefCell, marker::PhantomData, rc::Rc};
|
||||
use themes::{rose_pine, ThemeColors};
|
||||
use tokens::{margin::m4, text::lg};
|
||||
|
||||
impl Deref for Playground {
|
||||
type Target = playground_ui::Playground<Self>;
|
||||
mod color;
|
||||
mod frame;
|
||||
mod themes;
|
||||
mod tokens;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
#[derive(Element, Clone)]
|
||||
pub struct Playground<V: 'static>(PhantomData<V>);
|
||||
|
||||
impl<V> Playground<V> {
|
||||
pub fn new() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
|
||||
pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> impl Element<V> {
|
||||
workspace(&rose_pine::dawn())
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Playground {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
fn workspace<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||
column()
|
||||
.size(auto())
|
||||
.fill(theme.base(0.5))
|
||||
.text_color(theme.text(0.5))
|
||||
.child(title_bar(theme))
|
||||
.child(stage(theme))
|
||||
.child(status_bar(theme))
|
||||
}
|
||||
|
||||
fn title_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||
row()
|
||||
.fill(theme.base(0.2))
|
||||
.justify(0.)
|
||||
.width(auto())
|
||||
.child(text("Zed Playground"))
|
||||
}
|
||||
|
||||
fn stage<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||
row().fill(theme.surface(0.9))
|
||||
}
|
||||
|
||||
fn status_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||
row().fill(theme.surface(0.1))
|
||||
}
|
||||
|
||||
pub trait DialogDelegate<V>: 'static {}
|
||||
|
||||
impl<V> DialogDelegate<V> for () {}
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Dialog<V: 'static, D: DialogDelegate<V>> {
|
||||
title: Cow<'static, str>,
|
||||
description: Cow<'static, str>,
|
||||
delegate: Option<Rc<RefCell<D>>>,
|
||||
buttons: Vec<Box<dyn FnOnce() -> AnyElement<V>>>,
|
||||
view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
pub fn dialog<V>(
|
||||
title: impl Into<Cow<'static, str>>,
|
||||
description: impl Into<Cow<'static, str>>,
|
||||
) -> Dialog<V, ()> {
|
||||
Dialog {
|
||||
title: title.into(),
|
||||
description: description.into(),
|
||||
delegate: None,
|
||||
buttons: Vec::new(),
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Playground {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for Playground {
|
||||
fn ui_name() -> &'static str {
|
||||
"PlaygroundView"
|
||||
impl<V, D: DialogDelegate<V>> Dialog<V, D> {
|
||||
pub fn delegate(mut self, delegate: D) -> Dialog<V, D> {
|
||||
let old_delegate = self.delegate.replace(Rc::new(RefCell::new(delegate)));
|
||||
debug_assert!(old_delegate.is_none(), "delegate already set");
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> AnyElement<Playground> {
|
||||
self.0.clone().into_any()
|
||||
pub fn button<L, Data, H>(mut self, label: L, data: Data, handler: H) -> Self
|
||||
where
|
||||
L: 'static + Into<Cow<'static, str>>,
|
||||
Data: 'static + Clone,
|
||||
H: ClickHandler<V, Data>,
|
||||
{
|
||||
let label = label.into();
|
||||
self.buttons.push(Box::new(move || {
|
||||
button(label).data(data).click(handler).into_any()
|
||||
}));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Element)]
|
||||
struct Button<V: 'static, D: 'static, H: ClickHandler<V, D>> {
|
||||
label: Cow<'static, str>,
|
||||
click_handler: Option<H>,
|
||||
data: Option<D>,
|
||||
view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
pub trait ClickHandler<V, D>: 'static {
|
||||
fn handle(&self, view: &mut V, data: &D, cx: &mut ViewContext<V>);
|
||||
}
|
||||
|
||||
impl<V, M, F: 'static + Fn(&mut V, &M, &mut ViewContext<V>)> ClickHandler<V, M> for F {
|
||||
fn handle(&self, view: &mut V, data: &M, cx: &mut ViewContext<V>) {
|
||||
self(view, data, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, D> ClickHandler<V, D> for () {
|
||||
fn handle(&self, view: &mut V, data: &D, cx: &mut ViewContext<V>) {}
|
||||
}
|
||||
|
||||
fn button<V>(label: impl Into<Cow<'static, str>>) -> Button<V, (), ()> {
|
||||
Button {
|
||||
label: label.into(),
|
||||
click_handler: None,
|
||||
data: None,
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, D, F> Button<V, D, F>
|
||||
where
|
||||
F: ClickHandler<V, D>,
|
||||
{
|
||||
fn render(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> AnyElement<V> {
|
||||
// TODO! Handle click etc
|
||||
row().child(text(self.label.clone())).into_any()
|
||||
}
|
||||
}
|
||||
|
||||
// impl<V, D, F> Button<V, D, F>
|
||||
// where
|
||||
// V,
|
||||
// F: ClickHandler<V, D>,
|
||||
// {
|
||||
// fn render(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> impl Element<V> {
|
||||
// // TODO! Handle click etc
|
||||
// row()
|
||||
// .fill(theme.colors.primary(5))
|
||||
// .child(text(self.label.clone()).text_color(theme.colors.on_primary()))
|
||||
// }
|
||||
// }
|
||||
|
||||
// struct Tab<V> {
|
||||
// active: bool,
|
||||
// }
|
||||
|
||||
// impl<V> Tab<V>
|
||||
// where
|
||||
// V,
|
||||
// {
|
||||
// fn tab(&mut self, _: &mut V, _: &mut LayoutContext<V>) -> impl Element<V> {
|
||||
// let theme = todo!();
|
||||
// // TODO! Handle click etc
|
||||
// row()
|
||||
// .fill(theme.colors.neutral(6))
|
||||
// .child(text(self.label.clone()).text_color(theme.colors.on_neutral()))
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<V> Button<V, (), ()> {
|
||||
fn data<D>(self, data: D) -> Button<V, D, ()>
|
||||
where
|
||||
D: 'static,
|
||||
{
|
||||
Button {
|
||||
label: self.label,
|
||||
click_handler: self.click_handler,
|
||||
data: Some(data),
|
||||
view_type: self.view_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, D> Button<V, D, ()> {
|
||||
fn click<H>(self, handler: H) -> Button<V, D, H>
|
||||
where
|
||||
H: 'static + ClickHandler<V, D>,
|
||||
{
|
||||
Button {
|
||||
label: self.label,
|
||||
click_handler: Some(handler),
|
||||
data: self.data,
|
||||
view_type: self.view_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, D: DialogDelegate<V>> Dialog<V, D> {
|
||||
pub fn render(&mut self, _: &mut V, _: &mut gpui::ViewContext<V>) -> AnyElement<V> {
|
||||
column()
|
||||
.child(text(self.title.clone()).text_size(lg()))
|
||||
.child(text(self.description.clone()).margins((m4(), auto())))
|
||||
.child(row().children(self.buttons.drain(..).map(|button| (button)())))
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
|
|
106
crates/gpui/playground/src/themes.rs
Normal file
106
crates/gpui/playground/src/themes.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use crate::color::{Hsla, Lerp};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
pub mod rose_pine;
|
||||
|
||||
pub struct ThemeColors {
|
||||
pub base: Range<Hsla>,
|
||||
pub surface: Range<Hsla>,
|
||||
pub overlay: Range<Hsla>,
|
||||
pub muted: Range<Hsla>,
|
||||
pub subtle: Range<Hsla>,
|
||||
pub text: Range<Hsla>,
|
||||
pub highlight_low: Range<Hsla>,
|
||||
pub highlight_med: Range<Hsla>,
|
||||
pub highlight_high: Range<Hsla>,
|
||||
pub success: Range<Hsla>,
|
||||
pub warning: Range<Hsla>,
|
||||
pub error: Range<Hsla>,
|
||||
pub inserted: Range<Hsla>,
|
||||
pub deleted: Range<Hsla>,
|
||||
pub modified: Range<Hsla>,
|
||||
}
|
||||
|
||||
impl ThemeColors {
|
||||
pub fn base(&self, level: f32) -> Hsla {
|
||||
self.base.lerp(level)
|
||||
}
|
||||
|
||||
pub fn surface(&self, level: f32) -> Hsla {
|
||||
self.surface.lerp(level)
|
||||
}
|
||||
|
||||
pub fn overlay(&self, level: f32) -> Hsla {
|
||||
self.overlay.lerp(level)
|
||||
}
|
||||
|
||||
pub fn muted(&self, level: f32) -> Hsla {
|
||||
self.muted.lerp(level)
|
||||
}
|
||||
|
||||
pub fn subtle(&self, level: f32) -> Hsla {
|
||||
self.subtle.lerp(level)
|
||||
}
|
||||
|
||||
pub fn text(&self, level: f32) -> Hsla {
|
||||
self.text.lerp(level)
|
||||
}
|
||||
|
||||
pub fn highlight_low(&self, level: f32) -> Hsla {
|
||||
self.highlight_low.lerp(level)
|
||||
}
|
||||
|
||||
pub fn highlight_med(&self, level: f32) -> Hsla {
|
||||
self.highlight_med.lerp(level)
|
||||
}
|
||||
|
||||
pub fn highlight_high(&self, level: f32) -> Hsla {
|
||||
self.highlight_high.lerp(level)
|
||||
}
|
||||
|
||||
pub fn success(&self, level: f32) -> Hsla {
|
||||
self.success.lerp(level)
|
||||
}
|
||||
|
||||
pub fn warning(&self, level: f32) -> Hsla {
|
||||
self.warning.lerp(level)
|
||||
}
|
||||
|
||||
pub fn error(&self, level: f32) -> Hsla {
|
||||
self.error.lerp(level)
|
||||
}
|
||||
|
||||
pub fn inserted(&self, level: f32) -> Hsla {
|
||||
self.inserted.lerp(level)
|
||||
}
|
||||
|
||||
pub fn deleted(&self, level: f32) -> Hsla {
|
||||
self.deleted.lerp(level)
|
||||
}
|
||||
|
||||
pub fn modified(&self, level: f32) -> Hsla {
|
||||
self.modified.lerp(level)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Entity {
|
||||
class: String,
|
||||
#[serde(rename = "type")]
|
||||
kind: String,
|
||||
id: Arc<str>,
|
||||
name: String,
|
||||
value: String,
|
||||
description: String,
|
||||
category_id: String,
|
||||
last_updated_by: String,
|
||||
last_updated: String,
|
||||
tags: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Category {
|
||||
id: String,
|
||||
label: String,
|
||||
}
|
133
crates/gpui/playground/src/themes/rose_pine.rs
Normal file
133
crates/gpui/playground/src/themes/rose_pine.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
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 }
|
||||
}
|
166
crates/gpui/playground/src/tokens.rs
Normal file
166
crates/gpui/playground/src/tokens.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
pub mod color {
|
||||
use crate::color::{scale, ColorScale, Hsla};
|
||||
|
||||
pub fn ramp(color: impl Into<Hsla>) -> ColorScale {
|
||||
let color = color.into();
|
||||
let end_color = color.desaturate(0.1).brighten(0.5);
|
||||
let start_color = color.desaturate(0.1).darken(0.4);
|
||||
scale([start_color, color, end_color])
|
||||
}
|
||||
}
|
||||
|
||||
pub mod text {
|
||||
use crate::frame::length::{rems, Rems};
|
||||
|
||||
pub fn xs() -> Rems {
|
||||
rems(0.75)
|
||||
}
|
||||
|
||||
pub fn sm() -> Rems {
|
||||
rems(0.875)
|
||||
}
|
||||
|
||||
pub fn base() -> Rems {
|
||||
rems(1.0)
|
||||
}
|
||||
|
||||
pub fn lg() -> Rems {
|
||||
rems(1.125)
|
||||
}
|
||||
|
||||
pub fn xl() -> Rems {
|
||||
rems(1.25)
|
||||
}
|
||||
|
||||
pub fn xxl() -> Rems {
|
||||
rems(1.5)
|
||||
}
|
||||
|
||||
pub fn xxxl() -> Rems {
|
||||
rems(1.875)
|
||||
}
|
||||
|
||||
pub fn xxxxl() -> Rems {
|
||||
rems(2.25)
|
||||
}
|
||||
|
||||
pub fn xxxxxl() -> Rems {
|
||||
rems(3.0)
|
||||
}
|
||||
|
||||
pub fn xxxxxxl() -> Rems {
|
||||
rems(4.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod padding {
|
||||
use crate::frame::length::{rems, Rems};
|
||||
|
||||
pub fn p1() -> Rems {
|
||||
rems(0.25)
|
||||
}
|
||||
|
||||
pub fn p2() -> Rems {
|
||||
rems(0.5)
|
||||
}
|
||||
|
||||
pub fn p3() -> Rems {
|
||||
rems(0.75)
|
||||
}
|
||||
|
||||
pub fn p4() -> Rems {
|
||||
rems(1.0)
|
||||
}
|
||||
|
||||
pub fn p5() -> Rems {
|
||||
rems(1.25)
|
||||
}
|
||||
|
||||
pub fn p6() -> Rems {
|
||||
rems(1.5)
|
||||
}
|
||||
|
||||
pub fn p8() -> Rems {
|
||||
rems(2.0)
|
||||
}
|
||||
|
||||
pub fn p10() -> Rems {
|
||||
rems(2.5)
|
||||
}
|
||||
|
||||
pub fn p12() -> Rems {
|
||||
rems(3.0)
|
||||
}
|
||||
|
||||
pub fn p16() -> Rems {
|
||||
rems(4.0)
|
||||
}
|
||||
|
||||
pub fn p20() -> Rems {
|
||||
rems(5.0)
|
||||
}
|
||||
|
||||
pub fn p24() -> Rems {
|
||||
rems(6.0)
|
||||
}
|
||||
|
||||
pub fn p32() -> Rems {
|
||||
rems(8.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod margin {
|
||||
use crate::frame::length::{rems, Rems};
|
||||
|
||||
pub fn m1() -> Rems {
|
||||
rems(0.25)
|
||||
}
|
||||
|
||||
pub fn m2() -> Rems {
|
||||
rems(0.5)
|
||||
}
|
||||
|
||||
pub fn m3() -> Rems {
|
||||
rems(0.75)
|
||||
}
|
||||
|
||||
pub fn m4() -> Rems {
|
||||
rems(1.0)
|
||||
}
|
||||
|
||||
pub fn m5() -> Rems {
|
||||
rems(1.25)
|
||||
}
|
||||
|
||||
pub fn m6() -> Rems {
|
||||
rems(1.5)
|
||||
}
|
||||
|
||||
pub fn m8() -> Rems {
|
||||
rems(2.0)
|
||||
}
|
||||
|
||||
pub fn m10() -> Rems {
|
||||
rems(2.5)
|
||||
}
|
||||
|
||||
pub fn m12() -> Rems {
|
||||
rems(3.0)
|
||||
}
|
||||
|
||||
pub fn m16() -> Rems {
|
||||
rems(4.0)
|
||||
}
|
||||
|
||||
pub fn m20() -> Rems {
|
||||
rems(5.0)
|
||||
}
|
||||
|
||||
pub fn m24() -> Rems {
|
||||
rems(6.0)
|
||||
}
|
||||
|
||||
pub fn m32() -> Rems {
|
||||
rems(8.0)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue