mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-20 11:28:30 +08:00
Improve history states (#932)
* Add some more history states
* Fix undo whilst drawing
* Paste image history
* Toggle output and preview history
* Code review nits
* Remove extra '{'
* Fix typo
* Fix about.toml
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
6e142627a3
commit
2bcc3d3baf
@@ -100,8 +100,8 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
|
||||
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
||||
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
||||
|
||||
// Send Properties to the frontend
|
||||
tool_data.tools.get(&tool_type).unwrap().register_properties(responses, LayoutTarget::ToolOptions);
|
||||
// Send tool options to the frontend
|
||||
responses.push_back(ToolMessage::RefreshToolOptions.into());
|
||||
|
||||
// Notify the frontend about the new active tool to be displayed
|
||||
tool_data.register_properties(responses, LayoutTarget::ToolShelf);
|
||||
|
||||
@@ -310,7 +310,7 @@ impl SelectedGradient {
|
||||
};
|
||||
|
||||
// Clear the gradient if layer deleted
|
||||
let Ok(layer) = document.document_legacy.layer(&inner_gradient.path) else{
|
||||
let Ok(layer) = document.document_legacy.layer(&inner_gradient.path) else {
|
||||
responses.push_back(ToolMessage::RefreshToolOptions.into());
|
||||
*gradient = None;
|
||||
return;
|
||||
@@ -320,7 +320,7 @@ impl SelectedGradient {
|
||||
inner_gradient.transform = gradient_space_transform(&inner_gradient.path, layer, document, font_cache);
|
||||
|
||||
// Clear if no longer a gradient
|
||||
let Some(gradient) = layer.style().ok().and_then(|style|style.fill().as_gradient()) else{
|
||||
let Some(gradient) = layer.style().ok().and_then(|style|style.fill().as_gradient()) else {
|
||||
responses.push_back(ToolMessage::RefreshToolOptions.into());
|
||||
*gradient = None;
|
||||
return;
|
||||
@@ -462,7 +462,7 @@ impl Fsm for GradientToolFsmState {
|
||||
self
|
||||
}
|
||||
(GradientToolFsmState::Ready, GradientToolMessage::DeleteStop) => {
|
||||
let Some(selected_gradient) = &mut tool_data.selected_gradient else{
|
||||
let Some(selected_gradient) = &mut tool_data.selected_gradient else {
|
||||
return self;
|
||||
};
|
||||
|
||||
|
||||
@@ -473,11 +473,15 @@ impl Fsm for SelectToolFsmState {
|
||||
// If the user clicks on new shape, make that layer their new selection.
|
||||
// Otherwise enter the box select mode
|
||||
let state = if tool_data.pivot.is_over(input.mouse.position) {
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
|
||||
tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true);
|
||||
tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]);
|
||||
|
||||
DraggingPivot
|
||||
} else if let Some(selected_edges) = dragging_bounds {
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
|
||||
let snap_x = selected_edges.2 || selected_edges.3;
|
||||
let snap_y = selected_edges.0 || selected_edges.1;
|
||||
|
||||
@@ -498,6 +502,8 @@ impl Fsm for SelectToolFsmState {
|
||||
|
||||
ResizingBounds
|
||||
} else if rotating_bounds {
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
|
||||
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
||||
let selected = selected.iter().collect::<Vec<_>>();
|
||||
let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.center_of_transformation, &selected, responses, &document.document_legacy);
|
||||
@@ -679,6 +685,12 @@ impl Fsm for SelectToolFsmState {
|
||||
Ready
|
||||
}
|
||||
(ResizingBounds, DragStop) => {
|
||||
let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON {
|
||||
true => DocumentMessage::Undo,
|
||||
false => DocumentMessage::CommitTransaction,
|
||||
};
|
||||
responses.push_back(response.into());
|
||||
|
||||
tool_data.snap_manager.cleanup(responses);
|
||||
|
||||
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
||||
@@ -688,6 +700,12 @@ impl Fsm for SelectToolFsmState {
|
||||
Ready
|
||||
}
|
||||
(RotatingBounds, DragStop) => {
|
||||
let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON {
|
||||
true => DocumentMessage::Undo,
|
||||
false => DocumentMessage::CommitTransaction,
|
||||
};
|
||||
responses.push_back(response.into());
|
||||
|
||||
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
||||
bounds.original_transforms.clear();
|
||||
}
|
||||
@@ -695,6 +713,12 @@ impl Fsm for SelectToolFsmState {
|
||||
Ready
|
||||
}
|
||||
(DraggingPivot, DragStop) => {
|
||||
let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON {
|
||||
true => DocumentMessage::Undo,
|
||||
false => DocumentMessage::CommitTransaction,
|
||||
};
|
||||
responses.push_back(response.into());
|
||||
|
||||
tool_data.snap_manager.cleanup(responses);
|
||||
|
||||
Ready
|
||||
@@ -769,6 +793,8 @@ impl Fsm for SelectToolFsmState {
|
||||
self
|
||||
}
|
||||
(_, SetPivot { position }) => {
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
|
||||
let pos: Option<DVec2> = position.into();
|
||||
tool_data.pivot.set_normalized_position(pos.unwrap(), document, font_cache, responses);
|
||||
|
||||
|
||||
@@ -210,10 +210,18 @@ impl Default for TextToolFsmState {
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct TextToolData {
|
||||
path: Vec<LayerId>,
|
||||
layer_path: Vec<LayerId>,
|
||||
overlays: Vec<Vec<LayerId>>,
|
||||
}
|
||||
|
||||
impl TextToolData {
|
||||
/// Set the editing state of the currently modifying layer
|
||||
fn set_editing(&self, editable: bool, responses: &mut VecDeque<Message>) {
|
||||
let path = self.layer_path.clone();
|
||||
responses.push_back(DocumentMessage::SetTextboxEditability { path, editable }.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_from_box(pos1: DVec2, pos2: DVec2) -> [f64; 6] {
|
||||
DAffine2::from_scale_angle_translation((pos2 - pos1).round(), 0., pos1.round() - DVec2::splat(0.5)).to_cols_array()
|
||||
}
|
||||
@@ -293,55 +301,44 @@ impl Fsm for TextToolFsmState {
|
||||
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
|
||||
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
|
||||
|
||||
let new_state = if let Some(l) = document
|
||||
// Check if the user has selected an existing text layer
|
||||
let new_state = if let Some(clicked_text_layer_path) = document
|
||||
.document_legacy
|
||||
.intersects_quad_root(quad, font_cache)
|
||||
.last()
|
||||
.filter(|l| document.document_legacy.layer(l).map(|l| l.as_text().is_ok()).unwrap_or(false))
|
||||
// Editing existing text
|
||||
{
|
||||
if state == TextToolFsmState::Editing {
|
||||
responses.push_back(
|
||||
DocumentMessage::SetTextboxEditability {
|
||||
path: tool_data.path.clone(),
|
||||
editable: false,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
tool_data.set_editing(false, responses);
|
||||
}
|
||||
|
||||
tool_data.path = l.clone();
|
||||
tool_data.layer_path = clicked_text_layer_path.clone();
|
||||
|
||||
responses.push_back(
|
||||
DocumentMessage::SetTextboxEditability {
|
||||
path: tool_data.path.clone(),
|
||||
editable: true,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(
|
||||
DocumentMessage::SetSelectedLayers {
|
||||
replacement_selected_layers: vec![tool_data.path.clone()],
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
|
||||
tool_data.set_editing(true, responses);
|
||||
|
||||
let replacement_selected_layers = vec![tool_data.layer_path.clone()];
|
||||
responses.push_back(DocumentMessage::SetSelectedLayers { replacement_selected_layers }.into());
|
||||
|
||||
Editing
|
||||
}
|
||||
// Creating new text
|
||||
// Create new text
|
||||
else if state == TextToolFsmState::Ready {
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
|
||||
let transform = DAffine2::from_translation(input.mouse.position).to_cols_array();
|
||||
let font_size = tool_options.font_size;
|
||||
let font_name = tool_options.font_name.clone();
|
||||
let font_style = tool_options.font_style.clone();
|
||||
tool_data.path = document.get_path_for_new_layer();
|
||||
tool_data.layer_path = document.get_path_for_new_layer();
|
||||
|
||||
responses.push_back(
|
||||
Operation::AddText {
|
||||
path: tool_data.path.clone(),
|
||||
path: tool_data.layer_path.clone(),
|
||||
transform: DAffine2::ZERO.to_cols_array(),
|
||||
insert_index: -1,
|
||||
text: r#""#.to_string(),
|
||||
text: String::new(),
|
||||
style: style::PathStyle::new(None, Fill::solid(global_tool_data.primary_color)),
|
||||
size: font_size as f64,
|
||||
font_name,
|
||||
@@ -351,37 +348,22 @@ impl Fsm for TextToolFsmState {
|
||||
);
|
||||
responses.push_back(
|
||||
Operation::SetLayerTransformInViewport {
|
||||
path: tool_data.path.clone(),
|
||||
path: tool_data.layer_path.clone(),
|
||||
transform,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
responses.push_back(
|
||||
DocumentMessage::SetTextboxEditability {
|
||||
path: tool_data.path.clone(),
|
||||
editable: true,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
tool_data.set_editing(true, responses);
|
||||
|
||||
responses.push_back(
|
||||
DocumentMessage::SetSelectedLayers {
|
||||
replacement_selected_layers: vec![tool_data.path.clone()],
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
let replacement_selected_layers = vec![tool_data.layer_path.clone()];
|
||||
|
||||
responses.push_back(DocumentMessage::SetSelectedLayers { replacement_selected_layers }.into());
|
||||
|
||||
Editing
|
||||
} else {
|
||||
// Removing old text as editable
|
||||
responses.push_back(
|
||||
DocumentMessage::SetTextboxEditability {
|
||||
path: tool_data.path.clone(),
|
||||
editable: false,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
tool_data.set_editing(false, responses);
|
||||
|
||||
resize_overlays(&mut tool_data.overlays, responses, 0);
|
||||
|
||||
@@ -392,13 +374,7 @@ impl Fsm for TextToolFsmState {
|
||||
}
|
||||
(state, Abort) => {
|
||||
if state == TextToolFsmState::Editing {
|
||||
responses.push_back(
|
||||
DocumentMessage::SetTextboxEditability {
|
||||
path: tool_data.path.clone(),
|
||||
editable: false,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
tool_data.set_editing(false, responses);
|
||||
}
|
||||
|
||||
resize_overlays(&mut tool_data.overlays, responses, 0);
|
||||
@@ -411,21 +387,10 @@ impl Fsm for TextToolFsmState {
|
||||
Editing
|
||||
}
|
||||
(Editing, TextChange { new_text }) => {
|
||||
responses.push_back(
|
||||
Operation::SetTextContent {
|
||||
path: tool_data.path.clone(),
|
||||
new_text,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
let path = tool_data.layer_path.clone();
|
||||
responses.push_back(Operation::SetTextContent { path, new_text }.into());
|
||||
|
||||
responses.push_back(
|
||||
DocumentMessage::SetTextboxEditability {
|
||||
path: tool_data.path.clone(),
|
||||
editable: false,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
tool_data.set_editing(false, responses);
|
||||
|
||||
resize_overlays(&mut tool_data.overlays, responses, 0);
|
||||
|
||||
@@ -433,10 +398,10 @@ impl Fsm for TextToolFsmState {
|
||||
}
|
||||
(Editing, UpdateBounds { new_text }) => {
|
||||
resize_overlays(&mut tool_data.overlays, responses, 1);
|
||||
let text = document.document_legacy.layer(&tool_data.path).unwrap().as_text().unwrap();
|
||||
let text = document.document_legacy.layer(&tool_data.layer_path).unwrap().as_text().unwrap();
|
||||
let quad = text.bounding_box(&new_text, text.load_face(font_cache));
|
||||
|
||||
let transformed_quad = document.document_legacy.multiply_transforms(&tool_data.path).unwrap() * quad;
|
||||
let transformed_quad = document.document_legacy.multiply_transforms(&tool_data.layer_path).unwrap() * quad;
|
||||
let bounds = transformed_quad.bounding_box();
|
||||
|
||||
let operation = Operation::SetLayerTransformInViewport {
|
||||
|
||||
Reference in New Issue
Block a user