doxygen: add newline after \file
[blender.git] / source / blender / editors / interface / interface_widgets.c
index a148681..7e43ac8 100644 (file)
@@ -17,8 +17,8 @@
  * All rights reserved.
  */
 
-/** \file blender/editors/interface/interface_widgets.c
- *  \ingroup edinterface
+/** \file
+ * \ingroup edinterface
  */
 
 #include <limits.h>
 #include "BLI_utildefines.h"
 
 #include "BKE_context.h"
-#include "BKE_curve.h"
 
 #include "RNA_access.h"
 
-#include "BIF_gl.h"
-#include "BIF_glutil.h"
-
 #include "BLF_api.h"
 
 #include "UI_interface.h"
 
 #include "interface_intern.h"
 
-#include "GPU_basic_shader.h"
+#include "GPU_batch.h"
+#include "GPU_batch_presets.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_matrix.h"
+#include "GPU_state.h"
 
 #ifdef WITH_INPUT_IME
 #  include "WM_types.h"
@@ -66,13 +67,19 @@ enum {
        /* Show that holding the button opens a menu. */
        UI_STATE_HOLD_ACTION = UI_BUT_UPDATE_DELAY,
        UI_STATE_TEXT_INPUT  = UI_BUT_UNDO,
+       UI_STATE_ACTIVE_LEFT  = UI_BUT_VALUE_CLEAR,
+       UI_STATE_ACTIVE_RIGHT = UI_BUT_TEXTEDIT_UPDATE,
 
-       UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT),
+       UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION |
+                             UI_STATE_TEXT_INPUT |
+                             UI_STATE_ACTIVE_LEFT |
+                             UI_STATE_ACTIVE_RIGHT),
 };
 /* Prevent accidental use. */
 #define UI_BUT_UPDATE_DELAY ((void)0)
 #define UI_BUT_UNDO ((void)0)
 
+
 /* ************** widget base functions ************** */
 /**
  * - in: roundbox codes for corner types and radius
@@ -92,6 +99,8 @@ enum {
 
 typedef struct uiWidgetTrias {
        uint tot;
+       int type;
+       float size, center[2];
 
        float vec[16][2];
        const uint (*index)[3];
@@ -99,21 +108,24 @@ typedef struct uiWidgetTrias {
 } uiWidgetTrias;
 
 /* max as used by round_box__edges */
+/* Make sure to change widget_base_vert.glsl accordingly. */
 #define WIDGET_CURVE_RESOLU 9
 #define WIDGET_SIZE_MAX (WIDGET_CURVE_RESOLU * 4)
 
 typedef struct uiWidgetBase {
-
+       /* TODO remove these completely */
        int totvert, halfwayvert;
        float outer_v[WIDGET_SIZE_MAX][2];
        float inner_v[WIDGET_SIZE_MAX][2];
        float inner_uv[WIDGET_SIZE_MAX][2];
 
-       bool draw_inner, draw_outline, draw_emboss, draw_shadedir;
+       bool draw_inner, draw_outline, draw_emboss;
 
        uiWidgetTrias tria1;
        uiWidgetTrias tria2;
 
+       /* Widget shader parameters, must match the shader layout. */
+       uiWidgetBaseParameters uniform_params;
 } uiWidgetBase;
 
 /** uiWidgetType: for time being only for visual appearance,
@@ -122,16 +134,16 @@ typedef struct uiWidgetBase {
 typedef struct uiWidgetType {
 
        /* pointer to theme color definition */
-       uiWidgetColors *wcol_theme;
+       const uiWidgetColors *wcol_theme;
        uiWidgetStateColors *wcol_state;
 
        /* converted colors for state */
        uiWidgetColors wcol;
 
-       void (*state)(struct uiWidgetType *, int state);
+       void (*state)(struct uiWidgetType *, int state, int drawflag);
        void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign);
        void (*custom)(uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign);
-       void (*text)(uiFontStyle *, uiWidgetColors *, uiBut *, rcti *);
+       void (*text)(const uiFontStyle *, const uiWidgetColors *, uiBut *, rcti *);
 
 } uiWidgetType;
 
@@ -141,56 +153,58 @@ typedef struct uiWidgetType {
 static const float cornervec[WIDGET_CURVE_RESOLU][2] = {
        {0.0, 0.0}, {0.195, 0.02}, {0.383, 0.067},
        {0.55, 0.169}, {0.707, 0.293}, {0.831, 0.45},
-       {0.924, 0.617}, {0.98, 0.805}, {1.0, 1.0}
+       {0.924, 0.617}, {0.98, 0.805}, {1.0, 1.0},
 };
 
-#define WIDGET_AA_JITTER 8
-static const float jit[WIDGET_AA_JITTER][2] = {
+
+const float ui_pixel_jitter[UI_PIXEL_AA_JITTER][2] = {
        { 0.468813, -0.481430}, {-0.155755, -0.352820},
        { 0.219306, -0.238501}, {-0.393286, -0.110949},
        {-0.024699,  0.013908}, { 0.343805,  0.147431},
-       {-0.272855,  0.269918}, { 0.095909,  0.388710}
+       {-0.272855,  0.269918}, { 0.095909,  0.388710},
 };
+#define WIDGET_AA_JITTER UI_PIXEL_AA_JITTER
+#define jit ui_pixel_jitter
 
 /* -------------------------------------------------------------------- */
 /** \name Shape Preset Data
  * \{ */
 
 static const float g_shape_preset_number_arrow_vert[3][2] = {
-       {-0.352077, 0.532607}, {-0.352077, -0.549313}, {0.330000, -0.008353}
+       {-0.352077, 0.532607}, {-0.352077, -0.549313}, {0.330000, -0.008353},
 };
 static const uint g_shape_preset_number_arrow_face[1][3] = {
-       {0, 1, 2}
+       {0, 1, 2},
 };
 
 static const float g_shape_preset_scroll_circle_vert[16][2] = {
        {0.382684, 0.923879}, {0.000001, 1.000000}, {-0.382683, 0.923880}, {-0.707107, 0.707107},
        {-0.923879, 0.382684}, {-1.000000, 0.000000}, {-0.923880, -0.382684}, {-0.707107, -0.707107},
        {-0.382683, -0.923880}, {0.000000, -1.000000}, {0.382684, -0.923880}, {0.707107, -0.707107},
-       {0.923880, -0.382684}, {1.000000, -0.000000}, {0.923880, 0.382683}, {0.707107, 0.707107}
+       {0.923880, -0.382684}, {1.000000, -0.000000}, {0.923880, 0.382683}, {0.707107, 0.707107},
 };
 static const uint g_shape_preset_scroll_circle_face[14][3] = {
        {0, 1, 2}, {2, 0, 3}, {3, 0, 15}, {3, 15, 4}, {4, 15, 14}, {4, 14, 5}, {5, 14, 13}, {5, 13, 6},
-       {6, 13, 12}, {6, 12, 7}, {7, 12, 11}, {7, 11, 8}, {8, 11, 10}, {8, 10, 9}
+       {6, 13, 12}, {6, 12, 7}, {7, 12, 11}, {7, 11, 8}, {8, 11, 10}, {8, 10, 9},
 };
 
 static const float g_shape_preset_menu_arrow_vert[6][2] = {
        {-0.33, 0.16}, {0.33, 0.16}, {0, 0.82},
-       {0, -0.82}, {-0.33, -0.16}, {0.33, -0.16}
+       {0, -0.82}, {-0.33, -0.16}, {0.33, -0.16},
 };
 static const uint g_shape_preset_menu_arrow_face[2][3] = {{2, 0, 1}, {3, 5, 4}};
 
 static const float g_shape_preset_checkmark_vert[6][2] = {
        {-0.578579, 0.253369},  {-0.392773, 0.412794},  {-0.004241, -0.328551},
-       {-0.003001, 0.034320},  {1.055313, 0.864744},   {0.866408, 1.026895}
+       {-0.003001, 0.034320},  {1.055313, 0.864744},   {0.866408, 1.026895},
 };
 
 static const uint g_shape_preset_checkmark_face[4][3] = {
-       {3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3}
+       {3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3},
 };
 
-#define OY -0.2
-#define SC 0.35
+#define OY (-0.2 / 2)
+#define SC (0.35 * 2)
 static const float g_shape_preset_hold_action_vert[6][2] = {
        {-0.5 + SC, 1.0 + OY},  {0.5, 1.0 + OY},  {0.5, 0.0 + OY + SC},
 };
@@ -200,53 +214,365 @@ static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}}
 
 /** \} */
 
+/* **************** Batch creations ****************** */
+/**
+ * In order to speed up UI drawing we create some batches that are then
+ * modified by specialized shaders to draw certain elements really fast.
+ * TODO: find a better place. Maybe it's own file?
+ **/
+
+/* offset in triavec[] in shader per type */
+static const int tria_ofs[ROUNDBOX_TRIA_MAX] = {
+       [ROUNDBOX_TRIA_NONE]              = 0,
+       [ROUNDBOX_TRIA_ARROWS]            = 0,
+       [ROUNDBOX_TRIA_SCROLL]            = 12,
+       [ROUNDBOX_TRIA_MENU]              = 28,
+       [ROUNDBOX_TRIA_CHECK]             = 34,
+       [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 40,
+};
+static const int tria_vcount[ROUNDBOX_TRIA_MAX] = {
+       [ROUNDBOX_TRIA_NONE]              = 0,
+       [ROUNDBOX_TRIA_ARROWS]            = 6,
+       [ROUNDBOX_TRIA_SCROLL]            = 16,
+       [ROUNDBOX_TRIA_MENU]              = 6,
+       [ROUNDBOX_TRIA_CHECK]             = 6,
+       [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 3,
+};
+
+static struct {
+       GPUBatch *roundbox_widget[ROUNDBOX_TRIA_MAX];
+
+       GPUBatch *roundbox_simple;
+       GPUBatch *roundbox_simple_aa;
+       GPUBatch *roundbox_simple_outline;
+       GPUBatch *roundbox_shadow;
+
+       GPUVertFormat format;
+       uint vflag_id;
+} g_ui_batch_cache = {{0}};
+
+static GPUVertFormat *vflag_format(void)
+{
+       if (g_ui_batch_cache.format.attr_len == 0) {
+               GPUVertFormat *format = &g_ui_batch_cache.format;
+               g_ui_batch_cache.vflag_id = GPU_vertformat_attr_add(format, "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT);
+       }
+       return &g_ui_batch_cache.format;
+}
+
+#define INNER 0
+#define OUTLINE 1
+#define EMBOSS 2
+#define NO_AA WIDGET_AA_JITTER
+
+static void set_roundbox_vertex_data(
+        GPUVertBufRaw *vflag_step, uint32_t d)
+{
+       uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
+       *data = d;
+}
+
+static uint32_t set_roundbox_vertex(
+        GPUVertBufRaw *vflag_step,
+        int corner_id, int corner_v, int jit_v, bool inner, bool emboss, int color)
+{
+       uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
+       *data  = corner_id;
+       *data |= corner_v << 2;
+       *data |= jit_v << 6;
+       *data |= color << 12;
+       *data |= (inner) ? (1 << 10) : 0; /* is inner vert */
+       *data |= (emboss) ? (1 << 11) : 0; /* is emboss vert */
+       return *data;
+}
+
+static uint32_t set_tria_vertex(
+        GPUVertBufRaw *vflag_step,
+        int tria_type, int tria_v, int tria_id, int jit_v)
+{
+       uint32_t *data = GPU_vertbuf_raw_step(vflag_step);
+       if (ELEM(tria_type, ROUNDBOX_TRIA_ARROWS)) {
+               tria_v += tria_id * tria_vcount[ROUNDBOX_TRIA_ARROWS];
+       }
+       *data  = tria_ofs[tria_type] + tria_v;
+       *data |= jit_v << 6;
+       *data |= (tria_id == 0) ? (1 << 10) : 0; /* is first tria */
+       *data |= 1 << 14; /* is tria vert */
+       return *data;
+}
+
+static void roundbox_batch_add_tria(GPUVertBufRaw *vflag_step, int tria, uint32_t last_data)
+{
+       const int tria_num = ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU) ? 1 : 2;
+       /* for each tria */
+       for (int t = 0; t < tria_num; ++t) {
+               for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+                       /* restart */
+                       set_roundbox_vertex_data(vflag_step, last_data);
+                       set_tria_vertex(vflag_step, tria, 0, t, j);
+                       for (int v = 0; v < tria_vcount[tria]; v++) {
+                               last_data = set_tria_vertex(vflag_step, tria, v, t, j);
+                       }
+               }
+       }
+}
+
+GPUBatch *ui_batch_roundbox_widget_get(int tria)
+{
+       if (g_ui_batch_cache.roundbox_widget[tria] == NULL) {
+               uint32_t last_data;
+               GPUVertBufRaw vflag_step;
+               GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
+               int vcount = WIDGET_SIZE_MAX; /* inner */
+               vcount += 2; /* restart */
+               vcount += ((WIDGET_SIZE_MAX + 1) * 2) * WIDGET_AA_JITTER; /* outline (edges) */
+               vcount += 2; /* restart */
+               vcount += ((WIDGET_CURVE_RESOLU * 2) * 2) * WIDGET_AA_JITTER; /* emboss */
+               if (tria) {
+                       vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria1 */
+                       if (!ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU)) {
+                               vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria2 */
+                       }
+               }
+               GPU_vertbuf_data_alloc(vbo, vcount);
+               GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
+               /* Inner */
+               for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
+                       for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) {
+                               last_data = set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER);
+                               last_data = set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER);
+                       }
+               }
+               /* restart */
+               set_roundbox_vertex_data(&vflag_step, last_data);
+               set_roundbox_vertex(&vflag_step, 0, 0, 0, true, false, OUTLINE);
+               /* Outlines */
+               for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+                       for (int c = 0; c < 4; c++) {
+                               for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
+                                       set_roundbox_vertex(&vflag_step, c, a, j, true, false, OUTLINE);
+                                       set_roundbox_vertex(&vflag_step, c, a, j, false, false, OUTLINE);
+                               }
+                       }
+                       /* Close the loop. */
+                       set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, OUTLINE);
+                       last_data = set_roundbox_vertex(&vflag_step, 0, 0, j, false, false, OUTLINE);
+               }
+               /* restart */
+               set_roundbox_vertex_data(&vflag_step, last_data);
+               set_roundbox_vertex(&vflag_step, 0, 0, 0, false, false, EMBOSS);
+               /* Emboss */
+               /* go back and forth : avoid degenerate triangle (but beware of backface cull) */
+               bool rev = false;
+               for (int j = 0; j < WIDGET_AA_JITTER; j++, rev = !rev) {
+                       for (int c = (rev) ? 1 : 0; (rev) ? c >= 0 : c < 2; (rev) ? c-- : c++) {
+                               int sta = (rev) ? WIDGET_CURVE_RESOLU - 1 : 0;
+                               int end = WIDGET_CURVE_RESOLU;
+                               for (int a = sta; (rev) ? a >= 0 : a < end; (rev) ? a-- : a++) {
+                                       set_roundbox_vertex(&vflag_step, c, a, j, false, false, EMBOSS);
+                                       last_data = set_roundbox_vertex(&vflag_step, c, a, j, false, true, EMBOSS);
+                               }
+                       }
+               }
+               if (tria) {
+                       roundbox_batch_add_tria(&vflag_step, tria, last_data);
+               }
+               g_ui_batch_cache.roundbox_widget[tria] = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
+               gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget[tria]);
+       }
+       return g_ui_batch_cache.roundbox_widget[tria];
+}
+
+GPUBatch *ui_batch_roundbox_get(bool filled, bool antialiased)
+{
+       GPUBatch **batch = NULL;
+
+       if (filled) {
+               if (antialiased)
+                       batch = &g_ui_batch_cache.roundbox_simple_aa;
+               else
+                       batch = &g_ui_batch_cache.roundbox_simple;
+       }
+       else {
+               if (antialiased)
+                       BLI_assert(0); /* Use GL_LINE_SMOOTH instead!!: */
+               else
+                       batch = &g_ui_batch_cache.roundbox_simple_outline;
+       }
+
+       if (*batch == NULL) {
+               uint32_t last_data;
+               GPUVertBufRaw vflag_step;
+               GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
+               int vcount = WIDGET_SIZE_MAX;
+               vcount += (filled) ? 2 : 0;
+               vcount *= (antialiased) ? WIDGET_AA_JITTER : 1;
+               GPU_vertbuf_data_alloc(vbo, vcount);
+               GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
+
+               if (filled) {
+                       for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+                               if (!antialiased) {
+                                       j = NO_AA;
+                               }
+                               /* restart */
+                               set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, INNER);
+                               for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
+                                       for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) {
+                                               last_data = set_roundbox_vertex(&vflag_step, c1, a1, j, true, false, INNER);
+                                               last_data = set_roundbox_vertex(&vflag_step, c2, a2, j, true, false, INNER);
+                                       }
+                               }
+                               /* restart */
+                               set_roundbox_vertex_data(&vflag_step, last_data);
+                               if (!antialiased) {
+                                       break;
+                               }
+                       }
+                       *batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
+               }
+               else {
+                       for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+                               if (!antialiased) {
+                                       j = NO_AA;
+                               }
+                               for (int c = 0; c < 4; c++) {
+                                       for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
+                                               set_roundbox_vertex(&vflag_step, c, a, j, true, false, INNER);
+                                       }
+                               }
+                               if (!antialiased) {
+                                       break;
+                               }
+                       }
+                       *batch = GPU_batch_create_ex(GPU_PRIM_LINE_LOOP, vbo, NULL, GPU_BATCH_OWNS_VBO);
+               }
+
+               gpu_batch_presets_register(*batch);
+       }
+       return *batch;
+}
+
+GPUBatch *ui_batch_roundbox_shadow_get(void)
+{
+       if (g_ui_batch_cache.roundbox_shadow == NULL) {
+               uint32_t last_data;
+               GPUVertBufRaw vflag_step;
+               GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format());
+               int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX;
+               GPU_vertbuf_data_alloc(vbo, vcount);
+               GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step);
+
+               for (int c = 0; c < 4; c++) {
+                       for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
+                               set_roundbox_vertex(&vflag_step, c, a, NO_AA, true, false, INNER);
+                               set_roundbox_vertex(&vflag_step, c, a, NO_AA, false, false, INNER);
+                       }
+               }
+               /* close loop */
+               last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER);
+               last_data = set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, false, false, INNER);
+               /* restart */
+               set_roundbox_vertex_data(&vflag_step, last_data);
+               set_roundbox_vertex(&vflag_step, 0, 0, NO_AA, true, false, INNER);
+               /* filled */
+               for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) {
+                       for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU -1; a2 >= 0; a1++, a2--) {
+                               set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER);
+                               set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER);
+                       }
+               }
+               g_ui_batch_cache.roundbox_shadow = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
+               gpu_batch_presets_register(g_ui_batch_cache.roundbox_shadow);
+       }
+       return g_ui_batch_cache.roundbox_shadow;
+}
+
+#undef INNER
+#undef OUTLINE
+#undef EMBOSS
+#undef NO_AA
+
 /* ************************************************* */
 
-void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3)
+void UI_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3,
+                       const float color[4])
 {
        float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}};
-       float color[4];
-       int j;
+       float draw_color[4];
+
+       copy_v4_v4(draw_color, color);
+       /* Note: This won't give back the original color. */
+       draw_color[3] *= 1.0f / WIDGET_AA_JITTER;
+
+       GPU_blend(true);
 
-       glEnable(GL_BLEND);
-       glGetFloatv(GL_CURRENT_COLOR, color);
-       color[3] *= 0.125f;
-       glColor4fv(color);
+       uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 
-       glEnableClientState(GL_VERTEX_ARRAY);
-       glVertexPointer(2, GL_FLOAT, 0, tri_arr);
+       immUniformColor4fv(draw_color);
+       immBegin(GPU_PRIM_TRIS, 3 * WIDGET_AA_JITTER);
 
        /* for each AA step */
-       for (j = 0; j < WIDGET_AA_JITTER; j++) {
-               glTranslate2fv(jit[j]);
-               glDrawArrays(GL_TRIANGLES, 0, 3);
-               glTranslatef(-jit[j][0], -jit[j][1], 0.0f);
+       for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+               immVertex2f(pos, tri_arr[0][0] + jit[j][0], tri_arr[0][1] + jit[j][1]);
+               immVertex2f(pos, tri_arr[1][0] + jit[j][0], tri_arr[1][1] + jit[j][1]);
+               immVertex2f(pos, tri_arr[2][0] + jit[j][0], tri_arr[2][1] + jit[j][1]);
        }
 
-       glDisableClientState(GL_VERTEX_ARRAY);
-       glDisable(GL_BLEND);
+       immEnd();
+
+       immUnbindProgram();
+
+       GPU_blend(false);
 }
 
-void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha)
+/* triangle 'icon' inside rect */
+void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4])
 {
-       float color[4];
-       int j;
-
-       glEnable(GL_BLEND);
-       glGetFloatv(GL_CURRENT_COLOR, color);
-       if (use_alpha) {
-               color[3] = 0.5f;
+       if (dir == 'h') {
+               float half = 0.5f * BLI_rctf_size_y(rect);
+               UI_draw_anti_tria(rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color);
+       }
+       else {
+               float half = 0.5f * BLI_rctf_size_x(rect);
+               UI_draw_anti_tria(rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color);
        }
-       color[3] *= 0.125f;
-       glColor4fv(color);
+}
+
+
+void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4])
+{
+       float draw_color[4];
+
+       copy_v4_v4(draw_color, color);
+       draw_color[3] *= 2.0f / WIDGET_AA_JITTER;
+
+       GPU_blend(true);
+
+       uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+       immUniformColor4fv(draw_color);
+
+       /* for each AA step */
+       for (int j = 0; j < WIDGET_AA_JITTER; j++) {
+               immBegin(GPU_PRIM_TRI_FAN, length);
+               immVertex2f(pos, tri_array[0][0], tri_array[0][1]);
+               immVertex2f(pos, tri_array[1][0], tri_array[1][1]);
+
+               /* We jitter only the middle of the fan, the extremes are pinned. */
+               for (int i = 2; i < length - 1; i++) {
+                       immVertex2f(pos, tri_array[i][0] + jit[j][0], tri_array[i][1] + jit[j][1]);
+               }
 
-       for (j = 0; j < WIDGET_AA_JITTER; j++) {
-               glTranslate2fv(jit[j]);
-               UI_draw_roundbox_gl_mode(mode, minx, miny, maxx, maxy, rad);
-               glTranslatef(-jit[j][0], -jit[j][1], 0.0f);
+               immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]);
+               immEnd();
        }
 
-       glDisable(GL_BLEND);
+       immUnbindProgram();
+
+       GPU_blend(false);
 }
 
 static void widget_init(uiWidgetBase *wtb)
@@ -254,11 +580,16 @@ static void widget_init(uiWidgetBase *wtb)
        wtb->totvert = wtb->halfwayvert = 0;
        wtb->tria1.tot = 0;
        wtb->tria2.tot = 0;
+       wtb->tria1.type = ROUNDBOX_TRIA_NONE;
+       wtb->tria1.size = 0;
+       wtb->tria2.size = 0;
 
        wtb->draw_inner = true;
        wtb->draw_outline = true;
        wtb->draw_emboss = true;
-       wtb->draw_shadedir = true;
+
+       wtb->uniform_params.shade_dir = 1.0f;
+       wtb->uniform_params.alpha_discard = 1.0f;
 }
 
 /* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */
@@ -370,6 +701,17 @@ static void round_box__edges(uiWidgetBase *wt, int roundboxalign, const rcti *re
        if (2.0f * (radi + 1.0f) > minsize)
                radi = 0.5f * minsize - U.pixelsize;
 
+       wt->uniform_params.rad = rad;
+       wt->uniform_params.radi = radi;
+       wt->uniform_params.facxi = facxi;
+       wt->uniform_params.facyi = facyi;
+       wt->uniform_params.round_corners[0] = (roundboxalign & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f;
+       wt->uniform_params.round_corners[1] = (roundboxalign & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f;
+       wt->uniform_params.round_corners[2] = (roundboxalign & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f;
+       wt->uniform_params.round_corners[3] = (roundboxalign & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f;
+       BLI_rctf_rcti_copy(&wt->uniform_params.rect, rect);
+       BLI_rctf_init(&wt->uniform_params.recti, minxi, maxxi, minyi, maxyi);
+
        /* mult */
        for (a = 0; a < WIDGET_CURVE_RESOLU; a++) {
                veci[a][0] = radi * cornervec[a][0];
@@ -514,23 +856,30 @@ static void shape_preset_init_trias_ex(
        float centx, centy, sizex, sizey, minsize;
        int a, i1 = 0, i2 = 1;
 
-       minsize = min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect));
+       if (where == 'r' || where == 'l') {
+               minsize = BLI_rcti_size_y(rect);
+       }
+       else {
+               minsize = BLI_rcti_size_x(rect);
+       }
 
        /* center position and size */
        centx = (float)rect->xmin + 0.4f * minsize;
        centy = (float)rect->ymin + 0.5f * minsize;
-       sizex = sizey = -0.5f * triasize * minsize;
+       tria->size = sizex = sizey = -0.5f * triasize * minsize;
 
        if (where == 'r') {
                centx = (float)rect->xmax - 0.4f * minsize;
                sizex = -sizex;
        }
        else if (where == 't') {
+               centx = (float)rect->xmin + 0.5f * minsize;
                centy = (float)rect->ymax - 0.5f * minsize;
                sizey = -sizey;
                i2 = 0; i1 = 1;
        }
        else if (where == 'b') {
+               centx = (float)rect->xmin + 0.5f * minsize;
                sizex = -sizex;
                i2 = 0; i1 = 1;
        }
@@ -540,12 +889,16 @@ static void shape_preset_init_trias_ex(
                tria->vec[a][1] = sizey * verts[a][i2] + centy;
        }
 
+       tria->center[0] = centx;
+       tria->center[1] = centy;
+
        tria->tot = tris_tot;
        tria->index = tris;
 }
 
 static void shape_preset_init_number_arrows(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
 {
+       tria->type = ROUNDBOX_TRIA_ARROWS;
        shape_preset_init_trias_ex(
                tria, rect, triasize, where,
                g_shape_preset_number_arrow_vert, ARRAY_SIZE(g_shape_preset_number_arrow_vert),
@@ -554,6 +907,12 @@ static void shape_preset_init_number_arrows(uiWidgetTrias *tria, const rcti *rec
 
 static void shape_preset_init_hold_action(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
 {
+       tria->type = ROUNDBOX_TRIA_HOLD_ACTION_ARROW;
+       /* With the current changes to use batches for widget drawing, the code
+        * below is doing almost nothing effectively. 'where' doesn't work either,
+        * shader is currently hardcoded to work for the button triangle pointing
+        * at the lower right. The same limitation applies to other trias as well.
+        * XXX Should be addressed. */
        shape_preset_init_trias_ex(
                tria, rect, triasize, where,
                g_shape_preset_hold_action_vert, ARRAY_SIZE(g_shape_preset_hold_action_vert),
@@ -562,31 +921,46 @@ static void shape_preset_init_hold_action(uiWidgetTrias *tria, const rcti *rect,
 
 static void shape_preset_init_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where)
 {
+       tria->type = ROUNDBOX_TRIA_SCROLL;
        shape_preset_init_trias_ex(
                tria, rect, triasize, where,
                g_shape_preset_scroll_circle_vert, ARRAY_SIZE(g_shape_preset_scroll_circle_vert),
                g_shape_preset_scroll_circle_face, ARRAY_SIZE(g_shape_preset_scroll_circle_face));
 }
 
-static void shape_preset_draw_trias(uiWidgetTrias *tria)
+static void widget_draw_vertex_buffer(uint pos, uint col, int mode,
+                                      const float quads_pos[WIDGET_SIZE_MAX][2],
+                                      const uchar quads_col[WIDGET_SIZE_MAX][4],
+                                      uint totvert)
 {
-       glEnableClientState(GL_VERTEX_ARRAY);
-       glVertexPointer(2, GL_FLOAT, 0, tria->vec);
-       glDrawElements(GL_TRIANGLES, tria->tot * 3, GL_UNSIGNED_INT, tria->index);
-       glDisableClientState(GL_VERTEX_ARRAY);
+       immBegin(mode, totvert);
+       for (int i = 0; i < totvert; ++i) {
+               if (quads_col)
+                       immAttr4ubv(col, quads_col[i]);
+               immVertex2fv(pos, quads_pos[i]);
+       }
+       immEnd();
 }
 
 static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect)
 {
+       float width = BLI_rcti_size_x(rect);
+       float height = BLI_rcti_size_y(rect);
        float centx, centy, size;
-       int a;
 
-       /* center position and size */
-       centx = rect->xmax - 0.32f * BLI_rcti_size_y(rect);
-       centy = rect->ymin + 0.50f * BLI_rcti_size_y(rect);
-       size = 0.4f * BLI_rcti_size_y(rect);
+       tria->type = ROUNDBOX_TRIA_MENU;
+
+       /* Center position and size. */
+       tria->center[0] = centx = rect->xmin + 0.52f * BLI_rcti_size_y(rect);
+       tria->center[1] = centy = rect->ymin + 0.52f * BLI_rcti_size_y(rect);
+       tria->size = size = 0.4f * height;
+
+       if (width > height * 1.1f) {
+               /* For wider buttons align tighter to the right. */
+               tria->center[0] = centx = rect->xmax - 0.32f * height;
+       }
 
-       for (a = 0; a < 6; a++) {
+       for (int a = 0; a < 6; a++) {
                tria->vec[a][0] = size * g_shape_preset_menu_arrow_vert[a][0] + centx;
                tria->vec[a][1] = size * g_shape_preset_menu_arrow_vert[a][1] + centy;
        }
@@ -598,14 +972,15 @@ static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *r
 static void shape_preset_trias_from_rect_checkmark(uiWidgetTrias *tria, const rcti *rect)
 {
        float centx, centy, size;
-       int a;
 
-       /* center position and size */
-       centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect);
-       centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect);
-       size = 0.5f * BLI_rcti_size_y(rect);
+       tria->type = ROUNDBOX_TRIA_CHECK;
+
+       /* Center position and size. */
+       tria->center[0] = centx = rect->xmin + 0.5f * BLI_rcti_size_y(rect);
+       tria->center[1] = centy = rect->ymin + 0.5f * BLI_rcti_size_y(rect);
+       tria->size = size = 0.5f * BLI_rcti_size_y(rect);
 
-       for (a = 0; a < 6; a++) {
+       for (int a = 0; a < 6; a++) {
                tria->vec[a][0] = size * g_shape_preset_checkmark_vert[a][0] + centx;
                tria->vec[a][1] = size * g_shape_preset_checkmark_vert[a][1] + centy;
        }
@@ -653,192 +1028,220 @@ static void widget_verts_to_triangle_strip(uiWidgetBase *wtb, const int totvert,
        copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[0]);
 }
 
-static void widget_verts_to_triangle_strip_open(uiWidgetBase *wtb, const int totvert, float triangle_strip[WIDGET_SIZE_MAX * 2][2])
-{
-       int a;
-       for (a = 0; a < totvert; a++) {
-               triangle_strip[a * 2][0] = wtb->outer_v[a][0];
-               triangle_strip[a * 2][1] = wtb->outer_v[a][1];
-               triangle_strip[a * 2 + 1][0] = wtb->outer_v[a][0];
-               triangle_strip[a * 2 + 1][1] = wtb->outer_v[a][1] - 1.0f;
-       }
-}
-
-static void widgetbase_outline(uiWidgetBase *wtb)
+static void widgetbase_outline(uiWidgetBase *wtb, uint pos)
 {
        float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
        widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
 
-       glEnableClientState(GL_VERTEX_ARRAY);
-       glVertexPointer(2, GL_FLOAT, 0, triangle_strip);
-       glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2);
-       glDisableClientState(GL_VERTEX_ARRAY);
+       widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
 }
 
-static void widgetbase_draw_ex(
-        uiWidgetBase *wtb, uiWidgetColors *wcol,
-        const bool show_alpha_checkers)
+static void widgetbase_set_uniform_alpha_discard(
+        uiWidgetBase *wtb,
+        const bool alpha_check,
+        const float discard_factor)
 {
-       int j, a;
+       if (alpha_check) {
+               wtb->uniform_params.alpha_discard = -discard_factor;
+       }
+       else {
+               wtb->uniform_params.alpha_discard = discard_factor;
+       }
+}
 
-       glEnable(GL_BLEND);
+static void widgetbase_set_uniform_alpha_check(
+        uiWidgetBase *wtb,
+        const bool alpha_check)
+{
+       const float discard_factor = fabs(wtb->uniform_params.alpha_discard);
+       widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor);
+}
 
-       /* backdrop non AA */
-       if (wtb->draw_inner) {
-               BLI_assert(wtb->totvert != 0);
-               if (wcol->shaded == 0) {
-                       if (show_alpha_checkers) {
-                               float inner_v_half[WIDGET_SIZE_MAX][2];
-                               float x_mid = 0.0f; /* used for dumb clamping of values */
+static void widgetbase_set_uniform_discard_factor(
+        uiWidgetBase *wtb,
+        const float discard_factor)
+{
+       bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f;
+       widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor);
+}
 
-                               /* dark checkers */
-                               glColor4ub(UI_ALPHA_CHECKER_DARK, UI_ALPHA_CHECKER_DARK, UI_ALPHA_CHECKER_DARK, 255);
-                               glEnableClientState(GL_VERTEX_ARRAY);
-                               glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v);
-                               glDrawArrays(GL_POLYGON, 0, wtb->totvert);
+static void widgetbase_set_uniform_colors_ubv(
+        uiWidgetBase *wtb,
+        const uchar *col1, const uchar *col2,
+        const uchar *outline,
+        const uchar *emboss,
+        const uchar *tria,
+        const bool alpha_check)
+{
+       widgetbase_set_uniform_alpha_check(wtb, alpha_check);
+       rgba_float_args_set_ch(wtb->uniform_params.color_inner1, col1[0], col1[1], col1[2], col1[3]);
+       rgba_float_args_set_ch(wtb->uniform_params.color_inner2, col2[0], col2[1], col2[2], col2[3]);
+       rgba_float_args_set_ch(wtb->uniform_params.color_outline, outline[0], outline[1], outline[2], outline[3]);
+       rgba_float_args_set_ch(wtb->uniform_params.color_emboss, emboss[0], emboss[1], emboss[2], emboss[3]);
+       rgba_float_args_set_ch(wtb->uniform_params.color_tria, tria[0], tria[1], tria[2], tria[3]);
+}
 
-                               /* light checkers */
-                               GPU_basic_shader_bind(GPU_SHADER_STIPPLE | GPU_SHADER_USE_COLOR);
-                               glColor4ub(UI_ALPHA_CHECKER_LIGHT, UI_ALPHA_CHECKER_LIGHT, UI_ALPHA_CHECKER_LIGHT, 255);
-                               GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_CHECKER_8PX);
+/* keep in sync with shader */
+#define MAX_WIDGET_BASE_BATCH 6
+#define MAX_WIDGET_PARAMETERS 11
 
-                               glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v);
-                               glDrawArrays(GL_POLYGON, 0, wtb->totvert);
+static struct {
+       GPUBatch *batch; /* Batch type */
+       uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH];
+       int count;
+       bool enabled;
+} g_widget_base_batch = {0};
 
-                               GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
+void UI_widgetbase_draw_cache_flush(void)
+{
+       float checker_params[3] = {UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
 
-                               /* alpha fill */
-                               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       if (g_widget_base_batch.count == 0)
+               return;
 
-                               glColor4ubv((uchar *)wcol->inner);
+       GPUBatch *batch = g_widget_base_batch.batch;
+       if (g_widget_base_batch.count == 1) {
+               /* draw single */
+               GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+               GPU_batch_uniform_4fv_array(batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)g_widget_base_batch.params);
+               GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
+               GPU_batch_draw(batch);
+       }
+       else {
+               GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE_INST);
+               GPU_batch_uniform_4fv_array(batch, "parameters", MAX_WIDGET_PARAMETERS * MAX_WIDGET_BASE_BATCH,
+                                           (float *)g_widget_base_batch.params);
+               GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
+               GPU_matrix_bind(batch->interface);
+               GPU_batch_draw_range_ex(batch, 0, g_widget_base_batch.count, true);
+               GPU_batch_program_use_end(batch);
+       }
+       g_widget_base_batch.count = 0;
+}
 
-                               for (a = 0; a < wtb->totvert; a++) {
-                                       x_mid += wtb->inner_v[a][0];
-                               }
-                               x_mid /= wtb->totvert;
+void UI_widgetbase_draw_cache_begin(void)
+{
+       BLI_assert(g_widget_base_batch.enabled == false);
+       g_widget_base_batch.enabled = true;
+}
 
-                               glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v);
-                               glDrawArrays(GL_POLYGON, 0, wtb->totvert);
+void UI_widgetbase_draw_cache_end(void)
+{
+       BLI_assert(g_widget_base_batch.enabled == true);
+       g_widget_base_batch.enabled = false;
 
-                               /* 1/2 solid color */
-                               glColor4ub(wcol->inner[0], wcol->inner[1], wcol->inner[2], 255);
+       GPU_blend(true);
 
-                               for (a = 0; a < wtb->totvert; a++) {
-                                       inner_v_half[a][0] = MIN2(wtb->inner_v[a][0], x_mid);
-                                       inner_v_half[a][1] = wtb->inner_v[a][1];
-                               }
+       UI_widgetbase_draw_cache_flush();
 
-                               glVertexPointer(2, GL_FLOAT, 0, inner_v_half);
-                               glDrawArrays(GL_POLYGON, 0, wtb->totvert);
-                               glDisableClientState(GL_VERTEX_ARRAY);
-                       }
-                       else {
-                               /* simple fill */
-                               glColor4ubv((uchar *)wcol->inner);
+       GPU_blend(false);
+}
 
-                               glEnableClientState(GL_VERTEX_ARRAY);
-                               glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v);
-                               glDrawArrays(GL_POLYGON, 0, wtb->totvert);
-                               glDisableClientState(GL_VERTEX_ARRAY);
-                       }
+static void draw_widgetbase_batch(GPUBatch *batch, uiWidgetBase *wtb)
+{
+       wtb->uniform_params.tria1_size = wtb->tria1.size;
+       wtb->uniform_params.tria2_size = wtb->tria2.size;
+       copy_v2_v2(wtb->uniform_params.tria1_center, wtb->tria1.center);
+       copy_v2_v2(wtb->uniform_params.tria2_center, wtb->tria2.center);
+
+       if (g_widget_base_batch.enabled) {
+               if (g_widget_base_batch.batch == NULL) {
+                       g_widget_base_batch.batch = ui_batch_roundbox_widget_get(ROUNDBOX_TRIA_ARROWS);
                }
-               else {
-                       char col1[4], col2[4];
-                       uchar col_array[WIDGET_SIZE_MAX * 4];
-                       uchar *col_pt = col_array;
-
-                       shadecolors4(col1, col2, wcol->inner, wcol->shadetop, wcol->shadedown);
-
-                       for (a = 0; a < wtb->totvert; a++, col_pt += 4) {
-                               round_box_shade_col4_r(col_pt, col1, col2, wtb->inner_uv[a][wtb->draw_shadedir ? 1 : 0]);
-                       }
 
-                       glEnableClientState(GL_VERTEX_ARRAY);
-                       glEnableClientState(GL_COLOR_ARRAY);
-                       glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v);
-                       glColorPointer(4, GL_UNSIGNED_BYTE, 0, col_array);
-                       glDrawArrays(GL_POLYGON, 0, wtb->totvert);
-                       glDisableClientState(GL_VERTEX_ARRAY);
-                       glDisableClientState(GL_COLOR_ARRAY);
+               /* draw multi */
+               if (batch != g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE] &&
+                   batch != g_widget_base_batch.batch)
+               {
+                       /* issue previous calls before changing batch type. */
+                       UI_widgetbase_draw_cache_flush();
+                       g_widget_base_batch.batch = batch;
                }
-       }
-
-       /* for each AA step */
-       if (wtb->draw_outline) {
-               BLI_assert(wtb->totvert != 0);
-               float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */
-               float triangle_strip_emboss[WIDGET_SIZE_MAX * 2][2]; /* only for emboss */
 
-               const uchar tcol[4] = {
-                       wcol->outline[0],
-                       wcol->outline[1],
-                       wcol->outline[2],
-                       wcol->outline[3] / WIDGET_AA_JITTER,
-               };
-               uchar emboss[4];
+               /* No need to change batch if tria is not visible. Just scale it to 0. */
+               if (batch == g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE]) {
+                       wtb->uniform_params.tria1_size = wtb->uniform_params.tria2_size = 0;
+               }
 
-               widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
+               g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params;
+               g_widget_base_batch.count++;
 
-               if (wtb->draw_emboss) {
-                       widget_verts_to_triangle_strip_open(wtb, wtb->halfwayvert, triangle_strip_emboss);
-                       UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss);
+               if (g_widget_base_batch.count == MAX_WIDGET_BASE_BATCH) {
+                       UI_widgetbase_draw_cache_flush();
                }
+       }
+       else {
+               float checker_params[3] = {UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f};
+               /* draw single */
+               GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
+               GPU_batch_uniform_4fv_array(batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)&wtb->uniform_params);
+               GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
+               GPU_batch_draw(batch);
+       }
+}
 
-               glEnableClientState(GL_VERTEX_ARRAY);
-
-               for (j = 0; j < WIDGET_AA_JITTER; j++) {
-                       glTranslate2fv(jit[j]);
+static void widgetbase_draw_ex(
+        uiWidgetBase *wtb, const uiWidgetColors *wcol,
+        bool show_alpha_checkers)
+{
+       uchar inner_col1[4] = {0};
+       uchar inner_col2[4] = {0};
+       uchar emboss_col[4] = {0};
+       uchar outline_col[4] = {0};
+       uchar tria_col[4] = {0};
+       /* For color widget. */
+       if (wcol->shaded != 0) {
+               show_alpha_checkers = false;
+       }
 
-                       /* outline */
-                       glColor4ubv(tcol);
+       GPU_blend(true);
 
-                       glVertexPointer(2, GL_FLOAT, 0, triangle_strip);
-                       glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2);
+       /* backdrop non AA */
+       if (wtb->draw_inner) {
+               if (wcol->shaded == 0) {
+                       /* simple fill */
+                       inner_col1[0] = inner_col2[0] = (uchar)wcol->inner[0];
+                       inner_col1[1] = inner_col2[1] = (uchar)wcol->inner[1];
+                       inner_col1[2] = inner_col2[2] = (uchar)wcol->inner[2];
+                       inner_col1[3] = inner_col2[3] = (uchar)wcol->inner[3];
+               }
+               else {
+                       /* gradient fill */
+                       shadecolors4((char *)inner_col1, (char *)inner_col2, wcol->inner, wcol->shadetop, wcol->shadedown);
+               }
+       }
 
-                       /* emboss bottom shadow */
-                       if (wtb->draw_emboss) {
-                               if (emboss[3]) {
-                                       glColor4ubv(emboss);
-                                       glVertexPointer(2, GL_FLOAT, 0, triangle_strip_emboss);
-                                       glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->halfwayvert * 2);
-                               }
-                       }
+       if (wtb->draw_outline) {
+               outline_col[0] = wcol->outline[0];
+               outline_col[1] = wcol->outline[1];
+               outline_col[2] = wcol->outline[2];
+               outline_col[3] = wcol->outline[3] / WIDGET_AA_JITTER;
 
-                       glTranslatef(-jit[j][0], -jit[j][1], 0.0f);
+               /* emboss bottom shadow */
+               if (wtb->draw_emboss) {
+                       UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss_col);
                }
+       }
 
-               glDisableClientState(GL_VERTEX_ARRAY);
+       if (wtb->tria1.type != ROUNDBOX_TRIA_NONE) {
+               tria_col[0] = wcol->item[0];
+               tria_col[1] = wcol->item[1];
+               tria_col[2] = wcol->item[2];
+               tria_col[3] = (uchar)((float)wcol->item[3] / WIDGET_AA_JITTER);
        }
 
-       /* decoration */
-       if (wtb->tria1.tot || wtb->tria2.tot) {
-               const uchar tcol[4] = {
-                       wcol->item[0],
-                       wcol->item[1],
-                       wcol->item[2],
-                       (uchar)((float)wcol->item[3] / WIDGET_AA_JITTER),
-               };
-               glColor4ubv(tcol);
-
-               /* for each AA step */
-               for (j = 0; j < WIDGET_AA_JITTER; j++) {
-                       glTranslate2fv(jit[j]);
-
-                       if (wtb->tria1.tot) {
-                               shape_preset_draw_trias(&wtb->tria1);
-                       }
-                       if (wtb->tria2.tot) {
-                               shape_preset_draw_trias(&wtb->tria2);
-                       }
+       /* Draw everything in one drawcall */
+       if (inner_col1[3] || inner_col2[3] || outline_col[3] || emboss_col[3] || tria_col[3] || show_alpha_checkers) {
+               widgetbase_set_uniform_colors_ubv(wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, show_alpha_checkers);
 
-                       glTranslatef(-jit[j][0], -jit[j][1], 0.0f);
-               }
+               GPUBatch *roundbox_batch = ui_batch_roundbox_widget_get(wtb->tria1.type);
+               draw_widgetbase_batch(roundbox_batch, wtb);
        }
 
-       glDisable(GL_BLEND);
+       GPU_blend(false);
 }
 
-static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol)
+static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol)
 {
        widgetbase_draw_ex(wtb, wcol, false);
 }
@@ -877,17 +1280,17 @@ static int ui_but_draw_menu_icon(const uiBut *but)
 
 /* icons have been standardized... and this call draws in untransformed coordinates */
 
-static void widget_draw_icon_ex(
-        const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const bool show_menu_icon,
-        const int icon_size)
+static void widget_draw_icon(
+        const uiBut *but, BIFIconID icon, float alpha,
+        const rcti *rect, const char mono_color[4])
 {
        float xs = 0.0f, ys = 0.0f;
        float aspect, height;
 
        if (but->flag & UI_BUT_ICON_PREVIEW) {
-               glEnable(GL_BLEND);
+               GPU_blend(true);
                widget_draw_preview(icon, alpha, rect);
-               glDisable(GL_BLEND);
+               GPU_blend(false);
                return;
        }
 
@@ -895,13 +1298,13 @@ static void widget_draw_icon_ex(
        if (icon == ICON_BLANK1 && (but->flag & UI_BUT_ICON_SUBMENU) == 0) return;
 
        aspect = but->block->aspect / UI_DPI_FAC;
-       height = icon_size / aspect;
+       height = ICON_DEFAULT_HEIGHT / aspect;
 
        /* calculate blend color */
        if (ELEM(but->type, UI_BTYPE_TOGGLE, UI_BTYPE_ROW, UI_BTYPE_TOGGLE_N, UI_BTYPE_LISTROW)) {
                if (but->flag & UI_SELECT) {}
                else if (but->flag & UI_ACTIVE) {}
-               else alpha = 0.5f;
+               else alpha = 0.75f;
        }
        else if ((but->type == UI_BTYPE_LABEL)) {
                /* extra feature allows more alpha blending */
@@ -915,14 +1318,14 @@ static void widget_draw_icon_ex(
                }
        }
 
-       glEnable(GL_BLEND);
+       GPU_blend(true);
 
        if (icon && icon != ICON_BLANK1) {
                float ofs = 1.0f / aspect;
 
                if (but->drawflag & UI_BUT_ICON_LEFT) {
                        /* special case - icon_only pie buttons */
-                       if (ui_block_is_pie_menu(but->block) && but->type != UI_BTYPE_MENU && but->str && but->str[0] == '\0')
+                       if (ui_block_is_pie_menu(but->block) && !ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) && but->str && but->str[0] == '\0')
                                xs = rect->xmin + 2.0f * ofs;
                        else if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL)
                                xs = rect->xmin + 2.0f * ofs;
@@ -943,26 +1346,40 @@ static void widget_draw_icon_ex(
                /* to indicate draggable */
                if (but->dragpoin && (but->flag & UI_ACTIVE)) {
                        float rgb[3] = {1.25f, 1.25f, 1.25f};
-                       UI_icon_draw_aspect_color(xs, ys, icon, aspect, rgb);
+                       UI_icon_draw_aspect_color(xs, ys, icon, aspect, rgb, mono_color);
+               }
+               else if ((but->flag & (UI_ACTIVE | UI_SELECT | UI_SELECT_DRAW)) || !UI_but_is_tool(but)) {
+                       UI_icon_draw_aspect(xs, ys, icon, aspect, alpha, mono_color);
+               }
+               else {
+                       const bTheme *btheme = UI_GetTheme();
+                       UI_icon_draw_desaturate(xs, ys, icon, aspect, alpha, 1.0 - btheme->tui.icon_saturation, mono_color);
                }
-               else
-                       UI_icon_draw_aspect(xs, ys, icon, aspect, alpha);
-       }
-
-       if (show_menu_icon) {
-               xs = rect->xmax - UI_DPI_ICON_SIZE - aspect;
-               ys = (rect->ymin + rect->ymax - height) / 2.0f;
-
-               UI_icon_draw_aspect(xs, ys, ICON_RIGHTARROW_THIN, aspect, alpha);
        }
 
-       glDisable(GL_BLEND);
+       GPU_blend(false);
 }
 
-static void widget_draw_icon(
-        const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const bool show_menu_icon)
+static void widget_draw_submenu_tria(const uiBut *but, const rcti *rect, const uiWidgetColors *wcol)
 {
-       widget_draw_icon_ex(but, icon, alpha, rect, show_menu_icon, ICON_DEFAULT_HEIGHT);
+       const float aspect = but->block->aspect / UI_DPI_FAC;
+       const int tria_height = (int)(ICON_DEFAULT_HEIGHT / aspect);
+       const int tria_width = (int)(ICON_DEFAULT_WIDTH / aspect) - 2 * U.pixelsize;
+       const int xs = rect->xmax - tria_width;
+       const int ys = (rect->ymin + rect->ymax - tria_height) / 2.0f;
+       float col[4];
+       rctf tria_rect;
+
+       rgba_uchar_to_float(col, (const uchar *)wcol->text);
+       col[3] *= 0.8f;
+
+       BLI_rctf_init(&tria_rect, xs, xs + tria_width, ys, ys + tria_height);
+       BLI_rctf_scale(&tria_rect, 0.4f);
+
+       GPU_blend(true);
+       UI_widgetbase_draw_cache_flush();
+       GPU_blend(false);
+       ui_draw_anti_tria_rect(&tria_rect, 'h', col);
 }
 
 static void ui_text_clip_give_prev_off(uiBut *but, const char *str)
@@ -987,7 +1404,7 @@ static void ui_text_clip_give_next_off(uiBut *but, const char *str)
  * Return the length of modified (right-clipped + ellipsis) string.
  */
 static void ui_text_clip_right_ex(
-        uiFontStyle *fstyle, char *str, const size_t max_len, const float okwidth,
+        const uiFontStyle *fstyle, char *str, const size_t max_len, const float okwidth,
         const char *sep, const int sep_len, const float sep_strwidth, size_t *r_final_len)
 {
        float tmp;
@@ -1021,7 +1438,7 @@ static void ui_text_clip_right_ex(
  * for strings with shortcuts, like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O').
  */
 float UI_text_clip_middle_ex(
-        uiFontStyle *fstyle, char *str, float okwidth, const float minwidth,
+        const uiFontStyle *fstyle, char *str, float okwidth, const float minwidth,
         const size_t max_len, const char rpart_sep)
 {
        float strwidth;
@@ -1043,7 +1460,8 @@ float UI_text_clip_middle_ex(
        strwidth = BLF_width(fstyle->uifont_id, str, max_len);
 
        if ((okwidth > 0.0f) && (strwidth > okwidth)) {
-               /* utf8 ellipsis '..', some compilers complain */
+               /* utf8 two-dots leader '..' (shorter than ellipsis '...'),
+                * some compilers complain with real litteral string. */
                const char sep[] = {0xe2, 0x80, 0xA5, 0x0};
                const int sep_len = sizeof(sep) - 1;
                const float sep_strwidth = BLF_width(fstyle->uifont_id, sep, sep_len + 1);
@@ -1083,7 +1501,7 @@ float UI_text_clip_middle_ex(
                        rpart = rpart_buf;
                }
 
-               l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, &rpart_width);
+               l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, NULL);
                if (l_end < 10 || min_ff(parts_strwidth, strwidth - okwidth) < minwidth) {
                        /* If we really have no place, or we would clip a very small piece of string in the middle,
                         * only show start of string.
@@ -1093,7 +1511,7 @@ float UI_text_clip_middle_ex(
                else {
                        size_t r_offset, r_len;
 
-                       r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, &rpart_width);
+                       r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, NULL);
                        r_len = strlen(str + r_offset) + 1;  /* +1 for the trailing '\0'. */
 
                        if (l_end + sep_len + r_len + rpart_len > max_len) {
@@ -1107,13 +1525,25 @@ float UI_text_clip_middle_ex(
                        else {
                                memmove(str + l_end + sep_len, str + r_offset, r_len);
                                memcpy(str + l_end, sep, sep_len);
-                               final_lpart_len = (size_t)(l_end + sep_len + r_len - 1);  /* -1 to remove trailing '\0'! */
+                               /* -1 to remove trailing '\0'! */
+                               final_lpart_len = (size_t)(l_end + sep_len + r_len - 1);
+
+                               while (BLF_width(fstyle->uifont_id, str, max_len) > okwidth) {
+                                       /* This will happen because a lot of string width processing is done in integer pixels,
+                                        * which can introduce a rather high error in the end (about 2 pixels or so).
+                                        * Only one char removal shall ever be needed in real-life situation... */
+                                       r_len--;
+                                       final_lpart_len--;
+                                       char *c = str + l_end + sep_len;
+                                       memmove(c, c + 1, r_len);
+                               }
                        }
                }
 
                if (rpart) {
                        /* Add back preserved right part to our shorten str. */
                        memcpy(str + final_lpart_len, rpart, rpart_len + 1);  /* +1 for trailing '\0'. */
+                       okwidth += rpart_width;
                }
 
                strwidth = BLF_width(fstyle->uifont_id, str, max_len);
@@ -1123,16 +1553,18 @@ float UI_text_clip_middle_ex(
                BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
        }
 
+       BLI_assert(strwidth <= okwidth);
+
        return strwidth;
 }
 
 /**
  * Wrapper around UI_text_clip_middle_ex.
  */
-static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rect)
+static void ui_text_clip_middle(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
 {
        /* No margin for labels! */
-       const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
+       const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU, UI_BTYPE_POPOVER) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
        const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0);
        const size_t max_len = sizeof(but->drawstr);
        const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f;
@@ -1145,10 +1577,10 @@ static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rec
  * Like ui_text_clip_middle(), but protect/preserve at all cost the right part of the string after sep.
  * Useful for strings with shortcuts (like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O').
  */
-static void ui_text_clip_middle_protect_right(uiFontStyle *fstyle, uiBut *but, const rcti *rect, const char rsep)
+static void ui_text_clip_middle_protect_right(const uiFontStyle *fstyle, uiBut *but, const rcti *rect, const char rsep)
 {
        /* No margin for labels! */
-       const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
+       const int border = ELEM(but->type, UI_BTYPE_LABEL, UI_BTYPE_MENU, UI_BTYPE_POPOVER) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
        const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0);
        const size_t max_len = sizeof(but->drawstr);
        const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f;
@@ -1160,7 +1592,7 @@ static void ui_text_clip_middle_protect_right(uiFontStyle *fstyle, uiBut *but, c
 /**
  * Cut off the text, taking into account the cursor location (text display while editing).
  */
-static void ui_text_clip_cursor(uiFontStyle *fstyle, uiBut *but, const rcti *rect)
+static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
 {
        const int border = (int)(UI_TEXT_CLIP_MARGIN + 0.5f);
        const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0);
@@ -1222,7 +1654,7 @@ static void ui_text_clip_cursor(uiFontStyle *fstyle, uiBut *but, const rcti *rec
  *
  * \note deals with ': ' especially for number buttons
  */
-static void ui_text_clip_right_label(uiFontStyle *fstyle, uiBut *but, const rcti *rect)
+static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, const rcti *rect)
 {
        const int border = UI_TEXT_CLIP_MARGIN + 1;
        const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0);
@@ -1302,6 +1734,7 @@ static void widget_draw_text_ime_underline(
        int ofs_x, width;
        int rect_x = BLI_rcti_size_x(rect);
        int sel_start = ime_data->sel_start, sel_end = ime_data->sel_end;
+       float fcol[4];
 
        if (drawstr[0] != 0) {
                if (but->pos >= but->ofs) {
@@ -1315,8 +1748,8 @@ static void widget_draw_text_ime_underline(
                        fstyle->uifont_id, drawstr + but->ofs,
                        ime_data->composite_len + but->pos - but->ofs);
 
-               glColor4ubv((uchar *)wcol->text);
-               UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1);
+               rgba_uchar_to_float(fcol, wcol->text);
+               UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1, fcol);
 
                /* draw the thick line */
                if (sel_start != -1 && sel_end != -1) {
@@ -1334,13 +1767,13 @@ static void widget_draw_text_ime_underline(
                                fstyle->uifont_id, drawstr + but->ofs,
                                sel_end + sel_start - but->ofs);
 
-                       UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2);
+                       UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2, fcol);
                }
        }
 }
 #endif  /* WITH_INPUT_IME */
 
-static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
+static void widget_draw_text(const uiFontStyle *fstyle, const uiWidgetColors *wcol, uiBut *but, rcti *rect)
 {
        int drawstr_left_len = UI_MAX_DRAW_STR;
        const char *drawstr = but->drawstr;
@@ -1353,12 +1786,16 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
 
        UI_fontstyle_set(fstyle);
 
-       if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT))
-               fstyle->align = UI_STYLE_TEXT_LEFT;
-       else if (but->drawflag & UI_BUT_TEXT_RIGHT)
-               fstyle->align = UI_STYLE_TEXT_RIGHT;
-       else
-               fstyle->align = UI_STYLE_TEXT_CENTER;
+       eFontStyle_Align align;
+       if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT)) {
+               align = UI_STYLE_TEXT_LEFT;
+       }
+       else if (but->drawflag & UI_BUT_TEXT_RIGHT) {
+               align = UI_STYLE_TEXT_RIGHT;
+       }
+       else {
+               align = UI_STYLE_TEXT_CENTER;
+       }
 
        if (fstyle->kerning == 1) /* for BLF_width */
                BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
@@ -1370,7 +1807,7 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
                uiBut *but_edit = ui_but_drag_multi_edit_get(but);
                if (but_edit) {
                        drawstr = but_edit->editstr;
-                       fstyle->align = UI_STYLE_TEXT_LEFT;
+                       align = UI_STYLE_TEXT_LEFT;
                }
        }
        else {
@@ -1409,6 +1846,10 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
                        int selsta_draw, selwidth_draw;
 
                        if (drawstr[0] != 0) {
+                               /* We are drawing on top of widget bases. Flush cache. */
+                               GPU_blend(true);
+                               UI_widgetbase_draw_cache_flush();
+                               GPU_blend(false);
 
                                if (but->selsta >= but->ofs) {
                                        selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs);
@@ -1419,11 +1860,16 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
 
                                selwidth_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selend - but->ofs);
 
-                               glColor4ubv((uchar *)wcol->item);
-                               glRecti(rect->xmin + selsta_draw,
-                                       rect->ymin + 2,
-                                       min_ii(rect->xmin + selwidth_draw, rect->xmax - 2),
-                                       rect->ymax - 2);
+                               uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+                               immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+                               immUniformColor4ubv((uchar *)wcol->item);
+                               immRecti(pos, rect->xmin + selsta_draw,
+                                        rect->ymin + 2,
+                                        min_ii(rect->xmin + selwidth_draw, rect->xmax - 2),
+                                        rect->ymax - 2);
+
+                               immUnbindProgram();
                        }
                }
 
@@ -1445,14 +1891,23 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
                        else {
                                t = 0;
                        }
+                       /* We are drawing on top of widget bases. Flush cache. */
+                       GPU_blend(true);
+                       UI_widgetbase_draw_cache_flush();
+                       GPU_blend(false);
+
+                       uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+                       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 
-                       glColor3f(0.2, 0.6, 0.9);
+                       immUniformColor3f(0.2f, 0.6f, 0.9f);
 
                        tx = rect->xmin + t + 2;
                        ty = rect->ymin + 2;
 
                        /* draw cursor */
-                       glRecti(rect->xmin + t, ty, tx, rect->ymax - 2);
+                       immRecti(pos, rect->xmin + t, ty, tx, rect->ymax - 2);
+
+                       immUnbindProgram();
                }
 
 #ifdef WITH_INPUT_IME
@@ -1477,7 +1932,7 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
 #endif
 
        /* cut string in 2 parts - only for menu entries */
-       if ((but->block->theme_style == UI_BLOCK_THEME_STYLE_POPUP) &&
+       if ((but->drawflag & UI_BUT_HAS_SHORTCUT) &&
            (but->editstr == NULL))
        {
                if (but->flag & UI_BUT_HAS_SEP_CHAR) {
@@ -1513,44 +1968,48 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
        }
 #endif
 
-       glColor4ubv((uchar *)wcol->text);
-
        if (!use_right_only) {
                /* for underline drawing */
                float font_xofs, font_yofs;
 
-               UI_fontstyle_draw_ex(
-                       fstyle, rect, drawstr + but->ofs,
-                       drawstr_left_len - but->ofs, &font_xofs, &font_yofs);
+               int drawlen = (drawstr_left_len == INT_MAX) ? strlen(drawstr + but->ofs) : (drawstr_left_len - but->ofs);
 
-               if (but->menu_key != '\0') {
-                       char fixedbuf[128];
-                       const char *str;
+               if (drawlen > 0) {
+                       UI_fontstyle_draw_ex(
+                               fstyle, rect, drawstr + but->ofs, (uchar *)wcol->text,
+                               &(struct uiFontStyleDraw_Params) { .align = align, },
+                               drawlen, &font_xofs, &font_yofs);
 
-                       BLI_strncpy(fixedbuf, drawstr + but->ofs, min_ii(sizeof(fixedbuf), drawstr_left_len));
+                       if (but->menu_key != '\0') {
+                               char fixedbuf[128];
+                               const char *str;
 
-                       str = strchr(fixedbuf, but->menu_key - 32); /* upper case */
-                       if (str == NULL)
-                               str = strchr(fixedbuf, but->menu_key);
+                               BLI_strncpy(fixedbuf, drawstr + but->ofs, min_ii(sizeof(fixedbuf), drawlen));
 
-                       if (str) {
-                               int ul_index = -1;
-                               float ul_advance;
+                               str = strchr(fixedbuf, but->menu_key - 32); /* upper case */
+                               if (str == NULL)
+                                       str = strchr(fixedbuf, but->menu_key);
 
-                               ul_index = (int)(str - fixedbuf);
+                               if (str) {
+                                       int ul_index = -1;
+                                       float ul_advance;
 
-                               if (fstyle->kerning == 1) {
-                                       BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
-                               }
+                                       ul_index = (int)(str - fixedbuf);
+
+                                       if (fstyle->kerning == 1) {
+                                               BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
+                                       }
 
-                               fixedbuf[ul_index] = '\0';
-                               ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index);
+                                       fixedbuf[ul_index] = '\0';
+                                       ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index) + (1.0f * UI_DPI_FAC);
 
-                               BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f);
-                               BLF_draw(fstyle->uifont_id, "_", 2);
+                                       BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f);
+                                       BLF_color4ubv(fstyle->uifont_id, (uchar *)wcol->text);
+                                       BLF_draw(fstyle->uifont_id, "_", 2);
 
-                               if (fstyle->kerning == 1) {
-                                       BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
+                                       if (fstyle->kerning == 1) {
+                                               BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
+                                       }
                                }
                        }
                }
@@ -1558,14 +2017,36 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
 
        /* part text right aligned */
        if (drawstr_right) {
-               fstyle->align = UI_STYLE_TEXT_RIGHT;
+               char col[4];
+               copy_v4_v4_char(col, wcol->text);
+               if (but->drawflag & UI_BUT_HAS_SHORTCUT) {
+                       col[3] *= 0.5f;
+               }
+
                rect->xmax -= UI_TEXT_CLIP_MARGIN;
-               UI_fontstyle_draw(fstyle, rect, drawstr_right);
+               UI_fontstyle_draw(
+                       fstyle, rect, drawstr_right, (const uchar *)col,
+                       &(struct uiFontStyleDraw_Params) { .align = UI_STYLE_TEXT_RIGHT, });
+       }
+}
+
+static BIFIconID widget_icon_id(uiBut *but)
+{
+       if (!(but->flag & UI_HAS_ICON)) {
+               return ICON_NONE;
+       }
+
+       /* Consecutive icons can be toggle between. */
+       if (but->drawflag & UI_BUT_ICON_REVERSE) {
+               return but->icon - but->iconadd;
+       }
+       else {
+               return but->icon + but->iconadd;
        }
 }
 
 /* draws text and icons for buttons */
-static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
+static void widget_draw_text_icon(const uiFontStyle *fstyle, const uiWidgetColors *wcol, uiBut *but, rcti *rect)
 {
        const uiButExtraIconType extra_icon_type = ui_but_icon_extra_get(but);
        const bool show_menu_icon = ui_but_draw_menu_icon(but);
@@ -1575,10 +2056,10 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
        ui_but_text_password_hide(password_str, but, false);
 
        /* check for button text label */
-       if (but->type == UI_BTYPE_MENU && (but->flag & UI_BUT_NODE_LINK)) {
+       if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_POPOVER) && (but->flag & UI_BUT_NODE_LINK)) {
                rcti temp = *rect;
                temp.xmin = rect->xmax - BLI_rcti_size_y(rect) - 1;
-               widget_draw_icon(but, ICON_LAYER_USED, alpha, &temp, false);
+               widget_draw_icon(but, ICON_LAYER_USED, alpha, &temp, wcol->text);
                rect->xmax = temp.xmin;
        }
 
@@ -1587,7 +2068,7 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
 
        /* Big previews with optional text label below */
        if (but->flag & UI_BUT_ICON_PREVIEW && ui_block_is_menu(but->block)) {
-               const BIFIconID icon = (but->flag & UI_HAS_ICON) ? but->icon + but->iconadd : ICON_NONE;
+               const BIFIconID icon = widget_icon_id(but);
                int icon_size = BLI_rcti_size_y(rect);
                int text_size = 0;
 
@@ -1600,9 +2081,9 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
 
                /* draw icon in rect above the space reserved for the label */
                rect->ymin += text_size;
-               glEnable(GL_BLEND);
+               GPU_blend(true);
                widget_draw_preview(icon, alpha, rect);
-               glDisable(GL_BLEND);
+               GPU_blend(false);
 
                /* offset rect to draw label in */
                rect->ymin -= text_size;
@@ -1613,25 +2094,53 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
        }
        /* Icons on the left with optional text label on the right */
        else if (but->flag & UI_HAS_ICON || show_menu_icon) {
-               const BIFIconID icon = (but->flag & UI_HAS_ICON) ? but->icon + but->iconadd : ICON_NONE;
-               const float icon_size = ICON_DEFAULT_WIDTH_SCALE;
+               const bool is_tool = UI_but_is_tool(but);
+
+               /* XXX add way to draw icons at a different size!
+                * Use small icons for popup. */
+#ifdef USE_UI_TOOLBAR_HACK
+               const float aspect_orig = but->block->aspect;
+               if (is_tool && (but->block->flag & UI_BLOCK_POPOVER)) {
+                       but->block->aspect *= 2.0f;
+               }
+#endif
+
+               const BIFIconID icon = widget_icon_id(but);
+               int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT;
+               const float icon_size = icon_size_init / (but->block->aspect / UI_DPI_FAC);
+               const float icon_padding = 2 * UI_DPI_FAC;
+
+#ifdef USE_UI_TOOLBAR_HACK
+               if (is_tool) {
+                       /* pass (even if its a menu toolbar) */
+                       but->drawflag |= UI_BUT_TEXT_LEFT;
+                       but->drawflag |= UI_BUT_ICON_LEFT;
+               }
+#endif
 
                /* menu item - add some more padding so menus don't feel cramped. it must
                 * be part of the button so that this area is still clickable */
-               if (ui_block_is_pie_menu(but->block)) {
+               if (is_tool) {
+                       /* pass (even if its a menu toolbar) */
+               }
+               else if (ui_block_is_pie_menu(but->block)) {
                        if (but->dt == UI_EMBOSS_RADIAL)
                                rect->xmin += 0.3f * U.widget_unit;
                }
                else if (ui_block_is_menu(but->block))
-                       rect->xmin += 0.3f * U.widget_unit;
-
-               widget_draw_icon(but, icon, alpha, rect, show_menu_icon);
+                       rect->xmin += 0.2f * U.widget_unit;
 
-               rect->xmin += icon_size;
-               /* without this menu keybindings will overlap the arrow icon [#38083] */
+               widget_draw_icon(but, icon, alpha, rect, wcol->text);
                if (show_menu_icon) {
-                       rect->xmax -= icon_size / 2.0f;
+                       BLI_assert(but->block->content_hints & UI_BLOCK_CONTAINS_SUBMENU_BUT);
+                       widget_draw_submenu_tria(but, rect, wcol);
                }
+
+#ifdef USE_UI_TOOLBAR_HACK
+               but->block->aspect = aspect_orig;
+#endif
+
+               rect->xmin += icon_size + icon_padding;
        }
 
        if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT)) {
@@ -1641,6 +2150,12 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
                rect->xmax -= (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect;
        }
 
+       /* Menu contains sub-menu items with triangle icon on their right. Shortcut
+        * strings should be drawn with some padding to the right then. */
+       if (ui_block_is_menu(but->block) && (but->block->content_hints & UI_BLOCK_CONTAINS_SUBMENU_BUT)) {
+               rect->xmax -= UI_MENU_SUBMENU_PADDING;
+       }
+
        /* extra icons, e.g. 'x' icon to clear text or icon for eyedropper */
        if (extra_icon_type != UI_BUT_ICONEXTRA_NONE) {
                rcti temp = *rect;
@@ -1648,10 +2163,10 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
                temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f);
 
                if (extra_icon_type == UI_BUT_ICONEXTRA_CLEAR) {
-                       widget_draw_icon(but, ICON_PANEL_CLOSE, alpha, &temp, false);
+                       widget_draw_icon(but, ICON_PANEL_CLOSE, alpha, &temp, wcol->text);
                }
                else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) {
-                       widget_draw_icon(but, ICON_EYEDROPPER, alpha, &temp, false);
+                       widget_draw_icon(but, ICON_EYEDROPPER, alpha, &temp, wcol->text);
                }
                else {
                        BLI_assert(0);
@@ -1684,6 +2199,9 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
        widget_draw_text(fstyle, wcol, but, rect);
 
        ui_but_text_password_hide(password_str, but, true);
+
+       /* if a widget uses font shadow it has to be deactivated now */
+       BLF_disable(fstyle->uifont_id, BLF_SHADOW);
 }
 
 #undef UI_TEXT_CLIP_MARGIN
@@ -1691,327 +2209,43 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
 
 /* *********************** widget types ************************************* */
 
-static struct uiWidgetStateColors wcol_state_colors = {
-       {115, 190, 76, 255},
-       {90, 166, 51, 255},
-       {240, 235, 100, 255},
-       {215, 211, 75, 255},
-       {180, 0, 255, 255},
-       {153, 0, 230, 255},
-       0.5f, 0.0f
-};
+/* ************ button callbacks, state ***************** */
 
-static struct uiWidgetColors wcol_num = {
-       {25, 25, 25, 255},
-       {180, 180, 180, 255},
-       {153, 153, 153, 255},
-       {90, 90, 90, 255},
+static void widget_state_blend(char cp[3], const char cpstate[3], const float fac)
+{
+       if (fac != 0.0f) {
+               cp[0] = (int)((1.0f - fac) * cp[0] + fac * cpstate[0]);
+               cp[1] = (int)((1.0f - fac) * cp[1] + fac * cpstate[1]);
+               cp[2] = (int)((1.0f - fac) * cp[2] + fac * cpstate[2]);
+       }
+}
 
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
+/* put all widget colors on half alpha, use local storage */
+static void ui_widget_color_disabled(uiWidgetType *wt)
+{
+       static uiWidgetColors wcol_theme_s;
 
-       1,
-       -20, 0
-};
+       wcol_theme_s = *wt->wcol_theme;
 
-static struct uiWidgetColors wcol_numslider = {
-       {25, 25, 25, 255},
-       {180, 180, 180, 255},
-       {153, 153, 153, 255},
-       {128, 128, 128, 255},
+       wcol_theme_s.outline[3] *= 0.5;
+       wcol_theme_s.inner[3] *= 0.5;
+       wcol_theme_s.inner_sel[3] *= 0.5;
+       wcol_theme_s.item[3] *= 0.5;
+       wcol_theme_s.text[3] *= 0.5;
+       wcol_theme_s.text_sel[3] *= 0.5;
 
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       1,
-       -20, 0
-};
-
-static struct uiWidgetColors wcol_text = {
-       {25, 25, 25, 255},
-       {153, 153, 153, 255},
-       {153, 153, 153, 255},
-       {90, 90, 90, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       1,
-       0, 25
-};
-
-static struct uiWidgetColors wcol_option = {
-       {0, 0, 0, 255},
-       {70, 70, 70, 255},
-       {70, 70, 70, 255},
-       {255, 255, 255, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       1,
-       15, -15
-};
-
-/* button that shows popup */
-static struct uiWidgetColors wcol_menu = {
-       {0, 0, 0, 255},
-       {70, 70, 70, 255},
-       {70, 70, 70, 255},
-       {255, 255, 255, 255},
-
-       {255, 255, 255, 255},
-       {204, 204, 204, 255},
-
-       1,
-       15, -15
-};
-
-/* button that starts pulldown */
-static struct uiWidgetColors wcol_pulldown = {
-       {0, 0, 0, 255},
-       {63, 63, 63, 255},
-       {86, 128, 194, 255},
-       {255, 255, 255, 255},
-
-       {0, 0, 0, 255},
-       {0, 0, 0, 255},
-
-       0,
-       25, -20
-};
-
-/* button inside menu */
-static struct uiWidgetColors wcol_menu_item = {
-       {0, 0, 0, 255},
-       {0, 0, 0, 0},
-       {86, 128, 194, 255},
-       {172, 172, 172, 128},
-
-       {255, 255, 255, 255},
-       {0, 0, 0, 255},
-
-       1,
-       38, 0
-};
-
-/* backdrop menu + title text color */
-static struct uiWidgetColors wcol_menu_back = {
-       {0, 0, 0, 255},
-       {25, 25, 25, 230},
-       {45, 45, 45, 230},
-       {100, 100, 100, 255},
-
-       {160, 160, 160, 255},
-       {255, 255, 255, 255},
-
-       0,
-       25, -20
-};
-
-/* pie menus */
-static struct uiWidgetColors wcol_pie_menu = {
-       {10, 10, 10, 200},
-       {25, 25, 25, 230},
-       {140, 140, 140, 255},
-       {45, 45, 45, 230},
-
-       {160, 160, 160, 255},
-       {255, 255, 255, 255},
-
-       1,
-       10, -10
-};
-
-
-/* tooltip color */
-static struct uiWidgetColors wcol_tooltip = {
-       {0, 0, 0, 255},
-       {25, 25, 25, 230},
-       {45, 45, 45, 230},
-       {100, 100, 100, 255},
-
-       {255, 255, 255, 255},
-       {255, 255, 255, 255},
-
-       0,
-       25, -20
-};
-
-static struct uiWidgetColors wcol_radio = {
-       {0, 0, 0, 255},
-       {70, 70, 70, 255},
-       {86, 128, 194, 255},
-       {255, 255, 255, 255},
-
-       {255, 255, 255, 255},
-       {0, 0, 0, 255},
-
-       1,
-       15, -15
-};
-
-static struct uiWidgetColors wcol_regular = {
-       {25, 25, 25, 255},
-       {153, 153, 153, 255},
-       {100, 100, 100, 255},
-       {25, 25, 25, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       0,
-       0, 0
-};
-
-static struct uiWidgetColors wcol_tool = {
-       {25, 25, 25, 255},
-       {153, 153, 153, 255},
-       {100, 100, 100, 255},
-       {25, 25, 25, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       1,
-       15, -15
-};
-
-static struct uiWidgetColors wcol_box = {
-       {25, 25, 25, 255},
-       {128, 128, 128, 255},
-       {100, 100, 100, 255},
-       {25, 25, 25, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       0,
-       0, 0
-};
-
-static struct uiWidgetColors wcol_toggle = {
-       {25, 25, 25, 255},
-       {153, 153, 153, 255},
-       {100, 100, 100, 255},
-       {25, 25, 25, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       0,
-       0, 0
-};
-
-static struct uiWidgetColors wcol_scroll = {
-       {50, 50, 50, 180},
-       {80, 80, 80, 180},
-       {100, 100, 100, 180},
-       {128, 128, 128, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       1,
-       5, -5
-};
-
-static struct uiWidgetColors wcol_progress = {
-       {0, 0, 0, 255},
-       {190, 190, 190, 255},
-       {100, 100, 100, 180},
-       {128, 128, 128, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       0,
-       0, 0
-};
-
-static struct uiWidgetColors wcol_list_item = {
-       {0, 0, 0, 255},
-       {0, 0, 0, 0},
-       {86, 128, 194, 255},
-       {90, 90, 90, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       0,
-       0, 0
-};
-
-/* free wcol struct to play with */
-static struct uiWidgetColors wcol_tmp = {
-       {0, 0, 0, 255},
-       {128, 128, 128, 255},
-       {100, 100, 100, 255},
-       {25, 25, 25, 255},
-
-       {0, 0, 0, 255},
-       {255, 255, 255, 255},
-
-       0,
-       0, 0
-};
-
-
-/* called for theme init (new theme) and versions */
-void ui_widget_color_init(ThemeUI *tui)
-{
-       tui->wcol_regular = wcol_regular;
-       tui->wcol_tool = wcol_tool;
-       tui->wcol_text = wcol_text;
-       tui->wcol_radio = wcol_radio;
-       tui->wcol_option = wcol_option;
-       tui->wcol_toggle = wcol_toggle;
-       tui->wcol_num = wcol_num;
-       tui->wcol_numslider = wcol_numslider;
-       tui->wcol_menu = wcol_menu;
-       tui->wcol_pulldown = wcol_pulldown;
-       tui->wcol_menu_back = wcol_menu_back;
-       tui->wcol_pie_menu = wcol_pie_menu;
-       tui->wcol_tooltip = wcol_tooltip;
-       tui->wcol_menu_item = wcol_menu_item;
-       tui->wcol_box = wcol_box;
-       tui->wcol_scroll = wcol_scroll;
-       tui->wcol_list_item = wcol_list_item;
-       tui->wcol_progress = wcol_progress;
-
-       tui->wcol_state = wcol_state_colors;
-}
-
-/* ************ button callbacks, state ***************** */
-
-static void widget_state_blend(char cp[3], const char cpstate[3], const float fac)
-{
-       if (fac != 0.0f) {
-               cp[0] = (int)((1.0f - fac) * cp[0] + fac * cpstate[0]);
-               cp[1] = (int)((1.0f - fac) * cp[1] + fac * cpstate[1]);
-               cp[2] = (int)((1.0f - fac) * cp[2] + fac * cpstate[2]);
-       }
+       wt->wcol_theme = &wcol_theme_s;
 }
 
-/* put all widget colors on half alpha, use local storage */
-static void ui_widget_color_disabled(uiWidgetType *wt)
+static void widget_active_color(char cp[3])
 {
-       static uiWidgetColors wcol_theme_s;
-
-       wcol_theme_s = *wt->wcol_theme;
-
-       wcol_theme_s.outline[3] *= 0.5;
-       wcol_theme_s.inner[3] *= 0.5;
-       wcol_theme_s.inner_sel[3] *= 0.5;
-       wcol_theme_s.item[3] *= 0.5;
-       wcol_theme_s.text[3] *= 0.5;
-       wcol_theme_s.text_sel[3] *= 0.5;
-
-       wt->wcol_theme = &wcol_theme_s;
+       cp[0] = cp[0] >= 240 ? 255 : cp[0] + 15;
+       cp[1] = cp[1] >= 240 ? 255 : cp[1] + 15;
+       cp[2] = cp[2] >= 240 ? 255 : cp[2] + 15;
 }
 
 /* copy colors from theme, and set changes in it based on state */
-static void widget_state(uiWidgetType *wt, int state)
+static void widget_state(uiWidgetType *wt, int state, int drawflag)
 {
        uiWidgetStateColors *wcol_state = wt->wcol_state;
 
@@ -2029,13 +2263,16 @@ static void widget_state(uiWidgetType *wt, int state)
 
        if (state & UI_SELECT) {
                copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
-
-               if (state & UI_BUT_ANIMATED_KEY)
+               if (drawflag & UI_BUT_ANIMATED_CHANGED)
+                       widget_state_blend(wt->wcol.inner, wcol_state->inner_changed_sel, wcol_state->blend);
+               else if (state & UI_BUT_ANIMATED_KEY)
                        widget_state_blend(wt->wcol.inner, wcol_state->inner_key_sel, wcol_state->blend);
                else if (state & UI_BUT_ANIMATED)
                        widget_state_blend(wt->wcol.inner, wcol_state->inner_anim_sel, wcol_state->blend);
                else if (state & UI_BUT_DRIVEN)
                        widget_state_blend(wt->wcol.inner, wcol_state->inner_driven_sel, wcol_state->blend);
+               else if (state & UI_BUT_OVERRIDEN)
+                       widget_state_blend(wt->wcol.inner, wcol_state->inner_overridden_sel, wcol_state->blend);
 
                copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
 
@@ -2043,23 +2280,30 @@ static void widget_state(uiWidgetType *wt, int state)
                        SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
        }
        else {
-               if (state & UI_BUT_ANIMATED_KEY)
+               if (drawflag & UI_BUT_ANIMATED_CHANGED)
+                       widget_state_blend(wt->wcol.inner, wcol_state->inner_changed, wcol_state->blend);
+               else if (state & UI_BUT_ANIMATED_KEY)
                        widget_state_blend(wt->wcol.inner, wcol_state->inner_key, wcol_state->blend);
                else if (state & UI_BUT_ANIMATED)
                        widget_state_blend(wt->wcol.inner, wcol_state->inner_anim, wcol_state->blend);
                else if (state & UI_BUT_DRIVEN)
                        widget_state_blend(wt->wcol.inner, wcol_state->inner_driven, wcol_state->blend);
+               else if (state & UI_BUT_OVERRIDEN)
+                       widget_state_blend(wt->wcol.inner, wcol_state->inner_overridden, wcol_state->blend);
 
                if (state & UI_ACTIVE) { /* mouse over? */
-                       wt->wcol.inner[0] = wt->wcol.inner[0] >= 240 ? 255 : wt->wcol.inner[0] + 15;
-                       wt->wcol.inner[1] = wt->wcol.inner[1] >= 240 ? 255 : wt->wcol.inner[1] + 15;
-                       wt->wcol.inner[2] = wt->wcol.inner[2] >= 240 ? 255 : wt->wcol.inner[2] + 15;
+                       widget_active_color(wt->wcol.inner);
                }
        }
 
        if (state & UI_BUT_REDALERT) {
                char red[4] = {255, 0, 0};
-               widget_state_blend(wt->wcol.inner, red, 0.4f);
+               if (wt->draw) {
+                       widget_state_blend(wt->wcol.inner, red, 0.4f);
+               }
+               else {
+                       widget_state_blend(wt->wcol.text, red, 0.4f);
+               }
        }
 
        if (state & UI_BUT_DRAG_MULTI) {
@@ -2076,46 +2320,54 @@ static void widget_state(uiWidgetType *wt, int state)
 }
 
 /* sliders use special hack which sets 'item' as inner when drawing filling */
-static void widget_state_numslider(uiWidgetType *wt, int state)
+static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag)
 {
        uiWidgetStateColors *wcol_state = wt->wcol_state;
        /* XXX special tweak to make sure that bar will still be visible */
        float blend = wcol_state->blend - 0.2f;
 
        /* call this for option button */
-       widget_state(wt, state);
+       widget_state(wt, state, drawflag);
 
        /* now, set the inner-part so that it reflects state settings too */
        /* TODO: maybe we should have separate settings for the blending colors used for this case? */
        if (state & UI_SELECT) {
 
-               if (state & UI_BUT_ANIMATED_KEY)
+               if (drawflag & UI_BUT_ANIMATED_CHANGED)
+                       widget_state_blend(wt->wcol.item, wcol_state->inner_changed_sel, blend);
+               else if (state & UI_BUT_ANIMATED_KEY)
                        widget_state_blend(wt->wcol.item, wcol_state->inner_key_sel, blend);
                else if (state & UI_BUT_ANIMATED)
                        widget_state_blend(wt->wcol.item, wcol_state->inner_anim_sel, blend);
                else if (state & UI_BUT_DRIVEN)
                        widget_state_blend(wt->wcol.item, wcol_state->inner_driven_sel, blend);
+               else if (state & UI_BUT_OVERRIDEN)
+                       widget_state_blend(wt->wcol.item, wcol_state->inner_overridden_sel, blend);
 
                if (state & UI_SELECT)
                        SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
        }
        else {
-               if (state & UI_BUT_ANIMATED_KEY)
+               if (drawflag & UI_BUT_ANIMATED_CHANGED)
+                       widget_state_blend(wt->wcol.item, wcol_state->inner_changed, blend);
+               else if (state & UI_BUT_ANIMATED_KEY)
                        widget_state_blend(wt->wcol.item, wcol_state->inner_key, blend);
                else if (state & UI_BUT_ANIMATED)
                        widget_state_blend(wt->wcol.item, wcol_state->inner_anim, blend);
                else if (state & UI_BUT_DRIVEN)
                        widget_state_blend(wt->wcol.item, wcol_state->inner_driven, blend);
+               else if (state & UI_BUT_OVERRIDEN)
+                       widget_state_blend(wt->wcol.item, wcol_state->inner_overridden, blend);
        }
 }
 
 /* labels use theme colors for text */
-static void widget_state_option_menu(uiWidgetType *wt, int state)
+static void widget_state_option_menu(uiWidgetType *wt, int state, int drawflag)
 {
        bTheme *btheme = UI_GetTheme(); /* XXX */
 
        /* call this for option button */
-       widget_state(wt, state);
+       widget_state(wt, state, drawflag);
 
        /* if not selected we get theme from menu back */
        if (state & UI_SELECT)
@@ -2125,25 +2377,19 @@ static void widget_state_option_menu(uiWidgetType *wt, int state)
 }
 
 
-static void widget_state_nothing(uiWidgetType *wt, int UNUSED(state))
+static void widget_state_nothing(uiWidgetType *wt, int UNUSED(state), int UNUSED(drawflag))
 {
        wt->wcol = *(wt->wcol_theme);
 }
 
 /* special case, button that calls pulldown */
-static void widget_state_pulldown(uiWidgetType *wt, int state)
+static void widget_state_pulldown(uiWidgetType *wt, int UNUSED(state), int UNUSED(drawflag))
 {
        wt->wcol = *(wt->wcol_theme);
-
-       copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
-       copy_v3_v3_char(wt->wcol.outline, wt->wcol.inner);
-
-       if (state & UI_ACTIVE)
-               copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
 }
 
 /* special case, pie menu items */
-static void widget_state_pie_menu_item(uiWidgetType *wt, int state)
+static void widget_state_pie_menu_item(uiWidgetType *wt, int state, int UNUSED(drawflag))
 {
        wt->wcol = *(wt->wcol_theme);
 
@@ -2155,23 +2401,27 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, int state)
                copy_v4_v4_char(wt->wcol.inner, wt->wcol.item);
                wt->wcol.inner[3] = 64;
        }
-       /* regular disabled */
-       else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
-               widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
-       }
-       /* regular active */
-       else if (state & UI_SELECT) {
-               copy_v4_v4_char(wt->wcol.outline, wt->wcol.inner_sel);
-               copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
-       }
-       else if (state & UI_ACTIVE) {
-               copy_v4_v4_char(wt->wcol.inner, wt->wcol.item);
-               copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
+       else {
+               /* regular active */
+               if (state & (UI_SELECT | UI_ACTIVE)) {
+                       copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
+               }
+               else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
+                       /* regular disabled */
+                       widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
+               }
+
+               if (state & UI_SELECT) {
+                       copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
+               }
+               else if (state & UI_ACTIVE) {
+                       copy_v4_v4_char(wt->wcol.inner, wt->wcol.item);
+               }
        }
 }
 
 /* special case, menu items */
-static void widget_state_menu_item(uiWidgetType *wt, int state)
+static void widget_state_menu_item(uiWidgetType *wt, int state, int UNUSED(drawflag))
 {
        wt->wcol = *(wt->wcol_theme);
 
@@ -2183,14 +2433,19 @@ static void widget_state_menu_item(uiWidgetType *wt, int state)
                copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
                wt->wcol.inner[3] = 64;
        }
-       /* regular disabled */
-       else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
-               widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
-       }
-       /* regular active */
-       else if (state & UI_ACTIVE) {
-               copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
-               copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
+       else {
+               /* regular active */
+               if (state & UI_ACTIVE) {
+                       copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel);
+               }
+               else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
+                       /* regular disabled */
+                       widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f);
+               }
+
+               if (state & UI_ACTIVE) {
+                       copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
+               }
        }
 }
 
@@ -2224,22 +2479,23 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r
        /* we draw a number of increasing size alpha quad strips */
        alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout;
 
-       glEnableClientState(GL_VERTEX_ARRAY);
+       uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 
        for (step = 1; step <= (int)radout; step++) {
                float expfac = sqrtf(step / radout);
 
                round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, (float)step);
 
-               glColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac));
+               immUniformColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac));
 
                widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip);
 
-               glVertexPointer(2, GL_FLOAT, 0, triangle_strip);
-               glDrawArrays(GL_TRIANGLE_STRIP, 0, totvert * 2); /* add + 2 for getting a complete soft rect. Now it skips top edge to allow transparent menus */
+               widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, totvert * 2);
        }
 
-       glDisableClientState(GL_VERTEX_ARRAY);
+       immUnbindProgram();
 }
 
 static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int direction)
@@ -2263,33 +2519,34 @@ static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int dir
                rect->ymax += 0.1f * U.widget_unit;
        }
 
-       glEnable(GL_BLEND);
-       widget_softshadow(rect, roundboxalign, 0.25f * U.widget_unit);
+       GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+       GPU_blend(true);
+       widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit);
 
-       round_box_edges(&wtb, roundboxalign, rect, 0.25f * U.widget_unit);
+       round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit);
        wtb.draw_emboss = false;
        widgetbase_draw(&wtb, wcol);
 
-       glDisable(GL_BLEND);
+       GPU_blend(false);
 }
 
-
 static void ui_hsv_cursor(float x, float y)
 {
-       glPushMatrix();
-       glTranslatef(x, y, 0.0f);
+       uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 
-       glColor3f(1.0f, 1.0f, 1.0f);
-       glutil_draw_filled_arc(0.0f, M_PI * 2.0, 3.0f * U.pixelsize, 8);
+       immUniformColor3f(1.0f, 1.0f, 1.0f);
+       imm_draw_circle_fill_2d(pos, x, y, 3.0f * U.pixelsize, 8);
 
-       glEnable(GL_BLEND);
-       glEnable(GL_LINE_SMOOTH);
-       glColor3f(0.0f, 0.0f, 0.0f);
-       glutil_draw_lined_arc(0.0f, M_PI * 2.0, 3.0f * U.pixelsize, 12);
-       glDisable(GL_BLEND);
-       glDisable(GL_LINE_SMOOTH);
+       GPU_blend(true);
+       GPU_line_smooth(true);
+       immUniformColor3f(0.0f, 0.0f, 0.0f);
+       imm_draw_circle_wire_2d(pos, x, y, 3.0f * U.pixelsize, 12);
+       GPU_blend(false);
+       GPU_line_smooth(false);
 
-       glPopMatrix();
+       immUnbindProgram();
 }
 
 void ui_hsvcircle_vals_from_pos(
@@ -2328,35 +2585,27 @@ void ui_hsvcircle_pos_from_vals(uiBut *but, const rcti *rect, float *hsv, float
        *ypos = centy + sinf(-ang) * radius;
 }
 
-static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti *rect)
+static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const rcti *rect)
 {
+       /* TODO(merwin): reimplement as shader for pixel-perfect colors */
+
        const int tot = 64;
        const float radstep = 2.0f * (float)M_PI / (float)tot;
        const float centx = BLI_rcti_cent_x_fl(rect);
        const float centy = BLI_rcti_cent_y_fl(rect);
        float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f;
 
-       /* gouraud triangle fan */
        ColorPicker *cpicker = but->custom_data;
-       const float *hsv_ptr = cpicker->color_data;
-       float xpos, ypos, ang = 0.0f;
-       float rgb[3], hsvo[3], hsv[3], col[3], colcent[3];
-       int a;
-       bool color_profile = ui_but_is_colorpicker_display_space(but);
-
-       /* color */
-       ui_but_v3_get(but, rgb);
-
-       /* since we use compat functions on both 'hsv' and 'hsvo', they need to be initialized */
-       hsvo[0] = hsv[0] = hsv_ptr[0];
-       hsvo[1] = hsv[1] = hsv_ptr[1];
-       hsvo[2] = hsv[2] = hsv_ptr[2];
+       float rgb[3], hsv[3], rgb_center[3];
+       bool is_color_gamma = ui_but_is_color_gamma(but);
 
-       if (color_profile)
-               ui_block_cm_to_display_space_v3(but->block, rgb);
+       /* Initialize for compatibility. */
+       copy_v3_v3(hsv, cpicker->color_data);
 
+       /* Compute current hue. */
+       ui_but_v3_get(but, rgb);
+       ui_scene_linear_to_color_picker_space(but, rgb);
        ui_rgb_to_color_picker_compat_v(rgb, hsv);
-       copy_v3_v3(hsvo, hsv);
 
        CLAMP(hsv[2], 0.0f, 1.0f); /* for display only */
 
@@ -2370,39 +2619,72 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti *
                        hsv[2] = 0.5f;
        }
 
-       ui_color_picker_to_rgb(0.0f, 0.0f, hsv[2], colcent, colcent + 1, colcent + 2);
+       const float hsv_center[3] = {0.0f, 0.0f, hsv[2]};
+       ui_color_picker_to_rgb_v(hsv_center, rgb_center);
+       ui_color_picker_to_scene_linear_space(but, rgb_center);
+
+       if (!is_color_gamma) {
+               ui_block_cm_to_display_space_v3(but->block, rgb_center);
+       }
 
-       glBegin(GL_TRIANGLE_FAN);
-       glColor3fv(colcent);
-       glVertex2f(centx, centy);
+       GPUVertFormat *format = immVertexFormat();
+       uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+       uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
 
-       for (a = 0; a <= tot; a++, ang += radstep) {
+       immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+
+       immBegin(GPU_PRIM_TRI_FAN, tot + 2);
+       immAttr3fv(color, rgb_center);
+       immVertex2f(pos, centx, centy);
+
+       float ang = 0.0f;
+       for (int a = 0; a <= tot; a++, ang += radstep) {
                float si = sinf(ang);
                float co = cosf(ang);
+               float hsv_ang[3];
+               float rgb_ang[3];
+
+               ui_hsvcircle_vals_from_pos(hsv_ang, hsv_ang + 1, rect, centx + co * radius, centy + si * radius);
+               hsv_ang[2] = hsv[2];
 
-               ui_hsvcircle_vals_from_pos(hsv, hsv + 1, rect, centx + co * radius, centy + si * radius);
+               ui_color_picker_to_rgb_v(hsv_ang, rgb_ang);
+               ui_color_picker_to_scene_linear_space(but, rgb_ang);
 
-               ui_color_picker_to_rgb_v(hsv, col);
+               if (!is_color_gamma) {
+                       ui_block_cm_to_display_space_v3(but->block, rgb_ang);
+               }
 
-               glColor3fv(col);
-               glVertex2f(centx + co * radius, centy + si * radius);
+               immAttr3fv(color, rgb_ang);
+               immVertex2f(pos, centx + co * radius, centy + si * radius);
        }
-       glEnd();
+       immEnd();
+       immUnbindProgram();
 
        /* fully rounded outline */
-       glPushMatrix();
-       glTranslatef(centx, centy, 0.0f);
-       glEnable(GL_BLEND);
-       glEnable(GL_LINE_SMOOTH);
-       glColor3ubv((uchar *)wcol->outline);
-       glutil_draw_lined_arc(0.0f, M_PI * 2.0, radius, tot + 1);
-       glDisable(GL_BLEND);
-       glDisable(GL_LINE_SMOOTH);
-       glPopMatrix();
+       format = immVertexFormat();
+       pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+       GPU_blend(true);
+       GPU_line_smooth(true);
+
+       immUniformColor3ubv((uchar *)wcol->outline);
+       imm_draw_circle_wire_2d(pos, centx, centy, radius, tot);
+
+       immUnbindProgram();
+
+       GPU_blend(false);
+       GPU_line_smooth(false);
 
        /* cursor */
-       ui_hsvcircle_pos_from_vals(but, rect, hsvo, &xpos, &ypos);
+       copy_v3_v3(hsv, cpicker->color_data);
+       ui_but_v3_get(but, rgb);
+       ui_scene_linear_to_color_picker_space(but, rgb);
+       ui_rgb_to_color_picker_compat_v(rgb, hsv);
 
+       float xpos, ypos;
+       ui_hsvcircle_pos_from_vals(but, rect, hsv, &xpos, &ypos);
        ui_hsv_cursor(xpos, ypos);
 }
 
@@ -2412,7 +2694,8 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti *
 void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, const float alpha)
 {
        /* allows for 4 steps (red->yellow) */
-       const float color_step = 1.0f / 48.0f;
+       const int steps = 48;
+       const float color_step = 1.0f / steps;
        int a;
        float h = hsv[0], s = hsv[1], v = hsv[2];
        float dx, dy, sx1, sx2, sy;
@@ -2468,8 +2751,15 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons
        }
 
        /* old below */
+       GPUVertFormat *format = immVertexFormat();
+       uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+       uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+       immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+
+       immBegin(GPU_PRIM_TRIS, steps * 3 * 6);
 
-       for (dx = 0.0f; dx < 0.999f; dx += color_step) { /* 0.999 = prevent float inaccuracy for steps */
+       /* 0.999 = prevent float inaccuracy for steps */
+       for (dx = 0.0f; dx < 0.999f; dx += color_step) {
                const float dx_next = dx + color_step;
 
                /* previous color */
@@ -2526,34 +2816,29 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons
                sy = rect->ymin;
                dy = (float)BLI_rcti_size_y(rect) / 3.0f;
 
-               glBegin(GL_QUADS);
                for (a = 0; a < 3; a++, sy += dy) {
-                       glColor4f(col0[a][0], col0[a][1], col0[a][2], alpha);
-                       glVertex2f(sx1, sy);
+                       immAttr4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
+                       immVertex2f(pos, sx1, sy);
 
-                       glColor4f(col1[a][0], col1[a][1], col1[a][2], alpha);
-                       glVertex2f(sx2, sy);
+                       immAttr4f(col, col1[a][0], col1[a][1], col1[a][2], alpha);
+                       immVertex2f(pos, sx2, sy);
 
-                       glColor4f(col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
-                       glVertex2f(sx2, sy + dy);
+                       immAttr4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
+                       immVertex2f(pos, sx2, sy + dy);
 
-                       glColor4f(col0[a + 1][0], col0[a + 1][1], col0[a + 1][2], alpha);
-                       glVertex2f(sx1, sy + dy);
-               }
-               glEnd();
-       }
-}
+                       immAttr4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
+                       immVertex2f(pos, sx1, sy);
 
-bool ui_but_is_colorpicker_display_space(uiBut *but)
-{
-       bool color_profile = but->block->color_profile;
+                       immAttr4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
+                       immVertex2f(pos, sx2, sy + dy);
 
-       if (but->rnaprop) {
-               if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA)
-                       color_profile = false;
+                       immAttr4f(col, col0[a + 1][0], col0[a + 1][1], col0[a + 1][2], alpha);
+                       immVertex2f(pos, sx1, sy + dy);
+               }
        }
+       immEnd();
 
-       return color_profile;
+       immUnbindProgram();
 }
 
 void ui_hsvcube_pos_from_vals(uiBut *but, const rcti *rect, float *hsv, float *xp, float *yp)
@@ -2597,15 +2882,12 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
        ColorPicker *cpicker = but->custom_data;
        float *hsv = cpicker->color_data;
        float hsv_n[3];
-       bool use_display_colorspace = ui_but_is_colorpicker_display_space(but);
 
+       /* Initialize for compatibility. */
        copy_v3_v3(hsv_n, hsv);
 
        ui_but_v3_get(but, rgb);
-
-       if (use_display_colorspace)
-               ui_block_cm_to_display_space_v3(but->block, rgb);
-
+       ui_scene_linear_to_color_picker_space(but, rgb);
        rgb_to_hsv_compat_v(rgb, hsv_n);
 
        ui_draw_gradient(rect, hsv_n, but->a1, 1.0f);
@@ -2617,26 +2899,25 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
        ui_hsv_cursor(x, y);
 
        /* outline */
-       glColor3ub(0,  0,  0);
-       fdrawbox((rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
+       uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+       immUniformColor3ub(0, 0, 0);
+       imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
+       immUnbindProgram();
 }
 
 /* vertical 'value' slider, using new widget code */
 static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
 {
+       bTheme *btheme = UI_GetTheme();
+       uiWidgetColors *wcol = &btheme->tui.wcol_numslider;
        uiWidgetBase wtb;
-       const float rad = 0.5f * BLI_rcti_size_x(rect);
+       const float rad = wcol->roundness * BLI_rcti_size_x(rect);
        float x, y;
        float rgb[3], hsv[3], v;
-       bool color_profile = but->block->color_profile;
-
-       if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA)
-               color_profile = false;
 
        ui_but_v3_get(but, rgb);
-
-       if (color_profile)
-               ui_block_cm_to_display_space_v3(but->block, rgb);
+       ui_scene_linear_to_color_picker_space(but, rgb);
 
        if (but->a1 == UI_GRAD_L_ALT)
                rgb_to_hsl_v(rgb, hsv);
@@ -2647,9 +2928,6 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
        /* map v from property range to [0,1] */
        if (but->a1 == UI_GRAD_V_ALT) {
                float min = but->softmin, max = but->softmax;
-               if (color_profile) {
-                       ui_block_cm_to_display_space_range(but->block, &min, &max);
-               }
                v = (v - min) / (max - min);
        }
 
@@ -2659,13 +2937,21 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
        round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
 
        /* setup temp colors */
-       wcol_tmp.outline[0] = wcol_tmp.outline[1] = wcol_tmp.outline[2] = 0;
-       wcol_tmp.inner[0] = wcol_tmp.inner[1] = wcol_tmp.inner[2] = 128;
-       wcol_tmp.shadetop = 127;
-       wcol_tmp.shadedown = -128;
-       wcol_tmp.shaded = 1;
-
-       widgetbase_draw(&wtb, &wcol_tmp);
+       widgetbase_draw(
+               &wtb,
+               &((uiWidgetColors){
+                   .outline = {0, 0, 0, 255},
+                   .inner = {128, 128, 128, 255},
+                   .shadetop = 127,
+                   .shadedown = -128,
+                   .shaded = 1,
+               })
+       );
+
+       /* We are drawing on top of widget bases. Flush cache. */
+       GPU_blend(true);
+       UI_widgetbase_draw_cache_flush();
+       GPU_blend(false);
 
        /* cursor */
        x = rect->xmin + 0.5f * BLI_rcti_size_x(rect);
@@ -2675,31 +2961,55 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect)
        ui_hsv_cursor(x, y);
 }
 
+/* Generic round-box drawing. */
+static void ui_draw_roundbox(const rcti *rect, const float rad, const uiWidgetColors *wcol)
+{
+       uiWidgetBase wtb;
+       widget_init(&wtb);
+       round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
+       widgetbase_draw(&wtb, wcol);
+
+       /* We are drawing on top of widget bases. Flush cache. */
+       GPU_blend(true);
+       UI_widgetbase_draw_cache_flush();
+       GPU_blend(false);
+}
+
 
 /* ************ separator, for menus etc ***************** */
-static void ui_draw_separator(const rcti *rect,  uiWidgetColors *wcol)
+static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
 {
        int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1;
        uchar col[4] = {
                wcol->text[0],
                wcol->text[1],
                wcol->text[2],
-               30
+               30,
        };
 
-       glEnable(GL_BLEND);
-       glColor4ubv(col);
-       glLineWidth(1.0f);
-       sdrawline(rect->xmin, y, rect->xmax, y);
-       glDisable(GL_BLEND);
+       uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+       GPU_blend(true);
+       immUniformColor4ubv(col);
+       GPU_line_width(1.0f);
+
+       immBegin(GPU_PRIM_LINES, 2);
+       immVertex2f(pos, rect->xmin, y);
+       immVertex2f(pos, rect->xmax, y);
+       immEnd();
+
+       GPU_blend(false);
+
+       immUnbindProgram();
 }
 
 /* ************ button callbacks, draw ***************** */
 static void widget_numbut_draw(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, bool emboss)
 {
        uiWidgetBase wtb;
-       const float rad = 0.5f * BLI_rcti_size_y(rect);
-       float textofs = rad * 0.85f;
+       const float rad = wcol->roundness * BLI_rcti_size_y(rect);
+       const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f);
 
        if (state & UI_SELECT)
                SWAP(short, wcol->shadetop, wcol->shadedown);
@@ -2715,14 +3025,80 @@ static void widget_numbut_draw(uiWidgetColors *wcol, rcti *rect, int state, int
        }
 
        /* decoration */
-       if (!(state & UI_STATE_TEXT_INPUT)) {
-               shape_preset_init_number_arrows(&wtb.tria1, rect, 0.6f, 'l');
-               shape_preset_init_number_arrows(&wtb.tria2, rect, 0.6f, 'r');
-       }
+       if ((state & UI_ACTIVE) && !(state & UI_STATE_TEXT_INPUT)) {
+               uiWidgetColors wcol_zone;
+               uiWidgetBase wtb_zone;
+               rcti rect_zone;
+               int roundboxalign_zone;
+
+               /* left arrow zone */
+               widget_init(&wtb_zone);
+               wtb_zone.draw_outline = false;
+               wtb_zone.draw_emboss = false;
+
+               wcol_zone = *wcol;
+               copy_v3_v3_char(wcol_zone.item, wcol->text);
+               if (state & UI_STATE_ACTIVE_LEFT) {
+                       widget_active_color(wcol_zone.inner);
+               }
 
-       widgetbase_draw(&wtb, wcol);
+               rect_zone = *rect;
+               rect_zone.xmax = rect->xmin + handle_width + U.pixelsize;
+               roundboxalign_zone = roundboxalign & ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
+               round_box_edges(&wtb_zone, roundboxalign_zone, &rect_zone, rad);
+
+               shape_preset_init_number_arrows(&wtb_zone.tria1, &rect_zone, 0.6f, 'l');
+               widgetbase_draw(&wtb_zone, &wcol_zone);
+
+               /* right arrow zone */
+               widget_init(&wtb_zone);
+               wtb_zone.draw_outline = false;
+               wtb_zone.draw_emboss = false;
+               wtb_zone.tria1.type = ROUNDBOX_TRIA_ARROWS;
+
+               wcol_zone = *wcol;
+               copy_v3_v3_char(wcol_zone.item, wcol->text);
+               if (state & UI_STATE_ACTIVE_RIGHT) {
+                       widget_active_color(wcol_zone.inner);
+               }
+
+               rect_zone = *rect;
+               rect_zone.xmin = rect->xmax - handle_width - U.pixelsize;
+               roundboxalign_zone = roundboxalign & ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
+               round_box_edges(&wtb_zone, roundboxalign_zone, &rect_zone, rad);
+
+               shape_preset_init_number_arrows(&wtb_zone.tria2, &rect_zone, 0.6f, 'r');
+               widgetbase_draw(&wtb_zone, &wcol_zone);
+
+               /* middle highlight zone */
+               widget_init(&wtb_zone);
+               wtb_zone.draw_outline = false;
+               wtb_zone.draw_emboss = false;
+
+               wcol_zone = *wcol;
+               copy_v3_v3_char(wcol_zone.item, wcol->text);
+               if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) {
+                       widget_active_color(wcol_zone.inner);
+               }
+
+               rect_zone = *rect;
+               rect_zone.xmin = rect->xmin + handle_width - U.pixelsize;
+               rect_zone.xmax = rect->xmax - handle_width + U.pixelsize;
+               round_box_edges(&wtb_zone, 0, &rect_zone, 0);
+               widgetbase_draw(&wtb_zone, &wcol_zone);
+
+               /* outline */
+               wtb.draw_inner = false;
+               widgetbase_draw(&wtb, wcol);
+       }
+       else {
+               /* inner and outline */
+               widgetbase_draw(&wtb, wcol);
+       }
 
        if (!(state & UI_STATE_TEXT_INPUT)) {
+               const float textofs = 0.425f * BLI_rcti_size_y(rect);
+
                /* text space */
                rect->xmin += textofs;
                rect->xmax -= textofs;
@@ -2742,53 +3118,6 @@ static void widget_numbut_embossn(uiBut *UNUSED(but), uiWidgetColors *wcol, rcti
        widget_numbut_draw(wcol, rect, state, roundboxalign, true);
 }
 
-bool ui_link_bezier_points(const rcti *rect, float coord_array[][2], int resol)
-{
-       float dist, vec[4][2];
-
-       vec[0][0] = rect->xmin;
-       vec[0][1] = rect->ymin;
-       vec[3][0] = rect->xmax;
-       vec[3][1] = rect->ymax;
-
-       dist = 0.5f * fabsf(vec[0][0] - vec[3][0]);
-
-       vec[1][0] = vec[0][0] + dist;
-       vec[1][1] = vec[0][1];
-
-       vec[2][0] = vec[3][0] - dist;
-       vec[2][1] = vec[3][1];
-
-       BKE_curve_forward_diff_bezier(vec[0][0], vec[1][0], vec[2][0], vec[3][0], &coord_array[0][0], resol, sizeof(float[2]));
-       BKE_curve_forward_diff_bezier(vec[0][1], vec[1][1], vec[2][1], vec[3][1], &coord_array[0][1], resol, sizeof(float[2]));
-
-       /* TODO: why return anything if always true? */
-       return true;
-}
-
-#define LINK_RESOL  24
-void ui_draw_link_bezier(const rcti *rect)
-{
-       float coord_array[LINK_RESOL + 1][2];
-
-       if (ui_link_bezier_points(rect, coord_array, LINK_RESOL)) {
-#if 0 /* unused */
-               /* we can reuse the dist variable here to increment the GL curve eval amount*/
-               const float dist = 1.0f / (float)LINK_RESOL;
-#endif
-               glEnable(GL_BLEND);
-               glEnable(GL_LINE_SMOOTH);
-
-               glEnableClientState(GL_VERTEX_ARRAY);
-               glVertexPointer(2, GL_FLOAT, 0, coord_array);
-               glDrawArrays(GL_LINE_STRIP, 0, LINK_RESOL + 1);
-               glDisableClientState(GL_VERTEX_ARRAY);
-
-               glDisable(GL_BLEND);
-               glDisable(GL_LINE_SMOOTH);
-       }
-}
-
 /* function in use for buttons and for view2d sliders */
 void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state)
 {
@@ -2803,11 +3132,11 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s
        horizontal = (BLI_rcti_size_x(rect) > BLI_rcti_size_y(rect));
 
        if (horizontal)
-               rad = 0.5f * BLI_rcti_size_y(rect);
+               rad = wcol->roundness * BLI_rcti_size_y(rect);
        else
-               rad = 0.5f * BLI_rcti_size_x(rect);
+               rad = wcol->roundness * BLI_rcti_size_x(rect);
 
-       wtb.draw_shadedir = (horizontal) ? true : false;
+       wtb.uniform_params.shade_dir = (horizontal) ? 1.0f : 0.0;
 
        /* draw back part, colors swapped and shading inverted */
        if (horizontal)
@@ -2939,7 +3268,7 @@ static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, int
 
        /* round corners */
        float value = but->a1;
-       float offs = 0.25f * BLI_rcti_size_y(&rect_prog);
+       float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog);
        float w = value * BLI_rcti_size_x(&rect_prog);
 
        /* ensure minimium size */
@@ -2962,96 +3291,86 @@ static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, int
        rect->xmax += (BLI_rcti_size_x(&rect_prog) / 2);
 }
 
-static void widget_link(uiBut *but, uiWidgetColors *UNUSED(wcol), rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
-{
-
-       if (but->flag & UI_SELECT) {
-               rcti rectlink;
-
-               UI_ThemeColor(TH_TEXT_HI);
-
-               rectlink.xmin = BLI_rcti_cent_x(rect);
-               rectlink.ymin = BLI_rcti_cent_y(rect);
-               rectlink.xmax = but->linkto[0];
-               rectlink.ymax = but->linkto[1];
-
-               ui_draw_link_bezier(&rectlink);
-       }
-}
-
 static void widget_numslider(uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
 {
        uiWidgetBase wtb, wtb1;
        rcti rect1;
-       double value;
-       float offs, toffs, fac = 0;
+       float offs, toffs;
        char outline[3];
 
        widget_init(&wtb);
        widget_init(&wtb1);
 
-       /* backdrop first */
-
-       /* fully rounded */
-       offs = 0.5f * BLI_rcti_size_y(rect);
+       /* Backdrop first. */
+       offs = wcol->roundness * BLI_rcti_size_y(rect);
        toffs = offs * 0.75f;
        round_box_edges(&wtb, roundboxalign, rect, offs);
 
        wtb.draw_outline = false;
        widgetbase_draw(&wtb, wcol);
 
-       /* draw left/right parts only when not in text editing */
+       /* Draw slider part only when not in text editing. */
        if (!(state & UI_STATE_TEXT_INPUT)) {
-               int roundboxalign_slider;
+               int roundboxalign_slider = roundboxalign;
 
-               /* slider part */
                copy_v3_v3_char(outline, wcol->outline);
                copy_v3_v3_char(wcol->outline, wcol->item);
                copy_v3_v3_char(wcol->inner, wcol->item);
 
-               if (!(state & UI_SELECT))
+               if (!(state & UI_SELECT)) {
                        SWAP(short, wcol->shadetop, wcol->shadedown);
+               }
 
                rect1 = *rect;
+               float factor, factor_ui;
+               float factor_discard = 1.0f; /* No discard. */
+               float value = (float)ui_but_value_get(but);
 
-               value = ui_but_value_get(but);
-               if ((but->softmax - but->softmin) > 0) {
-                       fac = ((float)value - but->softmin) * (BLI_rcti_size_x(&rect1) - offs) / (but->softmax - but->softmin);
+               if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
+                       factor = value / but->softmax;
+               }
+               else {
+                       factor = (value - but->softmin) / (but->softmax - but->softmin);
                }
 
-               /* left part of slider, always rounded */
-               rect1.xmax = rect1.xmin + ceil(offs + U.pixelsize);
-               round_box_edges(&wtb1, roundboxalign & ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT), &rect1, offs);
-               wtb1.draw_outline = false;
-               widgetbase_draw(&wtb1, wcol);
-
-               /* right part of slider, interpolate roundness */
-               rect1.xmax = rect1.xmin + fac + offs;
-               rect1.xmin +=  floor(offs - U.pixelsize);
+               float width = (float)BLI_rcti_size_x(rect);
+               factor_ui = factor * width;
 
-               if (rect1.xmax + offs > rect->xmax) {
-                       roundboxalign_slider = roundboxalign & ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
-                       offs *= (rect1.xmax + offs - rect->xmax) / offs;
+               if (factor_ui <= offs) {
+                       /* Left part only. */
+                       roundboxalign_slider &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
+                       rect1.xmax = rect1.xmin + offs;
+                       factor_discard = factor_ui / offs;
+               }
+               else if (factor_ui <= width - offs) {
+                       /* Left part + middle part. */
+                       roundboxalign_slider &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
+                       rect1.xmax = rect1.xmin + factor_ui;
                }
                else {
-                       roundboxalign_slider = 0;
-                       offs = 0.0f;
+                       /* Left part + middle part + right part. */
+                       factor_discard = factor;
                }
-               round_box_edges(&wtb1, roundboxalign_slider, &rect1, offs);
 
+               round_box_edges(&wtb1, roundboxalign_slider, &rect1, offs);
+               wtb1.draw_outline = false;
+               widgetbase_set_uniform_discard_factor(&wtb1, factor_discard);
                widgetbase_draw(&wtb1, wcol);
+
                copy_v3_v3_char(wcol->outline, outline);
 
-               if (!(state & UI_SELECT))
+               if (!(state & UI_SELECT)) {
                        SWAP(short, wcol->shadetop, wcol->shadedown);
+               }
        }
 
-       /* outline */
+       /* Outline. */
        wtb.draw_outline = true;
        wtb.draw_inner = false;
        widgetbase_draw(&wtb, wcol);
 
-       /* add space at either side of the button so text aligns with numbuttons (which have arrow icons) */
+       /* Add space at either side of the button so text aligns with numbuttons
+        * (which have arrow icons). */
        if (!(state & UI_STATE_TEXT_INPUT)) {
                rect->xmax -= toffs;
                rect->xmin += toffs;
@@ -3065,16 +3384,12 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat
 {
        uiWidgetBase wtb;
        float rad, col[4];
-       bool color_profile = but->block->color_profile;
 
        col[3] = 1.0f;
 
        if (but->rnaprop) {
                BLI_assert(but->rnaindex == -1);
 
-               if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA)
-                       color_profile = false;
-
                if (RNA_property_array_length(&but->rnapoin, but->rnaprop) == 4) {
                        col[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
                }
@@ -3082,13 +3397,14 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat
 
        widget_init(&wtb);
 
-       /* half rounded */
-       rad = 0.25f * U.widget_unit;
+       rad = wcol->roundness * U.widget_unit;
        round_box_edges(&wtb, roundboxalign, rect, rad);
 
        ui_but_v3_get(but, col);
 
-       if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT)) {
+       if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDEN | UI_BUT_REDALERT)) ||
+           (but->drawflag & UI_BUT_ANIMATED_CHANGED))
+       {
                /* draw based on state - color for keyed etc */
                widgetbase_draw(&wtb, wcol);
 
@@ -3101,7 +3417,7 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat
                round_box_edges(&wtb, roundboxalign, rect, rad);
        }
 
-       if (color_profile)
+       if (!ui_but_is_color_gamma(but))
                ui_block_cm_to_display_space_v3(but->block, col);
 
        rgba_float_to_uchar((uchar *)wcol->inner, col);
@@ -3119,7 +3435,6 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat
        }
 
        widgetbase_draw_ex(&wtb, wcol, show_alpha_checkers);
-
        if (but->a1 == UI_PALETTE_COLOR && ((Palette *)but->rnapoin.id.data)->active_color == (int)but->a2) {
                float width = rect->xmax - rect->xmin;
                float height = rect->ymax - rect->ymin;
@@ -3128,12 +3443,22 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat
 
                bw += (bw < 0.5f) ? 0.5f : -0.5f;
 
-               glColor4f(bw, bw, bw, 1.0);
-               glBegin(GL_TRIANGLES);
-               glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.9f * height);
-               glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.5f * height);
-               glVertex2f(rect->xmin + 0.5f * width, rect->ymin + 0.9f * height);
-               glEnd();
+               /* We are drawing on top of widget bases. Flush cache. */
+               GPU_blend(true);
+               UI_widgetbase_draw_cache_flush();
+               GPU_blend(false);
+
+               uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+               immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+               immUniformColor3f(bw, bw, bw);
+               immBegin(GPU_PRIM_TRIS, 3);
+               immVertex2f(pos, rect->xmin + 0.1f * width, rect->ymin + 0.9f * height);
+               immVertex2f(pos, rect->xmin + 0.1f * width, rect->ymin + 0.5f * height);
+               immVertex2f(pos, rect->xmin + 0.5f * width, rect->ymin + 0.9f * height);
+               immEnd();
+
+               immUnbindProgram();
        }
 }
 
@@ -3151,8 +3476,7 @@ static void widget_icon_has_anim(uiBut *but, uiWidgetColors *wcol, rcti *rect, i
                widget_init(&wtb);
                wtb.draw_outline = false;
 
-               /* rounded */
-               rad = 0.5f * BLI_rcti_size_y(rect);
+               rad = wcol->roundness * BLI_rcti_size_y(rect);
                round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
                widgetbase_draw(&wtb, wcol);
        }
@@ -3174,8 +3498,7 @@ static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roun
 
        widget_init(&wtb);
 
-       /* half rounded */
-       rad = 0.2f * U.widget_unit;
+       rad = wcol->roundness * U.widget_unit;
        round_box_edges(&wtb, roundboxalign, rect, rad);
 
        widgetbase_draw(&wtb, wcol);
@@ -3189,12 +3512,13 @@ static void widget_menubut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state),
 
        widget_init(&wtb);
 
-       /* half rounded */
-       rad = 0.2f * U.widget_unit;
+       rad = wcol->roundness * U.widget_unit;
        round_box_edges(&wtb, roundboxalign, rect, rad);
 
        /* decoration */
        shape_preset_trias_from_rect_menu(&wtb.tria1, rect);
+       /* copy size and center to 2nd tria */
+       wtb.tria2 = wtb.tria1;
 
        widgetbase_draw(&wtb, wcol);
 
@@ -3209,44 +3533,31 @@ static void widget_menuiconbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(stat
 
        widget_init(&wtb);
 
-       /* half rounded */
-       rad = 0.2f * U.widget_unit;
-       round_box_edges(&wtb, roundboxalign, rect, rad);
-
-       /* decoration */
-       widgetbase_draw(&wtb, wcol);
-}
-
-static void widget_menunodebut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
-{
-       /* silly node link button hacks */
-       uiWidgetBase wtb;
-       uiWidgetColors wcol_backup = *wcol;
-       float rad;
-
-       widget_init(&wtb);
-
-       /* half rounded */
-       rad = 0.2f * U.widget_unit;
+       rad = wcol->roundness * U.widget_unit;
        round_box_edges(&wtb, roundboxalign, rect, rad);
 
-       wcol->inner[0] = min_ii(wcol->inner[0] + 15, 255);
-       wcol->inner[1] = min_ii(wcol->inner[1] + 15, 255);
-       wcol->inner[2] = min_ii(wcol->inner[2] + 15, 255);
-       wcol->outline[0] = min_ii(wcol->outline[0] + 15, 255);
-       wcol->outline[1] = min_ii(wcol->outline[1] + 15, 255);
-       wcol->outline[2] = min_ii(wcol->outline[2] + 15, 255);
-
        /* decoration */
        widgetbase_draw(&wtb, wcol);
-       *wcol = wcol_backup;
 }
 
 static void widget_pulldownbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
 {
-       if (state & UI_ACTIVE) {
+       float back[4];
+       UI_GetThemeColor4fv(TH_BACK, back);
+
+       if ((state & UI_ACTIVE) || (back[3] < 1.0f)) {
                uiWidgetBase wtb;
-               const float rad = 0.2f * U.widget_unit;
+               const float rad = wcol->roundness * U.widget_unit;
+
+               if (state & UI_ACTIVE) {
+                       copy_v4_v4_char(wcol->inner, wcol->inner_sel);
+                       copy_v3_v3_char(wcol->text, wcol->text_sel);
+                       copy_v3_v3_char(wcol->outline, wcol->inner);
+               }
+               else {
+                       wcol->inner[3] *= 1.0f - back[3];
+                       wcol->outline[3] = 0.0f;
+               }
 
                widget_init(&wtb);
 
@@ -3280,7 +3591,7 @@ static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *r
 
        wtb.draw_emboss = false;
 
-       rad = 0.5f * BLI_rcti_size_y(rect);
+       rad = wcol->roundness * BLI_rcti_size_y(rect);
        round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
 
        wcol->inner[3] *= fac;
@@ -3300,9 +3611,9 @@ static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(sta
 
        widget_init(&wtb);
 
-       /* rounded, but no outline */
+       /* no outline */
        wtb.draw_outline = false;
-       rad = 0.2f * U.widget_unit;
+       rad = wcol->roundness * U.widget_unit;
        round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
 
        widgetbase_draw(&wtb, wcol);
@@ -3327,8 +3638,7 @@ static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, int state, int UN
        recttemp.xmax -= delta;
        recttemp.ymax -= delta;
 
-       /* half rounded */
-       rad = BLI_rcti_size_y(&recttemp) / 3;
+       rad = wcol->roundness * BLI_rcti_size_y(&recttemp);
        round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad);
 
        /* decoration */
@@ -3343,23 +3653,28 @@ static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, int state, int UN
 }
 
 /* labels use Editor theme colors for text */
-static void widget_state_label(uiWidgetType *wt, int state)
+static void widget_state_label(uiWidgetType *wt, int state, int drawflag)
 {
        if (state & UI_BUT_LIST_ITEM) {
                /* Override default label theme's colors. */
                bTheme *btheme = UI_GetTheme();
                wt->wcol_theme = &btheme->tui.wcol_list_item;
                /* call this for option button */
-               widget_state(wt, state);
+               widget_state(wt, state, drawflag);
        }
        else {
                /* call this for option button */
-               widget_state(wt, state);
+               widget_state(wt, state, drawflag);
                if (state & UI_SELECT)
                        UI_GetThemeColor3ubv(TH_TEXT_HI, (uchar *)wt->wcol.text);
                else
                        UI_GetThemeColor3ubv(TH_TEXT, (uchar *)wt->wcol.text);
        }
+
+       if (state & UI_BUT_REDALERT) {
+               char red[4] = {255, 0, 0};
+               widget_state_blend(wt->wcol.text, red, 0.4f);
+       }
 }
 
 static void widget_radiobut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
@@ -3369,8 +3684,7 @@ static void widget_radiobut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state),
 
        widget_init(&wtb);
 
-       /* half rounded */
-       rad = 0.2f * U.widget_unit;
+       rad = wcol->roundness * U.widget_unit;
        round_box_edges(&wtb, roundboxalign, rect, rad);
 
        widgetbase_draw(&wtb, wcol);
@@ -3391,10 +3705,10 @@ static void widget_box(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(
                wcol->inner[0] = but->col[0];
                wcol->inner[1] = but->col[1];
                wcol->inner[2] = but->col[2];
+               wcol->inner[3] = but->col[3];
        }
 
-       /* half rounded */
-       rad = 0.2f * U.widget_unit;
+       rad = wcol->roundness * U.widget_unit;
        round_box_edges(&wtb, roundboxalign, rect, rad);
 
        widgetbase_draw(&wtb, wcol);
@@ -3409,8 +3723,7 @@ static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int
 
        widget_init(&wtb);
 
-       /* half rounded */
-       rad = 0.2f * U.widget_unit;
+       rad = wcol->roundness * U.widget_unit;
        round_box_edges(&wtb, roundboxalign, rect, rad);
 
        widgetbase_draw(&wtb, wcol);
@@ -3420,7 +3733,7 @@ static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int
 static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign)
 {
        uiWidgetBase wtb;
-       const float rad = 0.25f * U.widget_unit;
+       const float rad = wcol->roundness * U.widget_unit;
 
        widget_init(&wtb);
 
@@ -3434,7 +3747,7 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state),
 static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
 {
        uiWidgetBase wtb;
-       const float rad = 0.25f * U.widget_unit;
+       const float rad = wcol->roundness * U.widget_unit;
 
        widget_init(&wtb);
 
@@ -3449,10 +3762,62 @@ static void widget_roundbut_exec(uiWidgetColors *wcol, rcti *rect, int state, in
        widgetbase_draw(&wtb, wcol);
 }
 
+static void widget_tab(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+       const float rad = wcol->roundness * U.widget_unit;
+       const bool is_active = (state & UI_SELECT);
+
+/* Draw shaded outline - Disabled for now,
+ * seems incorrect and also looks nicer without it imho ;) */
+//#define USE_TAB_SHADED_HIGHLIGHT
+
+       uiWidgetBase wtb;
+       uchar theme_col_tab_highlight[3];
+
+#ifdef USE_TAB_SHADED_HIGHLIGHT
+       /* create outline highlight colors */
+       if (is_active) {
+               interp_v3_v3v3_uchar(theme_col_tab_highlight, (uchar *)wcol->inner_sel,
+                                    (uchar *)wcol->outline, 0.2f);
+       }
+       else {
+               interp_v3_v3v3_uchar(theme_col_tab_highlight, (uchar *)wcol->inner,
+                                    (uchar *)wcol->outline, 0.12f);
+       }
+#endif
+
+       widget_init(&wtb);
+
+       /* half rounded */
+       round_box_edges(&wtb, roundboxalign, rect, rad);
+
+       /* draw inner */
+#ifdef USE_TAB_SHADED_HIGHLIGHT
+       wtb.draw_outline = 0;
+#endif
+       widgetbase_draw(&wtb, wcol);
+
+       /* We are drawing on top of widget bases. Flush cache. */
+       GPU_blend(true);
+       UI_widgetbase_draw_cache_flush();
+       GPU_blend(false);
+
+#ifdef USE_TAB_SHADED_HIGHLIGHT
+       /* draw outline (3d look) */
+       ui_draw_but_TAB_outline(rect, rad, theme_col_tab_highlight, (uchar *)wcol->inner);
+#endif
+
+#ifndef USE_TAB_SHADED_HIGHLIGHT
+       UNUSED_VARS(is_active, theme_col_tab_highlight);
+#endif
+}
+
 static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *wt, rcti *rect)
 {
+       bTheme *btheme = UI_GetTheme();
+       uiWidgetColors *wcol = &btheme->tui.wcol_radio;
        uiWidgetBase wtb;
-       const float rad = 0.25f * U.widget_unit;
+       const float rad = wcol->roundness * U.widget_unit;
        uchar col[4];
 
        /* state copy! */
@@ -3464,12 +3829,17 @@ static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *
                /* note: drawextra can change rect +1 or -1, to match round errors of existing previews */
                but->block->drawextra(C, but->poin, but->block->drawextra_arg1, but->block->drawextra_arg2, rect);
 
+               uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+               immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
                /* make mask to draw over image */
                UI_GetThemeColor3ubv(TH_BACK, col);
-               glColor3ubv(col);
+               immUniformColor3ubv(col);
 
                round_box__edges(&wtb, UI_CNR_ALL, rect, 0.0f, rad);
-               widgetbase_outline(&wtb);
+               widgetbase_outline(&wtb, pos);
+
+               immUnbindProgram();
        }
 
        /* outline */
@@ -3531,6 +3901,16 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
                        wt.draw = widget_roundbut_exec;
                        break;
 
+               case UI_WTYPE_TOOLBAR_ITEM:
+                       wt.wcol_theme = &btheme->tui.wcol_toolbar_item;
+                       wt.draw = widget_roundbut_exec;
+                       break;
+
+               case UI_WTYPE_TAB:
+                       wt.wcol_theme = &btheme->tui.wcol_tab;
+                       wt.draw = widget_tab;
+                       break;
+
                case UI_WTYPE_TOOLTIP:
                        wt.wcol_theme = &btheme->tui.wcol_tooltip;
                        wt.draw = widget_menu_back;
@@ -3560,6 +3940,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
                        break;
 
                case UI_WTYPE_MENU_ICON_RADIO:
+               case UI_WTYPE_MENU_NODE_LINK:
                        wt.wcol_theme = &btheme->tui.wcol_menu;
                        wt.draw = widget_menuiconbut;
                        break;
@@ -3569,11 +3950,6 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
                        wt.draw = widget_menubut;
                        break;
 
-               case UI_WTYPE_MENU_NODE_LINK:
-                       wt.wcol_theme = &btheme->tui.wcol_menu;
-                       wt.draw = widget_menunodebut;
-                       break;
-
                case UI_WTYPE_PULLDOWN:
                        wt.wcol_theme = &btheme->tui.wcol_pulldown;
                        wt.draw = widget_pulldownbut;
@@ -3653,7 +4029,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect)
        /* alignment */
        if ((but->drawflag & UI_BUT_ALIGN) && but->type != UI_BTYPE_PULLDOWN) {
 
-               /* ui_block_position has this correction too, keep in sync */
+               /* ui_popup_block_position has this correction too, keep in sync */
                if (but->drawflag & (UI_BUT_ALIGN_TOP | UI_BUT_ALIGN_STITCH_TOP))
                        rect->ymax += U.pixelsize;
                if (but->drawflag & (UI_BUT_ALIGN_LEFT | UI_BUT_ALIGN_STITCH_LEFT))
@@ -3691,7 +4067,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect)
        }
 
        /* align with open menu */
-       if (but->active) {
+       if (but->active && (but->type != UI_BTYPE_POPOVER)) {
                int direction = ui_but_menu_direction(but);
 
                if      (direction == UI_DIR_UP)    roundbox &= ~(UI_CNR_TOP_RIGHT    | UI_CNR_TOP_LEFT);
@@ -3711,10 +4087,14 @@ static int widget_roundbox_set(uiBut *but, rcti *rect)
 void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rcti *rect)
 {
        bTheme *btheme = UI_GetTheme();
-       ThemeUI *tui = &btheme->tui;
-       uiFontStyle *fstyle = &style->widget;
+       const ThemeUI *tui = &btheme->tui;
+       const uiFontStyle *fstyle = &style->widget;
        uiWidgetType *wt = NULL;
 
+#ifdef USE_UI_POPOVER_ONCE
+       const rcti rect_orig = *rect;
+#endif
+
        /* handle menus separately */
        if (but->dt == UI_EMBOSS_PULLDOWN) {
                switch (but->type) {
@@ -3762,10 +4142,20 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
 
                        case UI_BTYPE_SEPR:
                        case UI_BTYPE_SEPR_LINE:
+                       case UI_BTYPE_SEPR_SPACER:
                                break;
 
                        case UI_BTYPE_BUT:
+#ifdef USE_UI_TOOLBAR_HACK
+                               if (UI_but_is_tool(but)) {
+                                       wt = widget_type(UI_WTYPE_TOOLBAR_ITEM);
+                               }
+                               else {
+                                       wt = widget_type(UI_WTYPE_EXEC);
+                               }
+#else
                                wt = widget_type(UI_WTYPE_EXEC);
+#endif
                                break;
 
                        case UI_BTYPE_NUM:
@@ -3795,6 +4185,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
                                }
                                break;
 
+                       case UI_BTYPE_TAB:
+                               wt = widget_type(UI_WTYPE_TAB);
+                               break;
+
                        case UI_BTYPE_BUT_TOGGLE:
                        case UI_BTYPE_TOGGLE:
                        case UI_BTYPE_TOGGLE_N:
@@ -3818,6 +4212,7 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
 
                        case UI_BTYPE_MENU:
                        case UI_BTYPE_BLOCK:
+                       case UI_BTYPE_POPOVER:
                                if (but->flag & UI_BUT_NODE_LINK) {
                                        /* new node-link button, not active yet XXX */
                                        wt = widget_type(UI_WTYPE_MENU_NODE_LINK);
@@ -3857,13 +4252,6 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
                                wt = widget_type(UI_WTYPE_BOX);
                                break;
 
-                       case UI_BTYPE_LINK:
-                       case UI_BTYPE_INLINK:
-                               wt = widget_type(UI_WTYPE_ICON);
-                               wt->custom = widget_link;
-
-                               break;
-
                        case UI_BTYPE_EXTRA:
                                widget_draw_extra_mask(C, but, widget_type(UI_WTYPE_BOX), rect);
                                break;
@@ -3939,13 +4327,14 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
 
        if (wt) {
                //rcti disablerect = *rect; /* rect gets clipped smaller for text */
-               int roundboxalign, state;
+               int roundboxalign, state, drawflag;
                bool disabled = false;
 
                roundboxalign = widget_roundbox_set(but, rect);
 
                /* Mask out flags re-used for local state. */
                state = but->flag & ~UI_STATE_FLAGS_ALL;
+               drawflag = but->drawflag;
 
                if (state & UI_SELECT_DRAW) {
                        state |= UI_SELECT;
@@ -3961,6 +4350,15 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
                        state |= UI_STATE_HOLD_ACTION;
                }
 
+               if (state & UI_ACTIVE) {
+                       if (but->drawflag & UI_BUT_ACTIVE_LEFT) {
+                               state |= UI_STATE_ACTIVE_LEFT;
+                       }
+                       else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) {
+                               state |= UI_STATE_ACTIVE_RIGHT;
+                       }
+               }
+
                if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE))
                        if (but->dt != UI_EMBOSS_PULLDOWN)
                                disabled = true;
@@ -3968,17 +4366,36 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
                if (disabled)
                        ui_widget_color_disabled(wt);
 
-               wt->state(wt, state);
+               wt->state(wt, state, drawflag);
                if (wt->custom)
                        wt->custom(but, &wt->wcol, rect, state, roundboxalign);
                else if (wt->draw)
                        wt->draw(&wt->wcol, rect, state, roundboxalign);
 
                if (disabled)
-                       glEnable(GL_BLEND);
+                       GPU_blend(true);
+
+#ifdef USE_UI_POPOVER_ONCE
+               if (but->block->flag & UI_BLOCK_POPOVER_ONCE) {
+                       if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) {
+                               uiWidgetType wt_back = *wt;
+                               uiWidgetType *wt_temp = widget_type(UI_WTYPE_MENU_ITEM);
+                               wt_temp->state(wt_temp, state, drawflag);
+                               copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel);
+                               wt->wcol.inner[3] = 128;
+                               wt->wcol.roundness = 0.5f;
+                               ui_draw_roundbox(
+                                       &rect_orig,
+                                       0.25f * min_ff(BLI_rcti_size_x(&rect_orig), BLI_rcti_size_y(&rect_orig)),
+                                       &wt_temp->wcol);
+                               *wt = wt_back;
+                       }
+               }
+#endif
+
                wt->text(fstyle, &wt->wcol, but, rect);
                if (disabled)
-                       glDisable(GL_BLEND);
+                       GPU_blend(false);
 
 //             if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE))
 //                     if (but->dt != UI_EMBOSS_PULLDOWN)
@@ -3986,28 +4403,111 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
        }
 }
 
+static void ui_draw_clip_tri(uiBlock *block, rcti *rect, uiWidgetType *wt)
+{
+       if (block) {
+               float draw_color[4];
+               uchar *color = (uchar *)wt->wcol.text;
+
+               draw_color[0] = ((float)color[0]) / 255.0f;
+               draw_color[1] = ((float)color[1]) / 255.0f;
+               draw_color[2] = ((float)color[2]) / 255.0f;
+               draw_color[3] = 1.0f;
+
+               if (block->flag & UI_BLOCK_CLIPTOP) {
+                       /* XXX no scaling for UI here yet */
+                       UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymax - 8, 't', draw_color);
+               }
+               if (block->flag & UI_BLOCK_CLIPBOTTOM) {
+                       /* XXX no scaling for UI here yet */
+                       UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymin + 10, 'v', draw_color);
+               }
+       }
+}
+
 void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect)
 {
        uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK);
 
-       wt->state(wt, 0);
+       wt->state(wt, 0, 0);
        if (block)
                wt->draw(&wt->wcol, rect, block->flag, block->direction);
        else
                wt->draw(&wt->wcol, rect, 0, 0);
 
-       if (block) {
-               if (block->flag & UI_BLOCK_CLIPTOP) {
-                       /* XXX no scaling for UI here yet */
-                       glColor3ubv((uchar *)wt->wcol.text);
-                       UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymax - 8, 't');
+       ui_draw_clip_tri(block, rect, wt);
+}
+
+/**
+ * Similar to 'widget_menu_back', however we can't use the widget preset system
+ * because we need to pass in the original location so we know where to show the arrow.
+ */
+static void ui_draw_popover_back_impl(
+        const uiWidgetColors *wcol, rcti *rect, int direction, const float unit_size,
+        const float mval_origin[2])
+{
+       /* tsk, this isn't nice. */
+       const float unit_half = unit_size / 2;
+       const float cent_x = mval_origin ? mval_origin[0] : BLI_rcti_cent_x(rect);
+       rect->ymax -= unit_half;
+       rect->ymin += unit_half;
+
+       GPU_blend(true);
+
+       /* Extracted from 'widget_menu_back', keep separate to avoid menu changes breaking popovers */
+       {
+               uiWidgetBase wtb;
+               widget_init(&wtb);
+
+               const int roundboxalign = UI_CNR_ALL;
+               widget_softshadow(rect, roundboxalign, wcol->roundness * U.widget_unit);
+
+               round_box_edges(&wtb, roundboxalign, rect, wcol->roundness * U.widget_unit);
+               wtb.draw_emboss = false;
+               widgetbase_draw(&wtb, wcol);
+       }
+
+       /* Draw popover arrow (top/bottom) */
+       if (ELEM(direction, UI_DIR_UP, UI_DIR_DOWN)) {
+               uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+               immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+               immUniformColor4ubv((uchar *)wcol->inner);
+               GPU_blend(true);
+               immBegin(GPU_PRIM_TRIS, 3);
+               if (direction == UI_DIR_DOWN) {
+                       const float y = rect->ymax;
+                       immVertex2f(pos, cent_x - unit_half, y);
+                       immVertex2f(pos, cent_x + unit_half, y);
+                       immVertex2f(pos, cent_x, y + unit_half);
                }
-               if (block->flag & UI_BLOCK_CLIPBOTTOM) {
-                       /* XXX no scaling for UI here yet */
-                       glColor3ubv((uchar *)wt->wcol.text);
-                       UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymin + 10, 'v');
+               else {
+                       const float y = rect->ymin;
+                       immVertex2f(pos, cent_x - unit_half, y);
+                       immVertex2f(pos, cent_x + unit_half, y);
+                       immVertex2f(pos, cent_x, y - unit_half);
                }
+               immEnd();
+               immUnbindProgram();
+       }
+
+       GPU_blend(false);
+}
+
+void ui_draw_popover_back(ARegion *ar, uiStyle *UNUSED(style), uiBlock *block, rcti *rect)
+{
+       uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK);
+
+       if (block) {
+               float mval_origin[2] = {block->mx, block->my};
+               ui_window_to_block_fl(ar, block, &mval_origin[0], &mval_origin[1]);
+               ui_draw_popover_back_impl(wt->wcol_theme, rect, block->direction, U.widget_unit / block->aspect,  mval_origin);
        }
+       else {
+               wt->state(wt, 0, 0);
+               wt->draw(&wt->wcol, rect, 0, 0);
+       }
+
+       ui_draw_clip_tri(block, rect, wt);
 }
 
 static void draw_disk_shaded(
@@ -4023,33 +4523,21 @@ static void draw_disk_shaded(
        float y1, y2;
        float fac;
        uchar r_col[4];
+       uint pos, col;
 
-       glBegin(GL_TRIANGLE_STRIP);
-
-       s = sinf(start);
-       c = cosf(start);
-
-       y1 = s * radius_int;
-       y2 = s * radius_ext;
-
+       GPUVertFormat *format = immVertexFormat();
+       pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
        if (shaded) {
-               fac = (y1 + radius_ext) * radius_ext_scale;
-               round_box_shade_col4_r(r_col, col1, col2, fac);
-
-               glColor4ubv(r_col);
+               col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+               immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
        }
-
-       glVertex2f(c * radius_int, s * radius_int);
-
-       if (shaded) {
-               fac = (y2 + radius_ext) * radius_ext_scale;
-               round_box_shade_col4_r(r_col, col1, col2, fac);
-
-               glColor4ubv(r_col);
+       else {
+               immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+               immUniformColor4ubv((uchar *)col1);
        }
-       glVertex2f(c * radius_ext, s * radius_ext);
 
-       for (i = 1; i < subd; i++) {
+       immBegin(GPU_PRIM_TRI_STRIP, subd * 2);
+       for (i = 0; i < subd; i++) {
                float a;
 
                a = start + ((i) / (float)(subd - 1)) * angle;
@@ -4061,20 +4549,20 @@ static void draw_disk_shaded(
                if (shaded) {
                        fac = (y1 + radius_ext) * radius_ext_scale;
                        round_box_shade_col4_r(r_col, col1, col2, fac);
-
-                       glColor4ubv(r_col);
+                       immAttr4ubv(col, r_col);
                }
-               glVertex2f(c * radius_int, s * radius_int);
+               immVertex2f(pos, c * radius_int, s * radius_int);
 
                if (shaded) {
                        fac = (y2 + radius_ext) * radius_ext_scale;
                        round_box_shade_col4_r(r_col, col1, col2, fac);
-
-                       glColor4ubv(r_col);
+                       immAttr4ubv(col, r_col);
                }
-               glVertex2f(c * radius_ext, s * radius_ext);
+               immVertex2f(pos, c * radius_ext, s * radius_ext);
        }
-       glEnd();
+       immEnd();
+
+       immUnbindProgram();
 }
 
 void ui_draw_pie_center(uiBlock *block)
@@ -4085,26 +4573,25 @@ void ui_draw_pie_center(uiBlock *block)
 
        float *pie_dir = block->pie_data.pie_dir;
 
-       float pie_radius_internal = U.pixelsize * U.pie_menu_threshold;
-       float pie_radius_external = U.pixelsize * (U.pie_menu_threshold + 7.0f);
+       float pie_radius_internal = U.dpi_fac * U.pie_menu_threshold;
+       float pie_radius_external = U.dpi_fac * (U.pie_menu_threshold + 7.0f);
 
        int subd = 40;
 
        float angle = atan2f(pie_dir[1], pie_dir[0]);
        float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4;
 
-       glPushMatrix();
-       glTranslatef(cx, cy, 0.0f);
+       GPU_matrix_push();
+       GPU_matrix_translate_2f(cx, cy);
 
-       glEnable(GL_BLEND);
+       GPU_blend(true);
        if (btheme->tui.wcol_pie_menu.shaded) {
                char col1[4], col2[4];
                shadecolors4(col1, col2, btheme->tui.wcol_pie_menu.inner, btheme->tui.wcol_pie_menu.shadetop, btheme->tui.wcol_pie_menu.shadedown);
                draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, col1, col2, true);
        }
        else {
-               glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner);
-               draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, NULL, NULL, false);
+               draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, btheme->tui.wcol_pie_menu.inner, NULL, false);
        }
 
        if (!(block->pie_data.flags & UI_PIE_INVALID_DIR)) {
@@ -4114,71 +4601,91 @@ void ui_draw_pie_center(uiBlock *block)
                        draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, col1, col2, true);
                }
                else {
-                       glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner_sel);
-                       draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, NULL, NULL, false);
+                       draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, btheme->tui.wcol_pie_menu.inner_sel, NULL, false);
                }
        }
 
-       glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.outline);
-       glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_internal, subd);
-       glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_external, subd);
+       GPUVertFormat *format = immVertexFormat();
+       uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+       immUniformColor4ubv((uchar *)btheme->tui.wcol_pie_menu.outline);
+
+       imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, pie_radius_internal, subd);
+       imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, pie_radius_external, subd);
+
+       immUnbindProgram();
 
        if (U.pie_menu_confirm > 0 && !(block->pie_data.flags & (UI_PIE_INVALID_DIR | UI_PIE_CLICK_STYLE))) {
-               float pie_confirm_radius = U.pixelsize * (pie_radius_internal + U.pie_menu_confirm);
-               float pie_confirm_external = U.pixelsize * (pie_radius_internal + U.pie_menu_confirm + 7.0f);
+               float pie_confirm_radius = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm);
+               float pie_confirm_external = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm + 7.0f);
+
+               const char col[4] = {btheme->tui.wcol_pie_menu.text_sel[0],
+                                    btheme->tui.wcol_pie_menu.text_sel[1],
+                                    btheme->tui.wcol_pie_menu.text_sel[2],
+                                    64};
 
-               glColor4ub(btheme->tui.wcol_pie_menu.text_sel[0], btheme->tui.wcol_pie_menu.text_sel[1], btheme->tui.wcol_pie_menu.text_sel[2], 64);
-               draw_disk_shaded(angle - range / 2.0f, range, pie_confirm_radius, pie_confirm_external, subd, NULL, NULL, false);
+               draw_disk_shaded(angle - range / 2.0f, range, pie_confirm_radius, pie_confirm_external, subd, col, NULL, false);
        }
 
-       glDisable(GL_BLEND);
-       glPopMatrix();
+       GPU_blend(false);
+       GPU_matrix_pop();
 }
 
 
-uiWidgetColors *ui_tooltip_get_theme(void)
+const uiWidgetColors *ui_tooltip_get_theme(void)
 {
        uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP);
        return wt->wcol_theme;
 }
 
+/**
+ * Generic drawing for background.
+ */
+void ui_draw_widget_back_color(
+        uiWidgetTypeEnum type, bool use_shadow, const rcti *rect,
+        const float color[4])
+{
+       uiWidgetType *wt = widget_type(type);
+
+       if (use_shadow) {
+               GPU_blend(true);
+               GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
+               widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit);
+               GPU_blend(false);
+       }
+
+       rcti rect_copy = *rect;
+       wt->state(wt, 0, 0);
+       if (color) {
+               rgba_float_to_uchar((uchar *)wt->wcol.inner, color);
+       }
+       wt->draw(&wt->wcol, &rect_copy, 0, UI_CNR_ALL);
+}
+void ui_draw_widget_back(uiWidgetTypeEnum type, bool use_shadow, const rcti *rect)
+{
+       ui_draw_widget_back_color(type, use_shadow, rect, NULL);
+}
+
 void ui_draw_tooltip_background(uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect)
 {
        uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP);
-       wt->state(wt, 0);
+       wt->state(wt, 0, 0);
        /* wt->draw ends up using same function to draw the tooltip as menu_back */
        wt->draw(&wt->wcol, rect, 0, 0);
 }
 
-void ui_draw_search_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect)
-{
-       uiWidgetType *wt = widget_type(UI_WTYPE_BOX);
-
-       glEnable(GL_BLEND);
-       widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit);
-       glDisable(GL_BLEND);
-
-       wt->state(wt, 0);
-       if (block)
-               wt->draw(&wt->wcol, rect, block->flag, UI_CNR_ALL);
-       else
-               wt->draw(&wt->wcol, rect, 0, UI_CNR_ALL);
-}
-
-
 /* helper call to draw a menu item without button */
 /* state: UI_ACTIVE or 0 */
-void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state, bool use_sep)
+void ui_draw_menu_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state, bool use_sep)
 {
        uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
        rcti _rect = *rect;
        char *cpoin = NULL;
 
-       wt->state(wt, state);
+       wt->state(wt, state, 0);
        wt->draw(&wt->wcol, rect, 0, 0);
 
        UI_fontstyle_set(fstyle);
-       fstyle->align = UI_STYLE_TEXT_LEFT;
 
        /* text location offset */
        rect->xmin += 0.25f * UI_UNIT_X;
@@ -4216,16 +4723,18 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic
                        UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0');
                }
 
-               glColor4ubv((uchar *)wt->wcol.text);
-               UI_fontstyle_draw(fstyle, rect, drawstr);
+               UI_fontstyle_draw(
+                       fstyle, rect, drawstr, (uchar *)wt->wcol.text,
+                       &(struct uiFontStyleDraw_Params) { .align = UI_STYLE_TEXT_LEFT, });
        }
 
        /* part text right aligned */
        if (use_sep) {
                if (cpoin) {
-                       fstyle->align = UI_STYLE_TEXT_RIGHT;
                        rect->xmax = _rect.xmax - 5;
-                       UI_fontstyle_draw(fstyle, rect, cpoin + 1);
+                       UI_fontstyle_draw(
+                               fstyle, rect, cpoin + 1, (uchar *)wt->wcol.text,
+                               &(struct uiFontStyleDraw_Params) { .align = UI_STYLE_TEXT_RIGHT, });
                        *cpoin = UI_SEP_CHAR;
                }
        }
@@ -4241,13 +4750,14 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic
                height = ICON_SIZE_FROM_BUTRECT(rect);
                aspect = ICON_DEFAULT_HEIGHT / height;
 
-               glEnable(GL_BLEND);
-               UI_icon_draw_aspect(xs, ys, iconid, aspect, 1.0f); /* XXX scale weak get from fstyle? */
-               glDisable(GL_BLEND);
+               GPU_blend(true);
+               /* XXX scale weak get from fstyle? */
+               UI_icon_draw_aspect(xs, ys, iconid, aspect, 1.0f, wt->wcol.text);
+               GPU_blend(false);
        }
 }
 
-void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state)
+void ui_draw_preview_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state)
 {
        rcti trect = *rect;
        const float text_size = UI_UNIT_Y;
@@ -4255,14 +4765,14 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int
        uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM);
 
        /* drawing button background */
-       wt->state(wt, state);
+       wt->state(wt, state, 0);
        wt->draw(&wt->wcol, rect, 0, 0);
 
        /* draw icon in rect above the space reserved for the label */
        rect->ymin += text_size;
-       glEnable(GL_BLEND);
+       GPU_blend(true);
        widget_draw_preview(iconid, 1.0f, rect);
-       glDisable(GL_BLEND);
+       GPU_blend(false);
 
        BLF_width_and_height(fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]);
 
@@ -4283,8 +4793,9 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int
                BLI_strncpy(drawstr, name, sizeof(drawstr));
                UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0');
 
-               glColor4ubv((uchar *)wt->wcol.text);
-               UI_fontstyle_draw(fstyle, &trect, drawstr);
+               UI_fontstyle_draw(
+                       fstyle, &trect, drawstr, (uchar *)wt->wcol.text,
+                       &(struct uiFontStyleDraw_Params) { .align = UI_STYLE_TEXT_CENTER, });
        }
 }