|
Backnumber May, 2006 April, 2006 March, 2006 February, 2006 January, 2006 December, 2005 November, 2005 October, 2005 September, 2005 August, 2005 July, 2005 June, 2005 May, 2005 April, 2005 March, 2005 Feburary, 2005 January, 2005 December, 2004 November, 2004 October, 2004 September, 2004 Augsut, 2004 July, 2004 June, 2004 May, 2004 April, 2004 March, 2004 February, 2004 January, 2004 December, 2003 November, 2003 October, 2003 September, 2003 August, 2003 |
- HMDT archive-
|
|
Web Kit を使ってページを読み込むときにエラーが発生すると、webView:didFailProvisionalLoadWithError:forFrame: か webView:didFailLoadWithError:forFrame: が呼び出される。このメソッドには、NSError が引数として渡されて、そこからエラー内容を知ることができる。 NSError には、ドメインとコードっていうのがあって、そこからエラーの種類を知ることができる。では、Web Kit で使われる NSError のドメインとコードはどこで定義されているのか?Foundation の NSURLError である。NSURLError.h を参照するとよろし。ドメインは NSURLErrorDomain で、コードはずらずらと定義されている。 追記:WebKitErrorDomain ってのもあったよん。WebKitError.h を見るべし。 |
|
メニューのショートカットを、動的に変更することにチャレンジしてみる。たとえば、Safari では、「ウィンドウを閉じる」と「タブを閉じる」っていう、似たようなメニューがあるでしょ。これらに対するショートカットは、タブが 2 つ以上表示されているときは、「ウィンドウを閉じる」が Cmd + Shift + W で、「タブを閉じる」が Cmd + W だ。
それに対して、タブが 1 つか、タブが無効化されていると、「ウィンドウを閉じる」が Cmd + W になって、「タブを閉じる」はショートカットがなくなり無効化される。
これを実現してみようとした。基本的には、適当なオブジェクトで validateMenuItem: を実装してやればいい。でも、実際やってみると、結構めんどくさいだな、これ。 まず、validateMenuItem: で、渡される NSMenuItem の tag をチェックするなりして、「ウィンドウを閉じる」か「タブを閉じる」かを知る。状況に合わせて適切なショートカットを設定して、メニューの有効/無効を表す YES か NO を返す。 ショートカットの設定は、setKeyEquivalent: で文字を設定し、setKeyEquivalendModifierMask: で修飾キーを設定する。ただし、通常文字の場合、setKeyEquivalent: に小文字を渡すか大文字を渡すかどうかで、シフトキーを設定する。つまり、Cmd + W を設定するときは、 (sample)
となり、Cmd + Shift + W を設定するときには、 (sample)
ってなる。小文字と大文字の違いに注意。ややこしいのは、修飾キーのマスクとして NSShiftKeyMastk が用意されていること。これは、ファンクションキーに使うらしい。まぎらわしいんじゃ、てめー。 validateMenuItem: が呼び出されない限り、ショートカットは更新されない。システムからも、いくつかのタイミングで validateMenuItem: が呼び出される。たとえば、メニューを表示させたときとか。でもたいていはそのタイミングではだめなので、NSMenu の update を呼んで、強制的に更新してやる。そうじゃないと、ショートカット変更したはずだけど効かない→メニューを表示させる→メニューが更新されて新しいショートカットが設定される→なんで効かないんじゃ、ぼけぇ、というループに陥る。 NSResponder に用意されているアクション、たとえば performClose: などに対しては、あらかじめ組み込まれているオブジェクトの validateMenuItem: が呼び出されるようだ。だから、自分で実装したものが効かない。アクションを自分のものに変えてしまうのが楽でよろしい。 validateMenuItem: は、メニューが有効化されているときにしか呼び出されない。つまり、他のウィンドウをメインウィンドウにしたとして、そのウィンドウがアクションに対応するメソッドを持っていないと、メニューは有効化されない。だから、validateMenuItem: も呼び出されない。必要なら、ダミーとしてなにもしないアクションメソッドを持っておく。 罠多し。注意されたし。つうか見事なくらい、だまされまくった。 |
|
Desire for wealth さんが、『ココア図書館』という Cocoa プログラミングの連載をしています。第一回目と第二回目は、縁取り文字の描き方で、NSGlyph を用いたものと NSAttributedString を用いたものを比較されています。カラオケとしての読みやすさにこだわっていて、なるほど、と納得です。 |
|
Cocoa バインディングと NSUserDefaultsController を使うと、簡単にデフォルトデータベースとユーザインタフェースの間で同期がとれる。では、その同期をユーザインタフェースではない、自分のクラスでやりたい場合は?つまり、ユーザインタフェースでデフォルトデータベースの値を変更したら、その通知が欲しいわけだ。たぶん、キー・バリュー・オブザービングを使えば、できるだろう。 はじめは、NSUserDefaultsController を使ってどうにかすんのかなー、と思っていたけど、そうじゃないんだね。キー・バリュー・オブザービングで使われるメソッドは NSObject で実装されているから、直接 NSUserDefaults を使ってやればいい。こんな風に。 (sample)
こうやって、NSUserDefaults に直接オブザーバとして登録してやれば、データベースの指定したキーの値が変更したときに、observeValueForKeyPath:ofObject:change:context: が呼び出される。便利だ。 |
|
ツールバーのアイテムにメニューを付ける方法が分からんちん。Xcode のツールバーみたいに、数秒間押すとメニューが出てくるようにしたいんだけど、あれって絶対非公開 API で実現しているよな。ちくしょう。 |
|
NSView の中を、ウィンドウの背景で使われている、しましま模様で塗りつぶす方法を探していたんだ。NSView にも NSWindow の中にもない。これは、もう、CoreGraphics しかねーな、と思って、CoreGraphics の pattern 塗りつぶしの API でいろいろ実験を重ねてみたよ。でも、手間の割にはぜんぜんうまくいかない。 ふと、別の API を調べていたら、Application Kit Functions の中に、NSDrawWindowBackground() なんてものがあるじゃねーか。これを使えば一発だった。うがー。あそこの関数群は、あなどれない。 |
|
NSTextField の先頭に、アイコンを付け加えてみようとした。ほら、10.3 になって、検索フィールド用の NSSearchField が追加されたでしょ。あれの先頭に、アイコンがついているじゃない。あれを実現してみようと。 どうすればいいかというと、NSTextField では、編集のときに NSText を表示する。このオブジェクトの位置を、いつもより右に動かしてやればいいのだ。で、空いた領域に、アイコンを描く。 似たようなことを考える人はたくさんいて、たとえば、こことかに例がある。また、Developer Tools についてくる、DragNDropOutlineView っていうサンプルの、ImageAndTextCell とかも参考になる。ふむふむ、NSCell の selectWithFrame:inView:editor:delegate:start:legth: と、editWithFrame:inView:editro:delegate:event: をオーバーライドしてやればいいのか。このメソッドで、渡されてきた frame の大きさを変更してやればいいらしい。 じゃやってみるか、とやってみたら、たしかにできた。でも、なんかフォーカスがおかしい。
どうやら、フォーカスの大きさも NSText の大きさにされてしまっているようだ。んじゃ、ということで次の手段を考える。Mac OS X 10.3 からは、フォーカスリングの表示を制御できるようになった。focusRingType っていうメソッドをオーバーライドして、NSFocusRingTypeNone を返せば表示されなくなる。こうやって表示させないで、自分でフォーカスリングを描いてやればいいじゃん? ちなみに、フォーカスリングを描くには、こうする。 (sample)
NSSetFocusRingStyle() を呼べばいいのだ。たしかに、これで描けた。でも、フォーカスを移動すると、なんかゴミが残ってしまう。
どうやら、もともとフォーカスがあたっていた場所しか、消す処理が走らないようだ。フォーカスがあたっていないときに、自分が描いたフォーカスを完全に消す方法を探したんだけど、これがわから〜ん。 ああでもない、こうでもないとやっていたら、いっそのことフォーカスを描くメソッドを直接上書きしちゃえばいいじゃん、ということに思い至った。普通の人が実装したら、そういうメソッドがあるはず。絶対あるに違いない!ということで探す。NSCell に _drawFocusRingWithFrame: っていうメソッドがあった。やっぱりあった。これを上書きして、フォーカスを描く範囲をコントロールビュー全体に広げてやればいい。 ImageTextFieldTest/ImageTextField.m
これでどうにか、思い通りの動作になったよ。
フォーカスを描くメソッドをいじるっていうのは直接的で分かりやすいんだけど、非公開メソッドなのが残念。誰か、もっといい方法知らない? ダウンロード
ImageTextFieldTest.dmg |
|
Cocoa では、ウィンドウに標準でツールバーを付けることができる。だから、ツールバーを実装するのは、すごく簡単だ。でも、ステータスバーはない。ステータスバーが必要なときは、自分で作らなくてはいけない。しくしく。 最近のステータスバーは、表示したり、隠したりすることが求められることが多い。具体的には、Safari のステータスバーを想像してくれ。あれはユーザが表示したり隠したり、または Java Script でコントロールされたりするでしょ。 さて、そういうステータスバーを実装するには、どうすればいいか?1 つの実装パターンを考えてみた。まず、ウィンドウとおなじ大きさになる、すべてのビューのルートとなるビューを用意する。仮に、rootView とでも呼んでおく。rootView はウィンドウの contentView ではなくて、contentView に含まれるビューだ。階層構造は、NSWindow → contentView → rootView → その他のビュー、っていう感じになる。この、その他のビューになるところに、ステータスバーに相当するビューを作る。 そして、ビューを隠すには、次のような手順を使う。これは、ステータスバーが下にある場合だ。
autoresizingMask を変更するのがポイント。いろいろ試してみたけど、これが面倒が無くていいと思う。ちなみに、contentView の autoresizingMask を変更すると、いろいろと悲しいことが起こる。それが、rootView を挿入した理由。 |
|
NSComboBox を使って、自動補完機能付き URL ロケーションバーを作ってみる実験。
まず、~/Library/Safari/History.plist ファイルを読み込んで、URL のリストを作る。そして、NSComboBox にデータソースを設定して、comboBox:completedString: で補完を行う。補完は入力された文字が、
っていう仕様でやっているぜ。ま、なんとなくそれらしい動きにはなった。 ダウンロード
AutoCompleteTest.dmg |
|
NSComboBox って、自動補完機能が付いていたのか!知らなかったよ!しかも、NSComboxCell の completedString: をオーバーライドすると、補完する文字列を制御できるって。使える、これは使えるよ! いやー。自分で補完機能作るとしたらめんどうだよなぁー、って思っていたところで気づいたから、とってもうれしい。 |
|
掲示板の棚卸し作業。せっかく掲示板にいろいろ書き込んでもらったのに、そのまま放っておいちゃ、もったいない!ということで、まとめておきます。 まずは、スクリーンショットを撮るときの話を。 書き込んでくれた keta さん、ありがとうございました。 |
|
追加。 |
|
NSSegmentedControl を使って、タブ表示を作ってみるテスト。NSTabView と似ているんだけど、ちょっと違う。どういう仕様にしてみたかというと、
という感じだ。つまり、ひとことで言ってしまえば、Safari のタブだね。タブのボタンは NSSegmentedControl 作り、その下に NSBox と NSTabView を配置した。手っ取り早さ優先で作ったら、ソースコードはちょっと汚いし、そんなにテストしていないからバグがあるかもしれんけど、as is でどうぞ。 何に使うかって?そりゃ、決まってるでしょ。 ダウンロード
SegmentedTab.dmg |
|
アイコンとテキストを持つボタンを作ってみるテスト。ボタンは枠を持たなくて、クリックすると、画像とテキストが強調表示される。こんな感じで。
NSButtonとNSButtonCellを継承して作ってみた。NSButtonCellを継承したクラスの、drawInteriorWithFrame:inView:をオーバーライドして、描画処理を書いている。画像の強調表示はNSRectFillUsingOperation()を、テキストの強調表示はNSShadowを使ってみたぜ。NSShadow使っているから、10.3専用ね。 ダウンロード
ImageButton.dmg |