Spec2入門(その3)

これはSmalltalk Advent Calendar 2019の記事です。

Spec2は、Pharo Smalltalk で採用されているUIフレームワークであるSpec の新しいバージョンです。Pharo 7.0 では Spec が使われていましたが、Pharo 8 では(ほぼ?)全てのUIが Spec2 で書き直されるようです。この記事では Spec2 を使った UI の実装方法について簡単に紹介します。

Squeak/Pharo Smalltalk では、UIの実現のために長らく Morphic を用いてきました。Morphic はシンプルで強力なフレームワークですが、複数のコンポーネントを組み合わせていくとアナーキーな実装をしがちで、リファクタリングや再利用の際に泣きたくなるような状況に陥りがちです。Spec/Spec2 はUIの構築に秩序をもたらし、コンポーネントの再利用がしやすく設計されています。

前回の記事ではボタンへのアイコン追加やウィンドウタイトルの設定方法について紹介しました。今回からはもう少し実用的なUI構築に向けて舵を切り直します。ただし、例によって内容は小出しにしていきます。


今回からファイル選択ダイアログを作っていこうと思います。とりあげた理由は、オレオレプロジェクトで取り組んでいるScratch 1.4風アプリに必要だからです。ちなみにScratch 1.4のファイル選択ダイアログは以下のようなものです。

左側にショートカットボタン、中央にファイル一覧があり、右側にファイルの概要が表示されるというものです。そっくり同じというわけでなくとも、Spec2を使って似たようなものを作ろうと思います。

メイン部のウィジェットを作る

中央に表示されるファイルファイル一覧のウィジェットから手を付けることにします。まずはファイルの一覧が表示されるようにしましょう。メイン部のクラス SpecFileListSample を登録します。

SpPresenter subclass: #SpecFileListSample
     instanceVariableNames: 'directory listEntries '
     classVariableNames: ''
     package: 'SpecSample'

directory は表示するディレクトリ(FileReferenceオブジェクト)を保持します。listEntries はファイルの一覧(Arrayオブジェクト)を保持します。

次に、initialize メソッドで directoryを初期化します。とりあえずPharoイメージのあるディレクトリにしておきます。

initialize
     directory := FileSystem workingDirectory.
     super initialize

メイン部にはファイル一覧を表示したいので、リストウィジェットが表示されるように initializePresenters メソッドを作ります。

initializePresenters
     listEntries := self newList.
     self focusOrder
         add: listEntries

さらに defaultSpec クラスメソッドでレイアウトを設定します。

defaultSpec
     ^ SpBoxLayout newVertical
         add: #listEntries;
         yourself

これでウィジェットの骨格ができました。実際に試してみましょう。

SpecFileListSample new openWithSpec.

ファイル一覧を表示する

リストウィジェットにファイルの一覧を表示させることにします。まず、directory が示すディレクトリ内のファイル一覧を取り出すメソッド getEntries を作ります。

getEntries
     ^ directory entries

リストウィジェットで項目を表示するためのメソッド showEntry: を作ります。項目がディレクトリなら両側をカッコで囲むようにしています。

showEntry: aFileReference
     | base |
     base := aFileReference basename.
     ^ aFileReference isDirectory
         ifTrue: [ '<' , base , '>' ]
         ifFalse: [ base ]

太字部分のように initializePresenters を修正して、ファイル一覧を設定します。

initializePresenters
     listEntries := self newList.
     listEntries items: self getEntries.
     self focusOrder
         add: listEntries

最後に connectPresenters で、設定された一覧を表示するように、リストウィジェットにブロック引数を付けて display: メッセージを送ります。

connectPresenters
     listEntries
         display: [ :m | self showEntry: m ]

これでファイル一覧が表示されました。