Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Sat, 16 Jun 2018 14:16:53 +0000 (16:16 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 16 Jun 2018 14:16:53 +0000 (16:16 +0200)
1  2 
source/blender/editors/interface/interface_handlers.c

@@@ -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"
@@@ -75,7 -78,6 +75,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);
@@@ -129,7 -133,7 +129,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
@@@ -280,7 -284,6 +280,7 @@@ typedef struct uiHandleButtonData 
        /* booleans (could be made into flags) */
        bool cancel, escapecancel;
        bool applied, applied_interactive;
 +      bool changed_cursor;
        wmTimer *flashtimer;
  
        /* edited value */
@@@ -506,16 -509,6 +506,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) {
@@@ -664,8 -657,11 +664,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);
        }
  }
  
@@@ -746,7 -742,8 +746,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;
  
@@@ -958,20 -955,6 +958,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) {
@@@ -1267,6 -1250,28 +1267,28 @@@ static void ui_multibut_states_apply(bC
  
  #ifdef USE_DRAG_TOGGLE
  
+ /* Helpers that wrap boolean functions, to support different kinds of buttons. */
+ static bool ui_drag_toggle_but_is_supported(const uiBut *but)
+ {
+       if (ui_but_is_bool(but)) {
+               return true;
+       }
+       else {
+               return false;
+       }
+ }
+ static bool ui_drag_toggle_but_is_pushed(uiBut *but)
+ {
+       if (ui_but_is_bool(but)) {
+               return ui_but_is_pushed(but);
+       }
+       else {
+               return false;
+       }
+ }
  typedef struct uiDragToggleHandle {
        /* init */
        bool is_init;
@@@ -1304,10 -1309,9 +1326,9 @@@ static bool ui_drag_toggle_set_xy_xy
                                if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) {
  
                                        /* execute the button */
-                                       if (ui_but_is_bool(but) && but->type == but_type_start) {
+                                       if (ui_drag_toggle_but_is_supported(but) && but->type == but_type_start) {
                                                /* is it pressed? */
-                                               bool is_set_but = ui_but_is_pushed(but);
-                                               BLI_assert(ui_but_is_bool(but) == true);
+                                               bool is_set_but = ui_drag_toggle_but_is_pushed(but);
                                                if (is_set_but != is_set) {
                                                        UI_but_execute(C, but);
                                                        if (do_check) {
@@@ -1437,7 -1441,7 +1458,7 @@@ static int ui_handler_region_drag_toggl
  
  static bool ui_but_is_drag_toggle(const uiBut *but)
  {
-       return ((ui_but_is_bool(but) == true) &&
+       return ((ui_drag_toggle_but_is_supported(but) == true) &&
                /* menu check is importnt so the button dragged over isn't removed instantly */
                (ui_block_is_menu(but->block) == false));
  }
@@@ -1745,7 -1749,7 +1766,7 @@@ static bool ui_but_drag_init
                button_activate_state(C, but, BUTTON_STATE_EXIT);
                data->cancel = true;
  #ifdef USE_DRAG_TOGGLE
-               if (ui_but_is_bool(but)) {
+               if (ui_drag_toggle_but_is_supported(but)) {
                        uiDragToggleHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__);
                        ARegion *ar_prev;
  
                         * typically 'button_activate_exit()' handles this */
                        ui_apply_but_autokey(C, but);
  
-                       drag_info->is_set = ui_but_is_pushed(but);
+                       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;
  
  /* ********************** 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);
@@@ -1941,9 -2162,6 +1962,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;
@@@ -2992,9 -3214,6 +3013,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;
@@@ -3466,7 -3685,6 +3487,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
        {
@@@ -3585,10 -3793,50 +3606,50 @@@ static uiBut *ui_but_list_row_text_acti
  
  /* ***************** events for different button types *************** */
  
+ #ifdef USE_DRAG_TOGGLE
+ /* Shared by any button that supports drag-toggle. */
+ static bool ui_do_but_ANY_drag_toggle(
+         bContext *C, uiBut *but,
+         uiHandleButtonData *data, const wmEvent *event,
+         int *r_retval)
+ {
+       if (data->state == BUTTON_STATE_HIGHLIGHT) {
+               if (event->type == LEFTMOUSE && event->val == KM_PRESS && ui_but_is_drag_toggle(but)) {
+ #if 0         /* UNUSED */
+                       data->togdual = event->ctrl;
+                       data->togonly = !event->shift;
+ #endif
+                       ui_apply_but(C, but->block, but, data, true);
+                       button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
+                       data->dragstartx = event->x;
+                       data->dragstarty = event->y;
+                       *r_retval = WM_UI_HANDLER_BREAK;
+                       return true;
+               }
+       }
+       else if (data->state == BUTTON_STATE_WAIT_DRAG) {
+               /* note: the 'BUTTON_STATE_WAIT_DRAG' part of 'ui_do_but_EXIT' could be refactored into its own function */
+               data->applied = false;
+               *r_retval = ui_do_but_EXIT(C, but, data, event);
+               return true;
+       }
+       return false;
+ }
+ #endif  /* USE_DRAG_TOGGLE */
  static int ui_do_but_BUT(
          bContext *C, uiBut *but,
          uiHandleButtonData *data, const wmEvent *event)
  {
+ #ifdef USE_DRAG_TOGGLE
+       {
+               int retval;
+               if (ui_do_but_ANY_drag_toggle(C, but, data, event, &retval)) {
+                       return retval;
+               }
+       }
+ #endif
        if (data->state == BUTTON_STATE_HIGHLIGHT) {
                if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
                        button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
@@@ -3726,43 -3974,6 +3787,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)
@@@ -3836,25 -4047,14 +3897,14 @@@ static int ui_do_but_TOG
          uiHandleButtonData *data, const wmEvent *event)
  {
  #ifdef USE_DRAG_TOGGLE
-       if (data->state == BUTTON_STATE_HIGHLIGHT) {
-               if (event->type == LEFTMOUSE && event->val == KM_PRESS && ui_but_is_drag_toggle(but)) {
- #if 0         /* UNUSED */
-                       data->togdual = event->ctrl;
-                       data->togonly = !event->shift;
- #endif
-                       ui_apply_but(C, but->block, but, data, true);
-                       button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
-                       data->dragstartx = event->x;
-                       data->dragstarty = event->y;
-                       return WM_UI_HANDLER_BREAK;
+       {
+               int retval;
+               if (ui_do_but_ANY_drag_toggle(C, but, data, event, &retval)) {
+                       return retval;
                }
        }
-       else if (data->state == BUTTON_STATE_WAIT_DRAG) {
-               /* note: the 'BUTTON_STATE_WAIT_DRAG' part of 'ui_do_but_EXIT' could be refactored into its own function */
-               data->applied = false;
-               return ui_do_but_EXIT(C, but, data, event);
-       }
  #endif
        if (data->state == BUTTON_STATE_HIGHLIGHT) {
                if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
  #if 0         /* UNUSED */
@@@ -4202,54 -4402,6 +4252,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;
                /* 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);
@@@ -5138,8 -5292,7 +5188,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) {
@@@ -6056,7 -6209,6 +6106,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);
                                }
                        }
  
@@@ -6351,6 -6503,35 +6401,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,
@@@ -6662,8 -6843,6 +6712,8 @@@ static bool ui_but_menu(bContext *C, ui
        MenuType *mt = WM_menutype_find("WM_MT_button_context", true);
        bool is_array, is_array_component;
        uiStringInfo label = {BUT_GET_LABEL, NULL};
 +      wmOperatorType *ot;
 +      PointerRNA op_ptr;
  
  /*    if ((but->rnapoin.data && but->rnaprop) == 0 && but->optype == NULL)*/
  /*            return 0;*/
                /*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);
  
 -              /* Set the (button_pointer, button_prop) and pointer data for Python access to the hovered ui element. */
 -              uiLayoutSetContextFromBut(layout, but);
 +              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)
                                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 */
@@@ -7081,9 -7211,6 +7131,9 @@@ static int ui_do_button(bContext *C, ui
                case UI_BTYPE_HOTKEY_EVENT:
                        retval = ui_do_but_HOTKEYEVT(C, but, data, event);
                        break;
 +              case UI_BTYPE_TAB:
 +                      retval = ui_do_but_TAB(C, block, but, data, event);
 +                      break;
                case UI_BTYPE_BUT_TOGGLE:
                case UI_BTYPE_TOGGLE:
                case UI_BTYPE_ICON_TOGGLE:
                        retval = ui_do_but_TEX(C, block, but, data, event);
                        break;
                case UI_BTYPE_MENU:
 +              case UI_BTYPE_POPOVER:
                case UI_BTYPE_BLOCK:
                case UI_BTYPE_PULLDOWN:
                        retval = ui_do_but_BLOCK(C, but, data, event);
                case UI_BTYPE_HSVCIRCLE:
                        retval = ui_do_but_HSVCIRCLE(C, block, but, data, event);
                        break;
 -              case UI_BTYPE_LINK:
 -              case UI_BTYPE_INLINK:
 -                      retval = ui_do_but_LINK(C, but, data, event);
 -                      break;
                case UI_BTYPE_TRACK_PREVIEW:
                        retval = ui_do_but_TRACKPREVIEW(C, block, but, data, event);
                        break;
                        /* quiet warnings for unhandled types */
                case UI_BTYPE_SEPR:
                case UI_BTYPE_SEPR_LINE:
 +              case UI_BTYPE_SEPR_SPACER:
                case UI_BTYPE_EXTRA:
                        break;
        }
@@@ -7354,11 -7483,12 +7404,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;
  
@@@ -7444,7 -7574,7 +7494,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;
                }
        }
@@@ -7599,7 -7729,7 +7649,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);
                }
@@@ -7668,7 -7798,7 +7718,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;
  
@@@ -7878,9 -8008,6 +7928,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) {
@@@ -8166,7 -8288,6 +8216,7 @@@ 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);
                                ED_region_tag_redraw(ar);
  
                                if (but->active) {
@@@ -8354,29 -8475,8 +8404,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)) {
@@@ -8721,8 -8826,13 +8771,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;
@@@ -8981,9 -9091,6 +9031,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;
@@@ -9136,7 -9243,7 +9186,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
 +
        /* if we set a menu return value, ensure we continue passing this on to
         * lower menus and buttons, so always set continue then, and if we are
         * inside the region otherwise, ensure we swallow the event */
@@@ -10142,10 -10240,10 +10192,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 */
@@@ -10391,18 -10489,3 +10441,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;
 +}