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