Onion/image: make Image a fill-type tab (None | Solid | Gradient | Image)

Image fill is now a tab in the Fill type row rather than a separate dropdown. When
Image is active, an asset-picker combo selects which image; switching to None/Solid/
Gradient clears the image fill (it otherwise overrides them). The Image tab only
appears when there are imported image assets.
This commit is contained in:
Skyler Lehmkuhl 2026-06-21 00:37:19 -04:00
parent 6fc3a131a6
commit aad2d5c515
1 changed files with 47 additions and 32 deletions

View File

@ -812,47 +812,67 @@ impl InfopanelPane {
self.shape_section_open = true; self.shape_section_open = true;
ui.add_space(4.0); ui.add_space(4.0);
// Fill — determine current fill type // Fill — determine current fill type. `image_fill` takes render priority,
// so when set it's the active type (overriding colour/gradient underneath);
// switching to None/Solid/Gradient clears it.
let has_image = current_image_fill.is_some();
let has_gradient = matches!(&info.fill_gradient, Some(Some(_))); let has_gradient = matches!(&info.fill_gradient, Some(Some(_)));
let has_solid = matches!(&info.fill_color, Some(Some(_))); let has_solid = matches!(&info.fill_color, Some(Some(_)));
let fill_is_none = matches!(&info.fill_color, Some(None)) let fill_is_none = matches!(&info.fill_color, Some(None))
&& matches!(&info.fill_gradient, Some(None)); && matches!(&info.fill_gradient, Some(None));
let fill_mixed = info.fill_color.is_none() && info.fill_gradient.is_none(); let fill_mixed = info.fill_color.is_none() && info.fill_gradient.is_none();
// Fill type toggle row // Fill type toggle row: None | Solid | Gradient | Image
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Fill:"); ui.label("Fill:");
if fill_mixed { if fill_mixed {
ui.label("--"); ui.label("--");
} else { } else {
if ui.selectable_label(fill_is_none, "None").clicked() && !fill_is_none { if ui.selectable_label(fill_is_none && !has_image, "None").clicked() {
let action = SetFillPaintAction::solid( if has_image {
layer_id, time, face_ids.clone(), None, shared.pending_actions.push(Box::new(
); SetImageFillAction::new(layer_id, time, face_ids.clone(), None)));
shared.pending_actions.push(Box::new(action)); }
shared.pending_actions.push(Box::new(
SetFillPaintAction::solid(layer_id, time, face_ids.clone(), None)));
} }
if ui.selectable_label(has_solid || (!has_gradient && !fill_is_none), "Solid").clicked() && !has_solid { if ui.selectable_label(has_solid && !has_image, "Solid").clicked() {
// Switch to solid: use existing color or default to black if has_image {
let color = info.fill_color.flatten() shared.pending_actions.push(Box::new(
.unwrap_or(ShapeColor::rgba(0, 0, 0, 255)); SetImageFillAction::new(layer_id, time, face_ids.clone(), None)));
let action = SetFillPaintAction::solid( }
layer_id, time, face_ids.clone(), Some(color), if !has_solid {
); let color = info.fill_color.flatten()
shared.pending_actions.push(Box::new(action)); .unwrap_or(ShapeColor::rgba(0, 0, 0, 255));
shared.pending_actions.push(Box::new(
SetFillPaintAction::solid(layer_id, time, face_ids.clone(), Some(color))));
}
} }
if ui.selectable_label(has_gradient, "Gradient").clicked() && !has_gradient { if ui.selectable_label(has_gradient && !has_image, "Gradient").clicked() {
let grad = info.fill_gradient.clone().flatten() if has_image {
.unwrap_or_default(); shared.pending_actions.push(Box::new(
let action = SetFillPaintAction::gradient( SetImageFillAction::new(layer_id, time, face_ids.clone(), None)));
layer_id, time, face_ids.clone(), Some(grad), }
); if !has_gradient {
shared.pending_actions.push(Box::new(action)); let grad = info.fill_gradient.clone().flatten().unwrap_or_default();
shared.pending_actions.push(Box::new(
SetFillPaintAction::gradient(layer_id, time, face_ids.clone(), Some(grad))));
}
}
// Image tab — only offered if there are imported image assets.
if !image_assets.is_empty() || has_image {
if ui.selectable_label(has_image, "Image").clicked() && !has_image {
if let Some((aid, _)) = image_assets.first() {
shared.pending_actions.push(Box::new(
SetImageFillAction::new(layer_id, time, face_ids.clone(), Some(*aid))));
}
}
} }
} }
}); });
// Solid fill color editor // Solid fill color editor
if !fill_mixed && has_solid { if !fill_mixed && has_solid && !has_image {
if let Some(Some(color)) = info.fill_color { if let Some(Some(color)) = info.fill_color {
ui.horizontal(|ui| { ui.horizontal(|ui| {
let mut rgba = [color.r, color.g, color.b, color.a]; let mut rgba = [color.r, color.g, color.b, color.a];
@ -868,7 +888,7 @@ impl InfopanelPane {
} }
// Gradient fill editor // Gradient fill editor
if !fill_mixed && has_gradient { if !fill_mixed && has_gradient && !has_image {
if let Some(Some(mut grad)) = info.fill_gradient.clone() { if let Some(Some(mut grad)) = info.fill_gradient.clone() {
if gradient_stop_editor(ui, &mut grad, &mut self.selected_shape_gradient_stop) { if gradient_stop_editor(ui, &mut grad, &mut self.selected_shape_gradient_stop) {
let action = SetFillPaintAction::gradient( let action = SetFillPaintAction::gradient(
@ -879,22 +899,17 @@ impl InfopanelPane {
} }
} }
// Image fill picker (assign/clear an imported image asset on the fill). // Image fill editor — pick which asset (active Image type).
if !face_ids.is_empty() { if has_image {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Image:"); ui.label("Image:");
let selected_text = current_image_fill let selected_text = current_image_fill
.and_then(|id| image_assets.iter().find(|(aid, _)| *aid == id)) .and_then(|id| image_assets.iter().find(|(aid, _)| *aid == id))
.map(|(_, n)| n.clone()) .map(|(_, n)| n.clone())
.unwrap_or_else(|| "None".to_string()); .unwrap_or_else(|| "(missing)".to_string());
egui::ComboBox::from_id_salt(("image_fill", path)) egui::ComboBox::from_id_salt(("image_fill", path))
.selected_text(selected_text) .selected_text(selected_text)
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
if ui.selectable_label(current_image_fill.is_none(), "None").clicked() {
shared.pending_actions.push(Box::new(
SetImageFillAction::new(layer_id, time, face_ids.clone(), None),
));
}
for (aid, name) in &image_assets { for (aid, name) in &image_assets {
if ui.selectable_label(current_image_fill == Some(*aid), name).clicked() { if ui.selectable_label(current_image_fill == Some(*aid), name).clicked() {
shared.pending_actions.push(Box::new( shared.pending_actions.push(Box::new(