ポップアップメニューを「n*十件ずつ」のサブメニューに自動分割する

提供: MeryWiki
移動先: 案内検索

分割サブメニュー[編集]

ポップアップメニューのアイテム数が非常に多いとき、メニューの上下に配置されたスピンハンドル ▲ ▼ でスクロールさせるのは効率が悪いので、「1 - 20 件目 ▸」「21 - 40 件目 ▸」のかたちでサブメニュー項目に分割して表示するためのサンプルコードです。

アイテム数にあわせて「1 - 20 件目 ▸」「21 - 40 件目 ▸」の分割サブメニュー項目を自動的に生成しますので、ポップアップメニューのアイテム数が増減する「検索」「抽出」系のマクロに仕込むと便利かも。


  • 「1 - 20 件目 ▸」「21 - 40 件目 ▸」の項目は、サブメニュー項目「20 件ずつ表示 ▸」配下の "孫" メニュー項目にすることもでき、分割サブメニュー内の行数「20 件目」の部分は設定項目で任意の数値に変更できます(10 件ごとにセパレータを挿入するので、10 の倍数を推奨)。
  • 「分割サブメニュー」を使用するかしないかは設定項目で選択できますが、「使用する」に設定した場合でもアイテム数が「分割する単位」の 2 倍以下のときは「分割サブメニュー」で表示せず、メインメニューの直下にアイテムを列挙します。


ポップアップメニューに列挙するアイテムは
PopupMenu.Add( "文字列", id [, flags ] ) メソッドの記法にあわせて

[ ["アイテム1",id_1], ["アイテム2",id_2], ["アイテム3",id_3], ["アイテム4",id_4] ... ] 

の形式の配列で用意してください。 コードを改造できるなら、任意でメニューの定数 flags や、アイテムを選択したときの個別動作 function() などを配列に入れてもよいでしょう。
※ サンプルコードでは、for() 文のループ処理で Array[i][0] と Array[i]1] をポップアップメニュー項目 Add( "アイテム1",id_1 ) に変換しています。


サンプルコードのポップアップメニュー画像
subMenuEnable = 1

subMenuEnable = 2

組み込み関数 DevidedSubMenu()[編集]

ポップアップメニューに列挙するアイテムとして用意した配列から「分割サブメニュー」を生成する関数コードです。

  • 設定項目の変数 subMenuHeight で、分割サブメニュー内の行数の指定。
    ※ 10 の倍数で指定してください。
  • 設定項目の変数 subMenuEnable で、
    ・分割サブメニュー「1 - 20 件目 ▸」「21 - 40 件目 ▸」... の項目を表示する/表示しない
    ・メニュー項目「20 件ずつ表示 ▸」の配下に置く /メインのメニュー直下に置く
    を指定する。
0: 分割サブメニューを使用しない(全アイテムを列挙)
1: 「20 件ずつ表示 ▸」の配下に分割サブメニュー「1 - 20 件目 ▸」... を表示する(画像:左)
2: 分割サブメニュー「1 - 20 件目 ▸」「21 - 40 件目 ▸」... だけを表示する(画像:右)

※設定変数や、ポップアップメニューに列挙するアイテムの配列は、関数から参照できる階層のスコープにおいてください(または、引数として関数に渡す)。


おまけ関数コード

  • AddCancelLines() 関数: 「セパレータ」「キャンセル」行の自動挿入
  • Pad() 関数: 連番の「ケタ埋め」(右揃え)

※ケタ埋めの空白文字は EN SPACE「 」(U+2002) にしてありますが、PCのコントロールパネル設定で指定した「メニュー」用のフォント種によっては半角数字との幅が合わないことがあります。

サンプルコード[編集]

アイテム数が多い/アイテム数が変動するサンプルとして、アクティブなタブのすべての論理行をポップアップメニューに表示する【仮】マクロです。
「キャンセル」行の表示や、行番号の「ケタ埋め」するためのコードも入れているで読みづらいかも知れませんが、 /* ... */ で説明をつけた部分が「分割サブメニュー」用のコードです。

配列の中身や、menu.Track(); 以降の動作コード【仮】の部分を、任意のコードに差し替えてお使いください。


「分割サブメニュー」に表示される「キャンセル」のアイテムは Space キーをアクセラレータにしてあるので、Escape キーで多階層のメニューを繰り上がりをせずにポップアップメニューを一発キャンセルできます。
メインメニュー直下に全アイテムを列挙しているときは、Space キーで「キャンセル」アイテムをトグル移動しながらメニュー全体をスクロールできます(Escape キーでポップアップメニューをキャンセル)。


#title = "論理行をポップアップメニューにリストアップ"

// ( function() {
  var start = new Date();	// 所要時間計測(開始)


// ---------- ▼ 設定項目 ▼ ----------

/* ■「分割サブメニュー」を使用するか? */
var subMenuEnable = 1;
  // 0: 使用しない(メインメニュー直下に全アイテムを列挙する)
  // 1: メインメニュー直下には「20 件ずつ表示」と全アイテムの列挙
  //   「20 件ずつ表示」の配下に「1-20 件目」「21-40 件目」の"孫"メニューを置く
  // 2: メインメニュー直下に「1-20 件目」「21-40 件目」のサブメニューを置く

/* ■ 分割する単位 (初期値: 20  >> 20件ずつに分割) */
// ※ subMenuHeight の 2 倍より多いアイテムがあれば分割サブメニューを適用する
var subMenuHeight = 20;


// ---------- ▼ メインのコード ▼ ----------

  // ポップアップメニューに表示する「アイテム」の配列
  var itemArray = new Array();

	// 【仮】 論理行数の連番アイテム(文字列と行番号)を生成する
	var d = editor.ActiveDocument;
	var lineText, lines = d.GetLines( 0 );
	for ( var y = 1; y <= lines; y++ ) {
          // 空白文字を詰め、「&」記号を保守する
	  lineText = d.GetLine( y, 0 )
                      .replace( /\t+|[ ]{4,}|[ ]{2,}/g, " › " )
	              .replace( /&/g, "&&" ).slice( 0, 30 );
	  itemArray.push( [ ":  " + lineText, y ] );
	}

  // 「アイテム」数
  var len = itemArray.length;
  // 「アイテム数」のケタ数(※ケタ埋めに使用する)
  var lenWidth = len.toString().length;

  if ( len > 0 ) {
    // ポップアップメニューの準備
    var menu = CreatePopupMenu();


    /**
     * 「アイテム数」が「分割単位」の 2 倍より多いなら
     * 「分割サブメニュー」を生成する
     */ 
    if ( subMenuEnable && len > subMenuHeight * 2 ) {
      DevidedSubMenu();
    }


    // subMenuEnable = 1 またはアイテム数が「分割単位」の 2 倍以下なら
    // メインメニュー直下に全アイテムを列挙する
    if ( subMenuEnable < 2 || len <= subMenuHeight * 2 ) {
      for ( var i = 0; i < len; i++ ) {
        // 10 件ごとにセパレータ、20* 件ごとに「キャンセル」行を追加する
        AddCancelLines( menu, i, subMenuHeight );

        // 配列の「アイテム」をメインメニュー直下に追加する
        menu.Add( Pad( itemArray[i][1], 2 )
                + itemArray[i][0]
                , itemArray[i][1] );
      }
      // 最下行に「キャンセル」をピン止めする(Space キーでスクロール可)
      menu.Add( "", 0, meMenuSeparator );
      menu.Add( Pad() + "キャンセル\t& ", 0 );
    }


    // ステータスバーにメニューの件数と処理時間を表示する
    var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
    Status = " メニューのアイテム: " + len + " 件 "
           + "  [ " + elapsedSec + " 秒 ]";

    // ポップアップメニューを表示する
    var yy = menu.Track( mePosMouse );

	  	// 【仮】 メニューの連番の論理行にジャンプする
	  	if ( yy > 0 ) {
	  	  d.selection.SetActivePoint( mePosLogical, 1, yy );
	  	}

  }	// if( len > 0 ) 閉じ


  // ---------- ▼ 関数 ▼ ----------
  // 関数は呼び出し元のスコープに配置する
  // ※ 呼び出し元のスコープが閉じていると変数を参照できなくなる

  /**
   * 関数 DevidedSubMenu()
   * サブメニュー「※ 20* 件ずつ表示 ※  ▶」/「1 - 20* 件目  ▶」に
   * 配列の「アイテム」を分割表示する
   * ※「20*」の部分の数値は設定用変数 subMenuHeight で指定
   * 
   * ※呼び出し元のスコープが閉じている場合は
   *  ふたつの設定変数と menu , itemArray オブジェクトを引き渡すこと
   *   e.g. DevidedSubMenu( objPopupMenu, items, mode, height )
   *  len = itemArray.length と lenWith = len.toString().length は
   *  関数のなかで再定義できる
   */
  function DevidedSubMenu() {
    var subMenu = CreatePopupMenu();
    var popupMenu = ( subMenuEnable == 1 ) ? subMenu : menu;
    if ( subMenuEnable == 1 ) {
      // 「※ 20* 件ずつ表示 ※  ▶」をピン止め
      menu.AddPopup( " ※ " + subMenuHeight
                   + " 行ずつ表示(&D) ※"
                   , subMenu );
    }
    // 「キャンセル」をピン止め
    popupMenu.Add( Pad() + "キャンセル", 0 );
    popupMenu.Add( "", 0, meMenuSeparator );

    // 配列のアイテムをポップアップメニューに追加していく
    var smArray = [];	// SubMenu Array
    var smId , _from , _to;
    for ( var i = 0; i < len; i++ ) {
      if ( i % subMenuHeight == 0 ) {
        // 配列にメニュー項目「_from - _to 件目  ▶」を生成する
        smArray.push( CreatePopupMenu() );
        // 「_from - _to 件目」メニューの index
        smId = smArray.length - 1;
        //  _from と _to の値
        _from = smId * subMenuHeight + 1;
        _to = Math.min( smArray.length * subMenuHeight, len );
        // 「_from - _to 件目」メニュー項目
        popupMenu.AddPopup( Pad( _from, 1 ) + " - "
                          + Pad( _to ) + " 件目"
                          , smArray[smId] );
      }

      // 「_from - _to 件目」メニュー配下の 10件 ごとにセパレータと
      // 先頭に「キャンセル」行(Space キーでキャンセル可)
      AddCancelLines( smArray[smId], i, subMenuHeight );

      // 「アイテム」の配列から「_from - _to 件目」メニュー配下に追加する
      // ※ smArray[smId] は CreatePopupMenu オブジェクト
      smArray[smId].Add( Pad( itemArray[i][1], 2 )
                       + itemArray[i][0]
                       , itemArray[i][1] );

      // 「_from - _to 件目」のさいごに「キャセル」行を追加する
      if ( i == len - 1 ) {
        popupMenu.Add( "", 0, meMenuSeparator );
        // (Space キーでキャンセル可)
        popupMenu.Add( Pad() + "キャンセル\t& ", 0 );
      }
    }
  }

  /**
   * 関数 AddCancelLines( objPopupMenu, id, distance )
   * 10 件ごとにセパレータと
   * 20* 件ごとに「キャンセル」行を追加する
   * ※Space キーでキャンセル可(またはスクロール)
   * 
   * 第1引数は ポップアップメニューオブジェクト
   * 第2引数は 呼び出し元の for() 文の「 i 」
   * 第3引数は 「キャンセル」行を表示する間隔*
   * (省略した場合は設定項目の変数 subMenuHeight)
   */
  function AddCancelLines( objPopupMenu, id, distance ) {
    var distance = distance || subMenuHeight || 30;
    // 10 件ごとにセパレータを追加 && id % distance != 0
    if ( id % 10 == 0 ) {
      objPopupMenu.Add( "", 0, meMenuSeparator );
    }
    // 30* 件(distance)ごとに「キャンセル」行を追加
    if ( id % distance == 0 ) {
      objPopupMenu.Add( Pad() + "キャンセル\t& ", 0 );
      objPopupMenu.Add( "", 0, meMenuSeparator );
    }
  }

  /**
   * 関数 Pad( num, amp )
   * EN SPACE (U+2002)「 」でケタ埋めして右揃えにする
   * ※ケタ数にはグローバルスコープの変数 lenWidth を使用する
   * 
   * 第1引数は ケタ埋め対象の数字
   * 第2引数は アクセラレータ用の「&」の指定
   * (1 なら先頭の数字、2 なら末尾の数字をアクセラレータにする)
   */
  function Pad( num, amp ) {
    num = num || "";
    var pad = ( amp == 1 )
              ? ( "          &" + num )
              : ( amp == 2 )
                ? ( "          " + num ).replace( /(\d)$/, "&$1" )
                : ( "          " + num );
    amp = amp ? "&" : "";
    return pad.slice( - lenWidth - amp.length );
  }

// }() );
スポンサーリンク