Smalltalk-72で学ぶOOPの原点:ウインドウを無理矢理サイズ変更&移動する

アラン・ケイの“オブジェクト指向”というアイデアをもとに(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72で遊んでみるシリーズです(なお最新のSmalltalkについては Pharo などでお楽しみください!)。他の記事はこちらから→Smalltalk-72で学ぶOOPの原点 Advent Calendar 2019 - Qiita


ユーザー定義クラス・アクションの一覧「defs」と定義の出力「show」の続き)

対話ウインドウクラスdispframeとそのインスタンスdisp

画面の下の方に表示されている矩形領域はdispframeという対話ウインドウクラスのインスタンスで、使用中のものはdispという変数に代入されています。

f:id:sumim:20191220000355p:plain
「disp」に代入された「dispframe」のインスタンス

例によってALLDEFSでto dispframe …で始まるコードやコメントを読むとこのウインドウの使い方(応答できるメッセージやその送り方)が分かります。

たとえば、すでに何度か出てきているように← <ASCIIコード>というメッセージを送れば指定したASCIIコードに割り振られた文字が、← <文字列>というメッセージを送ればその文字列が表示される…とか、

f:id:sumim:20191220000833p:plain
「dispframe」クラスに定義されたメッセージ「←」への応答(コメントにて)

f:id:sumim:20191220001002p:plain
「← <文字コード>」、「← <文字列>」への応答

画面が汚れてしまったらdisp clearで消して、disp displayで描き直すことができる…とか、です。

f:id:sumim:20191220001312p:plain
ウインドウ領域を消去するには「disp clear」

f:id:sumim:20191220001422p:plain
消去したあと、改めてウインドウを描き直すには「disp display」

一方で、「Open the ST-72 Manual」ボタンでダウンロードして読むことができる1976年刊の「Smalltalk-72 Instruction Manual」の記述にはある、ウインドウを移動したり(moveto)、サイズを変える(growto)といったいくつかのメッセージ

f:id:sumim:20191220001712p:plain
ここにある例のうち「hide」「moveto」「growto」は定義されておらずエラーになる

これらへの応答は、「ALLDEFS」ボタンで参照できる1974年のALLDEFSには(かつ、それに近いバージョンのALLDEFSで起動しているらしいこの処理系のメモリダンプにも)まだ実装されていないことも分かります。

また、マニュアルにある1976年ごろのバージョンには、新規ウインドウの作成、削除(閉じる)、大きさの変更、移動といったマウス操作も可能だったようですが、それらも当然まだ実装されていません。

f:id:sumim:20191220011517p:plain
1976年頃のウインドウ操作に関する記述(Smaltalk-72 Instruction Manual, pp 5-6)※このエミュレーターでは使えません

これらは矩形領域の高速な描画を可能とするBitBLTルーチンが発明され、あるいはそれがマイクロコード化された後に実装された機能だと思われます。

f:id:sumim:20191220012807p:plain
BitBLTルーチンへのアクセスに関する記述(Smalltalk-72 Instruction Manual, pp 42-43)※このエミュレーターでは使えません

put <文字列> at <x座標> <y座標>

ウインドウ操作に関連する振る舞いが軒並み未実装ななか、かろうじて、任意の文字列を任意の場所に表示するdisp put <文字列> at <x座標> <y座標>という式は使えるようです。

f:id:sumim:20191220002532p:plain
メッセージ「put <文字列> at <x座標> <y座標>」への応答を記述したコード

これは実質、ウインドウを移動しているコードなので、このメッセージを送ることでdispを画面の上の方へ移動してみましょう。ただしこのput …メッセージは、レシーバーであるdispframeインスタンス(ここではdisp)を移動しはするものの、指定した文字列を表示するだけでフレームの描き直しなどはしてくれません。そこで「disp display. disp clear」を続けて実行することで、枠やプロンプトの表示も行います。

f:id:sumim:20191220003349p:plain
「disp '' at 16 16」でウインドウを画面上部に移動し、「disp display. disp clear」で枠を描き直した結果

dispに手をつっこんで('s)縦の長さ(winhtfrmht)を変える

dispframeインスタンス生成式やそれについてのコメント、

f:id:sumim:20191220004512p:plain
「dispframe」のインスタンス生成式やインスタンス変数についてのコメント

あるいはdispframeインスタンス化時(つまりdispframeが直接コールされた結果、isnewが非偽値を返すときに実行される──)isnew ⇒(…以降の初期化コード片を読むと、ウインドウの高さはwinhtfrmhtに保持されていることが分かります。

f:id:sumim:20191220003909p:plain
「dispframe」のインスタンス生成時(「isnew」が非偽時)の初期化処理を記述したコード片

そこで、disp's (<評価したい式>)を送って、直接これらのパラメーターを書き換えてしまいます。より多くの行数を表示できるように、バッファー(buf)も増やしておきます。「disp display. disp clear」の追加も忘れずに。

f:id:sumim:20191220004906p:plain
「disp」の高さを無理矢理変えて描き直すと…

f:id:sumim:20191220005057p:plain
画面いっぱいに広がったウインドウ

任意の場所にスクロールさせることはできませんが、このくらいの縦幅があれば、クラスやアクションの定義を整形して表示するshowアクションもかなり快適に使えます。

f:id:sumim:20191220005438p:plain
「show obset」をdo-it(\キー)してコード全体を表示させてみた様子

サンプルコード「joe the box」(turtleにplace追加、squareとboxの定義)に続く)