Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / interface / interface_handlers.c
index 1bb479935c8ce766dcb70b183c133425fe08a3c6..bb54f26131a4431d71ba4c9e371541fc1855600e 100644 (file)
 #include "MEM_guardedalloc.h"
 
 #include "DNA_brush_types.h"
-#include "DNA_sensor_types.h"
-#include "DNA_controller_types.h"
-#include "DNA_actuator_types.h"
 
-#include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_screen_types.h"
 
@@ -72,6 +68,7 @@
 #include "ED_undo.h"
 
 #include "UI_interface.h"
+#include "UI_view2d.h"
 
 #include "BLF_api.h"
 
 #define UI_MAX_PASSWORD_STR 128
 
 /* proto */
-static void ui_but_smart_controller_add(bContext *C, uiBut *from, uiBut *to);
-static void ui_but_link_add(bContext *C, uiBut *from, uiBut *to);
 static int ui_do_but_EXIT(bContext *C, uiBut *but, struct uiHandleButtonData *data, const wmEvent *event);
 static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b);
 static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str);
@@ -125,7 +120,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
@@ -276,6 +271,7 @@ typedef struct uiHandleButtonData {
        /* booleans (could be made into flags) */
        bool cancel, escapecancel;
        bool applied, applied_interactive;
+       bool changed_cursor;
        wmTimer *flashtimer;
 
        /* edited value */
@@ -501,6 +497,16 @@ bool ui_but_is_toggle(const uiBut *but)
        );
 }
 
+#ifdef USE_UI_POPOVER_ONCE
+bool ui_but_is_popover_once_compat(const uiBut *but)
+{
+       return (
+               (but->type == UI_BTYPE_BUT) ||
+               ui_but_is_toggle(but)
+       );
+}
+#endif
+
 static uiBut *ui_but_prev(uiBut *but)
 {
        while (but->prev) {
@@ -649,11 +655,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);
        }
 }
 
@@ -734,8 +737,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;
 
@@ -947,6 +949,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) {
@@ -1249,6 +1265,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;
        }
@@ -1259,6 +1278,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;
        }
@@ -1269,7 +1291,6 @@ typedef struct uiDragToggleHandle {
        bool is_init;
        bool is_set;
        float but_cent_start[2];
-       eButType but_type_start;
 
        bool xy_lock[2];
        int  xy_init[2];
@@ -1277,7 +1298,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 */
@@ -1301,7 +1322,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) {
@@ -1373,7 +1394,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);
@@ -1752,7 +1773,6 @@ static bool ui_but_drag_init(
                        drag_info->is_set = ui_drag_toggle_but_is_pushed(but);
                        drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect);
                        drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect);
-                       drag_info->but_type_start = but->type;
                        copy_v2_v2_int(drag_info->xy_init, &event->x);
                        copy_v2_v2_int(drag_info->xy_last, &event->x);
 
@@ -1819,223 +1839,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);
@@ -2154,6 +1957,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:
@@ -2189,10 +1995,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;
@@ -3206,6 +3008,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;
@@ -3677,6 +3482,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) {
@@ -3696,6 +3502,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);
@@ -3720,6 +3531,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
        {
@@ -3966,6 +3782,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)
@@ -4394,6 +4247,54 @@ static bool ui_numedit_but_NUM(
        return changed;
 }
 
+static void ui_numedit_set_active(uiBut *but)
+{
+       int oldflag = but->drawflag;
+       but->drawflag &= ~(UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT);
+
+       uiHandleButtonData *data = but->active;
+       if (!data) {
+               return;
+       }
+
+       /* Ignore once we start dragging. */
+       if (data->dragchange == false) {
+               const  float handle_width = min_ff(BLI_rctf_size_x(&but->rect) / 3, BLI_rctf_size_y(&but->rect) * 0.7f);
+               /* we can click on the side arrows to increment/decrement,
+                * or click inside to edit the value directly */
+               int mx = data->window->eventstate->x;
+               int my = data->window->eventstate->x;
+               ui_window_to_block(data->region, but->block, &mx, &my);
+
+               if (mx < (but->rect.xmin + handle_width)) {
+                       but->drawflag |= UI_BUT_ACTIVE_LEFT;
+               }
+               else if (mx > (but->rect.xmax - handle_width)) {
+                       but->drawflag |= UI_BUT_ACTIVE_RIGHT;
+               }
+       }
+
+       /* Don't change the cursor once pressed. */
+       if ((but->flag & UI_SELECT) == 0) {
+               if ((but->drawflag & (UI_BUT_ACTIVE_LEFT)) || (but->drawflag & (UI_BUT_ACTIVE_RIGHT))) {
+                       if (data->changed_cursor) {
+                               WM_cursor_modal_restore(data->window);
+                               data->changed_cursor = false;
+                       }
+               }
+               else {
+                       if (data->changed_cursor == false) {
+                               WM_cursor_modal_set(data->window, CURSOR_X_MOVE);
+                               data->changed_cursor = true;
+                       }
+               }
+       }
+
+       if (but->drawflag != oldflag) {
+               ED_region_tag_redraw(data->region);
+       }
+}
+
 static int ui_do_but_NUM(
         bContext *C, uiBlock *block, uiBut *but,
         uiHandleButtonData *data, const wmEvent *event)
@@ -4407,6 +4308,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;
@@ -4420,10 +4322,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) {
@@ -4516,16 +4422,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;
@@ -4536,7 +4439,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;
@@ -4552,7 +4455,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);
@@ -4561,7 +4464,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);
@@ -5284,7 +5187,8 @@ static int ui_do_but_COLOR(
                                if (!event->ctrl) {
                                        float color[3];
                                        Scene *scene = CTX_data_scene(C);
-                                       Paint *paint = BKE_paint_get_active(scene);
+                                       ViewLayer *view_layer = CTX_data_view_layer(C);
+                                       Paint *paint = BKE_paint_get_active(scene, view_layer);
                                        Brush *brush = BKE_paint_brush(paint);
 
                                        if (brush->flag & BRUSH_USE_GRADIENT) {
@@ -6201,6 +6105,7 @@ static int ui_do_but_CURVE(
        int mx, my, a;
        bool changed = false;
        Scene *scene = CTX_data_scene(C);
+       ViewLayer *view_layer = CTX_data_view_layer(C);
 
        mx = event->x;
        my = event->y;
@@ -6329,7 +6234,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);
                                }
                        }
 
@@ -6495,35 +6400,6 @@ static int ui_do_but_WAVEFORM(
        return WM_UI_HANDLER_CONTINUE;
 }
 
-static int ui_do_but_LINK(
-        bContext *C, uiBut *but,
-        uiHandleButtonData *data, const wmEvent *event)
-{
-       VECCOPY2D(but->linkto, event->mval);
-
-       if (data->state == BUTTON_STATE_HIGHLIGHT) {
-               if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
-                       button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
-                       return WM_UI_HANDLER_BREAK;
-               }
-               else if (event->type == LEFTMOUSE && but->block->handle) {
-                       button_activate_state(C, but, BUTTON_STATE_EXIT);
-                       return WM_UI_HANDLER_BREAK;
-               }
-       }
-       else if (data->state == BUTTON_STATE_WAIT_RELEASE) {
-
-               if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
-                       if (!(but->flag & UI_SELECT))
-                               data->cancel = true;
-                       button_activate_state(C, but, BUTTON_STATE_EXIT);
-                       return WM_UI_HANDLER_BREAK;
-               }
-       }
-
-       return WM_UI_HANDLER_CONTINUE;
-}
-
 static bool ui_numedit_but_TRACKPREVIEW(
         bContext *C, uiBut *but, uiHandleButtonData *data,
         int mx, int my,
@@ -6667,6 +6543,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:
@@ -6724,6 +6603,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);
@@ -6752,10 +6632,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;
@@ -6763,6 +6639,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;
        }
@@ -6939,12 +6816,11 @@ bool ui_but_is_active(ARegion *ar)
 /* is called by notifier */
 void UI_screen_free_active_but(const bContext *C, bScreen *screen)
 {
-       ScrArea *sa = screen->areabase.first;
+       wmWindow *win = CTX_wm_window(C);
 
-       for (; sa; sa = sa->next) {
-               ARegion *ar = sa->regionbase.first;
-               for (; ar; ar = ar->next) {
-                       uiBut *but = ui_but_find_active_in_region(ar);
+       ED_screen_areas_iter(win, screen, area) {
+               for (ARegion *region = area->regionbase.first; region; region = region->next) {
+                       uiBut *but = ui_but_find_active_in_region(region);
                        if (but) {
                                uiHandleButtonData *data = but->active;
 
@@ -7030,7 +6906,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;
                }
        }
@@ -7185,7 +7061,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);
                }
@@ -7254,7 +7130,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;
 
@@ -7464,6 +7340,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(
@@ -7557,8 +7436,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) {
@@ -7744,6 +7628,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) {
@@ -7931,8 +7820,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);
@@ -7971,7 +7881,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 */
@@ -7982,10 +7891,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) {
@@ -8014,12 +7924,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)) {
@@ -8282,13 +8187,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;
@@ -8547,6 +8447,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;
@@ -8699,7 +8602,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;
@@ -9083,6 +8986,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;
@@ -9702,10 +9614,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 */
@@ -9962,3 +9874,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;
+}