







第72回
■
■SqueakではじめるSmalltalk入門 第72回 鷲見 正人
■
本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。前回は、製作中の簡易GUIビルダにおいて、メソッド「#addFieldTo:」で、設置するテキストフィールドの大きさを任意に指定できる機能を拡張しました。今回は、逆に既存のテキストフィールドを削除するメソッド「#removeWidgetFrom:」と、それをマウス操作により起動するためのメニュー項目「delete」を追加しましょう。
簡単のため、削除のためのメニュー項目を選択後、GUIビルダウインドウをクリックすると、そこにあるテキストフィールドを削除する…という仕様にします。
クリックした場所の画面上での座標は、すでに第64回で予習したとおり、クラスPointにメッセージfromUserを送信することで容易に得られます。一方、ウインドウは自分にサブモーフとして登録されているウィジェット(この場合、テキストフィールド)を知っていて、paneMorphというメッセージを送るとそれらを要素として収めたコレクションとして返してきます。これらのことを利用して、クリックした場所にあるウィジェット(サブモーフ)を判断して削除するメソッドは次のように書くことができそうです。
removeWidgetFrom: window
| clickPoint selected |
clickPoint := Point fromUser.
selected := window paneMorphs
detect: [:morph | morph bounds containsPoint: clickPoint]
ifNone: [].
selected ifNotNil: [selected delete]
クラス「GuiBuilder」のブラウズしている状態で、このコードを選択してコピー&ペーストでGuiBuilderをブラウズ中のシステムブラウザのコードペインに貼り付け、accept(cmd + S)してください。システムブラウザ右上のペインに「removeWidgetFrom:」が追加されれば、コンパイルは成功です。クリックした場所に存在するウィジェットを検出する手段はいくつか考えられますが、ここでは#containsPoint:による判定と#detect:ifNone:の組み合わせにより記述しました。
「#containsPoint:」はRectangleのメソッドで、起動すると、名前の通りレシーバである矩形(a Rectangle)がパラメータとして添えられた座標(aPoint)を含んでいるかどうかについて、その結果を真偽値で返してきます。
(0 @ 0 corner: 10 @ 10) containsPoint: 5 @ 5 " => true "
(0 @ 0 corner: 10 @ 10) containsPoint: -1 @ -1 " => false "
もうひとつのキーになるメソッド「#detect:ifNone:」は、レシーバとなるコレクションの中に、第一パラメータとして添えたブロックの評価値が真になる最初の要素を見つけて返します。
#(1 2 3 4) detect: [:each | each even] ifNone: [] " => 2 "
'string' detect: [:char | char isVowel] ifNone: [] " => $i "
該当する要素がひとつも見つからない場合は、代わりに第二パラメータに添えたブロックの評価値を返します。ブロック内に何も記述しない場合はnilが返ってきます。
'rhythm' detect: [:char | char isVowel] ifNone: [#none] " => #none "
'rhythm' detect: [:char | char isVowel] ifNone: [] " => nil "
先述の#removeWidgetFrom:では、クリックした場所にウィジェットが存在しない場合がこれにあたり、返値はselectedに関連づけされます。最後の仕上げの式では、selectedがnilでなければ、つまり該当するウィジェットが見つかっていれば、それに対してメッセージ「delete」を送って削除しています。
さて。このメソッドをGUI上での操作に結びつけるには、ウインドウメニューにこのメソッドを起動するためのメニュー項目を追加しておく必要があります。仮に「delete」とでもいたしましょう。「add field」メニュー項目と同様、#addModelItemsToWindowMenu:のコードの改変で簡単に指示できます。
addModelItemsToWindowMenu: aMenu
| window |
window := aMenu defaultTarget.
aMenu addLine.
aMenu add: 'add field' target: self selector: #addFieldTo: argument: window.
aMenu addLine.
aMenu add: 'delete' target: self selector: #removeWidgetFrom: argument: window
システムブラウザ右上のペイン(メソッド名リスト)より「addModelItemsToWindowMenu:」をクリックして選択し、コードを上のように書き換えるか、もしくは、メソッド「#removeWidgetFrom:」のときと同様にコピーしてから、select all(cmd + A)、paste(cmd + V)(既存のコードと置き換え…)して、最後にaccept(cmd + S)してください。
コンパイルがうまく通れば、その直後からGUIビルダウインドウのウインドウメニューには区切り線と「delete」コマンドが追加されるはずです。
[fig.A]追加された区切り線と「delete」コマンド

この「delete」を選択すると、直後にマウスカーソルがクロスヘア(十字)に変わるので、削除したいテキストフィールドをクリックして指定します。クリックした場所にあるテキストフィールドを消すことができれば、動作は正常です。
このページを編集 (4295 bytes)
 |
以下の 1 ページから参照されています。 |
This page has been visited 250 times.