Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Sat, 30 Jun 2018 07:29:38 +0000 (09:29 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 30 Jun 2018 07:29:38 +0000 (09:29 +0200)
1  2 
intern/cycles/render/nodes.cpp
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_layout.c
source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.c

index ea25056e9fdf139112f6b513b0b441541175a6d7,601d72c4b1b8a5121f67a7876af099689526eb09..e7f13f3123f3360c096a4ddc208e2dce44b1dc3c
@@@ -1489,19 -1489,6 +1489,19 @@@ void PointDensityTextureNode::attribute
        ShaderNode::attributes(shader, attributes);
  }
  
 +void PointDensityTextureNode::add_image()
 +{
 +      if(slot == -1) {
 +              ImageMetaData metadata;
 +              slot = image_manager->add_image(filename.string(), builtin_data,
 +                                              false, 0,
 +                                              interpolation,
 +                                              EXTENSION_CLIP,
 +                                              true,
 +                                              metadata);
 +      }
 +}
 +
  void PointDensityTextureNode::compile(SVMCompiler& compiler)
  {
        ShaderInput *vector_in = input("Vector");
        image_manager = compiler.image_manager;
  
        if(use_density || use_color) {
 -              if(slot == -1) {
 -                      ImageMetaData metadata;
 -                      slot = image_manager->add_image(filename.string(), builtin_data,
 -                                                      false, 0,
 -                                                      interpolation,
 -                                                      EXTENSION_CLIP,
 -                                                      true,
 -                                                      metadata);
 -              }
 +              add_image();
  
                if(slot != -1) {
                        compiler.stack_assign(vector_in);
@@@ -1557,7 -1552,15 +1557,7 @@@ void PointDensityTextureNode::compile(O
        image_manager = compiler.image_manager;
  
        if(use_density || use_color) {
 -              if(slot == -1) {
 -                      ImageMetaData metadata;
 -                      slot = image_manager->add_image(filename.string(), builtin_data,
 -                                                      false, 0,
 -                                                      interpolation,
 -                                                      EXTENSION_CLIP,
 -                                                      true,
 -                                                      metadata);
 -              }
 +              add_image();
  
                if(slot != -1) {
                        compiler.parameter("filename", string_printf("@i%d", slot).c_str());
@@@ -2792,14 -2795,14 +2792,14 @@@ NODE_DEFINE(AmbientOcclusionNode
  {
        NodeType* type = NodeType::add("ambient_occlusion", create, NodeType::SHADER);
  
-       SOCKET_INT(samples, "Samples", 8);
+       SOCKET_INT(samples, "Samples", 16);
  
        SOCKET_IN_COLOR(color, "Color", make_float3(1.0f, 1.0f, 1.0f));
        SOCKET_IN_FLOAT(distance, "Distance", 1.0f);
        SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL);
  
        SOCKET_BOOLEAN(inside, "Inside", false);
-       SOCKET_BOOLEAN(only_local, "Only Local", true);
+       SOCKET_BOOLEAN(only_local, "Only Local", false);
  
        SOCKET_OUT_COLOR(color, "Color");
        SOCKET_OUT_FLOAT(ao, "AO");
index bb54f26131a4431d71ba4c9e371541fc1855600e,d8771ebf93fc1aebedad6a11bda22e86b417658e..5edc66300ce6976fdf89778d94ea68b04b7318a9
  #include "MEM_guardedalloc.h"
  
  #include "DNA_brush_types.h"
 -#include "DNA_sensor_types.h"
 -#include "DNA_controller_types.h"
 -#include "DNA_actuator_types.h"
  
 -#include "DNA_object_types.h"
  #include "DNA_scene_types.h"
  #include "DNA_screen_types.h"
  
@@@ -68,7 -72,6 +68,7 @@@
  #include "ED_undo.h"
  
  #include "UI_interface.h"
 +#include "UI_view2d.h"
  
  #include "BLF_api.h"
  
  #define UI_MAX_PASSWORD_STR 128
  
  /* proto */
 -static void ui_but_smart_controller_add(bContext *C, uiBut *from, uiBut *to);
 -static void ui_but_link_add(bContext *C, uiBut *from, uiBut *to);
  static int ui_do_but_EXIT(bContext *C, uiBut *but, struct uiHandleButtonData *data, const wmEvent *event);
  static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b);
  static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str);
@@@ -120,7 -125,7 +120,7 @@@ static bool ui_mouse_motion_keynav_test
  #define BUTTON_FLASH_DELAY          0.020
  #define MENU_SCROLL_INTERVAL        0.1
  #define PIE_MENU_INTERVAL           0.01
 -#define BUTTON_AUTO_OPEN_THRESH     0.3
 +#define BUTTON_AUTO_OPEN_THRESH     0.2
  #define BUTTON_MOUSE_TOWARDS_THRESH 1.0
  /* pixels to move the cursor to get out of keyboard navigation */
  #define BUTTON_KEYNAV_PX_LIMIT      8
@@@ -271,7 -276,6 +271,7 @@@ typedef struct uiHandleButtonData 
        /* booleans (could be made into flags) */
        bool cancel, escapecancel;
        bool applied, applied_interactive;
 +      bool changed_cursor;
        wmTimer *flashtimer;
  
        /* edited value */
@@@ -497,16 -501,6 +497,16 @@@ bool ui_but_is_toggle(const uiBut *but
        );
  }
  
 +#ifdef USE_UI_POPOVER_ONCE
 +bool ui_but_is_popover_once_compat(const uiBut *but)
 +{
 +      return (
 +              (but->type == UI_BTYPE_BUT) ||
 +              ui_but_is_toggle(but)
 +      );
 +}
 +#endif
 +
  static uiBut *ui_but_prev(uiBut *but)
  {
        while (but->prev) {
@@@ -655,8 -649,11 +655,8 @@@ PointerRNA *ui_handle_afterfunc_add_ope
  
  static void popup_check(bContext *C, wmOperator *op)
  {
 -      if (op && op->type->check && op->type->check(C, op)) {
 -              /* check for popup and re-layout buttons */
 -              ARegion *ar_menu = CTX_wm_menu(C);
 -              if (ar_menu)
 -                      ED_region_tag_refresh_ui(ar_menu);
 +      if (op && op->type->check) {
 +              op->type->check(C, op);
        }
  }
  
@@@ -737,7 -734,8 +737,7 @@@ static void ui_apply_but_undo(uiBut *bu
                const char *str = NULL;
  
                /* define which string to use for undo */
 -              if (ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK)) str = "Add button link";
 -              else if (but->type == UI_BTYPE_MENU) str = but->drawstr;
 +              if (but->type == UI_BTYPE_MENU) str = but->drawstr;
                else if (but->drawstr[0]) str = but->drawstr;
                else str = but->tip;
  
@@@ -949,20 -947,6 +949,20 @@@ static void ui_apply_but_TEX(bContext *
        data->applied = true;
  }
  
 +static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data)
 +{
 +      if (data->str) {
 +              ui_but_string_set(C, but, data->str);
 +              ui_but_update_edited(but);
 +      }
 +      else {
 +              ui_apply_but_func(C, but);
 +      }
 +
 +      data->retval = but->retval;
 +      data->applied = true;
 +}
 +
  static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
  {
        if (data->str) {
@@@ -1265,9 -1249,6 +1265,9 @@@ static bool ui_drag_toggle_but_is_suppo
        if (ui_but_is_bool(but)) {
                return true;
        }
 +      else if (UI_but_is_decorator(but)) {
 +              return ELEM(but->icon, ICON_SPACE2, ICON_SPACE3, ICON_DOT, ICON_LIBRARY_DATA_OVERRIDE);
 +      }
        else {
                return false;
        }
@@@ -1278,9 -1259,6 +1278,9 @@@ static bool ui_drag_toggle_but_is_pushe
        if (ui_but_is_bool(but)) {
                return ui_but_is_pushed(but);
        }
 +      else if (UI_but_is_decorator(but)) {
 +              return (but->icon == ICON_SPACE2);
 +      }
        else {
                return false;
        }
@@@ -1291,6 -1269,7 +1291,6 @@@ typedef struct uiDragToggleHandle 
        bool is_init;
        bool is_set;
        float but_cent_start[2];
 -      eButType but_type_start;
  
        bool xy_lock[2];
        int  xy_init[2];
  } uiDragToggleHandle;
  
  static bool ui_drag_toggle_set_xy_xy(
 -        bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start,
 +        bContext *C, ARegion *ar, const bool is_set,
          const int xy_src[2], const int xy_dst[2])
  {
        /* popups such as layers won't re-evaluate on redraw */
                                if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) {
  
                                        /* execute the button */
 -                                      if (ui_drag_toggle_but_is_supported(but) && but->type == but_type_start) {
 +                                      if (ui_drag_toggle_but_is_supported(but)) {
                                                /* is it pressed? */
                                                bool is_set_but = ui_drag_toggle_but_is_pushed(but);
                                                if (is_set_but != is_set) {
@@@ -1394,7 -1373,7 +1394,7 @@@ static void ui_drag_toggle_set(bContex
  
  
        /* touch all buttons between last mouse coord and this one */
 -      do_draw = ui_drag_toggle_set_xy_xy(C, ar, drag_info->is_set, drag_info->but_type_start, drag_info->xy_last, xy);
 +      do_draw = ui_drag_toggle_set_xy_xy(C, ar, drag_info->is_set, drag_info->xy_last, xy);
  
        if (do_draw) {
                ED_region_tag_redraw(ar);
@@@ -1773,6 -1752,7 +1773,6 @@@ static bool ui_but_drag_init
                        drag_info->is_set = ui_drag_toggle_but_is_pushed(but);
                        drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect);
                        drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect);
 -                      drag_info->but_type_start = but->type;
                        copy_v2_v2_int(drag_info->xy_init, &event->x);
                        copy_v2_v2_int(drag_info->xy_last, &event->x);
  
  
  /* ********************** linklines *********************** */
  
 -static void ui_linkline_remove_active(uiBlock *block)
 -{
 -      uiBut *but;
 -      uiLink *link;
 -      uiLinkLine *line, *nline;
 -      int a, b;
 -
 -      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 = nline) {
 -                              nline = line->next;
 -
 -                              if (line->flag & UI_SELECT) {
 -                                      BLI_remlink(&but->link->lines, line);
 -
 -                                      link = line->from->link;
 -
 -                                      /* are there more pointers allowed? */
 -                                      if (link->ppoin) {
 -
 -                                              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))--;
 -                                              }
 -                                      }
 -                                      else {
 -                                              *(link->poin) = NULL;
 -                                      }
 -
 -                                      MEM_freeN(line);
 -                              }
 -                      }
 -              }
 -      }
 -}
 -
 -
 -static uiLinkLine *ui_but_find_link(uiBut *from, uiBut *to)
 -{
 -      uiLinkLine *line;
 -      uiLink *link;
 -
 -      link = from->link;
 -      if (link) {
 -              for (line = link->lines.first; line; line = line->next) {
 -                      if (line->from == from && line->to == to) {
 -                              return line;
 -                      }
 -              }
 -      }
 -      return NULL;
 -}
 -
 -/* XXX BAD BAD HACK, fixme later **************** */
 -/* Try to add an AND Controller between the sensor and the actuator logic bricks and to connect them all */
 -static void ui_but_smart_controller_add(bContext *C, uiBut *from, uiBut *to)
 -{
 -      Object *ob = NULL;
 -      bSensor *sens_iter;
 -      bActuator *act_to, *act_iter;
 -      bController *cont;
 -      bController ***sens_from_links;
 -      uiBut *tmp_but;
 -
 -      uiLink *link = from->link;
 -
 -      PointerRNA props_ptr, object_ptr;
 -
 -      if (link->ppoin)
 -              sens_from_links = (bController ***)(link->ppoin);
 -      else return;
 -
 -      act_to = (bActuator *)(to->poin);
 -
 -      /* (1) get the object */
 -      CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects)
 -      {
 -              for (sens_iter = ob_iter->sensors.first; sens_iter; sens_iter = sens_iter->next) {
 -                      if (&(sens_iter->links) == sens_from_links) {
 -                              ob = ob_iter;
 -                              break;
 -                      }
 -              }
 -              if (ob) break;
 -      } CTX_DATA_END;
 -
 -      if (!ob) return;
 -
 -      /* (2) check if the sensor and the actuator are from the same object */
 -      for (act_iter = ob->actuators.first; act_iter; act_iter = (bActuator *)act_iter->next) {
 -              if (act_iter == act_to)
 -                      break;
 -      }
 -
 -      /* only works if the sensor and the actuator are from the same object */
 -      if (!act_iter) return;
 -
 -      /* in case the linked controller is not the active one */
 -      RNA_pointer_create((ID *)ob, &RNA_Object, ob, &object_ptr);
 -
 -      WM_operator_properties_create(&props_ptr, "LOGIC_OT_controller_add");
 -      RNA_string_set(&props_ptr, "object", ob->id.name + 2);
 -
 -      /* (3) add a new controller */
 -      if (WM_operator_name_call(C, "LOGIC_OT_controller_add", WM_OP_EXEC_DEFAULT, &props_ptr) & OPERATOR_FINISHED) {
 -              cont = (bController *)ob->controllers.last;
 -              /* Quick fix to make sure we always have an AND controller.
 -               * It might be nicer to make sure the operator gives us the right one though... */
 -              cont->type = CONT_LOGIC_AND;
 -
 -              /* (4) link the sensor->controller->actuator */
 -              tmp_but = MEM_callocN(sizeof(uiBut), "uiBut");
 -              UI_but_link_set(
 -                      tmp_but, (void **)&cont, (void ***)&(cont->links),
 -                      &cont->totlinks, from->link->tocode, (int)to->hardmin);
 -              tmp_but->hardmin = from->link->tocode;
 -              tmp_but->poin = (char *)cont;
 -
 -              tmp_but->type = UI_BTYPE_INLINK;
 -              ui_but_link_add(C, from, tmp_but);
 -
 -              tmp_but->type = UI_BTYPE_LINK;
 -              ui_but_link_add(C, tmp_but, to);
 -
 -              /* (5) garbage collection */
 -              MEM_freeN(tmp_but->link);
 -              MEM_freeN(tmp_but);
 -      }
 -      WM_operator_properties_free(&props_ptr);
 -}
 -
 -static void ui_but_link_add(bContext *C, uiBut *from, uiBut *to)
 -{
 -      /* in 'from' we have to add a link to 'to' */
 -      uiLink *link;
 -      uiLinkLine *line;
 -      void **oldppoin;
 -      int a;
 -
 -      if ((line = ui_but_find_link(from, to))) {
 -              line->flag |= UI_SELECT;
 -              ui_linkline_remove_active(from->block);
 -              return;
 -      }
 -
 -      if (from->type == UI_BTYPE_INLINK && to->type == UI_BTYPE_INLINK) {
 -              return;
 -      }
 -      else if (from->type == UI_BTYPE_LINK && to->type == UI_BTYPE_INLINK) {
 -              if (from->link->tocode != (int)to->hardmin) {
 -                      ui_but_smart_controller_add(C, from, to);
 -                      return;
 -              }
 -      }
 -      else if (from->type == UI_BTYPE_INLINK && to->type == UI_BTYPE_LINK) {
 -              if (to->link->tocode == (int)from->hardmin) {
 -                      return;
 -              }
 -      }
 -
 -      link = from->link;
 -
 -      /* are there more pointers allowed? */
 -      if (link->ppoin) {
 -              oldppoin = *(link->ppoin);
 -
 -              (*(link->totlink))++;
 -              *(link->ppoin) = MEM_callocN(*(link->totlink) * sizeof(void *), "new link");
 -
 -              for (a = 0; a < (*(link->totlink)) - 1; a++) {
 -                      (*(link->ppoin))[a] = oldppoin[a];
 -              }
 -              (*(link->ppoin))[a] = to->poin;
 -
 -              if (oldppoin) MEM_freeN(oldppoin);
 -      }
 -      else {
 -              *(link->poin) = to->poin;
 -      }
 -
 -}
 -
 -
 -static void ui_apply_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data)
 -{
 -      ARegion *ar = CTX_wm_region(C);
 -      uiBut *bt;
 -
 -      for (bt = but->block->buttons.first; bt; bt = bt->next) {
 -              if (ui_but_contains_point_px(ar, bt, but->linkto[0] + ar->winrct.xmin, but->linkto[1] + ar->winrct.ymin) )
 -                      break;
 -      }
 -      if (bt && bt != but) {
 -              if (!ELEM(bt->type, UI_BTYPE_LINK, UI_BTYPE_INLINK) || !ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK))
 -                      return;
 -
 -              if (but->type == UI_BTYPE_LINK) ui_but_link_add(C, but, bt);
 -              else ui_but_link_add(C, bt, but);
 -
 -              ui_apply_but_func(C, but);
 -              data->retval = but->retval;
 -      }
 -      data->applied = true;
 -}
 -
  static void ui_apply_but_IMAGE(bContext *C, uiBut *but, uiHandleButtonData *data)
  {
        ui_apply_but_func(C, but);
@@@ -1957,9 -2154,6 +1957,9 @@@ static void ui_apply_but(bContext *C, u
                case UI_BTYPE_LISTROW:
                        ui_apply_but_ROW(C, block, but, data);
                        break;
 +              case UI_BTYPE_TAB:
 +                      ui_apply_but_TAB(C, but, data);
 +                      break;
                case UI_BTYPE_SCROLL:
                case UI_BTYPE_GRIP:
                case UI_BTYPE_NUM:
                case UI_BTYPE_HOTKEY_EVENT:
                        ui_apply_but_BUT(C, but, data);
                        break;
 -              case UI_BTYPE_LINK:
 -              case UI_BTYPE_INLINK:
 -                      ui_apply_but_LINK(C, but, data);
 -                      break;
                case UI_BTYPE_IMAGE:
                        ui_apply_but_IMAGE(C, but, data);
                        break;
@@@ -3008,9 -3206,6 +3008,9 @@@ static void ui_textedit_end(bContext *C
  
                        ui_searchbox_free(C, data->searchbox);
                        data->searchbox = NULL;
 +                      if (but->free_search_arg) {
 +                              MEM_SAFE_FREE(but->search_arg);
 +                      }
                }
  
                but->editstr = NULL;
@@@ -3482,7 -3677,6 +3482,7 @@@ static void ui_block_open_begin(bContex
        uiBlockCreateFunc func = NULL;
        uiBlockHandleCreateFunc handlefunc = NULL;
        uiMenuCreateFunc menufunc = NULL;
 +      uiMenuCreateFunc popoverfunc = NULL;
        void *arg = NULL;
  
        switch (but->type) {
                        menufunc = but->menu_create_func;
                        arg = but->poin;
                        break;
 +              case UI_BTYPE_POPOVER:
 +                      BLI_assert(but->menu_create_func);
 +                      popoverfunc = but->menu_create_func;
 +                      arg = but->poin;
 +                      break;
                case UI_BTYPE_COLOR:
                        ui_but_v3_get(but, data->origvec);
                        copy_v3_v3(data->vec, data->origvec);
                if (but->block->handle)
                        data->menu->popup = but->block->handle->popup;
        }
 +      else if (popoverfunc) {
 +              data->menu = ui_popover_panel_create(C, data->region, but, popoverfunc, arg);
 +              if (but->block->handle)
 +                      data->menu->popup = but->block->handle->popup;
 +      }
  
  #ifdef USE_ALLSELECT
        {
@@@ -3782,43 -3966,6 +3782,43 @@@ static bool ui_but_is_mouse_over_icon_e
        return BLI_rcti_isect_pt(&icon_rect, x, y);
  }
  
 +static int ui_do_but_TAB(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
 +{
 +      if (data->state == BUTTON_STATE_HIGHLIGHT) {
 +              if ((event->type == LEFTMOUSE) &&
 +                  ((event->val == KM_DBL_CLICK) || event->ctrl))
 +              {
 +                      button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
 +                      return WM_UI_HANDLER_BREAK;
 +              }
 +              else if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && (event->val == KM_CLICK)) {
 +                      const bool has_icon_extra = ui_but_icon_extra_get(but) == UI_BUT_ICONEXTRA_CLEAR;
 +
 +                      if (has_icon_extra && ui_but_is_mouse_over_icon_extra(data->region, but, &event->x)) {
 +                              uiButTab *tab = (uiButTab *)but;
 +                              wmOperatorType *ot_backup = but->optype;
 +
 +                              but->optype = tab->unlink_ot;
 +                              /* Force calling unlink/delete operator. */
 +                              ui_apply_but(C, block, but, data, true);
 +                              but->optype = ot_backup;
 +                      }
 +                      button_activate_state(C, but, BUTTON_STATE_EXIT);
 +                      return WM_UI_HANDLER_BREAK;
 +              }
 +      }
 +      else if (data->state == BUTTON_STATE_TEXT_EDITING) {
 +              ui_do_but_textedit(C, block, but, data, event);
 +              return WM_UI_HANDLER_BREAK;
 +      }
 +      else if (data->state == BUTTON_STATE_TEXT_SELECTING) {
 +              ui_do_but_textedit_select(C, block, but, data, event);
 +              return WM_UI_HANDLER_BREAK;
 +      }
 +
 +      return WM_UI_HANDLER_CONTINUE;
 +}
 +
  static int ui_do_but_TEX(
          bContext *C, uiBlock *block, uiBut *but,
          uiHandleButtonData *data, const wmEvent *event)
@@@ -4247,54 -4394,6 +4247,54 @@@ static bool ui_numedit_but_NUM
        return changed;
  }
  
 +static void ui_numedit_set_active(uiBut *but)
 +{
 +      int oldflag = but->drawflag;
 +      but->drawflag &= ~(UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT);
 +
 +      uiHandleButtonData *data = but->active;
 +      if (!data) {
 +              return;
 +      }
 +
 +      /* Ignore once we start dragging. */
 +      if (data->dragchange == false) {
 +              const  float handle_width = min_ff(BLI_rctf_size_x(&but->rect) / 3, BLI_rctf_size_y(&but->rect) * 0.7f);
 +              /* we can click on the side arrows to increment/decrement,
 +               * or click inside to edit the value directly */
 +              int mx = data->window->eventstate->x;
 +              int my = data->window->eventstate->x;
 +              ui_window_to_block(data->region, but->block, &mx, &my);
 +
 +              if (mx < (but->rect.xmin + handle_width)) {
 +                      but->drawflag |= UI_BUT_ACTIVE_LEFT;
 +              }
 +              else if (mx > (but->rect.xmax - handle_width)) {
 +                      but->drawflag |= UI_BUT_ACTIVE_RIGHT;
 +              }
 +      }
 +
 +      /* Don't change the cursor once pressed. */
 +      if ((but->flag & UI_SELECT) == 0) {
 +              if ((but->drawflag & (UI_BUT_ACTIVE_LEFT)) || (but->drawflag & (UI_BUT_ACTIVE_RIGHT))) {
 +                      if (data->changed_cursor) {
 +                              WM_cursor_modal_restore(data->window);
 +                              data->changed_cursor = false;
 +                      }
 +              }
 +              else {
 +                      if (data->changed_cursor == false) {
 +                              WM_cursor_modal_set(data->window, CURSOR_X_MOVE);
 +                              data->changed_cursor = true;
 +                      }
 +              }
 +      }
 +
 +      if (but->drawflag != oldflag) {
 +              ED_region_tag_redraw(data->region);
 +      }
 +}
 +
  static int ui_do_but_NUM(
          bContext *C, uiBlock *block, uiBut *but,
          uiHandleButtonData *data, const wmEvent *event)
        my = screen_my = event->y;
  
        ui_window_to_block(data->region, block, &mx, &my);
 +      ui_numedit_set_active(but);
  
        if (data->state == BUTTON_STATE_HIGHLIGHT) {
                int type = event->type, val = event->val;
                        retval = WM_UI_HANDLER_BREAK; /* allow accumulating values, otherwise scrolling gets preference */
                else if (type == WHEELDOWNMOUSE && event->ctrl) {
                        mx = but->rect.xmin;
 +                      but->drawflag &= ~UI_BUT_ACTIVE_RIGHT;
 +                      but->drawflag |= UI_BUT_ACTIVE_LEFT;
                        click = 1;
                }
                else if (type == WHEELUPMOUSE && event->ctrl) {
                        mx = but->rect.xmax;
 +                      but->drawflag &= ~UI_BUT_ACTIVE_LEFT;
 +                      but->drawflag |= UI_BUT_ACTIVE_RIGHT;
                        click = 1;
                }
                else if (event->val == KM_PRESS) {
                /* we can click on the side arrows to increment/decrement,
                 * or click inside to edit the value directly */
                float tempf, softmin, softmax;
 -              float handlewidth;
                int temp;
  
                softmin = but->softmin;
                softmax = but->softmax;
  
 -              handlewidth = min_ff(BLI_rctf_size_x(&but->rect) / 3, BLI_rctf_size_y(&but->rect));
 -
                if (!ui_but_is_float(but)) {
 -                      if (mx < (but->rect.xmin + handlewidth)) {
 +                      if (but->drawflag & UI_BUT_ACTIVE_LEFT) {
                                button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
  
                                temp = (int)data->value - 1;
  
                                button_activate_state(C, but, BUTTON_STATE_EXIT);
                        }
 -                      else if (mx > (but->rect.xmax - handlewidth)) {
 +                      else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) {
                                button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
  
                                temp = (int)data->value + 1;
                        }
                }
                else {
 -                      if (mx < (but->rect.xmin + handlewidth)) {
 +                      if (but->drawflag & UI_BUT_ACTIVE_LEFT) {
                                button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
  
                                tempf = (float)data->value - (UI_PRECISION_FLOAT_SCALE * but->a1);
  
                                button_activate_state(C, but, BUTTON_STATE_EXIT);
                        }
 -                      else if (mx > but->rect.xmax - handlewidth) {
 +                      else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) {
                                button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
  
                                tempf = (float)data->value + (UI_PRECISION_FLOAT_SCALE * but->a1);
@@@ -5187,8 -5284,7 +5187,8 @@@ static int ui_do_but_COLOR
                                if (!event->ctrl) {
                                        float color[3];
                                        Scene *scene = CTX_data_scene(C);
 -                                      Paint *paint = BKE_paint_get_active(scene);
 +                                      ViewLayer *view_layer = CTX_data_view_layer(C);
 +                                      Paint *paint = BKE_paint_get_active(scene, view_layer);
                                        Brush *brush = BKE_paint_brush(paint);
  
                                        if (brush->flag & BRUSH_USE_GRADIENT) {
@@@ -6105,7 -6201,6 +6105,7 @@@ static int ui_do_but_CURVE
        int mx, my, a;
        bool changed = false;
        Scene *scene = CTX_data_scene(C);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
  
        mx = event->x;
        my = event->y;
                                }
                                else {
                                        curvemapping_changed(cumap, true);  /* remove doubles */
 -                                      BKE_paint_invalidate_cursor_overlay(scene, cumap);
 +                                      BKE_paint_invalidate_cursor_overlay(scene, view_layer, cumap);
                                }
                        }
  
@@@ -6400,6 -6495,35 +6400,6 @@@ static int ui_do_but_WAVEFORM
        return WM_UI_HANDLER_CONTINUE;
  }
  
 -static int ui_do_but_LINK(
 -        bContext *C, uiBut *but,
 -        uiHandleButtonData *data, const wmEvent *event)
 -{
 -      VECCOPY2D(but->linkto, event->mval);
 -
 -      if (data->state == BUTTON_STATE_HIGHLIGHT) {
 -              if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
 -                      button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
 -                      return WM_UI_HANDLER_BREAK;
 -              }
 -              else if (event->type == LEFTMOUSE && but->block->handle) {
 -                      button_activate_state(C, but, BUTTON_STATE_EXIT);
 -                      return WM_UI_HANDLER_BREAK;
 -              }
 -      }
 -      else if (data->state == BUTTON_STATE_WAIT_RELEASE) {
 -
 -              if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
 -                      if (!(but->flag & UI_SELECT))
 -                              data->cancel = true;
 -                      button_activate_state(C, but, BUTTON_STATE_EXIT);
 -                      return WM_UI_HANDLER_BREAK;
 -              }
 -      }
 -
 -      return WM_UI_HANDLER_CONTINUE;
 -}
 -
  static bool ui_numedit_but_TRACKPREVIEW(
          bContext *C, uiBut *but, uiHandleButtonData *data,
          int mx, int my,
@@@ -6543,9 -6667,6 +6543,9 @@@ static int ui_do_button(bContext *C, ui
                case UI_BTYPE_HOTKEY_EVENT:
                        retval = ui_do_but_HOTKEYEVT(C, but, data, event);
                        break;
 +              case UI_BTYPE_TAB:
 +                      retval = ui_do_but_TAB(C, block, but, data, event);
 +                      break;
                case UI_BTYPE_BUT_TOGGLE:
                case UI_BTYPE_TOGGLE:
                case UI_BTYPE_ICON_TOGGLE:
                        retval = ui_do_but_TEX(C, block, but, data, event);
                        break;
                case UI_BTYPE_MENU:
 +              case UI_BTYPE_POPOVER:
                case UI_BTYPE_BLOCK:
                case UI_BTYPE_PULLDOWN:
                        retval = ui_do_but_BLOCK(C, but, data, event);
                case UI_BTYPE_HSVCIRCLE:
                        retval = ui_do_but_HSVCIRCLE(C, block, but, data, event);
                        break;
 -              case UI_BTYPE_LINK:
 -              case UI_BTYPE_INLINK:
 -                      retval = ui_do_but_LINK(C, but, data, event);
 -                      break;
                case UI_BTYPE_TRACK_PREVIEW:
                        retval = ui_do_but_TRACKPREVIEW(C, block, but, data, event);
                        break;
                        /* quiet warnings for unhandled types */
                case UI_BTYPE_SEPR:
                case UI_BTYPE_SEPR_LINE:
 +              case UI_BTYPE_SEPR_SPACER:
                case UI_BTYPE_EXTRA:
                        break;
        }
@@@ -6816,11 -6939,12 +6816,11 @@@ bool ui_but_is_active(ARegion *ar
  /* is called by notifier */
  void UI_screen_free_active_but(const bContext *C, bScreen *screen)
  {
 -      ScrArea *sa = screen->areabase.first;
 +      wmWindow *win = CTX_wm_window(C);
  
 -      for (; sa; sa = sa->next) {
 -              ARegion *ar = sa->regionbase.first;
 -              for (; ar; ar = ar->next) {
 -                      uiBut *but = ui_but_find_active_in_region(ar);
 +      ED_screen_areas_iter(win, screen, area) {
 +              for (ARegion *region = area->regionbase.first; region; region = region->next) {
 +                      uiBut *but = ui_but_find_active_in_region(region);
                        if (but) {
                                uiHandleButtonData *data = but->active;
  
@@@ -6906,7 -7030,7 +6906,7 @@@ static bool ui_region_contains_point_px
                ui_window_to_region(ar, &mx, &my);
  
                /* check if in the rect */
 -              if (!BLI_rcti_isect_pt(&v2d->mask, mx, my)) {
 +              if (!BLI_rcti_isect_pt(&v2d->mask, mx, my) || UI_view2d_mouse_in_scrollers(ar, &ar->v2d, x, y)) {
                        return false;
                }
        }
@@@ -7061,7 -7185,7 +7061,7 @@@ void UI_but_tooltip_refresh(bContext *C
  {
        uiHandleButtonData *data = but->active;
        if (data) {
 -              bScreen *sc = data->window->screen;
 +              bScreen *sc = WM_window_get_active_screen(data->window);
                if (sc->tool_tip && sc->tool_tip->region) {
                        WM_tooltip_refresh(C, data->window);
                }
@@@ -7122,15 -7246,12 +7122,12 @@@ static void button_activate_state(bCont
  
        /* highlight has timers for tooltips and auto open */
        if (state == BUTTON_STATE_HIGHLIGHT) {
-               /* for list-items (that are not drawn with regular emboss), don't change selection based on hovering */
-               if (((but->flag & UI_BUT_LIST_ITEM) == 0) && (but->dragflag & UI_EMBOSS_NONE)) {
-                       but->flag &= ~UI_SELECT;
-               }
+               but->flag &= ~UI_SELECT;
  
                button_tooltip_timer_reset(C, but);
  
                /* automatic open pulldown block timer */
 -              if (ELEM(but->type, UI_BTYPE_BLOCK, UI_BTYPE_PULLDOWN)) {
 +              if (ELEM(but->type, UI_BTYPE_BLOCK, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)) {
                        if (data->used_mouse && !data->autoopentimer) {
                                int time;
  
@@@ -7340,9 -7461,6 +7337,9 @@@ static void button_activate_init(bConte
                const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect));
                WM_cursor_modal_set(data->window, horizontal ? CURSOR_X_MOVE : CURSOR_Y_MOVE);
        }
 +      else if (but->type == UI_BTYPE_NUM) {
 +              ui_numedit_set_active(but);
 +      }
  }
  
  static void button_activate_exit(
        ui_selectcontext_end(but, &data->select_others);
  #endif
  
 -      /* redraw (data is but->active!) */
 +      if (data->changed_cursor) {
 +              WM_cursor_modal_restore(data->window);
 +      }
 +
 +      /* redraw and refresh (for popups) */
        ED_region_tag_redraw(data->region);
 +      ED_region_tag_refresh_ui(data->region);
  
        /* clean up button */
        if (but->active) {
@@@ -7628,11 -7741,6 +7625,11 @@@ void UI_context_update_anim_flag(const 
                for (block = ar->uiblocks.first; block; block = block->next) {
                        for (but = block->buttons.first; but; but = but->next) {
                                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);
 +                              }
 +
                                ED_region_tag_redraw(ar);
  
                                if (but->active) {
@@@ -7820,29 -7928,8 +7817,29 @@@ static int ui_handle_button_event(bCont
                        case EVT_BUT_CANCEL:
                                data->cancel = true;
                                button_activate_state(C, but, BUTTON_STATE_EXIT);
 -                              retval = WM_UI_HANDLER_CONTINUE;
                                break;
 +#ifdef USE_UI_POPOVER_ONCE
 +                      case LEFTMOUSE:
 +                      {
 +                              if (event->val == KM_RELEASE) {
 +                                      if (block->flag & UI_BLOCK_POPOVER_ONCE) {
 +                                              if (!(but->flag & UI_BUT_DISABLED)) {
 +                                                      if (ui_but_is_popover_once_compat(but)) {
 +                                                              data->cancel = false;
 +                                                              button_activate_state(C, but, BUTTON_STATE_EXIT);
 +                                                              retval = WM_UI_HANDLER_BREAK;
 +                                                              block->handle->menuretval = UI_RETURN_OK;
 +                                                      }
 +                                                      else if (ui_but_is_editable_as_text(but)) {
 +                                                              ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_TEXT_EDITING);
 +                                                              retval = WM_UI_HANDLER_BREAK;
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                              break;
 +                      }
 +#endif
                        case MOUSEMOVE:
                        {
                                uiBut *but_other = ui_but_find_mouse_over(ar, event);
                                                button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
                                }
  
 -                              retval = WM_UI_HANDLER_CONTINUE;
                                break;
                        }
                        /* XXX hardcoded keymap check... but anyway, while view changes, tooltips should be removed */
                                UI_but_tooltip_timer_remove(C, but);
                                ATTR_FALLTHROUGH;
                        default:
 -                              /* handle button type specific events */
 -                              retval = ui_do_button(C, block, but, event);
                                break;
                }
 +
 +              /* handle button type specific events */
 +              retval = ui_do_button(C, block, but, event);
        }
        else if (data->state == BUTTON_STATE_WAIT_RELEASE) {
                switch (event->type) {
                                break;
                        }
                        case MOUSEMOVE:
 -                              if (ELEM(but->type, UI_BTYPE_LINK, UI_BTYPE_INLINK)) {
 -                                      but->flag |= UI_SELECT;
 -                                      ui_do_button(C, block, but, event);
 -                                      ED_region_tag_redraw(ar);
 -                              }
 -                              else {
 +                              {
                                        /* deselect the button when moving the mouse away */
                                        /* also de-activate for buttons that only show higlights */
                                        if (ui_but_contains_point_px(ar, but, event->x, event->y)) {
@@@ -8187,8 -8279,13 +8184,8 @@@ static int ui_handle_list_event(bContex
        }
  
        if (redraw) {
 -              if (listbox->block->flag & UI_BLOCK_POPUP) {
 -                      /* popups need special refreshing */
 -                      ED_region_tag_refresh_ui(ar);
 -              }
 -              else {
 -                      ED_region_tag_redraw(ar);
 -              }
 +              ED_region_tag_redraw(ar);
 +              ED_region_tag_refresh_ui(ar);
        }
  
        return retval;
@@@ -8447,9 -8544,6 +8444,9 @@@ static int ui_menu_scroll(ARegion *ar, 
                                dy = block->rect.ymin - ymin + UI_MENU_SCROLL_PAD;
                }
  
 +              /* remember scroll offset for refreshes */
 +              block->handle->scrolloffset += dy;
 +
                /* apply scroll offset */
                for (bt = block->buttons.first; bt; bt = bt->next) {
                        bt->rect.ymin += dy;
@@@ -8602,7 -8696,7 +8599,7 @@@ static int ui_handle_menu_event
  
                                add_v2_v2v2_int(menu->popup_create_vars.event_xy, menu->popup_create_vars.event_xy, mdiff);
  
 -                              ui_popup_translate(C, ar, mdiff);
 +                              ui_popup_translate(ar, mdiff);
                        }
  
                        return retval;
                retval = ui_handle_menu_button(C, event, menu);
        }
  
 +#ifdef USE_UI_POPOVER_ONCE
 +      if (block->flag & UI_BLOCK_POPOVER_ONCE) {
 +              if ((event->type == LEFTMOUSE) && (event->val == KM_RELEASE)) {
 +                      UI_popover_once_clear(menu->popup_create_vars.arg);
 +                      block->flag &= ~UI_BLOCK_POPOVER_ONCE;
 +              }
 +      }
 +#endif
 +
        /* Don't handle double click events, rehandle as regular press/release. */
        if (retval == WM_UI_HANDLER_CONTINUE && event->val == KM_DBL_CLICK) {
                return retval;
@@@ -9614,10 -9699,10 +9611,10 @@@ static int ui_handler_region_menu(bCont
  
                if ((data->state == BUTTON_STATE_MENU_OPEN) &&
                    (is_inside_menu == false) && /* make sure mouse isn't inside another menu (see T43247) */
 -                  (but->type == UI_BTYPE_PULLDOWN) &&
 +                  (ELEM(but->type, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)) &&
                    (but_other = ui_but_find_mouse_over(ar, event)) &&
                    (but != but_other) &&
 -                  (but->type == but_other->type))
 +                  (ELEM(but_other->type, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)))
                {
                        /* if mouse moves to a different root-level menu button,
                         * open it to replace the current menu */
@@@ -9874,18 -9959,3 +9871,18 @@@ void ui_but_clipboard_free(void
  {
        curvemapping_free_data(&but_copypaste_curve);
  }
 +
 +bool UI_but_is_tool(const uiBut *but)
 +{
 +      /* very evil! */
 +      if (but->optype != NULL) {
 +              static wmOperatorType *ot = NULL;
 +              if (ot == NULL) {
 +                      ot = WM_operatortype_find("WM_OT_tool_set_by_name", false);
 +              }
 +              if (but->optype == ot) {
 +                      return true;
 +              }
 +      }
 +      return false;
 +}
index f4c0123567c29246eafa028b89ae3b3fef34afe5,35bd4da4d39d21e04c2b4223492592489e229039..0a794a18b2002fec1d9b31ed998f36aa4607dfee
@@@ -37,7 -37,6 +37,7 @@@
  #include "DNA_armature_types.h"
  #include "DNA_userdef_types.h"
  
 +#include "BLI_alloca.h"
  #include "BLI_listbase.h"
  #include "BLI_string.h"
  #include "BLI_rect.h"
@@@ -50,7 -49,6 +50,7 @@@
  #include "BKE_global.h"
  #include "BKE_idprop.h"
  #include "BKE_screen.h"
 +#include "BKE_animsys.h"
  
  #include "RNA_access.h"
  
  
  #include "interface_intern.h"
  
 +/* Show an icon button after each RNA button to use to quickly set keyframes,
 + * this is a way to display animation/driven/override status, see T54951. */
 +#define UI_PROP_DECORATE
 +/* Alternate draw mode where some buttons can use single icon width,
 + * giving more room for the text at the expense of nicely aligned text. */
 +#define UI_PROP_SEP_ICON_WIDTH_EXCEPTION
 +
  /************************ Structs and Defines *************************/
  
  #define UI_OPERATOR_ERROR_RET(_ot, _opname, return_statement)                 \
@@@ -80,7 -71,6 +80,7 @@@
                return_statement;                                                     \
        } (void)0                                                                 \
  
 +#define UI_ITEM_PROP_SEP_DIVIDE 0.5f
  
  /* uiLayoutRoot */
  
@@@ -110,7 -100,6 +110,7 @@@ typedef enum uiItemType 
        ITEM_LAYOUT_COLUMN,
        ITEM_LAYOUT_COLUMN_FLOW,
        ITEM_LAYOUT_ROW_FLOW,
 +      ITEM_LAYOUT_GRID_FLOW,
        ITEM_LAYOUT_BOX,
        ITEM_LAYOUT_ABSOLUTE,
        ITEM_LAYOUT_SPLIT,
@@@ -139,10 -128,6 +139,10 @@@ enum 
        UI_ITEM_MIN       = 1 << 1,
  
        UI_ITEM_BOX_ITEM  = 1 << 2, /* The item is "inside" a box item */
 +      UI_ITEM_PROP_SEP  = 1 << 3,
 +      /* Show an icon button next to each property (to set keyframes, show status).
 +       * Enabled by default, depends on 'UI_ITEM_PROP_SEP'. */
 +      UI_ITEM_PROP_DECORATE = 1 << 4,
  };
  
  typedef struct uiButtonItem {
@@@ -165,9 -150,7 +165,9 @@@ struct uiLayout 
        bool enabled;
        bool redalert;
        bool keepaspect;
 +      bool variable_size;  /* For layouts inside gridflow, they and their items shall never have a fixed maximal size. */
        char alignment;
 +      char emboss;
  };
  
  typedef struct uiLayoutItemFlow {
        int totcol;
  } uiLayoutItemFlow;
  
 +typedef struct uiLayoutItemGridFlow {
 +      uiLayout litem;
 +
 +      /* Extra parameters */
 +      bool row_major;     /* Fill first row first, instead of filling first column first. */
 +      bool even_columns;  /* Same width for all columns. */
 +      bool even_rows;     /* Same height for all rows. */
 +      /* If positive, absolute fixed number of columns.
 +       * If 0, fully automatic (based on available width).
 +       * If negative, automatic but only generates number of columns/rows multiple of given (absolute) value. */
 +      int num_columns;
 +
 +      /* Pure internal runtime storage. */
 +      int tot_items, tot_columns, tot_rows;
 +} uiLayoutItemGridFlow;
 +
  typedef struct uiLayoutItemBx {
        uiLayout litem;
        uiBut *roundbox;
@@@ -267,37 -234,29 +267,37 @@@ static int ui_layout_vary_direction(uiL
                UI_ITEM_VARY_X : UI_ITEM_VARY_Y);
  }
  
 +static bool ui_layout_variable_size(uiLayout *layout)
 +{
 +      /* Note that this code is probably a bit flacky, we'd probably want to know whether it's variable in X and/or Y,
 +       * etc. But for now it mimics previous one, with addition of variable flag set for children of gridflow layouts. */
 +      return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size;
 +}
 +
  /* estimated size of text + icon */
  static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact)
  {
        bool variable;
 +      const int unit_x = UI_UNIT_X * (layout->scale[0] ? layout->scale[0] : 1.0f);
  
        if (icon && !name[0])
 -              return UI_UNIT_X;  /* icon only */
 +              return unit_x;  /* icon only */
  
 -      variable = (ui_layout_vary_direction(layout) == UI_ITEM_VARY_X);
 +      variable = ui_layout_variable_size(layout);
  
        if (variable) {
                if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) {
                        layout->item.flag |= UI_ITEM_MIN;
                }
                const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
 -              /* it may seem odd that the icon only adds (UI_UNIT_X / 4)
 +              /* it may seem odd that the icon only adds (unit_x / 4)
                 * but taking margins into account its fine */
                return (UI_fontstyle_string_width(fstyle, name) +
 -                      (UI_UNIT_X * ((compact ? 1.25f : 1.50f) +
 -                                    (icon    ? 0.25f : 0.0f))));
 +                      (unit_x * ((compact ? 1.25f : 1.50f) +
 +                                 (icon    ? 0.25f : 0.0f))));
        }
        else {
 -              return UI_UNIT_X * 10;
 +              return unit_x * 10;
        }
  }
  
@@@ -384,7 -343,6 +384,7 @@@ static int ui_layout_local_dir(uiLayou
                        return UI_LAYOUT_HORIZONTAL;
                case ITEM_LAYOUT_COLUMN:
                case ITEM_LAYOUT_COLUMN_FLOW:
 +              case ITEM_LAYOUT_GRID_FLOW:
                case ITEM_LAYOUT_SPLIT:
                case ITEM_LAYOUT_ABSOLUTE:
                case ITEM_LAYOUT_BOX:
@@@ -434,7 -392,7 +434,7 @@@ static void ui_layer_but_cb(bContext *C
  static void ui_item_array(
          uiLayout *layout, uiBlock *block, const char *name, int icon,
          PointerRNA *ptr, PropertyRNA *prop, int len, int x, int y, int w, int UNUSED(h),
 -        bool expand, bool slider, bool toggle, bool icon_only)
 +        bool expand, bool slider, bool toggle, bool icon_only, bool compact, bool show_text)
  {
        uiStyle *style = layout->root->style;
        uiBut *but;
        UI_block_layout_set_current(block, sub);
  
        /* create label */
 -      if (name[0])
 +      if (name[0] && show_text) {
                uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
 +      }
  
        /* create buttons */
        if (type == PROP_BOOLEAN && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) {
                        /* layout for known array subtypes */
                        char str[3] = {'\0'};
  
 -                      if (!icon_only) {
 +                      if (!icon_only && show_text) {
                                if (type != PROP_BOOLEAN) {
                                        str[1] = ':';
                                }
                                RNA_property_boolean_get_array(ptr, prop, boolarr);
                        }
  
 +                      const char *str_buf = show_text ? str: "";
                        for (a = 0; a < len; a++) {
 -                              if (!icon_only) str[0] = RNA_property_array_item_char(prop, a);
 -                              if (boolarr) icon = boolarr[a] ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
 -                              but = uiDefAutoButR(block, ptr, prop, a, str, icon, 0, 0, w, UI_UNIT_Y);
 +                              int width_item;
 +
 +                              if (!icon_only && show_text) {
 +                                      str[0] = RNA_property_array_item_char(prop, a);
 +                              }
 +                              if (boolarr) {
 +                                      icon = boolarr[a] ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
 +                              }
 +
 +                              width_item = (compact && type == PROP_BOOLEAN) ?
 +                                               min_ii(w, ui_text_icon_width(layout, str_buf, icon, false)) : w;
 +
 +                              but = uiDefAutoButR(block, ptr, prop, a, str_buf, icon, 0, 0, width_item, UI_UNIT_Y);
                                if (slider && but->type == UI_BTYPE_NUM)
                                        but->type = UI_BTYPE_NUM_SLIDER;
                                if (toggle && but->type == UI_BTYPE_CHECKBOX)
@@@ -723,49 -669,27 +723,49 @@@ static void ui_keymap_but_cb(bContext *
        RNA_boolean_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) != 0);
  }
  
 -/* create label + button for RNA property */
 -static uiBut *ui_item_with_label(uiLayout *layout, uiBlock *block, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, int x, int y, int w, int h, int flag)
 +/**
 + * Create label + button for RNA property
 + *
 + * \param w_hint: For varying width layout, this becomes the label width.
 + *                Otherwise it's used to fit both items into it.
 + **/
 +static uiBut *ui_item_with_label(
 +        uiLayout *layout, uiBlock *block, const char *name, int icon,
 +        PointerRNA *ptr, PropertyRNA *prop, int index,
 +        int x, int y, int w_hint, int h, int flag)
  {
        uiLayout *sub;
        uiBut *but = NULL;
        PropertyType type;
        PropertySubType subtype;
 -      int labelw;
 +      int prop_but_width = w_hint;
 +      const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0);
  
        sub = uiLayoutRow(layout, layout->align);
        UI_block_layout_set_current(block, sub);
  
        if (name[0]) {
 -              /* XXX UI_fontstyle_string_width is not accurate */
 -#if 0
 -              labelw = UI_fontstyle_string_width(fstyle, name);
 -              CLAMP(labelw, w / 4, 3 * w / 4);
 -#endif
 -              labelw = w / 3;
 -              uiDefBut(block, UI_BTYPE_LABEL, 0, name, x, y, labelw, h, NULL, 0.0, 0.0, 0, 0, "");
 -              w = w - labelw;
 +              int w_label;
 +
 +              if (use_prop_sep) {
 +                      w_label = (int)((w_hint * 2) * UI_ITEM_PROP_SEP_DIVIDE);
 +              }
 +              else {
 +                      if (ui_layout_variable_size(layout)) {
 +                              /* w_hint is width for label in this case. Use a default width for property button(s) */
 +                              prop_but_width = UI_UNIT_X * 5;
 +                              w_label = w_hint;
 +                      }
 +                      else {
 +                              w_label = w_hint / 3;
 +                      }
 +              }
 +
 +              uiBut *but_label = uiDefBut(block, UI_BTYPE_LABEL, 0, name, x, y, w_label, h, NULL, 0.0, 0.0, 0, 0, "");
 +              if (use_prop_sep) {
 +                      but_label->drawflag |= UI_BUT_TEXT_RIGHT;
 +                      but_label->drawflag &= ~UI_BUT_TEXT_LEFT;
 +              }
        }
  
        type = RNA_property_type(prop);
  
        if (subtype == PROP_FILEPATH || subtype == PROP_DIRPATH) {
                UI_block_layout_set_current(block, uiLayoutRow(sub, true));
 -              but = uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, w - UI_UNIT_X, h);
 +              but = uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, prop_but_width - UI_UNIT_X, h);
  
                /* BUTTONS_OT_file_browse calls UI_context_active_but_prop_get_filebrowser */
                uiDefIconButO(block, UI_BTYPE_BUT, subtype == PROP_DIRPATH ? "BUTTONS_OT_directory_browse" : "BUTTONS_OT_file_browse",
                              WM_OP_INVOKE_DEFAULT, ICON_FILESEL, x, y, UI_UNIT_X, h, NULL);
        }
        else if (flag & UI_ITEM_R_EVENT) {
 -              but = uiDefButR_prop(block, UI_BTYPE_KEY_EVENT, 0, name, x, y, w, h, ptr, prop, index, 0, 0, -1, -1, NULL);
 +              but = uiDefButR_prop(block, UI_BTYPE_KEY_EVENT, 0, name, x, y, prop_but_width, h, ptr, prop, index, 0, 0, -1, -1, NULL);
        }
        else if (flag & UI_ITEM_R_FULL_EVENT) {
                if (RNA_struct_is_a(ptr->type, &RNA_KeyMapItem)) {
  
                        WM_keymap_item_to_string(ptr->data, false, buf, sizeof(buf));
  
 -                      but = uiDefButR_prop(block, UI_BTYPE_HOTKEY_EVENT, 0, buf, x, y, w, h, ptr, prop, 0, 0, 0, -1, -1, NULL);
 +                      but = uiDefButR_prop(block, UI_BTYPE_HOTKEY_EVENT, 0, buf, x, y, prop_but_width, h, ptr, prop, 0, 0, 0, -1, -1, NULL);
                        UI_but_func_set(but, ui_keymap_but_cb, but, NULL);
                        if (flag & UI_ITEM_R_IMMEDIATE)
                                UI_but_flag_enable(but, UI_BUT_IMMEDIATE);
                }
        }
 -      else
 -              but = uiDefAutoButR(block, ptr, prop, index, (type == PROP_ENUM && !(flag & UI_ITEM_R_ICON_ONLY)) ? NULL : "", icon, x, y, w, h);
 +      else {
 +              const char *str = (type == PROP_ENUM && !(flag & UI_ITEM_R_ICON_ONLY)) ? NULL : "";
 +              but = uiDefAutoButR(block, ptr, prop, index, str, icon,
 +                                  x, y, prop_but_width, h);
 +      }
  
        UI_block_layout_set_current(block, layout);
        return but;
@@@ -899,10 -820,8 +899,10 @@@ static uiBut *uiItemFullO_ptr_ex
  
        w = ui_text_icon_width(layout, name, icon, 0);
  
 -      if (flag & UI_ITEM_R_NO_BG)
 -              UI_block_emboss_set(block, UI_EMBOSS_NONE);
 +      int prev_emboss = layout->emboss;
 +      if (flag & UI_ITEM_R_NO_BG) {
 +              layout->emboss = UI_EMBOSS_NONE;
 +      }
  
        /* create the button */
        if (icon) {
        if ((layout->root->type == UI_LAYOUT_TOOLBAR) && !icon)
                but->drawflag |= UI_BUT_TEXT_LEFT;
  
 -      if (flag & UI_ITEM_R_NO_BG)
 -              UI_block_emboss_set(block, UI_EMBOSS);
 +      if (flag & UI_ITEM_R_NO_BG) {
 +              layout->emboss = prev_emboss;
 +      }
  
        if (flag & UI_ITEM_O_DEPRESS) {
                but->flag |= UI_SELECT_DRAW;
        return but;
  }
  
 -static void ui_item_hold_menu(struct bContext *C, ARegion *butregion, uiBut *but)
 +static void ui_item_menu_hold(struct bContext *C, ARegion *butregion, uiBut *but)
  {
        uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE);
        uiLayout *layout = UI_popup_menu_layout(pup);
        UI_popup_menu_but_set(pup, butregion, but);
  
        block->flag |= UI_BLOCK_POPUP_HOLD;
 +      block->flag |= UI_BLOCK_IS_FLIP;
 +
 +      char direction = UI_DIR_DOWN;
 +      if (!but->drawstr[0]) {
 +              if (butregion->alignment == RGN_ALIGN_LEFT) {
 +                      direction = UI_DIR_RIGHT;
 +              }
 +              else if (butregion->alignment == RGN_ALIGN_RIGHT) {
 +                      direction = UI_DIR_LEFT;
 +              }
 +              else if (butregion->alignment == RGN_ALIGN_BOTTOM) {
 +                      direction = UI_DIR_UP;
 +              }
 +              else {
 +                      direction = UI_DIR_DOWN;
 +              }
 +      }
 +      UI_block_direction_set(block, direction);
  
        const char *menu_id = but->hold_argN;
        MenuType *mt = WM_menutype_find(menu_id, true);
@@@ -1007,7 -907,7 +1007,7 @@@ void uiItemFullOMenuHold_ptr
          PointerRNA *r_opptr)
  {
        uiBut *but = uiItemFullO_ptr_ex(layout, ot, name, icon, properties, context, flag, r_opptr);
 -      UI_but_func_hold_set(but, ui_item_hold_menu, BLI_strdup(menu_id));
 +      UI_but_func_hold_set(but, ui_item_menu_hold, BLI_strdup(menu_id));
  }
  
  void uiItemFullO(
@@@ -1400,10 -1300,8 +1400,10 @@@ void uiItemO(uiLayout *layout, const ch
  /* RNA property items */
  
  static void ui_item_rna_size(
 -        uiLayout *layout, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop,
 -        int index, bool icon_only, int *r_w, int *r_h)
 +        uiLayout *layout, const char *name, int icon,
 +        PointerRNA *ptr, PropertyRNA *prop,
 +        int index, bool icon_only, bool compact,
 +        int *r_w, int *r_h)
  {
        PropertyType type;
        PropertySubType subtype;
                        RNA_property_enum_items_gettexted(layout->root->block->evil_C, ptr, prop, &item_array, NULL, &free);
                        for (item = item_array; item->identifier; item++) {
                                if (item->identifier[0]) {
 -                                      w = max_ii(w, ui_text_icon_width(layout, item->name, item->icon, 0));
 +                                      w = max_ii(w, ui_text_icon_width(layout, item->name, item->icon, compact));
                                }
                        }
                        if (free) {
  
        if (!w) {
                if (type == PROP_ENUM && icon_only) {
 -                      w = ui_text_icon_width(layout, "", ICON_BLANK1, 0);
 +                      w = ui_text_icon_width(layout, "", ICON_BLANK1, compact);
                        if (index != RNA_ENUM_VALUE)
                                w += 0.6f * UI_UNIT_X;
                }
                else {
 -                      w = ui_text_icon_width(layout, name, icon, 0);
 +                      /* not compact for float/int buttons, looks too squashed */
 +                      w = ui_text_icon_width(layout, name, icon, ELEM(type, PROP_FLOAT, PROP_INT) ? false : compact);
                }
        }
        h = UI_UNIT_Y;
        if (index == RNA_NO_INDEX && len > 0) {
                if (!name[0] && icon == ICON_NONE)
                        h = 0;
 -
 +              if (layout->item.flag & UI_ITEM_PROP_SEP)
 +                      h = 0;
                if (ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER))
                        h += 2 * UI_UNIT_Y;
                else if (subtype == PROP_MATRIX)
                else
                        h += len * UI_UNIT_Y;
        }
 -      else if (ui_layout_vary_direction(layout) == UI_ITEM_VARY_X) {
 +      else if (ui_layout_variable_size(layout)) {
                if (type == PROP_BOOLEAN && name[0])
                        w += UI_UNIT_X / 5;
                else if (type == PROP_ENUM && !icon_only)
@@@ -1484,22 -1380,8 +1484,22 @@@ void uiItemFullR(uiLayout *layout, Poin
        PropertyType type;
        char namestr[UI_MAX_NAME_STR];
        int len, w, h;
 -      bool slider, toggle, expand, icon_only, no_bg;
 +      bool slider, toggle, expand, icon_only, no_bg, compact;
        bool is_array;
 +      const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0);
 +
 +#ifdef UI_PROP_DECORATE
 +      struct {
 +              bool use_prop_decorate;
 +              int len;
 +              uiLayout *layout;
 +              uiBut *but;
 +      } ui_decorate = {
 +              .use_prop_decorate = (
 +                      ((layout->item.flag & UI_ITEM_PROP_DECORATE) != 0) &&
 +                      (use_prop_sep && ptr->id.data && id_can_have_animdata(ptr->id.data))),
 +      };
 +#endif  /* UI_PROP_DECORATE */
  
        UI_block_layout_set_current(block, layout);
  
                /* pass */
        }
        else if (ELEM(type, PROP_INT, PROP_FLOAT, PROP_STRING, PROP_POINTER)) {
 -              name = ui_item_name_add_colon(name, namestr);
 +              if (use_prop_sep == false) {
 +                      name = ui_item_name_add_colon(name, namestr);
 +              }
        }
        else if (type == PROP_BOOLEAN && is_array && index == RNA_NO_INDEX) {
 -              name = ui_item_name_add_colon(name, namestr);
 +              if (use_prop_sep == false) {
 +                      name = ui_item_name_add_colon(name, namestr);
 +              }
        }
        else if (type == PROP_ENUM && index != RNA_ENUM_VALUE) {
 -              name = ui_item_name_add_colon(name, namestr);
 +              if (flag & UI_ITEM_R_COMPACT) {
 +                      name = "";
 +              }
 +              else {
 +                      if (use_prop_sep == false) {
 +                              name = ui_item_name_add_colon(name, namestr);
 +                      }
 +              }
        }
  
        /* menus and pie-menus don't show checkbox without this */
        expand = (flag & UI_ITEM_R_EXPAND) != 0;
        icon_only = (flag & UI_ITEM_R_ICON_ONLY) != 0;
        no_bg = (flag & UI_ITEM_R_NO_BG) != 0;
 +      compact = (flag & UI_ITEM_R_COMPACT) != 0;
  
        /* get size */
 -      ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, &w, &h);
 +      ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, compact, &w, &h);
 +
 +      int prev_emboss = layout->emboss;
 +      if (no_bg) {
 +              layout->emboss = UI_EMBOSS_NONE;
 +      }
 +
 +      /* Split the label / property. */
 +      if (use_prop_sep) {
 +              uiLayout *layout_row = NULL;
 +#ifdef UI_PROP_DECORATE
 +              if (ui_decorate.use_prop_decorate) {
 +                      layout_row = uiLayoutRow(layout, true);
 +                      layout_row->space = 0;
 +                      ui_decorate.len = max_ii(1, len);
 +              }
 +#endif  /* UI_PROP_DECORATE */
 +
 +              if (name[0] == '\0') {
 +                      /* Ensure we get a column when text is not set. */
 +                      layout = uiLayoutColumn(layout_row ? layout_row : layout, true);
 +                      layout->space = 0;
 +              }
 +              else {
 +                      const PropertySubType subtype = RNA_property_subtype(prop);
 +                      uiLayout *layout_split;
 +#ifdef UI_PROP_SEP_ICON_WIDTH_EXCEPTION
 +                      if (type == PROP_BOOLEAN && (icon == ICON_NONE) && !icon_only) {
 +                              w = UI_UNIT_X;
 +                              layout_split = uiLayoutRow(layout_row ? layout_row : layout, true);
 +                      }
 +                      else
 +#endif  /* UI_PROP_SEP_ICON_WIDTH_EXCEPTION */
 +                      {
 +                              layout_split = uiLayoutSplit(
 +                                      layout_row ? layout_row : layout,
 +                                      UI_ITEM_PROP_SEP_DIVIDE, true);
 +                      }
 +                      layout_split->space = 0;
 +                      uiLayout *layout_sub = uiLayoutColumn(layout_split, true);
 +                      layout_sub->space = 0;
 +
 +                      if ((index == RNA_NO_INDEX && is_array) &&
 +                          ((!expand && ELEM(subtype, PROP_COLOR, PROP_COLOR_GAMMA, PROP_DIRECTION)) == 0))
 +                      {
 +                              char name_with_suffix[UI_MAX_DRAW_STR + 2];
 +                              char str[2] = {'\0'};
 +                              for (int a = 0; a < len; a++) {
 +                                      str[0] = RNA_property_array_item_char(prop, a);
 +                                      const bool use_prefix = (a == 0 && name && name[0]);
 +                                      if (use_prefix) {
 +                                              char *s = name_with_suffix;
 +                                              s += STRNCPY_RLEN(name_with_suffix, name);
 +                                              *s++ = ' ';
 +                                              *s++ = str[0];
 +                                              *s++ = '\0';
 +                                      }
 +                                      but = uiDefBut(
 +                                              block, UI_BTYPE_LABEL, 0, use_prefix ? name_with_suffix : str,
 +                                              0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
 +                                      but->drawflag |= UI_BUT_TEXT_RIGHT;
 +                                      but->drawflag &= ~UI_BUT_TEXT_LEFT;
 +                              }
 +                      }
 +                      else {
 +                              if (name) {
 +                                      but = uiDefBut(
 +                                              block, UI_BTYPE_LABEL, 0, name,
 +                                              0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
 +                                      but->drawflag |= UI_BUT_TEXT_RIGHT;
 +                                      but->drawflag &= ~UI_BUT_TEXT_LEFT;
 +                              }
 +                      }
  
 -      if (no_bg)
 -              UI_block_emboss_set(block, UI_EMBOSS_NONE);
 +                      /* Watch out! We can only write into the new column now. */
 +                      layout = uiLayoutColumn(layout_split, true);
 +                      layout->space = 0;
 +                      if ((type == PROP_ENUM) && (flag & UI_ITEM_R_EXPAND)) {
 +                              /* pass (expanded enums each have their own name) */
 +                      }
 +                      else {
 +                              name = "";
 +                      }
 +              }
 +
 +#ifdef UI_PROP_DECORATE
 +              if (ui_decorate.use_prop_decorate) {
 +                      ui_decorate.layout = uiLayoutColumn(layout_row, true);
 +                      ui_decorate.layout->space = 0;
 +                      UI_block_layout_set_current(block, layout);
 +                      ui_decorate.but = block->buttons.last;
 +              }
 +#endif  /* UI_PROP_DECORATE */
 +      }
 +      /* End split. */
  
        /* array property */
 -      if (index == RNA_NO_INDEX && is_array)
 -              ui_item_array(layout, block, name, icon, ptr, prop, len, 0, 0, w, h, expand, slider, toggle, icon_only);
 +      if (index == RNA_NO_INDEX && is_array) {
 +              ui_item_array(
 +                      layout, block, name, icon, ptr, prop, len, 0, 0, w, h,
 +                      expand, slider, toggle, icon_only, compact, !use_prop_sep);
 +      }
        /* enum item */
        else if (type == PROP_ENUM && index == RNA_ENUM_VALUE) {
                if (icon && name[0] && !icon_only)
        }
  
        /* Mark non-embossed textfields inside a listbox. */
-       if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->dt & UI_EMBOSS_NONE)) {
+       if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == UI_BTYPE_TEXT) && (but->dt & UI_EMBOSS_NONE)) {
                UI_but_flag_enable(but, UI_BUT_LIST_ITEM);
        }
  
 -      if (no_bg)
 -              UI_block_emboss_set(block, UI_EMBOSS);
 +#ifdef UI_PROP_DECORATE
 +      if (ui_decorate.use_prop_decorate) {
 +              const bool is_anim = RNA_property_animateable(ptr, prop);
 +              uiBut *but_decorate = ui_decorate.but ? ui_decorate.but->next : block->buttons.first;
 +              uiLayout *layout_col = uiLayoutColumn(ui_decorate.layout, false);
 +              layout_col->space = 0;
 +              layout_col->emboss = UI_EMBOSS_NONE;
 +              int i;
 +              for (i = 0; i < ui_decorate.len && but_decorate; i++) {
 +                      /* The icons are set in 'ui_but_anim_flag' */
 +                      if (is_anim) {
 +                              but = uiDefIconBut(
 +                                      block, UI_BTYPE_BUT, 0, ICON_DOT, 0, 0, UI_UNIT_X, UI_UNIT_Y,
 +                                      NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Animate property"));
 +                              UI_but_func_set(but, ui_but_anim_decorate_cb, but, NULL);
 +                              but->flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK;
 +                      }
 +                      else {
 +                              /* We may show other information here in future, for now use empty space. */
 +                              but = uiDefIconBut(
 +                                      block, UI_BTYPE_BUT, 0, ICON_BLANK1, 0, 0, UI_UNIT_X, UI_UNIT_Y,
 +                                      NULL, 0.0, 0.0, 0.0, 0.0, "");
 +                              but->flag |= UI_BUT_DISABLED;
 +                      }
 +                      /* Order the decorator after the button we decorate, this is used so we can always
 +                       * do a quick lookup. */
 +                      BLI_remlink(&block->buttons, but);
 +                      BLI_insertlinkafter(&block->buttons, but_decorate, but);
 +                      but_decorate = but->next;
 +              }
 +              BLI_assert(ELEM(i, 1, ui_decorate.len));
 +      }
 +#endif  /* UI_PROP_DECORATE */
 +
 +      if (no_bg) {
 +              layout->emboss = prev_emboss;
 +      }
  
        /* ensure text isn't added to icon_only buttons */
        if (but && icon_only) {
@@@ -1898,6 -1639,94 +1898,6 @@@ void uiItemsEnumR(uiLayout *layout, str
  
  /* Pointer RNA button with search */
  
 -typedef struct CollItemSearch {
 -      struct CollItemSearch *next, *prev;
 -      char *name;
 -      int index;
 -      int iconid;
 -} CollItemSearch;
 -
 -static int sort_search_items_list(const void *a, const void *b)
 -{
 -      const CollItemSearch *cis1 = a;
 -      const CollItemSearch *cis2 = b;
 -
 -      if (BLI_strcasecmp(cis1->name, cis2->name) > 0)
 -              return 1;
 -      else
 -              return 0;
 -}
 -
 -static void rna_search_cb(const struct bContext *C, void *arg_but, const char *str, uiSearchItems *items)
 -{
 -      uiBut *but = arg_but;
 -      char *name;
 -      int i = 0, iconid = 0, flag = RNA_property_flag(but->rnaprop);
 -      ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
 -      CollItemSearch *cis;
 -      const bool skip_filter = !but->changed;
 -
 -      /* build a temporary list of relevant items first */
 -      RNA_PROP_BEGIN (&but->rnasearchpoin, itemptr, but->rnasearchprop)
 -      {
 -              if (flag & PROP_ID_SELF_CHECK)
 -                      if (itemptr.data == but->rnapoin.id.data)
 -                              continue;
 -
 -              /* use filter */
 -              if (RNA_property_type(but->rnaprop) == PROP_POINTER) {
 -                      if (RNA_property_pointer_poll(&but->rnapoin, but->rnaprop, &itemptr) == 0)
 -                              continue;
 -              }
 -
 -              if (itemptr.type && RNA_struct_is_ID(itemptr.type)) {
 -                      ID *id = itemptr.data;
 -                      char name_ui[MAX_ID_NAME];
 -
 -#if 0       /* this name is used for a string comparison and can't be modified, TODO */
 -                      /* if ever enabled, make name_ui be MAX_ID_NAME+1 */
 -                      BKE_id_ui_prefix(name_ui, id);
 -#else
 -                      BLI_strncpy(name_ui, id->name + 2, sizeof(name_ui));
 -#endif
 -                      name = BLI_strdup(name_ui);
 -                      iconid = ui_id_icon_get(C, id, false);
 -              }
 -              else {
 -                      name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); /* could use the string length here */
 -                      iconid = 0;
 -              }
 -
 -              if (name) {
 -                      if (skip_filter || BLI_strcasestr(name, str)) {
 -                              cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch");
 -                              cis->name = MEM_dupallocN(name);
 -                              cis->index = i;
 -                              cis->iconid = iconid;
 -                              BLI_addtail(items_list, cis);
 -                      }
 -                      MEM_freeN(name);
 -              }
 -
 -              i++;
 -      }
 -      RNA_PROP_END;
 -
 -      BLI_listbase_sort(items_list, sort_search_items_list);
 -
 -      /* add search items from temporary list */
 -      for (cis = items_list->first; cis; cis = cis->next) {
 -              if (false == UI_search_item_add(items, cis->name, SET_INT_IN_POINTER(cis->index), cis->iconid)) {
 -                      break;
 -              }
 -      }
 -
 -      for (cis = items_list->first; cis; cis = cis->next) {
 -              MEM_freeN(cis->name);
 -      }
 -      BLI_freelistN(items_list);
 -      MEM_freeN(items_list);
 -}
  
  static void search_id_collection(StructRNA *ptype, PointerRNA *ptr, PropertyRNA **prop)
  {
@@@ -1940,8 -1769,6 +1940,8 @@@ void ui_but_add_search(uiBut *but, Poin
  
        /* turn button into search button */
        if (searchprop) {
 +              uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__);
 +
                but->type = UI_BTYPE_SEARCH_MENU;
                but->hardmax = MAX2(but->hardmax, 256.0f);
                but->rnasearchpoin = *searchptr;
                        but->flag |= UI_BUT_VALUE_CLEAR;
                }
  
 +              coll_search->target_ptr = *ptr;
 +              coll_search->target_prop = prop;
 +              coll_search->search_ptr = *searchptr;
 +              coll_search->search_prop = searchprop;
 +              coll_search->but_changed = &but->changed;
 +
                if (RNA_property_type(prop) == PROP_ENUM) {
                        /* XXX, this will have a menu string,
                         * but in this case we just want the text */
                        but->str[0] = 0;
                }
  
 -              UI_but_func_search_set(but, ui_searchbox_create_generic, rna_search_cb, but, NULL, NULL);
 +              UI_but_func_search_set(
 +                          but, ui_searchbox_create_generic, ui_rna_collection_search_cb,
 +                          coll_search, NULL, NULL);
 +              but->free_search_arg = true;
        }
        else if (but->type == UI_BTYPE_SEARCH_MENU) {
                /* In case we fail to find proper searchprop, so other code might have already set but->type to search menu... */
@@@ -1983,7 -1801,6 +1983,7 @@@ void uiItemPointerR(uiLayout *layout, s
        StructRNA *icontype;
        int w, h;
        char namestr[UI_MAX_NAME_STR];
 +      const bool use_prop_sep = ((layout->item.flag & UI_ITEM_PROP_SEP) != 0);
  
        /* validate arguments */
        prop = RNA_struct_find_property(ptr, propname);
        if (!name)
                name = RNA_property_ui_name(prop);
  
 -      name = ui_item_name_add_colon(name, namestr);
 +      if (use_prop_sep == false) {
 +              name = ui_item_name_add_colon(name, namestr);
 +      }
  
        /* create button */
        block = uiLayoutGetBlock(layout);
  
 -      ui_item_rna_size(layout, name, icon, ptr, prop, 0, 0, &w, &h);
 +      ui_item_rna_size(layout, name, icon, ptr, prop, 0, 0, false, &w, &h);
        w += UI_UNIT_X; /* X icon needs more space */
        but = ui_item_with_label(layout, block, name, icon, ptr, prop, 0, 0, 0, w, h, 0);
  
@@@ -2051,15 -1866,6 +2051,15 @@@ static void ui_item_menutype_func(bCont
        layout->root->block->flag ^= UI_BLOCK_IS_FLIP;
  }
  
 +void ui_item_paneltype_func(bContext *C, uiLayout *layout, void *arg_pt)
 +{
 +      PanelType *pt = (PanelType *)arg_pt;
 +      UI_paneltype_draw(C, pt, layout);
 +
 +      /* panels are created flipped (from event handling pov) */
 +      layout->root->block->flag ^= UI_BLOCK_IS_FLIP;
 +}
 +
  static uiBut *ui_item_menu(
          uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg, void *argN,
          const char *tip, bool force_menu)
  
        UI_block_layout_set_current(block, layout);
  
 -      if (layout->root->type == UI_LAYOUT_HEADER)
 -              UI_block_emboss_set(block, UI_EMBOSS);
 -
        if (!name)
                name = "";
        if (layout->root->type == UI_LAYOUT_MENU && !icon)
        h = UI_UNIT_Y;
  
        if (layout->root->type == UI_LAYOUT_HEADER) { /* ugly .. */
 -              if (force_menu) {
 +              if (icon == ICON_NONE && force_menu) {
 +                      /* pass */
 +              }
 +              else if (force_menu) {
                        w += UI_UNIT_X;
                }
                else {
                but->func_argN = argN;
        }
  
 -      if (layout->root->type == UI_LAYOUT_HEADER) {
 -              UI_block_emboss_set(block, UI_EMBOSS);
 -      }
        if (ELEM(layout->root->type, UI_LAYOUT_PANEL, UI_LAYOUT_TOOLBAR) ||
            (force_menu && layout->root->type != UI_LAYOUT_MENU))  /* We never want a dropdown in menu! */
        {
@@@ -2134,95 -1943,6 +2134,95 @@@ void uiItemM(uiLayout *layout, bContex
        ui_item_menu(layout, name, icon, ui_item_menutype_func, mt, NULL, TIP_(mt->description), false);
  }
  
 +/* popover */
 +void uiItemPopoverPanel_ptr(uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon)
 +{
 +      if (!name) {
 +              name = CTX_IFACE_(pt->translation_context, pt->label);
 +      }
 +
 +      if (layout->root->type == UI_LAYOUT_MENU && !icon) {
 +              icon = ICON_BLANK1;
 +      }
 +
 +      const bool ok = (pt->poll == NULL) || pt->poll(C, pt);
 +      if (ok && (pt->draw_header != NULL)) {
 +              layout = uiLayoutRow(layout, true);
 +              Panel panel = {
 +                      .type = pt,
 +                      .layout = layout,
 +                      .flag = PNL_POPOVER,
 +              };
 +              pt->draw_header(C, &panel);
 +      }
 +      uiBut *but = ui_item_menu(layout, name, icon, ui_item_paneltype_func, pt, NULL, NULL, true);
 +      but->type = UI_BTYPE_POPOVER;
 +      if (!ok) {
 +              but->flag |= UI_BUT_DISABLED;
 +      }
 +}
 +
 +void uiItemPopoverPanel(
 +        uiLayout *layout, bContext *C,
 +        int space_id, int region_id, const char *panel_type,
 +        const char *name, int icon)
 +{
 +      SpaceType *st = BKE_spacetype_from_id(space_id);
 +      if (st == NULL) {
 +              RNA_warning("space type not found %d", space_id);
 +              return;
 +      }
 +      ARegionType *art = BKE_regiontype_from_id(st, region_id);
 +      if (art == NULL) {
 +              RNA_warning("region type not found %d", region_id);
 +              return;
 +      }
 +
 +      PanelType *pt;
 +      for (pt = art->paneltypes.first; pt; pt = pt->next) {
 +              if (STREQ(pt->idname, panel_type)) {
 +                      break;
 +              }
 +      }
 +
 +      if (pt == NULL) {
 +              RNA_warning("area type not found %s", panel_type);
 +              return;
 +      }
 +
 +      uiItemPopoverPanel_ptr(layout, C, pt, name, icon);
 +}
 +
 +void uiItemPopoverPanelFromGroup(
 +        uiLayout *layout, bContext *C,
 +        int space_id, int region_id, const char *context, const char *category)
 +{
 +      SpaceType *st = BKE_spacetype_from_id(space_id);
 +      if (st == NULL) {
 +              RNA_warning("space type not found %d", space_id);
 +              return;
 +      }
 +      ARegionType *art = BKE_regiontype_from_id(st, region_id);
 +      if (art == NULL) {
 +              RNA_warning("region type not found %d", region_id);
 +              return;
 +      }
 +
 +      for (PanelType *pt = art->paneltypes.first; pt; pt = pt->next) {
 +              /* Causes too many panels, check context. */
 +              if (pt->parent_id[0] == '\0') {
 +                      if (/* (*context == '\0') || */ STREQ(pt->context, context)) {
 +                              if ((*category == '\0') || STREQ(pt->category, category)) {
 +                                      if (pt->poll == NULL || pt->poll(C, pt)) {
 +                                              uiItemPopoverPanel_ptr(layout, C, pt, NULL, ICON_NONE);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +
  /* label item */
  static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon)
  {
@@@ -2313,26 -2033,6 +2313,26 @@@ void uiItemS(uiLayout *layout
        uiDefBut(block, (is_menu) ? UI_BTYPE_SEPR_LINE : UI_BTYPE_SEPR, 0, "", 0, 0, space, space, NULL, 0.0, 0.0, 0, 0, "");
  }
  
 +/* Flexible spacing. */
 +void uiItemSpacer(uiLayout *layout)
 +{
 +      uiBlock *block = layout->root->block;
 +      bool is_menu = ui_block_is_menu(block);
 +
 +      if (is_menu) {
 +              printf("Error: separator_spacer() not supported in menus.\n");
 +              return;
 +      }
 +
 +      if (block->direction & UI_DIR_RIGHT) {
 +              printf("Error: separator_spacer() only supported in horizontal blocks.\n");
 +              return;
 +      }
 +
 +      UI_block_layout_set_current(block, layout);
 +      uiDefBut(block, UI_BTYPE_SEPR_SPACER, 0, "", 0, 0, 0.3f * UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
 +}
 +
  /* level items */
  void uiItemMenuF(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg)
  {
@@@ -2716,7 -2416,7 +2716,7 @@@ static bool ui_item_is_radial_displayab
  static bool ui_item_is_radial_drawable(uiButtonItem *bitem)
  {
  
 -      if (ELEM(bitem->but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE))
 +      if (ELEM(bitem->but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER))
                return false;
  
        return true;
@@@ -2989,364 -2689,6 +2989,364 @@@ static void ui_litem_layout_column_flow
        litem->y = miny;
  }
  
 +/* multi-column and multi-row layout. */
 +typedef struct UILayoutGridFlowInput {
 +      /* General layout controll settings. */
 +      const bool row_major : 1;  /* Fill rows before columns */
 +      const bool even_columns : 1;  /* All columns will have same width. */
 +      const bool even_rows : 1;  /* All rows will have same height. */
 +      const int space_x;  /* Space between columns. */
 +      const int space_y;  /* Space between rows. */
 +      /* Real data about current position and size of this layout item (either estimated, or final values). */
 +      const int litem_w;  /* Layout item width. */
 +      const int litem_x;  /* Layout item X position. */
 +      const int litem_y;  /* Layout item Y position. */
 +      /* Actual number of columns and rows to generate (computed from first pass usually). */
 +      const int tot_columns;  /* Number of columns. */
 +      const int tot_rows;  /* Number of rows. */
 +} UILayoutGridFlowInput;
 +
 +typedef struct UILayoutGridFlowOutput {
 +      int *tot_items;  /* Total number of items in this grid layout. */
 +      /* Width / X pos data. */
 +      float *global_avg_w;  /* Computed average width of the columns. */
 +      int *cos_x_array;  /* Computed X coordinate of each column. */
 +      int *widths_array;  /* Computed width of each column. */
 +      int *tot_w;  /* Computed total width. */
 +      /* Height / Y pos data. */
 +      int *global_max_h;  /* Computed height of the tallest item in the grid. */
 +      int *cos_y_array;  /* Computed Y coordinate of each column. */
 +      int *heights_array;  /* Computed height of each column. */
 +      int *tot_h;  /* Computed total height. */
 +} UILayoutGridFlowOutput;
 +
 +static void ui_litem_grid_flow_compute(
 +        ListBase *items, UILayoutGridFlowInput *parameters, UILayoutGridFlowOutput *results)
 +{
 +      uiItem *item;
 +      int i;
 +
 +      float tot_w = 0.0f, tot_h = 0.0f;
 +      float global_avg_w = 0.0f, global_totweight_w = 0.0f;
 +      int global_max_h = 0;
 +
 +      float *avg_w = NULL, *totweight_w = NULL;
 +      int *max_h = NULL;
 +
 +      BLI_assert(parameters->tot_columns != 0 || (results->cos_x_array == NULL && results->widths_array == NULL && results->tot_w == NULL));
 +      BLI_assert(parameters->tot_rows != 0 || (results->cos_y_array == NULL && results->heights_array == NULL && results->tot_h == NULL));
 +
 +      if (results->tot_items) {
 +              *results->tot_items = 0;
 +      }
 +
 +      if (items->first == NULL) {
 +              if (results->global_avg_w) {
 +                      *results->global_avg_w = 0.0f;
 +              }
 +              if (results->global_max_h) {
 +                      *results->global_max_h = 0;
 +              }
 +              return;
 +      }
 +
 +      if (parameters->tot_columns != 0) {
 +              avg_w = BLI_array_alloca(avg_w, parameters->tot_columns);
 +              totweight_w = BLI_array_alloca(totweight_w, parameters->tot_columns);
 +              memset(avg_w, 0, sizeof(*avg_w) * parameters->tot_columns);
 +              memset(totweight_w, 0, sizeof(*totweight_w) * parameters->tot_columns);
 +      }
 +      if (parameters->tot_rows != 0) {
 +              max_h = BLI_array_alloca(max_h, parameters->tot_rows);
 +              memset(max_h, 0, sizeof(*max_h) * parameters->tot_rows);
 +      }
 +
 +      for (i = 0, item = items->first; item; item = item->next, i++) {
 +              int item_w, item_h;
 +              ui_item_size(item, &item_w, &item_h);
 +
 +              global_avg_w += (float)(item_w * item_w);
 +              global_totweight_w += (float)item_w;
 +              global_max_h = max_ii(global_max_h, item_h);
 +
 +              if (parameters->tot_rows != 0 && parameters->tot_columns != 0) {
 +                      const int index_col = parameters->row_major ? i % parameters->tot_columns : i / parameters->tot_rows;
 +                      const int index_row = parameters->row_major ? i / parameters->tot_columns : i % parameters->tot_rows;
 +
 +                      avg_w[index_col] += (float)(item_w * item_w);
 +                      totweight_w[index_col] += (float)item_w;
 +
 +                      max_h[index_row] = max_ii(max_h[index_row], item_h);
 +              }
 +
 +              if (results->tot_items) {
 +                      (*results->tot_items)++;
 +              }
 +      }
 +
 +      /* Finalize computing of column average sizes */
 +      global_avg_w /= global_totweight_w;
 +      if (parameters->tot_columns != 0) {
 +              for (i = 0; i < parameters->tot_columns; i++) {
 +                      avg_w[i] /= totweight_w[i];
 +                      tot_w += avg_w[i];
 +              }
 +              if (parameters->even_columns) {
 +                      tot_w = ceilf(global_avg_w) * parameters->tot_columns;
 +              }
 +      }
 +      /* Finalize computing of rows max sizes */
 +      if (parameters->tot_rows != 0) {
 +              for (i = 0; i < parameters->tot_rows; i++) {
 +                      tot_h += max_h[i];
 +              }
 +              if (parameters->even_rows) {
 +                      tot_h = global_max_h * parameters->tot_columns;
 +              }
 +      }
 +
 +      /* Compute positions and sizes of all cells. */
 +      if (results->cos_x_array != NULL && results->widths_array != NULL) {
 +              /* We enlarge/narrow columns evenly to match available width. */
 +              const float wfac = (float)(parameters->litem_w - (parameters->tot_columns - 1) * parameters->space_x) / tot_w;
 +
 +              for (int col = 0; col < parameters->tot_columns; col++) {
 +                      results->cos_x_array[col] = (
 +                              col ?
 +                              results->cos_x_array[col - 1] + results->widths_array[col - 1] + parameters->space_x :
 +                              parameters->litem_x
 +                      );
 +                      if (parameters->even_columns) {
 +                              /* (< remaining width > - < space between remaining columns >) / < remaining columns > */
 +                              results->widths_array[col] = (
 +                                      ((parameters->litem_w - (results->cos_x_array[col] - parameters->litem_x)) -
 +                                       (parameters->tot_columns - col - 1) * parameters->space_x) / (parameters->tot_columns - col));
 +                      }
 +                      else if (col == parameters->tot_columns - 1) {
 +                              /* Last column copes width rounding errors... */
 +                              results->widths_array[col] = parameters->litem_w - (results->cos_x_array[col] - parameters->litem_x);
 +                      }
 +                      else {
 +                              results->widths_array[col] = (int)(avg_w[col] * wfac);
 +                      }
 +              }
 +      }
 +      if (results->cos_y_array != NULL && results->heights_array != NULL) {
 +              for (int row = 0; row < parameters->tot_rows; row++) {
 +                      if (parameters->even_rows) {
 +                              results->heights_array[row] = global_max_h;
 +                      }
 +                      else {
 +                              results->heights_array[row] = max_h[row];
 +                      }
 +                      results->cos_y_array[row] = (
 +                              row ?
 +                              results->cos_y_array[row - 1] - parameters->space_y - results->heights_array[row] :
 +                              parameters->litem_y - results->heights_array[row]);
 +              }
 +      }
 +
 +      if (results->global_avg_w) {
 +              *results->global_avg_w = global_avg_w;
 +      }
 +      if (results->global_max_h) {
 +              *results->global_max_h = global_max_h;
 +      }
 +      if (results->tot_w) {
 +              *results->tot_w = (int)tot_w + parameters->space_x * (parameters->tot_columns - 1);
 +      }
 +      if (results->tot_h) {
 +              *results->tot_h = tot_h + parameters->space_y * (parameters->tot_rows - 1);
 +      }
 +}
 +
 +static void ui_litem_estimate_grid_flow(uiLayout *litem)
 +{
 +      uiStyle *style = litem->root->style;
 +      uiLayoutItemGridFlow *gflow = (uiLayoutItemGridFlow *)litem;
 +
 +      const int space_x = style->columnspace;
 +      const int space_y = style->buttonspacey;
 +
 +      /* Estimate average needed width and height per item. */
 +      {
 +              float avg_w;
 +              int max_h;
 +
 +              ui_litem_grid_flow_compute(
 +                      &litem->items,
 +                      &((UILayoutGridFlowInput) {
 +                            .row_major = gflow->row_major,
 +                            .even_columns = gflow->even_columns,
 +                            .even_rows = gflow->even_rows,
 +                            .litem_w = litem->w,
 +                            .litem_x = litem->x,
 +                            .litem_y = litem->y,
 +                            .space_x = space_x,
 +                            .space_y = space_y,
 +                      }),
 +                      &((UILayoutGridFlowOutput) {
 +                            .tot_items = &gflow->tot_items,
 +                            .global_avg_w = &avg_w,
 +                            .global_max_h = &max_h,
 +                      }));
 +
 +              if (gflow->tot_items == 0) {
 +                      litem->w = litem->h = 0;
 +                      gflow->tot_columns = gflow->tot_rows = 0;
 +                      return;
 +              }
 +
 +              /* Even in varying column width case, we fix our columns number from weighted average width of items,
 +               * a proper solving of required width would be too costly, and this should give reasonably good results
 +               * in all resonable cases... */
 +              if (gflow->num_columns > 0) {
 +                      gflow->tot_columns = gflow->num_columns;
 +              }
 +              else {
 +                      if (avg_w == 0.0f) {
 +                              gflow->tot_columns = 1;
 +                      }
 +                      else {
 +                              gflow->tot_columns = min_ii(max_ii((int)(litem->w / avg_w), 1), gflow->tot_items);
 +                      }
 +              }
 +              gflow->tot_rows = (int)ceilf((float)gflow->tot_items / gflow->tot_columns);
 +
 +              /* Try to tweak number of columns and rows to get better filling of last column or row,
 +               * and apply 'modulo' value to number of columns or rows.
 +               * Note that modulo does not prevent ending with fewer columns/rows than modulo, if mandatory
 +               * to avoid empty column/row. */
 +              {
 +                      const int modulo = (gflow->num_columns < -1) ? -gflow->num_columns : 0;
 +                      const int step = modulo ? modulo : 1;
 +
 +                      if (gflow->row_major) {
 +                              /* Adjust number of columns to be mutiple of given modulo. */
 +                              if (modulo && gflow->tot_columns % modulo != 0 && gflow->tot_columns > modulo) {
 +                                      gflow->tot_columns = gflow->tot_columns - (gflow->tot_columns % modulo);
 +                              }
 +                              /* Find smallest number of columns conserving computed optimal number of rows. */
 +                              for (gflow->tot_rows = (int)ceilf((float)gflow->tot_items / gflow->tot_columns);
 +                                   (gflow->tot_columns - step) > 0 &&
 +                                   (int)ceilf((float)gflow->tot_items / (gflow->tot_columns - step)) <= gflow->tot_rows;
 +                                   gflow->tot_columns -= step);
 +                      }
 +                      else {
 +                              /* Adjust number of rows to be mutiple of given modulo. */
 +                              if (modulo && gflow->tot_rows % modulo != 0) {
 +                                      gflow->tot_rows = min_ii(gflow->tot_rows + modulo - (gflow->tot_rows % modulo), gflow->tot_items);
 +                              }
 +                              /* Find smallest number of rows conserving computed optimal number of columns. */
 +                              for (gflow->tot_columns = (int)ceilf((float)gflow->tot_items / gflow->tot_rows);
 +                                   (gflow->tot_rows - step) > 0 &&
 +                                   (int)ceilf((float)gflow->tot_items / (gflow->tot_rows - step)) <= gflow->tot_columns;
 +                                   gflow->tot_rows -= step);
 +                      }
 +              }
 +
 +              /* Set evenly-spaced axes size (quick optimization in case we have even columns and rows). */
 +              if (gflow->even_columns && gflow->even_rows) {
 +                      litem->w = (int)(gflow->tot_columns * avg_w) + space_x * (gflow->tot_columns - 1);
 +                      litem->h = (int)(gflow->tot_rows * max_h) + space_y * (gflow->tot_rows - 1);
 +                      return;
 +              }
 +      }
 +
 +      /* Now that we have our final number of columns and rows,
 +       * we can compute actual needed space for non-evenly sized axes. */
 +      {
 +              int tot_w, tot_h;
 +
 +              ui_litem_grid_flow_compute(
 +                      &litem->items,
 +                      &((UILayoutGridFlowInput) {
 +                            .row_major = gflow->row_major,
 +                            .even_columns = gflow->even_columns,
 +                            .even_rows = gflow->even_rows,
 +                            .litem_w = litem->w,
 +                            .litem_x = litem->x,
 +                            .litem_y = litem->y,
 +                            .space_x = space_x,
 +                            .space_y = space_y,
 +                            .tot_columns = gflow->tot_columns,
 +                            .tot_rows = gflow->tot_rows,
 +                      }),
 +                      &((UILayoutGridFlowOutput) {
 +                            .tot_w = &tot_w,
 +                            .tot_h = &tot_h,
 +                      }));
 +
 +              litem->w = tot_w;
 +              litem->h = tot_h;
 +      }
 +}
 +
 +static void ui_litem_layout_grid_flow(uiLayout *litem)
 +{
 +      int i;
 +      uiStyle *style = litem->root->style;
 +      uiLayoutItemGridFlow *gflow = (uiLayoutItemGridFlow *)litem;
 +      uiItem *item;
 +
 +      if (gflow->tot_items == 0) {
 +              litem->w = litem->h = 0;
 +              return;
 +      }
 +
 +      BLI_assert(gflow->tot_columns > 0);
 +      BLI_assert(gflow->tot_rows > 0);
 +
 +      const int space_x = style->columnspace;
 +      const int space_y = style->buttonspacey;
 +
 +      int *widths = BLI_array_alloca(widths, gflow->tot_columns);
 +      int *heights = BLI_array_alloca(heights, gflow->tot_rows);
 +      int *cos_x = BLI_array_alloca(cos_x, gflow->tot_columns);
 +      int *cos_y = BLI_array_alloca(cos_y, gflow->tot_rows);
 +
 +      /* This time we directly compute coordinates and sizes of all cells. */
 +      ui_litem_grid_flow_compute(
 +              &litem->items,
 +              &((UILayoutGridFlowInput) {
 +                    .row_major = gflow->row_major,
 +                    .even_columns = gflow->even_columns,
 +                    .even_rows = gflow->even_rows,
 +                    .litem_w = litem->w,
 +                    .litem_x = litem->x,
 +                    .litem_y = litem->y,
 +                    .space_x = space_x,
 +                    .space_y = space_y,
 +                    .tot_columns = gflow->tot_columns,
 +                    .tot_rows = gflow->tot_rows,
 +              }),
 +              &((UILayoutGridFlowOutput) {
 +                    .cos_x_array = cos_x,
 +                    .cos_y_array = cos_y,
 +                    .widths_array = widths,
 +                    .heights_array = heights,
 +              }));
 +
 +      for (item = litem->items.first, i = 0; item; item = item->next, i++) {
 +              const int col = gflow->row_major ? i % gflow->tot_columns : i / gflow->tot_rows;
 +              const int row = gflow->row_major ? i / gflow->tot_columns : i % gflow->tot_rows;
 +              int item_w, item_h;
 +              ui_item_size(item, &item_w, &item_h);
 +
 +              const int w = widths[col];
 +              const int h = heights[row];
 +
 +              item_w = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? w : min_ii(w, item_w);
 +              item_h = (litem->alignment == UI_LAYOUT_ALIGN_EXPAND) ? h : min_ii(h, item_h);
 +
 +              ui_item_position(item, cos_x[col], cos_y[row], item_w, item_h);
 +      }
 +
 +      litem->h = litem->y - cos_y[gflow->tot_rows - 1];
 +      litem->x = (cos_x[gflow->tot_columns - 1] - litem->x) + widths[gflow->tot_columns - 1];
 +      litem->y = litem->y - litem->h;
 +}
 +
  /* free layout */
  static void ui_litem_estimate_absolute(uiLayout *litem)
  {
@@@ -3516,32 -2858,22 +3516,32 @@@ static void ui_litem_layout_overlap(uiL
        litem->y = y - litem->h;
  }
  
 -/* layout create functions */
 -uiLayout *uiLayoutRow(uiLayout *layout, int align)
 +static void ui_litem_init_from_parent(uiLayout *litem, uiLayout *layout, int align)
  {
 -      uiLayout *litem;
 -
 -      litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRow");
 -      litem->item.type = ITEM_LAYOUT_ROW;
        litem->root = layout->root;
        litem->align = align;
 +      /* Children of gridflow layout shall never have "ideal big size" returned as estimated size. */
 +      litem->variable_size = layout->variable_size || layout->item.type == ITEM_LAYOUT_GRID_FLOW;
        litem->active = true;
        litem->enabled = true;
        litem->context = layout->context;
 -      litem->space = (align) ? 0 : layout->root->style->buttonspacex;
        litem->redalert = layout->redalert;
        litem->w = layout->w;
 +      litem->emboss = layout->emboss;
 +      litem->item.flag = (layout->item.flag & (UI_ITEM_PROP_SEP | UI_ITEM_PROP_DECORATE));
        BLI_addtail(&layout->items, litem);
 +}
 +
 +/* layout create functions */
 +uiLayout *uiLayoutRow(uiLayout *layout, int align)
 +{
 +      uiLayout *litem;
 +
 +      litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRow");
 +      ui_litem_init_from_parent(litem, layout, align);
 +
 +      litem->item.type = ITEM_LAYOUT_ROW;
 +      litem->space = (align) ? 0 : layout->root->style->buttonspacex;
  
        UI_block_layout_set_current(layout->root->block, litem);
  
@@@ -3553,10 -2885,16 +3553,10 @@@ uiLayout *uiLayoutColumn(uiLayout *layo
        uiLayout *litem;
  
        litem = MEM_callocN(sizeof(uiLayout), "uiLayoutColumn");
 +      ui_litem_init_from_parent(litem, layout, align);
 +
        litem->item.type = ITEM_LAYOUT_COLUMN;
 -      litem->root = layout->root;
 -      litem->align = align;
 -      litem->active = true;
 -      litem->enabled = true;
 -      litem->context = layout->context;
 -      litem->space = (litem->align) ? 0 : layout->root->style->buttonspacey;
 -      litem->redalert = layout->redalert;
 -      litem->w = layout->w;
 -      BLI_addtail(&layout->items, litem);
 +      litem->space = (align) ? 0 : layout->root->style->buttonspacey;
  
        UI_block_layout_set_current(layout->root->block, litem);
  
@@@ -3568,31 -2906,17 +3568,31 @@@ uiLayout *uiLayoutColumnFlow(uiLayout *
        uiLayoutItemFlow *flow;
  
        flow = MEM_callocN(sizeof(uiLayoutItemFlow), "uiLayoutItemFlow");
 +      ui_litem_init_from_parent(&flow->litem, layout, align);
 +
        flow->litem.item.type = ITEM_LAYOUT_COLUMN_FLOW;
 -      flow->litem.root = layout->root;
 -      flow->litem.align = align;
 -      flow->litem.active = true;
 -      flow->litem.enabled = true;
 -      flow->litem.context = layout->context;
        flow->litem.space = (flow->litem.align) ? 0 : layout->root->style->columnspace;
 -      flow->litem.redalert = layout->redalert;
 -      flow->litem.w = layout->w;
        flow->number = number;
 -      BLI_addtail(&layout->items, flow);
 +
 +      UI_block_layout_set_current(layout->root->block, &flow->litem);
 +
 +      return &flow->litem;
 +}
 +
 +uiLayout *uiLayoutGridFlow(
 +        uiLayout *layout, int row_major, int num_columns, int even_columns, int even_rows, int align)
 +{
 +      uiLayoutItemGridFlow *flow;
 +
 +      flow = MEM_callocN(sizeof(uiLayoutItemGridFlow), __func__);
 +      flow->litem.item.type = ITEM_LAYOUT_GRID_FLOW;
 +      ui_litem_init_from_parent(&flow->litem, layout, align);
 +
 +      flow->litem.space = (flow->litem.align) ? 0 : layout->root->style->columnspace;
 +      flow->row_major = row_major;
 +      flow->num_columns = num_columns;
 +      flow->even_columns = even_columns;
 +      flow->even_rows = even_rows;
  
        UI_block_layout_set_current(layout->root->block, &flow->litem);
  
@@@ -3604,10 -2928,15 +3604,10 @@@ static uiLayoutItemBx *ui_layout_box(ui
        uiLayoutItemBx *box;
  
        box = MEM_callocN(sizeof(uiLayoutItemBx), "uiLayoutItemBx");
 +      ui_litem_init_from_parent(&box->litem, layout, false);
 +
        box->litem.item.type = ITEM_LAYOUT_BOX;
 -      box->litem.root = layout->root;
 -      box->litem.active = 1;
 -      box->litem.enabled = 1;
 -      box->litem.context = layout->context;
        box->litem.space = layout->root->style->columnspace;
 -      box->litem.redalert = layout->redalert;
 -      box->litem.w = layout->w;
 -      BLI_addtail(&layout->items, box);
  
        UI_block_layout_set_current(layout->root->block, &box->litem);
  
@@@ -3635,9 -2964,14 +3635,9 @@@ uiLayout *uiLayoutRadial(uiLayout *layo
        }
  
        litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRadial");
 +      ui_litem_init_from_parent(litem, layout, false);
 +
        litem->item.type = ITEM_LAYOUT_RADIAL;
 -      litem->root = layout->root;
 -      litem->active = true;
 -      litem->enabled = true;
 -      litem->context = layout->context;
 -      litem->redalert = layout->redalert;
 -      litem->w = layout->w;
 -      BLI_addtail(&layout->root->layout->items, litem);
  
        UI_block_layout_set_current(layout->root->block, litem);
  
@@@ -3694,9 -3028,14 +3694,9 @@@ uiLayout *uiLayoutAbsolute(uiLayout *la
        uiLayout *litem;
  
        litem = MEM_callocN(sizeof(uiLayout), "uiLayoutAbsolute");
 +      ui_litem_init_from_parent(litem, layout, align);
 +
        litem->item.type = ITEM_LAYOUT_ABSOLUTE;
 -      litem->root = layout->root;
 -      litem->align = align;
 -      litem->active = 1;
 -      litem->enabled = 1;
 -      litem->context = layout->context;
 -      litem->redalert = layout->redalert;
 -      BLI_addtail(&layout->items, litem);
  
        UI_block_layout_set_current(layout->root->block, litem);
  
@@@ -3718,9 -3057,13 +3718,9 @@@ uiLayout *uiLayoutOverlap(uiLayout *lay
        uiLayout *litem;
  
        litem = MEM_callocN(sizeof(uiLayout), "uiLayoutOverlap");
 +      ui_litem_init_from_parent(litem, layout, false);
 +
        litem->item.type = ITEM_LAYOUT_OVERLAP;
 -      litem->root = layout->root;
 -      litem->active = true;
 -      litem->enabled = true;
 -      litem->context = layout->context;
 -      litem->redalert = layout->redalert;
 -      BLI_addtail(&layout->items, litem);
  
        UI_block_layout_set_current(layout->root->block, litem);
  
@@@ -3732,11 -3075,17 +3732,11 @@@ uiLayout *uiLayoutSplit(uiLayout *layou
        uiLayoutItemSplit *split;
  
        split = MEM_callocN(sizeof(uiLayoutItemSplit), "uiLayoutItemSplit");
 +      ui_litem_init_from_parent(&split->litem, layout, align);
 +
        split->litem.item.type = ITEM_LAYOUT_SPLIT;
 -      split->litem.root = layout->root;
 -      split->litem.align = align;
 -      split->litem.active = true;
 -      split->litem.enabled = true;
 -      split->litem.context = layout->context;
        split->litem.space = layout->root->style->columnspace;
 -      split->litem.redalert = layout->redalert;
 -      split->litem.w = layout->w;
        split->percentage = percentage;
 -      BLI_addtail(&layout->items, split);
  
        UI_block_layout_set_current(layout->root->block, &split->litem);
  
@@@ -3778,31 -3127,6 +3778,31 @@@ void uiLayoutSetScaleY(uiLayout *layout
        layout->scale[1] = scale;
  }
  
 +void uiLayoutSetEmboss(uiLayout *layout, char emboss)
 +{
 +      layout->emboss = emboss;
 +}
 +
 +bool uiLayoutGetPropSep(uiLayout *layout)
 +{
 +      return (layout->item.flag & UI_ITEM_PROP_SEP) != 0;
 +}
 +
 +void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
 +{
 +      SET_FLAG_FROM_TEST(layout->item.flag, is_sep, UI_ITEM_PROP_SEP);
 +}
 +
 +bool uiLayoutGetPropDecorate(uiLayout *layout)
 +{
 +      return (layout->item.flag & UI_ITEM_PROP_DECORATE) != 0;
 +}
 +
 +void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
 +{
 +      SET_FLAG_FROM_TEST(layout->item.flag, is_sep, UI_ITEM_PROP_DECORATE);
 +}
 +
  bool uiLayoutGetActive(uiLayout *layout)
  {
        return layout->active;
@@@ -3843,16 -3167,6 +3843,16 @@@ float uiLayoutGetScaleY(uiLayout *layou
        return layout->scale[1];
  }
  
 +int uiLayoutGetEmboss(uiLayout *layout)
 +{
 +      if (layout->emboss == UI_EMBOSS_UNDEFINED) {
 +              return layout->root->block->dt;
 +      }
 +      else {
 +              return layout->emboss;
 +      }
 +}
 +
  /********************** Layout *******************/
  
  static void ui_item_scale(uiLayout *litem, const float scale[2])
        int x, y, w, h;
  
        for (item = litem->items.last; item; item = item->prev) {
 +              if (item->type != ITEM_BUTTON) {
 +                      uiLayout *subitem = (uiLayout *)item;
 +                      ui_item_scale(subitem, scale);
 +              }
 +
                ui_item_size(item, &w, &h);
                ui_item_offset(item, &x, &y);
  
@@@ -3909,9 -3218,6 +3909,9 @@@ static void ui_item_estimate(uiItem *it
                        case ITEM_LAYOUT_COLUMN_FLOW:
                                ui_litem_estimate_column_flow(litem);
                                break;
 +                      case ITEM_LAYOUT_GRID_FLOW:
 +                              ui_litem_estimate_grid_flow(litem);
 +                              break;
                        case ITEM_LAYOUT_ROW:
                                ui_litem_estimate_row(litem);
                                break;
@@@ -4011,9 -3317,6 +4011,9 @@@ static void ui_item_layout(uiItem *item
                        case ITEM_LAYOUT_COLUMN_FLOW:
                                ui_litem_layout_column_flow(litem);
                                break;
 +                      case ITEM_LAYOUT_GRID_FLOW:
 +                              ui_litem_layout_grid_flow(litem);
 +                              break;
                        case ITEM_LAYOUT_ROW:
                                ui_litem_layout_row(litem);
                                break;
@@@ -4110,9 -3413,6 +4110,9 @@@ uiLayout *UI_block_layout(uiBlock *bloc
        layout = MEM_callocN(sizeof(uiLayout), "uiLayout");
        layout->item.type = ITEM_LAYOUT_ROOT;
  
 +      /* Only used when 'UI_ITEM_PROP_SEP' is set. */
 +      layout->item.flag = UI_ITEM_PROP_DECORATE;
 +
        layout->x = x;
        layout->y = y;
        layout->root = root;
        layout->active = 1;
        layout->enabled = 1;
        layout->context = NULL;
 +      layout->emboss = UI_EMBOSS_UNDEFINED;
  
        if (type == UI_LAYOUT_MENU || type == UI_LAYOUT_PIEMENU)
                layout->space = 0;
  
 +      if (type == UI_LAYOUT_TOOLBAR) {
 +              block->flag |= UI_BLOCK_SHOW_SHORTCUT_ALWAYS;
 +      }
 +
        if (dir == UI_LAYOUT_HORIZONTAL) {
                layout->h = size;
                layout->root->emh = em * UI_UNIT_Y;
@@@ -4185,10 -3480,6 +4185,10 @@@ void ui_layout_add_but(uiLayout *layout
                but->context = layout->context;
                but->context->used = true;
        }
 +
 +      if (layout->emboss != UI_EMBOSS_UNDEFINED) {
 +              but->dt = layout->emboss;
 +      }
  }
  
  void uiLayoutSetOperatorContext(uiLayout *layout, int opcontext)
@@@ -4268,18 -3559,6 +4268,18 @@@ MenuType *UI_but_menutype_get(uiBut *bu
        }
  }
  
 +/* this is a bit of a hack but best keep it in one place at least */
 +PanelType *UI_but_paneltype_get(uiBut *but)
 +{
 +      if (but->menu_create_func == ui_item_paneltype_func) {
 +              return (PanelType *)but->poin;
 +      }
 +      else {
 +              return NULL;
 +      }
 +}
 +
 +
  void UI_menutype_draw(bContext *C, MenuType *mt, struct uiLayout *layout)
  {
        Menu menu = {
                CTX_store_set(C, NULL);
        }
  }
 +
 +
 +static void ui_paneltype_draw_impl(
 +        bContext *C, PanelType *pt, uiLayout *layout, bool show_header)
 +{
 +      Panel *panel = MEM_callocN(sizeof(Panel), "popover panel");
 +      panel->type = pt;
 +      panel->flag = PNL_POPOVER;
 +
 +      uiLayout *last_item = layout->items.last;
 +
 +      /* Draw main panel. */
 +      if (show_header) {
 +              uiLayout *row = uiLayoutRow(layout, false);
 +              if (pt->draw_header) {
 +                      panel->layout = row;
 +                      pt->draw_header(C, panel);
 +                      panel->layout = NULL;
 +              }
 +              uiItemL(row, pt->label, ICON_NONE);
 +      }
 +
 +      panel->layout = layout;
 +      pt->draw(C, panel);
 +      panel->layout = NULL;
 +
 +      MEM_freeN(panel);
 +
 +      /* Draw child panels. */
 +      for (LinkData *link = pt->children.first; link; link = link->next) {
 +              PanelType *child_pt = link->data;
 +
 +              if (child_pt->poll == NULL || child_pt->poll(C, child_pt)) {
 +                      /* Add space if something was added to the layout. */
 +                      if (last_item != layout->items.last) {
 +                              uiItemS(layout);
 +                              last_item = layout->items.last;
 +                      }
 +
 +                      uiLayout *col = uiLayoutColumn(layout, false);
 +                      ui_paneltype_draw_impl(C, child_pt, col, true);
 +              }
 +      }
 +}
 +
 +/**
 + * Used for popup panels only.
 + */
 +void UI_paneltype_draw(bContext *C, PanelType *pt, uiLayout *layout)
 +{
 +      if (layout->context) {
 +              CTX_store_set(C, layout->context);
 +      }
 +
 +      ui_paneltype_draw_impl(C, pt, layout, false);
 +
 +      if (layout->context) {
 +              CTX_store_set(C, NULL);
 +      }
 +
 +}
index 673e6c61885310aae8986e1e319cce422d531abc,995ff0cfd4017354be74597da52e1b6c064282a4..4c770984717b56e9f4822d10245612aace163920
@@@ -42,18 -42,15 +42,18 @@@ static bNodeSocketTemplate sh_node_ambi
        {       -1, 0, ""       }
  };
  
 -static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
 +static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
  {
 -      return GPU_stack_link(mat, "node_ambient_occlusion", in, out, GPU_builtin(GPU_VIEW_NORMAL));
 +      if (!in[2].link)
 +              GPU_link(mat, "world_normals_get", &in[2].link);
 +
 +      return GPU_stack_link(mat, node, "node_ambient_occlusion", in, out);
  }
  
  static void node_shader_init_ambient_occlusion(bNodeTree *UNUSED(ntree), bNode *node)
  {
-       node->custom1 = 8; /* samples */
-       node->custom2 = SHD_AO_LOCAL;
+       node->custom1 = 16; /* samples */
+       node->custom2 = 0;
  }
  
  /* node type definition */