Switching to Vim #1 – Start at the beginning

The most helpful tip I read after deciding to use Vim exclusively was initially a massive PITA. Disable the arrow keys. If you really do want to learn Vim properly, do this.

No comments | Trackback

Switching from Sublime Text 2 to Vim

Compared to using an editor like Sublime Text 2, vim is hard. The way one interacts with vim compared to other editors seems about as different as Chinese is to English. Knowing this, why the hell would anyone want to learn it? Here are my reasons.

Comments (14) | Trackback

The new Nodegit maintainer

Some months ago I was invited to become a maintainer of and drive future development for the excellent nodegit module. Before this I had spent a week trialling and discarding all but nodegit as I struggled to find a Node.js module that would allow me to work with Git repositories programmatically, asynchronously, and predictably. Nodegit did these things, so I fixed some issues and submitted a pull request. And then, history.

No comments | Trackback

Chinese Idiom Database

I’ve rewritten my old Chinese Idiom Database completely, partly as an excuse to learn Symfony. The new database has over 12,000 Chinese Idioms!

Comments (2) | Trackback

More Usability-Oriented Method for Disabling an NSTabViewItem

Jump to tutorial

I was unable to find a way to easily disable an NSTabViewItem – before implementing the method described below, I achieved this by simply returning “NO” from the “shouldSelectTabViewItem” delegate method. I don’t like this method as I feel that from the user’s perspective:

  • The reason they can’t click the tab item isn’t clear – is it disabled? Has something broken?
  • If they do guess that the tab has been disabled, there is no way short of trial and error for them to discover how they may enable it

Instead of using this opaque method for disabling a given tab, I opted for a more complicated yet informative approach:

When the user clicks a disabled tab, a modal dialog opens, informing them that the tab has been disabled and why, and offers two options: ‘Continue’, and ‘Enable’. In my case, clicking enable simply checks a checkbox and switches to the disabled tab.

From a usability perspective, this approach is superior for two reasons:

  1. The user is informed of something that is otherwise not immediately apparent
  2. The user is given a ‘one click’ option to perform whatever action is required to remedy the situation

Howto:

Make sure your controller is the delegate for your NSTabView. Either programatically, as shown here in the class implementation or through XCode’s GUI.

AppController.m:

3
4
5
- (void) awakeFromNib {
    [yourTabView setDelegate:self];
}

Then make sure the same class implements the ‘shouldSelectTabViewItem’ delegate method, as below:

This is called when a user attempts to change tabs, and is where we detect the tab they’re attempting to change to, and if that tab is disabled, prevent the app from switching to the tab and show the user a message.

AppController.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (bool) tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem {
	if(![tabViewItem identifier] == yourIdentifier && isDisabled){
 
            NSAlert *alert = [[[NSAlert alloc] init] autorelease];
            [alert addButtonWithTitle:@"OK"];
            [alert addButtonWithTitle:@"Enable"];
 
            [alert setInformativeText:@"Tab Disabled"];
            [alert setMessageText:@"This tab has been disabled"];
 
            [alert setAlertStyle:NSWarningAlertStyle];
 
            [alert beginSheetModalForWindow:[NSApp mainWindow] 
                              modalDelegate:self 
                             didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) 
                                contextInfo:@"Tab Disabled"];
		return NO;	
	} else {
		return YES;	
	}
}

Now we need to implement the ‘alertDidEnd’ delegate method in the same class, which is called when the user clicks one of the buttons we added to the NSAlert above.

AppController.m:

1
2
3
4
5
6
- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
    if (contextInfo == @"Tab Disabled" && returnCode == NSAlertSecondButtonReturn) {
         // Perform whatever action is required to enable your tab here, and switch the user to that tab
        [tabsView selectTabViewItemWithIdentifier:yourIdentifier];
    }
}

And that’s it!

Bare-bones header & implementations below:

1
2
3
4
5
6
7
8
9
10
11
12
13
#import
 
#define MODAL_TAB_DISABLED =      @"Tab Disabled"
#define DISABLED_TAB_IDENTIFIER   @"Disabled Tab"
 
@interface AppController : NSObject {
     IBOutlet NSTabView *tabView;
}
 
- (bool) tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem;
- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
 
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#import <Cocoa/Cocoa.h>
#import <AppController.h>
 
@implementation AppController
 
-(void)awakeFromNib {
     [tabView setDelegate:self];    // Set self as the tab view's delegate
}
 
- (bool) tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem {
	if(![tabViewItem identifier] == DISABLED_TAB_IDENTIFIER && isDisabled){
 
            // Create and configure an alert
            NSAlert *alert = [[[NSAlert alloc] init] autorelease];
            [alert addButtonWithTitle:@"OK"];
            [alert addButtonWithTitle:@"Enable"];
 
            [alert setInformativeText:@"Tab Disabled"];
            [alert setMessageText:@"This tab has been disabled"];
 
            [alert setAlertStyle:NSWarningAlertStyle];
 
            [alert beginSheetModalForWindow:[NSApp mainWindow] 
                              modalDelegate:self 
                             didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) 
                                contextInfo:MODAL_TAB_DISABLED];
		return NO;	
	} else {
		return YES;	
	}
}
 
- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
    if (contextInfo == MODAL_TAB_DISABLED && returnCode == NSAlertSecondButtonReturn) {
         // Perform whatever action is required to enable your tab here, and switch the user to that tab
        [tabsView selectTabViewItemWithIdentifier:yourIdentifier];
    }
}
 
@end
No comments | Trackback