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;
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_solid = matches!(&info.fill_color, Some(Some(_)));
let fill_is_none = matches!(&info.fill_color, Some(None))
&& matches!(&info.fill_gradient, Some(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.label("Fill:");
if fill_mixed {
ui.label("--");
} else {
if ui.selectable_label(fill_is_none, "None").clicked() && !fill_is_none {
let action = SetFillPaintAction::solid(
layer_id, time, face_ids.clone(), None,
);
shared.pending_actions.push(Box::new(action));
if ui.selectable_label(fill_is_none && !has_image, "None").clicked() {
if has_image {
shared.pending_actions.push(Box::new(
SetImageFillAction::new(layer_id, time, face_ids.clone(), None)));
}
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 {
// Switch to solid: use existing color or default to black
let color = info.fill_color.flatten()
.unwrap_or(ShapeColor::rgba(0, 0, 0, 255));
let action = SetFillPaintAction::solid(
layer_id, time, face_ids.clone(), Some(color),
);
shared.pending_actions.push(Box::new(action));
if ui.selectable_label(has_solid && !has_image, "Solid").clicked() {
if has_image {
shared.pending_actions.push(Box::new(
SetImageFillAction::new(layer_id, time, face_ids.clone(), None)));
}
if !has_solid {
let color = info.fill_color.flatten()
.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 {
let grad = info.fill_gradient.clone().flatten()
.unwrap_or_default();
let action = SetFillPaintAction::gradient(
layer_id, time, face_ids.clone(), Some(grad),
);
shared.pending_actions.push(Box::new(action));
if ui.selectable_label(has_gradient && !has_image, "Gradient").clicked() {
if has_image {
shared.pending_actions.push(Box::new(
SetImageFillAction::new(layer_id, time, face_ids.clone(), None)));
}
if !has_gradient {
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
if !fill_mixed && has_solid {
if !fill_mixed && has_solid && !has_image {
if let Some(Some(color)) = info.fill_color {
ui.horizontal(|ui| {
let mut rgba = [color.r, color.g, color.b, color.a];
@ -868,7 +888,7 @@ impl InfopanelPane {
}
// 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 gradient_stop_editor(ui, &mut grad, &mut self.selected_shape_gradient_stop) {
let action = SetFillPaintAction::gradient(
@ -879,22 +899,17 @@ impl InfopanelPane {
}
}
// Image fill picker (assign/clear an imported image asset on the fill).
if !face_ids.is_empty() {
// Image fill editor — pick which asset (active Image type).
if has_image {
ui.horizontal(|ui| {
ui.label("Image:");
let selected_text = current_image_fill
.and_then(|id| image_assets.iter().find(|(aid, _)| *aid == id))
.map(|(_, n)| n.clone())
.unwrap_or_else(|| "None".to_string());
.unwrap_or_else(|| "(missing)".to_string());
egui::ComboBox::from_id_salt(("image_fill", path))
.selected_text(selected_text)
.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 {
if ui.selectable_label(current_image_fill == Some(*aid), name).clicked() {
shared.pending_actions.push(Box::new(