mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-25 02:30:15 -06:00
LR-BEHAV-BG-TASKS: unknown totals sweep, denoise bars keep their widths, Enter waits for the segmenter
This commit is contained in:
@@ -141,6 +141,22 @@ pub fn detail_toggle(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, on
|
||||
});
|
||||
}
|
||||
|
||||
/// The bar under the Denoise slider while a photo's picture is made: at most 240 px wide, and
|
||||
/// sweeping while the number of tiles is not known yet.
|
||||
pub(crate) fn detail_bar(ui: &mut egui::Ui, done: u64, total: u64) -> egui::Response {
|
||||
let fraction = crate::progress::fraction_of(done, total);
|
||||
let text = match fraction {
|
||||
Some(f) => tr_format!("Making the denoised picture… {}%", (f * 100.0).round() as u32),
|
||||
None => tr("Starting…").to_string(),
|
||||
};
|
||||
crate::progress::Progress::new(fraction).label(text).width(ui.available_width().min(240.0)).widget("denoise:progress").show(ui)
|
||||
}
|
||||
|
||||
/// The bar in Settings, AI Denoise: 380 px wide however wide the tab is.
|
||||
pub(crate) fn settings_bar(ui: &mut egui::Ui, done: u64, total: u64, text: String) -> egui::Response {
|
||||
crate::progress::Progress::new(crate::progress::fraction_of(done, total)).label(text).width(380.0).widget("denoise:settingsProgress").show(ui)
|
||||
}
|
||||
|
||||
/// The line under the Denoise slider for the photo `id`, whose Amount is `amount`.
|
||||
pub fn detail_status(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, amount: f64) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
@@ -198,13 +214,7 @@ pub fn detail_status(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, am
|
||||
}
|
||||
PhotoState::Running { done, total } => {
|
||||
egui::Frame::NONE.inner_margin(pad).show(ui, |ui| {
|
||||
let frac = if total > 0 { (done as f32 / total as f32).clamp(0.0, 1.0) } else { 0.0 };
|
||||
let text = if total > 0 {
|
||||
tr_format!("Making the denoised picture… {}%", (frac * 100.0).round() as u32)
|
||||
} else {
|
||||
tr("Starting…").to_string()
|
||||
};
|
||||
crate::progress::Progress::new(if total > 0 { Some(frac) } else { None }).label(text).widget("denoise:progress").show(ui);
|
||||
detail_bar(ui, done as u64, total as u64);
|
||||
});
|
||||
}
|
||||
PhotoState::Failed { why, unsupported } => {
|
||||
@@ -329,7 +339,6 @@ fn work_section(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens, list: &V
|
||||
let (running, queued) = (app.caches.denoise.running, app.caches.denoise.queued);
|
||||
match running {
|
||||
Some((done, total)) => {
|
||||
let frac = if total > 0 { (done as f32 / total as f32).clamp(0.0, 1.0) } else { 0.0 };
|
||||
let name = status["running"]["photo"]
|
||||
.as_u64()
|
||||
.and_then(|p| app.session.catalog.photo(PhotoId(p)).map(|p| p.file_name.clone()))
|
||||
@@ -341,7 +350,7 @@ fn work_section(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens, list: &V
|
||||
done = done,
|
||||
total = total
|
||||
);
|
||||
crate::progress::Progress::new(Some(frac)).label(text).widget("denoise:settingsProgress").show(ui);
|
||||
settings_bar(ui, done, total, text);
|
||||
}
|
||||
None if queued > 0 => hint(ui, t, &count(queued, "photo waiting", "photos waiting")),
|
||||
None => {
|
||||
@@ -445,13 +454,12 @@ fn model_row(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens, m: &Value,
|
||||
match (dl_state, dl) {
|
||||
(Some("running"), Some(d)) => {
|
||||
let (got, total) = (d["bytes"].as_u64().unwrap_or(0), d["total"].as_u64().unwrap_or(0));
|
||||
let frac = if total > 0 { (got as f32 / total as f32).clamp(0.0, 1.0) } else { 0.0 };
|
||||
let text = if total > 0 && got >= total {
|
||||
tr("Checking…").to_string()
|
||||
} else {
|
||||
tr_format!("{} of {} from {host}", mb(Some(got)), mb(Some(total)), host = host)
|
||||
};
|
||||
crate::progress::Progress::new(Some(frac))
|
||||
crate::progress::Progress::new(crate::progress::fraction_of(got, total))
|
||||
.label(text)
|
||||
.width(crate::progress::ROW_WIDTH.min(ui.available_width()))
|
||||
.widget(format!("denoise:progress:{id}"))
|
||||
|
||||
@@ -1674,9 +1674,8 @@ fn sam_model_body(app: &mut LightcraftApp, ui: &mut egui::Ui, error: Option<&str
|
||||
let seg = &app.session.segmenter;
|
||||
if d.running {
|
||||
ui.add_space(4.0);
|
||||
let frac = if d.total > 0 { d.done as f64 / d.total as f64 } else { 0.0 };
|
||||
let text = format!("{:.2} / {:.2} GB · {}", gb(d.done), gb(d.total), d.file);
|
||||
crate::progress::Progress::new(Some(frac as f32)).label(text).widget("progress:samDownload").show(ui);
|
||||
crate::progress::Progress::new(crate::progress::fraction_of(d.done, d.total)).label(text).widget("progress:samDownload").show(ui);
|
||||
let r = ui.button(crate::i18n::tr("Cancel Download"));
|
||||
crate::widgets::register(ui.ctx(), "button:samCancel", r.rect);
|
||||
if r.clicked() {
|
||||
|
||||
@@ -361,13 +361,12 @@ fn model_row(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens, m: &Value,
|
||||
match (dl_state, dl) {
|
||||
(Some("running"), Some(d)) => {
|
||||
let (got, total) = (d["bytes"].as_u64().unwrap_or(0), d["total"].as_u64().unwrap_or(0));
|
||||
let frac = if total > 0 { (got as f32 / total as f32).clamp(0.0, 1.0) } else { 0.0 };
|
||||
let text = if total > 0 && got >= total {
|
||||
"Checking…".to_string()
|
||||
} else {
|
||||
format!("{} of {} from {host}", mb(Some(got)), mb(Some(total)))
|
||||
};
|
||||
crate::progress::Progress::new(Some(frac))
|
||||
crate::progress::Progress::new(crate::progress::fraction_of(got, total))
|
||||
.label(text)
|
||||
.width(crate::progress::ROW_WIDTH.min(ui.available_width()))
|
||||
.widget(format!("faces:progress:{id}"))
|
||||
|
||||
@@ -183,8 +183,9 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) {
|
||||
let pct = if download.total > 0 { download.done as f64 / download.total as f64 } else { 0.0 };
|
||||
// the bar takes what the Details button leaves
|
||||
let details = ui.spacing().interact_size.x + 30.0;
|
||||
crate::progress::Progress::new(Some(pct as f32))
|
||||
.label(format!("SAM 3: {} %", (pct * 100.0).floor()))
|
||||
crate::progress::Progress::new(crate::progress::fraction_of(download.done, download.total))
|
||||
// no percentage to say while the size is unknown
|
||||
.label(if download.total > 0 { format!("SAM 3: {} %", (pct * 100.0).floor()) } else { String::new() })
|
||||
.width((ui.available_width() - details).max(96.0))
|
||||
.widget("maskSamProgress")
|
||||
.show(ui);
|
||||
@@ -767,6 +768,11 @@ pub(crate) fn start_describe(app: &mut LightcraftApp, op: &str) {
|
||||
}
|
||||
|
||||
/// The Describe field: type what to select ("sky", "the red car") and press Return.
|
||||
/// Enter in the Describe field is the Select button: it starts nothing while the segmenter works.
|
||||
fn enter_submits(working: bool, lost_focus: bool, enter_pressed: bool) -> bool {
|
||||
!working && lost_focus && enter_pressed
|
||||
}
|
||||
|
||||
fn describe_field(app: &mut LightcraftApp, ui: &mut egui::Ui, new_mask: bool) {
|
||||
let Some((op, mut text)) = app.ui.describe.clone() else { return };
|
||||
// a new mask's field sits under the tiles; one that combines, under the selected mask
|
||||
@@ -796,7 +802,8 @@ fn describe_field(app: &mut LightcraftApp, ui: &mut egui::Ui, new_mask: bool) {
|
||||
if !r.has_focus() && !r.lost_focus() && text.is_empty() {
|
||||
r.request_focus();
|
||||
}
|
||||
if r.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
// Enter is the Select button: nothing starts while the segmenter is working
|
||||
if enter_submits(working, r.lost_focus(), ui.input(|i| i.key_pressed(egui::Key::Enter))) {
|
||||
submit = true;
|
||||
}
|
||||
});
|
||||
@@ -904,6 +911,14 @@ fn brush_settings(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn enter_in_the_describe_field_starts_nothing_while_the_segmenter_works() {
|
||||
assert!(enter_submits(false, true, true));
|
||||
assert!(!enter_submits(true, true, true));
|
||||
assert!(!enter_submits(false, false, true));
|
||||
assert!(!enter_submits(false, true, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn masking_context_actions_use_catalog_translations() {
|
||||
let saved = crate::i18n::language();
|
||||
|
||||
@@ -39,6 +39,12 @@ pub fn clamp_fraction(fraction: f32) -> f32 {
|
||||
if fraction.is_nan() { 0.0 } else { fraction.clamp(0.0, 1.0) }
|
||||
}
|
||||
|
||||
/// `done` of `total` as a fraction, or `None` while the total is not known (0): an unknown total
|
||||
/// is a bar that sweeps, not one stuck at 0 %.
|
||||
pub fn fraction_of(done: u64, total: u64) -> Option<f32> {
|
||||
(total > 0).then(|| clamp_fraction(done as f32 / total as f32))
|
||||
}
|
||||
|
||||
/// A progress bar, built and then shown with [`Progress::show`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Progress {
|
||||
|
||||
@@ -7,7 +7,7 @@ use egui::{Pos2, Rect, pos2, vec2};
|
||||
|
||||
use crate::button::{Button, contrast};
|
||||
use crate::icons::Icon;
|
||||
use crate::progress::{Progress, clamp_fraction, spinner};
|
||||
use crate::progress::{Progress, clamp_fraction, fraction_of, spinner};
|
||||
use crate::theme::{ThemeKind, Tokens};
|
||||
|
||||
/// What a screen reader gets for one node.
|
||||
@@ -65,6 +65,15 @@ fn the_fraction_is_always_between_nothing_and_all() {
|
||||
assert_eq!(Progress::new(None).fraction(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unknown_total_is_an_indeterminate_bar_not_an_empty_one() {
|
||||
assert_eq!(fraction_of(0, 0), None);
|
||||
assert_eq!(fraction_of(5, 0), None);
|
||||
assert_eq!(fraction_of(0, 10), Some(0.0));
|
||||
assert_eq!(fraction_of(5, 10), Some(0.5));
|
||||
assert_eq!(fraction_of(20, 10), Some(1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_bar_is_progress_with_a_value_and_reachable_by_its_id() {
|
||||
let (nodes, ids, _) = frames(Vec::new(), |ui| {
|
||||
@@ -197,3 +206,15 @@ fn a_failed_filmstrip_thumbnail_is_announced_with_its_reason() {
|
||||
assert_eq!(spoken(false).as_deref(), Some("IMG_0001.jpg"));
|
||||
assert_eq!(spoken(true).as_deref(), Some("IMG_0001.jpg, This photo can't be opened"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_denoise_bars_keep_their_widths_in_a_wide_container() {
|
||||
let (_, ids, (detail, settings, unknown)) = frames(Vec::new(), |ui| {
|
||||
let detail = crate::panels::denoise::detail_bar(ui, 3, 10).rect.width();
|
||||
let settings = crate::panels::denoise::settings_bar(ui, 3, 10, "Denoising a.jpg: 3 of 10 tiles".into()).rect.width();
|
||||
let unknown = crate::panels::denoise::detail_bar(ui, 0, 0).rect.width();
|
||||
(detail, settings, unknown)
|
||||
});
|
||||
assert_eq!((detail, settings, unknown), (240.0, 380.0, 240.0));
|
||||
assert!(ids.iter().any(|(id, _)| id == "denoise:settingsProgress"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user