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

Indicators

Group: Data

Code

pub struct IndicatorsDemo {
    progress: f32,
    cancellable: bool,
    spinner_frame: usize,
    last_message: String,
}

impl IndicatorsDemo {
    pub fn new() -> Self {
        Self {
            progress: 0.3,
            cancellable: false,
            spinner_frame: 0,
            last_message: "space=+10% r=reset c=cancel".into(),
        }
    }

    fn progress_bar(&self, variant: usize) -> ProgressBar {
        ProgressBar {
            id: WidgetId::new("gallery:indicators:progress"),
            label: if variant == 1 {
                "Working...".into()
            } else {
                format!("{:.0}%", self.progress * 100.0)
            },
            value: if variant == 1 {
                None
            } else {
                Some(self.progress)
            },
            frame_idx: self.spinner_frame,
            cancellable: self.cancellable,
            accent: None,
        }
    }

    fn spinner(&self) -> Spinner {
        Spinner {
            id: WidgetId::new("gallery:indicators:spinner"),
            label: "Loading...".into(),
            frame_idx: self.spinner_frame,
            accent: None,
        }
    }

    fn status(&self) -> StatusBar {
        StatusBar {
            id: WidgetId::new("gallery:indicators:status"),
            left_segments: vec![StatusBarSegment {
                text: format!(" {} ", self.last_message),
                fg: Color::rgb(255, 255, 255),
                bg: Color::rgb(40, 80, 120),
                bold: false,
                action_id: None,
            }],
            right_segments: vec![],
        }
    }

    fn progress_rect(area: Rect, backend: &dyn Backend) -> Rect {
        let lh = backend.line_height();
        Rect::new(area.x + 1.0, area.y + lh, (area.width - 2.0).max(0.0), lh)
    }

    fn spinner_rect(area: Rect, backend: &dyn Backend) -> Rect {
        let lh = backend.line_height();
        Rect::new(
            area.x + 1.0,
            area.y + lh * 3.0,
            (area.width - 2.0).max(0.0),
            lh,
        )
    }

    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 IndicatorsDemo {
    fn default() -> Self {
        Self::new()
    }
}

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

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

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

    fn render(&self, variant: usize, backend: &mut dyn Backend, area: Rect) {
        let progress_rect = Self::progress_rect(area, backend);
        let _ = backend.draw_progress(progress_rect, &self.progress_bar(variant));

        let spinner_rect = Self::spinner_rect(area, backend);
        let _ = backend.draw_spinner(spinner_rect, &self.spinner());

        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::KeyPressed {
                key: Key::Char(' '),
                ..
            } => {
                self.progress = (self.progress + 0.1).min(1.0);
                self.spinner_frame += 1;
                self.last_message = format!("Progress: {:.0}%", self.progress * 100.0);
                Reaction::Redraw
            }
            UiEvent::KeyPressed {
                key: Key::Char('r'),
                ..
            } => {
                self.progress = 0.0;
                self.last_message = "Reset".into();
                Reaction::Redraw
            }
            UiEvent::KeyPressed {
                key: Key::Char('c'),
                ..
            } => {
                self.cancellable = !self.cancellable;
                self.last_message = if self.cancellable {
                    "Cancel enabled".into()
                } else {
                    "Cancel disabled".into()
                };
                Reaction::Redraw
            }
            UiEvent::MouseDown { position, .. } => {
                let progress_rect = Self::progress_rect(area, backend);
                let bar = self.progress_bar(variant);
                let layout = backend.progress_layout(progress_rect, &bar);
                match layout.hit_test(position.x, position.y) {
                    ProgressBarHit::Cancel(_) => {
                        self.last_message = "Cancelled!".into();
                        self.progress = 0.0;
                    }
                    ProgressBarHit::Body(_) => {
                        self.last_message = "Bar clicked".into();
                    }
                    ProgressBarHit::Empty => return Reaction::Continue,
                }
                Reaction::Redraw
            }
            _ => Reaction::Continue,
        }
    }

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

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

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

    fn tick(&mut self, _variant: usize, _backend: &mut dyn Backend) -> Reaction {
        self.spinner_frame = self.spinner_frame.wrapping_add(1);
        Reaction::Redraw
    }
}

Backends

VariantTUIGTKmacOSWindows
Progress
Indeterminate