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