image.rs

  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}