M13.7: Essential Graphics scripting hooks, Font and uniform Scale controls, Open in Essential Graphics

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-03 07:23:22 -04:00
co-authored by Claude Opus 5.5
parent 5aae82b05c
commit 558de71720
8 changed files with 462 additions and 26 deletions
+54 -3
View File
@@ -715,6 +715,19 @@ Property.prototype.property = function () { return null; };
});
def("isDropdownEffect", function () { return false; });
// Essential Graphics: expose this property in `comp` (its own composition).
P.canAddToMotionGraphicsTemplate = function (comp) {
if (!comp || comp.__id !== this.__layer.__comp) return false;
return __call("essential.canAdd", this.__ref()).ok === true;
};
P.addToMotionGraphicsTemplateAs = function (comp, name) {
if (!this.canAddToMotionGraphicsTemplate(comp)) return false;
var p = this.__ref();
if (name !== undefined && name !== null) p.name = String(name);
__call("essential.addProperty", p);
return true;
};
P.addToMotionGraphicsTemplate = function (comp) { return this.addToMotionGraphicsTemplateAs(comp); };
P.getSeparationFollower = function (dim) {
return this.parentProperty.property(["ADBE Position_0", "ADBE Position_1", "ADBE Position_2"][dim]);
};
@@ -875,8 +888,6 @@ Property.prototype.property = function () { return null; };
};
P.keyLabel = function () { return 0; };
P.setLabelAtKey = function () {};
P.addToMotionGraphicsTemplate = function () { return false; };
P.canAddToMotionGraphicsTemplate = function () { return false; };
})();
// Layer / composition markers (`layer.marker`, `comp.markerProperty`): keyframes are markers.
@@ -1422,7 +1433,47 @@ CompItem.prototype.constructor = CompItem;
};
P.openInViewer = function () { __call("comp.open", { comp: this.__id }); return null; };
P.duplicate = function () { return __item(__call("project.duplicate", { items: [this.__id] }).items[0]); };
P.exportAsMotionGraphicsTemplate = function () { return false; };
// Essential Graphics (templates are EffectCraft's open .ectemplate files).
P.__eg = function () { return __call("essential.list", { comp: this.__id }); };
P.__controllers = function () {
var out = [];
(function walk(list) {
for (var i = 0; i < list.length; i++) {
if (list[i].kind === "property" || list[i].kind === "media") out.push(list[i]);
if (list[i].children) walk(list[i].children);
}
})(this.__eg().controls);
return out;
};
def("motionGraphicsTemplateName", function () { return this.__eg().name; }, function (v) {
__call("essential.setName", { comp: this.__id, name: String(v) });
});
def("motionGraphicsTemplateControllerCount", function () { return this.__controllers().length; });
P.getMotionGraphicsTemplateControllerName = function (i) {
var c = this.__controllers();
i = __num(i, "index");
if (i < 1 || i > c.length) throw __err("controller index " + i + " is out of range 1.." + c.length);
return c[i - 1].name;
};
P.setMotionGraphicsControllerName = function (i, name) {
var c = this.__controllers();
i = __num(i, "index");
if (i < 1 || i > c.length) throw __err("controller index " + i + " is out of range 1.." + c.length);
__call("essential.rename", { comp: this.__id, control: c[i - 1].id, name: String(name) });
return String(name);
};
P.openInEssentialGraphics = function () { __call("comp.openInEssentialGraphics", { comp: this.__id }); };
P.exportAsMotionGraphicsTemplate = function (overwrite, path) {
var f = __str(path);
if (!f) return false;
if (!/\.[A-Za-z0-9]+$/.test(f)) f += ".ectemplate";
if (!overwrite && JSON.parse(__file("exists", f)) === true) return false;
if (this.__controllers().length === 0) return false;
// The file gate applies to templates too.
__file("write", f, "");
__call("essential.exportTemplate", { comp: this.__id, path: f });
return true;
};
})();
function FootageItem(id) {
+43
View File
@@ -142,6 +142,49 @@ fn on_changing_fires_per_keystroke_and_slider_step() {
s.execute("scriptui.close", json!({})).unwrap();
}
const MOGRT: &str = r#"
var c = app.project.activeItem;
var t = c.layer(1);
var st = t.property("ADBE Text Properties").property("ADBE Text Document");
var op = t.property("ADBE Transform Group").property("ADBE Opacity");
var r = [st.canAddToMotionGraphicsTemplate(c), st.addToMotionGraphicsTemplate(c), op.addToMotionGraphicsTemplateAs(c, "Fade")];
c.motionGraphicsTemplateName = "Lower Third";
c.setMotionGraphicsControllerName(1, "Title");
r.concat([c.motionGraphicsTemplateName, c.motionGraphicsTemplateControllerCount, c.getMotionGraphicsTemplateControllerName(1), c.getMotionGraphicsTemplateControllerName(2)]).join("|")
"#;
#[test]
fn essential_graphics_scripting_hooks() {
let mut s = session();
s.execute("comp.new", json!({"name": "Card", "width": 64, "height": 64, "duration": 1})).unwrap();
s.execute("layer.newText", json!({"text": "Hi"})).unwrap();
let o = run_code(&mut s, MOGRT, "mogrt.jsx");
assert!(o.error.is_none(), "{:?} {:?}", o.error, o.output);
assert_eq!(o.result, json!("true|true|true|Lower Third|2|Title|Fade"));
let cid = s.active_comp_id().unwrap();
assert_eq!(s.project.comp(cid).unwrap().essential.as_ref().unwrap().name, "Lower Third");
// Another comp can't take this comp's properties.
s.execute("comp.new", json!({"name": "Other", "width": 64, "height": 64, "duration": 1})).unwrap();
let code = "var card = null; for (var i = 1; i <= app.project.numItems; i++) if (app.project.item(i).name === 'Card') card = app.project.item(i); var o = app.project.activeItem; var p = card.layer(1).property('ADBE Transform Group').property('ADBE Position'); [p.canAddToMotionGraphicsTemplate(o), p.canAddToMotionGraphicsTemplate(card)].join('|')";
let o = run_code(&mut s, code, "x.jsx");
assert_eq!(o.result, json!("false|true"), "{:?}", o.error);
// Export (behind the file-write preference).
let dir = std::env::temp_dir().join(format!("ec-mogrt-{}", std::process::id()));
let _ = std::fs::create_dir_all(&dir);
let path = dir.join("lower.ectemplate");
let export = format!(
"var card = null; for (var i = 1; i <= app.project.numItems; i++) if (app.project.item(i).name === 'Card') card = app.project.item(i); card.exportAsMotionGraphicsTemplate(false, {:?})",
path.to_string_lossy()
);
assert!(run_code(&mut s, &export, "x.jsx").error.is_some());
s.prefs.scripting.allow_scripts_write_files = true;
assert_eq!(run_code(&mut s, &export, "x.jsx").result, json!(true));
assert!(std::fs::metadata(&path).unwrap().len() > 100);
// It exists now: no overwrite → false.
assert_eq!(run_code(&mut s, &export, "x.jsx").result, json!(false));
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn sockets_talk_tcp_behind_the_network_preference() {
use std::io::{BufRead, BufReader, Write};