FileMakerでサムネイル画像のタイルビューを作った その4

FileMakerで作るサムネイルタイルビューのテストを続けています。今回はテーブル構成つまり仕組みそのものの変更と、実用に向けて javascript の改編に勤しみます。

前回 は一夜漬けでjavascriptを使用し、WebビューアとFileMakerとの相互のやりとりを実現できて喜んでいたわけですが、今回はもうちょっと掘り下げ、苦しみました。

矢印キーでのアイテム移動、修飾キー+クリックによる「選択」の実現、それからサムネサイズの変更などを実現しています。

ご興味のある方はファイルをダウンロードしてみてください。

Download
tileView4.fmp12.zip

面倒を省くため最初からいくつか画像が入っていますが、全部消したりフォルダを指定してインポートもできます。

javascript素人が半泣きで行った試行錯誤と闘いの跡が整理し切れていないまま残っていたりもしますが、ご容赦のほどお願いします。一旦アップしてから人知れずこっそり更新したりするかもしれません。誤りを発見されましたら是非ご連絡を。

追加・変更したことがら

今回のファイルで実現できたことをまず挙げておきます。

矢印キーで移動

キーボードの矢印キーで移動できます。

キーボードの左右のキーで前・次への移動ができます。それだけじゃなく、上下キーでは上下への移動ができます。

シフトキー、コマンドキーで選択

これまでは選択する行為をselectボタンのオンオフで代用していました。あんなの選択行為とは言えません。シフトキー+クリックで連続選択、コマンドキー+クリックで個別に選択と解除、これを実現しました。

 

シフトクリックとコマンドクリックを組み合わせて自由に選択できます。これが真の選択、シン・選択というものではなかろうか。

この操作は、Webビューアでもリスト表示でも同じように機能します。通常リスト表示ではこのような感じ。

矢印キーで移動するのはこれまでも使ってきましたが、修飾キーと組み合わせて選択を作ることはやったことありませんでした。なんか新鮮。

フラグとキーワード

選択行為に革命が起きたので、選択したアイテムに対して何か操作をするスクリプトを作っておきたいですよね。まとめてキーワードを付けたり、フラグのオンオフができるようにしてみました。

選択しているすべてにキーワードを割り当て
選択しているすべてにキーワードを割り当て。

フラグは旗ボタンで一気に変更できます。

選択状態ならフラグはまとめてセットできる
選択状態ならフラグはまとめてセットできる

INFOパネル オンオフでmargin調整

本番メディア管理と言えば右側ペインの Info パネルがお馴染みです(お馴染みか?)こちらタイルビューテストでも同じようなものを置いていますが、オンオフに合わせてブラウザの領域が変化するという、margin-right を弄くっているだけなんですけど、こうしたちょっとしたことが実現できるのが嬉しいですね。

サムネイルのサイズ

サムネイルのサイズを変更できるようにしました。リアルタイム風にすぐに反応させる方法が判らなかったんですが、input range の onChange を onInput に変えてやればいいのですね。

ちょっぴり挙動が怪しいが、できないより出来たほうが良い。

そんなわけで、できたこと以上に、どうやってそれを実現したか、そのためにどうしたかってのが盛りだくさんです。

まずテーブルの構成を見直しリレーションを作り直しました。 同時に、理屈と物事の有り様を認識し、スクリプトと JavaScript を見直しました。

テーブル構成

tilePref というテーブルを新設し、タイルビューに必須なフィールドをまとめました。

tileViewテーブル にメインテーブルがいくつかぶら下がっています。それぞれ、リレーションの照合の仕方で役割が決まります。

tileView relationship
tileView とのリレーション

テーブルの役割

mainData

mainData テーブルはメディア管理本体で、リストビューとして使っています。

tilePref::GLB_id と mainData のリレーション
tilePref::GLB_id と mainData

ご覧の通り、照合が X で、常に全部表示できるという設定のリレーションです。このテーブルのレイアウトでは普通のFileMaker操作を行います。

リストビューレイアウト
リストビュー(mainData)レイアウト

mainData_rn

mainData_rn テーブルは、tilePref::GLB_rn と RN が1対1で繋がったリレーションで、tilePref::GLB_rn に値が入ると同時にレコードが一個繋がります。

tilePref::GLB_rn と mainData_rn リレーション
tilePref::GLB_rn と mainData_rn

タイルビューにポータルで配置し、Webビューア内でのクリックに素早く応えてポータル内にレコード情報を表示するのに使ってます。

mainDataFound

mainDataFound テーブルは mainData の対象レコードを常に同期しておくためのテーブルです。

tilePref::GLB_RN_0(値が0固定の照合用) と mainData_rn
tilePref::GLB_RN_0(値が0固定の照合用) と mainData_rn

タイルビューレイアウトではGLB_RN_0 という 0 固定のフィールドと「<」で繋がりますから、つまり、対象レコードだけがリレーションされます。「前へ」「次へ」が成立するのはそのためです。

mainData とは常に対象レコードを同期しておきます。mainData レイアウトで何か作業をしたら必ず found と同期する処理を忘れずに付け加えます。

mainData と敢えて別テーブルにしている理由は、mainDataテーブルの独立性を確保するためです。いずれホンモノのメディア管理と繋げるとき、重厚な mainData のレイアウトと直接繋がず一旦 found を介することで、スクリプトがややこしいことになるのを防ぎます。

タイルビューレイアウトのテーブルは?

タイルビューレイアウトのテーブルについて言及していませんが、テーブルは何でもいいです。

グローバルフィールドである GLB_id GLB_rn、それに繋がる mainData_rn と mainDataFound のポータルが配置できればいいので、テーブル関係ありません。でも、いろいろテストした結果、tilePref をベーステーブルに置きました。mainData上に置くより何かと処理が軽かったんです。

タイルビューに使用するリレーション
グローバルフィールドとそれに関係するポータルを配置できれば成立
タイルビューレイアウト
タイルビューレイアウト。図のものが配置出来さえすればテーブルは何でも良い

以上、テーブル構成とその役割でした。途中、GLB_rn だの意味不明の言葉が出てきました。次にそれについてです。

tilePref、GLB_id と GLB_rn

tilePref には重要なグローバルフィールドがあって、それはGLB_idGLB_rn です。

GLB_id と GLB_rn
rn が m に見えてとても厭なのですが我慢します。

このフィールドにIDとRNを入れることで、それぞれのリレーションが成立します。

RNフィールド

ID はmainDataのIDのことですが、RNというのは今回から最重要フィールドとして活躍するレコード番号のフィールドです。

レコード番号と言っても自動計算で作らず、スクリプトで明示的に振り直します。Webビューアの内容は対象レコードをHTML化しますから、HTML内の要素と対象レコードは等しく対になります。

HTML から FileMaker に特定レコードを伝達するには RN が最適です。検索不要でレコード移動、ポータル行移動、共にRNを指定すれば瞬時に特定レコードが表示できます。対象レコードも崩れません。

Webビューア内で要素を特定するには、RNよりIDが最適かと思います。手間を掛けずとも getElementById() で一発特定できます。

その理屈

タイルビューのあるFileMakerファイルを作るに当たって、作り手が混乱しないよう、どういう理屈で仕組みを作ってくかを頭に入れておきます。注意事項的な話です。

Webビューアの内と外

Webビューアがフォーカスされた状態かどうかが重要です。フォーカスされていればそれはHTML世界であり、javascript で制御します。そうでなければそれはFileMaker世界であり、スクリプトで制御します。

webビューア領域
Webビューア領域

WebビューアからFileMaker.PerformScriptWithOption でFileMakerに指令を出しますが、このとき指令されたスクリプトに「オブジェクトに移動」「フィールドに移動」「確定」「再表示」みたいなのが含まれていればどうなるでしょう。Webビューアからフォーカスが外れます。

外れても構わないスクリプトなら良いですが、次のような事例がありました。

  1. javascript M … 矢印キーで要素を移動してアクティブの処理
  2. javascript A … アクティブの処理後、FileMaker にRNを伝える
  3. スクリプトA … RNが伝えられたらポータルに移動して該当レコード番号に行移動し、確定し再表示

Webビューアからフォーカスが外れてキー入力の連続性が失われます。

こういうことがあったのでテーブル構成を見直し mainData_rn テーブルを追加しました。「RNを伝える」つまりGLB_rn にRN を入力した時点で mainData_rn には該当レコードが表示されますから、スクリプトで何もする必要がありません。

何にせよ、Webビューアからの指令スクリプト内にフォーカスを外す処理を含めることに気を留めます。

JavaScript とスクリプトで同じ目的の処理

矢印キーによる移動についてですが、javascript にもスクリプトにも、両方に同じ目的を達成する処理を作っておく必要があります。フォーカスあるなしで矢印キー押したときの挙動が異なると不快ですから。

FileMaker のスクリプト

FileMaker でスクリプトを作ってあれこれやるわけですが、それがどういうタイプのスクリプトかによって書き方のテンプレ的なものが変わってきます。

どこから開始されたか

どこから発動されてどこに対して操作を行うのかに応じて、条件で分岐するなりスクリプトそのものを分けたりします。

  • リストビューから発動する
  • タイルビューのポータルから発動する
  • Webビューアから送られた情報を元に発動する

現在リストビューにいてレコードに対して何か操作を行うとすると、指定するフィールドは mainData::フィールド名 になるし、RN で移動をするのはレコードです。

タイルビューにいる場合は、指定するフィールドは mainDataFound::フィールド名または mainData_rn::フィールド名です。RN で移動するのは指定ポータルの行です。

Webビューアから引数付きで命じられることのほとんどはタイルビュー内の出来事になります。

作業終了時にどうするか

操作完了後の後始末として何を行うかってのもいろいろ状況によって変わります。

  • RN を振り直す
  • mainData と found を同期する
  • HTML を再生成する
  • レイアウトを切り替えて戻る
  • Webビューア に指令を出す

検索、フィルタ、ソートなど対象レコードが変更される操作を行った後は必ず RN を振り直し、mainData と found を同期させます。この操作の開始レイアウトがタイルビューだったら、found からHTMLを再生成し、元のレイアウトに戻ります。

タイルビューレイアウトから例えばフラグをオンにするといった操作をした場合は、その操作のあと、Webビューア内でフラグをオンにするJavaScript指令を発します。

Webビューア内でフラグをオンにしたとすれば、そのことがWebビューアからFileMaker.performScript で送られるので、ポータル内でフラグをオンにする動作を行い、そこで終わります。

スクリプトの書き方を工夫

このように、状況によって指定フィールドが変わったり完了後の後始末が変わったりします。

これらを全部条件分岐してスクリプトを書いていったら結構な分量になります。可能なら、コア部分のスクリプトと、スクリプトを実行させるスクリプトを分けて作って組み合わせるか、スクリプト引数にスクリプト名を指定して動かせるようにしておきます。

まだちょっと整理しきれていないこともあって、現状ファイルのスクリプトワークスペース内はやや混沌としています。混沌としたからこそ、こういうことに考えが及んだとも言えます。

JavaScript のアクティブとセレクト

Active

JavaScriptの「アクティブ」処理は、次の通りです。

  1. class=”active” が付いた要素から”active”を取り除く
  2. 現在の要素に class=”active” を追加する
  3. 現在 class =”select”がもしあればすべて取り除く
  4. 現在 selectList(選択された要素の配列)がもしあれば初期化する
  5. 要素のid(idxxx)から、レコードのid(xxx)、rn(yyy)などを取得しグローバル変数に保存
  6. ビューア内に要素が表示されていなければスクロールして表示する
  7. FileMaker に rn を送る

修飾キー+クリックによる「選択」機能を追加したため、それに関する処理も含まれています。

これらを一つの javascript でやっていましたが関数を分割しました。パーツ化して必要に応じた使い方をします。

Select

前回まではチェックボックスで偽物のセレクトモドキを作っていましたが、今回は純粋なシン・選択なので別のアプローチを取ります。

複数の選択がなされると selectList というグローバル変数に記録します。

複数の選択、即ち「修飾キーを押し下げながらのクリック」が成された時に selectList に記録します(修飾キーなしの普通のクリックでは selectList を初期化します)

ということで修飾キーの押し下げと離脱にそれぞれ処理を割り当てます。

押し下げたときにやることは、前回のActiveを別名で変数に複製し、押し下げクリックされた要素を新たにActive保存します。これにより二つの要素が変数にセットされます。あとはシフトかコマンドかで処理を分け、selectList に記録します。

修飾キーから離れたときにやることは、FileMaker に selectList を伝達します。

受け取ったFileMaker側では、配列をリストに変換してループ、必要な処理をします。select なら selectフィールドに 1 を入れます。FileMaker側での Select 処理は前回までと同じで select フィールドを使います。

 

JavaScript の「クリック」

JavaScript では onclick をHTMLの中に混ぜ込んでいます。onclick(this)でクリックした要素の情報を簡単に取得するためです。

ダブルクリックの処理はクリックの中で分岐させます。同じ要素の中に onclick と ondblclick を両方を仕込むわけにはいきませんから。

setTimeout を使ってダブルクリックとクリックを振り分け、クリックの中では修飾キーが押されているかどうかでさらに分岐します。

  1. ダブルクリックなら
    • ダブルクリックの処理(FMでシングルを開く)
  2. クリックなら
    1. シフト+クリックなら
      • シフトクリックの処理(連続選択のリスト作成)
    2. コマンド+クリックなら
      • コマンドクリックの処理(選択のリスト作成)
    3. 普通のクリックなら
      • 普通のクリックの処理(アクティブ処理)
      • ダブルクリック判定(setTimeout を使った仕掛け)

onclick(this) で発動する JavaScript は以上のような動きです。この関数の名前が前回から引き続き「jsActive」というんですが、すでにその名前が相応しくありません。クリック総合案内みたいな動きになったので、余裕があれば名前を変えておきたいものです💦

ということで、理屈というか注意事項というか概要みたいにざっくり書きましたが具体性に欠ける判りにくい内容だったかも。具体的に書き出すと分量が多すぎるのでどうしよっかなあ。

リファレンス

具体的に今回追加した機能の説明ですが、やはり細かくはいきません。ざっくりだけ。もしかしたら将来、別の投稿で詳細を書くことがあればここから以下を書き換えたりすることもあり得ます。

矢印キーで移動

FileMaker

矢印キーでのレコード移動は、自分にとってFileMaker書類の定番です。OnLayoutKeystroke にスクリプトを仕込みます。

スクリプトは、まずどのフィールドにもいないことを条件に分岐してから、if でキーを指定していきます。

if ( Code ( Get ( トリガキー入力 ) ) = 28 )

こんな感じで、この後移動スクリプトなり何なりを実行させます(28番は left arrow)

よく使うキーコードはこんな感じです。

backspace 8
shift-tab 9
enter 10
return 13
escape 27
left arrow 28
up arrow 29
right arrow 30
down arrow 31
space 32
delete 127

矢印キーの移動は今回複雑です。というのも、タイルビューで、Webビューアがアクティブじゃない状態のときにWebビューアに対してアイテム移動を指示するという仕事もあります。

webビューアに矢印移動を指令

どうやるかというと、ビューアに移動先IDを伝えます。左右移動ならいいんですが上下移動をするには、その前にビューア内の1行にタイルが何個並んでいるかを知らねばなりません。コラム数と呼んでいますが、これが常に正しく数値を保持していなければ何も計算できません。それにはJavaScriptの仕込みが必要です。

javascript

JavaScript でキーによる移動は EventListener を使えばいいですね。

document.addEventListener('keydown', function (event) {
  key = event.key;
  i = // 基準となる番号(現在の i )
  if( key == 'ArroeLeft'){
    i = i - 1
  }
  else if( key = 'ArrowRight'){
    i = i + 1
  }
}

こんな感じで、移動先のインデックス番号を計算して作ります。

左右矢印の「前へ」「次へ」はいいんですが、上下移動ができるのが今回の売りです。上下に移動させようと思ったら、行の中にタイルが何個あるか数えなければなりません。その数を足したり引いたりします。

getColumn

そこで、行内のタイル個数を調べる関数を作って、開始時やウインドウサイズチェンジ時に発動させます。

具体例は示しませんが、表示直後に原始的な方法でコラム数を数えますね。まあ、何にせよこの数をグローバル変数に入れておいて、FileMaker にも伝えておきます。

修飾キー+クリック

FileMaker では keydown と keyup を区別できません。なのでクリック時に修飾キーが押されているかどうかをまず見て判断します。

クリックされたら発動するスクリプトに

if ( Get ( アクティブ修飾キー ) > 0 )

と、このように分岐してから、キーに応じた処理に向かいます。ここからさらにどのキーか分岐させてもいいし、最初から個別に if っても良いと思います。

if ( Get ( アクティブ修飾キー ) = 1 ) // シフトキー
if ( Get ( アクティブ修飾キー ) = 16 ) // コマンドキー

JavaScript では keydown と keyup を感知できますから、それぞれに機能を割り当てます。

キーを押したら選択を作り、キーを離したらそれを FileMaker に送信します。

フラグとキーワード

前回、セレクトをチェックボックスにしていましたが、そっくりそのまま、その仕組みをフラグ機能としました。

FIleMaker 側では、フラグは flag フィールドに 1 があるかないかです。HTML側では、input checkbox が true か false かです。フラグの input には fcxx(xxはidと同じ数字) というIDを割り当て、それを包む div には fxx というIDを割り当ててます。これらIDによって、getElementById が使えてターゲットが一発特定できます。

今回特別なのは、select状態ではまとめてセットできるという仕組みを作ったことです。これは JavaScript にもスクリプトにも両方に記述されています。

複数選択してどれかの旗をクリックすると、クリックした旗の状態の逆を、セットする値として保存します。

それから、selectLst(選択中のid rn の配列・リスト)があればそれらをループで、なければクリックした本人だけを処理します。この仕組みはスクリプトもJavaScriptも同じです。どっちもに同じような処理を作ってるって事だけが重要です。

キーワードに関しては、Webビューアにキーワードを表示することを現在やっていないのでFileMakerだけの処理です。理屈は旗と同じです。

可変のサムネイルサイズ

サムネイルサイズをスライダで変更できる仕組みは、サムネサイズのフィールドを設けて専用のWebビューアと組み合わせます。input の range に onInput  を加えます。

onChange ではスライダーを動かし終わったときに変更されますが、onInput ではインプット時点で変更されます。onInput が正義というほどではなくて、ちょっと挙動が変なところもあります。より理想的なスライダをどう作れば良いのか現時点では判っていませんのでこれで妥協。

ここでは詳しく説明しませんが、ダウンロードしたファイルのここで確認できるかと。

sliderのHTML
スライダのHTML

テンプレート

HTML化のテンプレートをいくつか変更、シングルの content を以下のようにしています。

<article id="id<!-- ID -->" data-rn="<!-- RN -->"class="content <!-- active --> <!-- select --> <!-- flag -->" onclick= "jsActive( this.id );">
 <div id="thumb<!-- ID -->" class="thumbnail" title="<!-- toolTip -->">
  <!-- imgTag -->
 </div>
 <div class="data-area">
  <div class="title"><!-- fileName --></div>
  <div id="f<!-- ID -->" class="flag-area" onClick="jsFlag( event,this );">
  <input id="fc<!-- ID -->" type="checkbox" class="flagbox" <!-- flagChecked --> />
  <label for="fc<!-- ID -->">⚑</label>
 </div>
</div>
</article>
  • article に data-rn 属性を作って RN を記載
  • 前回までの、select checkbox を廃止
  • 変わりに flag を新設
  • title 属性にちょっとした情報を表示
tooltip
title 属性を利用したツールチップみたいなこれ

他のテンプレートも少々変更があります。

レイアウト名の登録

テストファイルなこともあって、テーブルを変えただけの同じレイアウトを試したり、レイアウト名を変更したり、そういうことを頻繁にやらかします。その都度、スクリプト内に書いたレイアウト名を書き直すのがウザくて、レイアウト名を登録制にしました。

まずグローバルフィールドにレイアウト名を登録、それをグローバル変数にします。スクリプト内でレイアウト名を直書きする箇所はすべてグローバル変数にします。

例えば メインテーブルのリストビューなら $$_main というふうに。スクリプト内では
if(get(レイアウト名) =  $$_mein ; 〜〜 )
という書きかたになります。レイアウト名が変わってもスクリプトの変更は不要です。

この処置、ちょっと気に入ったんで他のファイルでも今後使っていこうかな。

レイアウト名の登録
レイアウト名の登録

head内のscript

WebビューアでJavaScript のsmart scroll 指定が効かないので plyfill  を、もう一つ、遅延読み込みの lazysizes を<head>内に入れました。

 polyfill: https://github.com/iamdustan/smoothscroll

lazysizes: https://github.com/aFarkas/lazysizes

css root

css の最初に :root { } で変数を登録しておけば共通の値として使い回せます。この root を書くためのフィールドを新設し、テンプレで css と合体させることにしました。

root には主に色が登録されるので、ここを入れ替えることでWebビューア内の配色を一気に変更できるようになります。

テキストファイルを保存

テンプレート、スタイル、スクリプト、計算されたHTML、それらをファイルとして書き出せるスクリプトを作っておきました。

FileMaker のWebビューアにはインスペクタがないので、素人がJavaScriptを書いてもエラー表示すら確認できずデバッグができません。お手上げなのでブラウザで表示するために書き出します。index.html を開くとインスペクタも拝見できます。

ただし、FileMaker.PerformScript があるのでブラウザでこれがエラーになります。他にも単にテキストに書き出しただけではエラーになる記述が多く含まれます。

そこで、いろいろ調整してから書き出すようにしました。

例えば HTML内にすべてべた書きされた css と script を分離します。FileMaker.PerformScript の部分はコメントアウトします。style.css は root 部分をちゃんとくっ付けます。サムネワイドは仕方なしに固定の値で置き換えます。

いい感じに工夫されたファイルが書き出されるので、index.html をブラウザで開き、css や js はエディタで開き、点検を行います。

いやまじでこの仕組みを作ってないと完全にお手上げでした。

 


Penguin icon 前回までのあらすじ

FileMakerでサムネイル画像のタイルビューを作った その1

FileMakerでサムネイル画像のタイルビューを作った その2

FileMakerでサムネイル画像のタイルビューを作った その3

 


別件のメディア管理を育てるシリーズはこちら。いずれタイルビューと合体できるか。

 FMメディア管理INDEXFileMakerで作るメディア管理 INDEX

 

 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください