Add png image loading to gpui

add zed logo into welcome experience

Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Mikayla Maki 2023-03-06 10:39:35 -08:00
parent f89f33347d
commit 4c179875ab
11 changed files with 76 additions and 33 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -823,7 +823,7 @@ impl CollabTitlebarItem {
avatar_style: AvatarStyle,
background_color: Color,
) -> ElementBox {
Image::new(avatar)
Image::from_data(avatar)
.with_style(avatar_style.image)
.aligned()
.contained()

View file

@ -128,7 +128,7 @@ impl PickerDelegate for ContactFinder {
.style_for(mouse_state, selected);
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.contact_finder.contact_avatar)
.aligned()
.left()

View file

@ -726,7 +726,7 @@ impl ContactList {
) -> ElementBox {
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()
@ -1080,7 +1080,7 @@ impl ContactList {
};
Stack::new()
.with_child(
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()
@ -1173,7 +1173,7 @@ impl ContactList {
let mut row = Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.contact_avatar)
.aligned()
.left()

View file

@ -108,7 +108,7 @@ impl IncomingCallNotification {
.unwrap_or(&default_project);
Flex::row()
.with_children(self.call.calling_user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.caller_avatar)
.aligned()
.boxed()

View file

@ -24,7 +24,7 @@ pub fn render_user_notification<V: View, A: Action + Clone>(
.with_child(
Flex::row()
.with_children(user.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.header_avatar)
.aligned()
.constrained()

View file

@ -108,7 +108,7 @@ impl ProjectSharedNotification {
let theme = &cx.global::<Settings>().theme.project_shared_notification;
Flex::row()
.with_children(self.owner.avatar.clone().map(|avatar| {
Image::new(avatar)
Image::from_data(avatar)
.with_style(theme.owner_avatar)
.aligned()
.boxed()

View file

@ -1,5 +1,8 @@
use anyhow::{anyhow, Result};
use std::{borrow::Cow, cell::RefCell, collections::HashMap};
use image::ImageFormat;
use std::{borrow::Cow, cell::RefCell, collections::HashMap, sync::Arc};
use crate::ImageData;
pub trait AssetSource: 'static + Send + Sync {
fn load(&self, path: &str) -> Result<Cow<[u8]>>;
@ -22,6 +25,7 @@ impl AssetSource for () {
pub struct AssetCache {
source: Box<dyn AssetSource>,
svgs: RefCell<HashMap<String, usvg::Tree>>,
pngs: RefCell<HashMap<String, Arc<ImageData>>>,
}
impl AssetCache {
@ -29,6 +33,7 @@ impl AssetCache {
Self {
source: Box::new(source),
svgs: RefCell::new(HashMap::new()),
pngs: RefCell::new(HashMap::new()),
}
}
@ -43,4 +48,18 @@ impl AssetCache {
Ok(svg)
}
}
pub fn png(&self, path: &str) -> Result<Arc<ImageData>> {
let mut pngs = self.pngs.borrow_mut();
if let Some(png) = pngs.get(path) {
Ok(png.clone())
} else {
let bytes = self.source.load(path)?;
let image = ImageData::new(
image::load_from_memory_with_format(&bytes, ImageFormat::Png)?.into_bgra8(),
);
pngs.insert(path.to_string(), image.clone());
Ok(image)
}
}
}

View file

@ -11,8 +11,13 @@ use crate::{
use serde::Deserialize;
use std::{ops::Range, sync::Arc};
enum ImageSource {
Path(&'static str),
Data(Arc<ImageData>),
}
pub struct Image {
data: Arc<ImageData>,
source: ImageSource,
style: ImageStyle,
}
@ -31,9 +36,16 @@ pub struct ImageStyle {
}
impl Image {
pub fn new(data: Arc<ImageData>) -> Self {
pub fn new(asset_path: &'static str) -> Self {
Self {
data,
source: ImageSource::Path(asset_path),
style: Default::default(),
}
}
pub fn from_data(data: Arc<ImageData>) -> Self {
Self {
source: ImageSource::Data(data),
style: Default::default(),
}
}
@ -45,39 +57,53 @@ impl Image {
}
impl Element for Image {
type LayoutState = ();
type LayoutState = Option<Arc<ImageData>>;
type PaintState = ();
fn layout(
&mut self,
constraint: SizeConstraint,
_: &mut LayoutContext,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let data = match &self.source {
ImageSource::Path(path) => match cx.asset_cache.png(path) {
Ok(data) => data,
Err(error) => {
log::error!("could not load image: {}", error);
return (Vector2F::zero(), None);
}
},
ImageSource::Data(data) => data.clone(),
};
let desired_size = vec2f(
self.style.width.unwrap_or_else(|| constraint.max.x()),
self.style.height.unwrap_or_else(|| constraint.max.y()),
);
let size = constrain_size_preserving_aspect_ratio(
constraint.constrain(desired_size),
self.data.size().to_f32(),
data.size().to_f32(),
);
(size, ())
(size, Some(data))
}
fn paint(
&mut self,
bounds: RectF,
_: RectF,
_: &mut Self::LayoutState,
layout: &mut Self::LayoutState,
cx: &mut PaintContext,
) -> Self::PaintState {
cx.scene.push_image(scene::Image {
bounds,
border: self.style.border,
corner_radius: self.style.corner_radius,
grayscale: self.style.grayscale,
data: self.data.clone(),
});
if let Some(data) = layout {
cx.scene.push_image(scene::Image {
bounds,
border: self.style.border,
corner_radius: self.style.corner_radius,
grayscale: self.style.grayscale,
data: data.clone(),
});
}
}
fn rect_for_text_range(

View file

@ -1325,7 +1325,7 @@ impl View for ProjectPanel {
Canvas::new(|bounds, _visible_bounds, cx| {
cx.scene.push_quad(gpui::Quad {
bounds,
background: Some(Color::red()),
background: Some(Color::transparent_black()),
..Default::default()
})
})

View file

@ -1,14 +1,13 @@
use std::borrow::Cow;
use gpui::{
color::Color,
elements::{Canvas, Empty, Flex, Label, MouseEventHandler, ParentElement, Stack, Svg},
elements::{Canvas, Empty, Flex, Image, Label, MouseEventHandler, ParentElement, Stack},
geometry::rect::RectF,
Action, Element, ElementBox, Entity, MouseButton, MouseRegion, MutableAppContext,
RenderContext, Subscription, View, ViewContext,
};
use settings::{settings_file::SettingsFile, Settings, SettingsFileContent};
use theme::{CheckboxStyle, ContainedText, Interactive};
use theme::CheckboxStyle;
use workspace::{item::Item, Welcome, Workspace};
pub fn init(cx: &mut MutableAppContext) {
@ -72,15 +71,14 @@ impl View for WelcomePage {
.with_children([
Flex::row()
.with_children([
Svg::new("icons/terminal_16.svg")
.with_color(Color::red())
Image::new("images/zed-logo-90x90.png")
.constrained()
.with_width(100.)
.with_height(100.)
.with_width(90.)
.with_height(90.)
.aligned()
.contained()
.boxed(),
Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(),
// Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(),
])
.boxed(),
Label::new(