Add Gradient tool control bar buttons, Reverse Stops and Reverse Direction (#3830)
* Add Gradient tool control bar buttons, Reverse Stops and Reverse Direction * Consolidate reused gradient line updating code
This commit is contained in:
parent
b4679b0675
commit
8117ddcdb3
|
|
@ -44,6 +44,8 @@ pub enum GradientToolMessage {
|
|||
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub enum GradientOptionsUpdate {
|
||||
Type(GradientType),
|
||||
ReverseStops,
|
||||
ReverseDirection,
|
||||
}
|
||||
|
||||
impl ToolMetadata for GradientTool {
|
||||
|
|
@ -63,52 +65,27 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Grad
|
|||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||
let ToolMessage::Gradient(GradientToolMessage::UpdateOptions { options }) = message else {
|
||||
self.fsm_state.process_event(message, &mut self.data, context, &self.options, responses, false);
|
||||
|
||||
let has_gradient = has_gradient_on_selected_layers(context.document);
|
||||
if has_gradient != self.data.has_selected_gradient {
|
||||
self.data.has_selected_gradient = has_gradient;
|
||||
responses.add(ToolMessage::RefreshToolOptions);
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
match options {
|
||||
GradientOptionsUpdate::Type(gradient_type) => {
|
||||
self.options.gradient_type = gradient_type;
|
||||
let selected_layers: Vec<_> = context
|
||||
.document
|
||||
.network_interface
|
||||
.selected_nodes()
|
||||
.selected_visible_layers(&context.document.network_interface)
|
||||
.collect();
|
||||
|
||||
let mut transaction_started = false;
|
||||
for layer in selected_layers {
|
||||
if NodeGraphLayer::is_raster_layer(layer, &mut context.document.network_interface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(mut gradient) = get_gradient(layer, &context.document.network_interface)
|
||||
&& gradient.gradient_type != gradient_type
|
||||
{
|
||||
if !transaction_started {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
transaction_started = true;
|
||||
}
|
||||
gradient.gradient_type = gradient_type;
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer,
|
||||
fill: Fill::Gradient(gradient),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if transaction_started {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
}
|
||||
if let Some(selected_gradient) = &mut self.data.selected_gradient
|
||||
&& let Some(layer) = selected_gradient.layer
|
||||
&& !NodeGraphLayer::is_raster_layer(layer, &mut context.document.network_interface)
|
||||
{
|
||||
selected_gradient.gradient.gradient_type = gradient_type;
|
||||
}
|
||||
apply_gradient_update(&mut self.data, context, responses, |g| g.gradient_type != gradient_type, |g| g.gradient_type = gradient_type);
|
||||
responses.add(ToolMessage::UpdateHints);
|
||||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
responses.add(ToolMessage::UpdateCursor);
|
||||
responses.add(ToolMessage::RefreshToolOptions);
|
||||
}
|
||||
GradientOptionsUpdate::ReverseStops => {
|
||||
apply_gradient_update(&mut self.data, context, responses, |_| true, |g| g.stops = g.stops.reversed());
|
||||
}
|
||||
GradientOptionsUpdate::ReverseDirection => {
|
||||
apply_gradient_update(&mut self.data, context, responses, |_| true, |g| std::mem::swap(&mut g.start, &mut g.end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +119,52 @@ impl LayoutHolder for GradientTool {
|
|||
.selected_index(Some((self.options.gradient_type == GradientType::Radial) as u32))
|
||||
.widget_instance();
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets: vec![gradient_type] }])
|
||||
let reverse_stops = IconButton::new("Reverse", 24)
|
||||
.tooltip_label("Reverse Stops")
|
||||
.tooltip_description("Reverse the gradient color stops.")
|
||||
.disabled(!self.data.has_selected_gradient)
|
||||
.on_update(|_| {
|
||||
GradientToolMessage::UpdateOptions {
|
||||
options: GradientOptionsUpdate::ReverseStops,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance();
|
||||
|
||||
let mut widgets = vec![gradient_type, Separator::new(SeparatorStyle::Unrelated).widget_instance(), reverse_stops];
|
||||
|
||||
if self.options.gradient_type == GradientType::Radial {
|
||||
let orientation = self
|
||||
.data
|
||||
.selected_gradient
|
||||
.as_ref()
|
||||
.map(|selected_gradient| {
|
||||
let (start, end) = (selected_gradient.gradient.start, selected_gradient.gradient.end);
|
||||
if (end.x - start.x).abs() > f64::EPSILON * 1e6 {
|
||||
end.x > start.x
|
||||
} else {
|
||||
(start.x + start.y) < (end.x + end.y)
|
||||
}
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
let reverse_direction = IconButton::new(if orientation { "ReverseRadialGradientToRight" } else { "ReverseRadialGradientToLeft" }, 24)
|
||||
.tooltip_label("Reverse Direction")
|
||||
.tooltip_description("Reverse which end the gradient radiates from.")
|
||||
.disabled(!self.data.has_selected_gradient)
|
||||
.on_update(|_| {
|
||||
GradientToolMessage::UpdateOptions {
|
||||
options: GradientOptionsUpdate::ReverseDirection,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_instance();
|
||||
|
||||
widgets.push(Separator::new(SeparatorStyle::Related).widget_instance());
|
||||
widgets.push(reverse_direction);
|
||||
}
|
||||
|
||||
Layout(vec![LayoutGroup::Row { widgets }])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -492,6 +514,7 @@ struct GradientToolData {
|
|||
auto_panning: AutoPanning,
|
||||
auto_pan_shift: DVec2,
|
||||
gradient_angle: f64,
|
||||
has_selected_gradient: bool,
|
||||
}
|
||||
|
||||
impl Fsm for GradientToolFsmState {
|
||||
|
|
@ -993,9 +1016,12 @@ impl Fsm for GradientToolFsmState {
|
|||
}
|
||||
|
||||
// Initialize `gradient_angle` from the existing gradient so Ctrl (lock angle) works from the first mouse move
|
||||
if let Some(sg) = &tool_data.selected_gradient {
|
||||
let (vp_start, vp_end) = (sg.transform.transform_point2(sg.gradient.start), sg.transform.transform_point2(sg.gradient.end));
|
||||
let delta = match sg.dragging {
|
||||
if let Some(selected_gradient) = &tool_data.selected_gradient {
|
||||
let (vp_start, vp_end) = (
|
||||
selected_gradient.transform.transform_point2(selected_gradient.gradient.start),
|
||||
selected_gradient.transform.transform_point2(selected_gradient.gradient.end),
|
||||
);
|
||||
let delta = match selected_gradient.dragging {
|
||||
// When dragging End, the fixed point is start and the mouse begins at end
|
||||
GradientDragTarget::End => vp_start - vp_end,
|
||||
// When dragging Start, the fixed point is end and the mouse begins at start
|
||||
|
|
@ -1318,6 +1344,63 @@ fn compute_selected_target(tool_data: &GradientToolData) -> GradientSelectedTarg
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_gradient_update(
|
||||
data: &mut GradientToolData,
|
||||
context: &mut ToolActionMessageContext,
|
||||
responses: &mut VecDeque<Message>,
|
||||
condition: impl Fn(&Gradient) -> bool,
|
||||
update: impl Fn(&mut Gradient),
|
||||
) {
|
||||
let selected_layers: Vec<_> = context
|
||||
.document
|
||||
.network_interface
|
||||
.selected_nodes()
|
||||
.selected_visible_layers(&context.document.network_interface)
|
||||
.collect();
|
||||
|
||||
let mut transaction_started = false;
|
||||
for layer in selected_layers {
|
||||
if NodeGraphLayer::is_raster_layer(layer, &mut context.document.network_interface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(mut gradient) = get_gradient(layer, &context.document.network_interface)
|
||||
&& condition(&gradient)
|
||||
{
|
||||
if !transaction_started {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
transaction_started = true;
|
||||
}
|
||||
update(&mut gradient);
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer,
|
||||
fill: Fill::Gradient(gradient),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if transaction_started {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
}
|
||||
if let Some(selected_gradient) = &mut data.selected_gradient
|
||||
&& let Some(layer) = selected_gradient.layer
|
||||
&& !NodeGraphLayer::is_raster_layer(layer, &mut context.document.network_interface)
|
||||
{
|
||||
update(&mut selected_gradient.gradient);
|
||||
}
|
||||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
data.has_selected_gradient = has_gradient_on_selected_layers(context.document);
|
||||
responses.add(ToolMessage::RefreshToolOptions);
|
||||
}
|
||||
|
||||
fn has_gradient_on_selected_layers(document: &DocumentMessageHandler) -> bool {
|
||||
document
|
||||
.network_interface
|
||||
.selected_nodes()
|
||||
.selected_visible_layers(&document.network_interface)
|
||||
.any(|layer| get_gradient(layer, &document.network_interface).is_some())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn midpoint_is_resettable(value: f64) -> bool {
|
||||
(value - 0.5).abs() >= f64::EPSILON * 1000.
|
||||
|
|
|
|||
Loading…
Reference in New Issue