mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-04 10:12:47 +00:00
Checkpoint
This commit is contained in:
parent
89519b1521
commit
e762f7f3c0
21 changed files with 29 additions and 2247 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -3340,6 +3340,31 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpui3"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytemuck",
|
||||
"derive_more",
|
||||
"font-kit",
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"parking_lot 0.11.2",
|
||||
"plane-split",
|
||||
"raw-window-handle",
|
||||
"refineable",
|
||||
"rust-embed",
|
||||
"schemars",
|
||||
"serde",
|
||||
"simplelog",
|
||||
"slotmap",
|
||||
"smallvec",
|
||||
"taffy",
|
||||
"util",
|
||||
"wgpu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpui_macros"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -35,6 +35,7 @@ members = [
|
|||
"crates/gpui_macros",
|
||||
"crates/gpui2",
|
||||
"crates/gpui2_macros",
|
||||
"crates/gpui3",
|
||||
"crates/install_cli",
|
||||
"crates/journal",
|
||||
"crates/language",
|
||||
|
|
|
@ -8,6 +8,9 @@ publish = false
|
|||
name = "storybook"
|
||||
path = "src/storybook.rs"
|
||||
|
||||
[features]
|
||||
test-support = []
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bytemuck = "1.14.0"
|
||||
|
|
|
@ -1,200 +0,0 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use slotmap::SlotMap;
|
||||
use std::{any::Any, marker::PhantomData, rc::Rc};
|
||||
|
||||
use super::{
|
||||
platform::Platform,
|
||||
window::{Window, WindowHandle, WindowId},
|
||||
Context, LayoutId, Reference, View, WindowContext,
|
||||
};
|
||||
|
||||
pub struct AppContext {
|
||||
platform: Rc<dyn Platform>,
|
||||
pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any>>>,
|
||||
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
|
||||
// We recycle this memory across layout requests.
|
||||
pub(crate) layout_id_buffer: Vec<LayoutId>,
|
||||
}
|
||||
|
||||
impl AppContext {
|
||||
pub fn new(platform: Rc<dyn Platform>) -> Self {
|
||||
AppContext {
|
||||
platform,
|
||||
entities: SlotMap::with_key(),
|
||||
windows: SlotMap::with_key(),
|
||||
layout_id_buffer: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test() -> Self {
|
||||
Self::new(TestPlatform::new())
|
||||
}
|
||||
|
||||
pub fn open_window<S: 'static>(
|
||||
&mut self,
|
||||
build_root_view: impl FnOnce(&mut WindowContext) -> View<S>,
|
||||
) -> WindowHandle<S> {
|
||||
let id = self.windows.insert(None);
|
||||
|
||||
let mut window = Window::new(id);
|
||||
let root_view = build_root_view(&mut WindowContext::mutable(self, &mut window));
|
||||
window.root_view.replace(Box::new(root_view));
|
||||
|
||||
self.windows.get_mut(id).unwrap().replace(window);
|
||||
WindowHandle::new(id)
|
||||
}
|
||||
|
||||
pub(crate) fn update_window<R>(
|
||||
&mut self,
|
||||
window_id: WindowId,
|
||||
update: impl FnOnce(&mut WindowContext) -> R,
|
||||
) -> Result<R> {
|
||||
let mut window = self
|
||||
.windows
|
||||
.get_mut(window_id)
|
||||
.ok_or_else(|| anyhow!("window not found"))?
|
||||
.take()
|
||||
.unwrap();
|
||||
|
||||
let result = update(&mut WindowContext::mutable(self, &mut window));
|
||||
|
||||
self.windows
|
||||
.get_mut(window_id)
|
||||
.ok_or_else(|| anyhow!("window not found"))?
|
||||
.replace(window);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Context for AppContext {
|
||||
type EntityContext<'a, 'w, T: 'static> = ModelContext<'a, T>;
|
||||
|
||||
fn entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
||||
) -> Handle<T> {
|
||||
let id = self.entities.insert(None);
|
||||
let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
|
||||
self.entities.get_mut(id).unwrap().replace(entity);
|
||||
|
||||
Handle::new(id)
|
||||
}
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Handle<T>,
|
||||
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
||||
) -> R {
|
||||
let mut entity = self
|
||||
.entities
|
||||
.get_mut(handle.id)
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap()
|
||||
.downcast::<T>()
|
||||
.unwrap();
|
||||
|
||||
let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
|
||||
self.entities.get_mut(handle.id).unwrap().replace(entity);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ModelContext<'a, T> {
|
||||
app: Reference<'a, AppContext>,
|
||||
entity_type: PhantomData<T>,
|
||||
entity_id: EntityId,
|
||||
}
|
||||
|
||||
impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
|
||||
Self {
|
||||
app: Reference::Mutable(app),
|
||||
entity_type: PhantomData,
|
||||
entity_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
|
||||
Self {
|
||||
app: Reference::Immutable(app),
|
||||
entity_type: PhantomData,
|
||||
entity_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
|
||||
let mut entity = self
|
||||
.app
|
||||
.entities
|
||||
.get_mut(self.entity_id)
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap();
|
||||
let result = update(entity.downcast_mut::<T>().unwrap(), self);
|
||||
self.app
|
||||
.entities
|
||||
.get_mut(self.entity_id)
|
||||
.unwrap()
|
||||
.replace(entity);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'static> Context for ModelContext<'a, T> {
|
||||
type EntityContext<'b, 'c, U: 'static> = ModelContext<'b, U>;
|
||||
|
||||
fn entity<U: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
|
||||
) -> Handle<U> {
|
||||
self.app.entity(build_entity)
|
||||
}
|
||||
|
||||
fn update_entity<U: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Handle<U>,
|
||||
update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
|
||||
) -> R {
|
||||
self.app.update_entity(handle, update)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Handle<T> {
|
||||
pub(crate) id: EntityId,
|
||||
pub(crate) entity_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
slotmap::new_key_type! { pub struct EntityId; }
|
||||
|
||||
impl<T: 'static> Handle<T> {
|
||||
fn new(id: EntityId) -> Self {
|
||||
Self {
|
||||
id,
|
||||
entity_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the entity referenced by this handle with the given function.
|
||||
///
|
||||
/// The update function receives a context appropriate for its environment.
|
||||
/// When updating in an `AppContext`, it receives a `ModelContext`.
|
||||
/// When updating an a `WindowContext`, it receives a `ViewContext`.
|
||||
pub fn update<C: Context, R>(
|
||||
&self,
|
||||
cx: &mut C,
|
||||
update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
|
||||
) -> R {
|
||||
cx.update_entity(self, update)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Handle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
id: self.id,
|
||||
entity_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use serde::de::{self, Deserialize, Deserializer, Visitor};
|
||||
use std::fmt;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
struct RgbaVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for RgbaVisitor {
|
||||
type Value = Rgba;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a string in the format #rrggbb or #rrggbbaa")
|
||||
}
|
||||
|
||||
fn visit_str<E: de::Error>(self, value: &str) -> Result<Rgba, E> {
|
||||
if value.len() == 7 || value.len() == 9 {
|
||||
let r = u8::from_str_radix(&value[1..3], 16).unwrap() as f32 / 255.0;
|
||||
let g = u8::from_str_radix(&value[3..5], 16).unwrap() as f32 / 255.0;
|
||||
let b = u8::from_str_radix(&value[5..7], 16).unwrap() as f32 / 255.0;
|
||||
let a = if value.len() == 9 {
|
||||
u8::from_str_radix(&value[7..9], 16).unwrap() as f32 / 255.0
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
Ok(Rgba { r, g, b, a })
|
||||
} else {
|
||||
Err(E::custom(
|
||||
"Bad format for RGBA. Expected #rrggbb or #rrggbbaa.",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Rgba {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
deserializer.deserialize_str(RgbaVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Hsla {
|
||||
pub h: f32,
|
||||
pub s: f32,
|
||||
pub l: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for Hsla {}
|
||||
unsafe impl Pod for Hsla {}
|
||||
|
||||
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.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn black() -> Hsla {
|
||||
Hsla {
|
||||
h: 0.,
|
||||
s: 0.,
|
||||
l: 0.,
|
||||
a: 1.,
|
||||
}
|
||||
}
|
||||
|
||||
impl Hsla {
|
||||
/// Returns true if the HSLA color is fully transparent, false otherwise.
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
self.a == 0.0
|
||||
}
|
||||
}
|
||||
|
||||
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<'de> Deserialize<'de> for Hsla {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
// First, deserialize it into Rgba
|
||||
let rgba = Rgba::deserialize(deserializer)?;
|
||||
|
||||
// Then, use the From<Rgba> for Hsla implementation to convert it
|
||||
Ok(Hsla::from(rgba))
|
||||
}
|
||||
}
|
|
@ -1,275 +0,0 @@
|
|||
use super::{Handle, Layout, LayoutId, Pixels, Point, Result, ViewContext, WindowContext};
|
||||
use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
|
||||
|
||||
pub trait Element: 'static {
|
||||
type State;
|
||||
type FrameState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
state: &mut Self::State,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<(LayoutId, Self::FrameState)>;
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
layout: Layout,
|
||||
state: &mut Self::State,
|
||||
frame_state: &mut Self::FrameState,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait ParentElement<S> {
|
||||
fn child(self, child: impl IntoAnyElement<S>) -> Self;
|
||||
}
|
||||
|
||||
trait ElementObject<S> {
|
||||
fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId>;
|
||||
fn paint(
|
||||
&mut self,
|
||||
parent_origin: super::Point<Pixels>,
|
||||
state: &mut S,
|
||||
cx: &mut ViewContext<S>,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
struct RenderedElement<E: Element> {
|
||||
element: E,
|
||||
phase: ElementRenderPhase<E::FrameState>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum ElementRenderPhase<S> {
|
||||
#[default]
|
||||
Rendered,
|
||||
LayoutRequested {
|
||||
layout_id: LayoutId,
|
||||
frame_state: S,
|
||||
},
|
||||
Painted {
|
||||
layout: Layout,
|
||||
frame_state: S,
|
||||
},
|
||||
}
|
||||
|
||||
/// Internal struct that wraps an element to store Layout and FrameState after the element is rendered.
|
||||
/// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
|
||||
/// improved usability.
|
||||
impl<E: Element> RenderedElement<E> {
|
||||
fn new(element: E) -> Self {
|
||||
RenderedElement {
|
||||
element,
|
||||
phase: ElementRenderPhase::Rendered,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Element> ElementObject<E::State> for RenderedElement<E> {
|
||||
fn layout(&mut self, state: &mut E::State, cx: &mut ViewContext<E::State>) -> Result<LayoutId> {
|
||||
let (layout_id, frame_state) = self.element.layout(state, cx)?;
|
||||
self.phase = ElementRenderPhase::LayoutRequested {
|
||||
layout_id,
|
||||
frame_state,
|
||||
};
|
||||
Ok(layout_id)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
parent_origin: Point<Pixels>,
|
||||
state: &mut E::State,
|
||||
cx: &mut ViewContext<E::State>,
|
||||
) -> Result<()> {
|
||||
self.phase = match std::mem::take(&mut self.phase) {
|
||||
ElementRenderPhase::Rendered => panic!("must call layout before paint"),
|
||||
|
||||
ElementRenderPhase::LayoutRequested {
|
||||
layout_id,
|
||||
mut frame_state,
|
||||
} => {
|
||||
let mut layout = cx.layout(layout_id)?;
|
||||
layout.bounds.origin += parent_origin;
|
||||
self.element
|
||||
.paint(layout.clone(), state, &mut frame_state, cx)?;
|
||||
ElementRenderPhase::Painted {
|
||||
layout,
|
||||
frame_state,
|
||||
}
|
||||
}
|
||||
|
||||
ElementRenderPhase::Painted {
|
||||
layout,
|
||||
mut frame_state,
|
||||
} => {
|
||||
self.element
|
||||
.paint(layout.clone(), state, &mut frame_state, cx)?;
|
||||
ElementRenderPhase::Painted {
|
||||
layout,
|
||||
frame_state,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyElement<S>(Box<dyn ElementObject<S>>);
|
||||
|
||||
impl<S> AnyElement<S> {
|
||||
pub fn layout(&mut self, state: &mut S, cx: &mut ViewContext<S>) -> Result<LayoutId> {
|
||||
self.0.layout(state, cx)
|
||||
}
|
||||
|
||||
pub fn paint(
|
||||
&mut self,
|
||||
parent_origin: Point<Pixels>,
|
||||
state: &mut S,
|
||||
cx: &mut ViewContext<S>,
|
||||
) -> Result<()> {
|
||||
self.0.paint(parent_origin, state, cx)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoAnyElement<S> {
|
||||
fn into_any(self) -> AnyElement<S>;
|
||||
}
|
||||
|
||||
impl<E: Element> IntoAnyElement<E::State> for E {
|
||||
fn into_any(self) -> AnyElement<E::State> {
|
||||
AnyElement(Box::new(RenderedElement::new(self)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> IntoAnyElement<S> for AnyElement<S> {
|
||||
fn into_any(self) -> AnyElement<S> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct View<S> {
|
||||
state: Handle<S>,
|
||||
render: Rc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S>>,
|
||||
}
|
||||
|
||||
pub fn view<S: 'static, E: Element<State = S>>(
|
||||
state: Handle<S>,
|
||||
render: impl 'static + Fn(&mut S, &mut ViewContext<S>) -> E,
|
||||
) -> View<S> {
|
||||
View {
|
||||
state,
|
||||
render: Rc::new(move |state, cx| render(state, cx).into_any()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> View<S> {
|
||||
pub fn into_any<ParentState>(self) -> AnyView<ParentState> {
|
||||
AnyView {
|
||||
view: Rc::new(RefCell::new(self)),
|
||||
parent_state_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> Element for View<S> {
|
||||
type State = ();
|
||||
type FrameState = AnyElement<S>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_: &mut Self::State,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<(LayoutId, Self::FrameState)> {
|
||||
self.state.update(cx, |state, cx| {
|
||||
let mut element = (self.render)(state, cx);
|
||||
let layout_id = element.layout(state, cx)?;
|
||||
Ok((layout_id, element))
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
layout: Layout,
|
||||
_: &mut Self::State,
|
||||
element: &mut Self::FrameState,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<()> {
|
||||
self.state.update(cx, |state, cx| {
|
||||
element.paint(layout.bounds.origin, state, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
trait ViewObject {
|
||||
fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)>;
|
||||
fn paint(
|
||||
&mut self,
|
||||
layout: Layout,
|
||||
element: &mut dyn Any,
|
||||
cx: &mut WindowContext,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
impl<S: 'static> ViewObject for View<S> {
|
||||
fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)> {
|
||||
self.state.update(cx, |state, cx| {
|
||||
let mut element = (self.render)(state, cx);
|
||||
let layout_id = element.layout(state, cx)?;
|
||||
let element = Box::new(element) as Box<dyn Any>;
|
||||
Ok((layout_id, element))
|
||||
})
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
layout: Layout,
|
||||
element: &mut dyn Any,
|
||||
cx: &mut WindowContext,
|
||||
) -> Result<()> {
|
||||
self.state.update(cx, |state, cx| {
|
||||
element
|
||||
.downcast_mut::<AnyElement<S>>()
|
||||
.unwrap()
|
||||
.paint(layout.bounds.origin, state, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyView<S> {
|
||||
view: Rc<RefCell<dyn ViewObject>>,
|
||||
parent_state_type: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S: 'static> Element for AnyView<S> {
|
||||
type State = S;
|
||||
type FrameState = Box<dyn Any>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_: &mut Self::State,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<(LayoutId, Self::FrameState)> {
|
||||
self.view.borrow_mut().layout(cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
layout: Layout,
|
||||
_: &mut Self::State,
|
||||
element: &mut Self::FrameState,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<()> {
|
||||
self.view.borrow_mut().paint(layout, element, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Clone for AnyView<S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
view: self.view.clone(),
|
||||
parent_state_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
pub mod div;
|
||||
pub mod editor;
|
||||
|
||||
use super::*;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use div::div;
|
||||
pub use editor::field;
|
|
@ -1,38 +0,0 @@
|
|||
use super::{
|
||||
Element, IntoAnyElement, Layout, LayoutId, ParentElement, PhantomData, Result, ViewContext,
|
||||
};
|
||||
|
||||
pub struct Div<S>(PhantomData<S>);
|
||||
|
||||
impl<S: 'static> Element for Div<S> {
|
||||
type State = S;
|
||||
type FrameState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
state: &mut Self::State,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<(LayoutId, Self::FrameState)> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
layout: Layout,
|
||||
state: &mut Self::State,
|
||||
frame_state: &mut Self::FrameState,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> ParentElement<S> for Div<S> {
|
||||
fn child(self, child: impl IntoAnyElement<S>) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn div<S>() -> Div<S> {
|
||||
todo!()
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
use super::{Element, Handle, Layout, LayoutId, Result, SharedString, ViewContext};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub fn field<S>(editor: Handle<Editor>) -> EditorElement<S> {
|
||||
EditorElement {
|
||||
editor,
|
||||
field: true,
|
||||
placeholder_text: None,
|
||||
parent_state: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EditorElement<S> {
|
||||
editor: Handle<Editor>,
|
||||
field: bool,
|
||||
placeholder_text: Option<SharedString>,
|
||||
parent_state: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S> EditorElement<S> {
|
||||
pub fn field(mut self) -> Self {
|
||||
self.field = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn placeholder_text(mut self, text: impl Into<SharedString>) -> Self {
|
||||
self.placeholder_text = Some(text.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> Element for EditorElement<S> {
|
||||
type State = S;
|
||||
type FrameState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_: &mut Self::State,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<(LayoutId, Self::FrameState)> {
|
||||
self.editor.update(cx, |editor, cx| todo!())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
layout: Layout,
|
||||
state: &mut Self::State,
|
||||
frame_state: &mut Self::FrameState,
|
||||
cx: &mut ViewContext<Self::State>,
|
||||
) -> Result<()> {
|
||||
self.editor.update(cx, |editor, cx| todo!())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Editor {}
|
||||
|
||||
impl Editor {
|
||||
pub fn new(_: &mut ViewContext<Self>) -> Self {
|
||||
Editor {}
|
||||
}
|
||||
}
|
|
@ -1,325 +0,0 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
use core::fmt::Debug;
|
||||
use derive_more::{Add, AddAssign, Div, Mul, Sub};
|
||||
use refineable::Refineable;
|
||||
use std::ops::Mul;
|
||||
|
||||
#[derive(Refineable, Default, Add, AddAssign, Sub, Mul, Div, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[refineable(debug)]
|
||||
#[repr(C)]
|
||||
pub struct Point<T: Clone + Debug> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug> Point<T> {
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug> Clone for Point<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Point<T> {}
|
||||
|
||||
unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Point<T> {}
|
||||
|
||||
#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq)]
|
||||
#[refineable(debug)]
|
||||
pub struct Size<T: Clone + Debug> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
}
|
||||
|
||||
impl Size<Length> {
|
||||
pub fn full() -> Self {
|
||||
Self {
|
||||
width: relative(1.),
|
||||
height: relative(1.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Size<DefiniteLength> {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
width: px(0.),
|
||||
height: px(0.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Size<Length> {
|
||||
pub fn auto() -> Self {
|
||||
Self {
|
||||
width: Length::Auto,
|
||||
height: Length::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Refineable, Clone, Default, Debug, PartialEq)]
|
||||
#[refineable(debug)]
|
||||
pub struct Bounds<T: Clone + Debug> {
|
||||
pub origin: Point<T>,
|
||||
pub size: Size<T>,
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug + Copy> Copy for Bounds<T> {}
|
||||
|
||||
#[derive(Refineable, Clone, Default, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct Edges<T: Clone + Debug> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
||||
|
||||
impl Edges<Length> {
|
||||
pub fn auto() -> Self {
|
||||
Self {
|
||||
top: Length::Auto,
|
||||
right: Length::Auto,
|
||||
bottom: Length::Auto,
|
||||
left: Length::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
top: px(0.),
|
||||
right: px(0.),
|
||||
bottom: px(0.),
|
||||
left: px(0.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edges<DefiniteLength> {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
top: px(0.),
|
||||
right: px(0.),
|
||||
bottom: px(0.),
|
||||
left: px(0.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edges<AbsoluteLength> {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
top: px(0.),
|
||||
right: px(0.),
|
||||
bottom: px(0.),
|
||||
left: px(0.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_pixels(&self, rem_size: Pixels) -> Edges<Pixels> {
|
||||
Edges {
|
||||
top: self.top.to_pixels(rem_size),
|
||||
right: self.right.to_pixels(rem_size),
|
||||
bottom: self.bottom.to_pixels(rem_size),
|
||||
left: self.left.to_pixels(rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edges<Pixels> {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.top == px(0.) && self.right == px(0.) && self.bottom == px(0.) && self.left == px(0.)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Add, AddAssign, Sub, Mul, Div, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Pixels(pub(crate) f32);
|
||||
|
||||
unsafe impl bytemuck::Pod for Pixels {}
|
||||
unsafe impl bytemuck::Zeroable for Pixels {}
|
||||
|
||||
impl Debug for Pixels {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} px", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pixels> for f32 {
|
||||
fn from(pixels: Pixels) -> Self {
|
||||
pixels.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Pixels> for f32 {
|
||||
fn from(pixels: &Pixels) -> Self {
|
||||
pixels.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Add, Sub, Mul, Div)]
|
||||
pub struct Rems(f32);
|
||||
|
||||
impl Mul<Pixels> for Rems {
|
||||
type Output = Pixels;
|
||||
|
||||
fn mul(self, other: Pixels) -> Pixels {
|
||||
Pixels(self.0 * other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Rems {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} rem", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum AbsoluteLength {
|
||||
Pixels(Pixels),
|
||||
Rems(Rems),
|
||||
}
|
||||
|
||||
impl From<Pixels> for AbsoluteLength {
|
||||
fn from(pixels: Pixels) -> Self {
|
||||
AbsoluteLength::Pixels(pixels)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rems> for AbsoluteLength {
|
||||
fn from(rems: Rems) -> Self {
|
||||
AbsoluteLength::Rems(rems)
|
||||
}
|
||||
}
|
||||
|
||||
impl AbsoluteLength {
|
||||
pub fn to_pixels(&self, rem_size: Pixels) -> Pixels {
|
||||
match self {
|
||||
AbsoluteLength::Pixels(pixels) => *pixels,
|
||||
AbsoluteLength::Rems(rems) => *rems * rem_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AbsoluteLength {
|
||||
fn default() -> Self {
|
||||
px(0.)
|
||||
}
|
||||
}
|
||||
|
||||
/// A non-auto length that can be defined in pixels, rems, or percent of parent.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DefiniteLength {
|
||||
Absolute(AbsoluteLength),
|
||||
/// A fraction of the parent's size between 0 and 1.
|
||||
Fraction(f32),
|
||||
}
|
||||
|
||||
impl Debug for DefiniteLength {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DefiniteLength::Absolute(length) => Debug::fmt(length, f),
|
||||
DefiniteLength::Fraction(fract) => write!(f, "{}%", (fract * 100.0) as i32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pixels> for DefiniteLength {
|
||||
fn from(pixels: Pixels) -> Self {
|
||||
Self::Absolute(pixels.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rems> for DefiniteLength {
|
||||
fn from(rems: Rems) -> Self {
|
||||
Self::Absolute(rems.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AbsoluteLength> for DefiniteLength {
|
||||
fn from(length: AbsoluteLength) -> Self {
|
||||
Self::Absolute(length)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DefiniteLength {
|
||||
fn default() -> Self {
|
||||
Self::Absolute(AbsoluteLength::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// A length that can be defined in pixels, rems, percent of parent, or auto.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Length {
|
||||
Definite(DefiniteLength),
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl Debug for Length {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Length::Definite(definite_length) => write!(f, "{:?}", definite_length),
|
||||
Length::Auto => write!(f, "auto"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
|
||||
DefiniteLength::Fraction(fraction).into()
|
||||
}
|
||||
|
||||
pub fn rems<T: From<Rems>>(rems: f32) -> T {
|
||||
Rems(rems).into()
|
||||
}
|
||||
|
||||
pub fn px<T: From<Pixels>>(pixels: f32) -> T {
|
||||
Pixels(pixels).into()
|
||||
}
|
||||
|
||||
pub fn auto() -> Length {
|
||||
Length::Auto
|
||||
}
|
||||
|
||||
impl From<Pixels> for Length {
|
||||
fn from(pixels: Pixels) -> Self {
|
||||
Self::Definite(pixels.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rems> for Length {
|
||||
fn from(rems: Rems) -> Self {
|
||||
Self::Definite(rems.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DefiniteLength> for Length {
|
||||
fn from(length: DefiniteLength) -> Self {
|
||||
Self::Definite(length)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AbsoluteLength> for Length {
|
||||
fn from(length: AbsoluteLength) -> Self {
|
||||
Self::Definite(length.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Length {
|
||||
fn default() -> Self {
|
||||
Self::Definite(DefiniteLength::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for Length {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Definite(DefiniteLength::default())
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
mod app;
|
||||
mod color;
|
||||
mod element;
|
||||
mod elements;
|
||||
mod geometry;
|
||||
mod platform;
|
||||
mod renderer;
|
||||
mod scene;
|
||||
mod style;
|
||||
mod taffy;
|
||||
mod window;
|
||||
|
||||
use self::editor::Editor;
|
||||
use anyhow::Result;
|
||||
pub use app::*;
|
||||
pub use color::*;
|
||||
pub use element::*;
|
||||
pub use elements::*;
|
||||
pub use geometry::*;
|
||||
pub use gpui2::ArcCow;
|
||||
use gpui2::Reference;
|
||||
pub use scene::*;
|
||||
pub use style::*;
|
||||
pub use taffy::LayoutId;
|
||||
use taffy::TaffyLayoutEngine;
|
||||
pub use window::*;
|
||||
|
||||
pub trait Context {
|
||||
type EntityContext<'a, 'w, T: 'static>;
|
||||
|
||||
fn entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
||||
) -> Handle<T>;
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Handle<T>,
|
||||
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
||||
) -> R;
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct SharedString(ArcCow<'static, str>);
|
||||
|
||||
impl std::fmt::Debug for SharedString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
|
||||
fn from(value: T) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
struct Workspace {
|
||||
left_panel: AnyView<Self>,
|
||||
}
|
||||
|
||||
fn workspace(cx: &mut WindowContext) -> View<Workspace> {
|
||||
let workspace = cx.entity(|cx| Workspace {
|
||||
left_panel: collab_panel(cx).into_any(),
|
||||
});
|
||||
view(workspace, |workspace, cx| {
|
||||
div().child(workspace.left_panel.clone())
|
||||
})
|
||||
}
|
||||
|
||||
struct CollabPanel {
|
||||
filter_editor: Handle<editor::Editor>,
|
||||
}
|
||||
|
||||
fn collab_panel(cx: &mut WindowContext) -> View<CollabPanel> {
|
||||
let panel = cx.entity(|cx| CollabPanel::new(cx));
|
||||
view(panel, |panel, cx| {
|
||||
div()
|
||||
.child(div())
|
||||
.child(field(panel.filter_editor.clone()).placeholder_text("Search channels, contacts"))
|
||||
})
|
||||
}
|
||||
|
||||
impl CollabPanel {
|
||||
fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||
Self {
|
||||
filter_editor: cx.entity(|cx| Editor::new(cx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut cx = AppContext::new();
|
||||
cx.open_window(|cx| workspace(cx));
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
mod test;
|
||||
use super::{AnyWindowHandle, Bounds, Point};
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
|
||||
pub trait Platform {
|
||||
fn open_window(
|
||||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowOptions,
|
||||
) -> Box<dyn PlatformWindow>;
|
||||
}
|
||||
|
||||
pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WindowOptions<'a> {
|
||||
pub bounds: WindowBounds,
|
||||
pub titlebar: Option<TitlebarOptions<'a>>,
|
||||
pub center: bool,
|
||||
pub focus: bool,
|
||||
pub show: bool,
|
||||
pub kind: WindowKind,
|
||||
pub is_movable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TitlebarOptions<'a> {
|
||||
pub title: Option<&'a str>,
|
||||
pub appears_transparent: bool,
|
||||
pub traffic_light_position: Option<Point<f32>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Appearance {
|
||||
Light,
|
||||
VibrantLight,
|
||||
Dark,
|
||||
VibrantDark,
|
||||
}
|
||||
|
||||
impl Default for Appearance {
|
||||
fn default() -> Self {
|
||||
Self::Light
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum WindowKind {
|
||||
Normal,
|
||||
PopUp,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum WindowBounds {
|
||||
Fullscreen,
|
||||
Maximized,
|
||||
Fixed(Bounds<f32>),
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
|
||||
use super::{Scene, Size};
|
||||
|
||||
pub struct Renderer {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
surface: wgpu::Surface,
|
||||
surface_config: wgpu::SurfaceConfiguration,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
vertex_buffer: wgpu::Buffer,
|
||||
vertex_count: u32,
|
||||
}
|
||||
|
||||
pub trait Window: HasRawWindowHandle + HasRawDisplayHandle {
|
||||
fn inner_size(&self) -> Size<u32>;
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub async fn new<W>(window: &W) -> Self
|
||||
where
|
||||
W: Window,
|
||||
{
|
||||
let instance = wgpu::Instance::new(Default::default());
|
||||
let surface = unsafe { instance.create_surface(window).unwrap() };
|
||||
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions::default())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor::default(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width: window.inner_size().width,
|
||||
height: window.inner_size().height,
|
||||
|
||||
// "FIFO" mode renders frames in queue synced with the display's refresh rate.
|
||||
// Avoids screen tearing but may not offer the lowest latency. Ideal when image
|
||||
// quality takes priority over input latency.
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
|
||||
// Use the Premultiplied alpha mode. With premultiplication, the color components
|
||||
// are multiplied by the alpha value before storage or blending, meaning calculations
|
||||
// with colors already factor in the influence of alpha. This typically results
|
||||
// in better performance and avoids a separate multiplication operation during blending.
|
||||
alpha_mode: wgpu::CompositeAlphaMode::PreMultiplied,
|
||||
|
||||
// Specify the color formats for the views the surface can have.
|
||||
// In this case, the format is BGRA (blue, green, red, alpha) with unsigned
|
||||
// normalised integers in the 8-bit range and the color space is sRGB (standard RGB).
|
||||
// sRGB is the standard color space for displaying images and video on digital displays,
|
||||
// as it optimises color accuracy and consistency.
|
||||
view_formats: vec![wgpu::TextureFormat::Bgra8UnormSrgb],
|
||||
};
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Vertex Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.vert.wgsl").into()),
|
||||
});
|
||||
|
||||
let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Fragment Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.frag.wgsl").into()),
|
||||
});
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Render Pipeline Layout"),
|
||||
bind_group_layouts: &[],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
size: 0,
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Render Pipeline"),
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &vs_module,
|
||||
entry_point: "main",
|
||||
buffers: &[],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
Self {
|
||||
device,
|
||||
queue,
|
||||
surface,
|
||||
surface_config,
|
||||
pipeline,
|
||||
vertex_buffer,
|
||||
vertex_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self, scene: &Scene) {
|
||||
let frame = self.surface.get_current_texture().unwrap();
|
||||
let view = frame
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
self.queue.write_buffer(
|
||||
&self.vertex_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&scene.opaque_primitives().quads),
|
||||
);
|
||||
self.vertex_count = scene.opaque_primitives().quads.len() as u32;
|
||||
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Render Encoder"),
|
||||
});
|
||||
|
||||
{
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
render_pass.draw(0..self.vertex_count, 0..1);
|
||||
}
|
||||
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
use super::{Bounds, Hsla, Pixels, Point};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use plane_split::BspSplitter;
|
||||
|
||||
pub struct Scene {
|
||||
opaque_primitives: PrimitiveBatch,
|
||||
transparent_primitives: slotmap::SlotMap<slotmap::DefaultKey, Primitive>,
|
||||
splitter: BspSplitter<slotmap::DefaultKey>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new() -> Scene {
|
||||
Scene {
|
||||
opaque_primitives: PrimitiveBatch::default(),
|
||||
transparent_primitives: slotmap::SlotMap::new(),
|
||||
splitter: BspSplitter::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, primitive: impl Into<Primitive>, is_transparent: bool) {
|
||||
if is_transparent {
|
||||
self.transparent_primitives.insert(primitive.into());
|
||||
} else {
|
||||
match primitive.into() {
|
||||
Primitive::Quad(quad) => self.opaque_primitives.quads.push(quad),
|
||||
Primitive::Glyph(glyph) => self.opaque_primitives.glyphs.push(glyph),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn opaque_primitives(&self) -> &PrimitiveBatch {
|
||||
&self.opaque_primitives
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Primitive {
|
||||
Quad(Quad),
|
||||
Glyph(Glyph),
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
match self {
|
||||
Primitive::Quad(quad) => {
|
||||
quad.background.is_transparent() && quad.border_color.is_transparent()
|
||||
}
|
||||
Primitive::Glyph(glyph) => glyph.color.is_transparent(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PrimitiveBatch {
|
||||
pub quads: Vec<Quad>,
|
||||
pub glyphs: Vec<Glyph>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Quad {
|
||||
pub order: f32,
|
||||
pub bounds: Bounds<Pixels>,
|
||||
pub background: Hsla,
|
||||
pub border_color: Hsla,
|
||||
pub corner_radius: Pixels,
|
||||
pub border_left: Pixels,
|
||||
pub border_right: Pixels,
|
||||
pub border_top: Pixels,
|
||||
pub border_bottom: Pixels,
|
||||
}
|
||||
|
||||
impl Quad {
|
||||
pub fn vertices(&self) -> impl Iterator<Item = Point<Pixels>> {
|
||||
let x1 = self.bounds.origin.x;
|
||||
let y1 = self.bounds.origin.y;
|
||||
let x2 = x1 + self.bounds.size.width;
|
||||
let y2 = y1 + self.bounds.size.height;
|
||||
[
|
||||
Point::new(x1, y1),
|
||||
Point::new(x2, y1),
|
||||
Point::new(x2, y2),
|
||||
Point::new(x1, y2),
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for Quad {}
|
||||
|
||||
unsafe impl Pod for Quad {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Glyph {
|
||||
pub order: f32,
|
||||
pub bounds: Bounds<Pixels>,
|
||||
pub color: Hsla,
|
||||
// ...
|
||||
}
|
||||
|
||||
impl From<Quad> for Primitive {
|
||||
fn from(quad: Quad) -> Self {
|
||||
Primitive::Quad(quad)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Glyph> for Primitive {
|
||||
fn from(glyph: Glyph) -> Self {
|
||||
Primitive::Glyph(glyph)
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,245 +0,0 @@
|
|||
use refineable::Refineable;
|
||||
|
||||
pub use super::taffy::style::{
|
||||
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
|
||||
Overflow, Position,
|
||||
};
|
||||
use super::{
|
||||
rems, AbsoluteLength, Bounds, DefiniteLength, Edges, EdgesRefinement, Hsla, Length, Pixels,
|
||||
Point, PointRefinement, Rems, SharedString, Size, SizeRefinement, ViewContext, WindowContext,
|
||||
};
|
||||
pub use gpui2::style::{FontStyle, FontWeight};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FontSize(f32);
|
||||
|
||||
#[derive(Clone, Refineable, Debug)]
|
||||
#[refineable(debug)]
|
||||
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_widths: Edges<AbsoluteLength>,
|
||||
|
||||
// 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 border color of this element
|
||||
pub border_color: Option<Hsla>,
|
||||
|
||||
/// 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>,
|
||||
|
||||
/// The font size in rems.
|
||||
pub font_size: Option<Rems>,
|
||||
|
||||
pub font_family: Option<SharedString>,
|
||||
|
||||
pub font_weight: Option<FontWeight>,
|
||||
|
||||
pub font_style: Option<FontStyle>,
|
||||
}
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct TextStyle {
|
||||
pub color: Hsla,
|
||||
pub font_family: SharedString,
|
||||
pub font_size: Rems,
|
||||
pub font_weight: FontWeight,
|
||||
pub font_style: FontStyle,
|
||||
pub underline: Underline,
|
||||
}
|
||||
|
||||
#[derive(Refineable, Clone, Default, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct Underline {
|
||||
pub origin: Point<Pixels>,
|
||||
pub width: Pixels,
|
||||
pub thickness: Pixels,
|
||||
pub color: Hsla,
|
||||
pub squiggly: bool,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn text_style(&self, cx: &WindowContext) -> Option<TextStyleRefinement> {
|
||||
if self.text_color.is_none()
|
||||
&& self.font_size.is_none()
|
||||
&& self.font_family.is_none()
|
||||
&& self.font_weight.is_none()
|
||||
&& self.font_style.is_none()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(TextStyleRefinement {
|
||||
color: self.text_color,
|
||||
font_family: self.font_family.clone(),
|
||||
font_size: self.font_size,
|
||||
font_weight: self.font_weight,
|
||||
font_style: self.font_style,
|
||||
underline: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Paints the background of an element styled with this style.
|
||||
pub fn paint_background<V: 'static>(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
|
||||
let rem_size = cx.rem_size();
|
||||
if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
/// Paints the foreground of an element styled with this style.
|
||||
pub fn paint_foreground<V: 'static>(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
|
||||
let rem_size = cx.rem_size();
|
||||
|
||||
if let Some(color) = self.border_color {
|
||||
let border = self.border_widths.to_pixels(rem_size);
|
||||
if !border.is_empty() {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_widths: Edges::<AbsoluteLength>::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,
|
||||
border_color: None,
|
||||
corner_radii: CornerRadii::default(),
|
||||
text_color: None,
|
||||
font_size: Some(rems(1.)),
|
||||
font_family: None,
|
||||
font_weight: None,
|
||||
font_style: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct CornerRadii {
|
||||
top_left: AbsoluteLength,
|
||||
top_right: AbsoluteLength,
|
||||
bottom_left: AbsoluteLength,
|
||||
bottom_right: AbsoluteLength,
|
||||
}
|
|
@ -1,238 +0,0 @@
|
|||
use super::{
|
||||
AbsoluteLength, Bounds, DefiniteLength, Edges, Layout, Length, Pixels, Point, Result, Size,
|
||||
Style,
|
||||
};
|
||||
use gpui2::taffy::{self, Taffy};
|
||||
use std::fmt::Debug;
|
||||
pub use taffy::*;
|
||||
|
||||
pub use gpui2::taffy::tree::NodeId as LayoutId;
|
||||
pub struct TaffyLayoutEngine(Taffy);
|
||||
|
||||
impl TaffyLayoutEngine {
|
||||
pub fn new() -> Self {
|
||||
TaffyLayoutEngine(Taffy::new())
|
||||
}
|
||||
|
||||
pub fn request_layout(
|
||||
&mut self,
|
||||
style: Style,
|
||||
rem_size: Pixels,
|
||||
children: &[LayoutId],
|
||||
) -> Result<LayoutId> {
|
||||
let style = style.to_taffy(rem_size);
|
||||
if children.is_empty() {
|
||||
Ok(self.0.new_leaf(style)?)
|
||||
} else {
|
||||
Ok(self.0.new_with_children(style, children)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layout(&mut self, id: LayoutId) -> Result<Layout> {
|
||||
Ok(self.0.layout(id).map(Into::into)?)
|
||||
}
|
||||
}
|
||||
|
||||
trait ToTaffy<Output> {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> Output;
|
||||
}
|
||||
|
||||
impl ToTaffy<taffy::style::Style> for Style {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> 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_widths.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),
|
||||
flex_grow: self.flex_grow,
|
||||
flex_shrink: self.flex_shrink,
|
||||
..Default::default() // Ignore grid properties for now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl ToTaffy for Bounds<Length> {
|
||||
// type Output = taffy::prelude::Bounds<taffy::prelude::LengthPercentageAuto>;
|
||||
|
||||
// fn to_taffy(
|
||||
// &self,
|
||||
// rem_size: Pixels,
|
||||
// ) -> taffy::prelude::Bounds<taffy::prelude::LengthPercentageAuto> {
|
||||
// taffy::prelude::Bounds {
|
||||
// origin: self.origin.to_taffy(rem_size),
|
||||
// size: self.size.to_taffy(rem_size),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ToTaffy<taffy::style::LengthPercentageAuto> for Length {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto {
|
||||
match self {
|
||||
Length::Definite(length) => length.to_taffy(rem_size),
|
||||
Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTaffy<taffy::style::Dimension> for Length {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::Dimension {
|
||||
match self {
|
||||
Length::Definite(length) => length.to_taffy(rem_size),
|
||||
Length::Auto => taffy::prelude::Dimension::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTaffy<taffy::style::LengthPercentage> for DefiniteLength {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage {
|
||||
match self {
|
||||
DefiniteLength::Absolute(length) => match length {
|
||||
AbsoluteLength::Pixels(pixels) => {
|
||||
taffy::style::LengthPercentage::Length(pixels.into())
|
||||
}
|
||||
AbsoluteLength::Rems(rems) => {
|
||||
taffy::style::LengthPercentage::Length((*rems * rem_size).into())
|
||||
}
|
||||
},
|
||||
DefiniteLength::Fraction(fraction) => {
|
||||
taffy::style::LengthPercentage::Percent(*fraction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTaffy<taffy::style::LengthPercentageAuto> for DefiniteLength {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentageAuto {
|
||||
match self {
|
||||
DefiniteLength::Absolute(length) => match length {
|
||||
AbsoluteLength::Pixels(pixels) => {
|
||||
taffy::style::LengthPercentageAuto::Length(pixels.into())
|
||||
}
|
||||
AbsoluteLength::Rems(rems) => {
|
||||
taffy::style::LengthPercentageAuto::Length((*rems * rem_size).into())
|
||||
}
|
||||
},
|
||||
DefiniteLength::Fraction(fraction) => {
|
||||
taffy::style::LengthPercentageAuto::Percent(*fraction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTaffy<taffy::style::Dimension> for DefiniteLength {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Dimension {
|
||||
match self {
|
||||
DefiniteLength::Absolute(length) => match length {
|
||||
AbsoluteLength::Pixels(pixels) => taffy::style::Dimension::Length(pixels.into()),
|
||||
AbsoluteLength::Rems(rems) => {
|
||||
taffy::style::Dimension::Length((*rems * rem_size).into())
|
||||
}
|
||||
},
|
||||
DefiniteLength::Fraction(fraction) => taffy::style::Dimension::Percent(*fraction),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTaffy<taffy::style::LengthPercentage> for AbsoluteLength {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage {
|
||||
match self {
|
||||
AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(pixels.into()),
|
||||
AbsoluteLength::Rems(rems) => {
|
||||
taffy::style::LengthPercentage::Length((*rems * rem_size).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, T2: Clone + Debug> From<taffy::geometry::Point<T>> for Point<T2>
|
||||
where
|
||||
T: Into<T2>,
|
||||
{
|
||||
fn from(point: taffy::geometry::Point<T>) -> Point<T2> {
|
||||
Point {
|
||||
x: point.x.into(),
|
||||
y: point.y.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug, T2> Into<taffy::geometry::Point<T2>> for Point<T>
|
||||
where
|
||||
T: Into<T2>,
|
||||
{
|
||||
fn into(self) -> taffy::geometry::Point<T2> {
|
||||
taffy::geometry::Point {
|
||||
x: self.x.into(),
|
||||
y: self.y.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToTaffy<U> + Clone + Debug, U> ToTaffy<taffy::geometry::Size<U>> for Size<T> {
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::geometry::Size<U> {
|
||||
taffy::geometry::Size {
|
||||
width: self.width.to_taffy(rem_size).into(),
|
||||
height: self.height.to_taffy(rem_size).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ToTaffy<taffy::geometry::Rect<U>> for Edges<T>
|
||||
where
|
||||
T: ToTaffy<U> + Clone + Debug,
|
||||
{
|
||||
fn to_taffy(&self, rem_size: Pixels) -> taffy::geometry::Rect<U> {
|
||||
taffy::geometry::Rect {
|
||||
top: self.top.to_taffy(rem_size).into(),
|
||||
right: self.right.to_taffy(rem_size).into(),
|
||||
bottom: self.bottom.to_taffy(rem_size).into(),
|
||||
left: self.left.to_taffy(rem_size).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T: Clone + Default + Debug> From<taffy::geometry::Size<S>> for Size<T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
fn from(value: taffy::geometry::Size<S>) -> Self {
|
||||
Self {
|
||||
width: value.width.into(),
|
||||
height: value.height.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&taffy::tree::Layout> for Layout {
|
||||
fn from(layout: &taffy::tree::Layout) -> Self {
|
||||
Layout {
|
||||
order: layout.order,
|
||||
bounds: Bounds {
|
||||
origin: layout.location.into(),
|
||||
size: layout.size.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Pixels {
|
||||
fn from(pixels: f32) -> Self {
|
||||
Pixels(pixels)
|
||||
}
|
||||
}
|
|
@ -1,231 +0,0 @@
|
|||
use super::{
|
||||
px, taffy::LayoutId, AppContext, Bounds, Context, EntityId, Handle, Pixels, Style,
|
||||
TaffyLayoutEngine,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use gpui2::Reference;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
pub struct AnyWindow {}
|
||||
|
||||
pub struct Window {
|
||||
id: WindowId,
|
||||
rem_size: Pixels,
|
||||
layout_engine: TaffyLayoutEngine,
|
||||
pub(crate) root_view: Option<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(id: WindowId) -> Window {
|
||||
Window {
|
||||
id,
|
||||
layout_engine: TaffyLayoutEngine::new(),
|
||||
rem_size: px(16.),
|
||||
root_view: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct WindowContext<'a, 'b> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
app: Reference<'a, AppContext>,
|
||||
window: Reference<'b, Window>,
|
||||
}
|
||||
|
||||
impl<'a, 'w> WindowContext<'a, 'w> {
|
||||
pub(crate) fn mutable(app: &'a mut AppContext, window: &'w mut Window) -> Self {
|
||||
Self {
|
||||
app: Reference::Mutable(app),
|
||||
window: Reference::Mutable(window),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn immutable(app: &'a AppContext, window: &'w Window) -> Self {
|
||||
Self {
|
||||
app: Reference::Immutable(app),
|
||||
window: Reference::Immutable(window),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_layout(
|
||||
&mut self,
|
||||
style: Style,
|
||||
children: impl IntoIterator<Item = LayoutId>,
|
||||
) -> Result<LayoutId> {
|
||||
self.app.layout_id_buffer.clear();
|
||||
self.app.layout_id_buffer.extend(children.into_iter());
|
||||
let rem_size = self.rem_size();
|
||||
|
||||
self.window
|
||||
.layout_engine
|
||||
.request_layout(style, rem_size, &self.app.layout_id_buffer)
|
||||
}
|
||||
|
||||
pub fn layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
|
||||
Ok(self
|
||||
.window
|
||||
.layout_engine
|
||||
.layout(layout_id)
|
||||
.map(Into::into)?)
|
||||
}
|
||||
|
||||
pub fn rem_size(&self) -> Pixels {
|
||||
self.window.rem_size
|
||||
}
|
||||
|
||||
fn update_window<R>(
|
||||
&mut self,
|
||||
window_id: WindowId,
|
||||
update: impl FnOnce(&mut WindowContext) -> R,
|
||||
) -> Result<R> {
|
||||
if window_id == self.window.id {
|
||||
Ok(update(self))
|
||||
} else {
|
||||
self.app.update_window(window_id, update)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Context for WindowContext<'_, '_> {
|
||||
type EntityContext<'a, 'w, T: 'static> = ViewContext<'a, 'w, T>;
|
||||
|
||||
fn entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
||||
) -> Handle<T> {
|
||||
let id = self.entities.insert(None);
|
||||
let entity = Box::new(build_entity(&mut ViewContext::mutable(
|
||||
&mut *self.app,
|
||||
&mut self.window,
|
||||
id,
|
||||
)));
|
||||
self.entities.get_mut(id).unwrap().replace(entity);
|
||||
|
||||
Handle {
|
||||
id,
|
||||
entity_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Handle<T>,
|
||||
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
||||
) -> R {
|
||||
let mut entity = self
|
||||
.app
|
||||
.entities
|
||||
.get_mut(handle.id)
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap()
|
||||
.downcast::<T>()
|
||||
.unwrap();
|
||||
|
||||
let result = update(
|
||||
&mut *entity,
|
||||
&mut ViewContext::mutable(&mut *self.app, &mut *self.window, handle.id),
|
||||
);
|
||||
|
||||
self.app
|
||||
.entities
|
||||
.get_mut(handle.id)
|
||||
.unwrap()
|
||||
.replace(entity);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct ViewContext<'a, 'w, T> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
window_cx: WindowContext<'a, 'w>,
|
||||
entity_type: PhantomData<T>,
|
||||
entity_id: EntityId,
|
||||
}
|
||||
|
||||
impl<'a, 'w, T: 'static> ViewContext<'a, 'w, T> {
|
||||
// fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
|
||||
|
||||
// self.window_cx.update_entity(handle, update)
|
||||
|
||||
// let mut entity = self.window_cx.app.entities.remove(&self.entity_id).unwrap();
|
||||
// let result = update(entity.downcast_mut::<T>().unwrap(), self);
|
||||
// self.window_cx
|
||||
// .app
|
||||
// .entities
|
||||
// .insert(self.entity_id, Box::new(entity));
|
||||
// result
|
||||
// }
|
||||
|
||||
fn mutable(app: &'a mut AppContext, window: &'w mut Window, entity_id: EntityId) -> Self {
|
||||
Self {
|
||||
window_cx: WindowContext::mutable(app, window),
|
||||
entity_id,
|
||||
entity_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn immutable(app: &'a AppContext, window: &'w Window, entity_id: EntityId) -> Self {
|
||||
Self {
|
||||
window_cx: WindowContext::immutable(app, window),
|
||||
entity_id,
|
||||
entity_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'w, T: 'static> Context for ViewContext<'a, 'w, T> {
|
||||
type EntityContext<'b, 'c, U: 'static> = ViewContext<'b, 'c, U>;
|
||||
|
||||
fn entity<T2: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T2>) -> T2,
|
||||
) -> Handle<T2> {
|
||||
self.window_cx.entity(build_entity)
|
||||
}
|
||||
|
||||
fn update_entity<U: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Handle<U>,
|
||||
update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
|
||||
) -> R {
|
||||
self.window_cx.update_entity(handle, update)
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
||||
slotmap::new_key_type! { pub struct WindowId; }
|
||||
|
||||
pub struct WindowHandle<S> {
|
||||
id: WindowId,
|
||||
state_type: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S> WindowHandle<S> {
|
||||
pub fn new(id: WindowId) -> Self {
|
||||
WindowHandle {
|
||||
id,
|
||||
state_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyWindowHandle {
|
||||
id: WindowId,
|
||||
state_type: TypeId,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Layout {
|
||||
pub order: u32,
|
||||
pub bounds: Bounds<Pixels>,
|
||||
}
|
|
@ -12,7 +12,6 @@ use simplelog::SimpleLogger;
|
|||
mod collab_panel;
|
||||
mod components;
|
||||
mod element_ext;
|
||||
mod gpui3;
|
||||
mod theme;
|
||||
mod workspace;
|
||||
|
||||
|
|
Loading…
Reference in a new issue