Fix Select tool's scale nudging with multi-layer selection (#1699)

* Fix scale nudging for multiple selection

* Use message discriminant for filtering where possible

* Remove unnecessary parameter from `selected_bounds_document_space`

* Fix the error `target.closest is not a function`

* Minor cleanup

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Elbert Ronnie 2024-03-26 01:50:30 +05:30 committed by GitHub
parent 0a9bd41be1
commit e9510c5fee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 41 additions and 29 deletions

View File

@ -40,7 +40,12 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerStructure),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
];
const DEBUG_MESSAGE_BLOCK_LIST: &[&str] = &["AnimationFrame", "PointerMove", "PointerOutsideViewport", "FrameTimeAdvance"];
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::AnimationFrame)),
MessageDiscriminant::InputPreprocessor(InputPreprocessorMessageDiscriminant::FrameTimeAdvance),
];
// TODO: Find a way to combine these with the list above. We use strings for now since these are the standard variant names used by multiple messages. But having these also type-checked would be best.
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport"];
impl Dispatcher {
pub fn new() -> Self {
@ -223,9 +228,11 @@ impl Dispatcher {
/// Logs a message that is about to be executed,
/// either as a tree with a discriminant or the entire payload (depending on settings)
fn log_message(&self, message: &Message, queues: &[VecDeque<Message>], message_logging_verbosity: MessageLoggingVerbosity) {
let message_name = MessageDiscriminant::from(message).local_name();
let discriminant = MessageDiscriminant::from(message);
let is_blocked = DEBUG_MESSAGE_BLOCK_LIST.iter().any(|&blocked_discriminant| discriminant == blocked_discriminant)
|| DEBUG_MESSAGE_ENDING_BLOCK_LIST.iter().any(|blocked_name| discriminant.local_name().ends_with(blocked_name));
if !DEBUG_MESSAGE_BLOCK_LIST.iter().any(|blocked_name| message_name.ends_with(blocked_name)) {
if !is_blocked {
match message_logging_verbosity {
MessageLoggingVerbosity::Off => {}
MessageLoggingVerbosity::Names => {

View File

@ -457,18 +457,23 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let opposite_corner = ipp.keyboard.key(resize_opposite_corner);
let delta = DVec2::new(delta_x, delta_y);
for layer in self.selected_nodes.selected_layers(self.metadata()) {
match ipp.keyboard.key(resize) {
// Nudge translation
if !ipp.keyboard.key(resize) {
responses.add(GraphOperationMessage::TransformChange {
layer,
transform: DAffine2::from_translation(delta),
transform_in: TransformIn::Local,
skip_rerender: false,
});
false => {
for layer in self.selected_nodes.selected_layers(self.metadata()) {
responses.add(GraphOperationMessage::TransformChange {
layer,
transform: DAffine2::from_translation(delta),
transform_in: TransformIn::Local,
skip_rerender: false,
});
}
}
// Nudge resize
else if let Some([existing_top_left, existing_bottom_right]) = self.metadata.bounding_box_document(layer) {
true => {
let selected_bounding_box = self.metadata().selected_bounds_document_space(false, &self.selected_nodes);
let Some([existing_top_left, existing_bottom_right]) = selected_bounding_box else { return };
let size = existing_bottom_right - existing_top_left;
let new_size = size + if opposite_corner { -delta } else { delta };
let enlargement_factor = new_size / size;
@ -486,16 +491,18 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let pivot = DAffine2::from_translation(pivot);
let transformation = pivot * scale * pivot.inverse();
let to = self.metadata().document_to_viewport.inverse() * self.metadata().downstream_transform_to_viewport(layer);
let original_transform = self.metadata().upstream_transform(layer.to_node());
let new = to.inverse() * transformation * to * original_transform;
responses.add(GraphOperationMessage::TransformSet {
layer,
transform: new,
transform_in: TransformIn::Local,
skip_rerender: false,
});
};
for layer in self.selected_nodes.selected_layers(self.metadata()) {
let to = self.metadata().document_to_viewport.inverse() * self.metadata().downstream_transform_to_viewport(layer);
let original_transform = self.metadata().upstream_transform(layer.to_node());
let new = to.inverse() * transformation * to * original_transform;
responses.add(GraphOperationMessage::TransformSet {
layer,
transform: new,
transform_in: TransformIn::Local,
skip_rerender: false,
});
}
}
}
}
DocumentMessage::PasteImage { image, mouse } => {

View File

@ -279,9 +279,9 @@ impl DocumentMetadata {
}
/// Calculates the selected layer bounds in document space
pub fn selected_bounds_document_space(&self, include_artboards: bool, metadata: &DocumentMetadata, selected_nodes: &SelectedNodes) -> Option<[DVec2; 2]> {
pub fn selected_bounds_document_space(&self, include_artboards: bool, selected_nodes: &SelectedNodes) -> Option<[DVec2; 2]> {
selected_nodes
.selected_layers(metadata)
.selected_layers(self)
.filter(|&layer| include_artboards || !self.is_artboard(layer))
.filter_map(|layer| self.bounding_box_document(layer))
.reduce(Quad::combine_bounds)

View File

@ -1031,7 +1031,7 @@ impl Fsm for SelectToolFsmState {
HintInfo::arrow_keys("Nudge Selected"),
HintInfo::keys([Key::Shift], "10x").prepend_plus(),
HintInfo::keys([Key::Alt], "Resize Corner").prepend_plus(),
HintInfo::keys([Key::Control], "Opp. Corner").prepend_plus(),
HintInfo::keys([Key::Control], "Other Corner").prepend_plus(),
]),
HintGroup(vec![
HintInfo::keys_and_mouse([Key::Alt], MouseMotion::LmbDrag, "Move Duplicate"),

View File

@ -474,9 +474,7 @@ impl NodeGraphExecutor {
// Calculate the bounding box of the region to be exported
let bounds = match export_config.bounds {
ExportBounds::AllArtwork => document.metadata().document_bounds_document_space(!export_config.transparent_background),
ExportBounds::Selection => document
.metadata()
.selected_bounds_document_space(!export_config.transparent_background, document.metadata(), &document.selected_nodes),
ExportBounds::Selection => document.metadata().selected_bounds_document_space(!export_config.transparent_background, &document.selected_nodes),
ExportBounds::Artboard(id) => document.metadata().bounding_box_document(id),
}
.ok_or_else(|| "No bounding box".to_string())?;

View File

@ -286,7 +286,7 @@
// Assumes the spawner is a sibling of this FloatingMenu component
const ownSpawner: HTMLElement | undefined = self?.parentElement?.querySelector(":scope > [data-floating-menu-spawner]") || undefined;
// Get the spawner element containing whatever element the user is hovering over now, if there is one
const targetSpawner: HTMLElement | undefined = target?.closest("[data-floating-menu-spawner]") || undefined;
const targetSpawner: HTMLElement | undefined = target?.closest?.("[data-floating-menu-spawner]") || undefined;
// HOVER TRANSFER
// Transfer from this open floating menu to a sibling floating menu if the pointer hovers to a valid neighboring floating menu spawner