image.rs

  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}