diff --git a/app/src/main/java/com/annimon/hotarufx/visual/Property.java b/app/src/main/java/com/annimon/hotarufx/visual/Property.java new file mode 100644 index 0000000..ce1da81 --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/visual/Property.java @@ -0,0 +1,16 @@ +package com.annimon.hotarufx.visual; + +import java.util.function.Supplier; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public final class Property { + + @Getter + private final PropertyType type; + + @Getter + private final Supplier> property; + +} diff --git a/app/src/main/java/com/annimon/hotarufx/visual/PropertyBindings.java b/app/src/main/java/com/annimon/hotarufx/visual/PropertyBindings.java new file mode 100644 index 0000000..1a5ac38 --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/visual/PropertyBindings.java @@ -0,0 +1,17 @@ +package com.annimon.hotarufx.visual; + +import java.util.HashMap; +import java.util.function.Supplier; + +public final class PropertyBindings extends HashMap { + + public PropertyBindings add(String key, PropertyType type, Supplier> property) { + super.put(key, new Property(type, property)); + return this; + } + + public PropertyBindings merge(PropertyBindings bindings) { + super.putAll(bindings); + return this; + } +} diff --git a/app/src/main/java/com/annimon/hotarufx/visual/PropertyType.java b/app/src/main/java/com/annimon/hotarufx/visual/PropertyType.java new file mode 100644 index 0000000..cf08e99 --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/visual/PropertyType.java @@ -0,0 +1,31 @@ +package com.annimon.hotarufx.visual; + +import com.annimon.hotarufx.lib.NumberValue; +import com.annimon.hotarufx.lib.StringValue; +import com.annimon.hotarufx.lib.Types; +import com.annimon.hotarufx.lib.Value; +import java.util.function.Function; +import javafx.scene.paint.Color; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +@Getter +public enum PropertyType { + + NUMBER(toNumber(), o -> NumberValue.of((Number) o)), + PAINT(v -> Color.valueOf(v.asString()), o -> new StringValue(o.toString())); + + private final Function fromHFX; + private final Function toHFX; + + private static Function toNumber() { + return value -> { + if (value.type() == Types.NUMBER) { + return ((NumberValue) value).raw(); + } + return value.asNumber(); + }; + } +} diff --git a/app/src/main/java/com/annimon/hotarufx/visual/objects/CircleNode.java b/app/src/main/java/com/annimon/hotarufx/visual/objects/CircleNode.java index 50be055..16b66f6 100644 --- a/app/src/main/java/com/annimon/hotarufx/visual/objects/CircleNode.java +++ b/app/src/main/java/com/annimon/hotarufx/visual/objects/CircleNode.java @@ -1,10 +1,12 @@ package com.annimon.hotarufx.visual.objects; +import com.annimon.hotarufx.visual.PropertyBindings; import com.annimon.hotarufx.visual.PropertyTimelineHolder; import com.annimon.hotarufx.visual.PropertyTimeline; import com.annimon.hotarufx.visual.TimeLine; import com.annimon.hotarufx.visual.visitors.NodeVisitor; import javafx.scene.shape.Circle; +import static com.annimon.hotarufx.visual.PropertyType.*; public class CircleNode extends ShapeNode { @@ -36,6 +38,7 @@ public class CircleNode extends ShapeNode { return radius.setIfEmptyThenGet(circle::radiusProperty); } + @Override public void buildTimeline(TimeLine timeline) { super.buildTimeline(timeline); centerX.ifPresent(PropertyConsumers.numberConsumer(timeline)); @@ -43,6 +46,16 @@ public class CircleNode extends ShapeNode { radius.ifPresent(PropertyConsumers.numberConsumer(timeline)); } + @Override + public PropertyBindings propertyBindings(PropertyBindings bindings) { + return super.propertyBindings(bindings) + .add("cx", NUMBER, this::centerXProperty) + .add("centerX", NUMBER, this::centerXProperty) + .add("cy", NUMBER, this::centerYProperty) + .add("centerY", NUMBER, this::centerYProperty) + .add("radius", NUMBER, this::radiusProperty); + } + @Override public R accept(NodeVisitor visitor, T input) { return visitor.visit(this, input); diff --git a/app/src/main/java/com/annimon/hotarufx/visual/objects/ObjectNode.java b/app/src/main/java/com/annimon/hotarufx/visual/objects/ObjectNode.java index 2b3a8d2..4013ad6 100644 --- a/app/src/main/java/com/annimon/hotarufx/visual/objects/ObjectNode.java +++ b/app/src/main/java/com/annimon/hotarufx/visual/objects/ObjectNode.java @@ -1,5 +1,6 @@ package com.annimon.hotarufx.visual.objects; +import com.annimon.hotarufx.visual.PropertyBindings; import com.annimon.hotarufx.visual.TimeLine; import com.annimon.hotarufx.visual.visitors.NodeVisitor; import javafx.scene.Node; @@ -12,8 +13,14 @@ public abstract class ObjectNode { this.node = node; } - public void buildTimeline(TimeLine timeline) { + public void buildTimeline(TimeLine timeline) { } + public final PropertyBindings propertyBindings() { + return propertyBindings(new PropertyBindings()); + } + + protected PropertyBindings propertyBindings(PropertyBindings bindings) { + return bindings; } public abstract R accept(NodeVisitor visitor, T input); diff --git a/app/src/main/java/com/annimon/hotarufx/visual/objects/ShapeNode.java b/app/src/main/java/com/annimon/hotarufx/visual/objects/ShapeNode.java index 98cecc8..fca2db2 100644 --- a/app/src/main/java/com/annimon/hotarufx/visual/objects/ShapeNode.java +++ b/app/src/main/java/com/annimon/hotarufx/visual/objects/ShapeNode.java @@ -1,10 +1,12 @@ package com.annimon.hotarufx.visual.objects; +import com.annimon.hotarufx.visual.PropertyBindings; import com.annimon.hotarufx.visual.PropertyTimeline; import com.annimon.hotarufx.visual.PropertyTimelineHolder; import com.annimon.hotarufx.visual.TimeLine; import javafx.scene.paint.Paint; import javafx.scene.shape.Shape; +import static com.annimon.hotarufx.visual.PropertyType.*; public abstract class ShapeNode extends ObjectNode { @@ -27,9 +29,17 @@ public abstract class ShapeNode extends ObjectNode { return stroke.setIfEmptyThenGet(shape::strokeProperty); } + @Override public void buildTimeline(TimeLine timeline) { super.buildTimeline(timeline); fill.ifPresent(PropertyConsumers.paintConsumer(timeline)); stroke.ifPresent(PropertyConsumers.paintConsumer(timeline)); } + + @Override + public PropertyBindings propertyBindings(PropertyBindings bindings) { + return super.propertyBindings(bindings) + .add("fill", PAINT, this::fillProperty) + .add("stroke", PAINT, this::strokeProperty); + } }