fix: resolve offset and theme regressions
Keep OFFSET picks current across newly created entities and preserve the command after pick-first offsets. Use palette-provided contrast colors for active status controls and Alias Editor removal buttons. Refs #653 Refs #658 Refs #659
This commit is contained in:
parent
62818cfd7b
commit
e35348525a
4 changed files with 50 additions and 31 deletions
|
|
@ -783,9 +783,6 @@ enum Step {
|
||||||
/// pre-selection when OFFSET starts with objects selected (#422).
|
/// pre-selection when OFFSET starts with objects selected (#422).
|
||||||
targets: Vec<EntityType>,
|
targets: Vec<EntityType>,
|
||||||
locked: Option<f64>,
|
locked: Option<f64>,
|
||||||
/// True when the targets came from the pre-selection: the commit ends
|
|
||||||
/// the command instead of looping back for another pick.
|
|
||||||
from_selection: bool,
|
|
||||||
/// Keep the side-pick step active and use each new result as the source
|
/// Keep the side-pick step active and use each new result as the source
|
||||||
/// for the next offset.
|
/// for the next offset.
|
||||||
multiple: bool,
|
multiple: bool,
|
||||||
|
|
@ -796,6 +793,10 @@ pub struct OffsetCommand {
|
||||||
step: Step,
|
step: Step,
|
||||||
all_entities: Vec<EntityType>,
|
all_entities: Vec<EntityType>,
|
||||||
entity_index: ModifyEntityIndex,
|
entity_index: ModifyEntityIndex,
|
||||||
|
/// Live entity supplied by the scene before an object-pick is handled.
|
||||||
|
/// Unlike the command's opening snapshot, this also includes objects
|
||||||
|
/// created by earlier offsets while the command remains active.
|
||||||
|
picked: Option<EntityType>,
|
||||||
/// Pre-selected offsettable objects (pick-first, #422); consumed when the
|
/// Pre-selected offsettable objects (pick-first, #422); consumed when the
|
||||||
/// distance step resolves.
|
/// distance step resolves.
|
||||||
preselected: Vec<EntityType>,
|
preselected: Vec<EntityType>,
|
||||||
|
|
@ -822,6 +823,7 @@ impl OffsetCommand {
|
||||||
step: Step::Distance,
|
step: Step::Distance,
|
||||||
all_entities,
|
all_entities,
|
||||||
entity_index,
|
entity_index,
|
||||||
|
picked: None,
|
||||||
preselected: Vec::new(),
|
preselected: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -834,6 +836,7 @@ impl OffsetCommand {
|
||||||
step: Step::Distance,
|
step: Step::Distance,
|
||||||
all_entities,
|
all_entities,
|
||||||
entity_index,
|
entity_index,
|
||||||
|
picked: None,
|
||||||
preselected: targets,
|
preselected: targets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -848,7 +851,6 @@ impl OffsetCommand {
|
||||||
self.step = Step::PickSide {
|
self.step = Step::PickSide {
|
||||||
targets: std::mem::take(&mut self.preselected),
|
targets: std::mem::take(&mut self.preselected),
|
||||||
locked,
|
locked,
|
||||||
from_selection: true,
|
|
||||||
multiple: false,
|
multiple: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -931,6 +933,14 @@ impl CadCommand for OffsetCommand {
|
||||||
matches!(self.step, Step::SelectObject { .. })
|
matches!(self.step, Step::SelectObject { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn inject_before_entity_pick(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inject_picked_entity(&mut self, entity: EntityType) {
|
||||||
|
self.picked = Some(entity);
|
||||||
|
}
|
||||||
|
|
||||||
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
|
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
|
||||||
let locked = match &self.step {
|
let locked = match &self.step {
|
||||||
Step::SelectObject { locked } => *locked,
|
Step::SelectObject { locked } => *locked,
|
||||||
|
|
@ -940,9 +950,11 @@ impl CadCommand for OffsetCommand {
|
||||||
return CmdResult::NeedPoint;
|
return CmdResult::NeedPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
let entity = self
|
let entity = self.picked.take().or_else(|| {
|
||||||
.entity_index.get(&self.all_entities, handle)
|
self.entity_index
|
||||||
.cloned();
|
.get(&self.all_entities, handle)
|
||||||
|
.cloned()
|
||||||
|
});
|
||||||
|
|
||||||
// Accept every type compute_offsets can offset — including XLine (#296),
|
// Accept every type compute_offsets can offset — including XLine (#296),
|
||||||
// and Ellipse/Spline whose offset functions existed but weren't reachable.
|
// and Ellipse/Spline whose offset functions existed but weren't reachable.
|
||||||
|
|
@ -951,7 +963,6 @@ impl CadCommand for OffsetCommand {
|
||||||
self.step = Step::PickSide {
|
self.step = Step::PickSide {
|
||||||
targets: vec![e],
|
targets: vec![e],
|
||||||
locked,
|
locked,
|
||||||
from_selection: false,
|
|
||||||
multiple: false,
|
multiple: false,
|
||||||
};
|
};
|
||||||
CmdResult::NeedPoint
|
CmdResult::NeedPoint
|
||||||
|
|
@ -1044,13 +1055,12 @@ impl CadCommand for OffsetCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||||
let (locked, targets, from_selection, multiple) = match &self.step {
|
let (locked, targets, multiple) = match &self.step {
|
||||||
Step::PickSide {
|
Step::PickSide {
|
||||||
locked,
|
locked,
|
||||||
targets,
|
targets,
|
||||||
from_selection,
|
|
||||||
multiple,
|
multiple,
|
||||||
} => (*locked, targets.clone(), *from_selection, *multiple),
|
} => (*locked, targets.clone(), *multiple),
|
||||||
_ => return CmdResult::NeedPoint,
|
_ => return CmdResult::NeedPoint,
|
||||||
};
|
};
|
||||||
// Each target offsets by its own through-distance (or the locked
|
// Each target offsets by its own through-distance (or the locked
|
||||||
|
|
@ -1073,7 +1083,6 @@ impl CadCommand for OffsetCommand {
|
||||||
self.step = Step::PickSide {
|
self.step = Step::PickSide {
|
||||||
targets: news.clone(),
|
targets: news.clone(),
|
||||||
locked,
|
locked,
|
||||||
from_selection,
|
|
||||||
multiple: true,
|
multiple: true,
|
||||||
};
|
};
|
||||||
return if news.len() == 1 {
|
return if news.len() == 1 {
|
||||||
|
|
@ -1082,17 +1091,13 @@ impl CadCommand for OffsetCommand {
|
||||||
CmdResult::CommitEntities(news)
|
CmdResult::CommitEntities(news)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if from_selection {
|
|
||||||
// Pre-selection commit ends the command in one undo step.
|
|
||||||
return CmdResult::ReplaceMany(vec![], news);
|
|
||||||
}
|
|
||||||
// Classic loop (#418): commit this offset and go back to the object
|
// Classic loop (#418): commit this offset and go back to the object
|
||||||
// pick at the same distance, until Enter / Esc finishes.
|
// pick at the same distance, until Enter / Esc finishes.
|
||||||
self.step = Step::SelectObject { locked };
|
self.step = Step::SelectObject { locked };
|
||||||
if news.len() == 1 {
|
if news.len() == 1 {
|
||||||
CmdResult::CommitEntity(news.pop().unwrap())
|
CmdResult::CommitEntity(news.pop().unwrap())
|
||||||
} else {
|
} else {
|
||||||
CmdResult::ReplaceMany(vec![], news)
|
CmdResult::CommitEntities(news)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -449,6 +449,20 @@ pub fn themed_primary<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a,
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Render an icon with the foreground chosen for a weak primary surface.
|
||||||
|
pub fn themed_primary_weak_text<'a, M: 'a>(
|
||||||
|
bytes: &'static [u8],
|
||||||
|
size: f32,
|
||||||
|
) -> Element<'a, M> {
|
||||||
|
svg(svg::Handle::from_memory(bytes))
|
||||||
|
.width(size)
|
||||||
|
.height(size)
|
||||||
|
.style(|theme: &Theme, _| svg::Style {
|
||||||
|
color: Some(theme.palette().primary.weak.text),
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Render a positive-state chrome icon with the active Iced theme's success color.
|
/// Render a positive-state chrome icon with the active Iced theme's success color.
|
||||||
pub fn themed_success<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, M> {
|
pub fn themed_success<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, M> {
|
||||||
svg(svg::Handle::from_memory(bytes))
|
svg(svg::Handle::from_memory(bytes))
|
||||||
|
|
@ -569,8 +583,8 @@ pub fn themed_arrow_left<'a, M: 'a>(size: f32) -> Element<'a, M> {
|
||||||
themed(TRI_LEFT, size)
|
themed(TRI_LEFT, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn themed_primary_arrow_down<'a, M: 'a>(size: f32) -> Element<'a, M> {
|
pub fn themed_primary_weak_arrow_down<'a, M: 'a>(size: f32) -> Element<'a, M> {
|
||||||
themed_primary(TRI_DOWN, size)
|
themed_primary_weak_text(TRI_DOWN, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn themed_secondary_arrow_down<'a, M: 'a>(size: f32) -> Element<'a, M> {
|
pub fn themed_secondary_arrow_down<'a, M: 'a>(size: f32) -> Element<'a, M> {
|
||||||
|
|
|
||||||
|
|
@ -636,7 +636,7 @@ fn tip_node<'a>(content: Element<'a, Message>, body: Element<'a, Message>) -> El
|
||||||
/// call site already wraps it with.
|
/// call site already wraps it with.
|
||||||
fn toggle_pill(icon: &'static [u8], active: bool, msg: Message) -> Element<'static, Message> {
|
fn toggle_pill(icon: &'static [u8], active: bool, msg: Message) -> Element<'static, Message> {
|
||||||
let icon = if active {
|
let icon = if active {
|
||||||
crate::ui::icons::themed_primary(icon, 17.0)
|
crate::ui::icons::themed_primary_weak_text(icon, 17.0)
|
||||||
} else {
|
} else {
|
||||||
crate::ui::icons::themed_secondary(icon, 17.0)
|
crate::ui::icons::themed_secondary(icon, 17.0)
|
||||||
};
|
};
|
||||||
|
|
@ -646,12 +646,12 @@ fn toggle_pill(icon: &'static [u8], active: bool, msg: Message) -> Element<'stat
|
||||||
let mut style = button::subtle(theme, status);
|
let mut style = button::subtle(theme, status);
|
||||||
if active {
|
if active {
|
||||||
let palette = theme.palette();
|
let palette = theme.palette();
|
||||||
style.background = Some(Background::Color(match status {
|
style.background = Some(Background::Color(palette.primary.weak.color));
|
||||||
button::Status::Hovered => palette.primary.base.color,
|
|
||||||
_ => palette.primary.weak.color,
|
|
||||||
}));
|
|
||||||
style.text_color = palette.primary.weak.text;
|
style.text_color = palette.primary.weak.text;
|
||||||
style.border.color = palette.primary.base.color;
|
style.border.color = match status {
|
||||||
|
button::Status::Hovered => palette.primary.strong.color,
|
||||||
|
_ => palette.primary.base.color,
|
||||||
|
};
|
||||||
style.border.width = 1.0;
|
style.border.width = 1.0;
|
||||||
}
|
}
|
||||||
style
|
style
|
||||||
|
|
@ -724,7 +724,7 @@ fn polar_pill<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let polar_icon = if active {
|
let polar_icon = if active {
|
||||||
crate::ui::icons::themed_primary(crate::ui::icons::ST_POLAR, 17.0)
|
crate::ui::icons::themed_primary_weak_text(crate::ui::icons::ST_POLAR, 17.0)
|
||||||
} else {
|
} else {
|
||||||
crate::ui::icons::themed_secondary(crate::ui::icons::ST_POLAR, 17.0)
|
crate::ui::icons::themed_secondary(crate::ui::icons::ST_POLAR, 17.0)
|
||||||
};
|
};
|
||||||
|
|
@ -735,7 +735,7 @@ fn polar_pill<'a>(
|
||||||
let palette = theme.palette();
|
let palette = theme.palette();
|
||||||
text::Style {
|
text::Style {
|
||||||
color: Some(if active {
|
color: Some(if active {
|
||||||
palette.primary.base.color
|
palette.primary.weak.text
|
||||||
} else {
|
} else {
|
||||||
palette.background.base.text.scale_alpha(0.72)
|
palette.background.base.text.scale_alpha(0.72)
|
||||||
}),
|
}),
|
||||||
|
|
@ -759,7 +759,7 @@ fn polar_pill<'a>(
|
||||||
menu_tip(
|
menu_tip(
|
||||||
mouse_area(
|
mouse_area(
|
||||||
container(if active {
|
container(if active {
|
||||||
crate::ui::icons::themed_primary_arrow_down(9.0)
|
crate::ui::icons::themed_primary_weak_arrow_down(9.0)
|
||||||
} else {
|
} else {
|
||||||
crate::ui::icons::themed_secondary_arrow_down(9.0)
|
crate::ui::icons::themed_secondary_arrow_down(9.0)
|
||||||
})
|
})
|
||||||
|
|
@ -789,7 +789,7 @@ fn osnap_btn<'a>(
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let on = active || snap_enabled;
|
let on = active || snap_enabled;
|
||||||
let snap_icon = if on {
|
let snap_icon = if on {
|
||||||
crate::ui::icons::themed_primary(crate::ui::icons::ST_OSNAP, 17.0)
|
crate::ui::icons::themed_primary_weak_text(crate::ui::icons::ST_OSNAP, 17.0)
|
||||||
} else {
|
} else {
|
||||||
crate::ui::icons::themed_secondary(crate::ui::icons::ST_OSNAP, 17.0)
|
crate::ui::icons::themed_secondary(crate::ui::icons::ST_OSNAP, 17.0)
|
||||||
};
|
};
|
||||||
|
|
@ -804,7 +804,7 @@ fn osnap_btn<'a>(
|
||||||
menu_tip(
|
menu_tip(
|
||||||
mouse_area(
|
mouse_area(
|
||||||
container(if on {
|
container(if on {
|
||||||
crate::ui::icons::themed_primary_arrow_down(9.0)
|
crate::ui::icons::themed_primary_weak_arrow_down(9.0)
|
||||||
} else {
|
} else {
|
||||||
crate::ui::icons::themed_secondary_arrow_down(9.0)
|
crate::ui::icons::themed_secondary_arrow_down(9.0)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ pub fn view_window(
|
||||||
.size(13)
|
.size(13)
|
||||||
.padding([3, 6])
|
.padding([3, 6])
|
||||||
.width(sizing.width);
|
.width(sizing.width);
|
||||||
let del = button(crate::ui::icons::themed_danger(crate::ui::icons::CLOSE, 12.0))
|
let del = button(crate::ui::icons::themed_danger_text(crate::ui::icons::CLOSE, 12.0))
|
||||||
.on_press(Message::AliasEditorRemove(idx))
|
.on_press(Message::AliasEditorRemove(idx))
|
||||||
.padding([2, 6])
|
.padding([2, 6])
|
||||||
.style(button::danger);
|
.style(button::danger);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue