mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-29 09:58:11 +08:00
Show outline for image frames on hover and when selected with frame tools
This commit is contained in:
@@ -56,13 +56,13 @@ impl OverlayRenderer {
|
|||||||
|
|
||||||
// Create an outline if we do not have a cached one
|
// Create an outline if we do not have a cached one
|
||||||
if outline_cache.is_none() {
|
if outline_cache.is_none() {
|
||||||
let outline_path = self.create_shape_outline_overlay(graphene_core::vector::Subpath::from_bezier_crate(&vector_data.subpaths), responses);
|
let outline_path = self.create_shape_outline_overlay(graphene_core::vector::Subpath::from_bezier_rs(&vector_data.subpaths), responses);
|
||||||
self.shape_overlay_cache.insert(*layer_id, outline_path.clone());
|
self.shape_overlay_cache.insert(*layer_id, outline_path.clone());
|
||||||
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
||||||
trace!("Overlay: Creating new outline {:?}", &outline_path);
|
trace!("Overlay: Creating new outline {:?}", &outline_path);
|
||||||
} else if let Some(outline_path) = outline_cache {
|
} else if let Some(outline_path) = outline_cache {
|
||||||
trace!("Overlay: Updating overlays for {:?} owning layer: {:?}", outline_path, layer_id);
|
trace!("Overlay: Updating overlays for {:?} owning layer: {:?}", outline_path, layer_id);
|
||||||
Self::modify_outline_overlays(outline_path.clone(), graphene_core::vector::Subpath::from_bezier_crate(&vector_data.subpaths), responses);
|
Self::modify_outline_overlays(outline_path.clone(), graphene_core::vector::Subpath::from_bezier_rs(&vector_data.subpaths), responses);
|
||||||
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,50 +30,57 @@ impl PathOutline {
|
|||||||
// Get layer data
|
// Get layer data
|
||||||
let document_layer = document.document_legacy.layer(&document_layer_path).ok()?;
|
let document_layer = document.document_legacy.layer(&document_layer_path).ok()?;
|
||||||
|
|
||||||
// TODO Purge this area of BezPath and Kurbo
|
// Get the subpath from the shape
|
||||||
// Get the bezpath from the shape or text
|
|
||||||
let subpath = match &document_layer.data {
|
let subpath = match &document_layer.data {
|
||||||
LayerDataType::Shape(shape) => Some(shape.shape.clone()),
|
LayerDataType::Shape(shape) => Some(shape.shape.clone()),
|
||||||
LayerDataType::Layer(layer) => layer.as_vector_data().map(|vector_data| Subpath::from_bezier_crate(&vector_data.subpaths)),
|
LayerDataType::Layer(layer) => {
|
||||||
|
if let Some(vector_data) = layer.as_vector_data() {
|
||||||
|
// Vector graph output
|
||||||
|
Some(Subpath::from_bezier_rs(&vector_data.subpaths))
|
||||||
|
} else {
|
||||||
|
// Frame graph output
|
||||||
|
Some(Subpath::new_rect(DVec2::new(0., 0.), DVec2::new(1., 1.)))
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => document_layer.aabb_for_transform(DAffine2::IDENTITY, render_data).map(|[p1, p2]| Subpath::new_rect(p1, p2)),
|
_ => document_layer.aabb_for_transform(DAffine2::IDENTITY, render_data).map(|[p1, p2]| Subpath::new_rect(p1, p2)),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
// Generate a new overlay layer if necessary
|
// Generate a new overlay layer if necessary
|
||||||
let overlay = match overlay_path {
|
let overlay = overlay_path.unwrap_or_else(|| {
|
||||||
Some(path) => path,
|
let overlay_path = vec![generate_uuid()];
|
||||||
None => {
|
|
||||||
let overlay_path = vec![generate_uuid()];
|
responses.add(DocumentMessage::Overlays(
|
||||||
let operation = Operation::AddShape {
|
(Operation::AddShape {
|
||||||
path: overlay_path.clone(),
|
path: overlay_path.clone(),
|
||||||
subpath: Default::default(),
|
subpath: Default::default(),
|
||||||
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), PATH_OUTLINE_WEIGHT)), Fill::None),
|
style: style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), PATH_OUTLINE_WEIGHT)), Fill::None),
|
||||||
insert_index: -1,
|
insert_index: -1,
|
||||||
transform: DAffine2::IDENTITY.to_cols_array(),
|
transform: DAffine2::IDENTITY.to_cols_array(),
|
||||||
};
|
})
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
|
||||||
responses.add(DocumentMessage::Overlays(operation.into()));
|
overlay_path
|
||||||
|
});
|
||||||
|
|
||||||
overlay_path
|
// Update the shape subpath
|
||||||
}
|
responses.add(DocumentMessage::Overlays((Operation::SetShapePath { path: overlay.clone(), subpath }).into()));
|
||||||
};
|
|
||||||
|
|
||||||
// Update the shape bezpath
|
|
||||||
let operation = Operation::SetShapePath { path: overlay.clone(), subpath };
|
|
||||||
responses.add(DocumentMessage::Overlays(operation.into()));
|
|
||||||
|
|
||||||
// Update the transform to match the document
|
// Update the transform to match the document
|
||||||
let operation = Operation::SetLayerTransform {
|
responses.add(DocumentMessage::Overlays(
|
||||||
path: overlay.clone(),
|
(Operation::SetLayerTransform {
|
||||||
transform: document.document_legacy.multiply_transforms(&document_layer_path).unwrap().to_cols_array(),
|
path: overlay.clone(),
|
||||||
};
|
transform: document.document_legacy.multiply_transforms(&document_layer_path).unwrap().to_cols_array(),
|
||||||
responses.add(DocumentMessage::Overlays(operation.into()));
|
})
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
|
||||||
Some(overlay)
|
Some(overlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an outline of a layer either with a pre-existing overlay or by generating a new one
|
/// Creates an outline of a layer either with a pre-existing overlay or by generating a new one.
|
||||||
///
|
///
|
||||||
/// Creates an outline, discarding the overlay on failure
|
/// Creates an outline, discarding the overlay on failure.
|
||||||
fn create_outline(
|
fn create_outline(
|
||||||
document_layer_path: Vec<LayerId>,
|
document_layer_path: Vec<LayerId>,
|
||||||
overlay_path: Option<Vec<LayerId>>,
|
overlay_path: Option<Vec<LayerId>>,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMot
|
|||||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::portfolio::document::node_graph;
|
use crate::messages::portfolio::document::node_graph;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
use crate::messages::tool::common_functionality::path_outline::PathOutline;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
use crate::messages::tool::common_functionality::resize::Resize;
|
||||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
@@ -25,6 +26,10 @@ pub enum FrameToolMessage {
|
|||||||
// Standard messages
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
DocumentIsDirty,
|
||||||
|
#[remain::unsorted]
|
||||||
|
SelectionChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
DragStart,
|
DragStart,
|
||||||
@@ -73,7 +78,9 @@ impl ToolMetadata for FrameTool {
|
|||||||
impl ToolTransition for FrameTool {
|
impl ToolTransition for FrameTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
|
document_dirty: Some(FrameToolMessage::DocumentIsDirty.into()),
|
||||||
tool_abort: Some(FrameToolMessage::Abort.into()),
|
tool_abort: Some(FrameToolMessage::Abort.into()),
|
||||||
|
selection_changed: Some(FrameToolMessage::SelectionChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,6 +96,7 @@ enum NodeGraphToolFsmState {
|
|||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct NodeGraphToolData {
|
struct NodeGraphToolData {
|
||||||
data: Resize,
|
data: Resize,
|
||||||
|
path_outlines: PathOutline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fsm for NodeGraphToolFsmState {
|
impl Fsm for NodeGraphToolFsmState {
|
||||||
@@ -110,7 +118,15 @@ impl Fsm for NodeGraphToolFsmState {
|
|||||||
|
|
||||||
if let ToolMessage::Frame(event) = event {
|
if let ToolMessage::Frame(event) = event {
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
|
(_, DocumentIsDirty | SelectionChanged) => {
|
||||||
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
|
tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
(Ready, DragStart) => {
|
(Ready, DragStart) => {
|
||||||
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
|
|
||||||
shape_data.start(responses, document, input, render_data);
|
shape_data.start(responses, document, input, render_data);
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
shape_data.path = Some(document.get_path_for_new_layer());
|
shape_data.path = Some(document.get_path_for_new_layer());
|
||||||
@@ -147,6 +163,12 @@ impl Fsm for NodeGraphToolFsmState {
|
|||||||
responses.add(DocumentMessage::AbortTransaction);
|
responses.add(DocumentMessage::AbortTransaction);
|
||||||
|
|
||||||
shape_data.cleanup(responses);
|
shape_data.cleanup(responses);
|
||||||
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
|
|
||||||
|
Ready
|
||||||
|
}
|
||||||
|
(_, Abort) => {
|
||||||
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMot
|
|||||||
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
|
||||||
use crate::messages::portfolio::document::node_graph::{self, IMAGINATE_NODE};
|
use crate::messages::portfolio::document::node_graph::{self, IMAGINATE_NODE};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
use crate::messages::tool::common_functionality::path_outline::PathOutline;
|
||||||
use crate::messages::tool::common_functionality::resize::Resize;
|
use crate::messages::tool::common_functionality::resize::Resize;
|
||||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
@@ -25,6 +26,10 @@ pub enum ImaginateToolMessage {
|
|||||||
// Standard messages
|
// Standard messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Abort,
|
Abort,
|
||||||
|
#[remain::unsorted]
|
||||||
|
DocumentIsDirty,
|
||||||
|
#[remain::unsorted]
|
||||||
|
SelectionChanged,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
DragStart,
|
DragStart,
|
||||||
@@ -73,7 +78,9 @@ impl ToolMetadata for ImaginateTool {
|
|||||||
impl ToolTransition for ImaginateTool {
|
impl ToolTransition for ImaginateTool {
|
||||||
fn event_to_message_map(&self) -> EventToMessageMap {
|
fn event_to_message_map(&self) -> EventToMessageMap {
|
||||||
EventToMessageMap {
|
EventToMessageMap {
|
||||||
|
document_dirty: Some(ImaginateToolMessage::DocumentIsDirty.into()),
|
||||||
tool_abort: Some(ImaginateToolMessage::Abort.into()),
|
tool_abort: Some(ImaginateToolMessage::Abort.into()),
|
||||||
|
selection_changed: Some(ImaginateToolMessage::SelectionChanged.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,6 +96,7 @@ enum ImaginateToolFsmState {
|
|||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ImaginateToolData {
|
struct ImaginateToolData {
|
||||||
data: Resize,
|
data: Resize,
|
||||||
|
path_outlines: PathOutline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fsm for ImaginateToolFsmState {
|
impl Fsm for ImaginateToolFsmState {
|
||||||
@@ -110,7 +118,15 @@ impl Fsm for ImaginateToolFsmState {
|
|||||||
|
|
||||||
if let ToolMessage::Imaginate(event) = event {
|
if let ToolMessage::Imaginate(event) = event {
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
|
(_, DocumentIsDirty | SelectionChanged) => {
|
||||||
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
|
tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
(Ready, DragStart) => {
|
(Ready, DragStart) => {
|
||||||
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
|
|
||||||
shape_data.start(responses, document, input, render_data);
|
shape_data.start(responses, document, input, render_data);
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
shape_data.path = Some(document.get_path_for_new_layer());
|
shape_data.path = Some(document.get_path_for_new_layer());
|
||||||
@@ -178,6 +194,12 @@ impl Fsm for ImaginateToolFsmState {
|
|||||||
responses.add(DocumentMessage::AbortTransaction);
|
responses.add(DocumentMessage::AbortTransaction);
|
||||||
|
|
||||||
shape_data.cleanup(responses);
|
shape_data.cleanup(responses);
|
||||||
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
|
|
||||||
|
Ready
|
||||||
|
}
|
||||||
|
(_, Abort) => {
|
||||||
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ impl Subpath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to the legacy Subpath from the `bezier_rs::Subpath`.
|
/// Convert to the legacy Subpath from the `bezier_rs::Subpath`.
|
||||||
pub fn from_bezier_crate(value: &[bezier_rs::Subpath<ManipulatorGroupId>]) -> Self {
|
pub fn from_bezier_rs(value: &[bezier_rs::Subpath<ManipulatorGroupId>]) -> Self {
|
||||||
let mut groups = IdBackedVec::new();
|
let mut groups = IdBackedVec::new();
|
||||||
for subpath in value {
|
for subpath in value {
|
||||||
for group in subpath.manipulator_groups() {
|
for group in subpath.manipulator_groups() {
|
||||||
|
|||||||
@@ -202,7 +202,6 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
let bounds_transform = bounds.eval(()).transform;
|
let bounds_transform = bounds.eval(()).transform;
|
||||||
if bounds_transform != DAffine2::ZERO {
|
if bounds_transform != DAffine2::ZERO {
|
||||||
background_bounds = CopiedNode::new(bounds_transform);
|
background_bounds = CopiedNode::new(bounds_transform);
|
||||||
log::debug!("setting transform to {:?}", bounds_transform);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let background_image = background_bounds.then(EmptyImageNode::new(CopiedNode::new(Color::TRANSPARENT)));
|
let background_image = background_bounds.then(EmptyImageNode::new(CopiedNode::new(Color::TRANSPARENT)));
|
||||||
|
|||||||
Reference in New Issue
Block a user