Make the Fill tool's fill click operation cancellable (#1666)

* Make FillTool a draggable tool with abortable fills

* Unify keyhints across tools

* Apply suggestions from code review

* Formatting

* Fix FillTool when draged outside of shape's layer and add toolswitch abort

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
milan-sedivy 2024-03-11 23:18:49 +02:00 committed by GitHub
parent 343523ab34
commit 01da53eba0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 17 deletions

View file

@ -253,6 +253,9 @@ pub fn default_mapping() -> Mapping {
// FillToolMessage
entry!(KeyDown(Lmb); action_dispatch=FillToolMessage::FillPrimaryColor),
entry!(KeyDown(Lmb); modifiers=[Shift], action_dispatch=FillToolMessage::FillSecondaryColor),
entry!(KeyUp(Lmb); action_dispatch=FillToolMessage::PointerUp),
entry!(KeyDown(Rmb); action_dispatch=FillToolMessage::Abort),
entry!(KeyDown(Escape); action_dispatch=FillToolMessage::Abort),
//
// BrushToolMessage
entry!(PointerMove; action_dispatch=BrushToolMessage::PointerMove),

View file

@ -10,7 +10,11 @@ pub struct FillTool {
#[impl_message(Message, ToolMessage, Fill)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize, specta::Type)]
pub enum FillToolMessage {
// Standard messages
Abort,
// Tool-specific messages
PointerUp,
FillPrimaryColor,
FillSecondaryColor,
}
@ -37,16 +41,28 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FillToo
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
self.fsm_state.process_event(message, &mut (), tool_data, &(), responses, true);
}
fn actions(&self) -> ActionList {
use FillToolFsmState::*;
advertise_actions!(FillToolMessageDiscriminant;
FillPrimaryColor,
FillSecondaryColor,
);
match self.fsm_state {
Ready => actions!(FillToolMessageDiscriminant;
FillPrimaryColor,
FillSecondaryColor,
),
Filling => actions!(FillToolMessageDiscriminant;
PointerUp,
Abort,
),
}
}
}
impl ToolTransition for FillTool {
fn event_to_message_map(&self) -> EventToMessageMap {
EventToMessageMap::default()
EventToMessageMap {
tool_abort: Some(FillToolMessage::Abort.into()),
..Default::default()
}
}
}
@ -54,6 +70,8 @@ impl ToolTransition for FillTool {
enum FillToolFsmState {
#[default]
Ready,
// Implemented as a fake dragging state that can be used to abort unwanted fills
Filling,
}
impl Fsm for FillToolFsmState {
@ -68,20 +86,33 @@ impl Fsm for FillToolFsmState {
let ToolMessage::Fill(event) = event else {
return self;
};
let Some(layer_identifier) = document.click(input.mouse.position, &document.network) else {
return self;
};
let color = match event {
FillToolMessage::FillPrimaryColor => global_tool_data.primary_color,
FillToolMessage::FillSecondaryColor => global_tool_data.secondary_color,
};
let fill = Fill::Solid(color);
responses.add(DocumentMessage::StartTransaction);
responses.add(GraphOperationMessage::FillSet { layer: layer_identifier, fill });
responses.add(DocumentMessage::CommitTransaction);
match (self, event) {
(FillToolFsmState::Ready, color_event) => {
let Some(layer_identifier) = document.click(input.mouse.position, &document.network) else {
return self;
};
// TODO: Use a match statement here instead of if-else
let color = if color_event == FillToolMessage::FillPrimaryColor {
global_tool_data.primary_color
} else {
global_tool_data.secondary_color
};
let fill = Fill::Solid(color);
FillToolFsmState::Ready
responses.add(DocumentMessage::StartTransaction);
responses.add(GraphOperationMessage::FillSet { layer: layer_identifier, fill });
responses.add(DocumentMessage::CommitTransaction);
FillToolFsmState::Filling
}
(FillToolFsmState::Filling, FillToolMessage::PointerUp) => FillToolFsmState::Ready,
(FillToolFsmState::Filling, FillToolMessage::Abort) => {
responses.add(DocumentMessage::AbortTransaction);
return FillToolFsmState::Ready;
}
_ => self,
}
}
fn update_hints(&self, responses: &mut VecDeque<Message>) {
@ -90,6 +121,7 @@ impl Fsm for FillToolFsmState {
HintInfo::mouse(MouseMotion::Lmb, "Fill with Primary"),
HintInfo::keys_and_mouse([Key::Shift], MouseMotion::Lmb, "Fill with Secondary"),
])]),
FillToolFsmState::Filling => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
};
responses.add(FrontendMessage::UpdateInputHints { hint_data });