bug-gnustep
[Top][All Lists]
Advanced

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

Re: nextValidKeyView (was setNextKeyView: dangling pointers)


From: Nicola Pero
Subject: Re: nextValidKeyView (was setNextKeyView: dangling pointers)
Date: Fri, 20 Dec 2002 12:55:50 +0000 (GMT)

Richard,

I was reordering my email, and I've got this old, very interesting mail
from Caba which I'm not sure we've actually addressed.

I suspect you might have lost/missed it in the email disaster you had.

I'm assuming you want to look at it yourself when you've got some spare
time (let me know if otherwise).

Thanks.


On Sun, 27 Oct 2002, Caba Conti wrote:

> 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>
> 




reply via email to

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