forked from 1b-agent/opencadstudio-upstream
176 lines
5.6 KiB
Rust
176 lines
5.6 KiB
Rust
//! Modal overlay shown while a CAD file is being loaded.
|
|
//!
|
|
//! Displays the file name, size, current phase, measured progress, and a
|
|
//! Cancel button.
|
|
|
|
use iced::time::Instant;
|
|
use iced::widget::{button, column, container, row, stack, text, Space};
|
|
use iced::{Background, Border, Element, Fill, Length, Theme};
|
|
use crate::t;
|
|
use std::borrow::Cow;
|
|
use std::sync::atomic::Ordering;
|
|
|
|
use crate::app::{
|
|
Message, OpenProgress, OPEN_PHASE_CACHING, OPEN_PHASE_FINALIZING, OPEN_PHASE_PARSING,
|
|
OPEN_PHASE_READING, OPEN_PHASE_XREF,
|
|
};
|
|
|
|
const CARD_WIDTH: f32 = 420.0;
|
|
const BAR_TRACK_WIDTH: f32 = 380.0;
|
|
const BAR_TRACK_HEIGHT: f32 = 6.0;
|
|
|
|
fn phase_label(phase: u8) -> Cow<'static, str> {
|
|
match phase {
|
|
OPEN_PHASE_READING => t!("Reading file…"),
|
|
OPEN_PHASE_PARSING => t!("Parsing entities…"),
|
|
OPEN_PHASE_XREF => t!("Loading references…"),
|
|
OPEN_PHASE_CACHING => t!("Building scene caches…"),
|
|
OPEN_PHASE_FINALIZING => t!("Finalizing…"),
|
|
_ => t!("Working…"),
|
|
}
|
|
}
|
|
|
|
fn format_size(bytes: u64) -> String {
|
|
const KB: f64 = 1024.0;
|
|
const MB: f64 = KB * 1024.0;
|
|
const GB: f64 = MB * 1024.0;
|
|
let b = bytes as f64;
|
|
if b >= GB {
|
|
format!("{:.2} GB", b / GB)
|
|
} else if b >= MB {
|
|
format!("{:.1} MB", b / MB)
|
|
} else if b >= KB {
|
|
format!("{:.1} KB", b / KB)
|
|
} else {
|
|
format!("{bytes} B")
|
|
}
|
|
}
|
|
|
|
pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Message> {
|
|
let phase = progress.state.phase.load(Ordering::Acquire);
|
|
let basis_points = progress
|
|
.state
|
|
.basis_points
|
|
.load(Ordering::Relaxed)
|
|
.min(10000);
|
|
let fraction = basis_points as f32 / 10000.0;
|
|
let fill_width = BAR_TRACK_WIDTH * fraction;
|
|
let trailing = (BAR_TRACK_WIDTH - fill_width).max(0.0);
|
|
|
|
let bar_fill: Element<'_, Message> = container(
|
|
Space::new()
|
|
.width(Length::Fixed(fill_width))
|
|
.height(Length::Fixed(BAR_TRACK_HEIGHT)),
|
|
)
|
|
.style(|theme: &Theme| container::Style {
|
|
background: Some(Background::Color(
|
|
theme.palette().primary.base.color
|
|
)),
|
|
border: Border {
|
|
radius: 3.0.into(),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.into();
|
|
|
|
let bar_value: Element<'_, Message> = row![
|
|
bar_fill,
|
|
Space::new()
|
|
.width(Length::Fixed(trailing))
|
|
.height(Length::Fixed(BAR_TRACK_HEIGHT)),
|
|
]
|
|
.into();
|
|
|
|
let bar_track: Element<'_, Message> = container(
|
|
stack![
|
|
container(Space::new().width(Length::Fixed(BAR_TRACK_WIDTH)).height(Length::Fixed(BAR_TRACK_HEIGHT)))
|
|
.style(|theme: &Theme| container::Style {
|
|
background: Some(Background::Color(
|
|
theme.palette().background.strong.color
|
|
)),
|
|
border: Border {
|
|
radius: 3.0.into(),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
}),
|
|
bar_value,
|
|
]
|
|
.width(Length::Fixed(BAR_TRACK_WIDTH))
|
|
.height(Length::Fixed(BAR_TRACK_HEIGHT)),
|
|
)
|
|
.into();
|
|
|
|
// ── Card body ────────────────────────────────────────────────────────
|
|
let title = text(t!("Opening file")).size(15);
|
|
|
|
let name_line = text(format!(
|
|
"{} ({})",
|
|
progress.name,
|
|
format_size(progress.size_bytes)
|
|
))
|
|
.size(13)
|
|
.style(|theme: &Theme| iced::widget::text::Style {
|
|
color: Some(theme.palette().background.base.text.scale_alpha(0.82)),
|
|
});
|
|
|
|
let phase_line = text(format!(
|
|
"{} {:.1}%",
|
|
phase_label(phase),
|
|
basis_points as f32 / 100.0
|
|
))
|
|
.size(12)
|
|
.style(|theme: &Theme| iced::widget::text::Style {
|
|
color: Some(theme.palette().primary.base.color),
|
|
});
|
|
|
|
let cancel_btn: Element<'_, Message> = button(text(t!("Cancel")).size(12))
|
|
.on_press(Message::OpenCancel)
|
|
.style(button::danger)
|
|
.padding([4, 14])
|
|
.into();
|
|
|
|
let cancel_row: Element<'_, Message> = container(cancel_btn).align_right(Fill).into();
|
|
|
|
let card = container(
|
|
column![title, name_line, bar_track, phase_line, cancel_row]
|
|
.spacing(10)
|
|
.width(Length::Fixed(CARD_WIDTH)),
|
|
)
|
|
.padding([18, 22])
|
|
.style(|theme: &Theme| {
|
|
let palette = theme.palette();
|
|
container::Style {
|
|
background: Some(Background::Color(palette.background.weak.color)),
|
|
border: Border {
|
|
color: palette.background.neutral.color,
|
|
width: 1.0,
|
|
radius: 6.0.into(),
|
|
},
|
|
..Default::default()
|
|
}
|
|
});
|
|
|
|
// ── Backdrop (click-blocker + dim) ────────────────────────────────────
|
|
let backdrop: Element<'_, Message> = container(Space::new().width(Fill).height(Fill))
|
|
.style(|theme: &Theme| container::Style {
|
|
background: Some(Background::Color(
|
|
theme.palette().background.strong.color.scale_alpha(0.72)
|
|
)),
|
|
..Default::default()
|
|
})
|
|
.width(Fill)
|
|
.height(Fill)
|
|
.into();
|
|
|
|
let centered: Element<'_, Message> = container(card)
|
|
.center_x(Fill)
|
|
.center_y(Fill)
|
|
.width(Fill)
|
|
.height(Fill)
|
|
.into();
|
|
|
|
stack![backdrop, centered].width(Fill).height(Fill).into()
|
|
}
|