mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-22 20:50:44 +00:00
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:
parent
81708584a5
commit
c29763d8cc
7 changed files with 227 additions and 6 deletions
23
ChangeLog
23
ChangeLog
|
@ -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:):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
37
Source/NSViewPrivate.h
Normal 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
|
|
@ -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]];
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue