Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Sat, 23 Jun 2018 08:45:19 +0000 (10:45 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 23 Jun 2018 08:46:09 +0000 (10:46 +0200)
1  2 
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_region_menu_popup.c

index 0b4817c8049a512cbe4766911d4967ce5305a6f4,6227ea15b711b2203a9aa24c4fafd80a5f1bbcc9..0666691607dd251a589dbbfcddbf6fbd603b6286
@@@ -42,7 -42,6 +42,7 @@@ struct ID
  struct IDProperty;
  struct ListBase;
  struct ARegion;
 +struct ARegionType;
  struct ScrArea;
  struct bScreen;
  struct wmEvent;
@@@ -65,6 -64,7 +65,6 @@@ struct Image
  struct ImageUser;
  struct wmKeyConfig;
  struct wmOperatorType;
 -struct uiWidgetColors;
  struct MTex;
  struct ImBuf;
  struct bNodeTree;
@@@ -73,9 -73,6 +73,9 @@@ struct bNodeSocket
  struct wmDropBox;
  struct wmDrag;
  struct wmEvent;
 +struct wmManipulator;
 +struct wmMsgBus;
 +struct wmKeyMap;
  
  typedef struct uiBut uiBut;
  typedef struct uiBlock uiBlock;
@@@ -102,8 -99,6 +102,8 @@@ enum 
        UI_EMBOSS_NONE          = 1,  /* Nothing, only icon and/or text */
        UI_EMBOSS_PULLDOWN      = 2,  /* Pulldown menu style */
        UI_EMBOSS_RADIAL        = 3,  /* Pie Menu */
 +
 +      UI_EMBOSS_UNDEFINED     = 255, /* For layout engine, use emboss from block. */
  };
  
  /* uiBlock->direction */
@@@ -112,8 -107,7 +112,8 @@@ enum 
        UI_DIR_DOWN         = (1 << 1),
        UI_DIR_LEFT         = (1 << 2),
        UI_DIR_RIGHT        = (1 << 3),
 -      UI_DIR_CENTER_Y     = (1 << 4),
 +      UI_DIR_CENTER_X     = (1 << 4),
 +      UI_DIR_CENTER_Y     = (1 << 5),
  
        UI_DIR_ALL          = (UI_DIR_UP | UI_DIR_DOWN | UI_DIR_LEFT | UI_DIR_RIGHT),
  };
  #define UI_BLOCK_POPUP_HOLD  (1 << 18)
  #define UI_BLOCK_LIST_ITEM   (1 << 19)
  #define UI_BLOCK_RADIAL      (1 << 20)
 +#define UI_BLOCK_POPOVER     (1 << 21)
 +#define UI_BLOCK_POPOVER_ONCE (1 << 22)
 +/** Always show keymaps, even for non-menus. */
 +#define UI_BLOCK_SHOW_SHORTCUT_ALWAYS (1 << 23)
  
  /* uiPopupBlockHandle->menuretval */
  #define UI_RETURN_CANCEL     (1 << 0)   /* cancel all menus cascading */
@@@ -188,13 -178,11 +188,13 @@@ enum 
        UI_BUT_COLOR_CUBIC     = (1 << 23),  /* cubic saturation for the color wheel */
        UI_BUT_LIST_ITEM       = (1 << 24),  /* This but is "inside" a list item (currently used to change theme colors). */
        UI_BUT_DRAG_MULTI      = (1 << 25),  /* edit this button as well as the active button (not just dragging) */
 -      UI_BUT_SCA_LINK_GREY   = (1 << 26),  /* used to flag if sca links shoud be gray out */
 +
        UI_BUT_HAS_SEP_CHAR    = (1 << 27),  /* but->str contains UI_SEP_CHAR, used for key shortcuts */
        UI_BUT_UPDATE_DELAY    = (1 << 28),  /* don't run updates while dragging (needed in rare cases). */
        UI_BUT_TEXTEDIT_UPDATE = (1 << 29),  /* when widget is in textedit mode, update value on each char stroke */
        UI_BUT_VALUE_CLEAR     = (1 << 30),  /* show 'x' icon to clear/unlink value of text or search button */
 +
 +      UI_BUT_OVERRIDEN       = (1 << 31),  /* RNA property of the button is overriden from linked reference data. */
  };
  
  #define UI_PANEL_WIDTH          340
@@@ -230,14 -218,11 +230,14 @@@ enum 
        UI_BUT_ALIGN_ALL         = (UI_BUT_ALIGN | UI_BUT_ALIGN_STITCH_TOP | UI_BUT_ALIGN_STITCH_LEFT),
  
        UI_BUT_BOX_ITEM          = (1 << 20), /* This but is "inside" a box item (currently used to change theme colors). */
 +
 +      UI_BUT_ACTIVE_LEFT       = (1 << 21), /* Active left part of number button */
 +      UI_BUT_ACTIVE_RIGHT      = (1 << 22), /* Active right part of number button */
  };
  
  /* scale fixed button widths by this to account for DPI */
  
 -#define UI_DPI_FAC ((U.pixelsize * (float)U.dpi) / 72.0f)
 +#define UI_DPI_FAC (U.dpi_fac)
  /* 16 to copy ICON_DEFAULT_HEIGHT */
  #define UI_DPI_ICON_SIZE ((float)16 * UI_DPI_FAC)
  
@@@ -276,11 -261,11 +276,11 @@@ typedef enum 
        UI_BTYPE_CHECKBOX               = (13 << 9),  /* similar to toggle, display a 'tick' */
        UI_BTYPE_CHECKBOX_N             = (14 << 9),
        UI_BTYPE_COLOR                  = (15 << 9),
 +      UI_BTYPE_TAB                    = (16 << 9),
 +      UI_BTYPE_POPOVER                = (17 << 9),
        UI_BTYPE_SCROLL                 = (18 << 9),
        UI_BTYPE_BLOCK                  = (19 << 9),
        UI_BTYPE_LABEL                  = (20 << 9),
 -      UI_BTYPE_LINK                   = (22 << 9),
 -      UI_BTYPE_INLINK                 = (23 << 9),
        UI_BTYPE_KEY_EVENT              = (24 << 9),
        UI_BTYPE_HSVCUBE                = (26 << 9),
        UI_BTYPE_PULLDOWN               = (27 << 9),  /* menu (often used in headers), **_MENU /w different draw-type */
        UI_BTYPE_NODE_SOCKET            = (53 << 9),
        UI_BTYPE_SEPR                   = (54 << 9),
        UI_BTYPE_SEPR_LINE              = (55 << 9),
 -      UI_BTYPE_GRIP                   = (56 << 9),  /* resize handle (resize uilist) */
 +      UI_BTYPE_SEPR_SPACER            = (56 << 9),  /* Dynamically fill available space. */
 +      UI_BTYPE_GRIP                   = (57 << 9),  /* resize handle (resize uilist) */
  } eButType;
  
  #define BUTTYPE     (63 << 9)
   * Functions to draw various shapes, taking theme settings into account.
   * Used for code that draws its own UI style elements. */
  
 -void UI_draw_roundbox(float minx, float miny, float maxx, float maxy, float rad);
 +void UI_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]);
 +void UI_draw_anti_fan(float tri_array[][2], unsigned int length, const float color[4]);
 +
  void UI_draw_roundbox_corner_set(int type);
 +void UI_draw_roundbox_aa(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float color[4]);
 +void UI_draw_roundbox_4fv(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float col[4]);
 +void UI_draw_roundbox_3ubAlpha(bool filled, float minx, float miny, float maxx, float maxy, float rad, const unsigned char col[3], unsigned char alpha);
 +void UI_draw_roundbox_3fvAlpha(bool filled, float minx, float miny, float maxx, float maxy, float rad, const float col[3], float alpha);
 +void UI_draw_roundbox_shade_x(bool filled, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown, const float col[4]);
 +
 +#if 0 /* unused */
  int  UI_draw_roundbox_corner_get(void);
 -void UI_draw_roundbox_unfilled(float minx, float miny, float maxx, float maxy, float rad);
 +void UI_draw_roundbox_shade_y(bool filled, float minx, float miny, float maxx, float maxy, float rad, float shadeleft, float shaderight, const float col[4]);
 +#endif
 +
  void UI_draw_box_shadow(unsigned char alpha, float minx, float miny, float maxx, float maxy);
 -void UI_draw_roundbox_gl_mode(int mode, float minx, float miny, float maxx, float maxy, float rad);
 -void UI_draw_roundbox_shade_x(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown);
 -void UI_draw_roundbox_shade_y(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadeLeft, float shadeRight);
 -void UI_draw_text_underline(int pos_x, int pos_y, int len, int height);
 +void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]);
  
 -void UI_draw_safe_areas(
 -        float x1, float x2, float y1, float y2,
 +void UI_draw_safe_areas(uint pos, float x1, float x2, float y1, float y2,
          const float title_aspect[2], const float action_aspect[2]);
  
  /* state for scrolldrawing */
@@@ -418,6 -395,7 +418,7 @@@ uiPopupMenu *UI_popup_menu_begin_ex
          struct bContext *C, const char *title, const char *block_name,
          int icon) ATTR_NONNULL();
  void UI_popup_menu_end(struct bContext *C, struct uiPopupMenu *head);
+ bool UI_popup_menu_end_or_cancel(struct bContext *C, struct uiPopupMenu *head);
  struct uiLayout *UI_popup_menu_layout(uiPopupMenu *head);
  
  void UI_popup_menu_reports(struct bContext *C, struct ReportList *reports) ATTR_NONNULL();
@@@ -426,19 -404,6 +427,19 @@@ int UI_popup_menu_invoke(struct bContex
  void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable);
  void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *but);
  
 +/* interface_region_popover.c */
 +
 +typedef struct uiPopover uiPopover;
 +
 +int UI_popover_panel_invoke(
 +        struct bContext *C, int space_id, int region_id, const char *idname,
 +        bool keep_open, struct ReportList *reports);
 +
 +uiPopover *UI_popover_begin(struct bContext *C) ATTR_NONNULL(1);
 +void UI_popover_end(struct bContext *C, struct uiPopover *head, struct wmKeyMap *keymap);
 +struct uiLayout *UI_popover_layout(uiPopover *head);
 +void UI_popover_once_clear(uiPopover *pup);
 +
  /* interface_region_menu_pie.c */
  /* Pie menus */
  typedef struct uiPieMenu uiPieMenu;
@@@ -488,10 -453,10 +489,10 @@@ uiBlock *UI_block_begin(const struct bC
  void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], int r_xy[2]);
  void UI_block_end(const struct bContext *C, uiBlock *block);
  void UI_block_draw(const struct bContext *C, struct uiBlock *block);
 +void UI_blocklist_update_window_matrix(const struct bContext *C, const struct ListBase *lb);
 +void UI_blocklist_draw(const struct bContext *C, const struct ListBase *lb);
  void UI_block_update_from_old(const struct bContext *C, struct uiBlock *block);
  
 -uiBlock *UI_block_find_in_region(const char *name, struct ARegion *ar);
 -
  void UI_block_emboss_set(uiBlock *block, char dt);
  
  void UI_block_free(const struct bContext *C, uiBlock *block);
@@@ -532,8 -497,9 +533,10 @@@ void    UI_block_direction_set(uiBlock 
  void    UI_block_order_flip(uiBlock *block);
  void    UI_block_flag_enable(uiBlock *block, int flag);
  void    UI_block_flag_disable(uiBlock *block, int flag);
 +void    UI_block_translate(uiBlock *block, int x, int y);
  
+ bool    UI_block_is_empty(const uiBlock *block);
  int     UI_but_return_value_get(uiBut *but);
  
  void    UI_but_drag_set_id(uiBut *but, struct ID *id);
@@@ -696,7 -662,6 +699,7 @@@ void UI_but_string_info_get(struct bCon
  #define UI_ID_FAKE_USER     (1 << 8)
  #define UI_ID_PIN           (1 << 9)
  #define UI_ID_PREVIEWS      (1 << 10)
 +#define UI_ID_OVERRIDE      (1 << 11)
  #define UI_ID_FULL          (UI_ID_RENAME | UI_ID_BROWSE | UI_ID_ADD_NEW | UI_ID_OPEN | UI_ID_ALONE | UI_ID_DELETE | UI_ID_LOCAL)
  
  /**
@@@ -730,30 -695,18 +733,30 @@@ uiBut *uiDefSearchButO_ptr(uiBlock *blo
                             void *arg, int retval, int icon, int maxlen, int x, int y,
                             short width, short height, float a1, float a2, const char *tip);
  
 -uiBut *uiDefAutoButR(uiBlock *block, struct PointerRNA *ptr, struct PropertyRNA *prop, int index, const char *name, int icon, int x1, int y1, int x2, int y2);
 -int uiDefAutoButsRNA(uiLayout *layout, struct PointerRNA *ptr, bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *), const char label_align);
 -
 -/* Links
 - *
 - * Game engine logic brick links. Non-functional currently in 2.5,
 - * code to handle and draw these is disabled internally. */
  
 -void UI_but_link_set(struct uiBut *but,  void **poin,  void ***ppoin,  short *tot,  int from, int to);
 +/* For uiDefAutoButsRNA */
 +typedef enum {
 +      /* Keep current layout for aligning label with property button. */
 +      UI_BUT_LABEL_ALIGN_NONE,
 +      /* Align label and property button vertically. */
 +      UI_BUT_LABEL_ALIGN_COLUMN,
 +      /* Split layout into a column for the label and one for property button. */
 +      UI_BUT_LABEL_ALIGN_SPLIT_COLUMN,
 +} eButLabelAlign;
 +
 +/* Return info for uiDefAutoButsRNA */
 +typedef enum {
 +      /* Returns when no buttons were added */
 +      UI_PROP_BUTS_NONE_ADDED       = (1 << 0),
 +      /* Returned when any property failed the custom check callback (check_prop) */
 +      UI_PROP_BUTS_ANY_FAILED_CHECK = (1 << 1),
 +} eAutoPropButsReturn;
  
 -void UI_block_links_compose(uiBlock *block);
 -uiBut *UI_block_links_find_inlink(uiBlock *block, void *poin);
 +uiBut *uiDefAutoButR(uiBlock *block, struct PointerRNA *ptr, struct PropertyRNA *prop, int index, const char *name, int icon, int x1, int y1, int x2, int y2);
 +eAutoPropButsReturn uiDefAutoButsRNA(
 +        uiLayout *layout, struct PointerRNA *ptr,
 +        bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *),
 +        eButLabelAlign label_align, const bool compact);
  
  /* use inside searchfunc to add items */
  bool    UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid);
@@@ -823,14 -776,11 +826,14 @@@ void UI_panels_begin(const struct bCont
  void UI_panels_end(const struct bContext *C, struct ARegion *ar, int *x, int *y);
  void UI_panels_draw(const struct bContext *C, struct ARegion *ar);
  
 -struct Panel *UI_panel_find_by_type(struct ARegion *ar, struct PanelType *pt);
 -struct Panel *UI_panel_begin(struct ScrArea *sa, struct ARegion *ar, uiBlock *block,
 -                             struct PanelType *pt, struct Panel *pa, bool *r_open);
 +struct Panel *UI_panel_find_by_type(struct ListBase *lb, struct PanelType *pt);
 +struct Panel *UI_panel_begin(struct ScrArea *sa, struct ARegion *ar, struct ListBase *lb,
 +                             uiBlock *block, struct PanelType *pt, struct Panel *pa,
 +                             bool *r_open);
  void UI_panel_end(uiBlock *block, int width, int height);
  void UI_panels_scale(struct ARegion *ar, float new_width);
 +void UI_panel_label_offset(struct uiBlock *block, int *x, int *y);
 +int UI_panel_size_y(const struct Panel *pa);
  
  bool                       UI_panel_category_is_visible(struct ARegion *ar);
  void                       UI_panel_category_add(struct ARegion *ar, const char *name);
@@@ -843,8 -793,6 +846,8 @@@ struct PanelCategoryDyn   *UI_panel_cat
  void                       UI_panel_category_clear_all(struct ARegion *ar);
  void                       UI_panel_category_draw_all(struct ARegion *ar, const char *category_id_active);
  
 +struct PanelType *UI_paneltype_find(int space_id, int region_id, const char *idname);
 +
  /* Handlers
   *
   * Handlers that can be registered in regions, areas and windows for
@@@ -864,6 -812,7 +867,6 @@@ void UI_popup_handlers_remove_all(struc
  void UI_init(void);
  void UI_init_userdef(struct Main *bmain);
  void UI_reinit_font(void);
 -void UI_reinit_gl_state(void);
  void UI_exit(void);
  
  /* Layout
  #define UI_LAYOUT_ALIGN_CENTER  2
  #define UI_LAYOUT_ALIGN_RIGHT   3
  
 +#define UI_ITEM_O_RETURN_PROPS  (1 << 0)
  #define UI_ITEM_R_EXPAND        (1 << 1)
  #define UI_ITEM_R_SLIDER        (1 << 2)
  #define UI_ITEM_R_TOGGLE        (1 << 3)
  #define UI_ITEM_R_NO_BG         (1 << 7)
  #define UI_ITEM_R_IMMEDIATE     (1 << 8)
  #define UI_ITEM_O_DEPRESS       (1 << 9)
 +#define UI_ITEM_R_COMPACT       (1 << 10)
 +
 +#define UI_HEADER_OFFSET ((void)0, 0.2f * UI_UNIT_X)
  
 -/* uiTemplateOperatorPropertyButs flags */
 -#define UI_TEMPLATE_OP_PROPS_SHOW_TITLE 1
 -#define UI_TEMPLATE_OP_PROPS_SHOW_EMPTY 2
 +/* uiLayoutOperatorButs flags */
 +enum {
 +      UI_TEMPLATE_OP_PROPS_SHOW_TITLE       = (1 << 0),
 +      UI_TEMPLATE_OP_PROPS_SHOW_EMPTY       = (1 << 1),
 +      UI_TEMPLATE_OP_PROPS_COMPACT          = (1 << 2),
 +};
  
  /* used for transp checkers */
  #define UI_ALPHA_CHECKER_DARK 100
@@@ -935,21 -877,20 +938,21 @@@ enum 
        UI_CNR_ALL          = (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT)
  };
  
 -/* not apart of the corner flags but mixed in some functions  */
 -#define UI_RB_ALPHA (UI_CNR_ALL + 1)
 -
  uiLayout *UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int size, int em, int padding, struct uiStyle *style);
  void UI_block_layout_set_current(uiBlock *block, uiLayout *layout);
  void UI_block_layout_resolve(uiBlock *block, int *x, int *y);
  
 +void UI_region_message_subscribe(struct ARegion *ar, struct wmMsgBus *mbus);
 +
  uiBlock *uiLayoutGetBlock(uiLayout *layout);
  
  void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv);
  void uiLayoutSetContextPointer(uiLayout *layout, const char *name, struct PointerRNA *ptr);
  void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context);
  struct MenuType *UI_but_menutype_get(uiBut *but);
 +struct PanelType *UI_but_paneltype_get(uiBut *but);
  void UI_menutype_draw(struct bContext *C, struct MenuType *mt, struct uiLayout *layout);
 +void UI_paneltype_draw(struct bContext *C, struct PanelType *pt, struct uiLayout *layout);
  
  /* Only for convenience. */
  void uiLayoutSetContextFromBut(uiLayout *layout, uiBut *but);
@@@ -962,9 -903,6 +965,9 @@@ void uiLayoutSetAlignment(uiLayout *lay
  void uiLayoutSetKeepAspect(uiLayout *layout, bool keepaspect);
  void uiLayoutSetScaleX(uiLayout *layout, float scale);
  void uiLayoutSetScaleY(uiLayout *layout, float scale);
 +void uiLayoutSetEmboss(uiLayout *layout, char emboss);
 +void uiLayoutSetPropSep(uiLayout *layout, bool is_sep);
 +void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep);
  
  int uiLayoutGetOperatorContext(uiLayout *layout);
  bool uiLayoutGetActive(uiLayout *layout);
@@@ -975,16 -913,11 +978,16 @@@ bool uiLayoutGetKeepAspect(uiLayout *la
  int uiLayoutGetWidth(uiLayout *layout);
  float uiLayoutGetScaleX(uiLayout *layout);
  float uiLayoutGetScaleY(uiLayout *layout);
 +int uiLayoutGetEmboss(uiLayout *layout);
 +bool uiLayoutGetPropSep(uiLayout *layout);
 +bool uiLayoutGetPropDecorate(uiLayout *layout);
  
  /* layout specifiers */
  uiLayout *uiLayoutRow(uiLayout *layout, int align);
  uiLayout *uiLayoutColumn(uiLayout *layout, int align);
  uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, int align);
 +uiLayout *uiLayoutGridFlow(
 +        uiLayout *layout, int row_major, int num_columns, int even_columns, int even_rows, int align);
  uiLayout *uiLayoutBox(uiLayout *layout);
  uiLayout *uiLayoutListBox(uiLayout *layout, struct uiList *ui_list, struct PointerRNA *ptr, struct PropertyRNA *prop,
                            struct PointerRNA *actptr, struct PropertyRNA *actprop);
@@@ -1005,35 -938,15 +1008,35 @@@ void uiTemplateIDBrowse
  void uiTemplateIDPreview(
          uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname,
          const char *newop, const char *openop, const char *unlinkop, int rows, int cols, int filter);
 +void uiTemplateIDTabs(
 +        uiLayout *layout, struct bContext *C,
 +        PointerRNA *ptr, const char *propname,
 +        const char *newop, const char *openop, const char *unlinkop,
 +        int filter);
  void uiTemplateAnyID(uiLayout *layout, struct PointerRNA *ptr, const char *propname,
                       const char *proptypename, const char *text);
 +void uiTemplateSearch(
 +        uiLayout *layout, struct bContext *C,
 +        struct PointerRNA *ptr, const char *propname,
 +        struct PointerRNA *searchptr, const char *searchpropname,
 +        const char *newop, const char *unlinkop);
 +void uiTemplateSearchPreview(
 +        uiLayout *layout, struct bContext *C,
 +        struct PointerRNA *ptr, const char *propname,
 +        struct PointerRNA *searchptr, const char *searchpropname,
 +        const char *newop, const char *unlinkop,
 +        const int rows, const int cols);
  void uiTemplatePathBuilder(uiLayout *layout, struct PointerRNA *ptr, const char *propname,
                             struct PointerRNA *root_ptr, const char *text);
  uiLayout *uiTemplateModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr);
 +
 +void uiTemplateOperatorRedoProperties(uiLayout *layout, const struct bContext *C);
 +
  uiLayout *uiTemplateConstraint(uiLayout *layout, struct PointerRNA *ptr);
  void uiTemplatePreview(uiLayout *layout, struct bContext *C, struct ID *id, int show_buttons, struct ID *parent,
                         struct MTex *slot, const char *preview_id);
  void uiTemplateColorRamp(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int expand);
 +void uiTemplateIcon(uiLayout *layout, int icon_value, float icon_scale);
  void uiTemplateIconView(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int show_labels, float icon_scale);
  void uiTemplateHistogram(uiLayout *layout, struct PointerRNA *ptr, const char *propname);
  void uiTemplateWaveform(uiLayout *layout, struct PointerRNA *ptr, const char *propname);
@@@ -1044,6 -957,8 +1047,6 @@@ void uiTemplateColorPicker(uiLayout *la
  void uiTemplatePalette(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color);
  void uiTemplateLayers(uiLayout *layout, struct PointerRNA *ptr, const char *propname,
                        PointerRNA *used_ptr, const char *used_propname, int active_layer);
 -void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char *propname,
 -                      PointerRNA *used_ptr, const char *used_propname, int active_state);
  void uiTemplateImage(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, struct PointerRNA *userptr, int compact, int multiview);
  void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr, int color_management);
  void uiTemplateImageStereo3d(uiLayout *layout, struct PointerRNA *stereo3d_format_ptr);
@@@ -1054,11 -969,9 +1057,11 @@@ void uiTemplateImageInfo(uiLayout *layo
  void uiTemplateRunningJobs(uiLayout *layout, struct bContext *C);
  void UI_but_func_operator_search(uiBut *but);
  void uiTemplateOperatorSearch(uiLayout *layout);
 -void uiTemplateOperatorPropertyButs(const struct bContext *C, uiLayout *layout, struct wmOperator *op,
 -                                    bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *),
 -                                    const char label_align, const short flag);
 +eAutoPropButsReturn uiTemplateOperatorPropertyButs(
 +        const struct bContext *C, uiLayout *layout, struct wmOperator *op,
 +        bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *),
 +        const eButLabelAlign label_align, const short flag);
 +void uiTemplateHeader3D_mode(uiLayout *layout, struct bContext *C);
  void uiTemplateHeader3D(uiLayout *layout, struct bContext *C);
  void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C);
  void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C);
@@@ -1132,20 -1045,6 +1135,20 @@@ void uiItemLDrag(uiLayout *layout, stru
  void uiItemM(uiLayout *layout, struct bContext *C, const char *menuname, const char *name, int icon); /* menu */
  void uiItemV(uiLayout *layout, const char *name, int icon, int argval); /* value */
  void uiItemS(uiLayout *layout); /* separator */
 +void uiItemSpacer(uiLayout *layout); /* Special separator. */
 +
 +void uiItemPopoverPanel_ptr(
 +        uiLayout *layout, struct bContext *C,
 +        struct PanelType *pt,
 +        const char *name, int icon);
 +void uiItemPopoverPanel(
 +        uiLayout *layout, struct bContext *C,
 +        int space_id, int region_id, const char *panelname,
 +        const char *name, int icon);
 +void uiItemPopoverPanelFromGroup(
 +        uiLayout *layout, struct bContext *C,
 +        int space_id, int region_id,
 +        const char *context, const char *category);
  
  void uiItemMenuF(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg);
  void uiItemMenuEnumO_ptr(uiLayout *layout, struct bContext *C, struct wmOperatorType *ot, const char *propname, const char *name, int icon);
@@@ -1188,22 -1087,20 +1191,22 @@@ uiBut *UI_region_active_but_get(struct 
  
  /* Styled text draw */
  void UI_fontstyle_set(const struct uiFontStyle *fs);
 -void UI_fontstyle_draw_ex(
 -        const struct uiFontStyle *fs, const struct rcti *rect, const char *str,
 -        size_t len, float *r_xofs, float *r_yofs);
 -void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str);
 -void UI_fontstyle_draw_rotated(const struct uiFontStyle *fs, const struct rcti *rect, const char *str);
 -void UI_fontstyle_draw_simple(const struct uiFontStyle *fs, float x, float y, const char *str);
 +void UI_fontstyle_draw_ex(const struct uiFontStyle *fs, const struct rcti *rect, const char *str,
 +                          const unsigned char col[4], size_t len, float *r_xofs, float *r_yofs);
 +void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str,
 +                       const unsigned char col[4]);
 +void UI_fontstyle_draw_rotated(const struct uiFontStyle *fs, const struct rcti *rect, const char *str,
 +                               const unsigned char col[4]);
 +void UI_fontstyle_draw_simple(const struct uiFontStyle *fs, float x, float y, const char *str,
 +                              const unsigned char col[4]);
  void UI_fontstyle_draw_simple_backdrop(
          const struct uiFontStyle *fs, float x, float y, const char *str,
 -        const unsigned char fg[4], const unsigned char bg[4]);
 +        const float col_fg[4], const float col_bg[4]);
  
  int UI_fontstyle_string_width(const struct uiFontStyle *fs, const char *str);
  int UI_fontstyle_height_max(const struct uiFontStyle *fs);
  
 -void UI_draw_icon_tri(float x, float y, char dir);
 +void UI_draw_icon_tri(float x, float y, char dir, const float[4]);
  
  struct uiStyle *UI_style_get(void);           /* use for fonts etc */
  struct uiStyle *UI_style_get_dpi(void);       /* DPI scaled settings for drawing */
@@@ -1231,7 -1128,6 +1234,7 @@@ void UI_butstore_unregister(uiButStore 
  
  /* ui_interface_region_tooltip.c */
  struct ARegion *UI_tooltip_create_from_button(struct bContext *C, struct ARegion *butregion, uiBut *but);
 +struct ARegion *UI_tooltip_create_from_manipulator(struct bContext *C, struct wmManipulator *mpr);
  void UI_tooltip_free(struct bContext *C, struct bScreen *sc, struct ARegion *ar);
  
  /* How long before a tool-tip shows. */
  
  int UI_calc_float_precision(int prec, double value);
  
 +/* widget batched drawing */
 +void UI_widgetbase_draw_cache_begin(void);
 +void UI_widgetbase_draw_cache_flush(void);
 +void UI_widgetbase_draw_cache_end(void);
 +
 +/* Special drawing for toolbar, mainly workarounds for inflexible icon sizing. */
 +#define USE_UI_TOOLBAR_HACK
 +
 +/* Support click-drag motion which presses the button and closes a popover (like a menu). */
 +#define USE_UI_POPOVER_ONCE
 +
 +bool UI_but_is_tool(const uiBut *but);
 +#define UI_but_is_decorator(but) \
 +      ((but)->func == ui_but_anim_decorate_cb)
 +
  #endif  /* __UI_INTERFACE_H__ */
index 8c37054098b3758e453c8128fe09219867453295,90fb477480ac58bf192c31106c59b3da5c94e511..a36f534807cd9d6d22778f6cce98e247ffd910d8
@@@ -41,7 -41,6 +41,7 @@@
  #include "DNA_scene_types.h"
  #include "DNA_screen_types.h"
  #include "DNA_userdef_types.h"
 +#include "DNA_workspace_types.h"
  
  #include "BLI_math.h"
  #include "BLI_listbase.h"
@@@ -51,7 -50,6 +51,7 @@@
  
  #include "BLI_utildefines.h"
  
 +#include "BKE_animsys.h"
  #include "BKE_context.h"
  #include "BKE_idprop.h"
  #include "BKE_main.h"
  #include "BKE_screen.h"
  #include "BKE_unit.h"
  
 -#include "BIF_gl.h"
 +#include "GPU_glew.h"
 +#include "GPU_matrix.h"
  
  #include "BLF_api.h"
  #include "BLT_translation.h"
  
  #include "UI_interface.h"
 +#include "UI_interface_icons.h"
  
  #include "IMB_imbuf.h"
  
  #include "WM_api.h"
  #include "WM_types.h"
 -#include "wm_subwindow.h"
 +#include "WM_message.h"
  
  #include "RNA_access.h"
  
  #include "BPY_extern.h"
  
 +#include "ED_screen.h"
 +
  #include "IMB_colormanagement.h"
  
 +#include "DEG_depsgraph_query.h"
 +
  #include "interface_intern.h"
  
 +/* prototypes. */
 +static void ui_but_to_pixelrect(struct rcti *rect, const struct ARegion *ar, struct uiBlock *block, struct uiBut *but);
 +
  /* avoid unneeded calls to ui_but_value_get */
  #define UI_BUT_VALUE_UNSET DBL_MAX
  #define UI_GET_BUT_VALUE_INIT(_but, _value) if (_value == DBL_MAX) {  (_value) = ui_but_value_get(_but); } (void)0
@@@ -228,84 -217,6 +228,84 @@@ void ui_region_to_window(const ARegion 
        *y += ar->winrct.ymin;
  }
  
 +static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block)
 +{
 +      int sepr_flex_len = 0;
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              if (but->type == UI_BTYPE_SEPR_SPACER) {
 +                      sepr_flex_len++;
 +              }
 +      }
 +
 +      if (sepr_flex_len == 0) {
 +              return;
 +      }
 +
 +      rcti rect;
 +      ui_but_to_pixelrect(&rect, region, block, block->buttons.last);
 +      const float buttons_width = (float)rect.xmax + UI_HEADER_OFFSET;
 +      const float region_width = (float)region->sizex * U.dpi_fac;
 +
 +      if (region_width <= buttons_width) {
 +              return;
 +      }
 +
 +      /* We could get rid of this loop if we agree on a max number of spacer */
 +      int *spacers_pos = alloca(sizeof(*spacers_pos) * (size_t)sepr_flex_len);
 +      int i = 0;
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              if (but->type == UI_BTYPE_SEPR_SPACER) {
 +                      ui_but_to_pixelrect(&rect, region, block, but);
 +                      spacers_pos[i] = rect.xmax + UI_HEADER_OFFSET;
 +                      i++;
 +              }
 +      }
 +
 +      const float segment_width = region_width / (float)sepr_flex_len;
 +      float offset = 0, remaining_space = region_width - buttons_width;
 +      i = 0;
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              BLI_rctf_translate(&but->rect, offset, 0);
 +              if (but->type == UI_BTYPE_SEPR_SPACER) {
 +                      /* How much the next block overlap with the current segment */
 +                      int overlap = (
 +                              (i == sepr_flex_len - 1) ?
 +                              buttons_width - spacers_pos[i] :
 +                              (spacers_pos[i + 1] - spacers_pos[i]) / 2);
 +                      int segment_end = segment_width * (i + 1);
 +                      int spacer_end = segment_end - overlap;
 +                      int spacer_sta = spacers_pos[i] + offset;
 +                      if (spacer_end > spacer_sta) {
 +                              float step = min_ff(remaining_space, spacer_end - spacer_sta);
 +                              remaining_space -= step;
 +                              offset += step;
 +                      }
 +                      i++;
 +              }
 +      }
 +      ui_block_bounds_calc(block);
 +}
 +
 +static void ui_update_window_matrix(const wmWindow *window, const ARegion *region, uiBlock *block)
 +{
 +      /* window matrix and aspect */
 +      if (region && region->visible) {
 +              /* Get projection matrix which includes View2D translation and zoom. */
 +              gpuGetProjectionMatrix(block->winmat);
 +              block->aspect = 2.0f / fabsf(region->winx * block->winmat[0][0]);
 +      }
 +      else {
 +              /* No subwindow created yet, for menus for example, so we use the main
 +               * window instead, since buttons are created there anyway. */
 +              int width = WM_window_pixels_x(window);
 +              int height = WM_window_pixels_y(window);
 +              rcti winrct = {0, width - 1, 0, height - 1};
 +
 +              wmGetProjectionMatrix(block->winmat, &winrct);
 +              block->aspect = 2.0f / fabsf(width * block->winmat[0][0]);
 +      }
 +}
 +
  /**
   * Popups will add a margin to #ARegion.winrct for shadow,
   * for interactivity (point-inside tests for eg), we want the winrct without the margin added.
@@@ -324,7 -235,7 +324,7 @@@ void ui_region_winrct_get_no_margin(con
  
  /* ******************* block calc ************************* */
  
 -void ui_block_translate(uiBlock *block, int x, int y)
 +void UI_block_translate(uiBlock *block, int x, int y)
  {
        uiBut *but;
  
@@@ -344,7 -255,7 +344,7 @@@ static void ui_block_bounds_calc_text(u
        UI_fontstyle_set(&style->widget);
  
        for (init_col_bt = bt = block->buttons.first; bt; bt = bt->next) {
 -              if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
 +              if (!ELEM(bt->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_SEPR_SPACER)) {
                        j = BLF_width(style->widget.uifont_id, bt->drawstr, sizeof(bt->drawstr));
  
                        if (j > i)
@@@ -434,7 -345,7 +434,7 @@@ static void ui_block_bounds_calc_center
        startx = (xmax * 0.5f) - (width * 0.5f);
        starty = (ymax * 0.5f) - (height * 0.5f);
  
 -      ui_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin);
 +      UI_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -448,7 -359,7 +448,7 @@@ static void ui_block_bounds_calc_center
            block->pie_data.pie_center_spawned[1]
        };
  
 -      ui_block_translate(block, xy[0], xy[1]);
 +      UI_block_translate(block, xy[0], xy[1]);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -508,7 -419,7 +508,7 @@@ static void ui_block_bounds_calc_popup
        rect_bounds.ymax = ymax - UI_POPUP_MENU_TOP;
  
        BLI_rcti_clamp(&rect, &rect_bounds, ofs_dummy);
 -      ui_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin);
 +      UI_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -592,6 -503,86 +592,6 @@@ static int ui_but_calc_float_precision(
        return UI_calc_float_precision(prec, value);
  }
  
 -/* ************** LINK LINE DRAWING  ************* */
 -
 -/* link line drawing is not part of buttons or theme.. so we stick with it here */
 -
 -static void ui_draw_linkline(uiLinkLine *line, int highlightActiveLines, int dashInactiveLines)
 -{
 -      rcti rect;
 -
 -      if (line->from == NULL || line->to == NULL) return;
 -
 -      rect.xmin = BLI_rctf_cent_x(&line->from->rect);
 -      rect.ymin = BLI_rctf_cent_y(&line->from->rect);
 -      rect.xmax = BLI_rctf_cent_x(&line->to->rect);
 -      rect.ymax = BLI_rctf_cent_y(&line->to->rect);
 -
 -      if (dashInactiveLines)
 -              UI_ThemeColor(TH_GRID);
 -      else if (line->flag & UI_SELECT)
 -              glColor3ub(100, 100, 100);
 -      else if (highlightActiveLines && ((line->from->flag & UI_ACTIVE) || (line->to->flag & UI_ACTIVE)))
 -              UI_ThemeColor(TH_TEXT_HI);
 -      else
 -              glColor3ub(0, 0, 0);
 -
 -      ui_draw_link_bezier(&rect);
 -}
 -
 -static void ui_draw_links(uiBlock *block)
 -{
 -      uiBut *but;
 -      uiLinkLine *line;
 -
 -      /* Draw the gray out lines. Do this first so they appear at the
 -       * bottom of inactive or active lines.
 -       * As we go, remember if we see any active or selected lines. */
 -      bool found_selectline = false;
 -      bool found_activeline = false;
 -
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (!(line->from->flag & UI_ACTIVE) && !(line->to->flag & UI_ACTIVE)) {
 -                                      if (line->deactive)
 -                                              ui_draw_linkline(line, 0, true);
 -                              }
 -                              else
 -                                      found_activeline = true;
 -
 -                              if ((line->from->flag & UI_SELECT) || (line->to->flag & UI_SELECT))
 -                                      found_selectline = true;
 -                      }
 -              }
 -      }
 -
 -      /* Draw the inactive lines (lines with neither button being hovered over) */
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (!(line->from->flag & UI_ACTIVE) && !(line->to->flag & UI_ACTIVE)) {
 -                                      if (!line->deactive)
 -                                              ui_draw_linkline(line, 0, false);
 -                              }
 -                      }
 -              }
 -      }
 -
 -      /* Draw any active lines (lines with either button being hovered over).
 -       * Do this last so they appear on top of inactive and gray out lines. */
 -      if (found_activeline) {
 -              for (but = block->buttons.first; but; but = but->next) {
 -                      if (but->type == UI_BTYPE_LINK && but->link) {
 -                              for (line = but->link->lines.first; line; line = line->next) {
 -                                      if ((line->from->flag & UI_ACTIVE) || (line->to->flag & UI_ACTIVE))
 -                                              ui_draw_linkline(line, !found_selectline, false);
 -                              }
 -                      }
 -              }
 -      }
 -}
 -
  /* ************** BLOCK ENDING FUNCTION ************* */
  
  /* NOTE: if but->poin is allocated memory for every defbut, things fail... */
@@@ -634,6 -625,38 +634,6 @@@ uiBut *ui_but_find_new(uiBlock *block_n
        return but_new;
  }
  
 -/* oldbut is being inserted in new block, so we use the lines from new button, and replace button pointers */
 -static void ui_but_update_linklines(uiBlock *block, uiBut *oldbut, uiBut *newbut)
 -{
 -      uiLinkLine *line;
 -      uiBut *but;
 -
 -      /* if active button is UI_BTYPE_LINK */
 -      if (newbut->type == UI_BTYPE_LINK && newbut->link) {
 -
 -              SWAP(uiLink *, oldbut->link, newbut->link);
 -
 -              for (line = oldbut->link->lines.first; line; line = line->next) {
 -                      if (line->to == newbut)
 -                              line->to = oldbut;
 -                      if (line->from == newbut)
 -                              line->from = oldbut;
 -              }
 -      }
 -
 -      /* check all other button links */
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but != newbut && but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (line->to == newbut)
 -                                      line->to = oldbut;
 -                              if (line->from == newbut)
 -                                      line->from = oldbut;
 -                      }
 -              }
 -      }
 -}
 -
  /**
   * \return true when \a but_p is set (only done for active buttons).
   */
@@@ -690,6 -713,8 +690,6 @@@ static bool ui_but_update_from_old_bloc
                but->selend = oldbut->selend;
                but->softmin = oldbut->softmin;
                but->softmax = oldbut->softmax;
 -              but->linkto[0] = oldbut->linkto[0];
 -              but->linkto[1] = oldbut->linkto[1];
                oldbut->active = NULL;
  #endif
  
                        oldbut->a1 = but->a1;
                }
  
 -              ui_but_update_linklines(block, oldbut, but);
 -
                if (!BLI_listbase_is_empty(&block->butstore)) {
                        UI_butstore_register_update(block, oldbut, but);
                }
@@@ -1158,7 -1185,7 +1158,7 @@@ static void ui_menu_block_set_keymaps(c
        uiBut *but;
        char buf[128];
  
 -      BLI_assert(block->flag & UI_BLOCK_LOOP);
 +      BLI_assert(block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS));
  
        /* only do it before bounding */
        if (block->rect.xmin != block->rect.xmax)
        }
        else {
                for (but = block->buttons.first; but; but = but->next) {
 -                      if (but->dt != UI_EMBOSS_PULLDOWN) {
 +                      if (block->flag & UI_BLOCK_SHOW_SHORTCUT_ALWAYS) {
 +                              /* Skip icon-only buttons (as used in the toolbar). */
 +                              if (but->drawstr[0] == '\0') {
 +                                      continue;
 +                              }
 +                      }
 +                      else if (but->dt != UI_EMBOSS_PULLDOWN) {
                                continue;
                        }
  
        }
  }
  
 +void ui_but_override_flag(uiBut *but)
 +{
 +      const int override_status = RNA_property_static_override_status(&but->rnapoin, but->rnaprop, but->rnaindex);
 +
 +      if (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN) {
 +              but->flag |= UI_BUT_OVERRIDEN;
 +      }
 +      else {
 +              but->flag &= ~UI_BUT_OVERRIDEN;
 +      }
 +}
 +
  void UI_block_update_from_old(const bContext *C, uiBlock *block)
  {
        uiBut *but_old;
@@@ -1243,7 -1252,6 +1243,7 @@@ void UI_block_end_ex(const bContext *C
  {
        wmWindow *window = CTX_wm_window(C);
        Scene *scene = CTX_data_scene(C);
 +      ARegion *region = CTX_wm_region(C);
        uiBut *but;
  
        BLI_assert(block->active);
                }
  
                ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f);
 +              ui_but_override_flag(but);
 +              if (UI_but_is_decorator(but)) {
 +                      ui_but_anim_decorate_update_from_flag(but);
 +              }
        }
  
  
        if (block->layouts.first) {
                UI_block_layout_resolve(block, NULL, NULL);
        }
 -      ui_block_align_calc(block);
 +      ui_block_align_calc(block, CTX_wm_region(C));
        if ((block->flag & UI_BLOCK_LOOP) && (block->flag & UI_BLOCK_NUMSELECT)) {
                ui_menu_block_set_keyaccels(block); /* could use a different flag to check */
        }
  
 -      if (block->flag & UI_BLOCK_LOOP) {
 +      if (block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS)) {
                ui_menu_block_set_keymaps(C, block);
        }
  
                UI_block_align_end(block);
        }
  
 +      ui_update_flexible_spacing(region, block);
 +
        block->endblock = 1;
  }
  
@@@ -1371,6 -1373,7 +1371,6 @@@ void UI_block_draw(const bContext *C, u
        ARegion *ar;
        uiBut *but;
        rcti rect;
 -      int multisample_enabled;
  
        /* get menu region or area region */
        ar = CTX_wm_menu(C);
        if (!block->endblock)
                UI_block_end(C, block);
  
 -      /* disable AA, makes widgets too blurry */
 -      multisample_enabled = glIsEnabled(GL_MULTISAMPLE);
 -      if (multisample_enabled)
 -              glDisable(GL_MULTISAMPLE);
 -
        /* we set this only once */
 -      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 +      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  
        /* scale fonts */
        ui_fontscale(&style.paneltitle.points, block->aspect);
        ui_but_to_pixelrect(&rect, ar, block, NULL);
  
        /* pixel space for AA widgets */
 -      glMatrixMode(GL_PROJECTION);
 -      glPushMatrix();
 -      glMatrixMode(GL_MODELVIEW);
 -      glPushMatrix();
 -      glLoadIdentity();
 +      gpuPushProjectionMatrix();
 +      gpuPushMatrix();
 +      gpuLoadIdentity();
  
        wmOrtho2_region_pixelspace(ar);
  
        /* back */
        if (block->flag & UI_BLOCK_RADIAL)
                ui_draw_pie_center(block);
 +      else if (block->flag & UI_BLOCK_POPOVER)
 +              ui_draw_popover_back(ar, &style, block, &rect);
        else if (block->flag & UI_BLOCK_LOOP)
                ui_draw_menu_back(&style, block, &rect);
        else if (block->panel)
                ui_draw_aligned_panel(&style, block, &rect, UI_panel_category_is_visible(ar));
  
 +      BLF_batch_draw_begin();
 +      UI_icon_draw_cache_begin();
 +      UI_widgetbase_draw_cache_begin();
 +
        /* widgets */
        for (but = block->buttons.first; but; but = but->next) {
                if (!(but->flag & (UI_HIDDEN | UI_SCROLLED))) {
                }
        }
  
 -      /* restore matrix */
 -      glMatrixMode(GL_PROJECTION);
 -      glPopMatrix();
 -      glMatrixMode(GL_MODELVIEW);
 -      glPopMatrix();
 +      UI_widgetbase_draw_cache_end();
 +      UI_icon_draw_cache_end();
 +      BLF_batch_draw_end();
  
 -      if (multisample_enabled)
 -              glEnable(GL_MULTISAMPLE);
 +      /* restore matrix */
 +      gpuPopProjectionMatrix();
 +      gpuPopMatrix();
 +}
 +
 +static void ui_block_message_subscribe(ARegion *ar, struct wmMsgBus *mbus, uiBlock *block)
 +{
 +      uiBut *but_prev = NULL;
 +      /* possibly we should keep the region this block is contained in? */
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              if (but->rnapoin.type && but->rnaprop) {
 +                      /* quick check to avoid adding buttons representing a vector, multiple times. */
 +                      if ((but_prev &&
 +                          (but_prev->rnaprop == but->rnaprop) &&
 +                          (but_prev->rnapoin.type == but->rnapoin.type) &&
 +                          (but_prev->rnapoin.data == but->rnapoin.data) &&
 +                          (but_prev->rnapoin.id.data == but->rnapoin.id.data)) == false)
 +                      {
 +                              /* TODO: could make this into utility function. */
 +                              WM_msg_subscribe_rna(
 +                                      mbus, &but->rnapoin, but->rnaprop,
 +                                      &(const wmMsgSubscribeValue){
 +                                          .owner = ar,
 +                                          .user_data = ar,
 +                                          .notify = ED_region_do_msg_notify_tag_redraw,
 +                                      }, __func__);
 +                              but_prev = but;
 +                      }
 +              }
 +      }
 +}
  
 -      ui_draw_links(block);
 +void UI_region_message_subscribe(ARegion *ar, struct wmMsgBus *mbus)
 +{
 +      for (uiBlock *block = ar->uiblocks.first; block; block = block->next) {
 +              ui_block_message_subscribe(ar, mbus, block);
 +      }
  }
  
  /* ************* EVENTS ************* */
@@@ -1523,18 -1496,6 +1523,18 @@@ int ui_but_is_pushed_ex(uiBut *but, dou
                                        if (*value == (double)but->hardmax) is_push = true;
                                }
                                break;
 +                      case UI_BTYPE_TAB:
 +                              if (but->rnaprop && but->custom_data) {
 +                                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                                      if (RNA_property_type(but->rnaprop) == PROP_POINTER) {
 +                                              PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
 +                                              if (active_ptr.data == but->custom_data) {
 +                                                      is_push = true;
 +                                              }
 +                                      }
 +                              }
 +                              break;
                        default:
                                is_push = -1;
                                break;
@@@ -1561,6 -1522,83 +1561,6 @@@ static void ui_but_update_select_flag(u
        }
  }
  
 -static uiBut *ui_linkline_find_inlink(uiBlock *block, void *poin)
 -{
 -      uiBut *but;
 -
 -      but = block->buttons.first;
 -      while (but) {
 -              if (but->type == UI_BTYPE_INLINK) {
 -                      if (but->poin == poin) return but;
 -              }
 -              but = but->next;
 -      }
 -      return NULL;
 -}
 -
 -static void ui_linkline_add(ListBase *listb, uiBut *but, uiBut *bt, short deactive)
 -{
 -      uiLinkLine *line;
 -
 -      line = MEM_callocN(sizeof(uiLinkLine), "linkline");
 -      BLI_addtail(listb, line);
 -      line->from = but;
 -      line->to = bt;
 -      line->deactive = deactive;
 -}
 -
 -uiBut *UI_block_links_find_inlink(uiBlock *block, void *poin)
 -{
 -      return ui_linkline_find_inlink(block, poin);
 -}
 -
 -void UI_block_links_compose(uiBlock *block)
 -{
 -      uiBut *but, *bt;
 -      uiLink *link;
 -      void ***ppoin;
 -      int a;
 -
 -      but = block->buttons.first;
 -      while (but) {
 -              if (but->type == UI_BTYPE_LINK) {
 -                      link = but->link;
 -
 -                      /* for all pointers in the array */
 -                      if (link) {
 -                              if (link->ppoin) {
 -                                      ppoin = link->ppoin;
 -                                      for (a = 0; a < *(link->totlink); a++) {
 -                                              bt = ui_linkline_find_inlink(block, (*ppoin)[a]);
 -                                              if (bt) {
 -                                                      if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) {
 -                                                              ui_linkline_add(&link->lines, but, bt, true);
 -                                                      }
 -                                                      else {
 -                                                              ui_linkline_add(&link->lines, but, bt, false);
 -                                                      }
 -
 -                                              }
 -                                      }
 -                              }
 -                              else if (link->poin) {
 -                                      bt = ui_linkline_find_inlink(block, *link->poin);
 -                                      if (bt) {
 -                                              if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) {
 -                                                      ui_linkline_add(&link->lines, but, bt, true);
 -                                              }
 -                                              else {
 -                                                      ui_linkline_add(&link->lines, but, bt, false);
 -                                              }
 -                                      }
 -                              }
 -                      }
 -              }
 -              but = but->next;
 -      }
 -}
 -
 -
  /* ************************************************ */
  
  void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr)
@@@ -1577,78 -1615,48 +1577,78 @@@ void UI_block_lock_clear(uiBlock *block
        block->lockstr = NULL;
  }
  
 -/* *************************************************************** */
 -
 -void ui_linkline_remove(uiLinkLine *line, uiBut *but)
 -{
 -      uiLink *link;
 -      int a, b;
 -
 -      BLI_remlink(&but->link->lines, line);
 -
 -      link = line->from->link;
 -
 -      /* are there more pointers allowed? */
 -      if (link->ppoin) {
 +/* *********************** data get/set ***********************
 + * this either works with the pointed to data, or can work with
 + * an edit override pointer while dragging for example */
  
 -              if (*(link->totlink) == 1) {
 -                      *(link->totlink) = 0;
 -                      MEM_freeN(*(link->ppoin));
 -                      *(link->ppoin) = NULL;
 -              }
 -              else {
 -                      b = 0;
 -                      for (a = 0; a < (*(link->totlink)); a++) {
 -                              if ((*(link->ppoin))[a] != line->to->poin) {
 -                                      (*(link->ppoin))[b] = (*(link->ppoin))[a];
 -                                      b++;
 -                              }
 -                      }
 -                      (*(link->totlink))--;
 +/* Get PointerRNA which will point to a data inside of an evaluated
 + * ID datablock.
 + */
 +static PointerRNA ui_but_evaluated_rnapoin_get(uiBut *but)
 +{
 +      BLI_assert(but->rnaprop != NULL);
 +      /* TODO(sergey): evil_C sounds.. EVIL! Any clear way to avoid this? */
 +      PointerRNA rnapoin_eval = but->rnapoin;
 +      /* If there is no animation or drivers, it doesn't matter if we read value
 +       * from evaluated datablock or from original one.
 +       *
 +       * Reading from original one is much faster, since we don't need to do any
 +       * PointerRNA remapping or hash lookup.
 +       */
 +      if (BKE_animdata_from_id(but->rnapoin.id.data) == NULL) {
 +              return rnapoin_eval;
 +      }
 +      /* Same goes for the properties which can not be animated. */
 +      if (!RNA_property_animateable(&but->rnapoin, but->rnaprop)) {
 +              return rnapoin_eval;
 +      }
 +      Depsgraph *depsgraph = CTX_data_depsgraph(but->block->evil_C);
 +      /* ID pointer we can always remap, they are inside of depsgraph. */
 +      rnapoin_eval.id.data =
 +              DEG_get_evaluated_id(depsgraph, rnapoin_eval.id.data);
 +      /* Some of ID datablocks do not have their evaluated copy inside
 +       * of dependency graph. If it's such datablock, no need to worry about
 +       * data pointer.
 +       */
 +      if (rnapoin_eval.id.data == but->rnapoin.id.data) {
 +              return rnapoin_eval;
 +      }
 +      /* For the data pointer it's getting a bit more involved, since it can
 +       * whether be and ID, or can be a property deep inside of ID.
 +       *
 +       * We start from checking if it's an ID, since that is the less involved
 +       * code path, and probably is executed in most of the cases.
 +       */
 +      if (but->rnapoin.data == but->rnapoin.id.data) {
 +              rnapoin_eval.data = DEG_get_evaluated_id(depsgraph, rnapoin_eval.data);
 +              return rnapoin_eval;
 +      }
 +      /* We aren't as lucky as we thought we are :(
 +       *
 +       * Since we don't know what the property is, we get it's RNA path
 +       * relative to the original ID, and then we decent down from evaluated
 +       * ID to the same property.
 +       *
 +       * This seems to be most straightforward way to get sub-data pointers
 +       * which can be buried deep inside of ID block.
 +       */
 +      const char *rna_path =
 +             RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
 +      if (rna_path != NULL) {
 +              PointerRNA id_ptr;
 +              RNA_id_pointer_create(rnapoin_eval.id.data, &id_ptr);
 +              if (!RNA_path_resolve_full(&id_ptr,
 +                                         rna_path,
 +                                         &rnapoin_eval,
 +                                         NULL, NULL))
 +              {
 +                      /* TODO(sergey): Anything to do here to recover? */
                }
 +              MEM_freeN((void *)rna_path);
        }
 -      else {
 -              *(link->poin) = NULL;
 -      }
 -
 -      MEM_freeN(line);
 -      //REDRAW
 +      return rnapoin_eval;
  }
  
 -/* *********************** data get/set ***********************
 - * this either works with the pointed to data, or can work with
 - * an edit override pointer while dragging for example */
 -
  /* for buttons pointing to color for example */
  void ui_but_v3_get(uiBut *but, float vec[3])
  {
  
                zero_v3(vec);
  
 +              PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but);
 +
                if (RNA_property_type(prop) == PROP_FLOAT) {
 -                      int tot = RNA_property_array_length(&but->rnapoin, prop);
 +                      int tot = RNA_property_array_length(&rnapoin_eval, prop);
                        BLI_assert(tot > 0);
                        if (tot == 3) {
 -                              RNA_property_float_get_array(&but->rnapoin, prop, vec);
 +                              RNA_property_float_get_array(&rnapoin_eval, prop, vec);
                        }
                        else {
                                tot = min_ii(tot, 3);
                                for (a = 0; a < tot; a++) {
 -                                      vec[a] = RNA_property_float_get_index(&but->rnapoin, prop, a);
 +                                      vec[a] = RNA_property_float_get_index(&rnapoin_eval, prop, a);
                                }
                        }
                }
@@@ -1855,29 -1861,27 +1855,29 @@@ double ui_but_value_get(uiBut *but
  
                BLI_assert(but->rnaindex != -1);
  
 +              PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but);
 +
                switch (RNA_property_type(prop)) {
                        case PROP_BOOLEAN:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_boolean_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_boolean_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_boolean_get(&but->rnapoin, prop);
 +                                      value = RNA_property_boolean_get(&rnapoin_eval, prop);
                                break;
                        case PROP_INT:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_int_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_int_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_int_get(&but->rnapoin, prop);
 +                                      value = RNA_property_int_get(&rnapoin_eval, prop);
                                break;
                        case PROP_FLOAT:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_float_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_float_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_float_get(&but->rnapoin, prop);
 +                                      value = RNA_property_float_get(&rnapoin_eval, prop);
                                break;
                        case PROP_ENUM:
 -                              value = RNA_property_enum_get(&but->rnapoin, prop);
 +                              value = RNA_property_enum_get(&rnapoin_eval, prop);
                                break;
                        default:
                                value = 0.0;
@@@ -2020,7 -2024,7 +2020,7 @@@ static bool ui_but_icon_extra_is_visibl
  
  static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but)
  {
 -      BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
 +      BLI_assert(ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB));
        return ((but->editstr == NULL) &&
                (but->drawstr[0] != '\0') &&
                (but->flag & UI_BUT_VALUE_CLEAR));
@@@ -2063,11 -2067,6 +2063,11 @@@ uiButExtraIconType ui_but_icon_extra_ge
                                return UI_BUT_ICONEXTRA_EYEDROPPER;
                        }
                        break;
 +              case UI_BTYPE_TAB:
 +                      if (ui_but_icon_extra_is_visible_search_unlink(but)) {
 +                              return UI_BUT_ICONEXTRA_CLEAR;
 +                      }
 +                      break;
                default:
                        break;
        }
@@@ -2181,23 -2180,14 +2181,23 @@@ void ui_but_string_get_ex(uiBut *but, c
                *r_use_exp_float = false;
        }
  
 -      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
 +      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB)) {
                PropertyType type;
                const char *buf = NULL;
                int buf_len;
  
                type = RNA_property_type(but->rnaprop);
  
 -              if (type == PROP_STRING) {
 +              if ((but->type == UI_BTYPE_TAB) && (but->custom_data)) {
 +                      StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
 +                      PointerRNA ptr;
 +
 +                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                      RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
 +                      buf = RNA_struct_name_get_alloc(&ptr, str, maxlen, &buf_len);
 +              }
 +              else if (type == PROP_STRING) {
                        /* RNA string */
                        buf = RNA_property_string_get_alloc(&but->rnapoin, but->rnaprop, str, maxlen, &buf_len);
                }
                        MEM_freeN((void *)buf);
                }
        }
 -      else if (but->type == UI_BTYPE_TEXT) {
 -              /* string */
 -              BLI_strncpy(str, but->poin, maxlen);
 -              return;
 -      }
 -      else if (but->type == UI_BTYPE_SEARCH_MENU) {
 +      else if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
                /* string */
                BLI_strncpy(str, but->poin, maxlen);
                return;
@@@ -2431,7 -2426,7 +2431,7 @@@ static void ui_but_string_free_internal
  
  bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
  {
 -      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
 +      if (but->rnaprop && but->rnapoin.data && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
                if (RNA_property_editable(&but->rnapoin, but->rnaprop)) {
                        PropertyType type;
  
                                return true;
                        }
                        else if (type == PROP_POINTER) {
 -                              /* RNA pointer */
 -                              PointerRNA ptr, rptr;
 -                              PropertyRNA *prop;
 -
                                if (str[0] == '\0') {
                                        RNA_property_pointer_set(&but->rnapoin, but->rnaprop, PointerRNA_NULL);
                                        return true;
                                }
                                else {
 -                                      ptr = but->rnasearchpoin;
 -                                      prop = but->rnasearchprop;
 +                                      /* RNA pointer */
 +                                      PointerRNA rptr;
 +                                      PointerRNA ptr = but->rnasearchpoin;
 +                                      PropertyRNA *prop = but->rnasearchprop;
  
                                        if (prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr))
                                                RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr);
                        }
                }
        }
 +      else if (but->type == UI_BTYPE_TAB) {
 +              if (but->rnaprop && but->custom_data) {
 +                      StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
 +                      PointerRNA ptr;
 +                      PropertyRNA *prop;
 +
 +                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                      RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
 +                      prop = RNA_struct_name_property(ptr_type);
 +                      if (RNA_property_editable(&ptr, prop)) {
 +                              RNA_property_string_set(&ptr, prop, str);
 +                      }
 +              }
 +      }
        else if (but->type == UI_BTYPE_TEXT) {
                /* string */
 -              if (ui_but_is_utf8(but)) BLI_strncpy_utf8(but->poin, str, but->hardmax);
 -              else BLI_strncpy(but->poin, str, but->hardmax);
 +              if (!but->poin || (str[0] == '\0')) {
 +                      str = "";
 +              }
 +              else if (ui_but_is_utf8(but)) {
 +                      BLI_strncpy_utf8(but->poin, str, but->hardmax);
 +              }
 +              else {
 +                      BLI_strncpy(but->poin, str, but->hardmax);
 +              }
  
                return true;
        }
@@@ -2683,6 -2658,14 +2683,6 @@@ static void ui_set_but_soft_range(uiBu
  
  /* ******************* Free ********************/
  
 -static void ui_free_link(uiLink *link)
 -{
 -      if (link) {
 -              BLI_freelistN(&link->lines);
 -              MEM_freeN(link);
 -      }
 -}
 -
  /* can be called with C==NULL */
  static void ui_but_free(const bContext *C, uiBut *but)
  {
                MEM_freeN(but->hold_argN);
        }
  
 +      if (!but->editstr && but->free_search_arg) {
 +              MEM_SAFE_FREE(but->search_arg);
 +      }
 +
        if (but->active) {
                /* XXX solve later, buttons should be free-able without context ideally,
                 * however they may have open tooltips or popup windows, which need to
        if (but->str && but->str != but->strdata) {
                MEM_freeN(but->str);
        }
 -      ui_free_link(but->link);
  
        if ((but->type == UI_BTYPE_IMAGE) && but->poin) {
                IMB_freeImBuf((struct ImBuf *)but->poin);
@@@ -2764,27 -2744,6 +2764,27 @@@ void UI_block_free(const bContext *C, u
        MEM_freeN(block);
  }
  
 +void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb)
 +{
 +      ARegion *region = CTX_wm_region(C);
 +      wmWindow *window = CTX_wm_window(C);
 +
 +      for (uiBlock *block = lb->first; block; block = block->next) {
 +              if (block->active) {
 +                      ui_update_window_matrix(window, region, block);
 +              }
 +      }
 +}
 +
 +void UI_blocklist_draw(const bContext *C, const ListBase *lb)
 +{
 +      for (uiBlock *block = lb->first; block; block = block->next) {
 +              if (block->active) {
 +                      UI_block_draw(C, block);
 +              }
 +      }
 +}
 +
  /* can be called with C==NULL */
  void UI_blocklist_free(const bContext *C, ListBase *lb)
  {
@@@ -2841,6 -2800,7 +2841,6 @@@ uiBlock *UI_block_begin(const bContext 
        uiBlock *block;
        wmWindow *window;
        Scene *scn;
 -      int getsizex, getsizey;
  
        window = CTX_wm_window(C);
        scn = CTX_data_scene(C);
        if (region)
                UI_block_region_set(block, region);
  
 -      /* window matrix and aspect */
 -      if (region && region->swinid) {
 -              wm_subwindow_matrix_get(window, region->swinid, block->winmat);
 -              wm_subwindow_size_get(window, region->swinid, &getsizex, &getsizey);
 +      /* Set window matrix and aspect for region and OpenGL state. */
 +      ui_update_window_matrix(window, region, block);
  
 -              block->aspect = 2.0f / fabsf(getsizex * block->winmat[0][0]);
 -      }
 -      else {
 -              /* no subwindow created yet, for menus for example, so we
 -               * use the main window instead, since buttons are created
 -               * there anyway */
 -              wm_subwindow_matrix_get(window, window->screen->mainwin, block->winmat);
 -              wm_subwindow_size_get(window, window->screen->mainwin, &getsizex, &getsizey);
 -
 -              block->aspect = 2.0f / fabsf(getsizex * block->winmat[0][0]);
 +      /* Tag as popup menu if not created within a region. */
 +      if (!(region && region->visible)) {
                block->auto_open = true;
 -              block->flag |= UI_BLOCK_LOOP; /* tag as menu */
 +              block->flag |= UI_BLOCK_LOOP;
        }
  
        return block;
  }
  
 -uiBlock *UI_block_find_in_region(const char *name, ARegion *ar)
 -{
 -      return BLI_findstring(&ar->uiblocks, name, offsetof(uiBlock, name));
 -}
 -
  void UI_block_emboss_set(uiBlock *block, char dt)
  {
        block->dt = dt;
@@@ -3038,7 -3013,6 +3038,7 @@@ void ui_but_update_ex(uiBut *but, cons
  
                case UI_BTYPE_TEXT:
                case UI_BTYPE_SEARCH_MENU:
 +              case UI_BTYPE_TAB:
                        if (!but->editstr) {
                                char str[UI_MAX_DRAW_STR];
  
@@@ -3162,16 -3136,6 +3162,16 @@@ void ui_block_cm_to_display_space_range
        *max = max_fff(UNPACK3(pixel));
  }
  
 +static uiBut *ui_but_alloc(const eButType type)
 +{
 +      switch (type) {
 +              case UI_BTYPE_TAB:
 +                      return MEM_callocN(sizeof(uiButTab), "uiButTab");
 +              default:
 +                      return MEM_callocN(sizeof(uiBut), "uiBut");
 +      }
 +}
 +
  /**
   * \brief ui_def_but is the function that draws many button types
   *
@@@ -3205,7 -3169,7 +3205,7 @@@ static uiBut *ui_def_but
                }
        }
  
 -      but = MEM_callocN(sizeof(uiBut), "uiBut");
 +      but = ui_but_alloc(type & BUTTYPE);
  
        but->type = type & BUTTYPE;
        but->pointype = type & UI_BUT_POIN_TYPES;
                 ELEM(but->type,
                      UI_BTYPE_MENU, UI_BTYPE_TEXT, UI_BTYPE_LABEL,
                      UI_BTYPE_BLOCK, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU,
 -                    UI_BTYPE_PROGRESS_BAR))
 +                    UI_BTYPE_PROGRESS_BAR, UI_BTYPE_POPOVER))
        {
                but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
        }
                 UI_BTYPE_BLOCK, UI_BTYPE_BUT, UI_BTYPE_LABEL,
                 UI_BTYPE_PULLDOWN, UI_BTYPE_ROUNDBOX, UI_BTYPE_LISTBOX,
                 UI_BTYPE_BUT_MENU, UI_BTYPE_SCROLL, UI_BTYPE_GRIP,
 -               UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE) ||
 +               UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE,
 +               UI_BTYPE_SEPR_SPACER) ||
            (but->type >= UI_BTYPE_SEARCH_MENU))
        {
                /* pass */
@@@ -3598,7 -3561,7 +3598,7 @@@ static uiBut *ui_def_but_rna
        }
  
        const char *info;
 -      if (!RNA_property_editable_info(&but->rnapoin, prop, &info)) {
 +      if (but->rnapoin.data && !RNA_property_editable_info(&but->rnapoin, prop, &info)) {
                ui_def_but_rna__disable(but, info);
        }
  
@@@ -4017,6 -3980,19 +4017,6 @@@ uiBut *uiDefIconTextButO(uiBlock *block
  
  /* END Button containing both string label and icon */
  
 -void UI_but_link_set(uiBut *but, void **poin, void ***ppoin, short *tot, int from, int to)
 -{
 -      uiLink *link;
 -
 -      link = but->link = MEM_callocN(sizeof(uiLink), "new uilink");
 -
 -      link->poin = poin;
 -      link->ppoin = ppoin;
 -      link->totlink = tot;
 -      link->fromcode = from;
 -      link->tocode = to;
 -}
 -
  /* cruft to make uiBlock and uiBut private */
  
  int UI_blocklist_min_y_get(ListBase *lb)
        return min;
  }
  
+ bool UI_block_is_empty(const uiBlock *block)
+ {
+       for (const uiBut *but = block->buttons.first; but; but = but->next) {
+               if (!ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
+                       return false;
+               }
+       }
+       return true;
+ }
  void UI_block_direction_set(uiBlock *block, char direction)
  {
        block->direction = direction;
@@@ -4484,7 -4470,7 +4494,7 @@@ static void operator_enum_search_cb(con
                for (item = item_array; item->identifier; item++) {
                        /* note: need to give the index rather than the identifier because the enum can be freed */
                        if (BLI_strcasestr(item->name, str)) {
 -                              if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), 0))
 +                              if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), item->icon))
                                        break;
                        }
                }
@@@ -4615,7 -4601,7 +4625,7 @@@ void UI_but_string_info_get(bContext *C
                                tmp = BLI_strdup(RNA_property_identifier(but->rnaprop));
                }
                else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) {
 -                      if (but->rnaprop)
 +                      if (but->rnaprop && but->rnapoin.data)
                                tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type));
                        else if (but->optype)
                                tmp = BLI_strdup(but->optype->idname);
                                PointerRNA *opptr = UI_but_operator_ptr_get(but);
                                wmOperatorType *ot = but->optype;
  
 +                              /* so the context is passed to itemf functions */
 +                              WM_operator_properties_sanitize(opptr, false);
 +
                                /* if the default property of the operator is enum and it is set,
                                 * fetch the tooltip of the selected value so that "Snap" and "Mirror"
                                 * operator menus in the Anim Editors will show tooltips for the different
@@@ -4780,4 -4763,3 +4790,3 @@@ void UI_exit(void
        ui_resources_free();
        ui_but_clipboard_free();
  }
index 9d202c870777f1609827451e7ebccb71fd380ea0,0654e4d5f1b417d8f5acd84c018ec5736e69b960..cb37c3010312d4098d73b02efefa31dd68721578
@@@ -39,6 -39,9 +39,6 @@@
  #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"
@@@ -76,7 -79,6 +76,7 @@@
  #include "ED_keyframing.h"
  
  #include "UI_interface.h"
 +#include "UI_view2d.h"
  
  #include "BLF_api.h"
  
  #define USE_KEYMAP_ADD_HACK
  
  /* 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);
@@@ -130,7 -134,7 +130,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
@@@ -281,7 -285,6 +281,7 @@@ typedef struct uiHandleButtonData 
        /* booleans (could be made into flags) */
        bool cancel, escapecancel;
        bool applied, applied_interactive;
 +      bool changed_cursor;
        wmTimer *flashtimer;
  
        /* edited value */
@@@ -507,16 -510,6 +507,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) {
@@@ -665,8 -658,11 +665,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);
        }
  }
  
@@@ -747,7 -743,8 +747,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;
  
@@@ -959,20 -956,6 +959,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) {
@@@ -1275,9 -1258,6 +1275,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;
        }
@@@ -1288,9 -1268,6 +1288,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;
        }
@@@ -1301,6 -1278,7 +1301,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) {
@@@ -1404,7 -1382,7 +1404,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);
@@@ -1783,6 -1761,7 +1783,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);
@@@ -1967,9 -2163,6 +1967,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;
@@@ -3018,9 -3215,6 +3018,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;
@@@ -3492,7 -3686,6 +3492,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
        {
@@@ -3792,43 -3975,6 +3792,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)
@@@ -4257,54 -4403,6 +4257,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);
@@@ -5197,8 -5293,7 +5197,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) {
@@@ -6115,7 -6210,6 +6115,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);
                                }
                        }
  
@@@ -6410,6 -6504,35 +6410,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,
@@@ -6714,46 -6837,34 +6714,36 @@@ static void ui_but_menu_add_path_operat
        RNA_string_set(&props_ptr, "filepath", dir);
  }
  
- static void ui_but_menu_lazy_init(
-         bContext *C, uiBut *but,
-         uiPopupMenu **pup_p, uiLayout **layout_p)
- {
-       if (*pup_p != NULL) {
-               return;
-       }
-       uiStringInfo label = {BUT_GET_LABEL, NULL};
-       /* highly unlikely getting the label ever fails */
-       UI_but_string_info_get(C, but, &label, NULL);
-       *pup_p = UI_popup_menu_begin(C, label.strinfo ? label.strinfo : "", ICON_NONE);
-       *layout_p = UI_popup_menu_layout(*pup_p);
-       if (label.strinfo) {
-               MEM_freeN(label.strinfo);
-       }
-       uiLayoutSetOperatorContext(*layout_p, WM_OP_INVOKE_DEFAULT);
- }
  static bool ui_but_menu(bContext *C, uiBut *but)
  {
-       uiPopupMenu *pup = NULL;
-       uiLayout *layout = NULL;
        MenuType *mt = WM_menutype_find("WM_MT_button_context", true);
        bool is_array, is_array_component;
 +      wmOperatorType *ot;
 +      PointerRNA op_ptr;
  
- /*    if ((but->rnapoin.data && but->rnaprop) == 0 && but->optype == NULL)*/
- /*            return 0;*/
        /* having this menu for some buttons makes no sense */
        if (but->type == UI_BTYPE_IMAGE) {
                return false;
        }
  
+       uiPopupMenu *pup;
+       uiLayout *layout;
+       {
+               uiStringInfo label = {BUT_GET_LABEL, NULL};
+               /* highly unlikely getting the label ever fails */
+               UI_but_string_info_get(C, but, &label, NULL);
+               pup = UI_popup_menu_begin(C, label.strinfo ? label.strinfo : "", ICON_NONE);
+               layout = UI_popup_menu_layout(pup);
+               if (label.strinfo) {
+                       MEM_freeN(label.strinfo);
+               }
+               uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
+       }
        if (but->rnapoin.data && but->rnaprop) {
-               ui_but_menu_lazy_init(C, but, &pup, &layout);
                PointerRNA *ptr = &but->rnapoin;
                PropertyRNA *prop = but->rnaprop;
                const PropertyType type = RNA_property_type(prop);
                /*bool is_idprop = RNA_property_is_idprop(prop);*/ /* XXX does not work as expected, not strictly needed */
                bool is_set = RNA_property_is_set(ptr, prop);
  
 -
 +              const int override_status = RNA_property_static_override_status(ptr, prop, -1);
 +              const bool is_overridable = (override_status & RNA_OVERRIDE_STATUS_OVERRIDABLE) != 0;
  
                /* second slower test, saved people finding keyframe items in menus when its not possible */
                if (is_anim)
  
                /* Keyframes */
                if (but->flag & UI_BUT_ANIMATED_KEY) {
 -                      /* Set the (button_pointer, button_prop) and pointer data for Python access to the hovered ui element. */
 -                      uiLayoutSetContextFromBut(layout, but);
 -
                        /* replace/delete keyfraemes */
                        if (is_array_component) {
                                uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Replace Keyframes"),
                                uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Paste Driver"),
                                        ICON_NONE, "ANIM_OT_paste_driver_button");
                        }
 +
 +                      uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Edit Driver"),
 +                              ICON_DRIVER, "ANIM_OT_driver_button_edit");
 +
 +                      uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Open Drivers Editor"),
 +                              ICON_NONE, "SCREEN_OT_drivers_editor_show");
                }
                else if (but->flag & (UI_BUT_ANIMATED_KEY | UI_BUT_ANIMATED)) {
                        /* pass */
                else if (is_anim) {
                        uiItemS(layout);
  
 -                      if (is_array_component) {
 -                              uiItemMenuEnumO(layout, C, "ANIM_OT_driver_button_add", "mapping_type",
 -                                              CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Drivers"),
 -                                              ICON_DRIVER);
 -                      }
 -                      else {
 -                              uiItemMenuEnumO(layout, C, "ANIM_OT_driver_button_add", "mapping_type",
 -                                              CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Driver"),
 -                                              ICON_DRIVER);
 -                      }
 +                      uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Driver"),
 +                              ICON_DRIVER, "ANIM_OT_driver_button_add");
  
                        if (ANIM_driver_can_paste()) {
                                uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Paste Driver"),
                                        ICON_NONE, "ANIM_OT_paste_driver_button");
                        }
 +
 +                      uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Open Drivers Editor"),
 +                              ICON_NONE, "SCREEN_OT_drivers_editor_show");
                }
  
                /* Keying Sets */
                        }
                }
  
 +              if (is_overridable) {
 +                      /* Override Operators */
 +                      uiItemS(layout);
 +
 +                      if (but->flag & UI_BUT_OVERRIDEN) {
 +                              if (is_array_component) {
 +#if 0  /* Disabled for now. */
 +                                      ot = WM_operatortype_find("UI_OT_override_type_set_button", false);
 +                                      uiItemFullO_ptr(layout, ot, "Overrides Type", ICON_NONE,
 +                                                      NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
 +                                      RNA_boolean_set(&op_ptr, "all", true);
 +                                      uiItemFullO_ptr(layout, ot, "Single Override Type", ICON_NONE,
 +                                                      NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
 +                                      RNA_boolean_set(&op_ptr, "all", false);
 +#endif
 +                                      uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove Overrides"),
 +                                                     ICON_X, "UI_OT_override_remove_button", "all", true);
 +                                      uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove Single Override"),
 +                                                     ICON_X, "UI_OT_override_remove_button", "all", false);
 +                              }
 +                              else {
 +#if 0  /* Disabled for now. */
 +                                      uiItemFullO(layout, "UI_OT_override_type_set_button", "Override Type", ICON_NONE,
 +                                                  NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
 +                                      RNA_boolean_set(&op_ptr, "all", false);
 +#endif
 +                                      uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove Override"),
 +                                                     ICON_X, "UI_OT_override_remove_button", "all", true);
 +                              }
 +                      }
 +                      else {
 +                              if (is_array_component) {
 +                                      ot = WM_operatortype_find("UI_OT_override_type_set_button", false);
 +                                      uiItemFullO_ptr(layout, ot, "Define Overrides", ICON_NONE,
 +                                                      NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
 +                                      RNA_boolean_set(&op_ptr, "all", true);
 +                                      uiItemFullO_ptr(layout, ot, "Define Single Override", ICON_NONE,
 +                                                      NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
 +                                      RNA_boolean_set(&op_ptr, "all", false);
 +                              }
 +                              else {
 +                                      uiItemFullO(layout, "UI_OT_override_type_set_button", "Define Override", ICON_NONE,
 +                                                  NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
 +                                      RNA_boolean_set(&op_ptr, "all", false);
 +                              }
 +                      }
 +              }
 +
                uiItemS(layout);
  
                /* Property Operators */
  
                /* We do have a shortcut, but only keyboard ones are editbale that way... */
                if (kmi) {
-                       ui_but_menu_lazy_init(C, but, &pup, &layout);
                        if (ISKEYBOARD(kmi->type)) {
  #if 0                 /* would rather use a block but, but gets weirdly positioned... */
                                uiDefBlockBut(block, menu_change_shortcut, but, "Change Shortcut",
                }
                /* only show 'add' if there's a suitable key map for it to go in */
                else if (WM_keymap_guess_opname(C, but->optype->idname)) {
-                       ui_but_menu_lazy_init(C, but, &pup, &layout);
                        but2 = uiDefIconTextBut(block, UI_BTYPE_BUT, 0, ICON_HAND,
                                                CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Shortcut"),
                                                0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
        if (ui_block_is_menu(but->block) == false) {
                ARegion *ar = CTX_wm_region(C);
                if (ar && (ar->regiontype == RGN_TYPE_HEADER)) {
-                       ui_but_menu_lazy_init(C, but, &pup, &layout);
                        uiItemMenuF(layout, IFACE_("Header"), ICON_NONE, ED_screens_header_tools_menu_create, NULL);
                        uiItemS(layout);
                }
                char buf[512];
  
                if (UI_but_online_manual_id(but, buf, sizeof(buf))) {
-                       ui_but_menu_lazy_init(C, but, &pup, &layout);
                        PointerRNA ptr_props;
                        uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Online Manual"),
                                ICON_URL, "WM_OT_doc_view_manual_ui_context");
        }
  
        if (but->optype) {
-               ui_but_menu_lazy_init(C, but, &pup, &layout);
                uiItemO(layout, NULL,
                        ICON_NONE, "UI_OT_copy_python_command_button");
        }
        /* perhaps we should move this into (G.debug & G_DEBUG) - campbell */
        if (U.flag & USER_DEVELOPER_UI) {
                if (ui_block_is_menu(but->block) == false) {
-                       ui_but_menu_lazy_init(C, but, &pup, &layout);
                        uiItemFullO(layout, "UI_OT_editsource", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, NULL);
                }
        }
  
        if (BKE_addon_find(&U.addons, "ui_translate")) {
-               ui_but_menu_lazy_init(C, but, &pup, &layout);
                uiItemFullO(layout, "UI_OT_edittranslation_init", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, NULL);
        }
  
        mt = WM_menutype_find("WM_MT_button_context", true);
        if (mt) {
-               ui_but_menu_lazy_init(C, but, &pup, &layout);
                UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
        }
  
-       if (pup != NULL) {
-               UI_popup_menu_end(C, pup);
-       }
-       return (pup != NULL);
+       return UI_popup_menu_end_or_cancel(C, pup);
  }
  
  static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *event)
                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;
        }
@@@ -7442,11 -7490,12 +7417,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;
  
@@@ -7532,7 -7581,7 +7507,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;
                }
        }
@@@ -7687,7 -7736,7 +7662,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);
                }
@@@ -7756,7 -7805,7 +7731,7 @@@ static void button_activate_state(bCont
                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;
  
@@@ -7966,9 -8015,6 +7941,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) {
@@@ -8254,11 -8295,6 +8229,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) {
@@@ -8446,29 -8482,8 +8421,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)) {
@@@ -8813,8 -8833,13 +8788,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;
@@@ -9073,9 -9098,6 +9048,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;
@@@ -9228,7 -9250,7 +9203,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;
@@@ -10240,10 -10253,10 +10215,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 */
@@@ -10500,18 -10513,3 +10475,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 a6046e551c648a88ac7296816c6322524c349c9d,fa7113f195ed9d7d8eec52f61a1e3931365d1385..b9222a75803a08a94e1ab94845e0151522e67e9a
@@@ -191,13 -191,7 +191,13 @@@ static uiBlock *ui_block_func_POPUP(bCo
  
        if (pup->but) {
                /* minimum width to enforece */
 -              minwidth = BLI_rctf_size_x(&pup->but->rect);
 +              if (pup->but->drawstr[0]) {
 +                      minwidth = BLI_rctf_size_x(&pup->but->rect);
 +              }
 +              else {
 +                      /* For buttons with no text, use the minimum (typically icon only). */
 +                      minwidth = UI_MENU_WIDTH_MIN;
 +              }
  
                /* settings (typically rna-enum-popups) show above the button,
                 * menu's like file-menu, show below */
                }
        }
        else {
 -              minwidth = 50;
 +              minwidth = UI_MENU_WIDTH_MIN;
                direction = UI_DIR_DOWN;
        }
  
                /* for a header menu we set the direction automatic */
                if (!pup->slideout && flip) {
                        ScrArea *sa = CTX_wm_area(C);
 -                      if (sa && sa->headertype == HEADERDOWN) {
 +                      if (sa && ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) {
                                ARegion *ar = CTX_wm_region(C);
                                if (ar && ar->regiontype == RGN_TYPE_HEADER) {
                                        UI_block_direction_set(block, UI_DIR_UP);
@@@ -365,6 -359,7 +365,6 @@@ uiPopupBlockHandle *ui_popup_menu_creat
                WM_event_add_mousemove(C);
        }
  
 -      handle->can_refresh = false;
        MEM_freeN(pup);
  
        return handle;
@@@ -455,9 -450,25 +455,24 @@@ void UI_popup_menu_end(bContext *C, uiP
        UI_popup_handlers_add(C, &window->modalhandlers, menu, 0);
        WM_event_add_mousemove(C);
  
 -      menu->can_refresh = false;
        MEM_freeN(pup);
  }
  
+ bool UI_popup_menu_end_or_cancel(bContext *C, uiPopupMenu *pup)
+ {
+       if (!UI_block_is_empty(pup->block)) {
+               UI_popup_menu_end(C, pup);
+               return true;
+       }
+       else {
+               UI_block_layout_resolve(pup->block, NULL, NULL);
+               MEM_freeN(pup->block->handle);
+               UI_block_free(C, pup->block);
+               MEM_freeN(pup);
+               return false;
+       }
+ }
  uiLayout *UI_popup_menu_layout(uiPopupMenu *pup)
  {
        return pup->layout;
@@@ -558,7 -569,6 +573,7 @@@ void UI_popup_block_invoke_ex(bContext 
  
        handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
        handle->popup = true;
 +      handle->can_refresh = true;
        handle->optype = (opname) ? WM_operatortype_find(opname, 0) : NULL;
        handle->opcontext = opcontext;
  
@@@ -581,7 -591,6 +596,7 @@@ void UI_popup_block_ex
        handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
        handle->popup = true;
        handle->retvalue = 1;
 +      handle->can_refresh = true;
  
        handle->popup_op = op;
        handle->popup_arg = arg;
@@@ -602,7 -611,6 +617,7 @@@ void uiPupBlockOperator(bContext *C, ui
        handle = ui_popup_block_create(C, NULL, NULL, func, NULL, op);
        handle->popup = 1;
        handle->retvalue = 1;
 +      handle->can_refresh = true;
  
        handle->popup_arg = op;
        handle->popup_func = operator_cb;
@@@ -619,13 -627,11 +634,13 @@@ void UI_popup_block_close(bContext *C, 
        /* if loading new .blend while popup is open, window will be NULL */
        if (block->handle) {
                if (win) {
 +                      const bScreen *screen = WM_window_get_active_screen(win);
 +
                        UI_popup_handlers_remove(&win->modalhandlers, block->handle);
                        ui_popup_block_free(C, block->handle);
  
                        /* In the case we have nested popups, closing one may need to redraw another, see: T48874 */
 -                      for (ARegion *ar = win->screen->regionbase.first; ar; ar = ar->next) {
 +                      for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) {
                                ED_region_tag_refresh_ui(ar);
                        }
                }