なでしこを明後日の方向に

日本語プログラム言語なでしこを応援しています

なでしこのツリー部品を使って苦労したこと

結論:
 ノードIDを
  ファイルパス(ファイル名)にすると
   仕様変更することになる

作るもの

 ・左にツリー部品があって、右側にイメージ部品。
 ・母艦パス以下のjpgファイルをツリーに表示
 ・クリックしたらイメージにjpgを表示する

f:id:tkizzz:20210114222033j:plain
ツリー部品
これはかんたんそう!
…だと思ったら、罠がありました!

ツリーのアイテム(ノード)を作る

ツリーのアイテムは「ツリーノード一括作成」の命令で作ります。

ツリーノード一括作成の書式は
 「親識別名,ノード識別名,テキスト,画像番号,選択画像番号」で指定
となっています。

//ツリーノード一括作成のサンプルより
テストとはツリー
これ「,Taiju,大樹
Taiju,Yadori_1,宿木
Taiju,Yadori_2,寄生木
Yadori_1,Tsuru_1,蔓
Yadori_2,Tsuru_2,ツル」ツリーノード一括作成。

画像番号と選択画像番号はひとまず省略するとして……
「親識別名,ノード識別名,テキスト」の部分に
「どのIDにぶら下げる,こいつのIDは何,表示するテキスト」を設定します。
ツリーのトップにぶら下げる場合は親識別名は空欄でOKです。

識別名という言葉が使われていますが、
■ツリーのメンバや引数を見ると「ノードID」という言葉が多く見られます。
識別名とIDは同じ意味です。(まずここで苦労)

 親識別名→親ノードID
 ノード識別名→ノードID
以後「識別名」という言葉は出てこなくなります。

ツリーノードIDの掟!

 掟1. 親ノードを先に作る
 掟2. 同じノードIDはわりふれない
 掟3. ノードIDは全角英数が半角英数になる

忘れるとエラーになったり、罠にかかったりします。
掟1,掟2を破ると以下のようなエラーメッセージが表示されます

  (ID)の親(親ID)が見当たりません。
  (ID)は既に使われているノードIDです

掟3は何かというと、意図しないノードIDになる可能性です。

//全角→半角の例
テストとはツリー
これ「,@,test」ツリーノード一括作成。 //ノードIDは全角の@
テストアイテム言う //→ ,@,"test",-1,-1  ノードIDが半角の@になった

これから作るのはファイル名のツリーです。
ファイルパスは重複しないのでノードIDとして使うには都合がよさそうです。
しかしファイル名は全角・半角が混在しますから……どうしよう?

全ファイル列挙したものをツリーにぶらさげるには

「全ファイル列挙で返ってくる値」を「ツリーノード一括作成」に沿った書式になおします。
必須項目は「親ノードID,ノードID,テキスト」です。
ノードIDとして割り振る文字列はファイルパスにしたいです(願望)

仮に全ファイル列挙で返ってくる値を
{母艦パス}ピクチャ\画像1.jpg
{母艦パス}ピクチャ\画像2.jpg

として、これをもとにノードを作ります。

//仮組み
左ツリーとはツリー
nodeとは変数
node追加済とはハッシュ

「{母艦パス}ピクチャ\画像1.jpg
{母艦パス}ピクチャ\画像2.jpg」反復
  file=対象母艦パス置換
  親ノードID=空
  file「\」区切って反復 //掟1.親ノードを先に作る
    子ノードID「{親ノードID}\{対象}」 //ファイルパスをノードIDにする
    もしnode追加済子ノードID==0ならば //掟2.同じノードを作るとエラーになるため
      node追加済子ノードID=1
      node「{親ノードID},{子ノードID},{対象}」一行追加
    
    親ノードID=子ノードID
  
node表示
左ツリーnodeツリーノード一括作成

変数「node」に要素を一行追加して最後に「nodeをツリーノード一括作成」します。
変数nodeの中身はこうなります。

//nodeの中身
,\ピクチャ,ピクチャ
\ピクチャ,\ピクチャ\画像1.jpg,画像1.jpg
\ピクチャ,\ピクチャ\画像2.jpg,画像2.jpg

途中で「もしnode追加済@子ノードID==0ならば」をしなかった場合、
同名の親ノード(\ピクチャ)が作成されてエラーになります。

掟1「親ノードを先に作る」と掟2「同じノードIDはわりふれない」に対応しました。
しかし、これでは掟3「ノードIDは全角英数が半角英数になる」に対応していないのです。

たとえばフォルダに
{母艦パス}ピクチャ\画像1.jpg
{母艦パス}ピクチャ\画像1.jpg
のように、半角の「1」を使ったファイルと全角の「1」を使ったファイルは共存できます。
ファイル列挙でこのような事例がないとは言い切れません。

掟3「ノードIDは全角英数が半角英数になる」によって両方半角の「1」にされると
掟2「同じノードIDはわりふれない」によってエラーが発生します

f:id:tkizzz:20210114224453j:plain

最初に「ノードIDに割り振るのはファイルパスにしたいです(願望)」と書きましたが、無理です

BASE64エンコードで解決してみた

全角が半角にされるなら、最初から半角だけでノードIDを割り振ればよいのです。
たとえば、ノードIDは全部BASE64エンコードしたものに置き換えます。

//ノードIDをBASE64エンコードするサンプル
左ツリーとはツリー
nodeとは変数
node追加済とはハッシュ

「{母艦パス}ピクチャ\画像1.jpg
{母艦パス}ピクチャ\画像1.jpg」反復
  ファイル名=対象母艦パス置換
  親ノードID=空
  ファイル名「\」区切って反復 //親から順につくる
    子ノードID「{親ノードID}\{対象}」//ファイルパスをノードIDにする
    
    もしnode追加済子ノードID==0ならば //同じノードを作るとエラーになるため
      node追加済子ノードID=1
      親64=親ノードIDBASE64エンコード //←変更部分
      子64=子ノードIDBASE64エンコード //←変更部分
      node「{親64},{子64},{対象}」一行追加 //←変更部分
    
    親ノードID=子ノードID
  
node表示
左ツリーnodeツリーノード一括作成

変数nodeの中身はこうなります。

//nodeの中身
,XINzg06DYIOD,ピクチャ
XINzg06DYIOD,XINzg06DYIODXInmkZwxLmpwZw==,画像1.jpg
XINzg06DYIOD,XINzg06DYIODXInmkZyCUC5qcGc=,画像1.jpg

これでファイル名の全角・半角に対応できました。 ちなみにBASE64デコードするとこんな感じ。

「XINzg06DYIOD」BASE64デコードして表示 //→\ピクチャ
「XINUg5ODdoOLXInmkZwxLmpwZw==」BASE64デコードして表示 //→\ピクチャ\画像1.jpg
「XINUg5ODdoOLXInmkZyCUC5qcGc=」BASE64デコードして表示 //→\ピクチャ\画像1.jpg

ちなみにファイル名がSJISにない、ユニコードの文字のファイル名(💛.txtなど)を使われると
ファイル列挙の時点で文字化けするので、別件で無理です。

難しいところはおわった

あとはイベントの設定です。

ツリーのイベントを設定する

「クリックした時」だとツリーアイテムのないエリアでも反応するので使いにくいかもしれません。
「変更した時」や「ダブルクリックした時」は使いやすいと思います。

リストの場合は値を-1にすると未選択状態になるのですが、
ツリーはなにかあるとすぐ一番上を選択した状態になります。
これにより意図しない動作になることがあるので注意します。

ツリーから取り出せる値

選択パス …テキストを「\」でつなげたもの
選択ID  …ファイルパスとファイル名をBASE64エンコードしたもの

アイテム、テキスト、値も取り出せますが
「選択パス」はツリー上部からの情報が返ってくるため使いやすいです。
「選択ID」は今回ファイルパスをBASE64エンコードしたものを使ったので、デコードすれば使えます。


完成図

自分用CGビューア。
クリックで選択後、マウスホイールでCG差分。
ツリーの表示順を五十音でない順序にしたり、差分の1枚目だけをツリーに表示するなど、自分好みのカスタマイズができる。 f:id:tkizzz:20210114234100j:plain