mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-20 03:18:06 +08:00
Fix several bugs causing warning spam in the console (#4547)
This commit is contained in:
@@ -480,7 +480,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
||||
NodeInput::value(TaggedValue::F64(10.), false),
|
||||
NodeInput::value(TaggedValue::Bool(Default::default()), false),
|
||||
NodeInput::value(TaggedValue::InterpolationDistribution(Default::default()), false),
|
||||
NodeInput::type_default(list!(Vector), false),
|
||||
NodeInput::type_default(item!(Vector), false),
|
||||
],
|
||||
input_metadata: vec![
|
||||
("Content", "TODO").into(),
|
||||
@@ -1533,6 +1533,26 @@ impl InputTypeConstraint {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a type reaches the constraint through an `input_adapter`, the per-connector node which the preprocessor places ahead of
|
||||
/// every ranked connector to convert or embed a convertible element, such as a color literal feeding a `Graphic` paint wire.
|
||||
#[must_use]
|
||||
fn satisfies_through_input_adapter(&self, ty: &Type) -> bool {
|
||||
let provided = Self::type_name(ty);
|
||||
|
||||
interpreted_executor::node_registry::NODE_REGISTRY
|
||||
.iter()
|
||||
.filter(|(identifier, _)| identifier.as_str().starts_with("input_adapter<"))
|
||||
.flat_map(|(_, implementations)| implementations.keys())
|
||||
.any(|node_io| node_io.inputs.first().is_some_and(|from| Self::type_name(from) == provided) && self.satisfies(&node_io.return_value))
|
||||
}
|
||||
|
||||
/// Check if a default value of this type is valid for the constraint.
|
||||
#[must_use]
|
||||
fn accepts_default_value(&self, ty: &Type) -> bool {
|
||||
// Empty types are used when the input must come from the graph so they can be skipped
|
||||
ty.nested_type() == &concrete!(()) || self.satisfies(ty) || self.satisfies_through_input_adapter(ty)
|
||||
}
|
||||
|
||||
/// Compute the type constraint for one input. Note that this cannot use the infrastructure in the node network interface as the node is not placed in a network.
|
||||
#[must_use]
|
||||
fn compute_constraint_for_input(template_document_node: &NodeTemplate, name: &str, input_index: usize) -> Self {
|
||||
@@ -1589,9 +1609,7 @@ impl InputTypeConstraint {
|
||||
for (index, (constraint, input)) in all_input_constraints.iter().zip(&template_document_node.inputs).enumerate() {
|
||||
if let Some(value) = input.as_value() {
|
||||
let input_ty = value.ty();
|
||||
|
||||
// Empty types are used when the input must come from the graph so they can be skipped.
|
||||
if input_ty.nested_type() != &concrete!(()) && !constraint.satisfies(&input_ty) {
|
||||
if !constraint.accepts_default_value(&input_ty) {
|
||||
warn!("The default value for input index {index} node {name} is {input_ty}, but does not satisfy {constraint:?}");
|
||||
}
|
||||
}
|
||||
@@ -1687,6 +1705,27 @@ mod test {
|
||||
|
||||
editor.eval_graph().await.expect("the Origins to Polyline chain should type-resolve and evaluate");
|
||||
}
|
||||
|
||||
// Guards the unconnected Path input, whose default must match the rank of the Morph connector it feeds
|
||||
#[tokio::test]
|
||||
async fn blend_resolves_and_evaluates_with_default_inputs() {
|
||||
let mut editor = EditorTestUtils::create();
|
||||
editor.new_document().await;
|
||||
editor.draw_rect(0., 0., 10., 10.).await;
|
||||
|
||||
let layer = editor.active_document().metadata().all_layers().next().expect("drawing a rectangle should create a layer");
|
||||
let node_id = NodeId::new();
|
||||
let node_template = resolve_network_node_type("Blend").expect("the Blend definition should exist").default_node_template();
|
||||
editor
|
||||
.handle_message(NodeGraphMessage::InsertNode {
|
||||
node_id,
|
||||
node_template: Box::new(node_template),
|
||||
})
|
||||
.await;
|
||||
editor.handle_message(NodeGraphMessage::MoveNodeToChainStart { node_id, parent: layer }).await;
|
||||
|
||||
editor.eval_graph().await.expect("the Blend network should type-resolve and evaluate");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1707,6 +1746,25 @@ mod test_type_constraints {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_definition_default_value_satisfies_its_constraint() {
|
||||
let mut violations = Vec::new();
|
||||
for definition in super::DOCUMENT_NODE_TYPES.values() {
|
||||
let name = &definition.node_template.display_name;
|
||||
let constraints = InputTypeConstraint::constraints_for_all_inputs(&definition.node_template, name);
|
||||
|
||||
for (index, (constraint, input)) in constraints.iter().zip(&definition.node_template.inputs).enumerate() {
|
||||
if let Some(value) = input.as_value()
|
||||
&& !constraint.accepts_default_value(&value.ty())
|
||||
{
|
||||
violations.push(format!("{name} input {index}: {} does not satisfy {constraint:?}", value.ty()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(violations.is_empty(), "Default values rejected by their input constraints:\n{}", violations.join("\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passthrough() {
|
||||
let node_type = resolve_proto_node_type(graphene_std::ops::passthrough::IDENTIFIER).expect("passthrough node");
|
||||
|
||||
@@ -173,6 +173,12 @@ pub struct WasmLog;
|
||||
impl log::Log for WasmLog {
|
||||
#[inline]
|
||||
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||
// Dependencies that log routine rendering details at the debug level are capped so they don't flood the console
|
||||
let crate_name = metadata.target().split("::").next().unwrap_or_default();
|
||||
if crate_name.starts_with("vello") {
|
||||
return metadata.level() <= log::Level::Info;
|
||||
}
|
||||
|
||||
metadata.level() <= log::max_level()
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,8 @@ async fn request_persistence() {
|
||||
match storage.persist() {
|
||||
Ok(promise) => match JsFuture::from(promise).await {
|
||||
Ok(value) if value.as_bool() == Some(true) => {}
|
||||
Ok(_) => log::warn!("OPFS persistence was not granted; browser may evict resources under storage pressure"),
|
||||
// Browsers deny this by default unless the site is bookmarked, installed, or highly engaged, so it isn't worth a warning
|
||||
Ok(_) => log::trace!("OPFS persistence was not granted; browser may evict resources under storage pressure"),
|
||||
Err(error) => log::warn!("OPFS persist() rejected: {error:?}"),
|
||||
},
|
||||
Err(error) => log::warn!("OPFS persist() threw: {error:?}"),
|
||||
|
||||
@@ -7,6 +7,7 @@ pub mod basic_brush;
|
||||
|
||||
pub use brush_types::*;
|
||||
|
||||
// Fallbacks for stroke items carrying no such attribute, mirroring the `Brush Strokes` defaults below (which the node macro requires as literals)
|
||||
pub(crate) const DEFAULT_DIAMETER: f64 = 40.;
|
||||
pub(crate) const DEFAULT_HARDNESS: f64 = 0.;
|
||||
pub(crate) const DEFAULT_FLOW: f64 = 100.;
|
||||
@@ -17,9 +18,9 @@ fn brush_strokes(
|
||||
_: impl Ctx,
|
||||
strokes: List<Stroke>,
|
||||
color: List<Color>,
|
||||
#[default(DEFAULT_DIAMETER)] diameter: Item<f64>,
|
||||
#[default(DEFAULT_HARDNESS)] hardness: Item<Percentage>,
|
||||
#[default(DEFAULT_FLOW)] flow: Item<Percentage>,
|
||||
#[default(40.)] diameter: Item<f64>,
|
||||
#[default(0.)] hardness: Item<Percentage>,
|
||||
#[default(100.)] flow: Item<Percentage>,
|
||||
) -> List<Graphic> {
|
||||
let (diameter, hardness, flow) = (diameter.into_element(), hardness.into_element(), flow.into_element());
|
||||
List::new_from_item(
|
||||
|
||||
Reference in New Issue
Block a user