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 .overflow_y_scroll()
79 .p_5()
80 .size_full()
81 .flex()
82 .flex_col()
83 .justify_center()
84 .items_center()
85 .gap_8()
86 .bg(rgb(0xffffff))
87 .child(
88 div()
89 .flex()
90 .flex_row()
91 .justify_center()
92 .items_center()
93 .gap_8()
94 .child(ImageContainer::new(
95 "Image loaded from a local file",
96 self.local_resource.clone(),
97 ))
98 .child(ImageContainer::new(
99 "Image loaded from a remote resource",
100 self.remote_resource.clone(),
101 ))
102 .child(ImageContainer::new(
103 "Image loaded from an asset",
104 self.asset_resource.clone(),
105 )),
106 )
107 .child(
108 div()
109 .flex()
110 .flex_row()
111 .gap_8()
112 .child(
113 div()
114 .flex_col()
115 .child("Auto Width")
116 .child(img("https://picsum.photos/800/400").h(px(180.))),
117 )
118 .child(
119 div()
120 .flex_col()
121 .child("Auto Height")
122 .child(img("https://picsum.photos/800/400").w(px(180.))),
123 ),
124 )
125 .child(
126 div()
127 .flex()
128 .flex_col()
129 .justify_center()
130 .items_center()
131 .w_full()
132 .border_1()
133 .border_color(rgb(0xC0C0C0))
134 .child("image with max width 100%")
135 .child(img("https://picsum.photos/800/400").max_w_full()),
136 )
137 }
138}
139
140actions!(image, [Quit]);
141
142fn main() {
143 env_logger::init();
144
145 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
146
147 Application::new()
148 .with_assets(Assets {
149 base: manifest_dir.join("examples"),
150 })
151 .run(move |cx: &mut App| {
152 let http_client = ReqwestClient::user_agent("gpui example").unwrap();
153 cx.set_http_client(Arc::new(http_client));
154
155 cx.activate(true);
156 cx.on_action(|_: &Quit, cx| cx.quit());
157 cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
158 cx.set_menus(vec![Menu {
159 name: "Image".into(),
160 items: vec![MenuItem::action("Quit", Quit)],
161 }]);
162
163 let window_options = WindowOptions {
164 titlebar: Some(TitlebarOptions {
165 title: Some(SharedString::from("Image Example")),
166 appears_transparent: false,
167 ..Default::default()
168 }),
169
170 window_bounds: Some(WindowBounds::Windowed(Bounds {
171 size: size(px(1100.), px(600.)),
172 origin: Point::new(px(200.), px(200.)),
173 })),
174
175 ..Default::default()
176 };
177
178 cx.open_window(window_options, |_, cx| {
179 cx.new(|_| ImageShowcase {
180 // Relative path to your root project path
181 local_resource: manifest_dir.join("examples/image/app-icon.png").into(),
182 remote_resource: "https://picsum.photos/800/400".into(),
183 asset_resource: "image/color.svg".into(),
184 })
185 })
186 .unwrap();
187 });
188}