Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Image

Group: Data

Code

pub struct ImageDemo {
    menu_bar: MenuBar,
    last_action: Option<String>,
}

impl ImageDemo {
    pub fn new() -> Self {
        Self {
            menu_bar: MenuBar {
                id: WidgetId::new("gallery:image:menu-bar"),
                items: vec![
                    menu_item("gallery:image:file", "&File"),
                    menu_item("gallery:image:edit", "&Edit"),
                    menu_item("gallery:image:view", "&View"),
                ],
                open_item: None,
                focused_item: None,
            },
            last_action: None,
        }
    }

    fn logo(&self, variant: usize) -> Image {
        Image {
            id: WidgetId::new("gallery:image:logo"),
            source: ImageSource::Bytes(LOGO_PNG.to_vec()),
            intrinsic_size: Some((24, 24)),
            fit: if variant == 1 {
                ImageFit::Cover
            } else {
                ImageFit::Contain
            },
            fallback_text: "[Q]".into(),
        }
    }

    fn bar_rects(area: Rect, backend: &dyn Backend) -> (Rect, Rect, Rect) {
        let lh = backend.line_height();
        let full = Rect::new(area.x, area.y, area.width, lh);
        let icon_width = lh * 4.0;
        let icon_rect = Rect::new(full.x, full.y, icon_width, full.height);
        let items_rect = Rect::new(
            full.x + icon_width,
            full.y,
            (full.width - icon_width).max(0.0),
            full.height,
        );
        (full, icon_rect, items_rect)
    }

    fn status(&self) -> StatusBar {
        let msg = match &self.last_action {
            Some(a) => format!(" last: {a} "),
            None => " click a menu item or the logo ".into(),
        };
        StatusBar {
            id: WidgetId::new("gallery:image:status"),
            left_segments: vec![StatusBarSegment {
                text: msg,
                fg: Color::rgb(255, 255, 255),
                bg: Color::rgb(40, 80, 120),
                bold: false,
                action_id: None,
            }],
            right_segments: vec![],
        }
    }

    fn status_rect(area: Rect, backend: &dyn Backend) -> Rect {
        let lh = backend.line_height();
        Rect::new(area.x, area.y + area.height - lh, area.width, lh)
    }
}

impl Default for ImageDemo {
    fn default() -> Self {
        Self::new()
    }
}

impl Demo for ImageDemo {
    fn name(&self) -> &'static str {
        "Image"
    }

    fn group(&self) -> &'static str {
        "Data"
    }

    fn variants(&self) -> &'static [&'static str] {
        &["Contain", "Cover"]
    }

    fn render(&self, variant: usize, backend: &mut dyn Backend, area: Rect) {
        let (_full, icon_rect, items_rect) = Self::bar_rects(area, backend);
        let _ = backend.draw_image(icon_rect, &self.logo(variant));
        let _ = backend.draw_menu_bar(items_rect, &self.menu_bar);

        let status_rect = Self::status_rect(area, backend);
        let _ = backend.draw_status_bar_interactive(
            status_rect,
            &self.status(),
            &InteractionState::new(),
        );
    }

    fn handle(
        &mut self,
        variant: usize,
        event: &UiEvent,
        backend: &mut dyn Backend,
        area: Rect,
    ) -> Reaction {
        match event {
            UiEvent::MouseDown {
                button: MouseButton::Left,
                position,
                ..
            } => {
                let (_full, icon_rect, items_rect) = Self::bar_rects(area, backend);
                let image_layout = self.logo(variant).layout(icon_rect);
                if image_layout.hit_test(position.x, position.y) == ImageHit::Image {
                    self.last_action = Some("clicked logo".into());
                    return Reaction::Redraw;
                }
                let layout = backend.menu_bar_layout(items_rect, &self.menu_bar);
                if let MenuBarHit::Item(idx) = layout.hit_test(position.x, position.y) {
                    self.last_action =
                        Some(format!("activated: {}", self.menu_bar.items[idx].label));
                    return Reaction::Redraw;
                }
                Reaction::Continue
            }
            _ => Reaction::Continue,
        }
    }

    fn source(&self) -> &'static str {
        extract_region(SOURCE)
    }

    fn data(&self, variant: usize) -> serde_json::Value {
        serde_json::json!({
            "image": self.logo(variant),
            "last_action": self.last_action,
        })
    }

    fn caps_note(&self, _variant: usize, _caps: &BackendCaps) -> Option<String> {
        None
    }
}

Backends

VariantTUIGTKmacOSWindows
Contain
Cover