UI: better support for dynamically sized regions in topbar.
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Sun, 29 Apr 2018 10:24:08 +0000 (12:24 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Sun, 29 Apr 2018 20:11:12 +0000 (22:11 +0200)
Dynamically sized regions in the topbar were flickering due to only updating
their size after redraws. Now there is an optional layout() callback for
all regions in an area to do UI layout first, then refresh the region layout,
and then do the actual drawing for each region.

Task T54753

source/blender/blenkernel/BKE_screen.h
source/blender/editors/include/ED_screen.h
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_region_popup.c
source/blender/editors/screen/area.c
source/blender/editors/screen/screen_edit.c
source/blender/editors/space_topbar/space_topbar.c
source/blender/makesdna/DNA_screen_types.h
source/blender/windowmanager/intern/wm_draw.c

index 17cca92c1fcfccf81cbae724213d9e95e505ad13..36708cb11a4e35d596b570c4695335faf0fb6951 100644 (file)
@@ -140,8 +140,8 @@ typedef struct ARegionType {
        void (*exit)(struct wmWindowManager *, struct ARegion *);
        /* draw entirely, view changes should be handled here */
        void (*draw)(const struct bContext *, struct ARegion *);
-       /* optional, refresh popup before drawing */
-       void (*refresh)(const struct bContext *, struct ARegion *);
+       /* optional, compute button layout before drawing for dynamic size */
+       void (*layout)(const struct bContext *, struct ARegion *);
        /* snap the size of the region (can be NULL for no snapping). */
        int (*snap_size)(const struct ARegion *ar, int size, int axis);
        /* contextual changes should be handled here */
index b5f1d9470768471278e1e96782ae8b3e398d1829..a1c429043ccf67e75b3678c3088221ad09774e1a 100644 (file)
@@ -65,6 +65,7 @@ struct wmMsgSubscribeValue;
 void    ED_region_do_listen(
         struct bScreen *sc, struct ScrArea *sa, struct ARegion *ar,
         struct wmNotifier *note, const Scene *scene);
+void    ED_region_do_layout(struct bContext *C, struct ARegion *ar);
 void    ED_region_do_draw(struct bContext *C, struct ARegion *ar);
 void    ED_region_exit(struct bContext *C, struct ARegion *ar);
 void    ED_region_pixelspace(struct ARegion *ar);
@@ -81,6 +82,8 @@ void    ED_region_panels(
             const bool vertical);
 void    ED_region_header_init(struct ARegion *ar);
 void    ED_region_header(const struct bContext *C, struct ARegion *ar);
+void    ED_region_header_layout(const struct bContext *C, struct ARegion *ar);
+void    ED_region_header_draw(const struct bContext *C, struct ARegion *ar);
 void    ED_region_cursor_set(struct wmWindow *win, struct ScrArea *sa, struct ARegion *ar);
 void    ED_region_toggle_hidden(struct bContext *C, struct ARegion *ar);
 void    ED_region_visibility_change_update(struct bContext *C, struct ARegion *ar);
@@ -135,6 +138,7 @@ int     ED_area_headersize(void);
 int     ED_area_global_size_y(const ScrArea *area);
 bool    ED_area_is_global(const ScrArea *area);
 int     ED_region_global_size_y(void);
+void    ED_area_update_region_sizes(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area);
 
 ScrArea *ED_screen_areas_iter_first(const struct wmWindow *win, const bScreen *screen);
 ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area);
index 7fd48ce081ccb90e3e8e2e28e7f262bafcf0bc15..20c4552e6b29a249cb1ef8d866acc72f2a51da6b 100644 (file)
@@ -475,6 +475,8 @@ uiBlock *UI_block_begin(const struct bContext *C, struct ARegion *region, const
 void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], int r_xy[2]);
 void UI_block_end(const struct bContext *C, uiBlock *block);
 void UI_block_draw(const struct bContext *C, struct uiBlock *block);
+void UI_blocklist_update_window_matrix(const struct bContext *C, const struct ListBase *lb);
+void UI_blocklist_draw(const struct bContext *C, const struct ListBase *lb);
 void UI_block_update_from_old(const struct bContext *C, struct uiBlock *block);
 
 uiBlock *UI_block_find_in_region(const char *name, struct ARegion *ar);
index eb69ccef2e49d5853d49a6677fd909dec01889d9..99387a47670fd85d9300d3f16379f8bf42895729 100644 (file)
@@ -224,6 +224,26 @@ void ui_region_to_window(const ARegion *ar, int *x, int *y)
        *y += ar->winrct.ymin;
 }
 
+static void ui_update_window_matrix(const wmWindow *window, const ARegion *region, uiBlock *block)
+{
+       /* window matrix and aspect */
+       if (region && region->visible) {
+               /* Get projection matrix which includes View2D translation and zoom. */
+               gpuGetProjectionMatrix(block->winmat);
+               block->aspect = 2.0f / fabsf(region->winx * block->winmat[0][0]);
+       }
+       else {
+               /* No subwindow created yet, for menus for example, so we use the main
+                * window instead, since buttons are created there anyway. */
+               int width = WM_window_pixels_x(window);
+               int height = WM_window_pixels_y(window);
+               rcti winrct = {0, width - 1, 0, height - 1};
+
+               wmGetProjectionMatrix(block->winmat, &winrct);
+               block->aspect = 2.0f / fabsf(width * block->winmat[0][0]);
+       }
+}
+
 /**
  * Popups will add a margin to #ARegion.winrct for shadow,
  * for interactivity (point-inside tests for eg), we want the winrct without the margin added.
@@ -2670,6 +2690,27 @@ void UI_block_free(const bContext *C, uiBlock *block)
        MEM_freeN(block);
 }
 
+void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb)
+{
+       ARegion *region = CTX_wm_region(C);
+       wmWindow *window = CTX_wm_window(C);
+
+       for (uiBlock *block = lb->first; block; block = block->next) {
+               if (block->active) {
+                       ui_update_window_matrix(window, region, block);
+               }
+       }
+}
+
+void UI_blocklist_draw(const bContext *C, const ListBase *lb)
+{
+       for (uiBlock *block = lb->first; block; block = block->next) {
+               if (block->active) {
+                       UI_block_draw(C, block);
+               }
+       }
+}
+
 /* can be called with C==NULL */
 void UI_blocklist_free(const bContext *C, ListBase *lb)
 {
@@ -2755,25 +2796,13 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh
        if (region)
                UI_block_region_set(block, region);
 
-       /* window matrix and aspect */
-       if (region && region->visible) {
-               gpuGetProjectionMatrix(block->winmat);
+       /* Set window matrix and aspect for region and OpenGL state. */
+       ui_update_window_matrix(window, region, block);
 
-               block->aspect = 2.0f / fabsf(region->winx * block->winmat[0][0]);
-       }
-       else {
-               /* no subwindow created yet, for menus for example, so we
-                * use the main window instead, since buttons are created
-                * there anyway */
-               int width = WM_window_pixels_x(window);
-               int height = WM_window_pixels_y(window);
-               rcti winrct = {0, width -1, 0, height - 1};
-
-               wmGetProjectionMatrix(block->winmat, &winrct);
-
-               block->aspect = 2.0f / fabsf(width * block->winmat[0][0]);
+       /* Tag as popup menu if not created within a region. */
+       if (!(region && region->visible)) {
                block->auto_open = true;
-               block->flag |= UI_BLOCK_LOOP; /* tag as menu */
+               block->flag |= UI_BLOCK_LOOP;
        }
 
        return block;
index f397c62920b32b5d1020bfd2656968a27ad0caf7..4f8e9090a4a652247a4225f7218116cf510dac2c 100644 (file)
@@ -682,7 +682,7 @@ uiPopupBlockHandle *ui_popup_block_create(
 
        memset(&type, 0, sizeof(ARegionType));
        type.draw = ui_block_region_draw;
-       type.refresh = ui_block_region_refresh;
+       type.layout = ui_block_region_refresh;
        type.regionid = RGN_TYPE_TEMPORARY;
        ar->type = &type;
 
index 6e45b184478968f7875cb451700abb3ebbc7aa28..5e68ad1218d22d422de8ed87fc70aa06e38f5c87 100644 (file)
@@ -500,6 +500,27 @@ void ED_area_do_msg_notify_tag_refresh(
        ED_area_tag_refresh(sa);
 }
 
+/* only exported for WM */
+void ED_region_do_layout(bContext *C, ARegion *ar)
+{
+       /* This is optional, only needed for dynamically sized regions. */
+       ScrArea *sa = CTX_wm_area(C);
+       ARegionType *at = ar->type;
+
+       if (!at->layout) {
+               return;
+       }
+
+       if (at->do_lock) {
+               return;
+       }
+
+       ar->do_draw |= RGN_DRAWING;
+
+       UI_SetTheme(sa ? sa->spacetype : 0, at->regionid);
+       at->layout(C, ar);
+}
+
 /* only exported for WM */
 void ED_region_do_draw(bContext *C, ARegion *ar)
 {
@@ -1187,9 +1208,8 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti
        if (ar->next == NULL && alignment != RGN_ALIGN_QSPLIT)
                alignment = RGN_ALIGN_NONE;
 
-       /* prefsize, for header we stick to exception (prevent dpi rounding error) */
-       const float sizex_dpi_fac = (ar->flag & RGN_SIZEX_DPI_APPLIED) ? 1.0f : UI_DPI_FAC;
-       prefsizex = sizex_dpi_fac * ((ar->sizex > 1) ? ar->sizex + 0.5f : ar->type->prefsizex);
+       /* prefsize, taking into account DPI */
+       prefsizex = UI_DPI_FAC * ((ar->sizex > 1) ? ar->sizex + 0.5f : ar->type->prefsizex);
 
        if (ar->regiontype == RGN_TYPE_HEADER) {
                prefsizey = ED_area_headersize();
@@ -1514,8 +1534,12 @@ static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *hand
        }
 }
 
-void screen_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *area)
+void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *area)
 {
+       if (!(area->flag & AREA_FLAG_REGION_SIZE_UPDATE)) {
+               return;
+       }
+
        const int size_x = WM_window_pixels_x(win);
        const int size_y = WM_window_pixels_y(win);
        rcti rect;
@@ -1898,6 +1922,23 @@ static ThemeColorID region_background_color_id(const bContext *C, const ARegion
        }
 }
 
+static void region_clear_color(const bContext *C, const ARegion *ar, ThemeColorID colorid)
+{
+       if (ar->overlap) {
+               /* view should be in pixelspace */
+               UI_view2d_view_restore(C);
+
+               float back[4];
+               UI_GetThemeColor4fv(colorid, back);
+               glClearColor(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]);
+               glClear(GL_COLOR_BUFFER_BIT);
+       }
+       else {
+               UI_ThemeClearColor(colorid);
+               glClear(GL_COLOR_BUFFER_BIT);
+       }
+}
+
 void ED_region_panels(const bContext *C, ARegion *ar, const char *context, int contextnr, const bool vertical)
 {
        const WorkSpace *workspace = CTX_wm_workspace(C);
@@ -2124,20 +2165,7 @@ void ED_region_panels(const bContext *C, ARegion *ar, const char *context, int c
                }
        }
 
-       /* clear */
-       if (ar->overlap) {
-               /* view should be in pixelspace */
-               UI_view2d_view_restore(C);
-
-               float back[4];
-               UI_GetThemeColor4fv((ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK, back);
-               glClearColor(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]);
-               glClear(GL_COLOR_BUFFER_BIT);
-       }
-       else {
-               UI_ThemeClearColor((ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK);
-               glClear(GL_COLOR_BUFFER_BIT);
-       }
+       region_clear_color(C, ar, (ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK);
        
        /* reset line width for drawing tabs */
        glLineWidth(1.0f);
@@ -2171,7 +2199,7 @@ void ED_region_panels_init(wmWindowManager *wm, ARegion *ar)
        WM_event_add_keymap_handler(&ar->handlers, keymap);
 }
 
-void ED_region_header(const bContext *C, ARegion *ar)
+void ED_region_header_layout(const bContext *C, ARegion *ar)
 {
        uiStyle *style = UI_style_get_dpi();
        uiBlock *block;
@@ -2183,10 +2211,6 @@ void ED_region_header(const bContext *C, ARegion *ar)
        const int start_ofs = 0.4f * UI_UNIT_X;
        bool region_layout_based = ar->flag & RGN_FLAG_DYNAMIC_SIZE;
 
-       /* clear */
-       UI_ThemeClearColor(region_background_color_id(C, ar));
-       glClear(GL_COLOR_BUFFER_BIT);
-       
        /* set view2d view matrix for scrolling (without scrollers) */
        UI_view2d_view_ortho(&ar->v2d);
 
@@ -2221,27 +2245,50 @@ void ED_region_header(const bContext *C, ARegion *ar)
                if (xco > maxco)
                        maxco = xco;
 
-               if (region_layout_based && (ar->sizex != (maxco + start_ofs))) {
+               int new_sizex = (maxco + start_ofs) / UI_DPI_FAC;
+
+               if (region_layout_based && (ar->sizex != new_sizex)) {
                        /* region size is layout based and needs to be updated */
                        ScrArea *sa = CTX_wm_area(C);
 
-                       ar->sizex = maxco + start_ofs;
-                       UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->sizex, ar->winy);
-
+                       ar->sizex = new_sizex;
                        sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
-                       ar->flag |= RGN_SIZEX_DPI_APPLIED;
                }
+
                UI_block_end(C, block);
-               UI_block_draw(C, block);
        }
 
        /* always as last  */
        UI_view2d_totRect_set(&ar->v2d, maxco + (region_layout_based ? 0 : UI_UNIT_X + 80), headery);
 
-       /* restore view matrix? */
+       /* restore view matrix */
+       UI_view2d_view_restore(C);
+}
+
+void ED_region_header_draw(const bContext *C, ARegion *ar)
+{
+       UI_view2d_view_ortho(&ar->v2d);
+
+       /* clear */
+       region_clear_color(C, ar, region_background_color_id(C, ar));
+
+       /* View2D matrix might have changed due to dynamic sized regions. */
+       UI_blocklist_update_window_matrix(C, &ar->uiblocks);
+
+       /* draw blocks */
+       UI_blocklist_draw(C, &ar->uiblocks);
+
+       /* restore view matrix */
        UI_view2d_view_restore(C);
 }
 
+void ED_region_header(const bContext *C, ARegion *ar)
+{
+       /* TODO: remove? */
+       ED_region_header_layout(C, ar);
+       ED_region_header_draw(C, ar);
+}
+
 void ED_region_header_init(ARegion *ar)
 {
        UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy);
index c7d1605cbbfa709680ea0f5e021088593689c0ca..1cadefadd5cf59b3ee37e8af8e59364a6634530c 100644 (file)
@@ -835,36 +835,6 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win)
        screen->context = ed_screen_context;
 }
 
-static bool screen_regions_need_size_refresh(
-        const wmWindow *win, const bScreen *screen)
-{
-       ED_screen_areas_iter(win, screen, area) {
-               if (area->flag & AREA_FLAG_REGION_SIZE_UPDATE) {
-                       return true;
-               }
-       }
-
-       return false;
-}
-
-static void screen_refresh_region_sizes_only(
-        wmWindowManager *wm, wmWindow *win,
-        bScreen *screen)
-{
-       const int window_size_x = WM_window_pixels_x(win);
-       const int window_size_y = WM_window_pixels_y(win);
-       const int screen_size_x = WM_window_screen_pixels_x(win);
-       const int screen_size_y = WM_window_screen_pixels_y(win);
-
-       screen_vertices_scale(win, screen, window_size_x, window_size_y, screen_size_x, screen_size_y);
-
-       ED_screen_areas_iter(win, screen, area) {
-               screen_area_update_region_sizes(wm, win, area);
-               /* XXX hack to force drawing */
-               ED_area_tag_redraw(area);
-       }
-}
-
 /* file read, set all screens, ... */
 void ED_screens_initialize(wmWindowManager *wm)
 {
@@ -887,9 +857,6 @@ void ED_screen_ensure_updated(wmWindowManager *wm, wmWindow *win, bScreen *scree
        if (screen->do_refresh) {
                ED_screen_refresh(wm, win);
        }
-       else if (screen_regions_need_size_refresh(win, screen)) {
-               screen_refresh_region_sizes_only(wm, win, screen);
-       }
 }
 
 
index f2824b3bf702bbce144e47c71e136bafa93553ff..fc76fd9c6381b68f6fe8d218e186e30f9ff39f69 100644 (file)
@@ -132,11 +132,6 @@ static void topbar_main_region_init(wmWindowManager *wm, ARegion *region)
        WM_event_add_keymap_handler(&region->handlers, keymap);
 }
 
-static void topbar_main_region_draw(const bContext *C, ARegion *region)
-{
-       ED_region_header(C, region);
-}
-
 static void topbar_operatortypes(void)
 {
 
@@ -156,11 +151,6 @@ static void topbar_header_region_init(wmWindowManager *UNUSED(wm), ARegion *ar)
        ED_region_header_init(ar);
 }
 
-static void topbar_header_region_draw(const bContext *C, ARegion *ar)
-{
-       ED_region_header(C, ar);
-}
-
 static void topbar_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar,
                                         wmNotifier *wmn, const Scene *UNUSED(scene))
 {
@@ -263,7 +253,8 @@ void ED_spacetype_topbar(void)
        art = MEM_callocN(sizeof(ARegionType), "spacetype topbar main region");
        art->regionid = RGN_TYPE_WINDOW;
        art->init = topbar_main_region_init;
-       art->draw = topbar_main_region_draw;
+       art->layout = ED_region_header_layout;
+       art->draw = ED_region_header_draw;
        art->listener = topbar_main_region_listener;
        art->prefsizex = UI_UNIT_X * 5; /* Mainly to avoid glitches */
        art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
@@ -279,7 +270,8 @@ void ED_spacetype_topbar(void)
        art->listener = topbar_header_listener;
        art->message_subscribe = topbar_header_region_message_subscribe;
        art->init = topbar_header_region_init;
-       art->draw = topbar_header_region_draw;
+       art->layout = ED_region_header_layout;
+       art->draw = ED_region_header_draw;
 
        BLI_addhead(&st->regiontypes, art);
 
index 18986206b2337fe3cd146908506351b11c02fe23..4274f348b2ddce04b09dd476b1be091975306732 100644 (file)
@@ -464,11 +464,6 @@ enum {
         * just big enough to show all its content (if enough space is available).
         * Note that only ED_region_header supports this right now. */
        RGN_FLAG_DYNAMIC_SIZE     = (1 << 2),
-       /* The region width stored in ARegion.sizex already has the DPI
-        * factor applied, skip applying it again (in region_rect_recursive).
-        * XXX Not nice at all. Leaving for now as temporary solution, but
-        * it might cause issues if we change how ARegion.sizex is used... */
-       RGN_SIZEX_DPI_APPLIED       = (1 << 3),
 };
 
 /* region do_draw */
index f111bc3e2d1781b049d7ab84a104867ca04a30fc..685e6f7e216b62b75fed7d408611a550f15f230d 100644 (file)
@@ -489,12 +489,25 @@ GPUViewport *WM_draw_region_get_bound_viewport(ARegion *ar)
 
 static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
 {
+       wmWindowManager *wm = CTX_wm_manager(C);
        bScreen *screen = WM_window_get_active_screen(win);
 
        /* Draw screen areas into own frame buffer. */
        ED_screen_areas_iter(win, screen, sa) {
                CTX_wm_area_set(C, sa);
 
+               /* 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) {
+                               CTX_wm_region_set(C, ar);
+                               ED_region_do_layout(C, ar);
+                               CTX_wm_region_set(C, NULL);
+                       }
+               }
+
+               ED_area_update_region_sizes(wm, win, sa);
+
+               /* Then do actual drawing of regions. */
                for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
                        if (ar->visible && ar->do_draw) {
                                CTX_wm_region_set(C, ar);
@@ -539,11 +552,11 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
                if (ar->visible) {
                        CTX_wm_menu_set(C, ar);
 
-                       if (ar->type && ar->type->refresh) {
-                               /* UI code reads the OpenGL state, but we have to
-                                * refresh beforehand in case the menu size changes. */
+                       if (ar->type && ar->type->layout) {
+                               /* UI code reads the OpenGL state, but we have to refesh
+                                * the UI layout beforehand in case the menu size changes. */
                                wmViewport(&ar->winrct);
-                               ar->type->refresh(C, ar);
+                               ar->type->layout(C, ar);
                        }
 
                        wm_draw_region_buffer_create(ar, false, false);