リンクをクリップボードにコピー
コピー完了
AEでのレイヤー生成を支援してくれるスクリプトの、UI実装部分の改修を行ってたんですが、「予約語の不正な使用」というエラーが多発するようになりました。
dialogとかeventとかをdlgとかevtみたいに置き換えて修正を試みたんですが治らず...という感じです。
何が原因なのでしょうか...?
スクリプトの動作に必要なアイコンフォルダーを添付します。
https://49.gigafile.nu/0616-beae379fd40f913fd2aa517115d03d65a
// @Target aftereffects
(function (thisObj) {
// 定数定義
var CONSTANTS = {
ADOBE: {
FILL: 'ADBE Fill',
FILL_COLOR: 'ADBE Fill-0002'
},
MODES: {
NULL: ['boundingBox', 'compCenter', 'layerCenter'],
CAMERA: ['camera', 'cameraWithNull']
},
SHAPES_BASE: ['Circle', 'Triangle', 'Square'], // 基本シェイプ
SHAPES_ALL: ['Circle', 'Triangle', 'Square', 'Fill'], // 全シェイプ
DEFAULTS: {
COLOR: [1, 1, 1], // デフォルトの白色
NULL_SIZE: 100, // ヌルレイヤーのデフォルトサイズ
// UI用の定数
BASE_WINDOW_WIDTH: 320, // 基準となるウィンドウ幅
DEFAULT_BUTTON_SIZE: 26, // デフォルトのボタンサイズ
MIN_BUTTON_SIZE: 20, // 最小ボタンサイズ
MAX_BUTTON_SIZE: 40, // 最大ボタンサイズ
BUTTON_SPACING: 4, // ボタン間隔
PANEL_MARGIN: 4, // パネルマージン
ROW_HEIGHT: 30, // ボタン行の高さ
},
LABELS: {
NULL: 1,
ADJUSTMENT: 5
},
TOOLTIPS: {
NULL: {
boundingBox: "Null: バウンディングボックスの中心に配置",
compCenter: "Null: コンポジション中心に配置",
layerCenter: "Null: レイヤーの中心に配置"
},
SHAPE: {
Circle: "円形シェイプ",
Triangle: "三角形シェイプ",
Square: "四角形シェイプ",
Fill: "塗りつぶしシェイプ"
},
CAMERA: {
camera: "カメラを作成",
cameraWithNull: "カメラ+ヌルを作成"
},
LANGUAGES: {
JA: "Japanese",
EN: "English"
},
LOCALIZED_TEXT: {
// 既存のローカライズテキスト定義
JA: {
// 設定パネル
SETTINGS_TITLE: "Salis LayerMaster - 設定",
TEXT_SETTINGS: "テキスト設定",
FONT_SIZE: "フォントサイズ:",
AUTO_TEXT_SIZE: "テキストサイズをコンポサイズに合わせて自動調整する",
TEXT_ANCHOR_CENTER: "テキストのアンカーポイントを常に中央に設定する",
RANDOM_TEXT: "ランダムなサンプルテキストを有効にする",
NULL_SETTINGS: "ヌルレイヤー設定",
NULL_DEFAULT_MODE: "起動時のデフォルトモード:",
AUTO_3D_NULL: "ヌルを自動的に3Dレイヤーにする",
SEPARATE_POSITION: "ヌルの位置を次元に分割する",
SHAPE_SETTINGS: "シェイプ設定",
DEFAULT_SHAPE_TYPE: "起動時のデフォルトタイプ:",
AUTO_SHAPE_SIZE: "シェイプサイズをコンポサイズに合わせて自動調整する",
SEPARATE_FILL: "塗りつぶしシェイプボタンを独立させる",
PARENT_TO_SELECTED: "選択レイヤーに親子付け",
FILL_SHAPE_TOOLTIP: "塗りつぶしシェイプレイヤー",
LANGUAGE_SETTINGS: "言語設定",
LANGUAGE: "言語:",
INTERFACE_SETTINGS: "インターフェース設定",
BUTTON_SIZE: "ボタンサイズ:",
SMALL: "小",
LARGE: "大",
OK_BUTTON: "OK",
NULL_KEEP_SELECTED: "作成後にヌルレイヤーを選択状態に保つ",
LABEL_SETTINGS: "ラベルカラー設定",
NULL_LABEL_COLORS: "ヌルレイヤーのラベルカラー",
NULL_BOUNDING_BOX: "バウンディングボックスモード:",
NULL_COMP_CENTER: "コンポジション中心モード:",
NULL_LAYER_CENTER: "レイヤー中心モード:",
OTHER_LABEL_COLORS: "他のレイヤーのラベルカラー",
SHAPE_LABEL: "シェイプレイヤー:",
ADJUSTMENT_LABEL: "調整レイヤー:",
SOLID_LABEL: "ソリッドレイヤー:",
TEXT_LABEL: "テキストレイヤー:",
CAMERA_LABEL: "カメラ:",
LIGHT_LABEL: "ライト:",
// ツールチップ
NULL_TOOLTIP: {
boundingBox: "ヌル: バウンディングボックスの中心に配置",
compCenter: "ヌル: コンポジション中心に配置",
layerCenter: "ヌル: レイヤーの中心に配置"
},
SHAPE_TOOLTIP: {
Circle: "円形シェイプ",
Triangle: "三角形シェイプ",
Square: "四角形シェイプ",
Fill: "塗りつぶしシェイプ"
},
CAMERA_TOOLTIP: {
camera: "カメラを作成",
cameraWithNull: "カメラ+ヌルを作成"
},
ADJUSTMENT_TOOLTIP: "調整レイヤー",
SOLID_TOOLTIP: "ソリッドレイヤー",
TEXT_TOOLTIP: "テキストレイヤー",
LIGHT_TOOLTIP: "ライトレイヤー",
SETTINGS_TOOLTIP: "設定"
},
EN: {
// 設定パネル
SETTINGS_TITLE: "Salis LayerMaster - Settings",
TEXT_SETTINGS: "Text Settings",
FONT_SIZE: "Font Size:",
AUTO_TEXT_SIZE: "Auto-adjust text size based on composition size",
TEXT_ANCHOR_CENTER: "Center text anchor point",
RANDOM_TEXT: "Enable random sample text",
NULL_SETTINGS: "Null Layer Settings",
NULL_DEFAULT_MODE: "Default Mode at Startup:",
AUTO_3D_NULL: "Auto 3D null layers",
SEPARATE_POSITION: "Separate null position dimensions",
SHAPE_SETTINGS: "Shape Settings",
DEFAULT_SHAPE_TYPE: "Default Type at Startup:",
AUTO_SHAPE_SIZE: "Auto-adjust shape size based on composition size",
SEPARATE_FILL: "Separate fill shape button",
PARENT_TO_SELECTED: "Parent to Selected",
FILL_SHAPE_TOOLTIP: "Fill Shape Layer",
LANGUAGE_SETTINGS: "Language Settings",
LANGUAGE: "Language:",
INTERFACE_SETTINGS: "Interface Settings",
BUTTON_SIZE: "Button Size:",
SMALL: "Small",
LARGE: "Large",
OK_BUTTON: "OK",
NULL_KEEP_SELECTED: "Keep null layer selected after creation",
LABEL_SETTINGS: "Label Color Settings",
NULL_LABEL_COLORS: "Null Layer Colors",
NULL_BOUNDING_BOX: "Bounding Box Mode:",
NULL_COMP_CENTER: "Composition Center Mode:",
NULL_LAYER_CENTER: "Layer Center Mode:",
OTHER_LABEL_COLORS: "Other Layer Colors",
SHAPE_LABEL: "Shape Layer:",
ADJUSTMENT_LABEL: "Adjustment Layer:",
SOLID_LABEL: "Solid Layer:",
TEXT_LABEL: "Text Layer:",
CAMERA_LABEL: "Camera:",
LIGHT_LABEL: "Light:",
// ツールチップ
NULL_TOOLTIP: {
boundingBox: "Null: Center to bounding box",
compCenter: "Null: Center to composition",
layerCenter: "Null: Center to layers"
},
SHAPE_TOOLTIP: {
Circle: "Circle Shape",
Triangle: "Triangle Shape",
Square: "Square Shape",
Fill: "Fill Shape"
},
CAMERA_TOOLTIP: {
camera: "Create Camera",
cameraWithNull: "Create Camera with Null"
},
ADJUSTMENT_TOOLTIP: "Adjustment Layer",
SOLID_TOOLTIP: "Solid Layer",
TEXT_TOOLTIP: "Text Layer",
LIGHT_TOOLTIP: "Light Layer",
SETTINGS_TOOLTIP: "Settings"
}
}
}
};
// バージョン情報
var scriptInfo = {
name: "Salis LayerMaster",
version: "4.5.3",
author: "Salis"
};
// 改良されたアイコン管理システム
var IconManager = {
images: {},
load: function () {
try {
var scriptFile = new File($.fileName);
var iconFolder = scriptFile.parent.fsName + "/SALIS_icons";
var paths = {
null_boundingbox: "/null_boundingbox.png",
null_compcenter: "/null_compcenter.png",
null_layercenter: "/null_layercenter.png",
shape_circle: "/shape_circle.png",
shape_triangle: "/shape_triangle.png",
shape_square: "/shape_square.png",
shape_fill: "/shape_fill.png",
adjustment: "/adjustment.png",
solid: "/solid.png",
text: "/text.png",
setting: "/setting.png",
camera: "/camera.png",
camerawithnull: "/camerawithnull.png",
light: "/light.png"
};
var images = {};
for (var key in paths) {
var file = new File(iconFolder + paths[key]);
if (file.exists) {
images[key] = ScriptUI.newImage(file);
} else {
alert("Missing icon: " + paths[key]);
}
}
this.images = images;
return images;
} catch (e) {
alert("Error loading icons: " + e.toString());
return null;
}
}
};
// ES3用のJSONポリフィル
var JSON = {
parse: function (str) {
return eval('(' + str + ')');
},
stringify: function (obj) {
var t = typeof obj;
if (t !== "object" || obj === null) {
if (t === "string") return '"' + obj.replace(/\"/g, '\\"') + '"';
return String(obj);
}
var n, v, json = [], arr = (obj && obj.constructor === Array);
for (n in obj) {
v = obj[n];
t = typeof v;
if (t === "function") continue;
if (t === "string") v = '"' + v.replace(/\"/g, '\\"') + '"';
else if (t === "object" && v !== null) v = JSON.stringify(v);
json.push((arr ? "" : '"' + n + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
}
};
// ユーティリティ関数群
var Utils = {
// 現在の選択レイヤーの情報を保存
storeSelectedLayers: function (comp) {
var selectedLayers = [];
var selected = comp.selectedLayers;
for (var i = 0; i < selected.length; i++) {
selectedLayers.push({
layer: selected[i],
index: selected[i].index,
inPoint: selected[i].inPoint,
outPoint: selected[i].outPoint
});
}
return selectedLayers;
},
// レイヤーのタイミングを設定
applyTimingToLayer: function (layer, comp, storedSelection) {
if (storedSelection && storedSelection.length > 0) {
var earliest = storedSelection[0].inPoint;
var latest = storedSelection[0].outPoint;
for (var i = 1; i < storedSelection.length; i++) {
earliest = Math.min(earliest, storedSelection[i].inPoint);
latest = Math.max(latest, storedSelection[i].outPoint);
}
try {
layer.startTime = earliest;
layer.inPoint = earliest;
layer.outPoint = latest;
} catch (e) {
alert("Error setting timing: " + e.toString());
}
} else {
layer.startTime = 0;
layer.inPoint = 0;
layer.outPoint = comp.duration;
}
},
// 既存のソリッドを探す
findExistingSolid: function (comp) {
for (var i = 1; i <= app.project.numItems; i++) {
var item = app.project.item(i);
if (item instanceof FootageItem &&
item.mainSource instanceof SolidSource &&
item.width === comp.width &&
item.height === comp.height) {
return item;
}
}
return null;
},
// レイヤーを選択レイヤーの上に移動
moveLayerAboveSelection: function (newLayer, comp, storedSelection) {
if (storedSelection && storedSelection.length > 0) {
var topLayer = storedSelection[0];
for (var i = 1; i < storedSelection.length; i++) {
if (storedSelection[i].index < topLayer.index) {
topLayer = storedSelection[i];
}
}
try {
if (topLayer.layer.index > 1) {
newLayer.moveBefore(comp.layer(topLayer.layer.index));
}
} catch (e) {
alert("Error moving layer: " + e.toString());
}
}
},
// バウンディングボックスの中心座標を取得
getBoundingBoxCenter: function (layer, comp) {
try {
// 元の親を保存
var originalParent = layer.parent;
// 一時的に親を解除
layer.parent = null;
// バウンディングボックスを取得
var bounds = layer.sourceRectAtTime(comp.time, false);
var localX = bounds.left + bounds.width / 2;
var localY = bounds.top + bounds.height / 2;
// ローカル座標をコンポジション座標に変換(より安全な実装)
var position;
// メソッド存在チェックと型チェックを組み合わせた安全な実装
if (layer instanceof AVLayer && typeof layer.toComp === "function") {
try {
// toComp メソッドが利用可能な場合
if (layer.threeDLayer) {
position = layer.toComp([localX, localY, 0]);
} else {
position = layer.toComp([localX, localY]);
}
} catch (toCompError) {
// toComp メソッドが例外を投げた場合のフォールバック
position = [
layer.position.value[0] + (localX - layer.anchorPoint.value[0]),
layer.position.value[1] + (localY - layer.anchorPoint.value[1])
];
if (layer.threeDLayer && layer.position.value.length > 2) {
position.push(layer.position.value[2]);
}
}
} else {
// シェイプレイヤーなど、toComp をサポートしていないレイヤーの場合
position = [
layer.position.value[0] + (localX - layer.anchorPoint.value[0]),
layer.position.value[1] + (localY - layer.anchorPoint.value[1])
];
if (layer.threeDLayer && layer.position.value.length > 2) {
position.push(layer.position.value[2]);
}
}
// 親を元に戻す
layer.parent = originalParent;
return position;
} catch (e) {
alert("バウンディングボックス中心の計算エラー: " + e.toString());
// エラーの場合はレイヤー位置を返す
return layer.position.value;
}
},
// レイヤーの実際の位置(コンポジション座標)を取得
getLayerPosition: function (layer) {
try {
// 元の親を保存
var originalParent = layer.parent;
// 一時的に親を解除
layer.parent = null;
// レイヤーの位置を取得(コンポジション座標系)
var position = layer.position.value.slice();
// 親を元に戻す
layer.parent = originalParent;
return position;
} catch (e) {
alert("レイヤー位置の取得エラー: " + e.toString());
return layer.position.value;
}
},
// 複数レイヤーの中心位置を計算
getMultiLayerCenter: function (layers) {
try {
var totalX = 0, totalY = 0, totalZ = 0;
var hasZ = false;
for (var i = 0; i < layers.length; i++) {
var pos = this.getLayerPosition(layers[i]);
totalX += pos[0];
totalY += pos[1];
// Zがある場合は加算
if (pos.length > 2) {
totalZ += pos[2];
hasZ = true;
}
}
var avgX = totalX / layers.length;
var avgY = totalY / layers.length;
if (hasZ) {
var avgZ = totalZ / layers.length;
return [avgX, avgY, avgZ];
} else {
return [avgX, avgY];
}
} catch (e) {
alert("複数レイヤー中心の計算エラー: " + e.toString());
return [0, 0];
}
},
getCombinedBoundingBoxCenter: function (layers, comp) {
try {
// Track extreme values for bounding box
var left = Number.MAX_VALUE;
var top = Number.MAX_VALUE;
var right = -Number.MAX_VALUE;
var bottom = -Number.MAX_VALUE;
var hasZ = false;
var sumZ = 0;
// Find the combined bounds
for (var i = 0; i < layers.length; i++) {
var layer = layers[i];
// Store original parent
var originalParent = layer.parent;
layer.parent = null;
// Get bounds for this layer
var bounds = layer.sourceRectAtTime(comp.time, false);
// Get layer position and anchor point
var layerPosition = layer.position.value;
var anchorPoint = layer.anchorPoint.value;
// Track Z dimension if present
if (layerPosition.length > 2) {
hasZ = true;
sumZ += layerPosition[2];
}
// Calculate layer corners in comp space
var layerLeft = layerPosition[0] - anchorPoint[0] + bounds.left;
var layerTop = layerPosition[1] - anchorPoint[1] + bounds.top;
var layerRight = layerLeft + bounds.width;
var layerBottom = layerTop + bounds.height;
// Update combined bounds
left = Math.min(left, layerLeft);
top = Math.min(top, layerTop);
right = Math.max(right, layerRight);
bottom = Math.max(bottom, layerBottom);
// Restore parent
layer.parent = originalParent;
}
// Calculate center of combined bounds
var centerX = (left + right) / 2;
var centerY = (top + bottom) / 2;
var result;
if (hasZ) {
var avgZ = sumZ / layers.length;
result = [centerX, centerY, avgZ];
} else {
result = [centerX, centerY];
}
return result;
} catch (e) {
alert("Error calculating combined bounding box: " + e.toString());
return [comp.width / 2, comp.height / 2]; // Fallback to comp center
}
},
// UI要素のサイズ計算用の関数
calculateUIScale: function (panel) {
try {
// Enhanced panel width detection with fallbacks
var panelWidth;
if (panel.size && panel.size[0] > 10) {
panelWidth = panel.size[0];
} else if (panel.bounds && panel.bounds.width > 10) {
panelWidth = panel.bounds.width;
} else {
// Default to base width if detection fails
panelWidth = CONSTANTS.DEFAULTS.BASE_WINDOW_WIDTH;
// Log this issue for debugging
$.writeln("Warning: Panel width detection failed, using default: " + CONSTANTS.DEFAULTS.BASE_WINDOW_WIDTH);
}
// Adjust scale calculation to be more stable
var scale = Math.max(0.5, Math.min(2.0, panelWidth / CONSTANTS.DEFAULTS.BASE_WINDOW_WIDTH));
// Calculate button size with more precise constraints
var buttonSize = Math.round(CONSTANTS.DEFAULTS.DEFAULT_BUTTON_SIZE * scale);
buttonSize = Math.max(buttonSize, CONSTANTS.DEFAULTS.MIN_BUTTON_SIZE);
buttonSize = Math.min(buttonSize, CONSTANTS.DEFAULTS.MAX_BUTTON_SIZE);
return {
buttonSize: buttonSize,
spacing: Math.max(Math.round(CONSTANTS.DEFAULTS.BUTTON_SPACING * scale), 2),
margin: Math.max(Math.round(CONSTANTS.DEFAULTS.PANEL_MARGIN * scale), 2),
rowHeight: Math.max(Math.round(CONSTANTS.DEFAULTS.ROW_HEIGHT * scale), 24)
};
} catch (e) {
$.writeln("Error in calculateUIScale: " + e);
// Return default values in case of error
return {
buttonSize: CONSTANTS.DEFAULTS.DEFAULT_BUTTON_SIZE,
spacing: CONSTANTS.DEFAULTS.BUTTON_SPACING,
margin: CONSTANTS.DEFAULTS.PANEL_MARGIN,
rowHeight: CONSTANTS.DEFAULTS.ROW_HEIGHT
};
}
}
};
function delayedUIInitialization(panel, buttons, buttonGroup) {
try {
// Use app.setTimeout for delayed execution
app.setTimeout(function () {
// Recalculate UI scale to get accurate dimensions
var newScale = Utils.calculateUIScale(panel);
var newButtonSize = newScale.buttonSize;
// Update all buttons with new size
for (var i = 0; i < buttons.length; i++) {
if (buttons[i]) {
updateButtonSize(buttons[i], newButtonSize);
}
}
// Update spacing and layout
if (buttonGroup) {
buttonGroup.spacing = newScale.spacing;
}
// Force layout update
panel.layout.layout(true);
$.writeln("Delayed UI initialization complete with button size: " + newButtonSize);
}, 200); // 200ms delay to ensure panel is fully created
} catch (e) {
$.writeln("Error in delayedUIInitialization: " + e);
}
}
// 配列内のアイテムのインデックスを取得
function indexOf(array, item) {
for (var i = 0; i < array.length; i++) {
if (array[i] === item) return i;
}
return -1;
}
// 設定管理機能
var settings = {
SECTION: "SalisLayerCreator",
KEY: "settings",
current: null,
// サンプルテキスト
sampleTexts: [
"愛のあるユニークで豊かな書体。",
"国語の授業で永遠を教わった",
"デジタル文字は美しく進化する",
"新しい時代のこころを映すタイプフェイスデザイン",
"The quick brown fox jumps over the lazy dog",
"あなたがおきにいりのフライパン",
"ピンク色の火星人と出会う",
"Agave titanotaの別名は「No.1」",
"東国永愛悪圧安暗案霊以位囲委意易異移",
"いろはにほへとちりぬるを",
"あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。"
],
// 初期設定の定義
init: function () {
this.current = {
parentToSelected: true,
nullMode: 'boundingBox',
shapeType: 'Circle',
cameraMode: 'camera',
textSize: 72,
textColor: [1, 1, 1],
randomTextEnabled: true,
lastUsedText: "",
textAnchorCenter: false,
auto3DNull: false,
separatePosition: false,
separateFillShape: false,
autoAdjustTextSize: true,
autoAdjustShapeSize: true,
buttonSize: CONSTANTS.DEFAULTS.DEFAULT_BUTTON_SIZE,
language: CONSTANTS.TOOLTIPS.LANGUAGES.JA,
keepNullSelected: true,
labelColors: {
null: {
boundingBox: 1, // Default Red
compCenter: 1, // Default Red
layerCenter: 1 // Default Red
},
shape: 9, // Default Light Yellow
adjustment: 5, // Default Yellow
solid: 6, // Default Light Green
text: 10, // Default Dark Yellow
camera: 2, // Default Yellow-Green
light: 3 // Default Pink
}
};
return this.current;
},
// 現在の言語のテキストを取得する関数
getLocalizedText: function () {
return CONSTANTS.TOOLTIPS.LOCALIZED_TEXT[this.current.language === CONSTANTS.TOOLTIPS.LANGUAGES.JA ? "JA" : "EN"];
},
// 設定の読み込み
load: function () {
try {
if (app.settings.haveSetting(this.SECTION, this.KEY)) {
this.current = eval('(' + app.settings.getSetting(this.SECTION, this.KEY) + ')');
}
} catch (e) {
this.current = this.init();
}
if (!this.current) {
this.current = this.init();
}
// ボタンサイズがない場合はデフォルト値を設定
if (!this.current.buttonSize) {
this.current.buttonSize = CONSTANTS.DEFAULTS.DEFAULT_BUTTON_SIZE;
}
return this.current;
},
// 設定の保存
save: function () {
try {
app.settings.saveSetting(this.SECTION, this.KEY, JSON.stringify(this.current));
} catch (e) {
alert("Settings save error: " + e.toString());
}
}
};
// 修正: 通常のiconbutton を使用し、後でサイズを調整するアプローチ
function createIconButton(parent, iconImage, tooltip, size) {
// iconbutton を使用して作成
var btn = parent.add("iconbutton", undefined, iconImage, { style: "toolbutton" });
btn.helpTip = tooltip;
// サイズ設定 (サイズは後でリサイズ時に再調整される)
if (size) {
btn.preferredSize = [size, size];
}
// iconKey プロパティを追加(アイコン変更時用)
btn.iconKey = "";
btn.setIcon = function (key) {
this.iconKey = key;
this.image = IconManager.images[key];
};
return btn;
}
// レイヤー生成機能
var LayerFactory = {
// ヌルレイヤーの作成
createNullShape: function (comp, position) {
var storedSelection = Utils.storeSelectedLayers(comp);
var nullShape = comp.layers.addShape();
var group = nullShape.property("Contents").addProperty("ADBE Vector Group");
var rect = group.property("Contents").addProperty("ADBE Vector Shape - Rect");
rect.property("ADBE Vector Rect Size").setValue([CONSTANTS.DEFAULTS.NULL_SIZE, CONSTANTS.DEFAULTS.NULL_SIZE]);
group.property("Contents").addProperty("ADBE Vector Graphic - Stroke").property("ADBE Vector Stroke Width").setValue(0);
nullShape.transform.anchorPoint.setValue([0, 0]);
if (position) nullShape.transform.position.setValue(position);
nullShape.name = "Null (" + comp.width + "x" + comp.height + ")";
// Set label based on null mode
nullShape.label = settings.current.labelColors.null[settings.current.nullMode];
// 3D layer setting
if (settings.current.auto3DNull) {
nullShape.threeDLayer = true;
}
// Position dimensions
if (settings.current.separatePosition) {
if (!nullShape.threeDLayer) {
nullShape.threeDLayer = true;
nullShape.transform.position.dimensionsSeparated = true;
if (!settings.current.auto3DNull) {
nullShape.threeDLayer = false;
}
} else {
nullShape.transform.position.dimensionsSeparated = true;
}
}
Utils.applyTimingToLayer(nullShape, comp, storedSelection);
Utils.moveLayerAboveSelection(nullShape, comp, storedSelection);
nullShape.guideLayer = false;
return nullShape;
},
// シェイプレイヤーの作成
createShape: function (comp, type) {
if (indexOf(CONSTANTS.SHAPES_ALL, type) === -1) {
throw new Error("Invalid shape type: " + type);
}
var storedSelection = Utils.storeSelectedLayers(comp);
var shapeLayer = comp.layers.addShape();
var group = shapeLayer.property("Contents").addProperty("ADBE Vector Group");
// シェイプサイズの計算
var shapeSize = 200; // デフォルト値
// 自動調整機能がオンの場合
if (settings.current.autoAdjustShapeSize && type !== 'Fill') {
var compSize = Math.min(comp.width, comp.height);
shapeSize = compSize * 0.2; // コンポサイズの20%
}
if (type === 'Fill') {
shapeLayer.transform.position.setValue([comp.width / 2, comp.height / 2]);
var rect = group.property("Contents").addProperty("ADBE Vector Shape - Rect");
rect.property("ADBE Vector Rect Size").setValue([comp.width, comp.height]);
// 塗りプロパティを追加
var fill = group.property("Contents").addProperty("ADBE Vector Graphic - Fill");
fill.property("ADBE Vector Fill Color").setValue(CONSTANTS.DEFAULTS.COLOR);
// エフェクトとしての塗りも追加
var fillEffect = shapeLayer.effect.addProperty(CONSTANTS.ADOBE.FILL);
fillEffect.property(CONSTANTS.ADOBE.FILL_COLOR).setValue(CONSTANTS.DEFAULTS.COLOR);
// ストロークプロパティを追加。太さは 0 に設定
var stroke = group.property("Contents").addProperty("ADBE Vector Graphic - Stroke");
stroke.property("ADBE Vector Stroke Width").setValue(0);
stroke.property("ADBE Vector Stroke Color").setValue([1, 1, 1]);
shapeLayer.transform.anchorPoint.setValue([0, 0]);
} else {
switch (type) {
case 'Circle':
var ellipse = group.property("Contents").addProperty("ADBE Vector Shape - Ellipse");
ellipse.property("ADBE Vector Ellipse Size").setValue([shapeSize, shapeSize]);
break;
case 'Triangle':
var star = group.property("Contents").addProperty("ADBE Vector Shape - Star");
star.property("ADBE Vector Star Type").setValue(2);
star.property("ADBE Vector Star Points").setValue(3);
star.property("ADBE Vector Star Outer Radius").setValue(shapeSize / 2);
break;
case 'Square':
var rect = group.property("Contents").addProperty("ADBE Vector Shape - Rect");
rect.property("ADBE Vector Rect Size").setValue([shapeSize, shapeSize]);
break;
}
var fill = group.property("Contents").addProperty("ADBE Vector Graphic - Fill");
fill.property("ADBE Vector Fill Color").setValue(CONSTANTS.DEFAULTS.COLOR);
var stroke = group.property("Contents").addProperty("ADBE Vector Graphic - Stroke");
stroke.property("ADBE Vector Stroke Width").setValue(0);
stroke.property("ADBE Vector Stroke Color").setValue([1, 1, 1]);
}
shapeLayer.name = "Shape (" + type + ")";
Utils.applyTimingToLayer(shapeLayer, comp, storedSelection);
Utils.moveLayerAboveSelection(shapeLayer, comp, storedSelection);
shapeLayer.label = settings.current.labelColors.shape;
return shapeLayer;
},
// カメラレイヤーの作成
createCamera: function (comp, withNull) {
var storedSelection = Utils.storeSelectedLayers(comp);
// カメラ作成前の状態を保存 (インデックスとUIDを記録)
var previousCameras = [];
for (var i = 1; i <= comp.numLayers; i++) {
var layer = comp.layer(i);
if (layer instanceof CameraLayer) {
previousCameras.push({
index: layer.index,
name: layer.name,
layer: layer
});
}
}
try {
// カメラ作成ダイアログを開く
app.executeCommand(2564);
// 新しいカメラを特定(インデックスベースで判断)
var newCamera = null;
// まず、新しいレイヤーとしてカメラを探す(インデックスで判断)
for (var j = 1; j <= comp.numLayers; j++) {
var currentLayer = comp.layer(j);
if (currentLayer instanceof CameraLayer) {
// 以前のカメラリストにあるか確認
var isNewCamera = true;
for (var k = 0; k < previousCameras.length; k++) {
if (currentLayer === previousCameras[k].layer) {
isNewCamera = false;
break;
}
}
if (isNewCamera) {
newCamera = currentLayer;
break;
}
}
}
if (newCamera) {
// 一時的に特殊な名前を付ける(他のカメラと区別するため)
var tempName = "NEW_TEMP_CAMERA_" + new Date().getTime();
newCamera.name = tempName;
// タイミングとレイヤー位置を調整
Utils.applyTimingToLayer(newCamera, comp, storedSelection);
Utils.moveLayerAboveSelection(newCamera, comp, storedSelection);
// For Camera Layer Label Assignment
newCamera.label = settings.current.labelColors.camera;
// ヌル付きカメラモードの場合
if (withNull) {
try {
// ヌルレイヤーを作成
var nullLayer = this.createNullShape(comp, [comp.width / 2, comp.height / 2]);
if (nullLayer) {
nullLayer.name = "Null (Camera Linked)";
nullLayer.guideLayer = true;
// 特殊な名前で新しいカメラを再取得(安全のため)
var confirmedCamera = null;
for (var l = 1; l <= comp.numLayers; l++) {
var layer = comp.layer(l);
if (layer.name === tempName) {
confirmedCamera = layer;
break;
}
}
// 改めて確認されたカメラの親をヌルに設定
if (confirmedCamera) {
confirmedCamera.parent = nullLayer;
// 最終的な名前を設定
confirmedCamera.name = "Camera (" + comp.width + "x" + comp.height + ")";
// カメラとヌルのレイヤー順を調整
nullLayer.moveBefore(confirmedCamera);
} else {
// カメラが見つからなかった場合は元のカメラを使用
newCamera.parent = nullLayer;
newCamera.name = "Camera (" + comp.width + "x" + comp.height + ")";
nullLayer.moveBefore(newCamera);
}
}
} catch (nullErr) {
// エラーが発生した場合も、カメラの名前を最終的なものに設定
newCamera.name = "Camera (" + comp.width + "x" + comp.height + ")";
alert("Error creating null for camera: " + nullErr.toString());
}
} else {
// ヌルなしの場合は単純に名前を設定
newCamera.name = "Camera (" + comp.width + "x" + comp.height + ")";
}
return newCamera;
} else {
$.writeln("Camera creation dialog was possibly canceled");
}
} catch (e) {
alert("Error in camera creation process: " + e.toString());
}
return null;
},
// ライトレイヤーの作成
createLight: function (comp) {
var storedSelection = Utils.storeSelectedLayers(comp);
app.beginUndoGroup("Create Light");
// AEの標準コマンドでライト作成ダイアログを表示
app.executeCommand(2563);
// 新しく作成されたライトを探す
var lightLayer = comp.layer(1);
if (lightLayer && lightLayer instanceof LightLayer) {
lightLayer.name = "Light (" + comp.width + "x" + comp.height + ")";
lightLayer.label = settings.current.labelColors.light;
Utils.applyTimingToLayer(lightLayer, comp, storedSelection);
Utils.moveLayerAboveSelection(lightLayer, comp, storedSelection);
}
app.endUndoGroup();
},
// テキストレイヤーの作成
createText: function (comp) {
var storedSelection = Utils.storeSelectedLayers(comp);
var textLayer = comp.layers.addText();
var textProp = textLayer.property("Source Text");
var textDocument = textProp.value;
// フォントサイズの設定
var fontSize = settings.current.textSize || 72; // デフォルト値
// 自動調整機能がオンの場合
if (settings.current.autoAdjustTextSize) {
var compSize = Math.min(comp.width, comp.height); // コンポの短辺を基準
if (settings.current.randomTextEnabled) {
// ランダムモード:テキスト選択
var availableTexts = [];
for (var i = 0; i < settings.sampleTexts.length; i++) {
if (settings.sampleTexts[i] !== settings.current.lastUsedText) {
availableTexts.push(settings.sampleTexts[i]);
}
}
var randomIndex = Math.floor(Math.random() * availableTexts.length);
var randomText = availableTexts[randomIndex];
textDocument.text = randomText;
settings.current.lastUsedText = randomText;
// テキストの長さに応じてサイズ調整
var textLength = randomText.length;
var baseFontSize = compSize * (0.08 - Math.min(0.06, (textLength * 0.002)));
fontSize = Math.round(baseFontSize);
} else {
// 固定テキスト「Text」
textDocument.text = "Text";
fontSize = Math.round(compSize * 0.15); // コンポサイズの15%
}
} else {
// 自動調整機能がオフの場合、設定値をそのまま使用
if (settings.current.randomTextEnabled) {
// ランダムテキストを選択
var availableTexts = [];
for (var i = 0; i < settings.sampleTexts.length; i++) {
if (settings.sampleTexts[i] !== settings.current.lastUsedText) {
availableTexts.push(settings.sampleTexts[i]);
}
}
var randomIndex = Math.floor(Math.random() * availableTexts.length);
var randomText = availableTexts[randomIndex];
textDocument.text = randomText;
settings.current.lastUsedText = randomText;
} else {
textDocument.text = "Text";
}
}
textDocument.fontSize = fontSize;
textDocument.fillColor = [1, 1, 1];
textProp.setValue(textDocument);
// アンカーポイント設定
if (settings.current.textAnchorCenter) {
textLayer.transform.anchorPoint.expression =
"[sourceRectAtTime(time, false).width / 2 + sourceRectAtTime(time, false).left, " +
"sourceRectAtTime(time, false).height / 2 + sourceRectAtTime(time, false).top]";
}
Utils.applyTimingToLayer(textLayer, comp, storedSelection);
Utils.moveLayerAboveSelection(textLayer, comp, storedSelection);
textLayer.label = settings.current.labelColors.text;
return textLayer;
},
};
// 改善したモード切り替え機能
function setupNullButton(btn) {
// 右クリック処理
btn.addEventListener("click", function (event) {
if (event.button === 2) { // 右クリック
var modes = CONSTANTS.MODES.NULL;
var currentIndex = indexOf(modes, settings.current.nullMode);
var nextIndex = (currentIndex + 1) % modes.length;
settings.current.nullMode = modes[nextIndex];
// アイコンの更新
var newIconKey = 'null_' + settings.current.nullMode.toLowerCase();
this.setIcon(newIconKey);
// ツールチップの更新
var locText = settings.getLocalizedText();
this.helpTip = locText.NULL_TOOLTIP[settings.current.nullMode];
// 設定の保存
settings.save();
event.preventDefault();
}
});
}
function setupShapeButton(btn) {
// 右クリック処理
btn.addEventListener("click", function (event) {
if (event.button === 2) { // 右クリック
// 利用可能なシェイプリストを取得
var availableShapes = settings.current.separateFillShape ?
CONSTANTS.SHAPES_BASE : CONSTANTS.SHAPES_ALL;
var currentIndex = indexOf(availableShapes, settings.current.shapeType);
var nextIndex = (currentIndex + 1) % availableShapes.length;
settings.current.shapeType = availableShapes[nextIndex];
// アイコンの更新
var newIconKey = 'shape_' + settings.current.shapeType.toLowerCase();
this.setIcon(newIconKey);
// ツールチップの更新
var locText = settings.getLocalizedText();
this.helpTip = locText.SHAPE_TOOLTIP[settings.current.shapeType];
// 設定の保存
settings.save();
event.preventDefault();
}
});
}
function setupCameraButton(btn) {
// 右クリック処理
btn.addEventListener("click", function (event) {
if (event.button === 2) { // 右クリック
var nextMode = settings.current.cameraMode === 'camera' ? 'cameraWithNull' : 'camera';
settings.current.cameraMode = nextMode;
// アイコンの更新
var newIconKey = nextMode === 'camera' ? 'camera' : 'camerawithnull';
this.setIcon(newIconKey);
// ツールチップの更新
var locText = settings.getLocalizedText();
this.helpTip = locText.CAMERA_TOOLTIP[nextMode];
// 設定の保存
settings.save();
event.preventDefault();
}
});
}
function createLabelColorDropdown(parent, labelText, currentValue) {
var group = parent.add("group");
group.orientation = "row";
group.alignChildren = ["left", "center"];
group.spacing = 5;
group.add("statictext", undefined, labelText);
var dropdown = group.add("dropdownlist");
// Add color options (None + 16 AE label colors)
var colorNames = [
"None", "Red", "Yellow", "Aqua", "Pink", "Lavender",
"Peach", "Sea Foam", "Blue", "Green", "Purple",
"Orange", "Brown", "Fuchsia", "Cyan", "Sandstone", "Dark Green"
];
for (var i = 0; i < colorNames.length; i++) {
dropdown.add("item", colorNames[i]);
}
// Set current selection
dropdown.selection = currentValue;
return dropdown;
}
// 設定ダイアログの作成
function createSettingsDialog() {
// Get localized text
var locText = settings.getLocalizedText();
// Create the dialog window
var dlg = new Window("dialog", locText.SETTINGS_TITLE);
dlg.orientation = "column";
dlg.alignChildren = ["fill", "top"];
dlg.spacing = 10;
dlg.margins = 16;
dlg.preferredSize.width = 340;
// Create header section
var hdrGrp = dlg.add("group");
hdrGrp.orientation = "column";
hdrGrp.alignChildren = ["center", "center"];
var ttlTxt = hdrGrp.add("statictext", undefined, scriptInfo.name + " v" + scriptInfo.version);
ttlTxt.graphics.font = ScriptUI.newFont("Arial", "BOLD", 14);
// Add separator line
dlg.add("panel", undefined, undefined, { height: 2 });
// Create tabbed panel
var tpanel = dlg.add("tabbedpanel");
tpanel.alignChildren = "fill";
// 基本設定タブ
var basicTab = tpanel.add("tab", undefined, "基本設定");
basicTab.orientation = "column";
basicTab.alignChildren = ["fill", "top"];
basicTab.spacing = 10;
basicTab.margins = 10;
// テキスト設定グループ
var textGroup = basicTab.add("panel", undefined, locText.TEXT_SETTINGS);
textGroup.orientation = "column";
textGroup.alignChildren = ["fill", "top"];
textGroup.margins = [10, 15, 10, 10];
textGroup.spacing = 5;
// フォントサイズ設定
var fontSizeGroup = textGroup.add("group");
fontSizeGroup.orientation = "row";
fontSizeGroup.alignChildren = ["left", "center"];
fontSizeGroup.spacing = 5;
fontSizeGroup.add("statictext", undefined, locText.FONT_SIZE);
var fontSizeSlider = fontSizeGroup.add("slider", undefined, settings.current.textSize, 1, 200);
fontSizeSlider.preferredSize.width = 100;
var fontSizeText = fontSizeGroup.add("edittext", undefined, settings.current.textSize);
fontSizeText.characters = 4;
fontSizeSlider.onChanging = function () {
fontSizeText.text = Math.round(this.value).toString();
};
fontSizeText.onChange = function () {
var value = parseInt(this.text);
if (!isNaN(value) && value >= 1 && value <= 200) {
fontSizeSlider.value = value;
}
};
// テキストサイズの自動調整
var autoTextSizeCheck = textGroup.add("checkbox", undefined, locText.AUTO_TEXT_SIZE);
autoTextSizeCheck.value = settings.current.autoAdjustTextSize;
// テキストのアンカーポイントオプション
var anchorCheck = textGroup.add("checkbox", undefined, locText.TEXT_ANCHOR_CENTER);
anchorCheck.value = settings.current.textAnchorCenter || false;
// ランダムテキストオプション
var randomTextCheck = textGroup.add("checkbox", undefined, locText.RANDOM_TEXT);
randomTextCheck.value = settings.current.randomTextEnabled;
// 言語設定(基本設定タブ内に配置)
var langGroup = basicTab.add("panel", undefined, locText.LANGUAGE_SETTINGS);
langGroup.orientation = "row";
langGroup.alignChildren = ["left", "center"];
langGroup.margins = [10, 15, 10, 10];
langGroup.spacing = 10;
langGroup.add("statictext", undefined, locText.LANGUAGE);
var langDropdown = langGroup.add("dropdownlist");
langDropdown.add("item", "日本語");
langDropdown.add("item", "English");
// 現在の言語を選択
if (settings.current.language === CONSTANTS.TOOLTIPS.LANGUAGES.JA) {
langDropdown.selection = 0;
} else {
langDropdown.selection = 1;
}
// ヌルレイヤー設定タブ
var nullTab = tpanel.add("tab", undefined, "ヌルレイヤー設定");
nullTab.orientation = "column";
nullTab.alignChildren = ["fill", "top"];
nullTab.spacing = 10;
nullTab.margins = 10;
var nullGroup = nullTab.add("panel", undefined, locText.NULL_SETTINGS);
nullGroup.orientation = "column";
nullGroup.alignChildren = ["fill", "top"];
nullGroup.margins = [10, 15, 10, 10];
nullGroup.spacing = 5;
var keepNullSelectedCheck = nullGroup.add("checkbox", undefined, locText.NULL_KEEP_SELECTED);
keepNullSelectedCheck.value = settings.current.keepNullSelected || false;
var auto3DCheck = nullGroup.add("checkbox", undefined, locText.AUTO_3D_NULL);
auto3DCheck.value = settings.current.auto3DNull || false;
var separatePositionCheck = nullGroup.add("checkbox", undefined, locText.SEPARATE_POSITION);
separatePositionCheck.value = settings.current.separatePosition || false;
// ヌルモードの選択肢
var nullDefaultModeGroup = nullGroup.add("group");
nullDefaultModeGroup.orientation = "row";
nullDefaultModeGroup.alignChildren = ["left", "center"];
nullDefaultModeGroup.spacing = 5;
nullDefaultModeGroup.add("statictext", undefined, locText.NULL_DEFAULT_MODE);
var nullModeDropdown = nullDefaultModeGroup.add("dropdownlist");
nullModeDropdown.add("item", "バウンディングボックス中心");
nullModeDropdown.add("item", "コンポジション中心");
nullModeDropdown.add("item", "レイヤー中心");
// 現在の設定値に基づいてドロップダウンを選択
switch (settings.current.nullMode) {
case 'boundingBox':
nullModeDropdown.selection = 0;
break;
case 'compCenter':
nullModeDropdown.selection = 1;
break;
case 'layerCenter':
nullModeDropdown.selection = 2;
break;
default:
nullModeDropdown.selection = 0;
}
// シェイプ設定タブ
var shapeTab = tpanel.add("tab", undefined, "シェイプ設定");
shapeTab.orientation = "column";
shapeTab.alignChildren = ["fill", "top"];
shapeTab.spacing = 10;
shapeTab.margins = 10;
var shapeGroup = shapeTab.add("panel", undefined, locText.SHAPE_SETTINGS);
shapeGroup.orientation = "column";
shapeGroup.alignChildren = ["fill", "top"];
shapeGroup.margins = [10, 15, 10, 10];
shapeGroup.spacing = 5;
var autoShapeSizeCheck = shapeGroup.add("checkbox", undefined, locText.AUTO_SHAPE_SIZE);
autoShapeSizeCheck.value = settings.current.autoAdjustShapeSize;
var separateFillCheck = shapeGroup.add("checkbox", undefined, locText.SEPARATE_FILL);
separateFillCheck.value = settings.current.separateFillShape || false;
// シェイプタイプの選択肢
var shapeTypeGroup = shapeGroup.add("group");
shapeTypeGroup.orientation = "row";
shapeTypeGroup.alignChildren = ["left", "center"];
shapeTypeGroup.spacing = 5;
shapeTypeGroup.add("statictext", undefined, locText.DEFAULT_SHAPE_TYPE);
var shapeTypeDropdown = shapeTypeGroup.add("dropdownlist");
shapeTypeDropdown.add("item", "円形");
shapeTypeDropdown.add("item", "三角形");
shapeTypeDropdown.add("item", "四角形");
var fillShapeItem = shapeTypeDropdown.add("item", "塗りつぶし");
// 塗りつぶしが分離されていたら無効化
fillShapeItem.enabled = !settings.current.separateFillShape;
// 現在の設定値に基づいてドロップダウンを選択
switch (settings.current.shapeType) {
case 'Circle':
shapeTypeDropdown.selection = 0;
break;
case 'Triangle':
shapeTypeDropdown.selection = 1;
break;
case 'Square':
shapeTypeDropdown.selection = 2;
break;
case 'Fill':
shapeTypeDropdown.selection = 3;
break;
default:
shapeTypeDropdown.selection = 0;
}
// 塗りつぶし分離設定が変更されたときにシェイプタイプドロップダウンを更新
separateFillCheck.onClick = function () {
fillShapeItem.enabled = !this.value;
if (this.value && shapeTypeDropdown.selection && shapeTypeDropdown.selection.index === 3) {
// 塗りつぶしが分離され、現在のタイプが塗りつぶしの場合、デフォルトを円形に変更
shapeTypeDropdown.selection = 0;
}
};
// 情報タブ
var infoTab = tpanel.add("tab", undefined, "情報");
infoTab.orientation = "column";
infoTab.alignChildren = ["fill", "top"];
infoTab.spacing = 10;
infoTab.margins = 10;
var infoPanel = infoTab.add("panel", undefined, "スクリプト情報");
infoPanel.orientation = "column";
infoPanel.alignChildren = ["left", "top"];
infoPanel.spacing = 5;
infoPanel.margins = 10;
infoPanel.add("statictext", undefined, "スクリプト名: " + scriptInfo.name);
infoPanel.add("statictext", undefined, "バージョン: " + scriptInfo.version);
infoPanel.add("statictext", undefined, "作者: " + scriptInfo.author);
var descriptionText = infoPanel.add("statictext", undefined, "説明:", { multiline: true });
var description = "このスクリプトは、After Effectsでの作業効率を向上させるためのレイヤー生成ツールです。ヌルレイヤー、シェイプレイヤー、調整レイヤー、テキストレイヤーなど、様々な種類のレイヤーをワンクリックで作成できます。";
descriptionText.text = description;
descriptionText.preferredSize.width = 280;
descriptionText.preferredSize.height = 60;
var oldSeparateFillShape = settings.current.separateFillShape;
var oldLanguage = settings.current.language;
// フッター
var footerPanel = dlg.add("group");
footerPanel.orientation = "column";
footerPanel.alignChildren = ["center", "center"];
var copyrightText = footerPanel.add("statictext", undefined, "© " + new Date().getFullYear() + " " + scriptInfo.author + ". All rights reserved.");
copyrightText.graphics.foregroundColor = copyrightText.graphics.newPen(copyrightText.graphics.PenType.SOLID_COLOR, [0.5, 0.5, 0.5], 1);
// Create a new tab for label colors
var labelTab = tpanel.add("tab", undefined, locText.LABEL_SETTINGS);
labelTab.orientation = "column";
labelTab.alignChildren = ["fill", "top"];
labelTab.spacing = 10;
labelTab.margins = 10;
// Create null layer label controls (for 3 modes)
var nullLabelsGroup = labelTab.add("panel", undefined, locText.NULL_LABEL_COLORS);
nullLabelsGroup.orientation = "column";
nullLabelsGroup.alignChildren = ["fill", "top"];
nullLabelsGroup.margins = [10, 15, 10, 10];
nullLabelsGroup.spacing = 5;
var nullBoundingBoxDropdown = createLabelColorDropdown(
nullLabelsGroup,
locText.NULL_BOUNDING_BOX,
settings.current.labelColors.null.boundingBox
);
var nullCompCenterDropdown = createLabelColorDropdown(
nullLabelsGroup,
locText.NULL_COMP_CENTER,
settings.current.labelColors.null.compCenter
);
var nullLayerCenterDropdown = createLabelColorDropdown(
nullLabelsGroup,
locText.NULL_LAYER_CENTER,
settings.current.labelColors.null.layerCenter
);
// Create other layer type label controls
var otherLabelsGroup = labelTab.add("panel", undefined, locText.OTHER_LABEL_COLORS);
otherLabelsGroup.orientation = "column";
otherLabelsGroup.alignChildren = ["fill", "top"];
otherLabelsGroup.margins = [10, 15, 10, 10];
otherLabelsGroup.spacing = 5;
var shapeDropdown = createLabelColorDropdown(
otherLabelsGroup, locText.SHAPE_LABEL,
settings.current.labelColors.shape
);
var adjustmentDropdown = createLabelColorDropdown(
otherLabelsGroup, locText.ADJUSTMENT_LABEL,
settings.current.labelColors.adjustment
);
var solidDropdown = createLabelColorDropdown(
otherLabelsGroup, locText.SOLID_LABEL,
settings.current.labelColors.solid
);
var textDropdown = createLabelColorDropdown(
otherLabelsGroup, locText.TEXT_LABEL,
settings.current.labelColors.text
);
var cameraDropdown = createLabelColorDropdown(
otherLabelsGroup, locText.CAMERA_LABEL,
settings.current.labelColors.camera
);
var lightDropdown = createLabelColorDropdown(
otherLabelsGroup, locText.LIGHT_LABEL,
settings.current.labelColors.light
);
// ボタングループ
var buttonGroup = dlg.add("group");
buttonGroup.orientation = "row";
buttonGroup.alignChildren = ["center", "center"];
buttonGroup.spacing = 10;
var okButton = buttonGroup.add("button", undefined, locText.OK_BUTTON, { name: "ok" });
var cancelButton = buttonGroup.add("button", undefined, "キャンセル", { name: "cancel" });
// OKボタンがクリックされたとき
okButton.onClick = function () {
// 設定の保存
settings.current.textSize = parseInt(fontSizeText.text);
settings.current.textAnchorCenter = anchorCheck.value;
settings.current.randomTextEnabled = randomTextCheck.value;
settings.current.auto3DNull = auto3DCheck.value;
settings.current.separatePosition = separatePositionCheck.value;
settings.current.separateFillShape = separateFillCheck.value;
settings.current.autoAdjustTextSize = autoTextSizeCheck.value;
settings.current.autoAdjustShapeSize = autoShapeSizeCheck.value;
settings.current.keepNullSelected = keepNullSelectedCheck.value;
settings.current.labelColors.null.boundingBox = nullBoundingBoxDropdown.selection.index;
settings.current.labelColors.null.compCenter = nullCompCenterDropdown.selection.index;
settings.current.labelColors.null.layerCenter = nullLayerCenterDropdown.selection.index;
settings.current.labelColors.shape = shapeDropdown.selection.index;
settings.current.labelColors.adjustment = adjustmentDropdown.selection.index;
settings.current.labelColors.solid = solidDropdown.selection.index;
settings.current.labelColors.text = textDropdown.selection.index;
settings.current.labelColors.camera = cameraDropdown.selection.index;
settings.current.labelColors.light = lightDropdown.selection.index;
// ヌルモードの設定(ドロップダウンのインデックスからモード取得)
if (nullModeDropdown.selection) {
switch (nullModeDropdown.selection.index) {
case 0:
settings.current.nullMode = 'boundingBox';
break;
case 1:
settings.current.nullMode = 'compCenter';
break;
case 2:
settings.current.nullMode = 'layerCenter';
break;
}
}
// シェイプタイプの設定(ドロップダウンのインデックスからタイプ取得)
if (shapeTypeDropdown.selection) {
switch (shapeTypeDropdown.selection.index) {
case 0:
settings.current.shapeType = 'Circle';
break;
case 1:
settings.current.shapeType = 'Triangle';
break;
case 2:
settings.current.shapeType = 'Square';
break;
case 3:
settings.current.shapeType = 'Fill';
break;
}
}
// 言語設定の保存
settings.current.language = langDropdown.selection.index === 0 ?
CONSTANTS.TOOLTIPS.LANGUAGES.JA : CONSTANTS.TOOLTIPS.LANGUAGES.EN;
// シェイプタイプの有効性確認
if (oldSeparateFillShape !== settings.current.separateFillShape) {
var availableShapes = settings.current.separateFillShape ?
CONSTANTS.SHAPES_BASE : CONSTANTS.SHAPES_ALL;
if (indexOf(availableShapes, settings.current.shapeType) === -1) {
settings.current.shapeType = 'Circle';
}
}
settings.save();
dlg.close();
// 言語変更またはフィルシェイプ設定変更時はパネルを再構築
if (oldSeparateFillShape !== settings.current.separateFillShape ||
oldLanguage !== settings.current.language) {
if (panel instanceof Panel) {
panel.remove(0);
buildUI(panel);
panel.layout.layout(true);
}
}
};
dlg.center();
return dlg.show();
}
// メインUI構築 - レスポンシブサポートを追加
function buildUI(thisObj) {
var panel = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Salis LayerMaster", undefined, { resizeable: true });
if (!panel) return null;
// Load icons and settings
IconManager.load();
settings.load();
var locText = settings.getLocalizedText();
// シェイプタイプの有効性確認と初期化
var availableShapes = settings.current.separateFillShape ?
CONSTANTS.SHAPES_BASE : CONSTANTS.SHAPES_ALL;
if (!settings.current.shapeType || indexOf(availableShapes, settings.current.shapeType) === -1) {
settings.current.shapeType = 'Circle';
settings.save();
}
if (!settings.current.nullMode) {
settings.current.nullMode = 'boundingBox';
settings.save();
}
if (!settings.current.cameraMode) {
settings.current.cameraMode = 'camera';
settings.save();
}
// パネルの基本設定
panel.orientation = "column";
panel.alignChildren = ["fill", "top"];
panel.spacing = 0;
panel.margins = 0;
// 現在のUIスケールを取得
var uiScale = Utils.calculateUIScale(panel);
var buttonSize = uiScale.buttonSize;
// Log initial UI scale for debugging
$.writeln("Initial UI scale calculated - button size: " + buttonSize);
// ボタングループの作成
var buttonGroup = panel.add("group");
buttonGroup.orientation = "row";
buttonGroup.alignChildren = ["left", "center"];
buttonGroup.spacing = uiScale.spacing;
buttonGroup.margins = 0;
// 各ボタンの作成
// ヌルボタン
var nullBtn = createIconButton(buttonGroup, IconManager.images['null_' + settings.current.nullMode.toLowerCase()],
locText.NULL_TOOLTIP[settings.current.nullMode], buttonSize);
nullBtn.setIcon('null_' + settings.current.nullMode.toLowerCase());
// シェイプボタ
var shapeBtn = createIconButton(buttonGroup, IconManager.images['shape_' + settings.current.shapeType.toLowerCase()],
locText.SHAPE_TOOLTIP[settings.current.shapeType], buttonSize);
shapeBtn.setIcon('shape_' + settings.current.shapeType.toLowerCase());
// 調整レイヤーボタン
var adjBtn = createIconButton(buttonGroup, IconManager.images.adjustment, locText.ADJUSTMENT_TOOLTIP, buttonSize);
adjBtn.setIcon('adjustment');
// ソリッドボタン
var solidBtn = createIconButton(buttonGroup, IconManager.images.solid, locText.SOLID_TOOLTIP, buttonSize);
solidBtn.setIcon('solid');
// 塗りつぶしシェイプボタン(オプション)
var fillShapeBtn;
if (settings.current.separateFillShape) {
fillShapeBtn = createIconButton(buttonGroup, IconManager.images.shape_fill, locText.FILL_SHAPE_TOOLTIP, buttonSize);
fillShapeBtn.setIcon('shape_fill');
}
// テキストボタン
var textBtn = createIconButton(buttonGroup, IconManager.images.text, locText.TEXT_TOOLTIP, buttonSize);
textBtn.setIcon('text');
// カメラボタン
var cameraBtn = createIconButton(buttonGroup, IconManager.images[settings.current.cameraMode === 'camera' ? 'camera' : 'camerawithnull'],
locText.CAMERA_TOOLTIP[settings.current.cameraMode], buttonSize);
cameraBtn.setIcon(settings.current.cameraMode === 'camera' ? 'camera' : 'camerawithnull');
// ライトボタン
var lightBtn = createIconButton(buttonGroup, IconManager.images.light, locText.LIGHT_TOOLTIP, buttonSize);
lightBtn.setIcon('light');
// 設定ボタン
var settingBtn = createIconButton(buttonGroup, IconManager.images.setting, locText.SETTINGS_TOOLTIP, buttonSize);
settingBtn.setIcon('setting');
// 親子付けチェックボックス
var parentCheckbox = buttonGroup.add("checkbox", undefined, locText.PARENT_TO_SELECTED);
parentCheckbox.value = settings.current.parentToSelected;
var allButtons = [nullBtn, shapeBtn, adjBtn, solidBtn];
if (fillShapeBtn) allButtons.push(fillShapeBtn);
allButtons = allButtons.concat([textBtn, cameraBtn, lightBtn, settingBtn]);
for (var i = 0; i < allButtons.length; i++) {
updateButtonSize(allButtons[i], buttonSize);
}
panel.onResizing = panel.onResize = function () {
// Update layout
this.layout.resize();
// Recalculate UI scale
var newScale = Utils.calculateUIScale(this);
var newButtonSize = newScale.buttonSize;
// Update all buttons
for (var i = 0; i < allButtons.length; i++) {
updateButtonSize(allButtons[i], newButtonSize);
}
// Update group spacing
buttonGroup.spacing = newScale.spacing;
// Refresh layout
this.layout.layout(true);
};
if (panel instanceof Window) {
panel.onShow = function () {
delayedUIInitialization(panel, allButtons, buttonGroup);
};
} else {
delayedUIInitialization(panel, allButtons, buttonGroup);
}
// ボタンのサイズ更新関数
function updateButtonSize(btn, size) {
if (!btn) return;
try {
// Set both size and preferredSize for maximum compatibility
btn.size = [size, size];
btn.preferredSize = [size, size];
// Force redraw to ensure changes take effect
if (btn.notify && typeof btn.notify === 'function') {
btn.notify("onDraw");
}
} catch (e) {
$.writeln("Error updating button size: " + e);
}
}
// 塗りつぶしシェイプボタンのクリックイベント
if (fillShapeBtn) {
fillShapeBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) return;
app.beginUndoGroup("Create Fill Shape");
try {
LayerFactory.createShape(comp, 'Fill');
} catch (e) {
alert("Error: " + e.toString());
}
app.endUndoGroup();
};
}
// モード切り替え機能のセットアップ
setupNullButton(nullBtn);
setupShapeButton(shapeBtn);
setupCameraButton(cameraBtn);
// ヌルボタンのクリックイベント
nullBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) return;
app.beginUndoGroup("Create Null");
try {
var selectedLayers = comp.selectedLayers;
var createdNull = null; // Track created null for selection
if (selectedLayers.length > 0) {
switch (settings.current.nullMode) {
case 'boundingBox':
if (selectedLayers.length === 1) {
// Single layer - use existing method
var layer = selectedLayers[0];
var individualSelection = [{
layer: layer,
index: layer.index,
inPoint: layer.inPoint,
outPoint: layer.outPoint
}];
// Get bounding box center
var position = Utils.getBoundingBoxCenter(layer, comp);
// Create null layer
createdNull = LayerFactory.createNullShape(comp, position);
if (settings.current.parentToSelected) {
layer.parent = createdNull;
}
Utils.applyTimingToLayer(createdNull, comp, individualSelection);
createdNull.moveBefore(layer);
} else {
// Multiple layers - use combined bounding box
var storedSelection = Utils.storeSelectedLayers(comp);
// Get combined bounding box center
var position = Utils.getCombinedBoundingBoxCenter(selectedLayers, comp);
// Create null layer
createdNull = LayerFactory.createNullShape(comp, position);
// Apply parenting if enabled
if (settings.current.parentToSelected) {
for (var i = 0; i < selectedLayers.length; i++) {
selectedLayers[i].parent = createdNull;
}
}
// Apply timing and move above selection
Utils.applyTimingToLayer(createdNull, comp, storedSelection);
Utils.moveLayerAboveSelection(createdNull, comp, storedSelection);
}
break;
case 'layerCenter':
if (selectedLayers.length === 1) {
// Single layer - use layer position
var layer = selectedLayers[0];
var position = Utils.getLayerPosition(layer);
createdNull = LayerFactory.createNullShape(comp, position);
if (settings.current.parentToSelected) {
layer.parent = createdNull;
}
} else {
// Multiple layers - use average center
var center = Utils.getMultiLayerCenter(selectedLayers);
createdNull = LayerFactory.createNullShape(comp, center);
if (settings.current.parentToSelected) {
for (var i = 0; i < selectedLayers.length; i++) {
selectedLayers[i].parent = createdNull;
}
}
}
break;
case 'compCenter':
// Place at composition center
var position = [comp.width / 2, comp.height / 2];
createdNull = LayerFactory.createNullShape(comp, position);
if (settings.current.parentToSelected) {
for (var i = 0; i < selectedLayers.length; i++) {
selectedLayers[i].parent = createdNull;
}
}
break;
}
} else {
// No selection - place null at comp center
createdNull = LayerFactory.createNullShape(comp, [comp.width / 2, comp.height / 2]);
}
// Apply selection preference
if (settings.current.keepNullSelected && createdNull) {
// Deselect all layers
for (var i = 1; i <= comp.layers.length; i++) {
comp.layer(i).selected = false;
}
// Select the newly created null
createdNull.selected = true;
}
} catch (e) {
alert("Error creating null: " + e.toString());
}
app.endUndoGroup();
};
// シェイプボタンのクリックイベント
shapeBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) return;
app.beginUndoGroup("Create Shape");
try {
LayerFactory.createShape(comp, settings.current.shapeType);
} catch (e) {
alert("Error: " + e.toString());
}
app.endUndoGroup();
};
// 調整レイヤーボタンのクリックイベント
adjBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) return;
app.beginUndoGroup("Create Adjustment Layer");
try {
var storedSelection = Utils.storeSelectedLayers(comp);
var existingItem = Utils.findExistingSolid(comp);
var newLayer;
var layerName = "Adjustment Layer (" + comp.width + "x" + comp.height + ")";
if (existingItem) {
newLayer = comp.layers.add(existingItem);
} else {
newLayer = comp.layers.addSolid(CONSTANTS.DEFAULTS.COLOR,
layerName, comp.width, comp.height, comp.pixelAspect, comp.duration);
}
newLayer.adjustmentLayer = true;
newLayer.name = layerName;
newLayer.label = settings.current.labelColors.adjustment;
Utils.applyTimingToLayer(newLayer, comp, storedSelection);
Utils.moveLayerAboveSelection(newLayer, comp, storedSelection);
} catch (e) {
alert("Error creating adjustment layer: " + e.toString());
}
app.endUndoGroup();
};
// ソリッドボタンのクリックイベント
solidBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) return;
app.beginUndoGroup("Create Solid");
try {
var storedSelection = Utils.storeSelectedLayers(comp);
var existingItem = Utils.findExistingSolid(comp);
var newLayer;
if (existingItem) {
newLayer = comp.layers.add(existingItem);
} else {
newLayer = comp.layers.addSolid(CONSTANTS.DEFAULTS.COLOR,
"Solid (" + comp.width + "x" + comp.height + ")",
comp.width, comp.height, comp.pixelAspect, comp.duration);
}
// 必ずSolidとして名前を再設定
newLayer.name = "Solid (" + comp.width + "x" + comp.height + ")";
newLayer.label = settings.current.labelColors.solid;
var fillEffect = newLayer.effect.addProperty(CONSTANTS.ADOBE.FILL);
fillEffect.property(CONSTANTS.ADOBE.FILL_COLOR).setValue(CONSTANTS.DEFAULTS.COLOR);
Utils.applyTimingToLayer(newLayer, comp, storedSelection);
Utils.moveLayerAboveSelection(newLayer, comp, storedSelection);
} catch (e) {
alert("Error creating solid: " + e.toString());
}
app.endUndoGroup();
};
// テキストボタンのクリックイベント
textBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) return;
app.beginUndoGroup("Create Text");
try {
LayerFactory.createText(comp);
} catch (e) {
alert("Error creating text: " + e.toString());
}
app.endUndoGroup();
};
// カメラボタンのクリックイベント
cameraBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) return;
app.beginUndoGroup("Create Camera");
var withNull = settings.current.cameraMode === 'cameraWithNull';
LayerFactory.createCamera(comp, withNull);
app.endUndoGroup();
};
// ライトボタンのクリックイベント
lightBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) return;
app.beginUndoGroup("Create Light");
try {
LayerFactory.createLight(comp);
} catch (e) {
alert("Error creating light: " + e.toString());
}
app.endUndoGroup();
};
// 設定ボタンのクリックイベント
settingBtn.onClick = function () {
createSettingsDialog();
};
// Parent to Selected チェックボックスのイベント
parentCheckbox.onClick = function () {
settings.current.parentToSelected = this.value;
settings.save();
};
// パネルのサイズを初期化
if (panel instanceof Window) {
panel.size = [320, 40]; // 初期サイズ
panel.center();
panel.show();
} else {
panel.layout.layout(true);
}
return panel;
}
// パネルの初期化と表示
var panel = buildUI(thisObj);
})(this);
リンクをクリップボードにコピー
コピー完了
このフォーラム、膨大なコードを表示するのに向いてないみたいなので確認できていないのですが
おそらくは辞書のキーにNULLを使っているのが問題なのかなと思います