Checkpoint: start rendering images
This commit is contained in:
parent
5c750b6880
commit
1816ab95a0
11 changed files with 231 additions and 77 deletions
|
@ -84,7 +84,6 @@ impl ImageCache {
|
||||||
let format = image::guess_format(&body)?;
|
let format = image::guess_format(&body)?;
|
||||||
let image =
|
let image =
|
||||||
image::load_from_memory_with_format(&body, format)?.into_bgra8();
|
image::load_from_memory_with_format(&body, format)?.into_bgra8();
|
||||||
|
|
||||||
Ok(ImageData::new(image))
|
Ok(ImageData::new(image))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ pub use model_context::*;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
current_platform, run_on_main, spawn_on_main, AssetSource, Context, LayoutId, MainThread,
|
current_platform, image_cache::ImageCache, run_on_main, spawn_on_main, AssetSource, Context,
|
||||||
MainThreadOnly, Platform, PlatformDispatcher, RootView, SvgRenderer, TextStyle,
|
LayoutId, MainThread, MainThreadOnly, Platform, PlatformDispatcher, RootView, SvgRenderer,
|
||||||
TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
|
TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use collections::{HashMap, VecDeque};
|
use collections::{HashMap, VecDeque};
|
||||||
|
@ -23,24 +23,33 @@ use std::{
|
||||||
mem,
|
mem,
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::{
|
||||||
|
http::{self, HttpClient},
|
||||||
|
ResultExt,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct App(Arc<Mutex<MainThread<AppContext>>>);
|
pub struct App(Arc<Mutex<MainThread<AppContext>>>);
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
|
pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
|
||||||
Self::new(current_platform(), asset_source)
|
let http_client = http::client();
|
||||||
|
Self::new(current_platform(), asset_source, http_client)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test"))]
|
#[cfg(any(test, feature = "test"))]
|
||||||
pub fn test() -> Self {
|
pub fn test() -> Self {
|
||||||
let platform = Arc::new(super::TestPlatform::new());
|
let platform = Arc::new(super::TestPlatform::new());
|
||||||
let asset_source = Arc::new(());
|
let asset_source = Arc::new(());
|
||||||
Self::new(platform, asset_source)
|
let http_client = util::http::FakeHttpClient::with_404_response();
|
||||||
|
Self::new(platform, asset_source, http_client)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(platform: Arc<dyn Platform>, asset_source: Arc<dyn AssetSource>) -> Self {
|
fn new(
|
||||||
|
platform: Arc<dyn Platform>,
|
||||||
|
asset_source: Arc<dyn AssetSource>,
|
||||||
|
http_client: Arc<dyn HttpClient>,
|
||||||
|
) -> Self {
|
||||||
let dispatcher = platform.dispatcher();
|
let dispatcher = platform.dispatcher();
|
||||||
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
||||||
let entities = EntityMap::new();
|
let entities = EntityMap::new();
|
||||||
|
@ -52,6 +61,7 @@ impl App {
|
||||||
dispatcher,
|
dispatcher,
|
||||||
text_system,
|
text_system,
|
||||||
svg_renderer: SvgRenderer::new(asset_source),
|
svg_renderer: SvgRenderer::new(asset_source),
|
||||||
|
image_cache: ImageCache::new(http_client),
|
||||||
pending_updates: 0,
|
pending_updates: 0,
|
||||||
text_style_stack: Vec::new(),
|
text_style_stack: Vec::new(),
|
||||||
state_stacks_by_type: HashMap::default(),
|
state_stacks_by_type: HashMap::default(),
|
||||||
|
@ -87,6 +97,7 @@ pub struct AppContext {
|
||||||
text_system: Arc<TextSystem>,
|
text_system: Arc<TextSystem>,
|
||||||
pending_updates: usize,
|
pending_updates: usize,
|
||||||
pub(crate) svg_renderer: SvgRenderer,
|
pub(crate) svg_renderer: SvgRenderer,
|
||||||
|
pub(crate) image_cache: ImageCache,
|
||||||
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
||||||
pub(crate) state_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
|
pub(crate) state_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
|
||||||
pub(crate) unit_entity: Handle<()>,
|
pub(crate) unit_entity: Handle<()>,
|
||||||
|
|
|
@ -3,7 +3,9 @@ use anyhow::anyhow;
|
||||||
use image::{Bgra, ImageBuffer};
|
use image::{Bgra, ImageBuffer};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
cmp::Ordering,
|
||||||
fmt,
|
fmt,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
sync::atomic::{AtomicUsize, Ordering::SeqCst},
|
sync::atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,18 +27,21 @@ impl AssetSource for () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
pub struct ImageId(usize);
|
||||||
|
|
||||||
pub struct ImageData {
|
pub struct ImageData {
|
||||||
pub id: usize,
|
pub id: ImageId,
|
||||||
data: ImageBuffer<Bgra<u8>, Vec<u8>>,
|
data: ImageBuffer<Bgra<u8>, Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageData {
|
impl ImageData {
|
||||||
pub fn from_raw(size: Size<DevicePixels>, bytes: Vec<u8>) -> Self {
|
pub fn new(data: ImageBuffer<Bgra<u8>, Vec<u8>>) -> Self {
|
||||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id: NEXT_ID.fetch_add(1, SeqCst),
|
id: ImageId(NEXT_ID.fetch_add(1, SeqCst)),
|
||||||
data: ImageBuffer::from_raw(size.width.into(), size.height.into(), bytes).unwrap(),
|
data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,10 +49,6 @@ impl ImageData {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_bytes(self) -> Vec<u8> {
|
|
||||||
self.data.into_raw()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> Size<DevicePixels> {
|
pub fn size(&self) -> Size<DevicePixels> {
|
||||||
let (width, height) = self.data.dimensions();
|
let (width, height) = self.data.dimensions();
|
||||||
size(width.into(), height.into())
|
size(width.into(), height.into())
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use crate::{Element, Layout, LayoutId, Result, Style, StyleHelpers, Styled};
|
use crate::{
|
||||||
|
Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled, ViewContext,
|
||||||
|
};
|
||||||
|
use futures::FutureExt;
|
||||||
use refineable::RefinementCascade;
|
use refineable::RefinementCascade;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use util::arc_cow::ArcCow;
|
use util::ResultExt;
|
||||||
|
|
||||||
pub struct Img<S> {
|
pub struct Img<S> {
|
||||||
style: RefinementCascade<Style>,
|
style: RefinementCascade<Style>,
|
||||||
uri: Option<ArcCow<'static, str>>,
|
uri: Option<SharedString>,
|
||||||
state_type: PhantomData<S>,
|
state_type: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +21,7 @@ pub fn img<S>() -> Img<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Img<S> {
|
impl<S> Img<S> {
|
||||||
pub fn uri(mut self, uri: impl Into<ArcCow<'static, str>>) -> Self {
|
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
|
||||||
self.uri = Some(uri.into());
|
self.uri = Some(uri.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -31,7 +34,7 @@ impl<S: 'static> Element for Img<S> {
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &mut Self::State,
|
_: &mut Self::State,
|
||||||
cx: &mut crate::ViewContext<Self::State>,
|
cx: &mut ViewContext<Self::State>,
|
||||||
) -> anyhow::Result<(LayoutId, Self::FrameState)>
|
) -> anyhow::Result<(LayoutId, Self::FrameState)>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
|
@ -46,7 +49,7 @@ impl<S: 'static> Element for Img<S> {
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
_: &mut Self::State,
|
_: &mut Self::State,
|
||||||
_: &mut Self::FrameState,
|
_: &mut Self::FrameState,
|
||||||
cx: &mut crate::ViewContext<Self::State>,
|
cx: &mut ViewContext<Self::State>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let style = self.computed_style();
|
let style = self.computed_style();
|
||||||
let order = layout.order;
|
let order = layout.order;
|
||||||
|
@ -54,36 +57,24 @@ impl<S: 'static> Element for Img<S> {
|
||||||
|
|
||||||
style.paint(order, bounds, cx);
|
style.paint(order, bounds, cx);
|
||||||
|
|
||||||
// if let Some(uri) = &self.uri {
|
if let Some(uri) = &self.uri {
|
||||||
// let image_future = cx.image_cache.get(uri.clone());
|
let image_future = cx.image_cache.get(uri.clone());
|
||||||
// if let Some(data) = image_future
|
if let Some(data) = image_future
|
||||||
// .clone()
|
.clone()
|
||||||
// .now_or_never()
|
.now_or_never()
|
||||||
// .and_then(ResultExt::log_err)
|
.and_then(ResultExt::log_err)
|
||||||
// {
|
{
|
||||||
// let rem_size = cx.rem_size();
|
cx.paint_image(bounds, order, data, false)?;
|
||||||
// cx.scene().push_image(scene::Image {
|
} else {
|
||||||
// bounds,
|
log::warn!("image not loaded yet");
|
||||||
// border: gpui::Border {
|
// cx.spawn(|this, mut cx| async move {
|
||||||
// color: style.border_color.unwrap_or_default().into(),
|
// if image_future.await.log_err().is_some() {
|
||||||
// top: style.border_widths.top.to_pixels(rem_size),
|
// this.update(&mut cx, |_, cx| cx.notify()).ok();
|
||||||
// right: style.border_widths.right.to_pixels(rem_size),
|
// }
|
||||||
// bottom: style.border_widths.bottom.to_pixels(rem_size),
|
// })
|
||||||
// left: style.border_widths.left.to_pixels(rem_size),
|
// .detach();
|
||||||
// },
|
}
|
||||||
// corner_radii: style.corner_radii.to_gpui(bounds.size(), rem_size),
|
}
|
||||||
// grayscale: false,
|
|
||||||
// data,
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// cx.spawn(|this, mut cx| async move {
|
|
||||||
// if image_future.await.log_err().is_some() {
|
|
||||||
// this.update(&mut cx, |_, cx| cx.notify()).ok();
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .detach();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -645,6 +645,10 @@ impl ScaledPixels {
|
||||||
pub fn floor(&self) -> Self {
|
pub fn floor(&self) -> Self {
|
||||||
Self(self.0.floor())
|
Self(self.0.floor())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ceil(&self) -> Self {
|
||||||
|
Self(self.0.ceil())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for ScaledPixels {}
|
impl Eq for ScaledPixels {}
|
||||||
|
|
|
@ -5,6 +5,7 @@ mod element;
|
||||||
mod elements;
|
mod elements;
|
||||||
mod executor;
|
mod executor;
|
||||||
mod geometry;
|
mod geometry;
|
||||||
|
mod image_cache;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod scene;
|
mod scene;
|
||||||
mod style;
|
mod style;
|
||||||
|
|
99
crates/gpui3/src/image_cache.rs
Normal file
99
crates/gpui3/src/image_cache.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use crate::{ImageData, ImageId, SharedString};
|
||||||
|
use collections::HashMap;
|
||||||
|
use futures::{
|
||||||
|
future::{BoxFuture, Shared},
|
||||||
|
AsyncReadExt, FutureExt,
|
||||||
|
};
|
||||||
|
use image::ImageError;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use thiserror::Error;
|
||||||
|
use util::http::{self, HttpClient};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||||
|
pub struct RenderImageParams {
|
||||||
|
pub(crate) image_id: ImageId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Clone)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("http error: {0}")]
|
||||||
|
Client(#[from] http::Error),
|
||||||
|
#[error("IO error: {0}")]
|
||||||
|
Io(Arc<std::io::Error>),
|
||||||
|
#[error("unexpected http status: {status}, body: {body}")]
|
||||||
|
BadStatus {
|
||||||
|
status: http::StatusCode,
|
||||||
|
body: String,
|
||||||
|
},
|
||||||
|
#[error("image error: {0}")]
|
||||||
|
Image(Arc<ImageError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for Error {
|
||||||
|
fn from(error: std::io::Error) -> Self {
|
||||||
|
Error::Io(Arc::new(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ImageError> for Error {
|
||||||
|
fn from(error: ImageError) -> Self {
|
||||||
|
Error::Image(Arc::new(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ImageCache {
|
||||||
|
client: Arc<dyn HttpClient>,
|
||||||
|
images: Arc<Mutex<HashMap<SharedString, FetchImageFuture>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type FetchImageFuture = Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>>;
|
||||||
|
|
||||||
|
impl ImageCache {
|
||||||
|
pub fn new(client: Arc<dyn HttpClient>) -> Self {
|
||||||
|
ImageCache {
|
||||||
|
client,
|
||||||
|
images: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(
|
||||||
|
&self,
|
||||||
|
uri: impl Into<SharedString>,
|
||||||
|
) -> Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>> {
|
||||||
|
let uri = uri.into();
|
||||||
|
let mut images = self.images.lock();
|
||||||
|
|
||||||
|
match images.get(&uri) {
|
||||||
|
Some(future) => future.clone(),
|
||||||
|
None => {
|
||||||
|
let client = self.client.clone();
|
||||||
|
let future = {
|
||||||
|
let uri = uri.clone();
|
||||||
|
async move {
|
||||||
|
let mut response = client.get(uri.as_ref(), ().into(), true).await?;
|
||||||
|
let mut body = Vec::new();
|
||||||
|
response.body_mut().read_to_end(&mut body).await?;
|
||||||
|
|
||||||
|
if !response.status().is_success() {
|
||||||
|
return Err(Error::BadStatus {
|
||||||
|
status: response.status(),
|
||||||
|
body: String::from_utf8_lossy(&body).into_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let format = image::guess_format(&body)?;
|
||||||
|
let image =
|
||||||
|
image::load_from_memory_with_format(&body, format)?.into_bgra8();
|
||||||
|
Ok(Arc::new(ImageData::new(image)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
|
.shared();
|
||||||
|
|
||||||
|
images.insert(uri, future.clone());
|
||||||
|
future
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ mod mac;
|
||||||
#[cfg(any(test, feature = "test"))]
|
#[cfg(any(test, feature = "test"))]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
|
use crate::image_cache::RenderImageParams;
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
|
AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
|
||||||
RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
|
RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
|
||||||
|
@ -14,6 +15,7 @@ use async_task::Runnable;
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use seahash::SeaHasher;
|
use seahash::SeaHasher;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -179,6 +181,7 @@ pub trait PlatformTextSystem: Send + Sync {
|
||||||
pub enum AtlasKey {
|
pub enum AtlasKey {
|
||||||
Glyph(RenderGlyphParams),
|
Glyph(RenderGlyphParams),
|
||||||
Svg(RenderSvgParams),
|
Svg(RenderSvgParams),
|
||||||
|
Image(RenderImageParams),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtlasKey {
|
impl AtlasKey {
|
||||||
|
@ -186,6 +189,7 @@ impl AtlasKey {
|
||||||
match self {
|
match self {
|
||||||
AtlasKey::Glyph(params) => !params.is_emoji,
|
AtlasKey::Glyph(params) => !params.is_emoji,
|
||||||
AtlasKey::Svg(_) => true,
|
AtlasKey::Svg(_) => true,
|
||||||
|
AtlasKey::Image(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,11 +206,17 @@ impl From<RenderSvgParams> for AtlasKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<RenderImageParams> for AtlasKey {
|
||||||
|
fn from(params: RenderImageParams) -> Self {
|
||||||
|
Self::Image(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait PlatformAtlas: Send + Sync {
|
pub trait PlatformAtlas: Send + Sync {
|
||||||
fn get_or_insert_with(
|
fn get_or_insert_with<'a>(
|
||||||
&self,
|
&self,
|
||||||
key: &AtlasKey,
|
key: &AtlasKey,
|
||||||
build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Vec<u8>)>,
|
build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
|
||||||
) -> Result<AtlasTile>;
|
) -> Result<AtlasTile>;
|
||||||
|
|
||||||
fn clear(&self);
|
fn clear(&self);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size,
|
AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size,
|
||||||
};
|
};
|
||||||
|
@ -31,10 +33,10 @@ struct MetalAtlasState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformAtlas for MetalAtlas {
|
impl PlatformAtlas for MetalAtlas {
|
||||||
fn get_or_insert_with(
|
fn get_or_insert_with<'a>(
|
||||||
&self,
|
&self,
|
||||||
key: &AtlasKey,
|
key: &AtlasKey,
|
||||||
build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Vec<u8>)>,
|
build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
|
||||||
) -> Result<AtlasTile> {
|
) -> Result<AtlasTile> {
|
||||||
let mut lock = self.0.lock();
|
let mut lock = self.0.lock();
|
||||||
if let Some(tile) = lock.tiles_by_key.get(key) {
|
if let Some(tile) = lock.tiles_by_key.get(key) {
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
px, AnyView, AppContext, AvailableSpace, BorrowAppContext, Bounds, Context, Corners,
|
image_cache::RenderImageParams, px, AnyView, AppContext, AvailableSpace, BorrowAppContext,
|
||||||
DevicePixels, Effect, Element, EntityId, FontId, GlyphId, Handle, Hsla, IsZero, LayerId,
|
Bounds, Context, Corners, DevicePixels, Effect, Element, EntityId, FontId, GlyphId, Handle,
|
||||||
LayoutId, MainThread, MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow,
|
Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread, MainThreadOnly, MonochromeSprite,
|
||||||
Point, PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene,
|
Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference, RenderGlyphParams,
|
||||||
SharedString, Size, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
|
RenderSvgParams, ScaledPixels, Scene, SharedString, Size, Style, TaffyLayoutEngine, WeakHandle,
|
||||||
|
WindowOptions, SUBPIXEL_VARIANTS,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{any::TypeId, marker::PhantomData, mem, sync::Arc};
|
use std::{any::TypeId, borrow::Cow, marker::PhantomData, mem, sync::Arc};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
pub struct AnyWindow {}
|
pub struct AnyWindow {}
|
||||||
|
@ -234,12 +235,13 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||||
let raster_bounds = self.text_system().raster_bounds(¶ms)?;
|
let raster_bounds = self.text_system().raster_bounds(¶ms)?;
|
||||||
if !raster_bounds.is_zero() {
|
if !raster_bounds.is_zero() {
|
||||||
let layer_id = self.current_layer_id();
|
let layer_id = self.current_layer_id();
|
||||||
let tile = self
|
let tile =
|
||||||
.window
|
self.window
|
||||||
.sprite_atlas
|
.sprite_atlas
|
||||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||||
self.text_system().rasterize_glyph(¶ms)
|
let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
|
||||||
})?;
|
Ok((size, Cow::Owned(bytes)))
|
||||||
|
})?;
|
||||||
let bounds = Bounds {
|
let bounds = Bounds {
|
||||||
origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
|
origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
|
||||||
size: tile.bounds.size.map(Into::into),
|
size: tile.bounds.size.map(Into::into),
|
||||||
|
@ -283,12 +285,13 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||||
let raster_bounds = self.text_system().raster_bounds(¶ms)?;
|
let raster_bounds = self.text_system().raster_bounds(¶ms)?;
|
||||||
if !raster_bounds.is_zero() {
|
if !raster_bounds.is_zero() {
|
||||||
let layer_id = self.current_layer_id();
|
let layer_id = self.current_layer_id();
|
||||||
let tile = self
|
let tile =
|
||||||
.window
|
self.window
|
||||||
.sprite_atlas
|
.sprite_atlas
|
||||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||||
self.text_system().rasterize_glyph(¶ms)
|
let (size, bytes) = self.text_system().rasterize_glyph(¶ms)?;
|
||||||
})?;
|
Ok((size, Cow::Owned(bytes)))
|
||||||
|
})?;
|
||||||
let bounds = Bounds {
|
let bounds = Bounds {
|
||||||
origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
|
origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
|
||||||
size: tile.bounds.size.map(Into::into),
|
size: tile.bounds.size.map(Into::into),
|
||||||
|
@ -331,7 +334,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||||
.sprite_atlas
|
.sprite_atlas
|
||||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||||
let bytes = self.svg_renderer.render(¶ms)?;
|
let bytes = self.svg_renderer.render(¶ms)?;
|
||||||
Ok((params.size, bytes))
|
Ok((params.size, Cow::Owned(bytes)))
|
||||||
})?;
|
})?;
|
||||||
let content_mask = self.content_mask().scale(scale_factor);
|
let content_mask = self.content_mask().scale(scale_factor);
|
||||||
|
|
||||||
|
@ -349,6 +352,39 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn paint_image(
|
||||||
|
&mut self,
|
||||||
|
bounds: Bounds<Pixels>,
|
||||||
|
order: u32,
|
||||||
|
data: Arc<ImageData>,
|
||||||
|
grayscale: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let scale_factor = self.scale_factor();
|
||||||
|
let bounds = bounds.scale(scale_factor);
|
||||||
|
let params = RenderImageParams { image_id: data.id };
|
||||||
|
|
||||||
|
let layer_id = self.current_layer_id();
|
||||||
|
let tile = self
|
||||||
|
.window
|
||||||
|
.sprite_atlas
|
||||||
|
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||||
|
Ok((data.size(), Cow::Borrowed(data.as_bytes())))
|
||||||
|
})?;
|
||||||
|
let content_mask = self.content_mask().scale(scale_factor);
|
||||||
|
|
||||||
|
self.window.scene.insert(
|
||||||
|
layer_id,
|
||||||
|
PolychromeSprite {
|
||||||
|
order,
|
||||||
|
bounds,
|
||||||
|
content_mask,
|
||||||
|
tile,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn draw(&mut self) -> Result<()> {
|
pub(crate) fn draw(&mut self) -> Result<()> {
|
||||||
let unit_entity = self.unit_entity.clone();
|
let unit_entity = self.unit_entity.clone();
|
||||||
self.update_entity(&unit_entity, |_, cx| {
|
self.update_entity(&unit_entity, |_, cx| {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::theme::{theme, Theme};
|
use crate::theme::{theme, Theme};
|
||||||
use gpui3::{
|
use gpui3::{
|
||||||
div, img, svg, view, AppContext, ArcCow, Context, Element, IntoAnyElement, ParentElement,
|
div, img, svg, view, AppContext, ArcCow, Context, Element, IntoAnyElement, ParentElement,
|
||||||
ScrollState, StyleHelpers, View, ViewContext, WindowContext,
|
ScrollState, SharedString, StyleHelpers, View, ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct CollabPanel {
|
pub struct CollabPanel {
|
||||||
|
@ -144,7 +144,7 @@ impl CollabPanel {
|
||||||
|
|
||||||
fn list_item(
|
fn list_item(
|
||||||
&self,
|
&self,
|
||||||
avatar_uri: impl Into<ArcCow<'static, str>>,
|
avatar_uri: impl Into<SharedString>,
|
||||||
label: impl IntoAnyElement<Self>,
|
label: impl IntoAnyElement<Self>,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
) -> impl Element<State = Self> {
|
) -> impl Element<State = Self> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue