Run the formatter over the face branch before it reaches CI
🐳 Android image / Build and push (push) Successful in 2s
Build and test / android-image (push) Successful in 2s
Build and test / Desktop (Linux) (push) Successful in 1h21m32s
Build and test / Layer separation (push) Successful in 37s
Traceability / Requirement traces (push) Successful in 25s
Build and test / Android (aarch64) (push) Failing after 33m58s
🐳 Android image / Build and push (push) Successful in 2s
Build and test / android-image (push) Successful in 2s
Build and test / Desktop (Linux) (push) Successful in 1h21m32s
Build and test / Layer separation (push) Successful in 37s
Traceability / Requirement traces (push) Successful in 25s
Build and test / Android (aarch64) (push) Failing after 33m58s
The merge of the SCRFD/MobileFaceNet work brought 69 rustfmt diffs across
dr-catalog, dr-face and dr-ui with it, so `cargo fmt --all -- --check` fails
on master and the Desktop job stops at its Format step — before clippy, the
tests or the release build have run at all. That makes the whole desktop
half of CI blind: a real compile error behind this would look exactly the
same from the outside. There was nothing behind it, as it turns out — with
the formatting fixed, clippy, the test suite and the release build all pass.
Every .rs hunk is `cargo fmt --all` on the pinned 1.92.0 toolchain, not a
hand edit, but it is worth being precise about what that moved, because it
is more than whitespace. Besides reflowing signatures and call chains,
rustfmt reordered the `pub mod` and `pub use` items in dr-face/src/lib.rs so
the `#[cfg(feature = "inference")]` entries sort in place, added the trailing
semicolon inside `let ... else { return }` bodies in identity_ui.rs, wrapped
a bare closure body in braces in cluster.rs, adjusted trailing commas, and
dropped a stray blank line at the end of identity_ui.rs. All of it is
semantically inert; none of it changes behaviour.
docs/traceability.md rides along because it has to. The matrix records each
TRACES tag by line number, and reflowing develop.rs, lib.rs, faces.rs,
identity.rs and identity_ui.rs moved them — FR-CAT-8, FR-CAT-9, FR-CULL-10,
FR-DEV-3, FR-DEV-3a and FR-DEV-3c all shift by a line or two. The matrix was
verified up to date on d777f7f before this commit, so this is drift these
formatting changes introduced, not pre-existing staleness being swept up.
Leaving it for a follow-up commit would hand traceability-check.yml a
failure caused entirely by a whitespace change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -189,8 +189,7 @@ impl SegmentationJob {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut seg =
|
||||
segmentation::compute(&self.ctx, &rgb, rw, rh, self.orientation, options)?;
|
||||
let mut seg = segmentation::compute(&self.ctx, &rgb, rw, rh, self.orientation, options)?;
|
||||
|
||||
// TRACES: FR-CULL-10
|
||||
// Put names on the people the segmenter found.
|
||||
|
||||
+16
-10
@@ -314,15 +314,15 @@ pub fn spawn_face_sweep(
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut embedder = match Embedder::from_path(&embedder_model, ModelId::new(model_id.clone()))
|
||||
{
|
||||
Ok(e) => e,
|
||||
Err(e) => {
|
||||
log::warn!("face sweep: cannot load the embedder: {e}");
|
||||
finish_empty(&tx);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut embedder =
|
||||
match Embedder::from_path(&embedder_model, ModelId::new(model_id.clone())) {
|
||||
Ok(e) => e,
|
||||
Err(e) => {
|
||||
log::warn!("face sweep: cannot load the embedder: {e}");
|
||||
finish_empty(&tx);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let wanted = match faces_outstanding(&catalog, &store, &model_id) {
|
||||
Ok(w) => w,
|
||||
@@ -723,7 +723,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn landmarks_normalise_against_the_long_edge() {
|
||||
let lm = [(512.0, 256.0), (0.0, 0.0), (1024.0, 512.0), (10.0, 20.0), (5.0, 5.0)];
|
||||
let lm = [
|
||||
(512.0, 256.0),
|
||||
(0.0, 0.0),
|
||||
(1024.0, 512.0),
|
||||
(10.0, 20.0),
|
||||
(5.0, 5.0),
|
||||
];
|
||||
let n = normalise_landmarks(&lm, 1024.0);
|
||||
assert!((n[0].0 - 0.5).abs() < 1e-6);
|
||||
assert!((n[0].1 - 0.25).abs() < 1e-6);
|
||||
|
||||
+62
-28
@@ -61,7 +61,10 @@ impl PersonRow {
|
||||
/// — the exact conflation the confirmed/suggested split exists to prevent.
|
||||
pub fn display_name(&self) -> String {
|
||||
if self.name.is_empty() {
|
||||
format!("Unnamed ({} faces)", self.confirmed_faces + self.suggested_faces)
|
||||
format!(
|
||||
"Unnamed ({} faces)",
|
||||
self.confirmed_faces + self.suggested_faces
|
||||
)
|
||||
} else {
|
||||
self.name.clone()
|
||||
}
|
||||
@@ -122,7 +125,10 @@ pub struct IdentityView {
|
||||
}
|
||||
|
||||
/// Read the people rail.
|
||||
pub fn load_people(catalog: &Catalog, model_id: &str) -> Result<IdentityView, dr_catalog::CatalogError> {
|
||||
pub fn load_people(
|
||||
catalog: &Catalog,
|
||||
model_id: &str,
|
||||
) -> Result<IdentityView, dr_catalog::CatalogError> {
|
||||
let conn = catalog.connection();
|
||||
let people = faces::people(conn)?
|
||||
.into_iter()
|
||||
@@ -410,10 +416,7 @@ pub fn rename(
|
||||
/// The operation the screen exists to make cheap. A cluster that is simply
|
||||
/// right — the common case for a well-photographed person — should cost one
|
||||
/// click, not forty.
|
||||
pub fn confirm_all(
|
||||
catalog: &Catalog,
|
||||
person: PersonId,
|
||||
) -> Result<usize, dr_catalog::CatalogError> {
|
||||
pub fn confirm_all(catalog: &Catalog, person: PersonId) -> Result<usize, dr_catalog::CatalogError> {
|
||||
let conn = catalog.connection();
|
||||
let mut n = 0;
|
||||
for f in faces::for_person(conn, person, true)? {
|
||||
@@ -662,7 +665,8 @@ mod tests {
|
||||
let c = catalog();
|
||||
let i1 = image(&c, 1);
|
||||
let i2 = image(&c, 2);
|
||||
let a = faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let a =
|
||||
faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
faces::record_detections(c.connection(), i2, "w600k_mbf", 1024, &[face(2)]).unwrap();
|
||||
|
||||
let anna = faces::create_person(c.connection(), "Anna").unwrap();
|
||||
@@ -672,7 +676,10 @@ mod tests {
|
||||
assert_eq!(view.people.len(), 1);
|
||||
assert_eq!(view.people[0].name, "Anna");
|
||||
assert_eq!(view.unassigned, 1);
|
||||
assert!(!view.calibrated, "a fresh library has no fitted calibration");
|
||||
assert!(
|
||||
!view.calibrated,
|
||||
"a fresh library has no fitted calibration"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -681,9 +688,12 @@ mod tests {
|
||||
let i1 = image(&c, 1);
|
||||
let i2 = image(&c, 2);
|
||||
let i3 = image(&c, 3);
|
||||
let a = faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let b = faces::record_detections(c.connection(), i2, "w600k_mbf", 1024, &[face(2)]).unwrap();
|
||||
let d = faces::record_detections(c.connection(), i3, "w600k_mbf", 1024, &[face(3)]).unwrap();
|
||||
let a =
|
||||
faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let b =
|
||||
faces::record_detections(c.connection(), i2, "w600k_mbf", 1024, &[face(2)]).unwrap();
|
||||
let d =
|
||||
faces::record_detections(c.connection(), i3, "w600k_mbf", 1024, &[face(3)]).unwrap();
|
||||
|
||||
let anna = faces::create_person(c.connection(), "Anna").unwrap();
|
||||
faces::confirm(c.connection(), a[0], anna).unwrap();
|
||||
@@ -692,7 +702,12 @@ mod tests {
|
||||
|
||||
assert_eq!(confirm_all(&c, anna).unwrap(), 2);
|
||||
assert_eq!(confirm_all(&c, anna).unwrap(), 0, "should be idempotent");
|
||||
assert_eq!(faces::for_person(c.connection(), anna, false).unwrap().len(), 3);
|
||||
assert_eq!(
|
||||
faces::for_person(c.connection(), anna, false)
|
||||
.unwrap()
|
||||
.len(),
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
/// The correction must stick. Splitting a face off and then re-running
|
||||
@@ -702,16 +717,24 @@ mod tests {
|
||||
let c = catalog();
|
||||
let i1 = image(&c, 1);
|
||||
let i2 = image(&c, 2);
|
||||
let a = faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let b = faces::record_detections(c.connection(), i2, "w600k_mbf", 1024, &[face(2)]).unwrap();
|
||||
let a =
|
||||
faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let b =
|
||||
faces::record_detections(c.connection(), i2, "w600k_mbf", 1024, &[face(2)]).unwrap();
|
||||
|
||||
let anna = faces::create_person(c.connection(), "Anna").unwrap();
|
||||
faces::confirm(c.connection(), a[0], anna).unwrap();
|
||||
faces::suggest(c.connection(), b[0], anna, 0.95).unwrap();
|
||||
|
||||
let bob = split_off(&c, anna, &[b[0]], "Bob").unwrap();
|
||||
assert_eq!(faces::for_person(c.connection(), bob, false).unwrap().len(), 1);
|
||||
assert_eq!(faces::for_person(c.connection(), anna, true).unwrap().len(), 1);
|
||||
assert_eq!(
|
||||
faces::for_person(c.connection(), bob, false).unwrap().len(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
faces::for_person(c.connection(), anna, true).unwrap().len(),
|
||||
1
|
||||
);
|
||||
|
||||
// The next clustering pass tries again and is refused.
|
||||
assert!(
|
||||
@@ -735,8 +758,10 @@ mod tests {
|
||||
let c = catalog();
|
||||
let i1 = image(&c, 1);
|
||||
let i2 = image(&c, 2);
|
||||
let a = faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let b = faces::record_detections(c.connection(), i2, "w600k_mbf", 1024, &[face(2)]).unwrap();
|
||||
let a =
|
||||
faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let b =
|
||||
faces::record_detections(c.connection(), i2, "w600k_mbf", 1024, &[face(2)]).unwrap();
|
||||
let anna = faces::create_person(c.connection(), "Anna").unwrap();
|
||||
let annie = faces::create_person(c.connection(), "Annie").unwrap();
|
||||
faces::confirm(c.connection(), a[0], anna).unwrap();
|
||||
@@ -752,8 +777,8 @@ mod tests {
|
||||
fn named_boxes_come_back_in_proxy_pixels() {
|
||||
let c = catalog();
|
||||
let img = image(&c, 1);
|
||||
let ids = faces::record_detections(c.connection(), img, "w600k_mbf", 1024, &[face(1)])
|
||||
.unwrap();
|
||||
let ids =
|
||||
faces::record_detections(c.connection(), img, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let anna = faces::create_person(c.connection(), "Anna").unwrap();
|
||||
faces::confirm(c.connection(), ids[0], anna).unwrap();
|
||||
|
||||
@@ -777,12 +802,14 @@ mod tests {
|
||||
fn a_suggested_person_does_not_name_a_region() {
|
||||
let c = catalog();
|
||||
let img = image(&c, 1);
|
||||
let ids = faces::record_detections(c.connection(), img, "w600k_mbf", 1024, &[face(1)])
|
||||
.unwrap();
|
||||
let ids =
|
||||
faces::record_detections(c.connection(), img, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let anna = faces::create_person(c.connection(), "Anna").unwrap();
|
||||
faces::suggest(c.connection(), ids[0], anna, 0.95).unwrap();
|
||||
|
||||
assert!(named_boxes_for_image(&c, img, 1024, 683).unwrap().is_empty());
|
||||
assert!(named_boxes_for_image(&c, img, 1024, 683)
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
|
||||
// Confirming it makes the name appear.
|
||||
faces::confirm(c.connection(), ids[0], anna).unwrap();
|
||||
@@ -794,12 +821,14 @@ mod tests {
|
||||
fn an_unnamed_group_does_not_name_a_region() {
|
||||
let c = catalog();
|
||||
let img = image(&c, 1);
|
||||
let ids = faces::record_detections(c.connection(), img, "w600k_mbf", 1024, &[face(1)])
|
||||
.unwrap();
|
||||
let ids =
|
||||
faces::record_detections(c.connection(), img, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let nameless = faces::create_person(c.connection(), "").unwrap();
|
||||
faces::confirm(c.connection(), ids[0], nameless).unwrap();
|
||||
|
||||
assert!(named_boxes_for_image(&c, img, 1024, 683).unwrap().is_empty());
|
||||
assert!(named_boxes_for_image(&c, img, 1024, 683)
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -817,7 +846,11 @@ mod tests {
|
||||
height: 4,
|
||||
rgba,
|
||||
};
|
||||
assert!((mean_luma(&crop) - 1.0).abs() < 1e-3, "{}", mean_luma(&crop));
|
||||
assert!(
|
||||
(mean_luma(&crop) - 1.0).abs() < 1e-3,
|
||||
"{}",
|
||||
mean_luma(&crop)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -835,7 +868,8 @@ mod tests {
|
||||
fn deleting_everything_empties_the_screen() {
|
||||
let c = catalog();
|
||||
let i1 = image(&c, 1);
|
||||
let a = faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let a =
|
||||
faces::record_detections(c.connection(), i1, "w600k_mbf", 1024, &[face(1)]).unwrap();
|
||||
let anna = faces::create_person(c.connection(), "Anna").unwrap();
|
||||
faces::confirm(c.connection(), a[0], anna).unwrap();
|
||||
|
||||
|
||||
+21
-17
@@ -109,8 +109,7 @@ pub fn refresh(
|
||||
// Drop portraits for people who no longer exist, so a long session that
|
||||
// merges and splits repeatedly does not accumulate them.
|
||||
{
|
||||
let live: std::collections::HashSet<PersonId> =
|
||||
view.people.iter().map(|p| p.id).collect();
|
||||
let live: std::collections::HashSet<PersonId> = view.people.iter().map(|p| p.id).collect();
|
||||
ctl.covers.borrow_mut().retain(|id, _| live.contains(id));
|
||||
}
|
||||
|
||||
@@ -152,8 +151,8 @@ pub fn refresh(
|
||||
|
||||
match (ctl.selected.get(), store) {
|
||||
(Some(person), Some(store)) => {
|
||||
let cells = identity::load_faces(cat, store, person, view.calibrated)
|
||||
.unwrap_or_else(|e| {
|
||||
let cells =
|
||||
identity::load_faces(cat, store, person, view.calibrated).unwrap_or_else(|e| {
|
||||
log::warn!("identity: reading faces: {e}");
|
||||
Vec::new()
|
||||
});
|
||||
@@ -335,7 +334,9 @@ pub fn wire<S, M, P>(
|
||||
let store = store.clone();
|
||||
window.on_identity_rename(move |name| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let Some(person) = ctl.selected.get() else { return };
|
||||
let Some(person) = ctl.selected.get() else {
|
||||
return;
|
||||
};
|
||||
if let Some(cat) = catalog.borrow().as_ref() {
|
||||
if let Err(e) = identity::rename(cat, person, &name) {
|
||||
log::warn!("identity: rename: {e}");
|
||||
@@ -352,7 +353,9 @@ pub fn wire<S, M, P>(
|
||||
let store = store.clone();
|
||||
window.on_identity_confirm_face(move |id| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let Some(person) = ctl.selected.get() else { return };
|
||||
let Some(person) = ctl.selected.get() else {
|
||||
return;
|
||||
};
|
||||
if let Some(cat) = catalog.borrow().as_ref() {
|
||||
if let Err(e) = identity::confirm(cat, FaceId(id as u64), person) {
|
||||
log::warn!("identity: confirm: {e}");
|
||||
@@ -369,7 +372,9 @@ pub fn wire<S, M, P>(
|
||||
let store = store.clone();
|
||||
window.on_identity_reject_face(move |id| {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let Some(person) = ctl.selected.get() else { return };
|
||||
let Some(person) = ctl.selected.get() else {
|
||||
return;
|
||||
};
|
||||
if let Some(cat) = catalog.borrow().as_ref() {
|
||||
if let Err(e) = identity::reject(cat, FaceId(id as u64), person) {
|
||||
log::warn!("identity: reject: {e}");
|
||||
@@ -408,7 +413,9 @@ pub fn wire<S, M, P>(
|
||||
let store = store.clone();
|
||||
window.on_identity_confirm_all(move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let Some(person) = ctl.selected.get() else { return };
|
||||
let Some(person) = ctl.selected.get() else {
|
||||
return;
|
||||
};
|
||||
if let Some(cat) = catalog.borrow().as_ref() {
|
||||
match identity::confirm_all(cat, person) {
|
||||
Ok(n) => log::info!("identity: confirmed {n} suggestion(s)"),
|
||||
@@ -426,7 +433,9 @@ pub fn wire<S, M, P>(
|
||||
let store = store.clone();
|
||||
window.on_identity_split_picked(move || {
|
||||
let Some(w) = weak.upgrade() else { return };
|
||||
let Some(person) = ctl.selected.get() else { return };
|
||||
let Some(person) = ctl.selected.get() else {
|
||||
return;
|
||||
};
|
||||
let members: Vec<FaceId> = ctl.picked.borrow().clone();
|
||||
if members.is_empty() {
|
||||
return;
|
||||
@@ -488,10 +497,8 @@ pub fn wire<S, M, P>(
|
||||
|
||||
ctl.progress.set((0, 0));
|
||||
ctl.faces_found.set(0);
|
||||
*ctl.activity.borrow_mut() = Some(activity.begin(
|
||||
crate::activity::Kind::Index,
|
||||
"Indexing faces",
|
||||
));
|
||||
*ctl.activity.borrow_mut() =
|
||||
Some(activity.begin(crate::activity::Kind::Index, "Indexing faces"));
|
||||
*ctl.sweep.borrow_mut() = Some(crate::faces::spawn_face_sweep(
|
||||
catalog_path,
|
||||
store_dir,
|
||||
@@ -531,9 +538,7 @@ pub fn wire<S, M, P>(
|
||||
FaceSweepMessage::Indexed { faces, .. } => {
|
||||
let (seen, total) = ctl_tick.progress.get();
|
||||
ctl_tick.progress.set((seen + 1, total));
|
||||
ctl_tick
|
||||
.faces_found
|
||||
.set(ctl_tick.faces_found.get() + faces);
|
||||
ctl_tick.faces_found.set(ctl_tick.faces_found.get() + faces);
|
||||
}
|
||||
FaceSweepMessage::Finished { .. } => done = true,
|
||||
}
|
||||
@@ -671,4 +676,3 @@ mod tests {
|
||||
assert!(ctl.picked.borrow().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+53
-48
@@ -22,13 +22,13 @@
|
||||
mod activity;
|
||||
mod collections_ui;
|
||||
mod derived_sync;
|
||||
pub mod faces;
|
||||
pub mod identity;
|
||||
mod identity_ui;
|
||||
mod develop;
|
||||
mod export;
|
||||
pub mod faces;
|
||||
mod gradient;
|
||||
mod histogram;
|
||||
pub mod identity;
|
||||
mod identity_ui;
|
||||
mod import;
|
||||
mod import_ui;
|
||||
mod labels;
|
||||
@@ -1295,54 +1295,59 @@ pub fn run(paths: Vec<PathBuf>) -> Result<()> {
|
||||
let lib = library.clone();
|
||||
let ctl = settings.clone();
|
||||
let weak = window.as_weak();
|
||||
settings_ui::wire(&window, settings.clone(), move |s| {
|
||||
// A ceiling that moved has to be applied to what is already on
|
||||
// disk, or lowering it would only affect future downloads and the
|
||||
// cache would sit over budget indefinitely.
|
||||
lib.set_cache_budget(s.cache.original_budget_bytes);
|
||||
lib.set_keep_opened_originals(s.cache.keep_opened_originals);
|
||||
if let Some(w) = weak.upgrade() {
|
||||
refresh_export_label(&w, &ctl);
|
||||
}
|
||||
|
||||
// The axis is cut into bars when the window is loaded, so a new
|
||||
// count only shows once something reloads it. Done here, and only
|
||||
// when the figure actually moved: a setting that appears to do
|
||||
// nothing until the user scrolls is one they will change twice and
|
||||
// then leave wrong.
|
||||
if lib.set_timeline_bars(s.library.timeline_bars) {
|
||||
settings_ui::wire(
|
||||
&window,
|
||||
settings.clone(),
|
||||
move |s| {
|
||||
// A ceiling that moved has to be applied to what is already on
|
||||
// disk, or lowering it would only affect future downloads and the
|
||||
// cache would sit over budget indefinitely.
|
||||
lib.set_cache_budget(s.cache.original_budget_bytes);
|
||||
lib.set_keep_opened_originals(s.cache.keep_opened_originals);
|
||||
if let Some(w) = weak.upgrade() {
|
||||
library_ui::reload(&w, &lib);
|
||||
refresh_export_label(&w, &ctl);
|
||||
}
|
||||
}
|
||||
|
||||
// Lowering the ceiling evicts, so the figure beside it has just
|
||||
// changed — leaving the old one would show the cache still over a
|
||||
// limit that was enforced a moment ago.
|
||||
ctl.set_usage_label(describe_cache_usage(&lib));
|
||||
if let Some(w) = weak.upgrade() {
|
||||
settings_ui::render(&w, &ctl);
|
||||
}
|
||||
},
|
||||
{
|
||||
// Face coverage, read when the page opens. The figures live in the
|
||||
// catalog and the settings page holds no session, so they arrive
|
||||
// through here rather than being kept up to date continuously —
|
||||
// they are only ever looked at while this page is on screen.
|
||||
let lib = library.clone();
|
||||
let catalog = library.catalog();
|
||||
move |w: &AppWindow| {
|
||||
let store = lib.session().and_then(|(_, s)| {
|
||||
dr_thumbs::ThumbStore::open(&library::thumbs_dir(&s.server, &s.user_id)).ok()
|
||||
});
|
||||
identity_ui::refresh_coverage(w, &catalog, store.as_ref());
|
||||
w.set_identity_model_missing(
|
||||
lib.session()
|
||||
.and_then(|(_, s)| library::face_models(&s.server, &s.user_id))
|
||||
.is_none(),
|
||||
);
|
||||
}
|
||||
});
|
||||
// The axis is cut into bars when the window is loaded, so a new
|
||||
// count only shows once something reloads it. Done here, and only
|
||||
// when the figure actually moved: a setting that appears to do
|
||||
// nothing until the user scrolls is one they will change twice and
|
||||
// then leave wrong.
|
||||
if lib.set_timeline_bars(s.library.timeline_bars) {
|
||||
if let Some(w) = weak.upgrade() {
|
||||
library_ui::reload(&w, &lib);
|
||||
}
|
||||
}
|
||||
|
||||
// Lowering the ceiling evicts, so the figure beside it has just
|
||||
// changed — leaving the old one would show the cache still over a
|
||||
// limit that was enforced a moment ago.
|
||||
ctl.set_usage_label(describe_cache_usage(&lib));
|
||||
if let Some(w) = weak.upgrade() {
|
||||
settings_ui::render(&w, &ctl);
|
||||
}
|
||||
},
|
||||
{
|
||||
// Face coverage, read when the page opens. The figures live in the
|
||||
// catalog and the settings page holds no session, so they arrive
|
||||
// through here rather than being kept up to date continuously —
|
||||
// they are only ever looked at while this page is on screen.
|
||||
let lib = library.clone();
|
||||
let catalog = library.catalog();
|
||||
move |w: &AppWindow| {
|
||||
let store = lib.session().and_then(|(_, s)| {
|
||||
dr_thumbs::ThumbStore::open(&library::thumbs_dir(&s.server, &s.user_id))
|
||||
.ok()
|
||||
});
|
||||
identity_ui::refresh_coverage(w, &catalog, store.as_ref());
|
||||
w.set_identity_model_missing(
|
||||
lib.session()
|
||||
.and_then(|(_, s)| library::face_models(&s.server, &s.user_id))
|
||||
.is_none(),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
window.set_total(entries.borrow().len() as i32);
|
||||
|
||||
Reference in New Issue
Block a user