1use std::fs;
2use std::path::PathBuf;
3use std::str::FromStr;
4use std::sync::Arc;
5
6use anyhow::Result;
7use gpui::{
8 actions, div, img, prelude::*, px, rgb, size, App, AppContext, Application, AssetSource,
9 Bounds, Context, ImageSource, KeyBinding, Menu, MenuItem, Point, SharedString, SharedUri,
10 TitlebarOptions, Window, WindowBounds, WindowOptions,
11};
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 .size_full()
78 .flex()
79 .flex_col()
80 .justify_center()
81 .items_center()
82 .gap_8()
83 .bg(rgb(0xffffff))
84 .child(
85 div()
86 .flex()
87 .flex_row()
88 .justify_center()
89 .items_center()
90 .gap_8()
91 .child(ImageContainer::new(
92 "Image loaded from a local file",
93 self.local_resource.clone(),
94 ))
95 .child(ImageContainer::new(
96 "Image loaded from a remote resource",
97 self.remote_resource.clone(),
98 ))
99 .child(ImageContainer::new(
100 "Image loaded from an asset",
101 self.asset_resource.clone(),
102 )),
103 )
104 .child(
105 div()
106 .flex()
107 .flex_row()
108 .gap_8()
109 .child(
110 div()
111 .flex_col()
112 .child("Auto Width")
113 .child(img("https://picsum.photos/800/400").h(px(180.))),
114 )
115 .child(
116 div()
117 .flex_col()
118 .child("Auto Height")
119 .child(img("https://picsum.photos/480/640").w(px(180.))),
120 ),
121 )
122 }
123}
124
125actions!(image, [Quit]);
126
127fn main() {
128 env_logger::init();
129
130 Application::new()
131 .with_assets(Assets {
132 base: PathBuf::from("crates/gpui/examples"),
133 })
134 .run(|cx: &mut App| {
135 cx.activate(true);
136 cx.on_action(|_: &Quit, cx| cx.quit());
137 cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
138 cx.set_menus(vec![Menu {
139 name: "Image".into(),
140 items: vec![MenuItem::action("Quit", Quit)],
141 }]);
142
143 let window_options = WindowOptions {
144 titlebar: Some(TitlebarOptions {
145 title: Some(SharedString::from("Image Example")),
146 appears_transparent: false,
147 ..Default::default()
148 }),
149
150 window_bounds: Some(WindowBounds::Windowed(Bounds {
151 size: size(px(1100.), px(600.)),
152 origin: Point::new(px(200.), px(200.)),
153 })),
154
155 ..Default::default()
156 };
157
158 cx.open_window(window_options, |_, cx| {
159 cx.new(|_| ImageShowcase {
160 // Relative path to your root project path
161 local_resource: PathBuf::from_str("crates/gpui/examples/image/app-icon.png")
162 .unwrap()
163 .into(),
164
165 remote_resource: "https://picsum.photos/512/512".into(),
166
167 asset_resource: "image/color.svg".into(),
168 })
169 })
170 .unwrap();
171 });
172}