Cocoaでいこう! Macらしく 第33回
Yoshiki(DreamField)
この記事は、MOSAが発行するデベロッパ向けのデジタルマガジンMOSADeN 第95号(2003年12月24日発行)に掲載された記事です。2〜3ヶ月遅れで、ここに掲載して行きます。

メリークリスマス!皆様、クリスマスを満喫しているでしょうか!私はもちろん!来週、ビックサイトで行われる某イベントのために、ひたすら原稿を書いていることでしょう(;_;)。あぅあぅ。

ウィンドウのサイズを調節しよう(後編)

前回までの実装で、ウィンドウは画像のサイズ以上には広がらなくなりました。では、画面サイズに対してはどうでしょうか。画面よりも大きな画像を開いてみましょう。縦方向は自動的に画面に収まるサイズになりますし、ドッグもちゃんと避けてくれます(fig.01)。

[fig.01] 縦方向はドックを避けて自動的に調節される
(サンプル画像は、ゆきみだいふくさんのご好意により、使用させていただいています。)

これは前回、setContentSize:メッセージをウィンドウに投げて、中身のサイズを教えたことにより、自動的に行ってくれるようになった動作です。では、横方向はどうでしょうか。実は横方向は、画面に合わせてはくれません(fig.02)。

[fig.02] 横方向は右側がはみ出してしまう
(サンプル画像は、ゆきみだいふくさんのご好意により、使用させていただいています。)

縦方向は調節してくれるのに、横方向は調節してくれない。何でこういう実装になっているのかとても不可解なのですが、これのおかげで、cocoaでは通常の方法でウィンドウを画面サイズに合わせることができません。こう書くと、画面のサイズを取得し、そこからはみ出すようなら適切なサイズを算出し、ウィンドウにセットすれば良いだけではないかと思われる方は多いと思います。ところが、これは一筋縄ではいかないのです。

複数のウィンドウを開く時、大抵のMacのアプリは階段状に開いて行きます。これは、裏に回ったウィンドウのタイトルバーが、なるべく隠れないようにするための配慮です(fig.03)。

[fig.03] ウィンドウを階段状に表示するのはタイトルバーが隠れないようにするため
(サンプル画像は、ゆきみだいふくさんのご好意により、使用させていただいています。)

cocoaのNSWindowControllerにも、当然この機能が実装されており、デフォルトではこれを自動的に行ってくれます。ところが、ここに問題があります。ウィンドウの表示位置を自動的に調節してくれるのは良いのですが、この表示位置を、表示される前に取得する方法がありません。つまり、一度表示させてからでないと、画面のサイズに合わせたウィンドウサイズの調節は出来ないのです。と言うことは、調節している姿が見えてしまいます。

NSWindowControllerが行っている自動調節を止めてしまえば、ウィンドウの表示位置を全てコントロールすることは可能です。こうすれば、表示前に、全ての調節を行っておくことができます。その代わり、ウィンドウを階段状に並べる機能を、自分で実装し直さないといけません。せっかく実装されている機能を殺して、自分で同じ様なものを実装し直すのでは、正当な方法とは言えないでしょう。

この様に、cocoaにおいては、画面サイズに合わせたウィンドウサイズの調節は、正当な手段がありません。いささか邪道ですが、考えられる方法は次の二つだと思います。

  1. ウィンドウ表示後に画面のサイズに合わせて調節する。
  2. NSWindowControllerに元から実装されている機能を殺して、自分で階段状に開く機能を実装する。

実を言えば、私が作るアプリは、今のところ全て2.で実装されています。使っている人にとって、見栄えは良くなりますが、プログラムとしてはあまり美しくありません。今回は、多少見栄えは悪くはなりますが、より簡潔で無駄の少ない1.の方法を説明します。

ウィンドウを開いた後に調節を行うのですから、これはwindowControllerDidLoadNib:から戻った後に行わければなりません。そこで、メッセージを一定時間後に送信する仕組みを使用します。これにはNSTimerクラスを使用しますが、このクラスの説明をここで行うと誌面が足りなくなりますので、これは次回以降に廻します。ここでは、とりあえず次の様にコードを追加して下さい。

MyDocument.hに、NSTimerクラスのインスタンスへのポインタを格納するインスタンス変数の宣言を追加します。

    NSTimer *adjustWindowTimer;

windowControllerDidLoadNib:の最後に、NSTimerクラスのインスタンスを生成するコードを追加します。

adjustWindowTimer =
    [[NSTimer scheduledTimerWithTimeInterval:0
                                      target:self
                                    selector:@selector( timerAdjustWindowSize:)
                                    userInfo:theWindow
                                     repeats:NO
     ] retain];

そして、MyDocument.mにウィンドウを調節するメソッドを以下の様に実装します。

※このソースには誤りがあります。次回説明しますので、合わせてお読みください。

- (void)timerAdjustWindowSize:( NSTimer *)aTimer
{
    NSWindow *theWindow;
    NSRect theWindowRect;

    theWindow = [ adjustWindowTimer userInfo];
    [ adjustWindowTimer invalidate];
    [ adjustWindowTimer release];
    adjustWindowTimer = nil;

    theWindowRect = NSIntersectionRect( [ theWindow frame], [ [ theWindow screen] visibleFrame]);
    [ theWindow setFrame:theWindowRect display:YES];
}

ウィンドウを調節しているのはこのメソッドの最後の2行のコードです。次のコードでウィンドウが存在するスクリーンの、メニューバーやドックを避けた画面の矩形を取得できます。

[ [ theWindow screen] visibleFrame]

そして、次のコードで、ウィンドウの矩形を取得できます。

[ theWindow frame]

この二つの矩形の交わる部分を、NSIntersectionRect()で算出しています。この算出した結果の矩形を、次のコードでウィンドウにセットしています。

[ theWindow setFrame:theWindowRect display:YES];

ビルドして実行してみて下さい。表示した直後に、画面サイズに合わせてウィンドウが調節されるようになったはずです(fig.04)。

[fig.04] ウィンドウの右端が画面からはみ出さないようになった
(サンプル画像は、ゆきみだいふくさんのご好意により、使用させていただいています。)

前頁目次次頁