1#![cfg_attr(target_family = "wasm", no_main)]
2
3use std::fs;
4use std::path::PathBuf;
5use std::sync::Arc;
6
7use anyhow::Result;
8use gpui::{
9 App, AppContext, AssetSource, Bounds, Context, ImageSource, KeyBinding, Menu, MenuItem, Point,
10 SharedString, SharedUri, TitlebarOptions, Window, WindowBounds, WindowOptions, actions, div,
11 img, prelude::*, px, rgb, size,
12};
13#[cfg(not(target_family = "wasm"))]
14use reqwest_client::ReqwestClient;
15
16struct Assets {
17 base: PathBuf,
18}
19
20impl AssetSource for Assets {
21 fn load(&self, path: &str) -> Result<Option<std::borrow::Cow<'static, [u8]>>> {
22 fs::read(self.base.join(path))
23 .map(|data| Some(std::borrow::Cow::Owned(data)))
24 .map_err(|e| e.into())
25 }
26
27 fn list(&self, path: &str) -> Result<Vec<SharedString>> {
28 fs::read_dir(self.base.join(path))
29 .map(|entries| {
30 entries
31 .filter_map(|entry| {
32 entry
33 .ok()
34 .and_then(|entry| entry.file_name().into_string().ok())
35 .map(SharedString::from)
36 })
37 .collect()
38 })
39 .map_err(|e| e.into())
40 }
41}
42
43#[derive(IntoElement)]
44struct ImageContainer {
45 text: SharedString,
46 src: ImageSource,
47}
48
49impl ImageContainer {
50 pub fn new(text: impl Into<SharedString>, src: impl Into<ImageSource>) -> Self {
51 Self {
52 text: text.into(),
53 src: src.into(),
54 }
55 }
56}
57
58impl RenderOnce for ImageContainer {
59 fn render(self, _window: &mut Window, _: &mut App) -> impl IntoElement {
60 div().child(
61 div()
62 .flex_row()
63 .size_full()
64 .gap_4()
65 .child(self.text)
66 .child(img(self.src).size(px(256.0))),
67 )
68 }
69}
70
71struct ImageShowcase {
72 local_resource: Arc<std::path::Path>,
73 remote_resource: SharedUri,
74 asset_resource: SharedString,
75}
76
77impl Render for ImageShowcase {
78 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
79 div()
80 .id("main")
81 .bg(gpui::white())
82 .overflow_y_scroll()
83 .p_5()
84 .size_full()
85 .child(
86 div()
87 .flex()
88 .flex_col()
89 .justify_center()
90 .items_center()
91 .gap_8()
92 .child(img(
93 "https://github.com/zed-industries/zed/actions/workflows/ci.yml/badge.svg",
94 ))
95 .child(
96 div()
97 .flex()
98 .flex_row()
99 .justify_center()
100 .items_center()
101 .gap_8()
102 .child(ImageContainer::new(
103 "Image loaded from a local file",
104 self.local_resource.clone(),
105 ))
106 .child(ImageContainer::new(
107 "Image loaded from a remote resource",
108 self.remote_resource.clone(),
109 ))
110 .child(ImageContainer::new(
111 "Image loaded from an asset",
112 self.asset_resource.clone(),
113 )),
114 )
115 .child(
116 div()
117 .flex()
118 .flex_row()
119 .gap_8()
120 .child(
121 div()
122 .flex_col()
123 .child("Auto Width")
124 .child(img("https://picsum.photos/800/400").h(px(180.))),
125 )
126 .child(
127 div()
128 .flex_col()
129 .child("Auto Height")
130 .child(img("https://picsum.photos/800/400").w(px(180.))),
131 ),
132 )
133 .child(
134 div()
135 .flex()
136 .flex_col()
137 .justify_center()
138 .items_center()
139 .w_full()
140 .border_1()
141 .border_color(rgb(0xC0C0C0))
142 .child("image with max width 100%")
143 .child(img("https://picsum.photos/800/400").max_w_full()),
144 ),
145 )
146 }
147}
148
149actions!(image, [Quit]);
150
151fn run_example() {
152 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
153
154 #[cfg(not(target_family = "wasm"))]
155 let app = gpui_platform::application();
156 #[cfg(target_family = "wasm")]
157 let app = gpui_platform::application();
158 app.with_assets(Assets {
159 base: manifest_dir.join("examples"),
160 })
161 .run(move |cx: &mut App| {
162 #[cfg(not(target_family = "wasm"))]
163 {
164 let http_client = ReqwestClient::user_agent("gpui example").unwrap();
165 cx.set_http_client(Arc::new(http_client));
166 }
167 #[cfg(target_family = "wasm")]
168 {
169 // Safety: the web examples run single-threaded; the client is
170 // created and used exclusively on the main thread.
171 let http_client = unsafe {
172 gpui_web::FetchHttpClient::with_user_agent("gpui example")
173 .expect("failed to create FetchHttpClient")
174 };
175 cx.set_http_client(Arc::new(http_client));
176 }
177
178 cx.activate(true);
179 cx.on_action(|_: &Quit, cx| cx.quit());
180 cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
181 cx.set_menus(vec![Menu {
182 name: "Image".into(),
183 items: vec![MenuItem::action("Quit", Quit)],
184 }]);
185
186 let window_options = WindowOptions {
187 titlebar: Some(TitlebarOptions {
188 title: Some(SharedString::from("Image Example")),
189 appears_transparent: false,
190 ..Default::default()
191 }),
192
193 window_bounds: Some(WindowBounds::Windowed(Bounds {
194 size: size(px(1100.), px(600.)),
195 origin: Point::new(px(200.), px(200.)),
196 })),
197
198 ..Default::default()
199 };
200
201 cx.open_window(window_options, |_, cx| {
202 cx.new(|_| ImageShowcase {
203 // Relative path to your root project path
204 local_resource: manifest_dir.join("examples/image/app-icon.png").into(),
205 remote_resource: "https://picsum.photos/800/400".into(),
206 asset_resource: "image/color.svg".into(),
207 })
208 })
209 .unwrap();
210 });
211}
212
213#[cfg(not(target_family = "wasm"))]
214fn main() {
215 env_logger::init();
216 run_example();
217}
218
219#[cfg(target_family = "wasm")]
220#[wasm_bindgen::prelude::wasm_bindgen(start)]
221pub fn start() {
222 gpui_platform::web_init();
223 run_example();
224}