ページ

2010年5月7日金曜日

Cocoa Bindingsを使ったボタンの Disable

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

アプリが選択されていない時は「ー」ボタンを Disable にしたい。

Outletでつないで制御するのも手だが、Cocoa Bindings を使えばもっと簡単にできる。今回表示データは NSArrayController で管理しているので、ここの値(メソッド)とボタンの Enabled との間でバインドできれば良さそうだ。状況としては、選択されているか、非選択のかの2つの状態が判断できればいい。

NSArrayController のリファレンスを眺めていると、そのものズバリのメソッドはなかったが -[NSArrayController selectionIndexes] が NSIndexSet を返すので、-[NSIndexSet count] をとれば 0(非選択)、0以外(選択)が取れそうだ。これを仕込んでみる。

Key Path に selectionIndexes.count と書けば、selectionIndexes、count と順番にメッセージを投げてくれる。

# もっといい方法があれば教えて欲しい。

- - - -
Cocoa Bindings は便利。


(追加)複数の方の指摘で -[NSObjectController canRemove] があることが判明。

NSObjectController Class Reference - canRemove

こちらの方が適切のようだ。実際期待通りの動作を確認しました。
指摘してくれた方々ありがとうございました。

2010年5月6日木曜日

SimpleCap - メニューキャプチャに新オプション追加

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

今までのメニューキャプチャはこんな感じ。


これに新オプションを追加してこんなキャプチャができるようにする。

下に表示されていたウィンドウなども映るようになって、ちょっと自然な感じ?になる。

元々キャプチャ範囲(CGRect)は計算済なので、それを CGWindowListCreateImage() に渡せば簡単にキャプチャができた。
おおまかは実装できたが、タイマーウィンドウが写り込む問題が残っている。この実装がうまく行ったらプリファレンス UIに追加し、バージョン 1.3 でリリースする。

(今日は時間切れ)

2010年5月5日水曜日

SimpleCap - Viewer改修:前回の位置を記憶

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

終了時に SimpleViewer の位置と大きさを記録し、次回起動時に反映させる。アプリ終了は NSNotificationCenter に登録し NSApplicationWillTerminateNotification の通知で知る。

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillTerminate:)
name:NSApplicationWillTerminateNotification object:nil];
  :


-(void)applicationWillTerminate:(id)sender
{
NSString* frameString = NSStringFromRect([self frame]);
[UserDefaults setValue:frameString forKey:UDKEY_VIEWER_WINDOW_POSITION];
}


次回起動時に上記値を使えば良い。


なおマルチスクリーン(液晶パネルが複数あるケース)や、ノートPCを使っていてモニタを取り外す場合、 Viewer を見失うケースがありうる。そこで範囲選択同様、Viewerの位置をリセットする機能を追加した。


以前の範囲リセットとまとめてサブメニューとしてみた。

2010年5月4日火曜日

SimpleCap - Viewer改修:常にトップ表示をやめるオプション追加

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

こんな感じ。

チェックを外すと他のウィンドウの下に隠れるようになる。


Simple Viewer のウィンドウは NSPanel を使っているので、この制御には -[NSPanel setFloatinPanel:] が使える。

NSPanel Class Reference - setFloatinPanel:


なおメニューから Simple Viewer を表示させる場合、その時だけ他のウィンドウの上に表示させたい。-[NSWindow makeKeyAndOrderFront:] と -[NSApplication activateIgnoringOtherApps:] を呼び出しているが、setFloatingPanel:NO のケースでは他のウィンドウの下に隠れてしまう場合がある。そこで一時的に setFloatingPanel:YES で最前面に表示するようにしてみたところうまくいった。


BOOL is_floating = [[UserDefaults valueForKey:UDKEY_VIEWER_ALWAYS_ON_TOP] boolValue];
[self setFloatingPanel:YES];
[self makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
[self setFloatingPanel:is_floating];

2010年5月3日月曜日

SImpleCap - プリファレンス改修:アプリ設定 (8) アプリが多い場合の表示 #4 (完成)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: SImpleCap - プリファレンス改修:アプリ設定 (7) アプリが多い場合の表示 #3

画像を差し替える必要があるのと後々の拡張性を考えて、結局カスタムビューを用意した。


@interface ApplicationMoreButton : NSView {

NSImage* _image1; // for checkboard
NSImage* _image2; // for white
NSImage* _image3; // for black
id target;
SEL action;
}

@property (assign) id target;
@property (assign) SEL action;
@end


SimpleViewerの背景毎に画像を用意してこれを切り替えて使う。

こんな感じ。

- - - -
やっとできた。ちょっとうれしい。
今月末ごろリリース予定の 1.3 に載せます。

2010年5月2日日曜日

今週のCocoa情報(5/2) - 今週気になった Cocoaプログラミング情報の紹介

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

NSArrayController 関連で少し。

How to get notified of changes to models via an NSArrayController? - Stack Overflow

NSArrayController 経由でモデルの変更を知るには? KVO と 自前で NSNotificationCenter にポストする方法,
bind:toObject:withKeyPath:options: をオーバーライドする方法が紹介されていた。先日の SimpleCap改修では一番簡単な KVO を使った。こんな感じ。

[arrayController addObserver:self
  forKeyPath:@"arrangedObjects"
 options:NSKeyValueObservingOptionNew
 context:nil];


Chris Hanson - NSArrayController design patterns

NSArrayController を使う場合で、それがバインドしている NSMutableArray を操作しても KVO通知がブロードキャストされない件について。
[[self mutableArrayValueForKey:@"buddies"] addObject:buddy]
or you can do
[self insertObject:buddy inBuddiesAtIndex:[self countOfBuddies]]
という方法が紹介されていた。

先日の SimpleCap 改修でもこの現象は把握していたので、この時には NSMutableArray操作後に -[NSArrayController rearrangeObjects] を呼び出して NSArrayController を強制的に更新していた。NSArrayController だけでモデルの操作が完結できればいいのだが、NSMutableArrayの方が操作メソッドは充実しているので、こちらを直接触った方が効率がいい。


Cocoa Bindings Examples and Hints
x
Cocoa Bindings のサンプルが10以上提供されている。今度眺めてみよう。

2010年5月1日土曜日

SImpleCap - プリファレンス改修:アプリ設定 (7) アプリが多い場合の表示 #3

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: SImpleCap - プリファレンス改修:アプリ設定 (6) アプリが多い場合の表示 #2

前回のチラツキはコーディングミスが原因で、これは解消した。似たような名前の変数を作ってしまうのは良くないと反省。

さて続いてはメニューを出すためのアイコンを表示する。まずは動くものを作りたいので NSButton のサブクラスを作って貼り付ける。


menu_button = [[NSButton alloc] initWithFrame:
  NSMakeRect(0, 0, MENU_BUTTON_WIDTH, MENU_BUTTON_HEIGHT)];
[menu_button setTarget:self];
[menu_button setAction:@selector(clickMenuButton:)];
[menu_button setBordered:NO];
[menu_button setImage:[NSImage imageNamed:@"viewer_more_app"]];
[menu_button setHidden:YES];


あらかじめインスタンスを作り親ビューへ貼りつけておく。その上で、条件が揃った時(アプリアイコンが表示しれないケース)に表示する。このアイコンは右下に位置を固定しておく。

if (number_of_displayed_icons < matrix_row) {

  :

[menu_button setHidden:NO];
[menu_button setFrameOrigin:menu_button_origin]; 
} else {

  :

[menu_button setHidden:YES];
}



ボタンが押されたら表示しきれないアイコンをメニューとして表示する。

-(void)clickMenuButton:(id)sender
{
NSMenu *menu = [[[NSMenu alloc] initWithTitle:@"Application Menu"] autorelease];

NSUInteger index = 0;
for (ApplicationEntry* entry in [arrayController arrangedObjects]) {
if (index >= number_of_displayed_icons) {
NSMenuItem* item = [[[NSMenuItem alloc] init] autorelease];
[item setTitle:entry.name];
[item setImage:entry.icon];
[item setTarget:self.target];
[item setAction:self.menuAction];
[item setRepresentedObject:
[NSDictionary dictionaryWithObjectsAndKeys:entry.path, @"path", nil]];
[menu addItem:item];
}
index++;
}
[NSMenu popUpContextMenu:menu withEvent:[[NSApplication sharedApplication] currentEvent] forView:nil];
}


表示できているアイコンの数はわかっているので、残りのものについて NSMenuItem を作り、NSMenuを作成する。そして最後に +[NSMenu popUpContextMenu:withEvent:] でコンテキストメニューを表示する。このメソッドは NSEventを必要とするが、持ち合わせが無いので -[NSApplication currentEvent] を渡してやる。メニュー選択時のターゲットとアクションは別途用意したプロパティを使う。

さて実行してみよう。

アイコンが表示しきれない場合、右下に >> が表示される。
ウィンドウを拡大させても同じ位置に表示される。
全部表示できるまで拡大すると、このアイコンは消える。
アイコンをクリックしてみよう。
出た。

メニューを選択するとちゃんとアプリが起動する。

- - - -
今回は安易に NSButton を使ったので、押された時に真っ黒になってしまう。この辺りの調整が必要。場合によってはカスタムビューを作ってもいい(その方が早いかもしれない)。
またアイコンは、今回のように背景がチェッカーだと識別できるが、背景が黒の場合識別できない。背景によってアイコンを差し替える必要がありそうだ。