Re: [AD] new GUI focus selection algorithm

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


> For example my program which made me think about the focusing at all:
>
>     Start
>   Fullscreen
>      End
>
> It will go up from Start to End, or down from End to Start, with the
> wrapping. With just a penalty, it will jump to Fullscreen in both cases.
> (The original code had negative distances, that's why it could just add
> 0x10000 to achieve the wrapping.)

Ok, I have just totally overlooked the wrapping requirement until now :-)

And thinking a bit more about it, I now understand your proposition to use 
the actual dimensions of the screen rather than the somewhat arbitrary 
current wrapping value.

Revised patch attached.

-- 
Eric Botcazou
--- /home/eric/cvs/allegro/src/gui.c	Sat Nov 29 08:25:22 2003
+++ allegro/src/gui.c	Sun Nov 30 09:58:45 2003
@@ -18,6 +18,8 @@
  *
  *      Eric Botcazou added the support for non-blocking menus.
  *
+ *      Elias Pschernig and Sven Sandberg improved the focus algorithm.
+ *
  *      See readme.txt for copyright information.
  */
 
@@ -506,6 +508,16 @@ typedef struct OBJ_LIST
 } OBJ_LIST;
 
 
+/* Weight ratio between the orthogonal direction and the main direction
+   when calculating the distance for the focus algorithm. */
+#define DISTANCE_RATIO  8
+
+/* Maximum size (in bytes) of a dialog array. */
+#define MAX_SIZE  0x10000  /* 64 kb */
+
+enum axis { X_AXIS, Y_AXIS };
+
+
 
 /* obj_list_cmp:
  *  Callback function for qsort().
@@ -524,8 +536,9 @@ static int cmp_tab(AL_CONST DIALOG *d1, 
 {
    int ret = (int)((AL_CONST unsigned long)d2 - (AL_CONST unsigned long)d1);
 
+   /* Wrap around if d2 is before d1 in the dialog array. */
    if (ret < 0)
-      ret += 0x10000;
+      ret += MAX_SIZE;
 
    return ret;
 }
@@ -539,25 +552,83 @@ static int cmp_shift_tab(AL_CONST DIALOG
 {
    int ret = (int)((AL_CONST unsigned long)d1 - (AL_CONST unsigned long)d2);
 
+   /* Wrap around if d2 is after d1 in the dialog array. */
    if (ret < 0)
-      ret += 0x10000;
+      ret += MAX_SIZE;
 
    return ret;
 }
 
 
 
+/* min_dist:
+ *  Returns the minimum distance between dialogs 'd1' and 'd2'. 'main_axis'
+ *  is taken account to give different weights to the axes in the distance
+ *  formula, as well as to shift the actual position of 'd2' along the axis
+ *  by the amount specified by 'bias'.
+ */
+static int min_dist(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2, enum axis main_axis, int bias)
+{
+   int x_left = d1->x - d2->x - d2->w + 1;
+   int x_right = d2->x - d1->x - d1->w + 1;
+   int y_top = d1->y - d2->y - d2->h + 1;
+   int y_bottom = d2->y - d1->y - d1->h + 1;
+
+   if (main_axis == X_AXIS) {
+      x_left -= bias;
+      x_right += bias;
+      y_top *= DISTANCE_RATIO;
+      y_bottom *= DISTANCE_RATIO;
+   }
+   else {
+      x_left *= DISTANCE_RATIO;
+      x_right *= DISTANCE_RATIO;
+      y_top -= bias;
+      y_bottom += bias;
+   }
+
+   if (x_left > 0) { /* d2 is left of d1 */
+      if (y_top > 0)  /* d2 is above d1 */
+         return x_left + y_top;
+      else if (y_bottom > 0)  /* d2 is below d1 */
+         return x_left + y_bottom;
+      else  /* vertically overlapping */
+         return x_left;
+   }
+   else if (x_right > 0) { /* d2 is right of d1 */
+      if (y_top > 0)  /* d2 is above d1 */
+         return x_right + y_top;
+      else if (y_bottom > 0)  /* d2 is below d1 */
+         return x_right + y_bottom;
+      else  /* vertically overlapping */
+         return x_right;
+   }
+   /* horizontally overlapping */
+   else if (y_top > 0)  /* d2 is above d1 */
+      return y_top;
+   else if (y_bottom > 0)  /* d2 is below d1 */
+      return y_bottom;
+   else  /* overlapping */
+      return 0;
+}
+
+
+
 /* cmp_right:
  *  Comparison function for right arrow key movement.
  */
 static int cmp_right(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
 {
-   int ret = (d2->x - d1->x) + ABS(d1->y - d2->y) * 8;
+   int bias;
 
-   if (d1->x >= d2->x)
-      ret += 0x10000;
+   /* Wrap around if d2 is not fully contained in the half-plan
+      delimited by d1's right edge and not containing it. */
+   if (d2->x < d1->x + d1->w)
+      bias = +SCREEN_W;
+   else
+      bias = 0;
 
-   return ret;
+   return min_dist(d1, d2, X_AXIS, bias);
 }
 
 
@@ -567,12 +638,16 @@ static int cmp_right(AL_CONST DIALOG *d1
  */
 static int cmp_left(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
 {
-   int ret = (d1->x - d2->x) + ABS(d1->y - d2->y) * 8;
+   int bias;
 
-   if (d1->x <= d2->x)
-      ret += 0x10000;
+   /* Wrap around if d2 is not fully contained in the half-plan
+      delimited by d1's left edge and not containing it. */
+   if (d2->x + d2->w > d1->x)
+      bias = -SCREEN_W;
+   else
+      bias = 0;
 
-   return ret;
+   return min_dist(d1, d2, X_AXIS, bias);
 }
 
 
@@ -582,12 +657,16 @@ static int cmp_left(AL_CONST DIALOG *d1,
  */
 static int cmp_down(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
 {
-   int ret = (d2->y - d1->y) + ABS(d1->x - d2->x) * 8;
+   int bias;
 
-   if (d1->y >= d2->y)
-      ret += 0x10000;
+   /* Wrap around if d2 is not fully contained in the half-plan
+      delimited by d1's bottom edge and not containing it. */
+   if (d2->y < d1->y + d1->h)
+      bias = +SCREEN_H;
+   else
+      bias = 0;
 
-   return ret;
+   return min_dist(d1, d2, Y_AXIS, bias);
 }
 
 
@@ -597,12 +676,16 @@ static int cmp_down(AL_CONST DIALOG *d1,
  */
 static int cmp_up(AL_CONST DIALOG *d1, AL_CONST DIALOG *d2)
 {
-   int ret = (d1->y - d2->y) + ABS(d1->x - d2->x) * 8;
+   int bias;
 
-   if (d1->y <= d2->y)
-      ret += 0x10000;
+   /* Wrap around if d2 is not fully contained in the half-plan 
+      delimited by d1's top edge and not containing it. */
+   if (d2->y + d2->h > d1->y)
+      bias = -SCREEN_H;
+   else
+      bias = 0;
 
-   return ret;
+   return min_dist(d1, d2, Y_AXIS, bias);
 }
 
 
@@ -631,7 +714,8 @@ static int move_focus(DIALOG *d, int asc
 
    /* fill temporary table */
    for (c=0; d[c].proc; c++) {
-      if ((*focus_obj < 0) || (c != *focus_obj)) {
+      if (((*focus_obj < 0) || (c != *focus_obj))
+	  && !(d[c].flags & (D_DISABLED | D_HIDDEN))) {
 	 obj[obj_count].index = c;
 	 if (*focus_obj >= 0)
 	    obj[obj_count].diff = cmp(d+*focus_obj, d+c);


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