[AD] new GUI focus selection algorithm

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Just a minor thing I got carried into when using the Allegro GUI for a
small program.

In this program, I have a GUI, which just consists of 3 buttons:

         Start
  Fullscreen Settings
          End

I expected to be able to cycle through them with KEY_UP/KEY_DOWN. But
instead, the middle button is ignored.

So, I looked into the source, and the current GUI just compares the
upper left corner of objects, and then chooses dialogs whose upper left
corners are in the same direction, where the arbitrarily chosen distance
formula used is x_distance + 8 * y_distance for horizontal and
y_distance + 8 * x_distance for vertical movement.

Now, to me, the obvious way to select the next focused dialog is to use
the dialog with which the current one would collide if it moved into the
direction for which the cursor key is pressed. The attached patch
changes the behavior to do this.

It solves my example above, and there should also be no regression in
any other cases. Only if previously another dialog was "jumped over",
this now doesn't happen anymore.

I also removed an unneeded parameter from move_focus, and excluded
hidden and disabled objects from the sorting, since offer_focus won't
give focus to them anyway.

(A test program to compare old and new behavior is here:
http://allefant.sf.net/allegro/guifocus.c)

-- 
Elias Pschernig <elias@xxxxxxxxxx>
Index: gui.c
===================================================================
RCS file: /cvsroot/alleg/allegro/src/gui.c,v
retrieving revision 1.65
diff -u -r1.65 gui.c
--- gui.c	17 Oct 2003 08:49:18 -0000	1.65
+++ gui.c	8 Nov 2003 15:18:53 -0000
@@ -502,32 +502,28 @@
 typedef struct OBJ_LIST
 {
    int index;
-   int diff;
+   DIALOG_PLAYER *dp;
 } OBJ_LIST;
 
 
 
-/* obj_list_cmp:
- *  Callback function for qsort().
- */
-static int obj_list_cmp(AL_CONST void *e1, AL_CONST void *e2)
-{
-   return (((OBJ_LIST *)e1)->diff - ((OBJ_LIST *)e2)->diff);
-}
-
-
-
 /* cmp_tab:
  *  Comparison function for tab key movement.
  */
-static int cmp_tab(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
+static int cmp_tab(AL_CONST void *v1, AL_CONST void *v2)
 {
-   int ret = (int)((AL_CONST unsigned long)d2 - (AL_CONST unsigned long)d1);
-
-   if (ret < 0)
-      ret += 0x10000;
-
-   return ret;
+   int i1 = ((OBJ_LIST *)v1)->index;
+   int i2 = ((OBJ_LIST *)v2)->index;
+   DIALOG_PLAYER *dp = ((OBJ_LIST *)v1)->dp;
+   if (dp->focus_obj >= 0)
+   {
+      int i = dp->focus_obj;
+      if (i1 > i && i2 <= i)
+         return -1;
+      if (i2 > i && i1 <= i)
+         return 1;
+   }
+   return i1 - i2;
 }
 
 
@@ -535,14 +531,41 @@
 /* cmp_shift_tab:
  *  Comparison function for shift+tab key movement.
  */
-static int cmp_shift_tab(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
+static int cmp_shift_tab(AL_CONST void *v1, AL_CONST void *v2)
 {
-   int ret = (int)((AL_CONST unsigned long)d1 - (AL_CONST unsigned long)d2);
+   int i1 = ((OBJ_LIST *)v1)->index;
+   int i2 = ((OBJ_LIST *)v2)->index;
+   DIALOG_PLAYER *dp = ((OBJ_LIST *)v1)->dp;
+   if (dp->focus_obj >= 0)
+   {
+      int i = dp->focus_obj;
+      if (i1 > i && i2 <= i)
+         return 1;
+      if (i2 > i && i1 <= i)
+         return -1;
+   }
+   return i2 - i1;
+}
 
-   if (ret < 0)
-      ret += 0x10000;
 
-   return ret;
+
+/* cmp_side:
+ *  Comparison function for arrow key movement.
+ *  The on_side parameters tell if the two dialogs are on the side of the current
+ *  dialog in which a key was pressed, the collide_with parameters tell if there
+ *  is a collision with the dialogs.
+ */
+static int cmp_side (int on_side_1, int on_side_2, int collide_with_1, int collide_with_2)
+{
+   if (collide_with_1 && !collide_with_2)
+      return -1;
+   if (collide_with_2 && !collide_with_1)
+      return 1;
+   if (on_side_1 && !on_side_2)
+      return -1;
+   if (on_side_2 && !on_side_1)
+      return 1;
+   return 0;
 }
 
 
@@ -550,14 +573,25 @@
 /* cmp_right:
  *  Comparison function for right arrow key movement.
  */
-static int cmp_right(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
+static int cmp_right (AL_CONST void *v1, AL_CONST void *v2)
 {
-   int ret = (d2->x - d1->x) + ABS(d1->y - d2->y) * 8;
-
-   if (d1->x >= d2->x)
-      ret += 0x10000;
-
-   return ret;
+   DIALOG *d1 = active_dialog_player->dialog + ((OBJ_LIST *)v1)->index;
+   DIALOG *d2 = active_dialog_player->dialog + ((OBJ_LIST *)v2)->index;
+   DIALOG_PLAYER *dp = ((OBJ_LIST *)v1)->dp;
+   if (dp->focus_obj >= 0)
+   {
+      DIALOG *d = dp->dialog + dp->focus_obj;
+      int on_side_1 = d1->x >= d->x + d->w;
+      int on_side_2 = d2->x >= d->x + d->w;
+      int collide_with_1 = (d1->y + d1->h > d->y && d1->y < d->y + d->h);
+      int collide_with_2 = (d2->y + d2->h > d->y && d2->y < d->y + d->h);
+      int ret = cmp_side (on_side_1, on_side_2, collide_with_1, collide_with_2);
+      if(ret)
+         return ret;
+      if (d1->x == d2->x)
+         return ABS(d1->y - d->y) - ABS(d2->y - d->y);
+   }
+   return d1->x - d2->x;
 }
 
 
@@ -565,14 +599,26 @@
 /* cmp_left:
  *  Comparison function for left arrow key movement.
  */
-static int cmp_left(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
+static int cmp_left (AL_CONST void *v1, AL_CONST void *v2)
 {
-   int ret = (d1->x - d2->x) + ABS(d1->y - d2->y) * 8;
-
-   if (d1->x <= d2->x)
-      ret += 0x10000;
-
-   return ret;
+   DIALOG *d1 = active_dialog_player->dialog + ((OBJ_LIST *)v1)->index;
+   DIALOG *d2 = active_dialog_player->dialog + ((OBJ_LIST *)v2)->index;
+   DIALOG_PLAYER *dp = ((OBJ_LIST *)v1)->dp;
+   if (dp->focus_obj >= 0)
+   {
+      DIALOG *d = dp->dialog + dp->focus_obj;
+      
+      int on_side_1 = d1->x + d1->w <= d->x;
+      int on_side_2 = d2->x + d2->w <= d->x;
+      int collide_with_1 = (d1->y + d1->h > d->y && d1->y < d->y + d->h);
+      int collide_with_2 = (d2->y + d2->h > d->y && d2->y < d->y + d->h);
+      int ret = cmp_side (on_side_1, on_side_2, collide_with_1, collide_with_2);
+      if(ret)
+         return ret;
+      if (d1->x + d1->w == d2->x + d2->w)
+         return ABS(d1->y - d->y) - ABS(d2->y - d->y);
+   }
+   return d2->x + d2->w - d1->x - d1->w;
 }
 
 
@@ -580,14 +626,26 @@
 /* cmp_down:
  *  Comparison function for down arrow key movement.
  */
-static int cmp_down(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
+static int cmp_down (AL_CONST void *v1, AL_CONST void *v2)
 {
-   int ret = (d2->y - d1->y) + ABS(d1->x - d2->x) * 8;
-
-   if (d1->y >= d2->y)
-      ret += 0x10000;
-
-   return ret;
+   DIALOG *d1 = active_dialog_player->dialog + ((OBJ_LIST *)v1)->index;
+   DIALOG *d2 = active_dialog_player->dialog + ((OBJ_LIST *)v2)->index;
+   DIALOG_PLAYER *dp = ((OBJ_LIST *)v1)->dp;
+   if (dp->focus_obj >= 0)
+   {
+      DIALOG *d = dp->dialog + dp->focus_obj;
+      
+      int on_side_1 = d1->y >= d->y + d->h;
+      int on_side_2 = d2->y >= d->y + d->h;
+      int collide_with_1 = (d1->x + d1->w > d->x && d1->x < d->x + d->w);
+      int collide_with_2 = (d2->x + d2->w > d->x && d2->x < d->x + d->w);
+      int ret = cmp_side (on_side_1, on_side_2, collide_with_1, collide_with_2);
+      if(ret)
+         return ret;
+      if (d1->y == d2->y)
+         return ABS(d1->x - d->x) - ABS(d2->x - d->x);
+   }
+   return d1->y - d2->y;
 }
 
 
@@ -595,14 +653,26 @@
 /* cmp_up:
  *  Comparison function for up arrow key movement.
  */
-static int cmp_up(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
+static int cmp_up (AL_CONST void *v1, AL_CONST void *v2)
 {
-   int ret = (d1->y - d2->y) + ABS(d1->x - d2->x) * 8;
-
-   if (d1->y <= d2->y)
-      ret += 0x10000;
-
-   return ret;
+   DIALOG *d1 = active_dialog_player->dialog + ((OBJ_LIST *)v1)->index;
+   DIALOG *d2 = active_dialog_player->dialog + ((OBJ_LIST *)v2)->index;
+   DIALOG_PLAYER *dp = ((OBJ_LIST *)v1)->dp;
+   if (dp->focus_obj >= 0)
+   {
+      DIALOG *d = dp->dialog + dp->focus_obj;
+      
+      int on_side_1 = d1->y + d1->h <= d->y;
+      int on_side_2 = d2->y + d2->h <= d->y;
+      int collide_with_1 = (d1->x + d1->w > d->x && d1->x < d->x + d->w);
+      int collide_with_2 = (d2->x + d2->w > d->x && d2->x < d->x + d->w);
+      int ret = cmp_side (on_side_1, on_side_2, collide_with_1, collide_with_2);
+      if(ret)
+         return ret;
+      if (d1->y + d1->h == d2->y + d2->h)
+         return ABS(d1->x - d->x) - ABS(d2->x - d->x);
+   }
+   return d2->y + d2->h - d1->y - d1->h;
 }
 
 
@@ -611,9 +681,9 @@
  *  Handles arrow key and tab movement through a dialog, deciding which
  *  object should be given the input focus.
  */
-static int move_focus(DIALOG *d, int ascii, int scan, int *focus_obj)
+static int move_focus(DIALOG_PLAYER *dp, int ascii, int scan)
 {
-   int (*cmp)(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2);
+   int (*cmp)(AL_CONST void *v1, AL_CONST void *v2);
    OBJ_LIST obj[MAX_OBJECTS];
    int obj_count = 0;
    int fobj, c;
@@ -630,13 +700,10 @@
    }
 
    /* fill temporary table */
-   for (c=0; d[c].proc; c++) {
-      if ((*focus_obj < 0) || (c != *focus_obj)) {
+   for (c=0; dp->dialog[c].proc; c++) {
+      if (c != dp->focus_obj && !(dp->dialog[c].flags & (D_HIDDEN | D_DISABLED))) {
 	 obj[obj_count].index = c;
-	 if (*focus_obj >= 0)
-	    obj[obj_count].diff = cmp(d+*focus_obj, d+c);
-	 else
-	    obj[obj_count].diff = c;
+	 obj[obj_count].dp = dp;
 	 obj_count++;
 	 if (obj_count >= MAX_OBJECTS)
 	    break;
@@ -644,13 +711,13 @@
    }
 
    /* sort table */
-   qsort(obj, obj_count, sizeof(OBJ_LIST), obj_list_cmp);
+   qsort(obj, obj_count, sizeof(OBJ_LIST), cmp);
 
    /* find an object to give the focus to */
-   fobj = *focus_obj;
+   fobj = dp->focus_obj;
    for (c=0; c<obj_count; c++) {
-      res |= offer_focus(d, obj[c].index, focus_obj, FALSE);
-      if (fobj != *focus_obj)
+      res |= offer_focus(dp->dialog, obj[c].index, &dp->focus_obj, FALSE);
+      if (fobj != dp->focus_obj)
 	 break;
    } 
 
@@ -1154,7 +1221,7 @@
       }
 
       /* move focus around the dialog */
-      player->res |= move_focus(player->dialog, cascii, cscan, &player->focus_obj);
+      player->res |= move_focus(player, cascii, cscan);
    }
 
    /* redraw? */


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/