Fix invalid region rectangles, sanitize dynamic region size calulations
authorJulian Eisel <eiseljulian@gmail.com>
Wed, 15 Jan 2020 12:03:51 +0000 (13:03 +0100)
committerJulian Eisel <eiseljulian@gmail.com>
Wed, 15 Jan 2020 14:05:11 +0000 (15:05 +0100)
It was too easy to end up with invalid region rectangles and we were
badly protected against them, so that they were hard to catch.
In fact we still create a main region for the top-bar, which ended up
getting a region height of -1. While this doesn't seem to have caused
issues in practice, we should prevent them entirely.

So idea was that at the end of region layout resolving,
`BLI_rcti_is_valid()` should return `true` for the region rectangle.
Further changes here ensure this is true: The `RGN_FLAG_TOO_SMALL` flag
is now set whenever there is not enough space for a region or if it
would get a size of zero or less.

Note: Should the assert fail, please do not just disable it and try to
actually address the root of the issue.

source/blender/editors/screen/area.c
source/blender/windowmanager/intern/wm_draw.c

index 3c5400bd021ebc2fbd1ee97bae98b95b58764ae5..a4318db0cb5ea6b073696078bb3e39dbf3842043 100644 (file)
@@ -1244,6 +1244,20 @@ static void region_rect_recursive(
     alignment = RGN_ALIGN_NONE;
   }
 
+  /* If both the ARegion.sizex/y and the prefsize are 0, the region is tagged as too small, even
+   * before the layout for dynamic regions is created. #wm_draw_window_offscreen() allows the
+   * layout to be created despite the RGN_FLAG_TOO_SMALL flag being set. But there may still be
+   * regions that don't have a separate ARegionType.layout callback. For those, set a default
+   * prefsize so they can become visible. */
+  if ((ar->flag & RGN_FLAG_DYNAMIC_SIZE) && !(ar->type->layout)) {
+    if ((ar->sizex == 0) && (ar->type->prefsizex == 0)) {
+      ar->type->prefsizex = AREAMINX;
+    }
+    if ((ar->sizey == 0) && (ar->type->prefsizey == 0)) {
+      ar->type->prefsizey = HEADERY;
+    }
+  }
+
   /* prefsize, taking into account DPI */
   int prefsizex = UI_DPI_FAC * ((ar->sizex > 1) ? ar->sizex + 0.5f : ar->type->prefsizex);
   int prefsizey;
@@ -1323,7 +1337,7 @@ static void region_rect_recursive(
   else if (alignment == RGN_ALIGN_TOP || alignment == RGN_ALIGN_BOTTOM) {
     rcti *winrct = (ar->overlap) ? overlap_remainder : remainder;
 
-    if (rct_fits(winrct, 'v', prefsizey) < 0) {
+    if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) {
       ar->flag |= RGN_FLAG_TOO_SMALL;
     }
     else {
@@ -1348,7 +1362,7 @@ static void region_rect_recursive(
   else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
     rcti *winrct = (ar->overlap) ? overlap_remainder : remainder;
 
-    if (rct_fits(winrct, 'h', prefsizex) < 0) {
+    if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) {
       ar->flag |= RGN_FLAG_TOO_SMALL;
     }
     else {
@@ -1478,9 +1492,12 @@ static void region_rect_recursive(
         ar->winrct.xmin = ar->winrct.xmax;
         break;
       case RGN_ALIGN_LEFT:
+        ar->winrct.xmax = ar->winrct.xmin;
+        break;
       default:
         /* prevent winrct to be valid */
         ar->winrct.xmax = ar->winrct.xmin;
+        BLI_rcti_sanitize(&ar->winrct);
         break;
     }
   }
@@ -1500,6 +1517,8 @@ static void region_rect_recursive(
     *overlap_remainder = *remainder;
   }
 
+  BLI_assert(BLI_rcti_is_valid(&ar->winrct));
+
   region_rect_recursive(sa, ar->next, remainder, overlap_remainder, quad);
 
   /* Tag for redraw if size changes. */
index 09b7d89fc2b489344d7df1bfbaf5c9ab95b44263..14d4e05b77ab4f9972b3f79c57b114ac910ba8d8 100644 (file)
@@ -581,7 +581,14 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
 
     /* Compute UI layouts for dynamically size regions. */
     for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
-      if (ar->visible && ar->do_draw && ar->type && ar->type->layout) {
+      /* Dynamic region may have been flagged as too small because their size on init is 0.
+       * ARegion.visible is false then, as expected. The layout should still be created then, so
+       * the region size can be updated (it may turn out to be not too small then). */
+      const bool ignore_visibility = (ar->flag & RGN_FLAG_DYNAMIC_SIZE) &&
+                                     (ar->flag & RGN_FLAG_TOO_SMALL) &&
+                                     !(ar->flag & RGN_FLAG_HIDDEN);
+
+      if ((ar->visible || ignore_visibility) && ar->do_draw && ar->type && ar->type->layout) {
         CTX_wm_region_set(C, ar);
         ED_region_do_layout(C, ar);
         CTX_wm_region_set(C, NULL);