bug-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

nextValidKeyView (was setNextKeyView: dangling pointers)


From: Caba Conti
Subject: nextValidKeyView (was setNextKeyView: dangling pointers)
Date: Sun, 27 Oct 2002 16:33:16 +0100

Nicola Pero wrote:


A relatively minor issue is that MacOS-X has no -setPreviousKeyView: method ...
which means that all changes to the key-view chain are done by the 
-setNextKeyView:
method (except for some stuff done in -dealloc).
> More importantly, the documentation talks about a key view loop ... but 
neither
MacOS-X nor GNUstep implement any such thing.
> GNUstep implemented a simple doubly linked list terminated with nil at either
end, which *could* be made into a loop by joining the ends.
> MacOS-X implemented a directed graph of one-to-many/many-to-one relationships,
which *could* be made into a loop as a special case.

I didn't know what the MacOS-X implementation was, and now that I know, it
doesn't make any sense to me :-)

It's called a 'key view loop' because it should be set up in such a way
(by joining the ends) that by pressing TAB enough times, you should walk
once through all the accept-first-responder controls in the window, till
you finally get back to the original one.

That's the basic point of key views ... that you can do things with the
keyboard.  To do things with the keyboard, you must be able to visit all
controls by pressing TAB.  If there is a control which can't be reached,
keyboard navigation is useless, because that control can't be visited and
edited using the keyboard.

Now, if you do

[A setNextKeyView: C];
[B setNextKeyView: C];

and if that means that both A and B go to C, then you've broken this
assumption.

  * if you are in C, by pressing TAB enough times, no matter how you set up
the rest of the key view chain, you never walk through all controls.  Because 
if by pressing TAB you are sent to A at a certain point, you are
immediately afterwards sent to C (and so you don't pass through B!), while
if you at a point are sent to B, you are not sent to A.  So if you start
from C, and press TAB, you will either pass through A or pass through B.  You 
will never pass through both of them, so one of them will be out of
the key view loop - it will be *impossible* to reach it by pressing TAB
enough times.  IMO this is certainly a mistake in the key view setup.

  * if you are in A, and press TAB, you go to C.  If you now realize you
didn't want to do that, and want to go back to A, and press Shift+TAB to
go back/undo your operation, you are sent back to B instead of A.  Not
very friendly I'd say.

So I'd say any code such as
[A setNextKeyView: C];
[B setNextKeyView: C];

(in the MacOS-X interpretation) is always an error in the key view setup.

Anyway - I understand your point of being compatible with MacOS-X (even if
I wish we were better than MacOS-X :-), and as soon as the thing works
correctly in the only 'proper' usage (which is the key view loop, every
key view has a single next, and the last key view has the first one as its
next), it's Ok.

I don't know why they spent time implementing a one-to-many relationship
when it's obvious that using this 'feature' can only be a bug in the
program ...


Basically, what I did was try to mimic the MacOS-X behavior as closely > as I 
could.

Ok ... I wonder if that's what we want, but since you made the effort to
be compatible, it's nice to have it.

I've not tested it, but I assume you did. :-)



I didn't know what the MacOS-X implementation is neither.
Maybe this graph stuff was implemented, so that we have a
simple way to setup alternative paths, in case some parts
of the gui get enabled/disabled in an alternate way.
Imagine A and B were buttons that were enabled/disabled
alternately. In this case you would want to return from C
to whichever is enabled at that time.

Without graphs you had to define a next key view
chain like this: A -> B -> C

With graphs you can do this: A -> C, B -> C
(which IMO is more intuitive, and doesn't get broken if you remove B)

If this is the way how things should work, then the
nextValidKeyView/previousValidKeyView methods of NSView
must perform a breadth first search on the graph.
The current implementation simply follows the key view chain
until it finds one that is willing to acceptFirstResponder.
(Using a CVS version from october 26th)

Here is a little program that tests the implementation behaviour:
------------------------------
#include <AppKit/AppKit.h>

static char tab2[6] = { 'a', 'b', 'c', 'd', '0', '?' };
static NSButton *tab1[sizeof(tab2)];

static char name_of(id v)
{
 int i;
 tab1[sizeof(tab2) - 1] = v;

 for (i = 0; ; ++i)
   if (tab1[i] == v)
     return tab2[i];
}

static void vkv(NSButton *v)
{
 NSButton *nv = [v nextValidKeyView];
 NSButton *pv = [v previousValidKeyView];
 NSLog(@"valid key views: %c <- %c(%d) -> %c",
   name_of(pv), name_of(v), [v acceptsFirstResponder], name_of(nv));
 if (nv && ![nv isEnabled])
   NSLog(@"next valid key view is disabled?");
 if (pv && ![pv isEnabled])
   NSLog(@"previous valid key view is disabled?");
}

static void enb(NSButton *v, BOOL f)
{
 NSLog(@"%@able: %c", (f ? @"en" : @"dis"), name_of(v));
 [v setEnabled:f];
}

int main(int argc, char *argv[])
{
 CREATE_AUTORELEASE_POOL(pool);
 NSButton *a = tab1[0] = [NSButton new];
 NSButton *b = tab1[1] = [NSButton new];
 NSButton *c = tab1[2] = [NSButton new];
 NSButton *d = tab1[3] = [NSButton new];

 [a setNextKeyView:b];
 [a setNextKeyView:c];
 [b setNextKeyView:d];
 [c setNextKeyView:d];
 [d setNextKeyView:a];
 enb(b, YES); enb(c, YES);
 vkv(a); vkv(b); vkv(c); vkv(d);
 enb(b, YES); enb(c, NO);
 vkv(a); vkv(b); vkv(c); vkv(d);
 enb(b, NO); enb(c, YES);
 vkv(a); vkv(b); vkv(c); vkv(d);
 enb(b, NO); enb(c, NO);
 vkv(a); vkv(b); vkv(c); vkv(d);
 enb(a, NO); enb(d, NO);
 NSLog(@"is your program caught in an infinite loop?");
 vkv(a); vkv(b); vkv(c); vkv(d);
 NSLog(@"This is the end.");
 RELEASE(pool);
 return 0;
}
------------------------------

Its output using the current implementation:
------------------------------
2002-10-27 14:39:12.446 MyTest[8658] enable: b
2002-10-27 14:39:12.447 MyTest[8658] enable: c
2002-10-27 14:39:12.448 MyTest[8658] valid key views: d <- a(1) -> c
2002-10-27 14:39:12.448 MyTest[8658] valid key views: a <- b(1) -> d
2002-10-27 14:39:12.449 MyTest[8658] valid key views: a <- c(1) -> d
2002-10-27 14:39:12.449 MyTest[8658] valid key views: c <- d(1) -> a
2002-10-27 14:39:12.450 MyTest[8658] enable: b
2002-10-27 14:39:12.450 MyTest[8658] disable: c
2002-10-27 14:39:12.451 MyTest[8658] valid key views: d <- a(1) -> d
2002-10-27 14:39:12.451 MyTest[8658] valid key views: a <- b(1) -> d
2002-10-27 14:39:12.452 MyTest[8658] valid key views: a <- c(0) -> d
2002-10-27 14:39:12.452 MyTest[8658] valid key views: a <- d(1) -> a
2002-10-27 14:39:12.453 MyTest[8658] disable: b
2002-10-27 14:39:12.453 MyTest[8658] enable: c
2002-10-27 14:39:12.454 MyTest[8658] valid key views: d <- a(1) -> c
2002-10-27 14:39:12.455 MyTest[8658] valid key views: a <- b(0) -> d
2002-10-27 14:39:12.455 MyTest[8658] valid key views: a <- c(1) -> d
2002-10-27 14:39:12.456 MyTest[8658] valid key views: c <- d(1) -> a
2002-10-27 14:39:12.456 MyTest[8658] disable: b
2002-10-27 14:39:12.457 MyTest[8658] disable: c
2002-10-27 14:39:12.460 MyTest[8658] valid key views: d <- a(1) -> d
2002-10-27 14:39:12.460 MyTest[8658] valid key views: a <- b(0) -> d
2002-10-27 14:39:12.461 MyTest[8658] valid key views: a <- c(0) -> d
2002-10-27 14:39:12.461 MyTest[8658] valid key views: a <- d(1) -> a
2002-10-27 14:39:12.462 MyTest[8658] disable: a
2002-10-27 14:39:12.462 MyTest[8658] disable: d
2002-10-27 14:39:12.463 MyTest[8658] is your program caught in an infinite loop?
2002-10-27 14:39:12.463 MyTest[8658] valid key views: a <- a(0) -> a
2002-10-27 14:39:12.464 MyTest[8658] next valid key view is disabled?
2002-10-27 14:39:12.465 MyTest[8658] previous valid key view is disabled?
------------------------------



Its output using breadth first search
------------------------------
2002-10-27 14:43:27.878 MyTest[9219] enable: b
2002-10-27 14:43:27.879 MyTest[9219] enable: c
2002-10-27 14:43:27.883 MyTest[9219] valid key views: d <- a(1) -> c
2002-10-27 14:43:27.884 MyTest[9219] valid key views: a <- b(1) -> d
2002-10-27 14:43:27.885 MyTest[9219] valid key views: a <- c(1) -> d
2002-10-27 14:43:27.886 MyTest[9219] valid key views: c <- d(1) -> a
2002-10-27 14:43:27.886 MyTest[9219] enable: b
2002-10-27 14:43:27.887 MyTest[9219] disable: c
2002-10-27 14:43:27.888 MyTest[9219] valid key views: d <- a(1) -> b
2002-10-27 14:43:27.889 MyTest[9219] valid key views: a <- b(1) -> d
2002-10-27 14:43:27.889 MyTest[9219] valid key views: a <- c(0) -> d
2002-10-27 14:43:27.890 MyTest[9219] valid key views: b <- d(1) -> a
2002-10-27 14:43:27.890 MyTest[9219] disable: b
2002-10-27 14:43:27.891 MyTest[9219] enable: c
2002-10-27 14:43:27.892 MyTest[9219] valid key views: d <- a(1) -> c
2002-10-27 14:43:27.893 MyTest[9219] valid key views: a <- b(0) -> d
2002-10-27 14:43:27.894 MyTest[9219] valid key views: a <- c(1) -> d
2002-10-27 14:43:27.895 MyTest[9219] valid key views: c <- d(1) -> a
2002-10-27 14:43:27.896 MyTest[9219] disable: b
2002-10-27 14:43:27.897 MyTest[9219] disable: c
2002-10-27 14:43:27.898 MyTest[9219] valid key views: d <- a(1) -> d
2002-10-27 14:43:27.900 MyTest[9219] valid key views: a <- b(0) -> d
2002-10-27 14:43:27.900 MyTest[9219] valid key views: a <- c(0) -> d
2002-10-27 14:43:27.901 MyTest[9219] valid key views: a <- d(1) -> a
2002-10-27 14:43:27.902 MyTest[9219] disable: a
2002-10-27 14:43:27.903 MyTest[9219] disable: d
2002-10-27 14:43:27.903 MyTest[9219] is your program caught in an infinite loop?
2002-10-27 14:43:27.904 MyTest[9219] valid key views: 0 <- a(0) -> 0
2002-10-27 14:43:27.905 MyTest[9219] valid key views: 0 <- b(0) -> 0
2002-10-27 14:43:27.906 MyTest[9219] valid key views: 0 <- c(0) -> 0
2002-10-27 14:43:27.907 MyTest[9219] valid key views: 0 <- d(0) -> 0
2002-10-27 14:43:27.908 MyTest[9219] This is the end.
------------------------------

BTW: this eliminates an infinite loop condition
    (occurring in very special cases only)

I attached the test program, and a patch to NSView.m to this mail.

-Caba
<ValidKeyViewsTest.m><NSView.diff>

Attachment: ValidKeyViewsTest.m
Description: Text document

Attachment: NSView.diff
Description: Binary data


reply via email to

[Prev in Thread] Current Thread [Next in Thread]