feat: Enhance brush settings and add new layer paste functionality
- Added brush color variant and variant amount settings to the brush panel. - Introduced a checkbox for color variant and a slider for variant amount in the brush settings. - Implemented "Paste as New Layer" functionality, allowing users to paste clipboard images directly onto a new layer. - Updated menus to include the new paste option with a shortcut. - Improved layer panel iconography by replacing text buttons with SVG icons for visibility and lock toggles. - Created new SVG assets for closed eye and watercolor icons. - Enhanced history panel to provide richer descriptions for brush strokes and vector shape modifications. - Fixed the issue where the first brush stroke was missing from the history panel.
This commit is contained in:
+292
-6
@@ -1325,6 +1325,10 @@ impl Engine {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let desc = format!(
|
||||
"Add Vector Shape ({})",
|
||||
vector_shape_name_or_kind(&shape)
|
||||
);
|
||||
if let Some(layer) = self.document.active_layer_mut() {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
shapes.push(shape);
|
||||
@@ -1333,21 +1337,18 @@ impl Engine {
|
||||
self.document.modified = true;
|
||||
}
|
||||
}
|
||||
self.document.push_vector_snapshot(
|
||||
layer_idx,
|
||||
before_shapes,
|
||||
"Add Vector Shape".to_string(),
|
||||
);
|
||||
self.document.push_vector_snapshot(layer_idx, before_shapes, desc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Create the final vector layer before recording history. This keeps the first shape and
|
||||
// its auto-created layer in one atomic Undo/Redo entry instead of capturing an empty
|
||||
// intermediate raster layer.
|
||||
let desc = format!("Add Vector Shape ({})", vector_shape_name_or_kind(&shape));
|
||||
self.document.add_vector_layer_with_shape(
|
||||
format!("Vector {}", self.document.layers.len()),
|
||||
shape,
|
||||
"Add Vector Shape",
|
||||
&desc,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1395,6 +1396,14 @@ impl Engine {
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_color(&mut self, layer_id: u64, shape_idx: usize, color: [u8; 4]) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if let Some(shape) = shapes.get_mut(shape_idx) {
|
||||
@@ -1405,9 +1414,33 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
if let Some(shape) = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => shapes.get(shape_idx),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
format!("Set Stroke Color ({})", vector_shape_name_or_kind(shape)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_fill(&mut self, layer_id: u64, shape_idx: usize, fill: bool) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if let Some(shape) = shapes.get_mut(shape_idx) {
|
||||
@@ -1420,9 +1453,33 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
if let Some(shape) = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => shapes.get(shape_idx),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
format!("Toggle Fill ({})", vector_shape_name_or_kind(shape)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_fill_color(&mut self, layer_id: u64, shape_idx: usize, color: [u8; 4]) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if let Some(shape) = shapes.get_mut(shape_idx) {
|
||||
@@ -1435,9 +1492,33 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
if let Some(shape) = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => shapes.get(shape_idx),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
format!("Set Fill Color ({})", vector_shape_name_or_kind(shape)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_stroke(&mut self, layer_id: u64, shape_idx: usize, stroke: f32) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if let Some(shape) = shapes.get_mut(shape_idx) {
|
||||
@@ -1448,9 +1529,33 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
if let Some(shape) = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => shapes.get(shape_idx),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
format!("Set Stroke Width ({})", vector_shape_name_or_kind(shape)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_opacity(&mut self, layer_id: u64, shape_idx: usize, opacity: f32) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if let Some(shape) = shapes.get_mut(shape_idx) {
|
||||
@@ -1461,6 +1566,22 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
if let Some(shape) = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => shapes.get(shape_idx),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
format!("Set Shape Opacity ({})", vector_shape_name_or_kind(shape)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_bounds(
|
||||
@@ -1472,6 +1593,14 @@ impl Engine {
|
||||
x2: f32,
|
||||
y2: f32,
|
||||
) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if let Some(shape) = shapes.get_mut(shape_idx) {
|
||||
@@ -1482,9 +1611,36 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
if let Some(shape) = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => shapes.get(shape_idx),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
format!(
|
||||
"Resize Vector Shape ({})",
|
||||
vector_shape_name_or_kind(shape)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_hardness(&mut self, layer_id: u64, shape_idx: usize, hardness: f32) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if let Some(shape) = shapes.get_mut(shape_idx) {
|
||||
@@ -1495,6 +1651,25 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
if let Some(shape) = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => shapes.get(shape_idx),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
format!(
|
||||
"Set Shape Hardness ({})",
|
||||
vector_shape_name_or_kind(shape)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_line_caps(
|
||||
@@ -1575,6 +1750,14 @@ impl Engine {
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_angle(&mut self, layer_id: u64, shape_idx: usize, angle: f32) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if let Some(shape) = shapes.get_mut(shape_idx) {
|
||||
@@ -1585,6 +1768,25 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
if let Some(shape) = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => shapes.get(shape_idx),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
format!(
|
||||
"Rotate Vector Shape ({})",
|
||||
vector_shape_name_or_kind(shape)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_vector_shape_svg(&mut self, layer_id: u64, shape_idx: usize, new_svg: &str) {
|
||||
@@ -1619,6 +1821,14 @@ impl Engine {
|
||||
}
|
||||
|
||||
pub fn reorder_vector_shape(&mut self, layer_id: u64, from_idx: usize, to_idx: usize) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if from_idx < shapes.len() && to_idx < shapes.len() {
|
||||
@@ -1630,9 +1840,27 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
self.document.push_vector_snapshot(
|
||||
idx,
|
||||
Some(before),
|
||||
"Reorder Vector Shape".to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_vector_shape(&mut self, layer_id: u64, shape_idx: usize) {
|
||||
let layer_idx = self.document.layer_index_by_id(layer_id);
|
||||
let before_shapes = self
|
||||
.document
|
||||
.get_layer_by_id(layer_id)
|
||||
.and_then(|l| match &l.data {
|
||||
LayerData::Vector { shapes } => Some(shapes.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let removed_kind = before_shapes.as_ref().and_then(|shapes| {
|
||||
shapes.get(shape_idx).map(vector_shape_name_or_kind)
|
||||
});
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(layer_id) {
|
||||
if let LayerData::Vector { ref mut shapes } = layer.data {
|
||||
if shape_idx < shapes.len() {
|
||||
@@ -1643,6 +1871,13 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let (Some(idx), Some(before)) = (layer_idx, before_shapes) {
|
||||
let desc = removed_kind.map_or_else(
|
||||
|| "Delete Vector Shape".to_string(),
|
||||
|kind| format!("Delete Vector Shape ({})", kind),
|
||||
);
|
||||
self.document.push_vector_snapshot(idx, Some(before), desc);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boolean_vector_shapes(
|
||||
@@ -3996,6 +4231,57 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a short label for the shape kind, used in history descriptions.
|
||||
pub fn vector_shape_kind(shape: &VectorShape) -> String {
|
||||
match shape {
|
||||
VectorShape::Line { .. } => "Line".to_string(),
|
||||
VectorShape::Rect { .. } => "Rect".to_string(),
|
||||
VectorShape::Circle { .. } => "Circle".to_string(),
|
||||
VectorShape::Arrow { .. } => "Arrow".to_string(),
|
||||
VectorShape::Star { .. } => "Star".to_string(),
|
||||
VectorShape::Polygon { .. } => "Polygon".to_string(),
|
||||
VectorShape::Rhombus { .. } => "Rhombus".to_string(),
|
||||
VectorShape::Cylinder { .. } => "Cylinder".to_string(),
|
||||
VectorShape::Heart { .. } => "Heart".to_string(),
|
||||
VectorShape::Bubble { .. } => "Bubble".to_string(),
|
||||
VectorShape::Gear { .. } => "Gear".to_string(),
|
||||
VectorShape::Cross { .. } => "Cross".to_string(),
|
||||
VectorShape::Crescent { .. } => "Crescent".to_string(),
|
||||
VectorShape::Bolt { .. } => "Bolt".to_string(),
|
||||
VectorShape::Arrow4 { .. } => "Arrow4".to_string(),
|
||||
VectorShape::SvgShape { kind, .. } => kind.clone(),
|
||||
VectorShape::FreePath { .. } => "Path".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the shape's name if non-empty, otherwise its kind label.
|
||||
pub fn vector_shape_name_or_kind(shape: &VectorShape) -> String {
|
||||
let name = match shape {
|
||||
VectorShape::Line { name, .. }
|
||||
| VectorShape::Rect { name, .. }
|
||||
| VectorShape::Circle { name, .. }
|
||||
| VectorShape::Arrow { name, .. }
|
||||
| VectorShape::Star { name, .. }
|
||||
| VectorShape::Polygon { name, .. }
|
||||
| VectorShape::Rhombus { name, .. }
|
||||
| VectorShape::Cylinder { name, .. }
|
||||
| VectorShape::Heart { name, .. }
|
||||
| VectorShape::Bubble { name, .. }
|
||||
| VectorShape::Gear { name, .. }
|
||||
| VectorShape::Cross { name, .. }
|
||||
| VectorShape::Crescent { name, .. }
|
||||
| VectorShape::Bolt { name, .. }
|
||||
| VectorShape::Arrow4 { name, .. }
|
||||
| VectorShape::SvgShape { name, .. }
|
||||
| VectorShape::FreePath { name, .. } => name.as_str(),
|
||||
};
|
||||
if name.is_empty() {
|
||||
vector_shape_kind(shape).to_string()
|
||||
} else {
|
||||
name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_hex_color(hex: &str) -> [u8; 4] {
|
||||
let h = hex.trim_start_matches('#');
|
||||
if h.len() >= 6 {
|
||||
|
||||
@@ -318,8 +318,11 @@ impl Engine {
|
||||
}
|
||||
|
||||
if let Some(before) = before_pixels {
|
||||
self.document
|
||||
.push_draw_snapshot(layer_idx, before, None, "Brush Stroke".to_string());
|
||||
let desc = format!(
|
||||
"Brush Stroke ({})",
|
||||
crate::stroke_brush::brush_style_label(tip.style)
|
||||
);
|
||||
self.document.push_draw_snapshot(layer_idx, before, None, desc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,7 +385,7 @@ impl Engine {
|
||||
}
|
||||
if let Some(before) = before_pixels {
|
||||
self.document
|
||||
.push_draw_snapshot(layer_idx, before, None, "Fill Rect".to_string());
|
||||
.push_draw_snapshot(layer_idx, before, None, "Fill Rectangle".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,3 +477,45 @@ impl Engine {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a short human-readable label for a built-in brush style.
|
||||
///
|
||||
/// Mirrors the names used in the GUI brush preset list so history entries
|
||||
/// can include the active style, e.g. "Brush Stroke (Round)".
|
||||
pub fn brush_style_label(style: BrushStyle) -> &'static str {
|
||||
match style {
|
||||
BrushStyle::Round | BrushStyle::Default => "Round",
|
||||
BrushStyle::Square => "Square",
|
||||
BrushStyle::HardRound => "Hard Round",
|
||||
BrushStyle::SoftRound => "Soft Round",
|
||||
BrushStyle::Star => "Star",
|
||||
BrushStyle::Noise => "Noise",
|
||||
BrushStyle::Texture => "Texture",
|
||||
BrushStyle::Spray => "Spray",
|
||||
BrushStyle::Pencil => "Pencil",
|
||||
BrushStyle::Pen => "Pen",
|
||||
BrushStyle::Calligraphy => "Calligraphy",
|
||||
BrushStyle::Oil => "Oil",
|
||||
BrushStyle::Charcoal => "Charcoal",
|
||||
BrushStyle::Leaf => "Leaf",
|
||||
BrushStyle::Rock => "Rock",
|
||||
BrushStyle::Meadow => "Meadow",
|
||||
BrushStyle::Wood => "Wood",
|
||||
BrushStyle::Watercolor => "Watercolor",
|
||||
BrushStyle::Marker => "Marker",
|
||||
BrushStyle::Sketch => "Sketch",
|
||||
BrushStyle::Hatch => "Hatch",
|
||||
BrushStyle::Glow => "Glow",
|
||||
BrushStyle::Airbrush => "Airbrush",
|
||||
BrushStyle::Crayon => "Crayon",
|
||||
BrushStyle::WetPaint => "Wet Paint",
|
||||
BrushStyle::InkPen => "Ink Pen",
|
||||
BrushStyle::Clouds => "Clouds",
|
||||
BrushStyle::Dirt => "Dirt",
|
||||
BrushStyle::Tree => "Tree",
|
||||
BrushStyle::Bristle => "Bristle",
|
||||
BrushStyle::Mixer => "Mixer",
|
||||
BrushStyle::Blender => "Blender",
|
||||
BrushStyle::Bitmap => "Bitmap",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,16 +62,28 @@ pub struct PendingHistoryItem {
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
/// Returns true if at least one background-thread history snapshot is still
|
||||
/// waiting to be committed to the document history.
|
||||
///
|
||||
/// This is used by the GUI to decide whether to schedule an additional
|
||||
/// delayed `commit_pending_history` poll after a brush stroke ends.
|
||||
pub fn has_pending_history(&self) -> bool {
|
||||
!self.pending_history.lock().unwrap().is_empty()
|
||||
}
|
||||
|
||||
/// Commit any pending history items computed in the background thread.
|
||||
/// This should be polled on the UI thread.
|
||||
///
|
||||
/// Also recycles returned buffers back into the engine's pool to avoid
|
||||
/// allocations on subsequent strokes.
|
||||
pub fn commit_pending_history(&mut self) {
|
||||
///
|
||||
/// Returns `true` if at least one pending snapshot was committed on this
|
||||
/// call, `false` if the queue was empty.
|
||||
pub fn commit_pending_history(&mut self) -> bool {
|
||||
let items = {
|
||||
let mut pending = self.pending_history.lock().unwrap();
|
||||
if pending.is_empty() {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
std::mem::take(&mut *pending)
|
||||
};
|
||||
@@ -106,6 +118,7 @@ impl Engine {
|
||||
);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Begin a new brush stroke on the given layer.
|
||||
@@ -259,6 +272,7 @@ impl Engine {
|
||||
None
|
||||
};
|
||||
|
||||
let style = self.current_tip.style;
|
||||
let bounds = self.last_stroke_bounds;
|
||||
let pending_history = self.pending_history.clone();
|
||||
|
||||
@@ -275,7 +289,10 @@ impl Engine {
|
||||
bounds: None,
|
||||
before_shapes,
|
||||
after_shapes,
|
||||
description: "Brush Stroke".to_string(),
|
||||
description: format!(
|
||||
"Brush Stroke ({})",
|
||||
crate::stroke_brush::brush_style_label(style)
|
||||
),
|
||||
return_before: None,
|
||||
return_after: None,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user