†Sibylのお部屋†

【Sibyl】初期設定ルーチンの様々な考慮点

作成開始日 2009.10.12
最終更新日 2022.03.02

Siblyは極めてバギーな処理系だが、中でも初期設定に関する部分には非常に深刻な問題を数多く抱えている。ユニットの循環参照、コントロールの配置の誤差、コントロールの生成のタイミングなど、極めてありふれた問題で頭を抱えてしまう。中でも、プロパティの設定のタイミングは未だにナゾだ。バグとしか思えない現象が多発する上に、対処が困難な場合も少なくない。

●ユニットの循環参照

外部ユニットを呼び込むときはUses命令を使うのだが、これが互いを呼び合うように設定されていると、循環参照となり無限ループに陥ってしまう。もうちょっと具体的に言うと、UnitAとUnitBがあり、UnitAに「Uses UnitB」が、UnitBに「Uses UnitA」があると循環参照に陥る。つまり、両ユニットでルーチンの相互利用をすることはできない。相互参照がプログラムの構造として好ましいかどうかは別として、現実問題として困ることも少なくない。ただし、この問題はFixPack4で解決された

●初期化のタイミング

Sibylには初期化のイベントハンドラが(少なくとも)3種類ある。OnCreate、OnActive、OnShowの3つで、マニュアル通りの解釈をすれば、本来、初期化ルーチンはOnCreateに置くべきものだと思うが;

@OnCreate内では設定できないプロパティがある。
AOnActiveOnShowは、なぜか仕様通りの振る舞いをせず、初期化に利用できてしまう。

@の問題は、FixPack4でかなり改善されている。以前のバージョンでは、OnCreateから各コントロールのプロパティにアクセスすることはできなかった。おそらく、インスタンスが未生成だったと思われる(だとすれば、強烈なバグだよ(^^;)。FixPack4を当てれば、ほぼ正常にアクセスできるようになるが、それでも一部に正常設定できないプロパティがあるようだ。たとえば、コントロールの配置関係のプロパティは、代入は可能でも機能しないことがある。

では、FixPack4以前の環境で、私がどうやってコントロールの初期化をしていたかと言うと……これが実に涙ぐましい裏技を使っていた。初期化ルーチンを変な場所に書いたり、挙げ句の果てはタイマー割り込みで初期化したり(^_^; イヤハヤたいへんだったんだ。

Aは何だか微妙で、本来OnActiveOnShowは、フォームが選択されたり最小化状態から復元されときに実行されるはず。ところが試してみると、プログラム起動直後に一度実行されるだけのようなのだ。したがって、初期化にも利用できると…この中なら以前のバージョンでもコントロールにアクセス出来るので、けっこう利用していた。しかし、本来の用途には使えない…?

初期化の問題は、大きく別けて;

の3つに大別できる。このうち、「グローバル変数」の初期化はほぼ問題なし。「それ以外のプロパティ」もFixPack4を当てればほぼ正常に設定できる。問題は「配置のプロパティ」。OnCreate内では配置プロパティの設定は有効にならない感じなのだ。また、OnShow内では機能しているが、配置に誤差が生じることがある(この点に関しては後述)。

ということで、初期化ルーチンを置く場所は;

FixPack4大前提、OnCreate内が原則、配置関連はOnShowを利用

ということで。何だかなぁ…ではあるけれど。

●コントロールの初期配置のコードによる設定

Siblyはフォームの表示直後の段階まで、コントロールの位置を指定する

TopプロパティがY方向に32ドットずれている。

原因は、おそらく、システムの初期化処理の段階で、フォーム自体の大きさ(Width, Height)と、クライアント領域の大きさ(ClietnWidth, ClientHeight)を混同しているため。実際、初期状態では、両者はまったく同じ値になってしまっている。本来、クライアント領域は、フォーム自体よりも、フレームとタイトルバーの幅の分だけ小さくなくてはならない。具体的には、X方向に8ドット、Y方向に32ドット分小さいはず。この、Y方向の差分の32ドットが丸々誤差になっている。しかも、フォームを一度最小化してから復元すると、この誤差は解消されて正しい位置に配置されるようになる。言い訳のしようがないバグである。ちなみに、WidthとClientWidthも混同しているようだが、X方向には誤差は生じない。また、同じY方向の位置指定のプロパティでも、Bottomには誤差は生じない。

Y方向に誤差が生じているのに、X方向に誤差が生じないのは、座標軸の方向の問題だろう。そもそも、領域の「大きさ」を間違えても、座標の「原点」が正しければ、座標自体は狂わない。単にフォーム自体の大きさとクライアント領域の大きさを間違えただけなら、Y方向も誤差は生じないはずである。しかし、X軸とY軸には決定的な違いがある。コントロール配置の利便性を考慮して、Y軸は方向が逆転しているのだ。本来フォームの座標原点は「左下」にあるのに、あたかも「左上」にあるように座標を変換している。すなわち、コントロールのtopプロパティは、<Y>ではなく、<ClientHeight - Y>なのである。そのClientHeightに32ドットの誤差が含まれるため、Y方向のみ32ドットずれるのだと思われる。

Bottomプロパティに誤差が生じないのもまったく同じ理由。Bottomプロパティは「左下」を原点とした座標系を取っているため、領域の大きさが間違っていても座標はずれない。したがって、初期化設定をする際にも、コントロールの座標を(x,y)=(Left,Bottom)で指定すれば誤差は生じない。ただし、現実には「左上」原点で考えないと、意図した通りにコントロールを配置するのは難しい。Bottom指定にしたからと言って、解決する問題ではない。

対処法方は……もちろん、Sibylのライブラリのソースを修正するのが王道だとも思うが、けっこう面倒臭そうだ。で、いろいろ考えた。上記の私の推測が正しければ、問題はClientHeightの値が不正だという点にある。ならば、初期化プロセスで、<ClientHeight:=Height-32>のように、強制的に修正すればよいのではないか?しかし、これはうまくいかなかった。どうも、代入が無効化されているというか、たぶん、代入直後に再代入が行われているようで、元の値に戻ってしまっている。

となると、あとは非常にスマートでない方法で対処するしかない。たとえば、ClientHeightとHeightが同じときは初期状態と判断して、強制的にtopプロパティに32を加えるとか…

  If Height=ClientHeight
    Then MyControl.top:=100+32
    Else MyControl.top:=100;
てな感じで…。誤差は環境依存の可能性があるので、変数化する方がよいだろう。ただ、いずれにしても、ひどくカッコ悪い。Sibylは開発自体が終了しているようなので、今後バグが修正される可能性もないしな〜〜

ふと思ったんだが、これもひょっとしたら《仕様》なのかもしれない。すなわち、初期化をする段階(たとえばOnShowハンドラ内)では、まだフレームやタイトルバーの設定が終っていなくて、ClietnHeightとHeightは本当に同じ状態だとか…。ない話ではないが、そうだとすれば、根本が間違っているよな……フォームが確定したあとに、コントロールの配置に関する初期設定を行うルーチンを作成できない!

まあ、コントロールの初期配置をコードでやる必要があるのか、という疑問もあるかも知れないが、今回の私の場合は絶対に必要だった。本当に必要なのは、起動時に特定のコントロールにフォーカスを持たせることなんだが、そのコントロールのOnEneterハンドラで各コントロールの位置を設定している。すなわち、本来は初期設定ではなく、フォーカス取得時の処理なんだが、初期状態でフォーカスを取得するために、初期状態で強制的に実行されてしまう。そこで位置がずれて困ったわけだ…


【Sibylのお部屋目次】 【ホーム】