scrybe_widgets/view.rs
1// Copyright 2026 Mathews Tom
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5// https://www.apache.org/licenses/LICENSE-2.0
6
7//! What every native indicator renders from.
8//!
9//! One rendering-independent snapshot, so the status item, the floating
10//! panel, and anything else showing a live recording cannot disagree
11//! about what they are showing. It carries no dependency on the service
12//! layer: whichever frontend owns a recording projects its own state
13//! model onto this, and the widgets never see a controller.
14
15use std::time::Duration;
16
17/// The recording lifecycle a native indicator shows.
18///
19/// Two states, not six. An indicator the size of a menu-bar item has
20/// room for "this is running" and "this is finishing", and a reader
21/// watching one wants exactly that distinction; the full state model
22/// belongs to the surface that has room to render it.
23#[derive(Copy, Clone, Debug, Eq, PartialEq)]
24pub enum ShellState {
25 Recording,
26 Saving,
27}
28
29/// One snapshot, as every native indicator sees it.
30#[derive(Copy, Clone, Debug, Eq, PartialEq)]
31pub struct ShellView {
32 pub state: ShellState,
33 pub elapsed: Duration,
34 pub stop_enabled: bool,
35}
36
37impl ShellView {
38 /// `HH:MM:SS`, or `MM:SS` under an hour.
39 #[must_use]
40 pub fn elapsed_label(self) -> String {
41 let total_seconds = self.elapsed.as_secs();
42 let seconds = total_seconds % 60;
43 let minutes = (total_seconds / 60) % 60;
44 let hours = total_seconds / 3_600;
45 if hours == 0 {
46 format!("{minutes:02}:{seconds:02}")
47 } else {
48 format!("{hours:02}:{minutes:02}:{seconds:02}")
49 }
50 }
51}