Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / interface / interface_handlers.c
index 0654e4d5f1b417d8f5acd84c018ec5736e69b960..cb37c3010312d4098d73b02efefa31dd68721578 100644 (file)
@@ -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"
@@ -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);
@@ -134,7 +130,7 @@ static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEve
 #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
@@ -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 */
@@ -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) {
@@ -658,11 +665,8 @@ PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext,
 
 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);
        }
 }
 
@@ -743,8 +747,7 @@ static void ui_apply_but_undo(uiBut *but)
                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;
 
@@ -956,6 +959,20 @@ static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
        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) {
@@ -1258,6 +1275,9 @@ static bool ui_drag_toggle_but_is_supported(const uiBut *but)
        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;
        }
@@ -1268,6 +1288,9 @@ static bool ui_drag_toggle_but_is_pushed(uiBut *but)
        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;
        }
@@ -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];
@@ -1286,7 +1308,7 @@ typedef struct uiDragToggleHandle {
 } 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 */
@@ -1310,7 +1332,7 @@ 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_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) {
@@ -1382,7 +1404,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const
 
 
        /* 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);
@@ -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);
 
@@ -1828,223 +1849,6 @@ static bool ui_but_drag_init(
 
 /* ********************** 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);
@@ -2163,6 +1967,9 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
                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:
@@ -2198,10 +2005,6 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
                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;
@@ -3215,6 +3018,9 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
 
                        ui_searchbox_free(C, data->searchbox);
                        data->searchbox = NULL;
+                       if (but->free_search_arg) {
+                               MEM_SAFE_FREE(but->search_arg);
+                       }
                }
 
                but->editstr = NULL;
@@ -3686,6 +3492,7 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat
        uiBlockCreateFunc func = NULL;
        uiBlockHandleCreateFunc handlefunc = NULL;
        uiMenuCreateFunc menufunc = NULL;
+       uiMenuCreateFunc popoverfunc = NULL;
        void *arg = NULL;
 
        switch (but->type) {
@@ -3705,6 +3512,11 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat
                        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);
@@ -3729,6 +3541,11 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat
                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
        {
@@ -3975,6 +3792,43 @@ static bool ui_but_is_mouse_over_icon_extra(const ARegion *region, uiBut *but, c
        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)
@@ -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)
@@ -4416,6 +4318,7 @@ static int ui_do_but_NUM(
        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;
@@ -4429,10 +4332,14 @@ static int ui_do_but_NUM(
                        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) {
@@ -4525,16 +4432,13 @@ static int ui_do_but_NUM(
                /* 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;
@@ -4545,7 +4449,7 @@ static int ui_do_but_NUM(
 
                                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;
@@ -4561,7 +4465,7 @@ static int ui_do_but_NUM(
                        }
                }
                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);
@@ -4570,7 +4474,7 @@ static int ui_do_but_NUM(
 
                                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);
@@ -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) {
@@ -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;
@@ -6338,7 +6244,7 @@ static int ui_do_but_CURVE(
                                }
                                else {
                                        curvemapping_changed(cumap, true);  /* remove doubles */
-                                       BKE_paint_invalidate_cursor_overlay(scene, cumap);
+                                       BKE_paint_invalidate_cursor_overlay(scene, view_layer, cumap);
                                }
                        }
 
@@ -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,
@@ -6841,6 +6718,8 @@ static bool ui_but_menu(bContext *C, uiBut *but)
 {
        MenuType *mt = WM_menutype_find("WM_MT_button_context", true);
        bool is_array, is_array_component;
+       wmOperatorType *ot;
+       PointerRNA op_ptr;
 
        /* having this menu for some buttons makes no sense */
        if (but->type == UI_BTYPE_IMAGE) {
@@ -6874,7 +6753,8 @@ static bool ui_but_menu(bContext *C, uiBut *but)
                /*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)
@@ -6886,9 +6766,6 @@ static bool ui_but_menu(bContext *C, uiBut *but)
 
                /* 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"),
@@ -6962,6 +6839,12 @@ static bool ui_but_menu(bContext *C, uiBut *but)
                                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 */
@@ -6969,21 +6852,16 @@ static bool ui_but_menu(bContext *C, uiBut *but)
                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 */
@@ -7007,6 +6885,54 @@ static bool ui_but_menu(bContext *C, uiBut *but)
                        }
                }
 
+               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 */
@@ -7218,6 +7144,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
                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:
@@ -7275,6 +7204,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
                        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);
@@ -7303,10 +7233,6 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
                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;
@@ -7314,6 +7240,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
                        /* quiet warnings for unhandled types */
                case UI_BTYPE_SEPR:
                case UI_BTYPE_SEPR_LINE:
+               case UI_BTYPE_SEPR_SPACER:
                case UI_BTYPE_EXTRA:
                        break;
        }
@@ -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;
 
@@ -7581,7 +7507,7 @@ static bool ui_region_contains_point_px(ARegion *ar, int x, int y)
                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;
                }
        }
@@ -7736,7 +7662,7 @@ void UI_but_tooltip_refresh(bContext *C, uiBut *but)
 {
        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);
                }
@@ -7805,7 +7731,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
                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;
 
@@ -8015,6 +7941,9 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA
                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(
@@ -8108,8 +8037,13 @@ 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) {
@@ -8295,6 +8229,11 @@ void UI_context_update_anim_flag(const bContext *C)
                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) {
@@ -8482,8 +8421,29 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
                        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);
@@ -8522,7 +8482,6 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
                                                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 */
@@ -8533,10 +8492,11 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
                                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) {
@@ -8565,12 +8525,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
                                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)) {
@@ -8833,13 +8788,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *ar,
        }
 
        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;
@@ -9098,6 +9048,9 @@ static int ui_menu_scroll(ARegion *ar, uiBlock *block, int my, uiBut *to_bt)
                                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;
@@ -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;
@@ -9634,6 +9587,15 @@ static int ui_handle_menu_event(
                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;
@@ -10253,10 +10215,10 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE
 
                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 */
@@ -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;
+}