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            .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}