Remove all references to legacy layers (#1523)

* Remove visible field from LegacyLayer

* Remove LegacyLayer wrapper around LegacyLayerType

* Remove FolderLegacyLayer and LayerLegacyLayer wrappers around their data

* Remove legacy layers
This commit is contained in:
Keavon Chambers 2023-12-19 20:50:45 -08:00
parent 9a7d7de8fa
commit dcd38f2e4c
26 changed files with 211 additions and 1078 deletions

View file

@ -1,7 +1,4 @@
use crate::document_metadata::{is_artboard, DocumentMetadata, LayerNodeIdentifier};
use crate::layers::folder_layer::FolderLegacyLayer;
use crate::layers::layer_info::{LegacyLayer, LegacyLayerType};
use crate::DocumentError;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeNetwork, NodeOutput};
use graphene_core::renderer::ClickTarget;
@ -23,9 +20,6 @@ pub type LayerId = u64;
pub struct Document {
#[serde(default)]
pub document_network: NodeNetwork,
/// The root layer, usually a [FolderLegacyLayer](layers::folder_layer::FolderLegacyLayer) that contains all other [LegacyLayers](layers::layer_info::LegacyLayer).
#[serde(skip)]
pub root: LegacyLayer,
/// The state_identifier serves to provide a way to uniquely identify a particular state that the document is in.
/// This identifier is not a hash and is not guaranteed to be equal for equivalent documents.
#[serde(skip)]
@ -43,11 +37,6 @@ impl PartialEq for Document {
impl Default for Document {
fn default() -> Self {
Self {
root: LegacyLayer {
name: None,
visible: true,
data: LegacyLayerType::Folder(FolderLegacyLayer::default()),
},
state_identifier: DefaultHasher::new(),
document_network: {
use graph_craft::document::{value::TaggedValue, NodeInput};
@ -156,100 +145,4 @@ impl Document {
pub fn current_state_identifier(&self) -> u64 {
self.state_identifier.finish()
}
/// Returns a reference to the requested folder. Fails if the path does not exist,
/// or if the requested layer is not of type folder.
pub fn folder(&self, path: impl AsRef<[LayerId]>) -> Result<&FolderLegacyLayer, DocumentError> {
let mut root = &self.root;
for id in path.as_ref() {
root = root.as_folder()?.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.as_ref().into()))?;
}
root.as_folder()
}
/// Returns a mutable reference to the requested folder. Fails if the path does not exist,
/// or if the requested layer is not of type folder.
fn folder_mut(&mut self, path: &[LayerId]) -> Result<&mut FolderLegacyLayer, DocumentError> {
let mut root = &mut self.root;
for id in path {
root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
}
root.as_folder_mut()
}
/// Returns a reference to the layer or folder at the path.
pub fn layer(&self, path: &[LayerId]) -> Result<&LegacyLayer, DocumentError> {
if path.is_empty() {
return Ok(&self.root);
}
let (path, id) = split_path(path)?;
self.folder(path)?.layer(id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))
}
/// Returns a mutable reference to the layer or folder at the path.
pub fn layer_mut(&mut self, path: &[LayerId]) -> Result<&mut LegacyLayer, DocumentError> {
if path.is_empty() {
return Ok(&mut self.root);
}
let (path, id) = split_path(path)?;
self.folder_mut(path)?.layer_mut(id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))
}
pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> &'a [LayerId] {
layers.reduce(|a, b| &a[..a.iter().zip(b.iter()).take_while(|&(a, b)| a == b).count()]).unwrap_or_default()
}
/// Returns the shallowest folder given the selection, even if the selection doesn't contain any folders
pub fn shallowest_common_folder<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> Result<&'a [LayerId], DocumentError> {
let common_prefix_of_path = self.common_layer_path_prefix(layers);
Ok(match self.layer(common_prefix_of_path)?.data {
LegacyLayerType::Folder(_) => common_prefix_of_path,
_ => &common_prefix_of_path[..common_prefix_of_path.len() - 1],
})
}
/// Returns all layers that are not contained in any other of the given folders
/// Takes and Iterator over &[LayerId] or &Vec<LayerId>.
pub fn shallowest_unique_layers<'a, T>(layers: impl Iterator<Item = T>) -> Vec<T>
where
T: AsRef<[LayerId]> + std::cmp::Ord + 'a,
{
let mut sorted_layers: Vec<_> = layers.collect();
sorted_layers.sort();
// Sorting here creates groups of similar UUID paths
sorted_layers.dedup_by(|a, b| a.as_ref().starts_with(b.as_ref()));
sorted_layers
}
/// Given a path to a layer, returns a vector of the indices in the layer tree
/// These indices can be used to order a list of layers
pub fn indices_for_path(&self, path: &[LayerId]) -> Result<Vec<usize>, DocumentError> {
let mut root = self.root.as_folder()?;
let mut indices = vec![];
let (path, layer_id) = split_path(path)?;
// TODO: appears to be n^2? should we maintain a lookup table?
for id in path {
let pos = root.layer_ids.iter().position(|x| *x == *id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
indices.push(pos);
root = match root.layer(*id) {
Some(LegacyLayer {
data: LegacyLayerType::Folder(folder),
..
}) => Some(folder),
_ => None,
}
.ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
}
indices.push(root.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?);
Ok(indices)
}
}
fn split_path(path: &[LayerId]) -> Result<(&[LayerId], LayerId), DocumentError> {
let (id, path) = path.split_last().ok_or(DocumentError::InvalidPath)?;
Ok((path, *id))
}

View file

@ -154,24 +154,20 @@ impl DocumentMetadata {
// selected layer modifications
impl DocumentMetadata {
#[must_use]
pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) -> SelectionChanged {
pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) {
self.selected_nodes.retain(f);
SelectionChanged
}
#[must_use]
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) -> SelectionChanged {
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) {
self.selected_nodes = new;
SelectionChanged
}
#[must_use]
pub fn add_selected_nodes(&mut self, iter: impl IntoIterator<Item = NodeId>) -> SelectionChanged {
pub fn add_selected_nodes(&mut self, iter: impl IntoIterator<Item = NodeId>) {
self.selected_nodes.extend(iter);
SelectionChanged
}
#[must_use]
pub fn clear_selected_nodes(&mut self) -> SelectionChanged {
self.set_selected_nodes(Vec::new())
pub fn clear_selected_nodes(&mut self) {
self.set_selected_nodes(Vec::new());
}
/// Loads the structure of layer nodes from a node graph.
@ -374,8 +370,8 @@ impl LayerNodeIdentifier {
#[track_caller]
pub fn new(node_id: NodeId, network: &NodeNetwork) -> Self {
debug_assert!(
is_layer_node(node_id, network),
"Layer identifier constructed from non layer node {node_id}: {:#?}",
node_id == LayerNodeIdentifier::ROOT.to_node() || network.nodes.get(&node_id).is_some_and(|node| node.is_layer()),
"Layer identifier constructed from non-layer node {node_id}: {:#?}",
network.nodes.get(&node_id)
);
Self::new_unchecked(node_id)
@ -633,10 +629,6 @@ pub struct NodeRelations {
last_child: Option<LayerNodeIdentifier>,
}
fn is_layer_node(node: NodeId, network: &NodeNetwork) -> bool {
node == LayerNodeIdentifier::ROOT.to_node() || network.nodes.get(&node).is_some_and(|node| node.is_layer())
}
#[test]
fn test_tree() {
let mut document_metadata = DocumentMetadata::default();

View file

@ -1,27 +0,0 @@
use super::layer_info::LegacyLayer;
use crate::document::LayerId;
use crate::DocumentError;
use serde::{Deserialize, Serialize};
/// A layer that encapsulates other layers, including potentially more folders.
/// The contained layers are rendered in the same order they are stored.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Default)]
pub struct FolderLegacyLayer {
/// The IDs of the [Layer]s contained within the Folder
pub layer_ids: Vec<LayerId>,
/// The [Layer]s contained in the folder
pub layers: Vec<LegacyLayer>,
}
impl FolderLegacyLayer {
pub fn layer(&self, layer_id: LayerId) -> Option<&LegacyLayer> {
let index = self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into())).ok()?;
Some(&self.layers[index])
}
pub fn layer_mut(&mut self, layer_id: LayerId) -> Option<&mut LegacyLayer> {
let index = self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into())).ok()?;
Some(&mut self.layers[index])
}
}

View file

@ -1,124 +0,0 @@
use super::folder_layer::FolderLegacyLayer;
use super::layer_layer::LayerLegacyLayer;
use crate::DocumentError;
use core::fmt;
use serde::{Deserialize, Serialize};
// ===============
// LegacyLayerType
// ===============
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
/// Represents different types of layers.
pub enum LegacyLayerType {
/// A layer that wraps a [FolderLegacyLayer] struct.
Folder(FolderLegacyLayer),
/// A layer that wraps an [LayerLegacyLayer] struct.
Layer(LayerLegacyLayer),
}
impl Default for LegacyLayerType {
fn default() -> Self {
LegacyLayerType::Layer(Default::default())
}
}
// =========================
// LayerDataTypeDiscriminant
// =========================
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, specta::Type)]
pub enum LayerDataTypeDiscriminant {
Folder,
Layer,
}
impl fmt::Display for LayerDataTypeDiscriminant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
LayerDataTypeDiscriminant::Folder => write!(f, "Folder"),
LayerDataTypeDiscriminant::Layer => write!(f, "Layer"),
}
}
}
impl From<&LegacyLayerType> for LayerDataTypeDiscriminant {
fn from(data: &LegacyLayerType) -> Self {
use LegacyLayerType::*;
match data {
Folder(_) => LayerDataTypeDiscriminant::Folder,
Layer(_) => LayerDataTypeDiscriminant::Layer,
}
}
}
// ===========
// LegacyLayer
// ===========
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct LegacyLayer {
/// The user-given name of the layer.
pub name: Option<String>,
/// Whether the layer is currently visible or hidden.
pub visible: bool,
/// The type of layer, such as folder or shape.
pub data: LegacyLayerType,
}
impl LegacyLayer {
/// Iterate over the layers encapsulated by this layer.
/// If the [Layer type](Layer::data) is not a folder, the only item in the iterator will be the layer itself.
/// If the [Layer type](Layer::data) wraps a [Folder](LegacyLayerType::Folder), the iterator will recursively yield all the layers contained in the folder as well as potential sub-folders.
pub fn iter(&self) -> LayerIter<'_> {
LayerIter { stack: vec![self] }
}
/// Get a mutable reference to the Folder wrapped by the layer.
/// This operation will fail if the [Layer type](Layer::data) is not `LegacyLayerType::Folder`.
pub fn as_folder_mut(&mut self) -> Result<&mut FolderLegacyLayer, DocumentError> {
match &mut self.data {
LegacyLayerType::Folder(f) => Ok(f),
_ => Err(DocumentError::NotFolder),
}
}
/// Get a reference to the Folder wrapped by the layer.
/// This operation will fail if the [Layer type](Layer::data) is not `LegacyLayerType::Folder`.
pub fn as_folder(&self) -> Result<&FolderLegacyLayer, DocumentError> {
match &self.data {
LegacyLayerType::Folder(f) => Ok(f),
_ => Err(DocumentError::NotFolder),
}
}
}
// =========
// LayerIter
// =========
/// An iterator over the layers encapsulated by this layer.
/// See [Layer::iter] for more information.
#[derive(Debug, Default)]
pub struct LayerIter<'a> {
pub stack: Vec<&'a LegacyLayer>,
}
impl<'a> Iterator for LayerIter<'a> {
type Item = &'a LegacyLayer;
fn next(&mut self) -> Option<Self::Item> {
match self.stack.pop() {
Some(layer) => {
if let LegacyLayerType::Folder(folder) = &layer.data {
let layers = folder.layers.as_slice();
self.stack.extend(layers);
};
Some(layer)
}
None => None,
}
}
}

View file

@ -1,11 +0,0 @@
use serde::{Deserialize, Serialize};
// ================
// LayerLegacyLayer
// ================
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub struct LayerLegacyLayer {
/// The document node network that this layer contains
pub network: graph_craft::document::NodeNetwork,
}

View file

@ -1,20 +0,0 @@
//! # Layers
//! A document consists of a set of [Layers](layer_info::Layer).
//! Layers allow the user to mutate part of the document while leaving the rest unchanged.
//! There are currently these different types of layers:
//! * [Folder layers](folder_layer::FolderLegacyLayer), which encapsulate sub-layers
//! * [Layer layers](layer_layer::LayerLegacyLayer), which contain a node graph layer
//!
//! Refer to the module-level documentation for detailed information on each layer.
//!
//! ## Overlapping layers
//! Layers are rendered on top of each other.
//! When different layers overlap, they are blended together according to the [BlendMode](blend_mode::BlendMode)
//! using the CSS [`mix-blend-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode) property and the layer opacity.
/// Contains the [FolderLegacyLayer](folder_layer::FolderLegacyLayer) type that encapsulates other layers, including more folders.
pub mod folder_layer;
/// Contains the base [Layer](layer_info::Layer) type, an abstraction over the different types of layers.
pub mod layer_info;
/// Contains the [LayerLegacyLayer](nodegraph_layer::LayerLegacyLayer) type that contains a node graph.
pub mod layer_layer;

View file

@ -1,16 +1,2 @@
// `macro_use` puts the log macros (`error!`, `warn!`, `debug!`, `info!` and `trace!`) in scope for the crate
// #[macro_use]
extern crate log;
pub mod document;
pub mod document_metadata;
pub mod layers;
/// A set of different errors that can occur when using this crate.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DocumentError {
LayerNotFound(Vec<document::LayerId>),
InvalidPath,
NotFolder,
InvalidFile(String),
}

View file

@ -37,7 +37,6 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
PropertiesPanelMessageDiscriminant::Refresh,
))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::FolderChanged)),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::DocumentStructureChanged)),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerTreeStructure),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
@ -453,41 +452,6 @@ mod test {
assert_eq!(layers_after_copy[5], shape_id);
}
#[test]
#[ignore] // TODO: Re-enable test, see issue #444 (https://github.com/GraphiteEditor/Graphite/pull/444)
/// - create rect, shape and ellipse
/// - select ellipse and rect
/// - move them down and back up again
fn move_selection() {
let mut editor = create_editor_with_three_layers();
fn map_to_vec(paths: Vec<&[LayerId]>) -> Vec<Vec<LayerId>> {
paths.iter().map(|layer| layer.to_vec()).collect::<Vec<_>>()
}
let sorted_layers = map_to_vec(editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().all_layers_sorted());
println!("Sorted layers: {sorted_layers:?}");
let verify_order = |handler: &mut DocumentMessageHandler| {
(
map_to_vec(handler.all_layers_sorted()),
map_to_vec(handler.non_selected_layers_sorted()),
map_to_vec(handler.selected_layers_sorted()),
)
};
editor.handle_message(DocumentMessage::SelectedLayersRaise);
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut().unwrap());
assert_eq!(all, non_selected.into_iter().chain(selected).collect::<Vec<_>>());
editor.handle_message(DocumentMessage::SelectedLayersLower);
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut().unwrap());
assert_eq!(all, selected.into_iter().chain(non_selected).collect::<Vec<_>>());
editor.handle_message(DocumentMessage::SelectedLayersRaiseToFront);
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut().unwrap());
assert_eq!(all, non_selected.into_iter().chain(selected).collect::<Vec<_>>());
}
#[test]
/// If this test is failing take a look at `GRAPHITE_DOCUMENT_VERSION` in `editor/src/consts.rs`, it may need to be updated.
/// This test will fail when you make changes to the underlying serialization format for a document.

View file

@ -2,12 +2,10 @@ use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use document_legacy::document::LayerId;
use graphene_core::raster::color::Color;
use graphene_core::text::Font;
use serde_json::Value;
use std::ops::Not;
#[derive(Debug, Clone, Default)]
pub struct LayoutMessageHandler {
@ -157,19 +155,6 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
let callback_message = (invisible.on_update.callback)(&());
responses.add(callback_message);
}
Widget::LayerReferenceInput(layer_reference_input) => {
let update_value = value.is_null().not().then(|| {
value
.as_str()
.expect("LayerReferenceInput update was not of type: string")
.split(',')
.map(|id| id.parse::<LayerId>().unwrap())
.collect::<Vec<_>>()
});
layer_reference_input.value = update_value;
let callback_message = (layer_reference_input.on_update.callback)(layer_reference_input);
responses.add(callback_message);
}
Widget::NumberInput(number_input) => match value {
Value::Number(num) => {
let update_value = num.as_f64().unwrap();

View file

@ -326,7 +326,6 @@ impl LayoutGroup {
Widget::IconButton(x) => &mut x.tooltip,
Widget::IconLabel(x) => &mut x.tooltip,
Widget::ImageLabel(x) => &mut x.tooltip,
Widget::LayerReferenceInput(x) => &mut x.tooltip,
Widget::NumberInput(x) => &mut x.tooltip,
Widget::OptionalInput(x) => &mut x.tooltip,
Widget::ParameterExposeButton(x) => &mut x.tooltip,
@ -478,7 +477,6 @@ pub enum Widget {
IconLabel(IconLabel),
ImageLabel(ImageLabel),
InvisibleStandinInput(InvisibleStandinInput),
LayerReferenceInput(LayerReferenceInput),
NumberInput(NumberInput),
OptionalInput(OptionalInput),
ParameterExposeButton(ParameterExposeButton),
@ -548,7 +546,6 @@ impl DiffUpdate {
Widget::DropdownInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
Widget::FontInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
Widget::IconButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
Widget::LayerReferenceInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
Widget::NumberInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
Widget::OptionalInput(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),
Widget::ParameterExposeButton(widget) => Some((&mut widget.tooltip, &mut widget.tooltip_shortcut)),

View file

@ -1,8 +1,6 @@
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::layout::utility_types::widget_prelude::*;
use document_legacy::document::LayerId;
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use graphene_core::raster::curve::Curve;
use graphite_proc_macros::WidgetBuilder;
@ -137,33 +135,6 @@ pub struct InvisibleStandinInput {
pub on_update: WidgetCallback<()>,
}
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)]
pub struct LayerReferenceInput {
#[widget_builder(constructor)]
pub value: Option<Vec<LayerId>>,
#[serde(rename = "layerName")]
#[widget_builder(constructor)]
pub layer_name: Option<String>,
#[serde(rename = "layerType")]
#[widget_builder(constructor)]
pub layer_type: Option<LayerDataTypeDiscriminant>,
pub disabled: bool,
pub tooltip: String,
#[serde(skip)]
pub tooltip_shortcut: Option<ActionKeys>,
// Callbacks
#[serde(skip)]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub on_update: WidgetCallback<LayerReferenceInput>,
}
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)]
#[derivative(Debug, PartialEq, Default)]
pub struct NumberInput {

View file

@ -36,9 +36,6 @@ pub enum DocumentMessage {
// Messages
AbortTransaction,
AddSelectedLayers {
additional_layers: Vec<Vec<LayerId>>,
},
AlignSelectedLayers {
axis: AlignAxis,
aggregate: AlignAggregate,
@ -65,9 +62,6 @@ pub enum DocumentMessage {
FlipSelectedLayers {
flip_axis: FlipAxis,
},
FolderChanged {
affected_folder_path: Vec<LayerId>,
},
GroupSelectedLayers,
ImaginateClear {
layer_path: Vec<LayerId>,
@ -83,9 +77,6 @@ pub enum DocumentMessage {
InputFrameRasterizeRegionBelowLayer {
layer_path: Vec<LayerId>,
},
LayerChanged {
affected_layer_path: Vec<LayerId>,
},
MoveSelectedLayersTo {
parent: LayerNodeIdentifier,
insert_index: isize,

View file

@ -7,7 +7,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::NodeGraphHandlerData;
use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData;
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::document::utility_types::layer_panel::{LayerMetadata, LayerPanelEntry, RawBuffer};
use crate::messages::portfolio::document::utility_types::layer_panel::{LayerMetadata, RawBuffer};
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, DocumentSave, FlipAxis};
use crate::messages::portfolio::document::utility_types::vectorize_layer_metadata;
use crate::messages::portfolio::utility_types::PersistentData;
@ -19,8 +19,6 @@ use crate::node_graph_executor::NodeGraphExecutor;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use document_legacy::DocumentError;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeInput, NodeNetwork};
use graphene_core::raster::BlendMode;
@ -175,17 +173,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.extend([RenderDocument.into(), DocumentStructureChanged.into()]);
}
}
AddSelectedLayers { additional_layers } => {
for layer_path in &additional_layers {
responses.extend(self.select_layer(layer_path));
}
// TODO: Correctly update layer panel in clear_selection instead of here
responses.add(FolderChanged { affected_folder_path: vec![] });
responses.add(BroadcastEvent::SelectionChanged);
self.update_layers_panel_options_bar_widgets(responses);
}
AlignSelectedLayers { axis, aggregate } => {
self.backup(responses);
@ -271,6 +258,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
DocumentHistoryBackward => self.undo(responses),
DocumentHistoryForward => self.redo(responses),
DocumentStructureChanged => {
self.update_layers_panel_options_bar_widgets(responses);
let data_buffer: RawBuffer = self.serialize_root().as_slice().into();
responses.add(FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer })
}
@ -302,10 +291,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(BroadcastEvent::DocumentIsDirty);
}
}
FolderChanged { affected_folder_path } => {
let affected_layer_path = affected_folder_path;
responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]);
}
GroupSelectedLayers => {
// TODO: Add code that changes the insert index of the new folder based on the selected layer
let parent = self.metadata().deepest_common_ancestor(self.metadata().selected_layers(), true).unwrap_or(LayerNodeIdentifier::ROOT);
@ -354,12 +339,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}
}
InputFrameRasterizeRegionBelowLayer { layer_path } => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path }),
LayerChanged { affected_layer_path } => {
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone()) {
responses.add(FrontendMessage::UpdateDocumentLayerDetails { data: layer_entry });
}
self.update_layers_panel_options_bar_widgets(responses);
}
MoveSelectedLayersTo { parent, insert_index } => {
let selected_layers = self.metadata().selected_layers().collect::<Vec<_>>();
@ -368,7 +347,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
return;
}
let insert_index = self.update_insert_index(&selected_layers, parent, insert_index).unwrap();
let insert_index = self.update_insert_index(&selected_layers, parent, insert_index);
responses.add(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
responses.add(DocumentMessage::DeleteSelectedLayers);
@ -472,7 +451,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(DocumentHistoryForward);
responses.add(BroadcastEvent::DocumentIsDirty);
responses.add(RenderDocument);
responses.add(FolderChanged { affected_folder_path: vec![] });
responses.add(DocumentStructureChanged);
}
RenameDocument { new_name } => {
self.name = new_name;
@ -654,7 +633,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(DocumentHistoryBackward);
responses.add(BroadcastEvent::DocumentIsDirty);
responses.add(RenderDocument);
responses.add(FolderChanged { affected_folder_path: vec![] });
responses.add(DocumentStructureChanged);
responses.add(UndoFinished);
}
UndoFinished => self.undo_in_progress = false,
@ -764,14 +743,14 @@ impl DocumentMessageHandler {
val.unwrap()
}
pub fn deserialize_document(serialized_content: &str) -> Result<Self, DocumentError> {
let deserialized_result: Result<Self, DocumentError> = serde_json::from_str(serialized_content).map_err(|e| DocumentError::InvalidFile(e.to_string()));
pub fn deserialize_document(serialized_content: &str) -> Result<Self, EditorError> {
let deserialized_result: Result<Self, EditorError> = serde_json::from_str(serialized_content).map_err(|e| EditorError::DocumentDeserialization(e.to_string()));
match deserialized_result {
Ok(document) => {
if document.version == GRAPHITE_DOCUMENT_VERSION {
Ok(document)
} else {
Err(DocumentError::InvalidFile("Graphite document version mismatch".to_string()))
Err(EditorError::DocumentDeserialization("Graphite document version mismatch".to_string()))
}
}
Err(e) => Err(e),
@ -788,71 +767,15 @@ impl DocumentMessageHandler {
}
pub fn with_name_and_content(name: String, serialized_content: String) -> Result<Self, EditorError> {
match Self::deserialize_document(&serialized_content) {
Ok(mut document) => {
document.name = name;
Ok(document)
}
Err(DocumentError::InvalidFile(msg)) => Err(EditorError::DocumentDeserialization(msg)),
_ => Err(EditorError::Document(String::from("Failed to open file"))),
}
}
pub fn is_unmodified_default(&self) -> bool {
self.serialize_root().len() == Self::default().serialize_root().len()
&& self.document_undo_history.is_empty()
&& self.document_redo_history.is_empty()
&& self.name.starts_with(DEFAULT_DOCUMENT_NAME)
}
fn select_layer(&mut self, path: &[LayerId]) -> Option<Message> {
println!("Select_layer fail: {:?}", self.all_layers_sorted());
if let Some(layer) = self.layer_metadata.get_mut(path) {
layer.selected = true;
let data = self.layer_panel_entry(path.to_vec()).ok()?;
(!path.is_empty()).then(|| FrontendMessage::UpdateDocumentLayerDetails { data }.into())
} else {
warn!("Tried to select non-existing layer {path:?}");
None
}
let mut document = Self::deserialize_document(&serialized_content)?;
document.name = name;
Ok(document)
}
pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice()))
}
pub fn selected_layers_with_type(&self, discriminant: LayerDataTypeDiscriminant) -> impl Iterator<Item = &[LayerId]> {
self.selected_layers().filter(move |path| {
self.document_legacy
.layer(path)
.map(|layer| LayerDataTypeDiscriminant::from(&layer.data) == discriminant)
.unwrap_or(false)
})
}
pub fn non_selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
self.layer_metadata.iter().filter_map(|(path, data)| (!data.selected).then_some(path.as_slice()))
}
pub fn selected_layers_without_children(&self) -> Vec<&[LayerId]> {
let unique_layers = DocumentLegacy::shallowest_unique_layers(self.selected_layers());
// We need to maintain layer ordering
self.sort_layers(unique_layers.iter().copied())
}
pub fn selected_layers_contains(&self, path: &[LayerId]) -> bool {
self.layer_metadata.get(path).map(|layer| layer.selected).unwrap_or(false)
}
pub fn visible_layers(&self) -> impl Iterator<Item = &[LayerId]> {
self.all_layers().filter(|path| match self.document_legacy.layer(path) {
Ok(layer) => layer.visible,
Err(_) => false,
})
}
/// Returns the bounding boxes for all visible layers.
pub fn bounding_boxes<'a>(&'a self) -> impl Iterator<Item = [DVec2; 2]> + 'a {
// TODO: Remove this function entirely?
@ -920,63 +843,10 @@ impl DocumentMessageHandler {
structure
}
/// Returns an unsorted list of all layer paths including folders at all levels, except the document's top-level root folder itself
pub fn all_layers(&self) -> impl Iterator<Item = &[LayerId]> {
self.layer_metadata.keys().filter_map(|path| (!path.is_empty()).then_some(path.as_slice()))
}
/// Returns the paths to all layers in order
fn sort_layers<'a>(&self, paths: impl Iterator<Item = &'a [LayerId]>) -> Vec<&'a [LayerId]> {
// Compute the indices for each layer to be able to sort them
let mut layers_with_indices: Vec<(&[LayerId], Vec<usize>)> = paths
// 'path.len() > 0' filters out root layer since it has no indices
.filter(|path| !path.is_empty())
.filter_map(|path| {
// TODO: `indices_for_path` can return an error. We currently skip these layers and log a warning. Once this problem is solved this code can be simplified.
match self.document_legacy.indices_for_path(path) {
Err(err) => {
warn!("layers_sorted: Could not get indices for the layer {path:?}: {err:?}");
None
}
Ok(indices) => Some((path, indices)),
}
})
.collect();
layers_with_indices.sort_by_key(|(_, indices)| indices.clone());
layers_with_indices.into_iter().map(|(path, _)| path).collect()
}
/// Returns the paths to all layers in order
pub fn all_layers_sorted(&self) -> Vec<&[LayerId]> {
self.sort_layers(self.all_layers())
}
/// Returns the paths to all selected layers in order
pub fn selected_layers_sorted(&self) -> Vec<&[LayerId]> {
self.sort_layers(self.selected_layers())
}
/// Returns the paths to all non_selected layers in order
#[allow(dead_code)] // used for test cases
pub fn non_selected_layers_sorted(&self) -> Vec<&[LayerId]> {
self.sort_layers(self.non_selected_layers())
}
pub fn layer_metadata(&self, path: &[LayerId]) -> &LayerMetadata {
self.layer_metadata.get(path).unwrap_or_else(|| panic!("Editor's layer metadata for {path:?} does not exist"))
}
pub fn layer_metadata_mut(&mut self, path: &[LayerId]) -> &mut LayerMetadata {
Self::layer_metadata_mut_no_borrow_self(&mut self.layer_metadata, path)
}
pub fn layer_metadata_mut_no_borrow_self<'a>(layer_metadata: &'a mut HashMap<Vec<LayerId>, LayerMetadata>, path: &[LayerId]) -> &'a mut LayerMetadata {
layer_metadata
.get_mut(path)
.unwrap_or_else(|| panic!("Layer data cannot be found because the path {path:?} does not exist"))
}
/// Places a document into the history system
fn backup_with_document(&mut self, document: DocumentLegacy, layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>, responses: &mut VecDeque<Message>) {
self.document_redo_history.clear();
@ -1002,12 +872,6 @@ impl DocumentMessageHandler {
});
}
pub fn rollback(&mut self, responses: &mut VecDeque<Message>) {
self.backup(responses);
self.undo(responses);
// TODO: Consider if we should check if the document is saved
}
/// Replace the document with a new document save, returning the document save.
pub fn replace_document(&mut self, DocumentSave { document, layer_metadata }: DocumentSave) -> DocumentSave {
// Keeping the root is required if the bounds of the viewport have changed during the operation
@ -1042,10 +906,7 @@ impl DocumentMessageHandler {
self.document_redo_history.pop_front();
}
for layer in self.layer_metadata.keys() {
responses.add(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() })
}
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
}
}
@ -1071,10 +932,7 @@ impl DocumentMessageHandler {
self.document_undo_history.pop_front();
}
for layer in self.layer_metadata.keys() {
responses.add(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() })
}
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
}
}
@ -1113,42 +971,14 @@ impl DocumentMessageHandler {
}
}
// TODO: This should probably take a slice not a vec, also why does this even exist when `layer_panel_entry_from_path` also exists?
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>) -> Result<LayerPanelEntry, EditorError> {
let data: LayerMetadata = *self
.layer_metadata
.get_mut(&path)
.ok_or_else(|| EditorError::Document(format!("Could not get layer metadata for {path:?}")))?;
let layer = self.document_legacy.layer(&path)?;
let entry = LayerPanelEntry::new(&data, layer, path);
Ok(entry)
}
pub fn layer_panel_entry_from_path(&self, path: &[LayerId]) -> Option<LayerPanelEntry> {
let layer_metadata = self.layer_metadata(path);
let layer = self.document_legacy.layer(path).ok()?;
Some(LayerPanelEntry::new(layer_metadata, layer, path.to_vec()))
}
/// When working with an insert index, deleting the layers may cause the insert index to point to a different location (if the layer being deleted was located before the insert index).
///
/// This function updates the insert index so that it points to the same place after the specified `layers` are deleted.
fn update_insert_index(&self, layers: &[LayerNodeIdentifier], parent: LayerNodeIdentifier, insert_index: isize) -> Result<isize, DocumentError> {
fn update_insert_index(&self, layers: &[LayerNodeIdentifier], parent: LayerNodeIdentifier, insert_index: isize) -> isize {
let layer_ids_above = parent.children(self.metadata()).take(if insert_index < 0 { usize::MAX } else { insert_index as usize });
let new_insert_index = layer_ids_above.filter(|layer_id| !layers.contains(layer_id)).count() as isize;
Ok(new_insert_index)
}
/// Calculate the path that new layers should be inserted to.
/// Depends on the selected layers as well as their types (Folder/Non-Folder)
pub fn get_path_for_new_layer(&self) -> Vec<u64> {
// If the selected layers don't actually exist, a new uuid for the
// root folder will be returned
let mut path = self.document_legacy.shallowest_common_folder(self.selected_layers()).map_or(vec![], |v| v.to_vec());
path.push(generate_uuid());
path
new_insert_index
}
pub fn new_layer_parent(&self) -> LayerNodeIdentifier {

View file

@ -564,7 +564,8 @@ impl<'a> ModifyInputsContext<'a> {
}
}
self.responses.add(self.document_metadata.retain_selected_nodes(|id| !delete_nodes.contains(id)));
self.document_metadata.retain_selected_nodes(|id| !delete_nodes.contains(id));
self.responses.add(BroadcastEvent::SelectionChanged);
self.responses.add(DocumentMessage::DocumentStructureChanged);
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });

View file

@ -147,12 +147,6 @@ impl Default for NodeGraphMessageHandler {
}
}
impl Into<Message> for document_legacy::document_metadata::SelectionChanged {
fn into(self) -> Message {
BroadcastMessage::TriggerEvent(BroadcastEvent::SelectionChanged).into()
}
}
impl NodeGraphMessageHandler {
/// Send the cached layout to the frontend for the options bar at the top of the node panel
fn send_node_bar_layout(&self, responses: &mut VecDeque<Message>) {
@ -429,7 +423,8 @@ impl NodeGraphMessageHandler {
return false;
}
network.nodes.remove(&node_id);
responses.add(document.metadata.retain_selected_nodes(|&id| id != node_id));
document.metadata.retain_selected_nodes(|&id| id != node_id);
responses.add(BroadcastEvent::SelectionChanged);
true
}
@ -627,13 +622,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(DocumentMessage::StartTransaction);
let new_ids = &document.metadata.selected_nodes().map(|&id| (id, crate::application::generate_uuid())).collect();
responses.add(document.metadata.clear_selected_nodes());
document.metadata.clear_selected_nodes();
responses.add(BroadcastEvent::SelectionChanged);
// Copy the selected nodes
let copied_nodes = Self::copy_nodes(network, new_ids).collect::<Vec<_>>();
// Select the new nodes
responses.add(document.metadata.add_selected_nodes(copied_nodes.iter().map(|(node_id, _)| *node_id)));
document.metadata.add_selected_nodes(copied_nodes.iter().map(|(node_id, _)| *node_id));
responses.add(BroadcastEvent::SelectionChanged);
for (node_id, mut document_node) in copied_nodes {
// Shift duplicated node
@ -649,7 +647,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
NodeGraphMessage::ExitNestedNetwork { depth_of_nesting } => {
responses.add(document.metadata.clear_selected_nodes());
document.metadata.clear_selected_nodes();
responses.add(BroadcastEvent::SelectionChanged);
for _ in 0..depth_of_nesting {
self.network.pop();
}
@ -755,13 +755,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
NodeGraphMessage::RunDocumentGraph => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path: Vec::new() }),
NodeGraphMessage::SelectedNodesAdd { nodes } => {
responses.add(document.metadata.add_selected_nodes(nodes));
document.metadata.add_selected_nodes(nodes);
responses.add(BroadcastEvent::SelectionChanged);
}
NodeGraphMessage::SelectedNodesRemove { nodes } => {
responses.add(document.metadata.retain_selected_nodes(|node| !nodes.contains(node)));
document.metadata.retain_selected_nodes(|node| !nodes.contains(node));
responses.add(BroadcastEvent::SelectionChanged);
}
NodeGraphMessage::SelectedNodesSet { nodes } => {
responses.add(document.metadata.set_selected_nodes(nodes));
document.metadata.set_selected_nodes(nodes);
responses.add(BroadcastEvent::SelectionChanged);
responses.add(PropertiesPanelMessage::Refresh);
}
NodeGraphMessage::SendGraph { should_rerender } => {
@ -953,7 +956,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
NodeGraphMessage::UpdateNewNodeGraph => {
if let Some(network) = document.document_network.nested_network(&self.network) {
responses.add(document.metadata.clear_selected_nodes());
document.metadata.clear_selected_nodes();
responses.add(BroadcastEvent::SelectionChanged);
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);

View file

@ -5,10 +5,9 @@ use super::FrontendGraphDataType;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateServerStatus, ImaginateStatus};
use graph_craft::imaginate_input::{ImaginateSamplingMethod, ImaginateServerStatus, ImaginateStatus};
use graphene_core::memo::IORecord;
use graphene_core::raster::{
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, ImageFrame, LuminanceCalculation, NoiseType, RedGreenBlue, RelativeAbsolute, SelectiveColorChoice,
@ -1505,10 +1504,10 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
let neg_index = resolve_input("Negative Prompt");
let base_img_index = resolve_input("Adapt Input Image");
let img_creativity_index = resolve_input("Image Creativity");
let mask_index = resolve_input("Masking Layer");
let inpaint_index = resolve_input("Inpaint");
let mask_blur_index = resolve_input("Mask Blur");
let mask_fill_index = resolve_input("Mask Starting Fill");
// let mask_index = resolve_input("Masking Layer");
// let inpaint_index = resolve_input("Inpaint");
// let mask_blur_index = resolve_input("Mask Blur");
// let mask_fill_index = resolve_input("Mask Starting Fill");
let faces_index = resolve_input("Improve Faces");
let tiling_index = resolve_input("Tiling");
@ -1877,44 +1876,6 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
)
};
let mut layer_reference_input_layer_is_some = false;
let layer_mask = {
let mut widgets = start_widgets(document_node, node_id, mask_index, "Masking Layer", FrontendGraphDataType::General, true);
if let NodeInput::Value {
tagged_value: TaggedValue::LayerPath(layer_path),
exposed: false,
} = &document_node.inputs[mask_index]
{
let layer_reference_input_layer = layer_path
.as_ref()
.and_then(|path| context.document.layer(path).ok())
.map(|layer| (layer.name.clone().unwrap_or_default(), LayerDataTypeDiscriminant::from(&layer.data)));
layer_reference_input_layer_is_some = layer_reference_input_layer.is_some();
let layer_reference_input_layer_name = layer_reference_input_layer.as_ref().map(|(layer_name, _)| layer_name);
let layer_reference_input_layer_type = layer_reference_input_layer.as_ref().map(|(_, layer_type)| layer_type);
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
if !transform_not_connected {
widgets.push(
LayerReferenceInput::new(layer_path.clone(), layer_reference_input_layer_name.cloned(), layer_reference_input_layer_type.cloned())
.disabled(!use_base_image)
.on_update(update_value(|input: &LayerReferenceInput| TaggedValue::LayerPath(input.value.clone()), node_id, mask_index))
.widget_holder(),
);
} else {
widgets.push(TextLabel::new("Requires Transform Input").italic(true).widget_holder());
}
}
LayoutGroup::Row { widgets }.with_tooltip(
"Reference to a layer or folder which masks parts of the input image. Image generation is constrained to masked areas.\n\
\n\
Black shapes represent the masked regions. Lighter shades of gray act as a partial mask, and colors become grayscale. (This is the reverse of traditional masks because it is easier to draw black shapes; this will be changed later when the mask input is a bitmap.)",
)
};
let mut layout = vec![
server_status,
progress,
@ -1928,73 +1889,73 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
negative_prompt,
base_image,
image_creativity,
layer_mask,
// layer_mask,
];
if use_base_image && layer_reference_input_layer_is_some {
let in_paint = {
let mut widgets = start_widgets(document_node, node_id, inpaint_index, "Inpaint", FrontendGraphDataType::Boolean, true);
// if use_base_image && layer_reference_input_layer_is_some {
// let in_paint = {
// let mut widgets = start_widgets(document_node, node_id, inpaint_index, "Inpaint", FrontendGraphDataType::Boolean, true);
if let &NodeInput::Value {
tagged_value: TaggedValue::Bool(in_paint),
exposed: false,
} = &document_node.inputs[inpaint_index]
{
widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
RadioInput::new(
[(true, "Inpaint"), (false, "Outpaint")]
.into_iter()
.map(|(paint, name)| RadioEntryData::new(name).on_update(update_value(move |_| TaggedValue::Bool(paint), node_id, inpaint_index)))
.collect(),
)
.selected_index(Some(1 - in_paint as u32))
.widget_holder(),
]);
}
LayoutGroup::Row { widgets }.with_tooltip(
"Constrain image generation to the interior (inpaint) or exterior (outpaint) of the mask, while referencing the other unchanged parts as context imagery.\n\
\n\
An unwanted part of an image can be replaced by drawing around it with a black shape and inpainting with that mask layer.\n\
\n\
An image can be uncropped by resizing the Imaginate layer to the target bounds and outpainting with a black rectangle mask matching the original image bounds.",
)
};
// if let &NodeInput::Value {
// tagged_value: TaggedValue::Bool(in_paint),
// exposed: false,
// } = &document_node.inputs[inpaint_index]
// {
// widgets.extend_from_slice(&[
// Separator::new(SeparatorType::Unrelated).widget_holder(),
// RadioInput::new(
// [(true, "Inpaint"), (false, "Outpaint")]
// .into_iter()
// .map(|(paint, name)| RadioEntryData::new(name).on_update(update_value(move |_| TaggedValue::Bool(paint), node_id, inpaint_index)))
// .collect(),
// )
// .selected_index(Some(1 - in_paint as u32))
// .widget_holder(),
// ]);
// }
// LayoutGroup::Row { widgets }.with_tooltip(
// "Constrain image generation to the interior (inpaint) or exterior (outpaint) of the mask, while referencing the other unchanged parts as context imagery.\n\
// \n\
// An unwanted part of an image can be replaced by drawing around it with a black shape and inpainting with that mask layer.\n\
// \n\
// An image can be uncropped by resizing the Imaginate layer to the target bounds and outpainting with a black rectangle mask matching the original image bounds.",
// )
// };
let blur_radius = {
let number_props = NumberInput::default().unit(" px").min(0.).max(25.).int();
let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", number_props, true);
LayoutGroup::Row { widgets }.with_tooltip("Blur radius for the mask. Useful for softening sharp edges to blend the masked area with the rest of the image.")
};
// let blur_radius = {
// let number_props = NumberInput::default().unit(" px").min(0.).max(25.).int();
// let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", number_props, true);
// LayoutGroup::Row { widgets }.with_tooltip("Blur radius for the mask. Useful for softening sharp edges to blend the masked area with the rest of the image.")
// };
let mask_starting_fill = {
let mut widgets = start_widgets(document_node, node_id, mask_fill_index, "Mask Starting Fill", FrontendGraphDataType::General, true);
// let mask_starting_fill = {
// let mut widgets = start_widgets(document_node, node_id, mask_fill_index, "Mask Starting Fill", FrontendGraphDataType::General, true);
if let &NodeInput::Value {
tagged_value: TaggedValue::ImaginateMaskStartingFill(starting_fill),
exposed: false,
} = &document_node.inputs[mask_fill_index]
{
let mask_fill_content_modes = ImaginateMaskStartingFill::list();
let mut entries = Vec::with_capacity(mask_fill_content_modes.len());
for mode in mask_fill_content_modes {
entries.push(MenuListEntry::new(mode.to_string()).on_update(update_value(move |_| TaggedValue::ImaginateMaskStartingFill(mode), node_id, mask_fill_index)));
}
let entries = vec![entries];
// if let &NodeInput::Value {
// tagged_value: TaggedValue::ImaginateMaskStartingFill(starting_fill),
// exposed: false,
// } = &document_node.inputs[mask_fill_index]
// {
// let mask_fill_content_modes = ImaginateMaskStartingFill::list();
// let mut entries = Vec::with_capacity(mask_fill_content_modes.len());
// for mode in mask_fill_content_modes {
// entries.push(MenuListEntry::new(mode.to_string()).on_update(update_value(move |_| TaggedValue::ImaginateMaskStartingFill(mode), node_id, mask_fill_index)));
// }
// let entries = vec![entries];
widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
DropdownInput::new(entries).selected_index(Some(starting_fill as u32)).widget_holder(),
]);
}
LayoutGroup::Row { widgets }.with_tooltip(
"Begin in/outpainting the masked areas using this fill content as the starting input image.\n\
\n\
Each option can be visualized by generating with 'Sampling Steps' set to 0.",
)
};
layout.extend_from_slice(&[in_paint, blur_radius, mask_starting_fill]);
}
// widgets.extend_from_slice(&[
// Separator::new(SeparatorType::Unrelated).widget_holder(),
// DropdownInput::new(entries).selected_index(Some(starting_fill as u32)).widget_holder(),
// ]);
// }
// LayoutGroup::Row { widgets }.with_tooltip(
// "Begin in/outpainting the masked areas using this fill content as the starting input image.\n\
// \n\
// Each option can be visualized by generating with 'Sampling Steps' set to 0.",
// )
// };
// layout.extend_from_slice(&[in_paint, blur_radius, mask_starting_fill]);
// }
let improve_faces = {
let widgets = bool_widget(document_node, node_id, faces_index, "Improve Faces", true);

View file

@ -1,4 +1,3 @@
use document_legacy::DocumentError;
use graphene_core::raster::color::Color;
use thiserror::Error;
@ -38,4 +37,3 @@ macro_rules! derive_from {
derive_from!(&str, Misc);
derive_from!(String, Misc);
derive_from!(Color, Color);
derive_from!(DocumentError, Document);

View file

@ -1,5 +1,4 @@
use document_legacy::document::LayerId;
use document_legacy::layers::layer_info::{LayerDataTypeDiscriminant, LegacyLayer};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
@ -30,7 +29,7 @@ impl Serialize for JsRawBuffer {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Copy, specta::Type)]
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, Copy, specta::Type)]
pub struct LayerMetadata {
pub selected: bool,
pub expanded: bool,
@ -42,31 +41,22 @@ impl LayerMetadata {
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, specta::Type)]
pub enum LayerClassification {
#[default]
Folder,
Artboard,
Layer,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, specta::Type)]
pub struct LayerPanelEntry {
pub name: String,
pub tooltip: String,
pub visible: bool,
#[serde(rename = "layerType")]
pub layer_type: LayerDataTypeDiscriminant,
#[serde(rename = "layerClassification")]
pub layer_classification: LayerClassification,
#[serde(rename = "layerMetadata")]
pub layer_metadata: LayerMetadata,
pub path: Vec<LayerId>,
pub thumbnail: String,
}
impl LayerPanelEntry {
// TODO: Deprecate this because it's using document-legacy layer data which is no longer linked to data from the node graph,
// TODO: so this doesn't feed `name` (that's fed elsewhere) or `visible` (that's broken entirely), etc.
pub fn new(layer_metadata: &LayerMetadata, layer: &LegacyLayer, path: Vec<LayerId>) -> Self {
Self {
name: "".to_string(), // Replaced before it gets used
tooltip: "".to_string(), // Replaced before it gets used
visible: layer.visible,
layer_type: (&layer.data).into(),
layer_metadata: *layer_metadata,
path,
thumbnail: r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 0 0"></svg>"#.to_string(),
}
}
}

View file

@ -146,12 +146,6 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(DocumentMessage::RenderDocument);
responses.add(DocumentMessage::DocumentStructureChanged);
if let Some(document) = self.active_document() {
for layer in document.layer_metadata.keys() {
responses.add(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() });
}
}
}
PortfolioMessage::CloseDocumentWithConfirmation { document_id } => {
let target_document = self.documents.get(&document_id).unwrap();
@ -474,9 +468,6 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
responses.add(FrontendMessage::UpdateActiveDocument { document_id });
responses.add(DocumentMessage::RenderDocument);
responses.add(DocumentMessage::DocumentStructureChanged);
for layer in self.documents.get(&document_id).unwrap().layer_metadata.keys() {
responses.add(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() });
}
responses.add(BroadcastEvent::SelectionChanged);
responses.add(BroadcastEvent::DocumentIsDirty);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
@ -649,14 +640,6 @@ impl PortfolioMessageHandler {
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, responses: &mut VecDeque<Message>) {
self.document_ids.push(document_id);
responses.extend(
new_document
.layer_metadata
.keys()
.filter_map(|path| new_document.layer_panel_entry_from_path(path))
.map(|entry| FrontendMessage::UpdateDocumentLayerDetails { data: entry }.into())
.collect::<Vec<_>>(),
);
new_document.update_layers_panel_options_bar_widgets(responses);
self.documents.insert(document_id, new_document);

View file

@ -3,7 +3,6 @@ use crate::consts::{SNAP_AXIS_TOLERANCE, SNAP_POINT_TOLERANCE};
use crate::messages::prelude::*;
use document_legacy::document::LayerId;
use document_legacy::layers::layer_info::LegacyLayer;
use glam::DVec2;
@ -102,66 +101,21 @@ impl SnapManager {
}
}
/// Add the [ManipulatorGroup]s (optionally including handles) of the specified shape layer to the snapping points
///
/// This should be called after start_snap
pub fn add_snap_path(
&mut self,
_document_message_handler: &DocumentMessageHandler,
_input: &InputPreprocessorMessageHandler,
_layer: &LegacyLayer,
_path: &[LayerId],
_include_handles: bool,
_ignore_points: &[ManipulatorPointInfo],
) {
todo!();
// let Some(vector_data) = &layer.as_vector_data() else { return };
// if !document_message_handler.snapping_state.node_snapping {
// return;
// };
// let transform = document_message_handler.document_legacy.multiply_transforms(path).unwrap();
// let snap_points = vector_data
// .manipulator_groups()
// .flat_map(|group| {
// if include_handles {
// [
// Some((ManipulatorPointId::new(group.id, SelectedType::Anchor), group.anchor)),
// group.in_handle.map(|pos| (ManipulatorPointId::new(group.id, SelectedType::InHandle), pos)),
// group.out_handle.map(|pos| (ManipulatorPointId::new(group.id, SelectedType::OutHandle), pos)),
// ]
// } else {
// [Some((ManipulatorPointId::new(group.id, SelectedType::Anchor), group.anchor)), None, None]
// }
// })
// .flatten()
// .filter(|&(point_id, _)| {
// !ignore_points.contains(&ManipulatorPointInfo {
// layer: LayerNodeIdentifier::from_path(path, document_message_handler.network()),
// point_id,
// })
// })
// .map(|(_, pos)| transform.transform_point2(pos));
// self.add_snap_points(document_message_handler, input, snap_points);
}
/// Adds all of the shape handles in the document, including bézier handles of the points specified
pub fn add_all_document_handles(
&mut self,
document_message_handler: &DocumentMessageHandler,
input: &InputPreprocessorMessageHandler,
include_handles: &[&[LayerId]],
exclude: &[&[LayerId]],
ignore_points: &[ManipulatorPointInfo],
_document_message_handler: &DocumentMessageHandler,
_input: &InputPreprocessorMessageHandler,
_include_handles: &[&[LayerId]],
_exclude: &[&[LayerId]],
_ignore_points: &[ManipulatorPointInfo],
) {
for path in document_message_handler.all_layers() {
if !exclude.contains(&path) {
let layer = document_message_handler.document_legacy.layer(path).expect("Could not get layer for snapping");
self.add_snap_path(document_message_handler, input, layer, path, include_handles.contains(&path), ignore_points);
}
}
// for path in document_message_handler.all_layers() {
// if !exclude.contains(&path) {
// let layer = document_message_handler.document_legacy.layer(path).expect("Could not get layer for snapping");
// self.add_snap_path(document_message_handler, input, layer, path, include_handles.contains(&path), ignore_points);
// }
// }
}
/// Finds the closest snap from an array of layers to the specified snap targets in viewport coords.

View file

@ -2,13 +2,13 @@ use crate::consts::FILE_SAVE_SUFFIX;
use crate::messages::frontend::utility_types::FrontendImageData;
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use crate::messages::portfolio::document::node_graph::wrap_network_in_scope;
use crate::messages::portfolio::document::utility_types::layer_panel::LayerClassification;
use crate::messages::portfolio::document::utility_types::misc::{LayerMetadata, LayerPanelEntry};
use crate::messages::prelude::*;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::layer_info::{LayerDataTypeDiscriminant, LegacyLayerType};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork};
use graph_craft::graphene_compiler::Compiler;
@ -430,10 +430,6 @@ impl NodeGraphExecutor {
.expect("Failed to send imaginate preferences");
}
pub fn previous_output_type(&self, path: &[LayerId]) -> Option<Type> {
self.last_output_type.get(path).cloned().flatten()
}
pub fn introspect_node_in_network<T: std::any::Any + core::fmt::Debug, U, F1: FnOnce(&NodeNetwork) -> Option<NodeId>, F2: FnOnce(&T) -> U>(
&mut self,
network: &NodeNetwork,
@ -493,17 +489,7 @@ impl NodeGraphExecutor {
/// Evaluates a node graph, computing the entire graph
pub fn submit_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, layer_path: Vec<LayerId>, viewport_resolution: UVec2) -> Result<(), String> {
// Get the node graph layer
let network = if layer_path.is_empty() {
document.network().clone()
} else {
let layer = document.document_legacy.layer(&layer_path).map_err(|e| format!("No layer: {e:?}"))?;
let layer_layer = match &layer.data {
LegacyLayerType::Layer(layer) => Ok(layer),
_ => Err("Invalid layer type".to_string()),
}?;
layer_layer.network.clone()
};
let network = document.network().clone();
let render_config = RenderConfig {
viewport: Footprint {
@ -625,11 +611,12 @@ impl NodeGraphExecutor {
data: LayerPanelEntry {
name: document.document_network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(),
tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() },
visible: !document.document_network.disabled.contains(&layer.to_node()),
layer_type: if document.metadata.is_folder(layer) {
LayerDataTypeDiscriminant::Folder
layer_classification: if document.metadata.is_artboard(layer) {
LayerClassification::Artboard
} else if document.metadata.is_folder(layer) {
LayerClassification::Folder
} else {
LayerDataTypeDiscriminant::Layer
LayerClassification::Layer
},
layer_metadata: LayerMetadata {
expanded: layer.has_children(&document.metadata) && !collapsed_folders.contains(&layer),
@ -645,9 +632,6 @@ impl NodeGraphExecutor {
document.metadata.update_click_targets(new_click_targets);
responses.extend(updates);
self.process_node_graph_output(node_graph_output, execution_context.layer_path.clone(), transform, responses)?;
responses.add(DocumentMessage::LayerChanged {
affected_layer_path: execution_context.layer_path,
});
responses.add(DocumentMessage::RenderDocument);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(BroadcastEvent::DocumentIsDirty);

View file

@ -5,7 +5,7 @@
import { platformIsMac } from "@graphite/utility-functions/platform";
import type { Editor } from "@graphite/wasm-communication/editor";
import { defaultWidgetLayout, patchWidgetLayout, UpdateDocumentLayerDetails, UpdateDocumentLayerTreeStructureJs, UpdateLayersPanelOptionsLayout } from "@graphite/wasm-communication/messages";
import type { LayerType, LayerPanelEntry } from "@graphite/wasm-communication/messages";
import type { LayerClassification, LayerPanelEntry } from "@graphite/wasm-communication/messages";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
@ -136,8 +136,8 @@
editor.instance.deselectAllLayers();
}
function isGroupOrArtboard(layerType: LayerType) {
return layerType === "Folder" || layerType === "Artboard";
function isNestingLayer(layerClassification: LayerClassification) {
return layerClassification === "Folder" || layerClassification === "Artboard";
}
function calculateDragIndex(tree: LayoutCol, clientY: number, select?: () => void): DraggingData {
@ -179,9 +179,9 @@
}
// Inserting below current row
else if (distance > -closest && distance > -RANGE_TO_INSERT_WITHIN_BOTTOM_FOLDER_NOT_ROOT && distance < 0) {
insertFolder = isGroupOrArtboard(layer.layerType) ? layer.path : layer.path.slice(0, layer.path.length - 1);
insertIndex = isGroupOrArtboard(layer.layerType) ? 0 : folderIndex + 1;
highlightFolder = isGroupOrArtboard(layer.layerType);
insertFolder = isNestingLayer(layer.layerClassification) ? layer.path : layer.path.slice(0, layer.path.length - 1);
insertIndex = isNestingLayer(layer.layerClassification) ? 0 : folderIndex + 1;
highlightFolder = isNestingLayer(layer.layerClassification);
closest = -distance;
markerHeight = index === treeChildren.length - 1 ? rect.bottom - INSERT_MARK_OFFSET : rect.bottom;
}
@ -320,11 +320,11 @@
on:dragstart={(e) => draggable && dragStart(e, listing)}
on:click={(e) => selectLayerWithModifiers(e, listing)}
>
{#if isGroupOrArtboard(listing.entry.layerType)}
{#if isNestingLayer(listing.entry.layerClassification)}
<button class="expand-arrow" class:expanded={listing.entry.layerMetadata.expanded} on:click|stopPropagation={() => handleExpandArrowClick(listing.entry.path)} tabindex="0" />
{#if listing.entry.layerType === "Artboard"}
{#if listing.entry.layerClassification === "Artboard"}
<IconLabel icon="Artboard" class={"layer-type-icon"} />
{:else if listing.entry.layerType === "Folder"}
{:else if listing.entry.layerClassification === "Folder"}
<IconLabel icon="Folder" class={"layer-type-icon"} />
{/if}
{:else}
@ -337,7 +337,7 @@
data-text-input
type="text"
value={listing.entry.name}
placeholder={listing.entry.layerType}
placeholder={listing.entry.layerClassification}
disabled={!listing.editingName}
on:blur={() => onEditLayerNameDeselect(listing)}
on:keydown={(e) => e.key === "Escape" && onEditLayerNameDeselect(listing)}
@ -349,8 +349,8 @@
class={"visibility"}
action={(e) => (toggleLayerVisibility(listing.entry.path), e?.stopPropagation())}
size={24}
icon={listing.entry.visible ? "EyeVisible" : "EyeHidden"}
tooltip={listing.entry.visible ? "Visible" : "Hidden"}
icon={(() => true)() ? "EyeVisible" : "EyeHidden"}
tooltip={(() => true)() ? "Visible" : "Hidden"}
/>
</LayoutRow>
{/each}

View file

@ -17,7 +17,6 @@
import CurveInput from "@graphite/components/widgets/inputs/CurveInput.svelte";
import DropdownInput from "@graphite/components/widgets/inputs/DropdownInput.svelte";
import FontInput from "@graphite/components/widgets/inputs/FontInput.svelte";
import LayerReferenceInput from "@graphite/components/widgets/inputs/LayerReferenceInput.svelte";
import NumberInput from "@graphite/components/widgets/inputs/NumberInput.svelte";
import OptionalInput from "@graphite/components/widgets/inputs/OptionalInput.svelte";
import PivotInput from "@graphite/components/widgets/inputs/PivotInput.svelte";
@ -126,10 +125,6 @@
{#if imageLabel}
<ImageLabel {...exclude(imageLabel)} />
{/if}
{@const layerReferenceInput = narrowWidgetProps(component.props, "LayerReferenceInput")}
{#if layerReferenceInput}
<LayerReferenceInput {...exclude(layerReferenceInput)} on:value={({ detail }) => updateLayout(index, detail)} />
{/if}
{@const numberInput = narrowWidgetProps(component.props, "NumberInput")}
{#if numberInput}
<NumberInput

View file

@ -1,136 +0,0 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { currentDraggingElement } from "@graphite/io-managers/drag";
import type { LayerType } from "@graphite/wasm-communication/messages";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte";
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
const dispatch = createEventDispatcher<{ value: string | undefined }>();
export let value: string | undefined = undefined;
export let layerName: string | undefined = undefined;
export let layerType: LayerType | undefined = undefined;
export let disabled = false;
export let tooltip: string | undefined = undefined;
export let sharpRightCorners = false;
let hoveringDrop = false;
$: droppable = hoveringDrop && Boolean(currentDraggingElement());
function dragOver(e: DragEvent) {
hoveringDrop = true;
e.preventDefault();
}
function drop(e: DragEvent) {
hoveringDrop = false;
const element = currentDraggingElement();
const layerPath = element?.getAttribute("data-layer") || undefined;
if (layerPath) {
e.preventDefault();
dispatch("value", layerPath);
}
}
</script>
<LayoutRow
class="layer-reference-input"
classes={{ disabled, droppable, "sharp-right-corners": sharpRightCorners }}
{tooltip}
on:dragover={(e) => !disabled && dragOver(e)}
on:dragleave={() => !disabled && (hoveringDrop = false)}
on:drop={(e) => !disabled && drop(e)}
>
{#if value === undefined || droppable}
<LayoutRow class="drop-zone" />
<TextLabel italic={true}>{droppable ? "Drop" : "Drag"} Layer Here</TextLabel>
{:else}
{#if layerName !== undefined && layerType}
<IconLabel icon={layerType} class="layer-icon" />
<TextLabel italic={layerName === ""} class="layer-name">{layerName || `Untitled ${layerType || "[Unknown Layer Type]"}`}</TextLabel>
{:else}
<TextLabel bold={true} italic={true} class="missing">Layer Missing</TextLabel>
{/if}
<IconButton icon="CloseX" size={16} {disabled} action={() => dispatch("value", undefined)} />
{/if}
</LayoutRow>
<style lang="scss" global>
.layer-reference-input {
position: relative;
flex: 1 0 auto;
height: 24px;
border-radius: 2px;
background: var(--color-1-nearblack);
.drop-zone {
pointer-events: none;
border: 1px dashed var(--color-4-dimgray);
border-radius: 1px;
position: absolute;
top: 2px;
bottom: 2px;
left: 2px;
right: 2px;
}
&.droppable .drop-zone {
border: 1px dashed var(--color-e-nearwhite);
}
.layer-icon {
margin: 4px 8px;
+ .text-label {
padding-left: 0;
}
}
.text-label {
line-height: 18px;
padding: 3px calc(8px + 2px);
width: 100%;
text-align: center;
&.missing {
// TODO: Define this as a permanent color palette choice (search the project for all uses of this hex code)
color: #d6536e;
}
&.layer-name {
text-align: left;
}
}
.icon-button {
margin: 4px;
margin-left: 0;
}
&.disabled {
background: var(--color-2-mildblack);
.drop-zone {
border: 1px dashed var(--color-4-dimgray);
}
.text-label {
color: var(--color-8-uppergray);
}
.icon-label svg {
fill: var(--color-8-uppergray);
}
}
}
</style>

View file

@ -667,9 +667,7 @@ export class LayerPanelEntry {
@Transform(({ value }: { value: string }) => value || undefined)
tooltip!: string | undefined;
visible!: boolean;
layerType!: LayerType;
layerClassification!: LayerClassification;
@Transform(({ value }: { value: bigint[] }) => new BigUint64Array(value))
path!: BigUint64Array;
@ -686,7 +684,7 @@ export class LayerMetadata {
selected!: boolean;
}
export type LayerType = "Folder" | "Layer" | "Artboard";
export type LayerClassification = "Folder" | "Artboard" | "Layer";
export class RenderedImageData {
readonly path!: BigUint64Array;
@ -877,24 +875,6 @@ export class ImageLabel extends WidgetProps {
tooltip!: string | undefined;
}
export class LayerReferenceInput extends WidgetProps {
@Transform(({ value }: { value: BigUint64Array | undefined }) => (value ? String(value) : undefined))
value!: string | undefined;
layerName!: string | undefined;
layerType!: LayerType | undefined;
disabled!: boolean;
@Transform(({ value }: { value: string }) => value || undefined)
tooltip!: string | undefined;
// Styling
minWidth!: number;
}
export type NumberInputIncrementBehavior = "Add" | "Multiply" | "Callback" | "None";
export type NumberInputMode = "Increment" | "Range";
@ -1129,7 +1109,6 @@ const widgetSubTypes = [
{ value: IconButton, name: "IconButton" },
{ value: IconLabel, name: "IconLabel" },
{ value: ImageLabel, name: "ImageLabel" },
{ value: LayerReferenceInput, name: "LayerReferenceInput" },
{ value: NumberInput, name: "NumberInput" },
{ value: OptionalInput, name: "OptionalInput" },
{ value: ParameterExposeButton, name: "ParameterExposeButton" },

View file

@ -1,22 +1,16 @@
use fern::colors::{Color, ColoredLevelConfig};
use std::{error::Error, sync::Arc};
use document_legacy::{document::Document, layers::layer_info::LegacyLayerType};
use futures::executor::block_on;
use graph_craft::{
concrete,
document::*,
graphene_compiler::{Compiler, Executor},
imaginate_input::ImaginatePreferences,
ProtoNodeIdentifier,
};
use graphene_core::{
application_io::{ApplicationIo, NodeGraphUpdateSender},
text::FontCache,
};
use graph_craft::document::*;
use graph_craft::graphene_compiler::Executor;
use graph_craft::imaginate_input::ImaginatePreferences;
use graph_craft::{concrete, ProtoNodeIdentifier};
use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateSender};
use graphene_core::text::FontCache;
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::DynamicExecutor;
use fern::colors::{Color, ColoredLevelConfig};
use futures::executor::block_on;
use std::{error::Error, sync::Arc};
struct UpdateLogger {}
impl NodeGraphUpdateSender for UpdateLogger {
@ -85,18 +79,17 @@ fn init_logging() {
.unwrap();
}
fn create_executor(document_string: String) -> Result<DynamicExecutor, Box<dyn Error>> {
let document: serde_json::Value = serde_json::from_str(&document_string).expect("Failed to parse document");
let document = serde_json::from_value::<Document>(document["document_legacy"].clone()).expect("Failed to parse document");
let Some(LegacyLayerType::Layer(ref node_graph)) = document.root.iter().find(|layer| matches!(layer.data, LegacyLayerType::Layer(_))).map(|x| &x.data) else {
panic!("Failed to extract node graph from document")
};
let network = &node_graph.network;
let wrapped_network = wrap_network_in_scope(network.clone());
let compiler = Compiler {};
let protograph = compiler.compile_single(wrapped_network)?;
let executor = block_on(DynamicExecutor::new(protograph))?;
Ok(executor)
fn create_executor(_document_string: String) -> Result<DynamicExecutor, Box<dyn Error>> {
// let document: serde_json::Value = serde_json::from_str(&document_string).expect("Failed to parse document");
// let document = serde_json::from_value::<Document>(document["document_legacy"].clone()).expect("Failed to parse document");
// let Some(LegacyLayerType::Layer(ref network)) = document.root.iter().find(|layer| matches!(layer, LegacyLayerType::Layer(_))) else {
panic!("Failed to extract node graph from document")
// };
// let wrapped_network = wrap_network_in_scope(network.clone());
// let compiler = Compiler {};
// let protograph = compiler.compile_single(wrapped_network)?;
// let executor = block_on(DynamicExecutor::new(protograph))?;
// Ok(executor)
}
pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
@ -193,41 +186,41 @@ fn begin_scope() -> DocumentNode {
}
}
#[cfg(test)]
mod test {
use super::*;
// #[cfg(test)]
// mod test {
// use super::*;
#[tokio::test]
#[cfg_attr(not(feature = "wayland"), ignore)]
async fn grays_scale() {
let document_string = include_str!("../test_files/gray.graphite");
let executor = create_executor(document_string.to_string()).unwrap();
let editor_api = WasmEditorApi {
image_frame: None,
font_cache: &FontCache::default(),
application_io: &block_on(WasmApplicationIo::new()),
node_graph_message_sender: &UpdateLogger {},
imaginate_preferences: &ImaginatePreferences::default(),
render_config: graphene_core::application_io::RenderConfig::default(),
};
let result = (&executor).execute(editor_api.clone()).await.unwrap();
println!("result: {result:?}");
}
// #[tokio::test]
// #[cfg_attr(not(feature = "wayland"), ignore)]
// async fn grays_scale() {
// let document_string = include_str!("../test_files/gray.graphite");
// let executor = create_executor(document_string.to_string()).unwrap();
// let editor_api = WasmEditorApi {
// image_frame: None,
// font_cache: &FontCache::default(),
// application_io: &block_on(WasmApplicationIo::new()),
// node_graph_message_sender: &UpdateLogger {},
// imaginate_preferences: &ImaginatePreferences::default(),
// render_config: graphene_core::application_io::RenderConfig::default(),
// };
// let result = (&executor).execute(editor_api.clone()).await.unwrap();
// println!("result: {result:?}");
// }
#[tokio::test]
#[cfg_attr(not(feature = "wayland"), ignore)]
async fn hue() {
let document_string = include_str!("../test_files/hue.graphite");
let executor = create_executor(document_string.to_string()).unwrap();
let editor_api = WasmEditorApi {
image_frame: None,
font_cache: &FontCache::default(),
application_io: &block_on(WasmApplicationIo::new()),
node_graph_message_sender: &UpdateLogger {},
imaginate_preferences: &ImaginatePreferences::default(),
render_config: graphene_core::application_io::RenderConfig::default(),
};
let result = (&executor).execute(editor_api.clone()).await.unwrap();
println!("result: {result:?}");
}
}
// #[tokio::test]
// #[cfg_attr(not(feature = "wayland"), ignore)]
// async fn hue() {
// let document_string = include_str!("../test_files/hue.graphite");
// let executor = create_executor(document_string.to_string()).unwrap();
// let editor_api = WasmEditorApi {
// image_frame: None,
// font_cache: &FontCache::default(),
// application_io: &block_on(WasmApplicationIo::new()),
// node_graph_message_sender: &UpdateLogger {},
// imaginate_preferences: &ImaginatePreferences::default(),
// render_config: graphene_core::application_io::RenderConfig::default(),
// };
// let result = (&executor).execute(editor_api.clone()).await.unwrap();
// println!("result: {result:?}");
// }
// }