Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gpui: Support loading image from filesystem #6978

Merged
merged 6 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/client/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ impl User {
Arc::new(User {
id: message.id,
github_login: message.github_login,
avatar_uri: message.avatar_url.into(),
avatar_uri: SharedUrl::network(message.avatar_url),
})
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/collab/src/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use fs::{repository::GitFileStatus, FakeFs, Fs as _, RemoveOptions};
use futures::StreamExt as _;
use gpui::{
px, size, AppContext, BackgroundExecutor, Model, Modifiers, MouseButton, MouseDownEvent,
TestAppContext,
SharedUrl, TestAppContext,
};
use language::{
language_settings::{AllLanguageSettings, Formatter},
Expand Down Expand Up @@ -1828,7 +1828,7 @@ async fn test_active_call_events(
owner: Arc::new(User {
id: client_a.user_id().unwrap(),
github_login: "user_a".to_string(),
avatar_uri: "avatar_a".into(),
avatar_uri: SharedUrl::network("avatar_a"),
}),
project_id: project_a_id,
worktree_root_names: vec!["a".to_string()],
Expand All @@ -1846,7 +1846,7 @@ async fn test_active_call_events(
owner: Arc::new(User {
id: client_b.user_id().unwrap(),
github_login: "user_b".to_string(),
avatar_uri: "avatar_b".into(),
avatar_uri: SharedUrl::network("avatar_b"),
}),
project_id: project_b_id,
worktree_root_names: vec!["b".to_string()]
Expand Down
4 changes: 2 additions & 2 deletions crates/collab_ui/src/chat_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ fn format_timestamp(
#[cfg(test)]
mod tests {
use super::*;
use gpui::HighlightStyle;
use gpui::{HighlightStyle, SharedUrl};
use pretty_assertions::assert_eq;
use rich_text::Highlight;
use time::{Date, OffsetDateTime, Time, UtcOffset};
Expand All @@ -730,7 +730,7 @@ mod tests {
timestamp: OffsetDateTime::now_utc(),
sender: Arc::new(client::User {
github_login: "fgh".into(),
avatar_uri: "avatar_fgh".into(),
avatar_uri: SharedUrl::network("avatar_fgh"),
id: 103,
}),
nonce: 5,
Expand Down
6 changes: 3 additions & 3 deletions crates/collab_ui/src/chat_panel/message_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ impl Render for MessageEditor {
mod tests {
use super::*;
use client::{Client, User, UserStore};
use gpui::TestAppContext;
use gpui::{SharedUrl, TestAppContext};
use language::{Language, LanguageConfig};
use rpc::proto;
use settings::SettingsStore;
Expand All @@ -392,7 +392,7 @@ mod tests {
user: Arc::new(User {
github_login: "a-b".into(),
id: 101,
avatar_uri: "avatar_a-b".into(),
avatar_uri: SharedUrl::network("avatar_a-b"),
}),
kind: proto::channel_member::Kind::Member,
role: proto::ChannelRole::Member,
Expand All @@ -401,7 +401,7 @@ mod tests {
user: Arc::new(User {
github_login: "C_D".into(),
id: 102,
avatar_uri: "avatar_C_D".into(),
avatar_uri: SharedUrl::network("avatar_C_D"),
}),
kind: proto::channel_member::Kind::Member,
role: proto::ChannelRole::Member,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use gpui::prelude::*;
use gpui::{prelude::*, SharedUrl};
use story::{StoryContainer, StoryItem, StorySection};
use ui::prelude::*;

Expand All @@ -19,7 +19,7 @@ impl Render for CollabNotificationStory {
"Incoming Call Notification",
window_container(400., 72.).child(
CollabNotification::new(
"https://avatars.githubusercontent.com/u/1486634?v=4",
SharedUrl::network("https://avatars.githubusercontent.com/u/1486634?v=4"),
Button::new("accept", "Accept"),
Button::new("decline", "Decline"),
)
Expand All @@ -36,7 +36,7 @@ impl Render for CollabNotificationStory {
"Project Shared Notification",
window_container(400., 72.).child(
CollabNotification::new(
"https://avatars.githubusercontent.com/u/1714999?v=4",
SharedUrl::network("https://avatars.githubusercontent.com/u/1714999?v=4"),
Button::new("open", "Open"),
Button::new("dismiss", "Dismiss"),
)
Expand Down
59 changes: 59 additions & 0 deletions crates/gpui/examples/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use gpui::*;

#[derive(IntoElement)]
struct ImageFromResource {
text: SharedString,
resource: SharedUrl,
}

impl RenderOnce for ImageFromResource {
fn render(self, _: &mut WindowContext) -> impl IntoElement {
div().child(
div()
.flex_row()
.size_full()
.gap_4()
.child(self.text)
.child(img(self.resource).w(px(512.0)).h(px(512.0))),
)
}
}

struct ImageShowcase {
local_resource: SharedUrl,
remote_resource: SharedUrl,
}

impl Render for ImageShowcase {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.flex()
.flex_row()
.size_full()
.justify_center()
.items_center()
.gap_8()
.bg(rgb(0xFFFFFF))
.child(ImageFromResource {
text: "Image loaded from a local file".into(),
resource: self.local_resource.clone(),
})
.child(ImageFromResource {
text: "Image loaded from a remote resource".into(),
resource: self.remote_resource.clone(),
})
}
}

fn main() {
env_logger::init();

App::new().run(|cx: &mut AppContext| {
cx.open_window(WindowOptions::default(), |cx| {
cx.new_view(|_cx| ImageShowcase {
local_resource: SharedUrl::file("../zed/resources/app-icon.png"),
remote_resource: SharedUrl::network("https://picsum.photos/512/512"),
})
});
});
}
12 changes: 0 additions & 12 deletions crates/gpui/src/elements/img.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,6 @@ impl From<SharedUrl> for ImageSource {
}
}

impl From<&'static str> for ImageSource {
fn from(uri: &'static str) -> Self {
Self::Uri(uri.into())
}
}

impl From<String> for ImageSource {
fn from(uri: String) -> Self {
Self::Uri(uri.into())
}
}

impl From<Arc<ImageData>> for ImageSource {
fn from(value: Arc<ImageData>) -> Self {
Self::Data(value)
Expand Down
37 changes: 23 additions & 14 deletions crates/gpui/src/image_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,31 @@ impl ImageCache {
{
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?;
match uri {
SharedUrl::File(uri) => {
let image = image::open(uri.as_ref())?.into_bgra8();
Ok(Arc::new(ImageData::new(image)))
}
SharedUrl::Network(uri) => {
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(),
});
}
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)))
let format = image::guess_format(&body)?;
let image =
image::load_from_memory_with_format(&body, format)?
.into_bgra8();
Ok(Arc::new(ImageData::new(image)))
}
}
}
}
.map_err({
Expand Down
64 changes: 52 additions & 12 deletions crates/gpui/src/shared_url.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,65 @@
use derive_more::{Deref, DerefMut};
use std::ops::{Deref, DerefMut};

use crate::SharedString;

/// A [`SharedString`] containing a URL.
#[derive(Deref, DerefMut, Default, PartialEq, Eq, Hash, Clone)]
pub struct SharedUrl(SharedString);
/// A URL stored in a `SharedString` pointing to a file or a remote resource.
#[derive(PartialEq, Eq, Hash, Clone)]
pub enum SharedUrl {
/// A path to a local file.
File(SharedString),
/// A URL to a remote resource.
Network(SharedString),
}

impl std::fmt::Debug for SharedUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
impl SharedUrl {
/// Create a URL pointing to a local file.
pub fn file<S: Into<SharedString>>(s: S) -> Self {
Self::File(s.into())
}

/// Create a URL pointing to a remote resource.
pub fn network<S: Into<SharedString>>(s: S) -> Self {
Self::Network(s.into())
}
}

impl std::fmt::Display for SharedUrl {
impl Default for SharedUrl {
fn default() -> Self {
Self::Network(SharedString::default())
}
}

impl Deref for SharedUrl {
type Target = SharedString;

fn deref(&self) -> &Self::Target {
match self {
Self::File(s) => s,
Self::Network(s) => s,
}
}
}

impl DerefMut for SharedUrl {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::File(s) => s,
Self::Network(s) => s,
}
}
}

impl std::fmt::Debug for SharedUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.as_ref())
match self {
Self::File(s) => write!(f, "File({:?})", s),
Self::Network(s) => write!(f, "Network({:?})", s),
}
}
}

impl<T: Into<SharedString>> From<T> for SharedUrl {
fn from(value: T) -> Self {
Self(value.into())
impl std::fmt::Display for SharedUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_ref())
}
}
42 changes: 25 additions & 17 deletions crates/theme/src/styles/stories/players.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use gpui::{div, img, px, IntoElement, ParentElement, Render, Styled, ViewContext};
use gpui::{div, img, px, IntoElement, ParentElement, Render, SharedUrl, Styled, ViewContext};
use story::Story;

use crate::{ActiveTheme, PlayerColors};
Expand Down Expand Up @@ -53,10 +53,12 @@ impl Render for PlayerStory {
.border_2()
.border_color(player.cursor)
.child(
img("https://avatars.githubusercontent.com/u/1714999?v=4")
.rounded_full()
.size_6()
.bg(gpui::red()),
img(SharedUrl::network(
"https://avatars.githubusercontent.com/u/1714999?v=4",
))
.rounded_full()
.size_6()
.bg(gpui::red()),
)
}),
))
Expand All @@ -82,10 +84,12 @@ impl Render for PlayerStory {
.border_color(player.background)
.size(px(28.))
.child(
img("https://avatars.githubusercontent.com/u/1714999?v=4")
.rounded_full()
.size(px(24.))
.bg(gpui::red()),
img(SharedUrl::network(
"https://avatars.githubusercontent.com/u/1714999?v=4",
))
.rounded_full()
.size(px(24.))
.bg(gpui::red()),
),
)
.child(
Expand All @@ -98,10 +102,12 @@ impl Render for PlayerStory {
.border_color(player.background)
.size(px(28.))
.child(
img("https://avatars.githubusercontent.com/u/1714999?v=4")
.rounded_full()
.size(px(24.))
.bg(gpui::red()),
img(SharedUrl::network(
"https://avatars.githubusercontent.com/u/1714999?v=4",
))
.rounded_full()
.size(px(24.))
.bg(gpui::red()),
),
)
.child(
Expand All @@ -114,10 +120,12 @@ impl Render for PlayerStory {
.border_color(player.background)
.size(px(28.))
.child(
img("https://avatars.githubusercontent.com/u/1714999?v=4")
.rounded_full()
.size(px(24.))
.bg(gpui::red()),
img(SharedUrl::network(
"https://avatars.githubusercontent.com/u/1714999?v=4",
))
.rounded_full()
.size(px(24.))
.bg(gpui::red()),
),
)
}),
Expand Down
Loading
Loading