Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Fri, 13 Jul 2018 08:31:30 +0000 (10:31 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 13 Jul 2018 08:31:30 +0000 (10:31 +0200)
1  2 
source/blender/editors/interface/interface.c

index 20361bde16fde32f4047a0d262bac01da46e6f64,e51822ac1254c6e36d50fad931e549ee0151724d..0b91a1c7aade38e47686914247468b3342a9faa5
@@@ -41,7 -41,6 +41,7 @@@
  #include "DNA_scene_types.h"
  #include "DNA_screen_types.h"
  #include "DNA_userdef_types.h"
 +#include "DNA_workspace_types.h"
  
  #include "BLI_math.h"
  #include "BLI_listbase.h"
@@@ -51,7 -50,6 +51,7 @@@
  
  #include "BLI_utildefines.h"
  
 +#include "BKE_animsys.h"
  #include "BKE_context.h"
  #include "BKE_idprop.h"
  #include "BKE_main.h"
  #include "BKE_screen.h"
  #include "BKE_unit.h"
  
 -#include "BIF_gl.h"
 +#include "GPU_glew.h"
 +#include "GPU_matrix.h"
 +#include "GPU_state.h"
  
  #include "BLF_api.h"
  #include "BLT_translation.h"
  
  #include "UI_interface.h"
 +#include "UI_interface_icons.h"
  
  #include "IMB_imbuf.h"
  
  #include "WM_api.h"
  #include "WM_types.h"
 -#include "wm_subwindow.h"
 +#include "WM_message.h"
  
  #include "RNA_access.h"
  
  #include "BPY_extern.h"
  
 +#include "ED_screen.h"
 +
  #include "IMB_colormanagement.h"
  
 +#include "DEG_depsgraph_query.h"
 +
  #include "interface_intern.h"
  
 +/* prototypes. */
 +static void ui_but_to_pixelrect(struct rcti *rect, const struct ARegion *ar, struct uiBlock *block, struct uiBut *but);
 +
  /* avoid unneeded calls to ui_but_value_get */
  #define UI_BUT_VALUE_UNSET DBL_MAX
  #define UI_GET_BUT_VALUE_INIT(_but, _value) if (_value == DBL_MAX) {  (_value) = ui_but_value_get(_but); } (void)0
@@@ -217,84 -205,6 +217,84 @@@ void ui_region_to_window(const ARegion 
        *y += ar->winrct.ymin;
  }
  
 +static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block)
 +{
 +      int sepr_flex_len = 0;
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              if (but->type == UI_BTYPE_SEPR_SPACER) {
 +                      sepr_flex_len++;
 +              }
 +      }
 +
 +      if (sepr_flex_len == 0) {
 +              return;
 +      }
 +
 +      rcti rect;
 +      ui_but_to_pixelrect(&rect, region, block, block->buttons.last);
 +      const float buttons_width = (float)rect.xmax + UI_HEADER_OFFSET;
 +      const float region_width = (float)region->sizex * U.dpi_fac;
 +
 +      if (region_width <= buttons_width) {
 +              return;
 +      }
 +
 +      /* We could get rid of this loop if we agree on a max number of spacer */
 +      int *spacers_pos = alloca(sizeof(*spacers_pos) * (size_t)sepr_flex_len);
 +      int i = 0;
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              if (but->type == UI_BTYPE_SEPR_SPACER) {
 +                      ui_but_to_pixelrect(&rect, region, block, but);
 +                      spacers_pos[i] = rect.xmax + UI_HEADER_OFFSET;
 +                      i++;
 +              }
 +      }
 +
 +      const float segment_width = region_width / (float)sepr_flex_len;
 +      float offset = 0, remaining_space = region_width - buttons_width;
 +      i = 0;
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              BLI_rctf_translate(&but->rect, offset, 0);
 +              if (but->type == UI_BTYPE_SEPR_SPACER) {
 +                      /* How much the next block overlap with the current segment */
 +                      int overlap = (
 +                              (i == sepr_flex_len - 1) ?
 +                              buttons_width - spacers_pos[i] :
 +                              (spacers_pos[i + 1] - spacers_pos[i]) / 2);
 +                      int segment_end = segment_width * (i + 1);
 +                      int spacer_end = segment_end - overlap;
 +                      int spacer_sta = spacers_pos[i] + offset;
 +                      if (spacer_end > spacer_sta) {
 +                              float step = min_ff(remaining_space, spacer_end - spacer_sta);
 +                              remaining_space -= step;
 +                              offset += step;
 +                      }
 +                      i++;
 +              }
 +      }
 +      ui_block_bounds_calc(block);
 +}
 +
 +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.
@@@ -313,7 -223,7 +313,7 @@@ void ui_region_winrct_get_no_margin(con
  
  /* ******************* block calc ************************* */
  
 -void ui_block_translate(uiBlock *block, int x, int y)
 +void UI_block_translate(uiBlock *block, int x, int y)
  {
        uiBut *but;
  
@@@ -333,7 -243,7 +333,7 @@@ static void ui_block_bounds_calc_text(u
        UI_fontstyle_set(&style->widget);
  
        for (init_col_bt = bt = block->buttons.first; bt; bt = bt->next) {
 -              if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
 +              if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) {
                        j = BLF_width(style->widget.uifont_id, bt->drawstr, sizeof(bt->drawstr));
  
                        if (j > i)
@@@ -423,7 -333,7 +423,7 @@@ static void ui_block_bounds_calc_center
        startx = (xmax * 0.5f) - (width * 0.5f);
        starty = (ymax * 0.5f) - (height * 0.5f);
  
 -      ui_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin);
 +      UI_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -437,7 -347,7 +437,7 @@@ static void ui_block_bounds_calc_center
            block->pie_data.pie_center_spawned[1]
        };
  
 -      ui_block_translate(block, xy[0], xy[1]);
 +      UI_block_translate(block, xy[0], xy[1]);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -497,7 -407,7 +497,7 @@@ static void ui_block_bounds_calc_popup
        rect_bounds.ymax = ymax - UI_POPUP_MENU_TOP;
  
        BLI_rcti_clamp(&rect, &rect_bounds, ofs_dummy);
 -      ui_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin);
 +      UI_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -581,6 -491,86 +581,6 @@@ static int ui_but_calc_float_precision(
        return UI_calc_float_precision(prec, value);
  }
  
 -/* ************** LINK LINE DRAWING  ************* */
 -
 -/* link line drawing is not part of buttons or theme.. so we stick with it here */
 -
 -static void ui_draw_linkline(uiLinkLine *line, int highlightActiveLines, int dashInactiveLines)
 -{
 -      rcti rect;
 -
 -      if (line->from == NULL || line->to == NULL) return;
 -
 -      rect.xmin = BLI_rctf_cent_x(&line->from->rect);
 -      rect.ymin = BLI_rctf_cent_y(&line->from->rect);
 -      rect.xmax = BLI_rctf_cent_x(&line->to->rect);
 -      rect.ymax = BLI_rctf_cent_y(&line->to->rect);
 -
 -      if (dashInactiveLines)
 -              UI_ThemeColor(TH_GRID);
 -      else if (line->flag & UI_SELECT)
 -              glColor3ub(100, 100, 100);
 -      else if (highlightActiveLines && ((line->from->flag & UI_ACTIVE) || (line->to->flag & UI_ACTIVE)))
 -              UI_ThemeColor(TH_TEXT_HI);
 -      else
 -              glColor3ub(0, 0, 0);
 -
 -      ui_draw_link_bezier(&rect);
 -}
 -
 -static void ui_draw_links(uiBlock *block)
 -{
 -      uiBut *but;
 -      uiLinkLine *line;
 -
 -      /* Draw the gray out lines. Do this first so they appear at the
 -       * bottom of inactive or active lines.
 -       * As we go, remember if we see any active or selected lines. */
 -      bool found_selectline = false;
 -      bool found_activeline = false;
 -
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (!(line->from->flag & UI_ACTIVE) && !(line->to->flag & UI_ACTIVE)) {
 -                                      if (line->deactive)
 -                                              ui_draw_linkline(line, 0, true);
 -                              }
 -                              else
 -                                      found_activeline = true;
 -
 -                              if ((line->from->flag & UI_SELECT) || (line->to->flag & UI_SELECT))
 -                                      found_selectline = true;
 -                      }
 -              }
 -      }
 -
 -      /* Draw the inactive lines (lines with neither button being hovered over) */
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (!(line->from->flag & UI_ACTIVE) && !(line->to->flag & UI_ACTIVE)) {
 -                                      if (!line->deactive)
 -                                              ui_draw_linkline(line, 0, false);
 -                              }
 -                      }
 -              }
 -      }
 -
 -      /* Draw any active lines (lines with either button being hovered over).
 -       * Do this last so they appear on top of inactive and gray out lines. */
 -      if (found_activeline) {
 -              for (but = block->buttons.first; but; but = but->next) {
 -                      if (but->type == UI_BTYPE_LINK && but->link) {
 -                              for (line = but->link->lines.first; line; line = line->next) {
 -                                      if ((line->from->flag & UI_ACTIVE) || (line->to->flag & UI_ACTIVE))
 -                                              ui_draw_linkline(line, !found_selectline, false);
 -                              }
 -                      }
 -              }
 -      }
 -}
 -
  /* ************** BLOCK ENDING FUNCTION ************* */
  
  /* NOTE: if but->poin is allocated memory for every defbut, things fail... */
@@@ -623,6 -613,38 +623,6 @@@ uiBut *ui_but_find_new(uiBlock *block_n
        return but_new;
  }
  
 -/* oldbut is being inserted in new block, so we use the lines from new button, and replace button pointers */
 -static void ui_but_update_linklines(uiBlock *block, uiBut *oldbut, uiBut *newbut)
 -{
 -      uiLinkLine *line;
 -      uiBut *but;
 -
 -      /* if active button is UI_BTYPE_LINK */
 -      if (newbut->type == UI_BTYPE_LINK && newbut->link) {
 -
 -              SWAP(uiLink *, oldbut->link, newbut->link);
 -
 -              for (line = oldbut->link->lines.first; line; line = line->next) {
 -                      if (line->to == newbut)
 -                              line->to = oldbut;
 -                      if (line->from == newbut)
 -                              line->from = oldbut;
 -              }
 -      }
 -
 -      /* check all other button links */
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but != newbut && but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (line->to == newbut)
 -                                      line->to = oldbut;
 -                              if (line->from == newbut)
 -                                      line->from = oldbut;
 -                      }
 -              }
 -      }
 -}
 -
  /**
   * \return true when \a but_p is set (only done for active buttons).
   */
@@@ -679,6 -701,8 +679,6 @@@ static bool ui_but_update_from_old_bloc
                but->selend = oldbut->selend;
                but->softmin = oldbut->softmin;
                but->softmax = oldbut->softmax;
 -              but->linkto[0] = oldbut->linkto[0];
 -              but->linkto[1] = oldbut->linkto[1];
                oldbut->active = NULL;
  #endif
  
                        oldbut->a1 = but->a1;
                }
  
 -              ui_but_update_linklines(block, oldbut, but);
 -
                if (!BLI_listbase_is_empty(&block->butstore)) {
                        UI_butstore_register_update(block, oldbut, but);
                }
@@@ -916,7 -942,6 +916,6 @@@ static void ui_menu_block_set_keyaccels
   * but this could be supported */
  void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_strip)
  {
        if (do_strip && (but->flag & UI_BUT_HAS_SEP_CHAR)) {
                char *cpoin = strrchr(but->str, UI_SEP_CHAR);
                if (cpoin) {
                MEM_freeN(butstr_orig);
                but->str = but->strdata;
                but->flag |= UI_BUT_HAS_SEP_CHAR;
 +              but->drawflag |= UI_BUT_HAS_SHORTCUT;
                ui_but_update(but);
        }
  }
  
- static bool ui_but_event_operator_string(
+ /* -------------------------------------------------------------------- */
+ /** \name Find Key Shortcut for Button
+  *
+  * - #ui_but_event_operator_string (and helpers)
+  * - #ui_but_event_property_operator_string
+  * \{ */
+ static bool ui_but_event_operator_string_from_operator(
          const bContext *C, uiBut *but,
          char *buf, const size_t buf_len)
  {
-       MenuType *mt;
+       BLI_assert(but->optype != NULL);
        bool found = false;
+       IDProperty *prop = (but->opptr) ? but->opptr->data : NULL;
  
-       if (but->optype) {
-               IDProperty *prop = (but->opptr) ? but->opptr->data : NULL;
-               if (WM_key_event_operator_string(
-                       C, but->optype->idname, but->opcontext, prop, true,
-                       buf, buf_len))
-               {
-                       found = true;
-               }
+       if (WM_key_event_operator_string(
+                   C, but->optype->idname, but->opcontext, prop, true,
+                   buf, buf_len))
+       {
+               found = true;
        }
-       else if ((mt = UI_but_menutype_get(but))) {
-               IDProperty *prop_menu;
-               IDProperty *prop_menu_name;
+       return found;
+ }
  
-               /* annoying, create a property */
-               IDPropertyTemplate val = {0};
-               prop_menu = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant  */
-               IDP_AddToGroup(prop_menu, (prop_menu_name = IDP_NewString("", "name", sizeof(mt->idname))));
+ static bool ui_but_event_operator_string_from_menu(
+         const bContext *C, uiBut *but,
+         char *buf, const size_t buf_len)
+ {
+       MenuType *mt = UI_but_menutype_get(but);
+       BLI_assert(mt != NULL);
  
-               IDP_AssignString(prop_menu_name, mt->idname, sizeof(mt->idname));
+       bool found = false;
+       IDProperty *prop_menu, *prop_menu_name;
  
-               if (WM_key_event_operator_string(
-                       C, "WM_OT_call_menu", WM_OP_INVOKE_REGION_WIN, prop_menu, true,
-                       buf, buf_len))
-               {
-                       found = true;
-               }
+       /* annoying, create a property */
+       IDPropertyTemplate val = {0};
+       prop_menu = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant  */
+       IDP_AddToGroup(prop_menu, (prop_menu_name = IDP_NewString("", "name", sizeof(mt->idname))));
  
-               IDP_FreeProperty(prop_menu);
-               MEM_freeN(prop_menu);
+       IDP_AssignString(prop_menu_name, mt->idname, sizeof(mt->idname));
+       if (WM_key_event_operator_string(
+               C, "WM_OT_call_menu", WM_OP_INVOKE_REGION_WIN, prop_menu, true,
+               buf, buf_len))
+       {
+               found = true;
+       }
+       IDP_FreeProperty(prop_menu);
+       MEM_freeN(prop_menu);
+       return found;
+ }
+ static bool ui_but_event_operator_string(
+         const bContext *C, uiBut *but,
+         char *buf, const size_t buf_len)
+ {
+       bool found = false;
+       if (but->optype != NULL) {
+               found = ui_but_event_operator_string_from_operator(C, but, buf, buf_len);
+       }
+       else if (UI_but_menutype_get(but) != NULL) {
+               found = ui_but_event_operator_string_from_menu(C, but, buf, buf_len);
        }
  
        return found;
@@@ -1106,6 -1157,8 +1132,8 @@@ static bool ui_but_event_property_opera
        return found;
  }
  
+ /** \} */
  /**
   * This goes in a seemingly weird pattern:
   *
@@@ -1149,7 -1202,7 +1177,7 @@@ static void ui_menu_block_set_keymaps(c
        uiBut *but;
        char buf[128];
  
 -      BLI_assert(block->flag & UI_BLOCK_LOOP);
 +      BLI_assert(block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS));
  
        /* only do it before bounding */
        if (block->rect.xmin != block->rect.xmax)
        }
        else {
                for (but = block->buttons.first; but; but = but->next) {
 -                      if (but->dt != UI_EMBOSS_PULLDOWN) {
 +                      if (block->flag & UI_BLOCK_SHOW_SHORTCUT_ALWAYS) {
 +                              /* Skip icon-only buttons (as used in the toolbar). */
 +                              if (but->drawstr[0] == '\0') {
 +                                      continue;
 +                              }
 +                      }
 +                      else if (but->dt != UI_EMBOSS_PULLDOWN) {
                                continue;
                        }
  
        }
  }
  
 +void ui_but_override_flag(uiBut *but)
 +{
 +      const int override_status = RNA_property_static_override_status(&but->rnapoin, but->rnaprop, but->rnaindex);
 +
 +      if (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN) {
 +              but->flag |= UI_BUT_OVERRIDEN;
 +      }
 +      else {
 +              but->flag &= ~UI_BUT_OVERRIDEN;
 +      }
 +}
 +
  void UI_block_update_from_old(const bContext *C, uiBlock *block)
  {
        uiBut *but_old;
@@@ -1234,7 -1269,6 +1262,7 @@@ void UI_block_end_ex(const bContext *C
  {
        wmWindow *window = CTX_wm_window(C);
        Scene *scene = CTX_data_scene(C);
 +      ARegion *region = CTX_wm_region(C);
        uiBut *but;
  
        BLI_assert(block->active);
                }
  
                ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f);
 +              ui_but_override_flag(but);
 +              if (UI_but_is_decorator(but)) {
 +                      ui_but_anim_decorate_update_from_flag(but);
 +              }
        }
  
  
        if (block->layouts.first) {
                UI_block_layout_resolve(block, NULL, NULL);
        }
 -      ui_block_align_calc(block);
 +      ui_block_align_calc(block, CTX_wm_region(C));
        if ((block->flag & UI_BLOCK_LOOP) && (block->flag & UI_BLOCK_NUMSELECT)) {
                ui_menu_block_set_keyaccels(block); /* could use a different flag to check */
        }
  
 -      if (block->flag & UI_BLOCK_LOOP) {
 +      if (block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS)) {
                ui_menu_block_set_keymaps(C, block);
        }
  
                UI_block_align_end(block);
        }
  
 +      ui_update_flexible_spacing(region, block);
 +
        block->endblock = 1;
  }
  
@@@ -1362,6 -1390,7 +1390,6 @@@ void UI_block_draw(const bContext *C, u
        ARegion *ar;
        uiBut *but;
        rcti rect;
 -      int multisample_enabled;
  
        /* get menu region or area region */
        ar = CTX_wm_menu(C);
        if (!block->endblock)
                UI_block_end(C, block);
  
 -      /* disable AA, makes widgets too blurry */
 -      multisample_enabled = glIsEnabled(GL_MULTISAMPLE);
 -      if (multisample_enabled)
 -              glDisable(GL_MULTISAMPLE);
 -
        /* we set this only once */
 -      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 +      GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
  
        /* scale fonts */
        ui_fontscale(&style.paneltitle.points, block->aspect);
        ui_but_to_pixelrect(&rect, ar, block, NULL);
  
        /* pixel space for AA widgets */
 -      glMatrixMode(GL_PROJECTION);
 -      glPushMatrix();
 -      glMatrixMode(GL_MODELVIEW);
 -      glPushMatrix();
 -      glLoadIdentity();
 +      gpuPushProjectionMatrix();
 +      gpuPushMatrix();
 +      gpuLoadIdentity();
  
        wmOrtho2_region_pixelspace(ar);
  
        /* back */
        if (block->flag & UI_BLOCK_RADIAL)
                ui_draw_pie_center(block);
 +      else if (block->flag & UI_BLOCK_POPOVER)
 +              ui_draw_popover_back(ar, &style, block, &rect);
        else if (block->flag & UI_BLOCK_LOOP)
                ui_draw_menu_back(&style, block, &rect);
        else if (block->panel)
                ui_draw_aligned_panel(&style, block, &rect, UI_panel_category_is_visible(ar));
  
 +      BLF_batch_draw_begin();
 +      UI_icon_draw_cache_begin();
 +      UI_widgetbase_draw_cache_begin();
 +
        /* widgets */
        for (but = block->buttons.first; but; but = but->next) {
                if (!(but->flag & (UI_HIDDEN | UI_SCROLLED))) {
                }
        }
  
 -      /* restore matrix */
 -      glMatrixMode(GL_PROJECTION);
 -      glPopMatrix();
 -      glMatrixMode(GL_MODELVIEW);
 -      glPopMatrix();
 +      UI_widgetbase_draw_cache_end();
 +      UI_icon_draw_cache_end();
 +      BLF_batch_draw_end();
  
 -      if (multisample_enabled)
 -              glEnable(GL_MULTISAMPLE);
 +      /* restore matrix */
 +      gpuPopProjectionMatrix();
 +      gpuPopMatrix();
 +}
 +
 +static void ui_block_message_subscribe(ARegion *ar, struct wmMsgBus *mbus, uiBlock *block)
 +{
 +      uiBut *but_prev = NULL;
 +      /* possibly we should keep the region this block is contained in? */
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              if (but->rnapoin.type && but->rnaprop) {
 +                      /* quick check to avoid adding buttons representing a vector, multiple times. */
 +                      if ((but_prev &&
 +                          (but_prev->rnaprop == but->rnaprop) &&
 +                          (but_prev->rnapoin.type == but->rnapoin.type) &&
 +                          (but_prev->rnapoin.data == but->rnapoin.data) &&
 +                          (but_prev->rnapoin.id.data == but->rnapoin.id.data)) == false)
 +                      {
 +                              /* TODO: could make this into utility function. */
 +                              WM_msg_subscribe_rna(
 +                                      mbus, &but->rnapoin, but->rnaprop,
 +                                      &(const wmMsgSubscribeValue){
 +                                          .owner = ar,
 +                                          .user_data = ar,
 +                                          .notify = ED_region_do_msg_notify_tag_redraw,
 +                                      }, __func__);
 +                              but_prev = but;
 +                      }
 +              }
 +      }
 +}
  
 -      ui_draw_links(block);
 +void UI_region_message_subscribe(ARegion *ar, struct wmMsgBus *mbus)
 +{
 +      for (uiBlock *block = ar->uiblocks.first; block; block = block->next) {
 +              ui_block_message_subscribe(ar, mbus, block);
 +      }
  }
  
  /* ************* EVENTS ************* */
@@@ -1514,18 -1513,6 +1542,18 @@@ int ui_but_is_pushed_ex(uiBut *but, dou
                                        if (*value == (double)but->hardmax) is_push = true;
                                }
                                break;
 +                      case UI_BTYPE_TAB:
 +                              if (but->rnaprop && but->custom_data) {
 +                                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                                      if (RNA_property_type(but->rnaprop) == PROP_POINTER) {
 +                                              PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
 +                                              if (active_ptr.data == but->custom_data) {
 +                                                      is_push = true;
 +                                              }
 +                                      }
 +                              }
 +                              break;
                        default:
                                is_push = -1;
                                break;
@@@ -1552,6 -1539,83 +1580,6 @@@ static void ui_but_update_select_flag(u
        }
  }
  
 -static uiBut *ui_linkline_find_inlink(uiBlock *block, void *poin)
 -{
 -      uiBut *but;
 -
 -      but = block->buttons.first;
 -      while (but) {
 -              if (but->type == UI_BTYPE_INLINK) {
 -                      if (but->poin == poin) return but;
 -              }
 -              but = but->next;
 -      }
 -      return NULL;
 -}
 -
 -static void ui_linkline_add(ListBase *listb, uiBut *but, uiBut *bt, short deactive)
 -{
 -      uiLinkLine *line;
 -
 -      line = MEM_callocN(sizeof(uiLinkLine), "linkline");
 -      BLI_addtail(listb, line);
 -      line->from = but;
 -      line->to = bt;
 -      line->deactive = deactive;
 -}
 -
 -uiBut *UI_block_links_find_inlink(uiBlock *block, void *poin)
 -{
 -      return ui_linkline_find_inlink(block, poin);
 -}
 -
 -void UI_block_links_compose(uiBlock *block)
 -{
 -      uiBut *but, *bt;
 -      uiLink *link;
 -      void ***ppoin;
 -      int a;
 -
 -      but = block->buttons.first;
 -      while (but) {
 -              if (but->type == UI_BTYPE_LINK) {
 -                      link = but->link;
 -
 -                      /* for all pointers in the array */
 -                      if (link) {
 -                              if (link->ppoin) {
 -                                      ppoin = link->ppoin;
 -                                      for (a = 0; a < *(link->totlink); a++) {
 -                                              bt = ui_linkline_find_inlink(block, (*ppoin)[a]);
 -                                              if (bt) {
 -                                                      if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) {
 -                                                              ui_linkline_add(&link->lines, but, bt, true);
 -                                                      }
 -                                                      else {
 -                                                              ui_linkline_add(&link->lines, but, bt, false);
 -                                                      }
 -
 -                                              }
 -                                      }
 -                              }
 -                              else if (link->poin) {
 -                                      bt = ui_linkline_find_inlink(block, *link->poin);
 -                                      if (bt) {
 -                                              if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) {
 -                                                      ui_linkline_add(&link->lines, but, bt, true);
 -                                              }
 -                                              else {
 -                                                      ui_linkline_add(&link->lines, but, bt, false);
 -                                              }
 -                                      }
 -                              }
 -                      }
 -              }
 -              but = but->next;
 -      }
 -}
 -
 -
  /* ************************************************ */
  
  void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr)
@@@ -1568,78 -1632,48 +1596,78 @@@ void UI_block_lock_clear(uiBlock *block
        block->lockstr = NULL;
  }
  
 -/* *************************************************************** */
 -
 -void ui_linkline_remove(uiLinkLine *line, uiBut *but)
 -{
 -      uiLink *link;
 -      int a, b;
 -
 -      BLI_remlink(&but->link->lines, line);
 -
 -      link = line->from->link;
 -
 -      /* are there more pointers allowed? */
 -      if (link->ppoin) {
 +/* *********************** data get/set ***********************
 + * this either works with the pointed to data, or can work with
 + * an edit override pointer while dragging for example */
  
 -              if (*(link->totlink) == 1) {
 -                      *(link->totlink) = 0;
 -                      MEM_freeN(*(link->ppoin));
 -                      *(link->ppoin) = NULL;
 -              }
 -              else {
 -                      b = 0;
 -                      for (a = 0; a < (*(link->totlink)); a++) {
 -                              if ((*(link->ppoin))[a] != line->to->poin) {
 -                                      (*(link->ppoin))[b] = (*(link->ppoin))[a];
 -                                      b++;
 -                              }
 -                      }
 -                      (*(link->totlink))--;
 +/* Get PointerRNA which will point to a data inside of an evaluated
 + * ID datablock.
 + */
 +static PointerRNA ui_but_evaluated_rnapoin_get(uiBut *but)
 +{
 +      BLI_assert(but->rnaprop != NULL);
 +      /* TODO(sergey): evil_C sounds.. EVIL! Any clear way to avoid this? */
 +      PointerRNA rnapoin_eval = but->rnapoin;
 +      /* If there is no animation or drivers, it doesn't matter if we read value
 +       * from evaluated datablock or from original one.
 +       *
 +       * Reading from original one is much faster, since we don't need to do any
 +       * PointerRNA remapping or hash lookup.
 +       */
 +      if (BKE_animdata_from_id(but->rnapoin.id.data) == NULL) {
 +              return rnapoin_eval;
 +      }
 +      /* Same goes for the properties which can not be animated. */
 +      if (!RNA_property_animateable(&but->rnapoin, but->rnaprop)) {
 +              return rnapoin_eval;
 +      }
 +      Depsgraph *depsgraph = CTX_data_depsgraph(but->block->evil_C);
 +      /* ID pointer we can always remap, they are inside of depsgraph. */
 +      rnapoin_eval.id.data =
 +              DEG_get_evaluated_id(depsgraph, rnapoin_eval.id.data);
 +      /* Some of ID datablocks do not have their evaluated copy inside
 +       * of dependency graph. If it's such datablock, no need to worry about
 +       * data pointer.
 +       */
 +      if (rnapoin_eval.id.data == but->rnapoin.id.data) {
 +              return rnapoin_eval;
 +      }
 +      /* For the data pointer it's getting a bit more involved, since it can
 +       * whether be and ID, or can be a property deep inside of ID.
 +       *
 +       * We start from checking if it's an ID, since that is the less involved
 +       * code path, and probably is executed in most of the cases.
 +       */
 +      if (but->rnapoin.data == but->rnapoin.id.data) {
 +              rnapoin_eval.data = DEG_get_evaluated_id(depsgraph, rnapoin_eval.data);
 +              return rnapoin_eval;
 +      }
 +      /* We aren't as lucky as we thought we are :(
 +       *
 +       * Since we don't know what the property is, we get it's RNA path
 +       * relative to the original ID, and then we decent down from evaluated
 +       * ID to the same property.
 +       *
 +       * This seems to be most straightforward way to get sub-data pointers
 +       * which can be buried deep inside of ID block.
 +       */
 +      const char *rna_path =
 +             RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
 +      if (rna_path != NULL) {
 +              PointerRNA id_ptr;
 +              RNA_id_pointer_create(rnapoin_eval.id.data, &id_ptr);
 +              if (!RNA_path_resolve_full(&id_ptr,
 +                                         rna_path,
 +                                         &rnapoin_eval,
 +                                         NULL, NULL))
 +              {
 +                      /* TODO(sergey): Anything to do here to recover? */
                }
 +              MEM_freeN((void *)rna_path);
        }
 -      else {
 -              *(link->poin) = NULL;
 -      }
 -
 -      MEM_freeN(line);
 -      //REDRAW
 +      return rnapoin_eval;
  }
  
 -/* *********************** data get/set ***********************
 - * this either works with the pointed to data, or can work with
 - * an edit override pointer while dragging for example */
 -
  /* for buttons pointing to color for example */
  void ui_but_v3_get(uiBut *but, float vec[3])
  {
  
                zero_v3(vec);
  
 +              PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but);
 +
                if (RNA_property_type(prop) == PROP_FLOAT) {
 -                      int tot = RNA_property_array_length(&but->rnapoin, prop);
 +                      int tot = RNA_property_array_length(&rnapoin_eval, prop);
                        BLI_assert(tot > 0);
                        if (tot == 3) {
 -                              RNA_property_float_get_array(&but->rnapoin, prop, vec);
 +                              RNA_property_float_get_array(&rnapoin_eval, prop, vec);
                        }
                        else {
                                tot = min_ii(tot, 3);
                                for (a = 0; a < tot; a++) {
 -                                      vec[a] = RNA_property_float_get_index(&but->rnapoin, prop, a);
 +                                      vec[a] = RNA_property_float_get_index(&rnapoin_eval, prop, a);
                                }
                        }
                }
@@@ -1846,29 -1878,27 +1874,29 @@@ double ui_but_value_get(uiBut *but
  
                BLI_assert(but->rnaindex != -1);
  
 +              PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but);
 +
                switch (RNA_property_type(prop)) {
                        case PROP_BOOLEAN:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_boolean_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_boolean_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_boolean_get(&but->rnapoin, prop);
 +                                      value = RNA_property_boolean_get(&rnapoin_eval, prop);
                                break;
                        case PROP_INT:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_int_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_int_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_int_get(&but->rnapoin, prop);
 +                                      value = RNA_property_int_get(&rnapoin_eval, prop);
                                break;
                        case PROP_FLOAT:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_float_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_float_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_float_get(&but->rnapoin, prop);
 +                                      value = RNA_property_float_get(&rnapoin_eval, prop);
                                break;
                        case PROP_ENUM:
 -                              value = RNA_property_enum_get(&but->rnapoin, prop);
 +                              value = RNA_property_enum_get(&rnapoin_eval, prop);
                                break;
                        default:
                                value = 0.0;
@@@ -2011,7 -2041,7 +2039,7 @@@ static bool ui_but_icon_extra_is_visibl
  
  static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but)
  {
 -      BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
 +      BLI_assert(ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB));
        return ((but->editstr == NULL) &&
                (but->drawstr[0] != '\0') &&
                (but->flag & UI_BUT_VALUE_CLEAR));
@@@ -2054,11 -2084,6 +2082,11 @@@ uiButExtraIconType ui_but_icon_extra_ge
                                return UI_BUT_ICONEXTRA_EYEDROPPER;
                        }
                        break;
 +              case UI_BTYPE_TAB:
 +                      if (ui_but_icon_extra_is_visible_search_unlink(but)) {
 +                              return UI_BUT_ICONEXTRA_CLEAR;
 +                      }
 +                      break;
                default:
                        break;
        }
@@@ -2173,23 -2198,14 +2201,23 @@@ void ui_but_string_get_ex(uiBut *but, c
                *r_use_exp_float = false;
        }
  
 -      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
 +      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB)) {
                PropertyType type;
                const char *buf = NULL;
                int buf_len;
  
                type = RNA_property_type(but->rnaprop);
  
 -              if (type == PROP_STRING) {
 +              if ((but->type == UI_BTYPE_TAB) && (but->custom_data)) {
 +                      StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
 +                      PointerRNA ptr;
 +
 +                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                      RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
 +                      buf = RNA_struct_name_get_alloc(&ptr, str, maxlen, &buf_len);
 +              }
 +              else if (type == PROP_STRING) {
                        /* RNA string */
                        buf = RNA_property_string_get_alloc(&but->rnapoin, but->rnaprop, str, maxlen, &buf_len);
                }
                        MEM_freeN((void *)buf);
                }
        }
 -      else if (but->type == UI_BTYPE_TEXT) {
 -              /* string */
 -              BLI_strncpy(str, but->poin, maxlen);
 -              return;
 -      }
 -      else if (but->type == UI_BTYPE_SEARCH_MENU) {
 +      else if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
                /* string */
                BLI_strncpy(str, but->poin, maxlen);
                return;
@@@ -2424,7 -2445,7 +2452,7 @@@ static void ui_but_string_free_internal
  
  bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
  {
 -      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
 +      if (but->rnaprop && but->rnapoin.data && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
                if (RNA_property_editable(&but->rnapoin, but->rnaprop)) {
                        PropertyType type;
  
                                return true;
                        }
                        else if (type == PROP_POINTER) {
 -                              /* RNA pointer */
 -                              PointerRNA ptr, rptr;
 -                              PropertyRNA *prop;
 -
                                if (str[0] == '\0') {
                                        RNA_property_pointer_set(&but->rnapoin, but->rnaprop, PointerRNA_NULL);
                                        return true;
                                }
                                else {
 -                                      ptr = but->rnasearchpoin;
 -                                      prop = but->rnasearchprop;
 +                                      /* RNA pointer */
 +                                      PointerRNA rptr;
 +                                      PointerRNA ptr = but->rnasearchpoin;
 +                                      PropertyRNA *prop = but->rnasearchprop;
  
                                        if (prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr))
                                                RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr);
                        }
                }
        }
 +      else if (but->type == UI_BTYPE_TAB) {
 +              if (but->rnaprop && but->custom_data) {
 +                      StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
 +                      PointerRNA ptr;
 +                      PropertyRNA *prop;
 +
 +                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                      RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
 +                      prop = RNA_struct_name_property(ptr_type);
 +                      if (RNA_property_editable(&ptr, prop)) {
 +                              RNA_property_string_set(&ptr, prop, str);
 +                      }
 +              }
 +      }
        else if (but->type == UI_BTYPE_TEXT) {
                /* string */
 -              if (ui_but_is_utf8(but)) BLI_strncpy_utf8(but->poin, str, but->hardmax);
 -              else BLI_strncpy(but->poin, str, but->hardmax);
 +              if (!but->poin || (str[0] == '\0')) {
 +                      str = "";
 +              }
 +              else if (ui_but_is_utf8(but)) {
 +                      BLI_strncpy_utf8(but->poin, str, but->hardmax);
 +              }
 +              else {
 +                      BLI_strncpy(but->poin, str, but->hardmax);
 +              }
  
                return true;
        }
@@@ -2676,6 -2677,14 +2704,6 @@@ static void ui_set_but_soft_range(uiBu
  
  /* ******************* Free ********************/
  
 -static void ui_free_link(uiLink *link)
 -{
 -      if (link) {
 -              BLI_freelistN(&link->lines);
 -              MEM_freeN(link);
 -      }
 -}
 -
  /* can be called with C==NULL */
  static void ui_but_free(const bContext *C, uiBut *but)
  {
                MEM_freeN(but->hold_argN);
        }
  
 +      if (!but->editstr && but->free_search_arg) {
 +              MEM_SAFE_FREE(but->search_arg);
 +      }
 +
        if (but->active) {
                /* XXX solve later, buttons should be free-able without context ideally,
                 * however they may have open tooltips or popup windows, which need to
        if (but->str && but->str != but->strdata) {
                MEM_freeN(but->str);
        }
 -      ui_free_link(but->link);
  
        if ((but->type == UI_BTYPE_IMAGE) && but->poin) {
                IMB_freeImBuf((struct ImBuf *)but->poin);
@@@ -2757,27 -2763,6 +2785,27 @@@ void UI_block_free(const bContext *C, u
        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)
  {
@@@ -2834,6 -2819,7 +2862,6 @@@ uiBlock *UI_block_begin(const bContext 
        uiBlock *block;
        wmWindow *window;
        Scene *scn;
 -      int getsizex, getsizey;
  
        window = CTX_wm_window(C);
        scn = CTX_data_scene(C);
        if (region)
                UI_block_region_set(block, region);
  
 -      /* window matrix and aspect */
 -      if (region && region->swinid) {
 -              wm_subwindow_matrix_get(window, region->swinid, block->winmat);
 -              wm_subwindow_size_get(window, region->swinid, &getsizex, &getsizey);
 -
 -              block->aspect = 2.0f / fabsf(getsizex * 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 */
 -              wm_subwindow_matrix_get(window, window->screen->mainwin, block->winmat);
 -              wm_subwindow_size_get(window, window->screen->mainwin, &getsizex, &getsizey);
 +      /* Set window matrix and aspect for region and OpenGL state. */
 +      ui_update_window_matrix(window, region, block);
  
 -              block->aspect = 2.0f / fabsf(getsizex * 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;
  }
  
 -uiBlock *UI_block_find_in_region(const char *name, ARegion *ar)
 -{
 -      return BLI_findstring(&ar->uiblocks, name, offsetof(uiBlock, name));
 -}
 -
  void UI_block_emboss_set(uiBlock *block, char dt)
  {
        block->dt = dt;
@@@ -3031,7 -3032,6 +3059,7 @@@ void ui_but_update_ex(uiBut *but, cons
  
                case UI_BTYPE_TEXT:
                case UI_BTYPE_SEARCH_MENU:
 +              case UI_BTYPE_TAB:
                        if (!but->editstr) {
                                char str[UI_MAX_DRAW_STR];
  
@@@ -3155,16 -3155,6 +3183,16 @@@ void ui_block_cm_to_display_space_range
        *max = max_fff(UNPACK3(pixel));
  }
  
 +static uiBut *ui_but_alloc(const eButType type)
 +{
 +      switch (type) {
 +              case UI_BTYPE_TAB:
 +                      return MEM_callocN(sizeof(uiButTab), "uiButTab");
 +              default:
 +                      return MEM_callocN(sizeof(uiBut), "uiBut");
 +      }
 +}
 +
  /**
   * \brief ui_def_but is the function that draws many button types
   *
@@@ -3198,7 -3188,7 +3226,7 @@@ static uiBut *ui_def_but
                }
        }
  
 -      but = MEM_callocN(sizeof(uiBut), "uiBut");
 +      but = ui_but_alloc(type & BUTTYPE);
  
        but->type = type & BUTTYPE;
        but->pointype = type & UI_BUT_POIN_TYPES;
                 ELEM(but->type,
                      UI_BTYPE_MENU, UI_BTYPE_TEXT, UI_BTYPE_LABEL,
                      UI_BTYPE_BLOCK, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU,
 -                    UI_BTYPE_PROGRESS_BAR))
 +                    UI_BTYPE_PROGRESS_BAR, UI_BTYPE_POPOVER))
        {
                but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
        }
                 UI_BTYPE_BLOCK, UI_BTYPE_BUT, UI_BTYPE_LABEL,
                 UI_BTYPE_PULLDOWN, UI_BTYPE_ROUNDBOX, UI_BTYPE_LISTBOX,
                 UI_BTYPE_BUT_MENU, UI_BTYPE_SCROLL, UI_BTYPE_GRIP,
 -               UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE) ||
 +               UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE,
 +               UI_BTYPE_SEPR_SPACER) ||
            (but->type >= UI_BTYPE_SEARCH_MENU))
        {
                /* pass */
@@@ -3454,12 -3443,6 +3482,12 @@@ static void ui_def_but_rna__menu(bConte
        block->flag |= UI_BLOCK_IS_FLIP;
  }
  
 +static void ui_but_submenu_enable(uiBlock *block, uiBut *but)
 +{
 +      but->flag |= UI_BUT_ICON_SUBMENU;
 +      block->content_hints |= BLOCK_CONTAINS_SUBMENU_BUT;
 +}
 +
  /**
   * ui_def_but_rna_propname and ui_def_but_rna
   * both take the same args except for propname vs prop, this is done so we can
@@@ -3595,11 -3578,11 +3623,11 @@@ static uiBut *ui_def_but_rna
        }
  
        if ((type == UI_BTYPE_MENU) && (but->dt == UI_EMBOSS_PULLDOWN)) {
 -              but->flag |= UI_BUT_ICON_SUBMENU;
 +              ui_but_submenu_enable(block, but);
        }
  
        const char *info;
 -      if (!RNA_property_editable_info(&but->rnapoin, prop, &info)) {
 +      if (but->rnapoin.data && !RNA_property_editable_info(&but->rnapoin, prop, &info)) {
                ui_def_but_rna__disable(but, info);
        }
  
@@@ -4018,6 -4001,19 +4046,6 @@@ uiBut *uiDefIconTextButO(uiBlock *block
  
  /* END Button containing both string label and icon */
  
 -void UI_but_link_set(uiBut *but, void **poin, void ***ppoin, short *tot, int from, int to)
 -{
 -      uiLink *link;
 -
 -      link = but->link = MEM_callocN(sizeof(uiLink), "new uilink");
 -
 -      link->poin = poin;
 -      link->ppoin = ppoin;
 -      link->totlink = tot;
 -      link->fromcode = from;
 -      link->tocode = to;
 -}
 -
  /* cruft to make uiBlock and uiBut private */
  
  int UI_blocklist_min_y_get(ListBase *lb)
@@@ -4334,7 -4330,7 +4362,7 @@@ uiBut *uiDefIconTextMenuBut(uiBlock *bl
        ui_def_but_icon(but, icon, UI_HAS_ICON);
  
        but->drawflag |= UI_BUT_ICON_LEFT;
 -      but->flag |= UI_BUT_ICON_SUBMENU;
 +      ui_but_submenu_enable(block, but);
  
        but->menu_create_func = func;
        ui_but_update(but);
@@@ -4366,7 -4362,7 +4394,7 @@@ uiBut *uiDefIconTextBlockBut(uiBlock *b
                but->drawflag |= UI_BUT_ICON_LEFT;
        }
        but->flag |= UI_HAS_ICON;
 -      but->flag |= UI_BUT_ICON_SUBMENU;
 +      ui_but_submenu_enable(block, but);
  
        but->block_create_func = func;
        ui_but_update(but);
@@@ -4485,7 -4481,7 +4513,7 @@@ static void operator_enum_search_cb(con
                for (item = item_array; item->identifier; item++) {
                        /* note: need to give the index rather than the identifier because the enum can be freed */
                        if (BLI_strcasestr(item->name, str)) {
 -                              if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), 0))
 +                              if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), item->icon))
                                        break;
                        }
                }
@@@ -4616,7 -4612,7 +4644,7 @@@ void UI_but_string_info_get(bContext *C
                                tmp = BLI_strdup(RNA_property_identifier(but->rnaprop));
                }
                else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) {
 -                      if (but->rnaprop)
 +                      if (but->rnaprop && but->rnapoin.data)
                                tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type));
                        else if (but->optype)
                                tmp = BLI_strdup(but->optype->idname);
                                PointerRNA *opptr = UI_but_operator_ptr_get(but);
                                wmOperatorType *ot = but->optype;
  
 +                              /* so the context is passed to itemf functions */
 +                              WM_operator_properties_sanitize(opptr, false);
 +
                                /* if the default property of the operator is enum and it is set,
                                 * fetch the tooltip of the selected value so that "Snap" and "Mirror"
                                 * operator menus in the Anim Editors will show tooltips for the different