Tutorial: カスタムサイドパネルセクション

カスタムサイドパネルセクション

カスタムサイドパネルセクションという機能を使うことで、スクリプトを作成する人は Synthesizer V Studio の画面上に常に表示しておくことができ、かつ、操作可能な独自のパネルを作ることができます。一時的なモーダルダイアログや、メッセージボックスとは異なり、サイドパネルセクションは常にアクセスができるため常設の作業スペースとして役に立ちます。

サイドパネルセクションの作成

サイドパネルセクション用のスクリプトは getClientInfo()「type」: 「SidePanelSection」 を返す必要があり、 main() の代わりに getSidePanelSectionState() 関数を実装しなければなりません。さらに、 minEditorVersion は131330(バージョン2.1.2)以上である必要があります。

基本構造

function getClientInfo() {
  return {
    "name": "My Panel",
    "category": "Utilities",
    "author": "Your Name",
    "versionNumber": 1,
    "minEditorVersion": 131330,
    "type": "SidePanelSection"
  };
}

function getSidePanelSectionState() {
  return {
    "title": "My Panel",
    "rows": [
      // UI ウィジェットをここに定義します。
    ]
  };
}

セクションオブジェクトの構造

getSidePanelSectionState() が返すオブジェクトは以下のプロパティを含みます:

  • title: string - パネル上部に表示されるタイトル
  • rows: array - ウィジェット行の配列

レイアウトシステム

サイドパネルは行と列からなる2階層のレイアウトシステムを採用しています。

  1. 第1階層 (行): rows配列にはLabelまたはContainerウィジェットのみを含めることができます
  2. 第2階層 (列): Containercolumns配列内には、TextBoxButtonSliderなどのインタラクティブウィジェットを配置できます

行は上から下に垂直に方向に積み重ねられます。各行はラベルまたはコンテナのいずれかである必要があります。

"rows": [
  // 行 1: ラベル
  {
    "type": "Label",
    "text": "Search options:"
  },
  // 行 2: ウィジェットを含むコンテナ
  {
    "type": "Container",
    "columns": [
      {
        "type": "TextBox",
        "value": searchValue,
        "width": 1.0
      }
    ]
  },
  // 行 3: もう一つのラベル
  {
    "type": "Label",
    "text": "Results:"
  }
]

列と相対幅指定

複数のウィジェットを並べて配置するには、 columns 配列を持つ Container ウィジェットを使用します。列内の各ウィジェットは、利用可能な水平方向のスペースに対する相対的な割合を表す width プロパティを指定します。

{
  "type": "Container",
  "columns": [
    {
      "type": "Button",
      "text": "Left",
      "value": leftButtonValue,
      "width": 0.3  // 幅30%
    },
    {
      "type": "Button",
      "text": "Right",
      "value": rightButtonValue,
      "width": 0.7  // 幅70%
    }
  ]
}

ウィジェットシステム

WidgetValue

サイドパネルセクションは、UIとスクリプト間で双方向にデータを伝送するためにWidgetValueオブジェクトを使用します。SV.create(「WidgetValue」)で作成します。

var myValue = SV.create("WidgetValue");
myValue.setValue(0);  // 初期化
myValue.setEnabled(true);  // ウィジェットの有効化/無効化

// 変更に応答するコールバック
myValue.setValueChangeCallback(function(newValue) {
  // 変更への対応
});

ウィジェットタイプ

TextBox

単一行のテキスト入力ウィジェットです。

var textValue = SV.create("WidgetValue");
textValue.setValue("initial text");

{
  "type": "TextBox",
  "value": textValue,
}

TextArea

複数行テキストの表示または入力を行うためのウィジェットです。

var textAreaValue = SV.create("WidgetValue");
textAreaValue.setValue("Multi-line\ntext here");

{
  "type": "TextArea",
  "value": textAreaValue,
  "height": 80,      // ピクセル単位の高さ
}

Button

クリック可能なアクションを実行するボタンです。

// 内部の値は重要ではありません。
// 主にコールバックの受信と、有効/無効状態の設定のために使用されます。
var buttonValue = SV.create("WidgetValue");
buttonValue.setValueChangeCallback(function() {
  // Button clicked
  SV.showMessageBox("Info", "Button pressed!");
});

{
  "type": "Button",
  "text": "Click Me",
  "value": buttonValue,
}

Slider

範囲と書式を設定可能な数値スライダーです。

var sliderValue = SV.create("WidgetValue");
sliderValue.setValue(3);

{
  "type": "Slider",
  "text": "Volume",
  "format": "%3.1f dB",  // printfスタイルの書式指定
  "minValue": -6,
  "maxValue": 6,
  "interval": 0.1,
  "value": sliderValue,
}

書式設定の例:

  • "%3.2f beats" - 単位付き、小数点以下2桁を表示
  • "%3.0f %%" - 整数でのパーセント表示
  • "%2.1f Hz" - 単位付き、小数点以下1桁を表示

CheckBox

真偽値を扱うチェックボックスのウィジェットです。

var checkValue = SV.create("WidgetValue");
checkValue.setValue(true);

{
  "type": "CheckBox",
  "text": "Apply this to all notes in the group",
  "value": checkValue,
}

ComboBox

ドロップダウンセクションを扱うウィジェットです。

var comboValue = SV.create("WidgetValue");
comboValue.setValue(0);  // 選択されている項目の添え字

{
  "type": "ComboBox",
  "choices": ["Option 1", "Option 2", "Option 3"],
  "value": comboValue,
}

エディタイベントへの応答

サイドパネルのセクションは、コールバックを使用してエディタの状態変化に応答できます。

選択範囲の変更

SV.getMainEditor().getSelection().registerSelectionCallback(function(selectionType, isSelected) {
  if(selectionType == "note") {
    // ノートの選択状態変更に応じて、UIを更新します。
    ...
  }
});

SV.getMainEditor().getSelection().registerClearCallback(function(selectionType) {
  if(selectionType == "notes") {
    // 全てのノートの選択が解除に応じて、UIを更新します。
    ...
  }
});

使用例

var SCRIPT_TITLE = "Selected Note Counter";

function getClientInfo() {
  return {
    "name": SCRIPT_TITLE,
    "category": "Utilities",
    "author": "Dreamtonics",
    "versionNumber": 1,
    "minEditorVersion": 131330,
    "type": "SidePanelSection"
  };
}

var countValue = SV.create("WidgetValue");
var setLyricsButtonValue = SV.create("WidgetValue");

countValue.setValue("0 notes selected");
countValue.setEnabled(false);

function updateSelectionCount() {
  var selection = SV.getMainEditor().getSelection();
  var selectedNotes = selection.getSelectedNotes();
  var count = selectedNotes.length;

  countValue.setValue(count + " note" + (count === 1 ? "" : "s") + " selected");
  setLyricsButtonValue.setEnabled(count > 0);
}

setLyricsButtonValue.setValueChangeCallback(function() {
  var selection = SV.getMainEditor().getSelection();
  var selectedNotes = selection.getSelectedNotes();

  if(selectedNotes.length === 0) return;

  SV.getProject().newUndoRecord();

  for(var i = 0; i < selectedNotes.length; i ++) {
    selectedNotes[i].setLyrics("a");
  }
});

// 選択コールバックを登録して、カウントを自動的に更新する
SV.getMainEditor().getSelection().registerSelectionCallback(function(selectionType, isSelected) {
  if(selectionType == "note") {
    updateSelectionCount();
  }
});

SV.getMainEditor().getSelection().registerClearCallback(function(selectionType) {
  if(selectionType == "notes") {
    updateSelectionCount();
  }
});

// 初期化
updateSelectionCount();

function getSidePanelSectionState() {
  return {
    "title": SCRIPT_TITLE,
    "rows": [
      {
        "type": "Label",
        "text": "Selection:"
      },
      {
        "type": "Container",
        "columns": [
          {
            "type": "TextBox",
            "value": countValue,
            "width": 1.0
          }
        ]
      },
      {
        "type": "Container",
        "columns": [
          {
            "type": "Button",
            "text": "Set lyrics to 'a'",
            "value": setLyricsButtonValue,
            "width": 1.0
          }
        ]
      }
    ]
  };
}