Never crash: ui-egui is panic-free (Rule 9) (#57)
- canvas: partial redraws read last_damage with unwrap_or(EMPTY), and the
GPU cache is looked up with let-else after the upload.
- icons: a missing icon (and missing fallback) yields empty bytes, which
egui shows as a broken image, instead of expect.
- type tool: the caret update is an if-let instead of expect("editing").
- Variables dialog: pages return early if their JSON state is malformed
instead of unwrap.
- parity: the same hunk as #40.
- ui-egui denies unwrap/expect/panic/unimplemented/todo/unreachable
(a one-line attribute in lib.rs).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6dffa0894c
commit
86cc989e72
|
|
@ -218,7 +218,7 @@ pub fn ensure_texture(app: &mut PhotocraftApp, ctx: &egui::Context, idx: usize)
|
|||
&& last_damage.is_some();
|
||||
let t0 = crate::gpu_canvas::now_ms();
|
||||
if partial {
|
||||
let r = last_damage.unwrap().intersect(&doc.bounds());
|
||||
let r = last_damage.unwrap_or(DRect::EMPTY).intersect(&doc.bounds());
|
||||
if !r.is_empty() {
|
||||
let buf = photocraft_compose::render(&doc, r);
|
||||
let t1 = crate::gpu_canvas::now_ms();
|
||||
|
|
@ -313,7 +313,7 @@ fn ensure_gpu(app: &mut PhotocraftApp, idx: usize) -> bool {
|
|||
}
|
||||
}
|
||||
if partial && !done {
|
||||
let r = last_damage.unwrap().intersect(&doc.bounds());
|
||||
let r = last_damage.unwrap_or(DRect::EMPTY).intersect(&doc.bounds());
|
||||
if r.is_empty() {
|
||||
done = true;
|
||||
} else {
|
||||
|
|
@ -331,7 +331,7 @@ fn ensure_gpu(app: &mut PhotocraftApp, idx: usize) -> bool {
|
|||
gpu.upload_buffer_full(id.0, &full);
|
||||
app.perf.record("full", size[0] as u64 * size[1] as u64, t1 - t0, crate::gpu_canvas::now_ms() - t1);
|
||||
}
|
||||
let cache = app.canvases.get_mut(&id).expect("inserted above");
|
||||
let Some(cache) = app.canvases.get_mut(&id) else { return true };
|
||||
cache.revision = revision;
|
||||
cache.preview_key = preview_key;
|
||||
cache.on_gpu = true;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ pub fn exists(name: &str) -> bool {
|
|||
|
||||
/// An egui image for an icon, tinted.
|
||||
pub fn image(name: &str, size: f32, tint: Color32) -> egui::Image<'static> {
|
||||
let bytes = white_icons().get(name).or_else(|| white_icons().get("square")).cloned().expect("square icon present");
|
||||
let bytes = white_icons().get(name).or_else(|| white_icons().get("square")).cloned().unwrap_or_default();
|
||||
egui::Image::from_bytes(format!("bytes://icons/{name}.svg"), egui::load::Bytes::Shared(bytes)).fit_to_exact_size(Vec2::splat(size)).tint(tint)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
//! data). The [`control`] module exposes both to automation, so agents can drive and inspect every
|
||||
//! part of the interface.
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
|
||||
pub mod actions;
|
||||
pub mod adjust_ui;
|
||||
|
|
|
|||
|
|
@ -85,12 +85,13 @@ pub fn pointer_down(app: &mut PhotocraftApp, x: f64, y: f64, shift: bool) -> boo
|
|||
let id = LayerId(ed.layer);
|
||||
if hit_layer(app, x, y) == Some(id) {
|
||||
let off = hit_offset(app, id, x, y);
|
||||
let e = app.ui.text_edit.as_mut().expect("editing");
|
||||
if let Some(e) = app.ui.text_edit.as_mut() {
|
||||
e.caret = off;
|
||||
if !shift {
|
||||
e.anchor = off;
|
||||
}
|
||||
e.dragging = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
commit(app);
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ pub fn body(app: &mut PhotocraftApp, ui: &mut egui::Ui, fields: &mut Map<String,
|
|||
}
|
||||
|
||||
fn define_page(ui: &mut egui::Ui, _t: &Tokens, layers: &[(u64, String, bool)], state: &mut Value) {
|
||||
let defs = state.get_mut("defs").and_then(Value::as_array_mut).unwrap();
|
||||
let Some(defs) = state.get_mut("defs").and_then(Value::as_array_mut) else { return };
|
||||
ui.label("Variables bind a layer's visibility, text or pixels to a named data slot.");
|
||||
ui.add_space(4.0);
|
||||
let mut remove = None;
|
||||
|
|
@ -199,7 +199,7 @@ fn data_sets_page(ui: &mut egui::Ui, _t: &Tokens, state: &mut Value, fields: &mu
|
|||
ui.weak("Define at least one variable first.");
|
||||
return;
|
||||
}
|
||||
let sets = state.get_mut("dataSets").and_then(Value::as_array_mut).unwrap();
|
||||
let Some(sets) = state.get_mut("dataSets").and_then(Value::as_array_mut) else { return };
|
||||
let mut cur = fields.get("__cur").and_then(Value::as_u64).unwrap_or(0) as usize;
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
|
|
@ -233,7 +233,7 @@ fn data_sets_page(ui: &mut egui::Ui, _t: &Tokens, state: &mut Value, fields: &mu
|
|||
}
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
let values = set.get_mut("values").and_then(Value::as_array_mut).unwrap();
|
||||
let Some(values) = set.get_mut("values").and_then(Value::as_array_mut) else { return };
|
||||
for (vname, vty) in &def_meta {
|
||||
// find or create a value entry for this variable
|
||||
let idx = values.iter().position(|v| v.get("variable").and_then(Value::as_str) == Some(vname));
|
||||
|
|
|
|||
Loading…
Reference in New Issue