Improve keyboard navigation by automatically computing a key view loop

for a window (and tab view item). This implicit key view is created
when a window is made key and does not have an explicit key view loop,
which is detected by checking the initial first responder of the
window.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@35632 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Wolfgang Lux 2012-10-04 09:20:08 +00:00
parent 81708584a5
commit c29763d8cc
7 changed files with 227 additions and 6 deletions

View file

@ -1,3 +1,26 @@
2012-10-04 Wolfgang Lux <wolfgang.lux@gmail.com>
Improve keyboard navigation by automatically computing a key view
loop for a window (and tab view item). This implicit key view is
created when a window is made key and does not have an explicit
key view loop, which is detected by checking the initial first
responder of the window.
* Source/NSViewPrivate.h: New header declaring auxiliary key view
loop methods.
* Source/NSWindow.m (-becomeKeyWindow, -recalculateKeyViewLoop):
* Source/NSView.m (-_setUpKeyViewLooopWithNextKeyView:,
-_recursiveSetUpKeyViewLoopWithNextKeyView:, -_viewWillMoveToWindow:,
cmpFrame()):
* Source/NSControl.m (-_setUpKeyViewLoopWithNextKeyView:):
* Source/NSTabView.m (-dealloc, -selectTabViewItem:, -setNextKeyView:,
_setUpKeyViewLoopWithNextKeyView:): Implement code to
automatically recalculate the key view loop of a window.
* Headers/AppKit/NSTabView.h: Add new member to maintain the
original next key view of a tab view.
2012-10-04 Wolfgang Lux <wolfgang.lux@gmail.com>
* Source/NSClipView.m (-setDocumentView:, -setNextKeyView:):

View file

@ -55,6 +55,7 @@ typedef enum {
BOOL _truncated_label;
id _delegate;
NSUInteger _selected_item;
NSView *_original_nextKeyView;
}
- (void)addTabViewItem:(NSTabViewItem *)tabViewItem;
- (void)insertTabViewItem:(NSTabViewItem *)tabViewItem

View file

@ -49,6 +49,7 @@
#import "AppKit/NSTextView.h"
#import "AppKit/NSWindow.h"
#import "GSBindingHelpers.h"
#import "NSViewPrivate.h"
/*
* Class variables
@ -1120,3 +1121,14 @@ static NSNotificationCenter *nc;
}
@end
@implementation NSControl(KeyViewLoop)
- (void) _setUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView
{
// Controls are expected to have no subviews
//NSLog(@"%@@%p -_setUpKeyViewLoopWithKeyKeyView:%@@%p", [self class], self, [nextKeyView class], nextKeyView);
[self setNextKeyView: nextKeyView];
}
@end

View file

@ -42,6 +42,12 @@
#import "AppKit/NSWindow.h"
#import "GNUstepGUI/GSTheme.h"
#import "GSBindingHelpers.h"
#import "NSViewPrivate.h"
@interface NSTabViewItem (KeyViewLoop)
- (void) _setUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView;
- (NSView *) _lastKeyView;
@end
@implementation NSTabView
@ -82,6 +88,9 @@
{
RELEASE(_items);
RELEASE(_font);
// Reset the _selected attribute to prevent crash when -dealloc calls
// -setNextKeyView:
_selected = nil;
[super dealloc];
}
@ -262,11 +271,23 @@
if (selectedView != nil)
{
NSView *firstResponder;
[self addSubview: selectedView];
// FIXME: We should not change this mask
[selectedView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
[selectedView setAutoresizingMask:
NSViewWidthSizable | NSViewHeightSizable];
[selectedView setFrame: [self contentRect]];
[_window makeFirstResponder: [_selected initialFirstResponder]];
firstResponder = [_selected initialFirstResponder];
if (firstResponder == nil)
{
firstResponder = [_selected view];
[_selected setInitialFirstResponder: firstResponder];
[firstResponder _setUpKeyViewLoopWithNextKeyView:
_original_nextKeyView];
}
[self setNextKeyView: firstResponder];
[_window makeFirstResponder: firstResponder];
}
/* Will need to redraw tabs and content area. */
@ -691,3 +712,63 @@
}
}
@end
@implementation NSTabViewItem (KeyViewLoop)
- (void) _setUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView
{
[self setInitialFirstResponder: [self view]];
[[self view] _setUpKeyViewLoopWithNextKeyView: nextKeyView];
}
- (NSView *) _lastKeyView
{
NSView *keyView = [self initialFirstResponder];
NSView *itemView = [self view];
NSView *lastKeyView = nil;
NSMutableArray *views = // cycle protection
[[NSMutableArray alloc] initWithCapacity: 1 + [[itemView subviews] count]];
if (keyView == nil && itemView != nil)
{
[self _setUpKeyViewLoopWithNextKeyView: itemView];
}
while ([keyView isDescendantOf: itemView] && ![views containsObject: keyView])
{
[views addObject: keyView];
lastKeyView = keyView;
keyView = [keyView nextKeyView];
}
[views release];
return lastKeyView;
}
@end
@implementation NSTabView (KeyViewLoop)
- (void) _setUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView
{
[_items makeObjectsPerform: @selector(_setUpKeyViewLoopWithNextKeyView:)
withObject: nextKeyView];
if (_selected)
{
[super setNextKeyView: [_selected initialFirstResponder]];
}
[self setNextKeyView: nextKeyView];
}
- (void) setNextKeyView: (NSView *)nextKeyView
{
_original_nextKeyView = nextKeyView;
if (_selected)
{
[[_selected _lastKeyView] setNextKeyView: nextKeyView];
}
else
{
[super setNextKeyView: nextKeyView];
}
}
@end

View file

@ -77,6 +77,7 @@
#import "GSToolTips.h"
#import "GSBindingHelpers.h"
#import "GSGuiPrivate.h"
#import "NSViewPrivate.h"
/*
* We need a fast array that can store objects without retain/release ...
@ -422,10 +423,18 @@ GSSetDragTypes(NSView* obj, NSArray *types)
if (_window != nil)
{
[GSDisplayServer removeDragTypes: t fromWindow: _window];
if ([_window autorecalculatesKeyViewLoop])
{
[_window recalculateKeyViewLoop];
}
}
if (newWindow != nil)
{
[GSDisplayServer addDragTypes: t toWindow: newWindow];
if ([newWindow autorecalculatesKeyViewLoop])
{
[newWindow recalculateKeyViewLoop];
}
}
}
@ -5057,3 +5066,54 @@ static NSView* findByTag(NSView *view, int aTag, unsigned *level)
@end
@implementation NSView(KeyViewLoop)
static int
cmpFrame(id view1, id view2, void *context)
{
BOOL flippedSuperView = [(NSView *)context isFlipped];
NSRect frame1 = [view1 frame];
NSRect frame2 = [view2 frame];
if (NSMinY(frame1) < NSMinY(frame2))
return flippedSuperView ? NSOrderedAscending : NSOrderedDescending;
if (NSMaxY(frame1) > NSMaxY(frame2))
return flippedSuperView ? NSOrderedDescending : NSOrderedAscending;
// FIXME Should use NSMaxX in a Hebrew or Arabic locale
if (NSMinX(frame1) < NSMinX(frame2))
return NSOrderedAscending;
if (NSMinX(frame1) > NSMinX(frame2))
return NSOrderedDescending;
return NSOrderedSame;
}
- (void) _setUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView
{
if (_rFlags.has_subviews)
{
[self _recursiveSetUpKeyViewLoopWithNextKeyView: nextKeyView];
}
else
{
[self setNextKeyView: nextKeyView];
}
}
- (void) _recursiveSetUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView
{
NSArray *sortedViews;
NSView *aView;
NSEnumerator *e;
sortedViews = [_sub_views sortedArrayUsingFunction: cmpFrame context: self];
e = [sortedViews reverseObjectEnumerator];
while ((aView = [e nextObject]) != nil)
{
[aView _setUpKeyViewLoopWithNextKeyView: nextKeyView];
nextKeyView = aView;
}
[self setNextKeyView: nextKeyView];
}
@end

37
Source/NSViewPrivate.h Normal file
View file

@ -0,0 +1,37 @@
/*
NSViewPrivate.h
The private methods of the NSView classes
Copyright (C) 2010 Free Software Foundation, Inc.
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; see the file COPYING.LIB.
If not, see <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _GNUstep_H_NSViewPrivate
#define _GNUstep_H_NSViewPrivate
#import "AppKit/NSView.h"
@interface NSView (KeyViewLoop)
- (void) _setUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView;
- (void) _recursiveSetUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView;
@end
#endif // _GNUstep_H_NSViewPrivate

View file

@ -89,6 +89,7 @@
#import "GSToolTips.h"
#import "GSIconManager.h"
#import "NSToolbarFrameworkPrivate.h"
#import "NSViewPrivate.h"
#define GSI_ARRAY_TYPES 0
#define GSI_ARRAY_TYPE NSWindow *
@ -1551,6 +1552,10 @@ titleWithRepresentedFilename(NSString *representedFilename)
if ((!_firstResponder) || (_firstResponder == self))
{
if (!_initialFirstResponder)
{
[self recalculateKeyViewLoop];
}
if (_initialFirstResponder)
{
[self makeFirstResponder: _initialFirstResponder];
@ -4367,7 +4372,7 @@ resetCursorRectsForView(NSView *theView)
_f.selectionDirection = NSSelectingNext;
[(id)theView selectText: self];
_f.selectionDirection = NSDirectSelection;
}
}
}
}
@ -4464,7 +4469,7 @@ resetCursorRectsForView(NSView *theView)
{
return;
}
if ([theView respondsToSelector:@selector(selectText:)])
if ([theView respondsToSelector:@selector(selectText:)])
{
_f.selectionDirection = NSSelectingPrevious;
[(id)theView selectText: self];
@ -4504,8 +4509,10 @@ current key view.<br />
- (void) recalculateKeyViewLoop
{
// FIXME
// Should be called from NSView viewWillMoveToWindow
// Should be called from NSView viewWillMoveToWindow (but only if
// -autorecalculatesKeyViewLoop returns YES)
[_contentView _setUpKeyViewLoopWithNextKeyView: _contentView];
[self setInitialFirstResponder: [_contentView nextValidKeyView]];
}