Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Sun, 27 May 2018 19:10:43 +0000 (21:10 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Sun, 27 May 2018 19:10:43 +0000 (21:10 +0200)
1  2 
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_widgets.c

index de1c1de4bcf343aec106123a50720b5e1fd80092,20ad6f00c5b2cccc96d260fc09cb4e3cad7236f4..a99950016598e1e84b9cd168d1d86ad4eeb14917
@@@ -41,7 -41,6 +41,7 @@@
  #include "DNA_scene_types.h"
  #include "DNA_screen_types.h"
  #include "DNA_userdef_types.h"
 +#include "DNA_workspace_types.h"
  
  #include "BLI_math.h"
  #include "BLI_listbase.h"
  
  #include "BLI_utildefines.h"
  
 +#include "BKE_animsys.h"
  #include "BKE_context.h"
  #include "BKE_unit.h"
  #include "BKE_scene.h"
  #include "BKE_screen.h"
  #include "BKE_idprop.h"
  
 -#include "BIF_gl.h"
 +#include "GPU_glew.h"
 +#include "GPU_matrix.h"
  
  #include "BLF_api.h"
  #include "BLT_translation.h"
  
  #include "UI_interface.h"
 +#include "UI_interface_icons.h"
  
  #include "IMB_imbuf.h"
  
  #include "WM_api.h"
  #include "WM_types.h"
 -#include "wm_subwindow.h"
 +#include "WM_message.h"
  
  #include "RNA_access.h"
  
  #include "BPY_extern.h"
  
 +#include "ED_screen.h"
 +
  #include "IMB_colormanagement.h"
  
 +#include "DEG_depsgraph_query.h"
 +
  #include "interface_intern.h"
  
  /* avoid unneeded calls to ui_but_value_get */
@@@ -224,26 -216,6 +224,26 @@@ void ui_region_to_window(const ARegion 
        *y += ar->winrct.ymin;
  }
  
 +static void ui_update_window_matrix(const wmWindow *window, const ARegion *region, uiBlock *block)
 +{
 +      /* window matrix and aspect */
 +      if (region && region->visible) {
 +              /* Get projection matrix which includes View2D translation and zoom. */
 +              gpuGetProjectionMatrix(block->winmat);
 +              block->aspect = 2.0f / fabsf(region->winx * block->winmat[0][0]);
 +      }
 +      else {
 +              /* No subwindow created yet, for menus for example, so we use the main
 +               * window instead, since buttons are created there anyway. */
 +              int width = WM_window_pixels_x(window);
 +              int height = WM_window_pixels_y(window);
 +              rcti winrct = {0, width - 1, 0, height - 1};
 +
 +              wmGetProjectionMatrix(block->winmat, &winrct);
 +              block->aspect = 2.0f / fabsf(width * block->winmat[0][0]);
 +      }
 +}
 +
  /**
   * Popups will add a margin to #ARegion.winrct for shadow,
   * for interactivity (point-inside tests for eg), we want the winrct without the margin added.
@@@ -262,7 -234,7 +262,7 @@@ void ui_region_winrct_get_no_margin(con
  
  /* ******************* block calc ************************* */
  
 -void ui_block_translate(uiBlock *block, int x, int y)
 +void UI_block_translate(uiBlock *block, int x, int y)
  {
        uiBut *but;
  
@@@ -372,7 -344,7 +372,7 @@@ static void ui_block_bounds_calc_center
        startx = (xmax * 0.5f) - (width * 0.5f);
        starty = (ymax * 0.5f) - (height * 0.5f);
  
 -      ui_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin);
 +      UI_block_translate(block, startx - block->rect.xmin, starty - block->rect.ymin);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -386,7 -358,7 +386,7 @@@ static void ui_block_bounds_calc_center
            block->pie_data.pie_center_spawned[1]
        };
  
 -      ui_block_translate(block, xy[0], xy[1]);
 +      UI_block_translate(block, xy[0], xy[1]);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -446,7 -418,7 +446,7 @@@ static void ui_block_bounds_calc_popup
        rect_bounds.ymax = ymax - UI_POPUP_MENU_TOP;
  
        BLI_rcti_clamp(&rect, &rect_bounds, ofs_dummy);
 -      ui_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin);
 +      UI_block_translate(block, rect.xmin - block->rect.xmin, rect.ymin - block->rect.ymin);
  
        /* now recompute bounds and safety */
        ui_block_bounds_calc(block);
@@@ -530,6 -502,86 +530,6 @@@ static int ui_but_calc_float_precision(
        return UI_calc_float_precision(prec, value);
  }
  
 -/* ************** LINK LINE DRAWING  ************* */
 -
 -/* link line drawing is not part of buttons or theme.. so we stick with it here */
 -
 -static void ui_draw_linkline(uiLinkLine *line, int highlightActiveLines, int dashInactiveLines)
 -{
 -      rcti rect;
 -
 -      if (line->from == NULL || line->to == NULL) return;
 -
 -      rect.xmin = BLI_rctf_cent_x(&line->from->rect);
 -      rect.ymin = BLI_rctf_cent_y(&line->from->rect);
 -      rect.xmax = BLI_rctf_cent_x(&line->to->rect);
 -      rect.ymax = BLI_rctf_cent_y(&line->to->rect);
 -
 -      if (dashInactiveLines)
 -              UI_ThemeColor(TH_GRID);
 -      else if (line->flag & UI_SELECT)
 -              glColor3ub(100, 100, 100);
 -      else if (highlightActiveLines && ((line->from->flag & UI_ACTIVE) || (line->to->flag & UI_ACTIVE)))
 -              UI_ThemeColor(TH_TEXT_HI);
 -      else
 -              glColor3ub(0, 0, 0);
 -
 -      ui_draw_link_bezier(&rect);
 -}
 -
 -static void ui_draw_links(uiBlock *block)
 -{
 -      uiBut *but;
 -      uiLinkLine *line;
 -
 -      /* Draw the gray out lines. Do this first so they appear at the
 -       * bottom of inactive or active lines.
 -       * As we go, remember if we see any active or selected lines. */
 -      bool found_selectline = false;
 -      bool found_activeline = false;
 -
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (!(line->from->flag & UI_ACTIVE) && !(line->to->flag & UI_ACTIVE)) {
 -                                      if (line->deactive)
 -                                              ui_draw_linkline(line, 0, true);
 -                              }
 -                              else
 -                                      found_activeline = true;
 -
 -                              if ((line->from->flag & UI_SELECT) || (line->to->flag & UI_SELECT))
 -                                      found_selectline = true;
 -                      }
 -              }
 -      }
 -
 -      /* Draw the inactive lines (lines with neither button being hovered over) */
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (!(line->from->flag & UI_ACTIVE) && !(line->to->flag & UI_ACTIVE)) {
 -                                      if (!line->deactive)
 -                                              ui_draw_linkline(line, 0, false);
 -                              }
 -                      }
 -              }
 -      }
 -
 -      /* Draw any active lines (lines with either button being hovered over).
 -       * Do this last so they appear on top of inactive and gray out lines. */
 -      if (found_activeline) {
 -              for (but = block->buttons.first; but; but = but->next) {
 -                      if (but->type == UI_BTYPE_LINK && but->link) {
 -                              for (line = but->link->lines.first; line; line = line->next) {
 -                                      if ((line->from->flag & UI_ACTIVE) || (line->to->flag & UI_ACTIVE))
 -                                              ui_draw_linkline(line, !found_selectline, false);
 -                              }
 -                      }
 -              }
 -      }
 -}
 -
  /* ************** BLOCK ENDING FUNCTION ************* */
  
  /* NOTE: if but->poin is allocated memory for every defbut, things fail... */
@@@ -572,6 -624,38 +572,6 @@@ uiBut *ui_but_find_new(uiBlock *block_n
        return but_new;
  }
  
 -/* oldbut is being inserted in new block, so we use the lines from new button, and replace button pointers */
 -static void ui_but_update_linklines(uiBlock *block, uiBut *oldbut, uiBut *newbut)
 -{
 -      uiLinkLine *line;
 -      uiBut *but;
 -
 -      /* if active button is UI_BTYPE_LINK */
 -      if (newbut->type == UI_BTYPE_LINK && newbut->link) {
 -
 -              SWAP(uiLink *, oldbut->link, newbut->link);
 -
 -              for (line = oldbut->link->lines.first; line; line = line->next) {
 -                      if (line->to == newbut)
 -                              line->to = oldbut;
 -                      if (line->from == newbut)
 -                              line->from = oldbut;
 -              }
 -      }
 -
 -      /* check all other button links */
 -      for (but = block->buttons.first; but; but = but->next) {
 -              if (but != newbut && but->type == UI_BTYPE_LINK && but->link) {
 -                      for (line = but->link->lines.first; line; line = line->next) {
 -                              if (line->to == newbut)
 -                                      line->to = oldbut;
 -                              if (line->from == newbut)
 -                                      line->from = oldbut;
 -                      }
 -              }
 -      }
 -}
 -
  /**
   * \return true when \a but_p is set (only done for active buttons).
   */
@@@ -628,6 -712,8 +628,6 @@@ static bool ui_but_update_from_old_bloc
                but->selend = oldbut->selend;
                but->softmin = oldbut->softmin;
                but->softmax = oldbut->softmax;
 -              but->linkto[0] = oldbut->linkto[0];
 -              but->linkto[1] = oldbut->linkto[1];
                oldbut->active = NULL;
  #endif
  
                        oldbut->a1 = but->a1;
                }
  
 -              ui_but_update_linklines(block, oldbut, but);
 -
                if (!BLI_listbase_is_empty(&block->butstore)) {
                        UI_butstore_register_update(block, oldbut, but);
                }
@@@ -1096,7 -1184,7 +1096,7 @@@ static void ui_menu_block_set_keymaps(c
        uiBut *but;
        char buf[128];
  
 -      BLI_assert(block->flag & UI_BLOCK_LOOP);
 +      BLI_assert(block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS));
  
        /* only do it before bounding */
        if (block->rect.xmin != block->rect.xmax)
        }
        else {
                for (but = block->buttons.first; but; but = but->next) {
 -                      if (but->dt != UI_EMBOSS_PULLDOWN) {
 +                      if (block->flag & UI_BLOCK_SHOW_SHORTCUT_ALWAYS) {
 +                              /* Skip icon-only buttons (as used in the toolbar). */
 +                              if (but->drawstr[0] == '\0') {
 +                                      continue;
 +                              }
 +                      }
 +                      else if (but->dt != UI_EMBOSS_PULLDOWN) {
                                continue;
                        }
  
        }
  }
  
 +void ui_but_override_flag(uiBut *but)
 +{
 +      const int override_status = RNA_property_static_override_status(&but->rnapoin, but->rnaprop, but->rnaindex);
 +
 +      if (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN) {
 +              but->flag |= UI_BUT_OVERRIDEN;
 +      }
 +      else {
 +              but->flag &= ~UI_BUT_OVERRIDEN;
 +      }
 +}
 +
  void UI_block_update_from_old(const bContext *C, uiBlock *block)
  {
        uiBut *but_old;
@@@ -1208,7 -1278,6 +1208,7 @@@ void UI_block_end_ex(const bContext *C
                }
  
                ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f);
 +              ui_but_override_flag(but);
        }
  
  
        if (block->layouts.first) {
                UI_block_layout_resolve(block, NULL, NULL);
        }
 -      ui_block_align_calc(block);
 +      ui_block_align_calc(block, CTX_wm_region(C));
        if ((block->flag & UI_BLOCK_LOOP) && (block->flag & UI_BLOCK_NUMSELECT)) {
                ui_menu_block_set_keyaccels(block); /* could use a different flag to check */
        }
  
 -      if (block->flag & UI_BLOCK_LOOP) {
 +      if (block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS)) {
                ui_menu_block_set_keymaps(C, block);
        }
  
@@@ -1303,6 -1372,7 +1303,6 @@@ void UI_block_draw(const bContext *C, u
        ARegion *ar;
        uiBut *but;
        rcti rect;
 -      int multisample_enabled;
  
        /* get menu region or area region */
        ar = CTX_wm_menu(C);
        if (!block->endblock)
                UI_block_end(C, block);
  
 -      /* disable AA, makes widgets too blurry */
 -      multisample_enabled = glIsEnabled(GL_MULTISAMPLE);
 -      if (multisample_enabled)
 -              glDisable(GL_MULTISAMPLE);
 -
        /* we set this only once */
 -      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 +      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  
        /* scale fonts */
        ui_fontscale(&style.paneltitle.points, block->aspect);
        ui_but_to_pixelrect(&rect, ar, block, NULL);
  
        /* pixel space for AA widgets */
 -      glMatrixMode(GL_PROJECTION);
 -      glPushMatrix();
 -      glMatrixMode(GL_MODELVIEW);
 -      glPushMatrix();
 -      glLoadIdentity();
 +      gpuPushProjectionMatrix();
 +      gpuPushMatrix();
 +      gpuLoadIdentity();
  
        wmOrtho2_region_pixelspace(ar);
  
        /* back */
        if (block->flag & UI_BLOCK_RADIAL)
                ui_draw_pie_center(block);
 +      else if (block->flag & UI_BLOCK_POPOVER)
 +              ui_draw_popover_back(ar, &style, block, &rect);
        else if (block->flag & UI_BLOCK_LOOP)
                ui_draw_menu_back(&style, block, &rect);
        else if (block->panel)
                ui_draw_aligned_panel(&style, block, &rect, UI_panel_category_is_visible(ar));
  
 +      BLF_batch_draw_begin();
 +      UI_icon_draw_cache_begin();
 +      UI_widgetbase_draw_cache_begin();
 +
        /* widgets */
        for (but = block->buttons.first; but; but = but->next) {
                if (!(but->flag & (UI_HIDDEN | UI_SCROLLED))) {
                }
        }
  
 -      /* restore matrix */
 -      glMatrixMode(GL_PROJECTION);
 -      glPopMatrix();
 -      glMatrixMode(GL_MODELVIEW);
 -      glPopMatrix();
 +      UI_widgetbase_draw_cache_end();
 +      UI_icon_draw_cache_end();
 +      BLF_batch_draw_end();
  
 -      if (multisample_enabled)
 -              glEnable(GL_MULTISAMPLE);
 +      /* restore matrix */
 +      gpuPopProjectionMatrix();
 +      gpuPopMatrix();
 +}
 +
 +static void ui_block_message_subscribe(ARegion *ar, struct wmMsgBus *mbus, uiBlock *block)
 +{
 +      uiBut *but_prev = NULL;
 +      /* possibly we should keep the region this block is contained in? */
 +      for (uiBut *but = block->buttons.first; but; but = but->next) {
 +              if (but->rnapoin.type && but->rnaprop) {
 +                      /* quick check to avoid adding buttons representing a vector, multiple times. */
 +                      if ((but_prev &&
 +                          (but_prev->rnaprop == but->rnaprop) &&
 +                          (but_prev->rnapoin.type == but->rnapoin.type) &&
 +                          (but_prev->rnapoin.data == but->rnapoin.data) &&
 +                          (but_prev->rnapoin.id.data == but->rnapoin.id.data)) == false)
 +                      {
 +                              /* TODO: could make this into utility function. */
 +                              WM_msg_subscribe_rna(
 +                                      mbus, &but->rnapoin, but->rnaprop,
 +                                      &(const wmMsgSubscribeValue){
 +                                          .owner = ar,
 +                                          .user_data = ar,
 +                                          .notify = ED_region_do_msg_notify_tag_redraw,
 +                                      }, __func__);
 +                              but_prev = but;
 +                      }
 +              }
 +      }
 +}
  
 -      ui_draw_links(block);
 +void UI_region_message_subscribe(ARegion *ar, struct wmMsgBus *mbus)
 +{
 +      for (uiBlock *block = ar->uiblocks.first; block; block = block->next) {
 +              ui_block_message_subscribe(ar, mbus, block);
 +      }
  }
  
  /* ************* EVENTS ************* */
@@@ -1455,18 -1495,6 +1455,18 @@@ int ui_but_is_pushed_ex(uiBut *but, dou
                                        if (*value == (double)but->hardmax) is_push = true;
                                }
                                break;
 +                      case UI_BTYPE_TAB:
 +                              if (but->rnaprop && but->custom_data) {
 +                                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                                      if (RNA_property_type(but->rnaprop) == PROP_POINTER) {
 +                                              PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
 +                                              if (active_ptr.data == but->custom_data) {
 +                                                      is_push = true;
 +                                              }
 +                                      }
 +                              }
 +                              break;
                        default:
                                is_push = -1;
                                break;
@@@ -1493,6 -1521,83 +1493,6 @@@ static void ui_but_update_select_flag(u
        }
  }
  
 -static uiBut *ui_linkline_find_inlink(uiBlock *block, void *poin)
 -{
 -      uiBut *but;
 -
 -      but = block->buttons.first;
 -      while (but) {
 -              if (but->type == UI_BTYPE_INLINK) {
 -                      if (but->poin == poin) return but;
 -              }
 -              but = but->next;
 -      }
 -      return NULL;
 -}
 -
 -static void ui_linkline_add(ListBase *listb, uiBut *but, uiBut *bt, short deactive)
 -{
 -      uiLinkLine *line;
 -
 -      line = MEM_callocN(sizeof(uiLinkLine), "linkline");
 -      BLI_addtail(listb, line);
 -      line->from = but;
 -      line->to = bt;
 -      line->deactive = deactive;
 -}
 -
 -uiBut *UI_block_links_find_inlink(uiBlock *block, void *poin)
 -{
 -      return ui_linkline_find_inlink(block, poin);
 -}
 -
 -void UI_block_links_compose(uiBlock *block)
 -{
 -      uiBut *but, *bt;
 -      uiLink *link;
 -      void ***ppoin;
 -      int a;
 -
 -      but = block->buttons.first;
 -      while (but) {
 -              if (but->type == UI_BTYPE_LINK) {
 -                      link = but->link;
 -
 -                      /* for all pointers in the array */
 -                      if (link) {
 -                              if (link->ppoin) {
 -                                      ppoin = link->ppoin;
 -                                      for (a = 0; a < *(link->totlink); a++) {
 -                                              bt = ui_linkline_find_inlink(block, (*ppoin)[a]);
 -                                              if (bt) {
 -                                                      if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) {
 -                                                              ui_linkline_add(&link->lines, but, bt, true);
 -                                                      }
 -                                                      else {
 -                                                              ui_linkline_add(&link->lines, but, bt, false);
 -                                                      }
 -
 -                                              }
 -                                      }
 -                              }
 -                              else if (link->poin) {
 -                                      bt = ui_linkline_find_inlink(block, *link->poin);
 -                                      if (bt) {
 -                                              if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) {
 -                                                      ui_linkline_add(&link->lines, but, bt, true);
 -                                              }
 -                                              else {
 -                                                      ui_linkline_add(&link->lines, but, bt, false);
 -                                              }
 -                                      }
 -                              }
 -                      }
 -              }
 -              but = but->next;
 -      }
 -}
 -
 -
  /* ************************************************ */
  
  void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr)
@@@ -1509,78 -1614,48 +1509,78 @@@ void UI_block_lock_clear(uiBlock *block
        block->lockstr = NULL;
  }
  
 -/* *************************************************************** */
 -
 -void ui_linkline_remove(uiLinkLine *line, uiBut *but)
 -{
 -      uiLink *link;
 -      int a, b;
 -
 -      BLI_remlink(&but->link->lines, line);
 -
 -      link = line->from->link;
 -
 -      /* are there more pointers allowed? */
 -      if (link->ppoin) {
 +/* *********************** data get/set ***********************
 + * this either works with the pointed to data, or can work with
 + * an edit override pointer while dragging for example */
  
 -              if (*(link->totlink) == 1) {
 -                      *(link->totlink) = 0;
 -                      MEM_freeN(*(link->ppoin));
 -                      *(link->ppoin) = NULL;
 -              }
 -              else {
 -                      b = 0;
 -                      for (a = 0; a < (*(link->totlink)); a++) {
 -                              if ((*(link->ppoin))[a] != line->to->poin) {
 -                                      (*(link->ppoin))[b] = (*(link->ppoin))[a];
 -                                      b++;
 -                              }
 -                      }
 -                      (*(link->totlink))--;
 +/* Get PointerRNA which will point to a data inside of an evaluated
 + * ID datablock.
 + */
 +static PointerRNA ui_but_evaluated_rnapoin_get(uiBut *but)
 +{
 +      BLI_assert(but->rnaprop != NULL);
 +      /* TODO(sergey): evil_C sounds.. EVIL! Any clear way to avoid this? */
 +      PointerRNA rnapoin_eval = but->rnapoin;
 +      /* If there is no animation or drivers, it doesn't matter if we read value
 +       * from evaluated datablock or from original one.
 +       *
 +       * Reading from original one is much faster, since we don't need to do any
 +       * PointerRNA remapping or hash lookup.
 +       */
 +      if (BKE_animdata_from_id(but->rnapoin.id.data) == NULL) {
 +              return rnapoin_eval;
 +      }
 +      /* Same goes for the properties which can not be animated. */
 +      if (!RNA_property_animateable(&but->rnapoin, but->rnaprop)) {
 +              return rnapoin_eval;
 +      }
 +      Depsgraph *depsgraph = CTX_data_depsgraph(but->block->evil_C);
 +      /* ID pointer we can always remap, they are inside of depsgraph. */
 +      rnapoin_eval.id.data =
 +              DEG_get_evaluated_id(depsgraph, rnapoin_eval.id.data);
 +      /* Some of ID datablocks do not have their evaluated copy inside
 +       * of dependency graph. If it's such datablock, no need to worry about
 +       * data pointer.
 +       */
 +      if (rnapoin_eval.id.data == but->rnapoin.id.data) {
 +              return rnapoin_eval;
 +      }
 +      /* For the data pointer it's getting a bit more involved, since it can
 +       * whether be and ID, or can be a property deep inside of ID.
 +       *
 +       * We start from checking if it's an ID, since that is the less involved
 +       * code path, and probably is executed in most of the cases.
 +       */
 +      if (but->rnapoin.data == but->rnapoin.id.data) {
 +              rnapoin_eval.data = DEG_get_evaluated_id(depsgraph, rnapoin_eval.data);
 +              return rnapoin_eval;
 +      }
 +      /* We aren't as lucky as we thought we are :(
 +       *
 +       * Since we don't know what the property is, we get it's RNA path
 +       * relative to the original ID, and then we decent down from evaluated
 +       * ID to the same property.
 +       *
 +       * This seems to be most straightforward way to get sub-data pointers
 +       * which can be buried deep inside of ID block.
 +       */
 +      const char *rna_path =
 +             RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
 +      if (rna_path != NULL) {
 +              PointerRNA id_ptr;
 +              RNA_id_pointer_create(rnapoin_eval.id.data, &id_ptr);
 +              if (!RNA_path_resolve_full(&id_ptr,
 +                                         rna_path,
 +                                         &rnapoin_eval,
 +                                         NULL, NULL))
 +              {
 +                      /* TODO(sergey): Anything to do here to recover? */
                }
 +              MEM_freeN((void *)rna_path);
        }
 -      else {
 -              *(link->poin) = NULL;
 -      }
 -
 -      MEM_freeN(line);
 -      //REDRAW
 +      return rnapoin_eval;
  }
  
 -/* *********************** data get/set ***********************
 - * this either works with the pointed to data, or can work with
 - * an edit override pointer while dragging for example */
 -
  /* for buttons pointing to color for example */
  void ui_but_v3_get(uiBut *but, float vec[3])
  {
  
                zero_v3(vec);
  
 +              PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but);
 +
                if (RNA_property_type(prop) == PROP_FLOAT) {
 -                      int tot = RNA_property_array_length(&but->rnapoin, prop);
 +                      int tot = RNA_property_array_length(&rnapoin_eval, prop);
                        BLI_assert(tot > 0);
                        if (tot == 3) {
 -                              RNA_property_float_get_array(&but->rnapoin, prop, vec);
 +                              RNA_property_float_get_array(&rnapoin_eval, prop, vec);
                        }
                        else {
                                tot = min_ii(tot, 3);
                                for (a = 0; a < tot; a++) {
 -                                      vec[a] = RNA_property_float_get_index(&but->rnapoin, prop, a);
 +                                      vec[a] = RNA_property_float_get_index(&rnapoin_eval, prop, a);
                                }
                        }
                }
@@@ -1787,29 -1860,27 +1787,29 @@@ double ui_but_value_get(uiBut *but
  
                BLI_assert(but->rnaindex != -1);
  
 +              PointerRNA rnapoin_eval = ui_but_evaluated_rnapoin_get(but);
 +
                switch (RNA_property_type(prop)) {
                        case PROP_BOOLEAN:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_boolean_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_boolean_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_boolean_get(&but->rnapoin, prop);
 +                                      value = RNA_property_boolean_get(&rnapoin_eval, prop);
                                break;
                        case PROP_INT:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_int_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_int_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_int_get(&but->rnapoin, prop);
 +                                      value = RNA_property_int_get(&rnapoin_eval, prop);
                                break;
                        case PROP_FLOAT:
                                if (RNA_property_array_check(prop))
 -                                      value = RNA_property_float_get_index(&but->rnapoin, prop, but->rnaindex);
 +                                      value = RNA_property_float_get_index(&rnapoin_eval, prop, but->rnaindex);
                                else
 -                                      value = RNA_property_float_get(&but->rnapoin, prop);
 +                                      value = RNA_property_float_get(&rnapoin_eval, prop);
                                break;
                        case PROP_ENUM:
 -                              value = RNA_property_enum_get(&but->rnapoin, prop);
 +                              value = RNA_property_enum_get(&rnapoin_eval, prop);
                                break;
                        default:
                                value = 0.0;
@@@ -1952,7 -2023,7 +1952,7 @@@ static bool ui_but_icon_extra_is_visibl
  
  static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but)
  {
 -      BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
 +      BLI_assert(ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB));
        return ((but->editstr == NULL) &&
                (but->drawstr[0] != '\0') &&
                (but->flag & UI_BUT_VALUE_CLEAR));
@@@ -1995,11 -2066,6 +1995,11 @@@ uiButExtraIconType ui_but_icon_extra_ge
                                return UI_BUT_ICONEXTRA_EYEDROPPER;
                        }
                        break;
 +              case UI_BTYPE_TAB:
 +                      if (ui_but_icon_extra_is_visible_search_unlink(but)) {
 +                              return UI_BUT_ICONEXTRA_CLEAR;
 +                      }
 +                      break;
                default:
                        break;
        }
@@@ -2113,23 -2179,14 +2113,23 @@@ void ui_but_string_get_ex(uiBut *but, c
                *r_use_exp_float = false;
        }
  
 -      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
 +      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_TAB)) {
                PropertyType type;
                const char *buf = NULL;
                int buf_len;
  
                type = RNA_property_type(but->rnaprop);
  
 -              if (type == PROP_STRING) {
 +              if ((but->type == UI_BTYPE_TAB) && (but->custom_data)) {
 +                      StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
 +                      PointerRNA ptr;
 +
 +                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                      RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
 +                      buf = RNA_struct_name_get_alloc(&ptr, str, maxlen, &buf_len);
 +              }
 +              else if (type == PROP_STRING) {
                        /* RNA string */
                        buf = RNA_property_string_get_alloc(&but->rnapoin, but->rnaprop, str, maxlen, &buf_len);
                }
                        MEM_freeN((void *)buf);
                }
        }
 -      else if (but->type == UI_BTYPE_TEXT) {
 -              /* string */
 -              BLI_strncpy(str, but->poin, maxlen);
 -              return;
 -      }
 -      else if (but->type == UI_BTYPE_SEARCH_MENU) {
 +      else if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
                /* string */
                BLI_strncpy(str, but->poin, maxlen);
                return;
@@@ -2363,7 -2425,7 +2363,7 @@@ static void ui_but_string_free_internal
  
  bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
  {
 -      if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
 +      if (but->rnaprop && but->rnapoin.data && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
                if (RNA_property_editable(&but->rnapoin, but->rnaprop)) {
                        PropertyType type;
  
                                return true;
                        }
                        else if (type == PROP_POINTER) {
 -                              /* RNA pointer */
 -                              PointerRNA ptr, rptr;
 -                              PropertyRNA *prop;
 -
                                if (str[0] == '\0') {
                                        RNA_property_pointer_set(&but->rnapoin, but->rnaprop, PointerRNA_NULL);
                                        return true;
                                }
                                else {
 -                                      ptr = but->rnasearchpoin;
 -                                      prop = but->rnasearchprop;
 +                                      /* RNA pointer */
 +                                      PointerRNA rptr;
 +                                      PointerRNA ptr = but->rnasearchpoin;
 +                                      PropertyRNA *prop = but->rnasearchprop;
  
                                        if (prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr))
                                                RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr);
                        }
                }
        }
 +      else if (but->type == UI_BTYPE_TAB) {
 +              if (but->rnaprop && but->custom_data) {
 +                      StructRNA *ptr_type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop);
 +                      PointerRNA ptr;
 +                      PropertyRNA *prop;
 +
 +                      /* uiBut.custom_data points to data this tab represents (e.g. workspace).
 +                       * uiBut.rnapoin/prop store an active value (e.g. active workspace). */
 +                      RNA_pointer_create(but->rnapoin.id.data, ptr_type, but->custom_data, &ptr);
 +                      prop = RNA_struct_name_property(ptr_type);
 +                      if (RNA_property_editable(&ptr, prop)) {
 +                              RNA_property_string_set(&ptr, prop, str);
 +                      }
 +              }
 +      }
        else if (but->type == UI_BTYPE_TEXT) {
                /* string */
 -              if (ui_but_is_utf8(but)) BLI_strncpy_utf8(but->poin, str, but->hardmax);
 -              else BLI_strncpy(but->poin, str, but->hardmax);
 +              if (!but->poin || (str[0] == '\0')) {
 +                      str = "";
 +              }
 +              else if (ui_but_is_utf8(but)) {
 +                      BLI_strncpy_utf8(but->poin, str, but->hardmax);
 +              }
 +              else {
 +                      BLI_strncpy(but->poin, str, but->hardmax);
 +              }
  
                return true;
        }
@@@ -2615,6 -2657,14 +2615,6 @@@ static void ui_set_but_soft_range(uiBu
  
  /* ******************* Free ********************/
  
 -static void ui_free_link(uiLink *link)
 -{
 -      if (link) {
 -              BLI_freelistN(&link->lines);
 -              MEM_freeN(link);
 -      }
 -}
 -
  /* can be called with C==NULL */
  static void ui_but_free(const bContext *C, uiBut *but)
  {
                MEM_freeN(but->hold_argN);
        }
  
 +      if (!but->editstr && but->free_search_arg) {
 +              MEM_SAFE_FREE(but->search_arg);
 +      }
 +
        if (but->active) {
                /* XXX solve later, buttons should be free-able without context ideally,
                 * however they may have open tooltips or popup windows, which need to
        if (but->str && but->str != but->strdata) {
                MEM_freeN(but->str);
        }
 -      ui_free_link(but->link);
  
        if ((but->type == UI_BTYPE_IMAGE) && but->poin) {
                IMB_freeImBuf((struct ImBuf *)but->poin);
@@@ -2696,27 -2743,6 +2696,27 @@@ void UI_block_free(const bContext *C, u
        MEM_freeN(block);
  }
  
 +void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb)
 +{
 +      ARegion *region = CTX_wm_region(C);
 +      wmWindow *window = CTX_wm_window(C);
 +
 +      for (uiBlock *block = lb->first; block; block = block->next) {
 +              if (block->active) {
 +                      ui_update_window_matrix(window, region, block);
 +              }
 +      }
 +}
 +
 +void UI_blocklist_draw(const bContext *C, const ListBase *lb)
 +{
 +      for (uiBlock *block = lb->first; block; block = block->next) {
 +              if (block->active) {
 +                      UI_block_draw(C, block);
 +              }
 +      }
 +}
 +
  /* can be called with C==NULL */
  void UI_blocklist_free(const bContext *C, ListBase *lb)
  {
@@@ -2773,6 -2799,7 +2773,6 @@@ uiBlock *UI_block_begin(const bContext 
        uiBlock *block;
        wmWindow *window;
        Scene *scn;
 -      int getsizex, getsizey;
  
        window = CTX_wm_window(C);
        scn = CTX_data_scene(C);
        if (region)
                UI_block_region_set(block, region);
  
 -      /* window matrix and aspect */
 -      if (region && region->swinid) {
 -              wm_subwindow_matrix_get(window, region->swinid, block->winmat);
 -              wm_subwindow_size_get(window, region->swinid, &getsizex, &getsizey);
 -
 -              block->aspect = 2.0f / fabsf(getsizex * block->winmat[0][0]);
 -      }
 -      else {
 -              /* no subwindow created yet, for menus for example, so we
 -               * use the main window instead, since buttons are created
 -               * there anyway */
 -              wm_subwindow_matrix_get(window, window->screen->mainwin, block->winmat);
 -              wm_subwindow_size_get(window, window->screen->mainwin, &getsizex, &getsizey);
 +      /* Set window matrix and aspect for region and OpenGL state. */
 +      ui_update_window_matrix(window, region, block);
  
 -              block->aspect = 2.0f / fabsf(getsizex * block->winmat[0][0]);
 +      /* Tag as popup menu if not created within a region. */
 +      if (!(region && region->visible)) {
                block->auto_open = true;
 -              block->flag |= UI_BLOCK_LOOP; /* tag as menu */
 +              block->flag |= UI_BLOCK_LOOP;
        }
  
        return block;
@@@ -2975,7 -3012,6 +2975,7 @@@ void ui_but_update_ex(uiBut *but, cons
  
                case UI_BTYPE_TEXT:
                case UI_BTYPE_SEARCH_MENU:
 +              case UI_BTYPE_TAB:
                        if (!but->editstr) {
                                char str[UI_MAX_DRAW_STR];
  
@@@ -3099,16 -3135,6 +3099,16 @@@ void ui_block_cm_to_display_space_range
        *max = max_fff(UNPACK3(pixel));
  }
  
 +static uiBut *ui_but_alloc(const eButType type)
 +{
 +      switch (type) {
 +              case UI_BTYPE_TAB:
 +                      return MEM_callocN(sizeof(uiButTab), "uiButTab");
 +              default:
 +                      return MEM_callocN(sizeof(uiBut), "uiBut");
 +      }
 +}
 +
  /**
   * \brief ui_def_but is the function that draws many button types
   *
@@@ -3142,7 -3168,7 +3142,7 @@@ static uiBut *ui_def_but
                }
        }
  
 -      but = MEM_callocN(sizeof(uiBut), "uiBut");
 +      but = ui_but_alloc(type & BUTTYPE);
  
        but->type = type & BUTTYPE;
        but->pointype = type & UI_BUT_POIN_TYPES;
                 ELEM(but->type,
                      UI_BTYPE_MENU, UI_BTYPE_TEXT, UI_BTYPE_LABEL,
                      UI_BTYPE_BLOCK, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU,
 -                    UI_BTYPE_PROGRESS_BAR))
 +                    UI_BTYPE_PROGRESS_BAR, UI_BTYPE_POPOVER))
        {
                but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
        }
  #ifdef USE_NUMBUTS_LR_ALIGN
        else if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) {
-               but->drawflag |= UI_BUT_TEXT_LEFT;
+               if (slen != 0) {
+                       but->drawflag |= UI_BUT_TEXT_LEFT;
+               }
        }
  #endif
  
@@@ -3532,7 -3560,7 +3534,7 @@@ static uiBut *ui_def_but_rna
        }
  
        const char *info;
 -      if (!RNA_property_editable_info(&but->rnapoin, prop, &info)) {
 +      if (but->rnapoin.data && !RNA_property_editable_info(&but->rnapoin, prop, &info)) {
                ui_def_but_rna__disable(but, info);
        }
  
@@@ -3951,6 -3979,19 +3953,6 @@@ uiBut *uiDefIconTextButO(uiBlock *block
  
  /* END Button containing both string label and icon */
  
 -void UI_but_link_set(uiBut *but, void **poin, void ***ppoin, short *tot, int from, int to)
 -{
 -      uiLink *link;
 -
 -      link = but->link = MEM_callocN(sizeof(uiLink), "new uilink");
 -
 -      link->poin = poin;
 -      link->ppoin = ppoin;
 -      link->totlink = tot;
 -      link->fromcode = from;
 -      link->tocode = to;
 -}
 -
  /* cruft to make uiBlock and uiBut private */
  
  int UI_blocklist_min_y_get(ListBase *lb)
@@@ -4418,7 -4459,7 +4420,7 @@@ static void operator_enum_search_cb(con
                for (item = item_array; item->identifier; item++) {
                        /* note: need to give the index rather than the identifier because the enum can be freed */
                        if (BLI_strcasestr(item->name, str)) {
 -                              if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), 0))
 +                              if (false == UI_search_item_add(items, item->name, SET_INT_IN_POINTER(item->value), item->icon))
                                        break;
                        }
                }
@@@ -4549,7 -4590,7 +4551,7 @@@ void UI_but_string_info_get(bContext *C
                                tmp = BLI_strdup(RNA_property_identifier(but->rnaprop));
                }
                else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) {
 -                      if (but->rnaprop)
 +                      if (but->rnaprop && but->rnapoin.data)
                                tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type));
                        else if (but->optype)
                                tmp = BLI_strdup(but->optype->idname);
                                PointerRNA *opptr = UI_but_operator_ptr_get(but);
                                wmOperatorType *ot = but->optype;
  
 +                              /* so the context is passed to itemf functions */
 +                              WM_operator_properties_sanitize(opptr, false);
 +
                                /* if the default property of the operator is enum and it is set,
                                 * fetch the tooltip of the selected value so that "Snap" and "Mirror"
                                 * operator menus in the Anim Editors will show tooltips for the different
index 01150758db404d56dae64dddf4f20ba8a747cea5,e3230d39ae2899a8924e2aa9dba1e740f84b3257..e4faac5129ee948bf8f74fa90b0879b0778dd527
  #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_immediate.h"
 +#include "GPU_immediate_util.h"
 +#include "GPU_matrix.h"
  
  #ifdef WITH_INPUT_IME
  #  include "WM_types.h"
@@@ -72,19 -72,13 +72,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
  
  typedef struct uiWidgetTrias {
        unsigned int tot;
 +      int type;
 +      float size, center[2];
  
        float vec[16][2];
        const unsigned int (*index)[3];
  } 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,
@@@ -161,15 -150,13 +161,15 @@@ static const float cornervec[WIDGET_CUR
        {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}
  };
 +#define WIDGET_AA_JITTER UI_PIXEL_AA_JITTER
 +#define jit ui_pixel_jitter
  
  /* -------------------------------------------------------------------- */
  /** \name Shape Preset Data
@@@ -208,8 -195,8 +208,8 @@@ static const uint g_shape_preset_checkm
        {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},
  };
@@@ -219,349 -206,52 +219,349 @@@ static const uint g_shape_preset_hold_a
  
  /** \} */
  
 +/* **************** 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]            = 6,
 +      [ROUNDBOX_TRIA_MENU]              = 22,
 +      [ROUNDBOX_TRIA_CHECK]             = 28,
 +      [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 34,
 +};
 +static const int tria_vcount[ROUNDBOX_TRIA_MAX] = {
 +      [ROUNDBOX_TRIA_NONE]              = 0,
 +      [ROUNDBOX_TRIA_ARROWS]            = 3,
 +      [ROUNDBOX_TRIA_SCROLL]            = 16,
 +      [ROUNDBOX_TRIA_MENU]              = 3,
 +      [ROUNDBOX_TRIA_CHECK]             = 6,
 +      [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 3,
 +};
 +
 +static struct {
 +      Gwn_Batch *roundbox_widget[ROUNDBOX_TRIA_MAX];
 +
 +      Gwn_Batch *roundbox_simple;
 +      Gwn_Batch *roundbox_simple_aa;
 +      Gwn_Batch *roundbox_simple_outline;
 +      Gwn_Batch *roundbox_shadow;
 +
 +      Gwn_VertFormat format;
 +      uint vflag_id;
 +} g_ui_batch_cache = {{0}};
 +
 +static Gwn_VertFormat *vflag_format(void)
 +{
 +      if (g_ui_batch_cache.format.attrib_ct == 0) {
 +              Gwn_VertFormat *format = &g_ui_batch_cache.format;
 +              g_ui_batch_cache.vflag_id = GWN_vertformat_attr_add(format, "vflag", GWN_COMP_U32, 1, GWN_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(
 +        Gwn_VertBufRaw *vflag_step, uint32_t d)
 +{
 +      uint32_t *data = GWN_vertbuf_raw_step(vflag_step);
 +      *data = d;
 +}
 +
 +static uint32_t set_roundbox_vertex(
 +        Gwn_VertBufRaw *vflag_step,
 +        int corner_id, int corner_v, int jit_v, bool inner, bool emboss, int color)
 +{
 +      uint32_t *data = GWN_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(
 +        Gwn_VertBufRaw *vflag_step,
 +        int tria_type, int tria_v, int tria_id, int jit_v)
 +{
 +      uint32_t *data = GWN_vertbuf_raw_step(vflag_step);
 +      if (ELEM(tria_type, ROUNDBOX_TRIA_ARROWS, ROUNDBOX_TRIA_MENU)) {
 +              tria_v += tria_id * 3;
 +      }
 +      *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(Gwn_VertBufRaw *vflag_step, int tria, uint32_t last_data)
 +{
 +      const int tria_num = ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW) ? 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);
 +                      }
 +              }
 +      }
 +}
 +
 +Gwn_Batch *ui_batch_roundbox_widget_get(int tria)
 +{
 +      if (g_ui_batch_cache.roundbox_widget[tria] == NULL) {
 +              uint32_t last_data;
 +              Gwn_VertBufRaw vflag_step;
 +              Gwn_VertBuf *vbo = GWN_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)) {
 +                              vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria2 */
 +                      }
 +              }
 +              GWN_vertbuf_data_alloc(vbo, vcount);
 +              GWN_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 */
 +              bool rev = false; /* go back and forth : avoid degenerate triangle (but beware of backface cull) */
 +              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] = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_BATCH_OWNS_VBO);
 +              gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget[tria]);
 +      }
 +      return g_ui_batch_cache.roundbox_widget[tria];
 +}
 +
 +Gwn_Batch *ui_batch_roundbox_get(bool filled, bool antialiased)
 +{
 +      Gwn_Batch **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;
 +              Gwn_VertBufRaw vflag_step;
 +              Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(vflag_format());
 +              int vcount = WIDGET_SIZE_MAX;
 +              vcount += (filled) ? 2 : 0;
 +              vcount *= (antialiased) ? WIDGET_AA_JITTER : 1;
 +              GWN_vertbuf_data_alloc(vbo, vcount);
 +              GWN_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 = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_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 = GWN_batch_create_ex(GWN_PRIM_LINE_LOOP, vbo, NULL, GWN_BATCH_OWNS_VBO);
 +              }
 +
 +              gpu_batch_presets_register(*batch);
 +      }
 +      return *batch;
 +}
 +
 +Gwn_Batch *ui_batch_roundbox_shadow_get(void)
 +{
 +      if (g_ui_batch_cache.roundbox_shadow == NULL) {
 +              uint32_t last_data;
 +              Gwn_VertBufRaw vflag_step;
 +              Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(vflag_format());
 +              int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX;
 +              GWN_vertbuf_data_alloc(vbo, vcount);
 +              GWN_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 = GWN_batch_create_ex(GWN_PRIM_TRI_STRIP, vbo, NULL, GWN_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;
  
        glEnable(GL_BLEND);
 -      glGetFloatv(GL_CURRENT_COLOR, color);
 -      color[3] *= 0.125f;
 -      glColor4fv(color);
  
 -      glEnableClientState(GL_VERTEX_ARRAY);
 -      glVertexPointer(2, GL_FLOAT, 0, tri_arr);
 +      unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +      immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +
 +      immUniformColor4fv(draw_color);
 +      immBegin(GWN_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);
 +      immEnd();
 +
 +      immUnbindProgram();
 +
        glDisable(GL_BLEND);
  }
  
 -void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha)
 +void UI_draw_anti_fan(float tri_array[][2], unsigned int length, const float color[4])
  {
 -      float color[4];
 -      int j;
 +      float draw_color[4];
 +
 +      copy_v4_v4(draw_color, color);
 +      draw_color[3] *= 2.0f / WIDGET_AA_JITTER;
  
        glEnable(GL_BLEND);
 -      glGetFloatv(GL_CURRENT_COLOR, color);
 -      if (use_alpha) {
 -              color[3] = 0.5f;
 -      }
 -      color[3] *= 0.125f;
 -      glColor4fv(color);
  
 -      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);
 +      unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_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(GWN_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]);
 +              }
 +
 +              immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]);
 +              immEnd();
        }
  
 +      immUnbindProgram();
 +
        glDisable(GL_BLEND);
  }
  
@@@ -570,16 -260,11 +570,16 @@@ static void widget_init(uiWidgetBase *w
        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 */
@@@ -687,17 -372,6 +687,17 @@@ static void round_box__edges(uiWidgetBa
        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];
@@@ -842,30 -516,23 +842,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;
        }
                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),
  
  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),
  
  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(unsigned int pos, unsigned int col, int mode,
 +                                      const float quads_pos[WIDGET_SIZE_MAX][2],
 +                                      const unsigned char quads_col[WIDGET_SIZE_MAX][4],
 +                                      unsigned int 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)
 +                      immAttrib4ubv(col, quads_col[i]);
 +              immVertex2fv(pos, quads_pos[i]);
 +      }
 +      immEnd();
  }
  
  static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect)
  {
        float centx, centy, size;
        int a;
 +      tria->type = ROUNDBOX_TRIA_MENU;
  
        /* 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->center[0] = centx = rect->xmax - 0.32f * BLI_rcti_size_y(rect);
 +      tria->center[1] = centy = rect->ymin + 0.50f * BLI_rcti_size_y(rect);
 +      tria->size = size = 0.4f * BLI_rcti_size_y(rect);
  
        for (a = 0; a < 6; a++) {
                tria->vec[a][0] = size * g_shape_preset_menu_arrow_vert[a][0] + centx;
@@@ -952,12 -601,11 +952,12 @@@ static void shape_preset_trias_from_rec
  {
        float centx, centy, size;
        int a;
 +      tria->type = ROUNDBOX_TRIA_CHECK;
  
        /* 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->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++) {
                tria->vec[a][0] = size * g_shape_preset_checkmark_vert[a][0] + centx;
@@@ -1007,210 -655,180 +1007,210 @@@ static void widget_verts_to_triangle_st
        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, unsigned int 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(uiWidgetBase *wtb, uiWidgetColors *wcol)
 +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 (wcol->alpha_check) {
 -                              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 unsigned char *col1, const unsigned char *col2,
 +        const unsigned char *outline,
 +        const unsigned char *emboss,
 +        const unsigned char *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);
 +struct {
 +      Gwn_Batch *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((unsigned char *)wcol->inner);
 +      Gwn_Batch *batch = g_widget_base_batch.batch;
 +      if (g_widget_base_batch.count == 1) {
 +              /* draw single */
 +              GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
 +              GWN_batch_uniform_4fv_array(batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)g_widget_base_batch.params);
 +              GWN_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
 +              GWN_batch_draw(batch);
 +      }
 +      else {
 +              GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE_INST);
 +              GWN_batch_uniform_4fv_array(batch, "parameters", MAX_WIDGET_PARAMETERS * MAX_WIDGET_BASE_BATCH,
 +                                          (float *)g_widget_base_batch.params);
 +              GWN_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
 +              gpuBindMatrices(batch->interface);
 +              GWN_batch_draw_range_ex(batch, 0, g_widget_base_batch.count, true);
 +              GWN_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);
 +      glEnable(GL_BLEND);
  
 -                              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((unsigned char *)wcol->inner);
 +      glDisable(GL_BLEND);
 +}
  
 -                              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(Gwn_Batch *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];
 -                      unsigned char col_array[WIDGET_SIZE_MAX * 4];
 -                      unsigned char *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 unsigned char tcol[4] = {wcol->outline[0],
 -                                             wcol->outline[1],
 -                                             wcol->outline[2],
 -                                             wcol->outline[3] / WIDGET_AA_JITTER};
 -              unsigned char 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 */
 +              GWN_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE);
 +              GWN_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&wtb->uniform_params);
 +              GWN_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params);
 +              GWN_batch_draw(batch);
 +      }
 +}
  
 -              glEnableClientState(GL_VERTEX_ARRAY);
 -
 -              for (j = 0; j < WIDGET_AA_JITTER; j++) {
 -                      glTranslate2fv(jit[j]);
 +static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol)
 +{
 +      unsigned char inner_col1[4] = {0};
 +      unsigned char inner_col2[4] = {0};
 +      unsigned char emboss_col[4] = {0};
 +      unsigned char outline_col[4] = {0};
 +      unsigned char tria_col[4] = {0};
 +      /* For color widget. */
 +      bool alpha_check = (wcol->alpha_check && (wcol->shaded == 0));
  
 -                      /* outline */
 -                      glColor4ubv(tcol);
 +      glEnable(GL_BLEND);
  
 -                      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] = (unsigned char)wcol->inner[0];
 +                      inner_col1[1] = inner_col2[1] = (unsigned char)wcol->inner[1];
 +                      inner_col1[2] = inner_col2[2] = (unsigned char)wcol->inner[2];
 +                      inner_col1[3] = inner_col2[3] = (unsigned char)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] = (unsigned char)((float)wcol->item[3] / WIDGET_AA_JITTER);
        }
  
 -      /* decoration */
 -      if (wtb->tria1.tot || wtb->tria2.tot) {
 -              const unsigned char tcol[4] = {wcol->item[0],
 -                                             wcol->item[1],
 -                                             wcol->item[2],
 -                                             (unsigned char)((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] || alpha_check) {
 +              widgetbase_set_uniform_colors_ubv(wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, alpha_check);
  
 -                      glTranslatef(-jit[j][0], -jit[j][1], 0.0f);
 -              }
 +              Gwn_Batch *roundbox_batch = ui_batch_roundbox_widget_get(wtb->tria1.type);
 +              draw_widgetbase_batch(roundbox_batch, wtb);
        }
  
        glDisable(GL_BLEND);
@@@ -1295,7 -913,7 +1295,7 @@@ static void widget_draw_icon_ex
  
                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;
                        float rgb[3] = {1.25f, 1.25f, 1.25f};
                        UI_icon_draw_aspect_color(xs, ys, icon, aspect, rgb);
                }
 -              else
 +              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);
 +              }
 +              else {
 +                      const bTheme *btheme = UI_GetTheme();
 +                      UI_icon_draw_desaturate(xs, ys, icon, aspect, alpha, 1.0 - btheme->tui.icon_saturation);
 +              }
        }
  
        if (show_menu_icon) {
@@@ -1509,7 -1122,7 +1509,7 @@@ float UI_text_clip_middle_ex
  static void ui_text_clip_middle(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;
  static void ui_text_clip_middle_protect_right(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;
@@@ -1677,7 -1290,6 +1677,7 @@@ static void widget_draw_text_ime_underl
        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) {
                width = BLF_width(fstyle->uifont_id, drawstr + but->ofs,
                                  ime_data->composite_len + but->pos - but->ofs);
  
 -              glColor4ubv((unsigned char *)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) {
                        width = BLF_width(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);
                }
        }
  }
@@@ -1782,10 -1394,6 +1782,10 @@@ static void widget_draw_text(uiFontStyl
                        int selsta_draw, selwidth_draw;
  
                        if (drawstr[0] != 0) {
 +                              /* We are drawing on top of widget bases. Flush cache. */
 +                              glEnable(GL_BLEND);
 +                              UI_widgetbase_draw_cache_flush();
 +                              glDisable(GL_BLEND);
  
                                if (but->selsta >= but->ofs) {
                                        selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs);
  
                                selwidth_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selend - but->ofs);
  
 -                              glColor4ubv((unsigned char *)wcol->item);
 -                              glRecti(rect->xmin + selsta_draw,
 -                                      rect->ymin + 2,
 -                                      min_ii(rect->xmin + selwidth_draw, rect->xmax - 2),
 -                                      rect->ymax - 2);
 +                              unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
 +                              immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +
 +                              immUniformColor4ubv((unsigned char *)wcol->item);
 +                              immRecti(pos, rect->xmin + selsta_draw,
 +                                       rect->ymin + 2,
 +                                       min_ii(rect->xmin + selwidth_draw, rect->xmax - 2),
 +                                       rect->ymax - 2);
 +
 +                              immUnbindProgram();
                        }
                }
  
                        else {
                                t = 0;
                        }
 +                      /* We are drawing on top of widget bases. Flush cache. */
 +                      glEnable(GL_BLEND);
 +                      UI_widgetbase_draw_cache_flush();
 +                      glDisable(GL_BLEND);
 +
 +                      unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_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
  #endif
  
        /* cut string in 2 parts - only for menu entries */
 -      if ((but->block->flag & UI_BLOCK_LOOP) &&
 +      if ((but->block->flag & (UI_BLOCK_LOOP | UI_BLOCK_SHOW_SHORTCUT_ALWAYS)) &&
            (but->editstr == NULL))
        {
                if (but->flag & UI_BUT_HAS_SEP_CHAR) {
        }
  
  #ifdef USE_NUMBUTS_LR_ALIGN
-       if (!drawstr_right && ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
+       if (!drawstr_right &&
+           (but->drawflag & UI_BUT_TEXT_LEFT) &&
+           ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
            /* if we're editing or multi-drag (fake editing), then use left alignment */
            (but->editstr == NULL) && (drawstr == but->drawstr))
        {
        }
  #endif
  
 -      glColor4ubv((unsigned char *)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, (unsigned char *)wcol->text,
 +                                           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);
  
 -                              fixedbuf[ul_index] = '\0';
 -                              ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index);
 +                                      if (fstyle->kerning == 1) {
 +                                              BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
 +                                      }
  
 -                              BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f);
 -                              BLF_draw(fstyle->uifont_id, "_", 2);
 +                                      fixedbuf[ul_index] = '\0';
 +                                      ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index);
  
 -                              if (fstyle->kerning == 1) {
 -                                      BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
 +                                      BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f);
 +                                      BLF_color4ubv(fstyle->uifont_id, (unsigned char *)wcol->text);
 +                                      BLF_draw(fstyle->uifont_id, "_", 2);
 +
 +                                      if (fstyle->kerning == 1) {
 +                                              BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
 +                                      }
                                }
                        }
                }
        if (drawstr_right) {
                fstyle->align = UI_STYLE_TEXT_RIGHT;
                rect->xmax -= UI_TEXT_CLIP_MARGIN;
 -              UI_fontstyle_draw(fstyle, rect, drawstr_right);
 +              UI_fontstyle_draw(fstyle, rect, drawstr_right, (unsigned char *)wcol->text);
        }
  }
  
@@@ -1966,7 -1559,7 +1968,7 @@@ static void widget_draw_text_icon(uiFon
        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);
        }
        /* Icons on the left with optional text label on the right */
        else if (but->flag & UI_HAS_ICON || show_menu_icon) {
 +              const bool is_tool = UI_but_is_tool(but);
 +
                const BIFIconID icon = (but->flag & UI_HAS_ICON) ? but->icon + but->iconadd : ICON_NONE;
 -              const float icon_size = ICON_DEFAULT_WIDTH_SCALE;
 +              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);
 +
 +#ifdef USE_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;
                }
        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
@@@ -2106,8 -1682,6 +2108,8 @@@ static struct uiWidgetStateColors wcol_
        {215, 211, 75, 255},
        {180, 0, 255, 255},
        {153, 0, 230, 255},
 +      {74, 137, 137, 255},
 +      {49, 112, 112, 255},
        0.5f, 0.0f
  };
  
@@@ -2121,9 -1695,7 +2123,9 @@@ static struct uiWidgetColors wcol_num 
        {255, 255, 255, 255},
  
        1,
 -      -20, 0
 +      -20, 0,
 +      0,
 +      0.5f,
  };
  
  static struct uiWidgetColors wcol_numslider = {
        {255, 255, 255, 255},
  
        1,
 -      -20, 0
 +      -20, 0,
 +      0,
 +      0.5f,
  };
  
  static struct uiWidgetColors wcol_text = {
        {255, 255, 255, 255},
  
        1,
 -      0, 25
 +      0, 25,
 +      0,
 +      0.2f,
  };
  
  static struct uiWidgetColors wcol_option = {
        {255, 255, 255, 255},
  
        1,
 -      15, -15
 +      15, -15,
 +      0,
 +      0.3333333f,
  };
  
  /* button that shows popup */
@@@ -2182,9 -1748,7 +2184,9 @@@ static struct uiWidgetColors wcol_menu 
        {204, 204, 204, 255},
  
        1,
 -      15, -15
 +      15, -15,
 +      0,
 +      0.2f,
  };
  
  /* button that starts pulldown */
@@@ -2198,9 -1762,7 +2200,9 @@@ static struct uiWidgetColors wcol_pulld
        {0, 0, 0, 255},
  
        0,
 -      25, -20
 +      25, -20,
 +      0,
 +      0.2f,
  };
  
  /* button inside menu */
@@@ -2214,9 -1776,7 +2216,9 @@@ static struct uiWidgetColors wcol_menu_
        {0, 0, 0, 255},
  
        1,
 -      38, 0
 +      38, 0,
 +      0,
 +      0.2f,
  };
  
  /* backdrop menu + title text color */
@@@ -2230,9 -1790,7 +2232,9 @@@ static struct uiWidgetColors wcol_menu_
        {255, 255, 255, 255},
  
        0,
 -      25, -20
 +      25, -20,
 +      0,
 +      0.25f,
  };
  
  /* pie menus */
@@@ -2246,9 -1804,7 +2248,9 @@@ static struct uiWidgetColors wcol_pie_m
        {255, 255, 255, 255},
  
        1,
 -      10, -10
 +      10, -10,
 +      0,
 +      0.5f,
  };
  
  
@@@ -2263,9 -1819,7 +2265,9 @@@ static struct uiWidgetColors wcol_toolt
        {255, 255, 255, 255},
  
        0,
 -      25, -20
 +      25, -20,
 +      0,
 +      0.25f,
  };
  
  static struct uiWidgetColors wcol_radio = {
        {0, 0, 0, 255},
  
        1,
 -      15, -15
 +      15, -15,
 +      0,
 +      0.2f,
  };
  
  static struct uiWidgetColors wcol_regular = {
        {255, 255, 255, 255},
  
        0,
 -      0, 0
 +      0, 0,
 +      0,
 +      0.25f,
  };
  
  static struct uiWidgetColors wcol_tool = {
        {255, 255, 255, 255},
  
        1,
 -      15, -15
 +      15, -15,
 +      0,
 +      0.2f,
 +};
 +
 +static struct uiWidgetColors wcol_toolbar_item = {
 +      .outline = {0x19, 0x19, 0x19, 0xff},
 +      .inner = {0x46, 0x46, 0x46, 0xff},
 +      .inner_sel = {0xcc, 0xcc, 0xcc, 0xff},
 +      .item = {0x0, 0x0, 0x0, 0xff},
 +
 +      .text = {0xff, 0xff, 0xff, 0xff},
 +      .text_sel = {0x33, 0x33, 0x33, 0xff},
 +
 +      .shaded = 0,
 +      .shadetop = 0,
 +      .shadedown = 0,
 +      .alpha_check = 0,
 +      .roundness = 0.3f,
  };
  
  static struct uiWidgetColors wcol_box = {
        {255, 255, 255, 255},
  
        0,
 -      0, 0
 +      0, 0,
 +      0,
 +      0.2f,
  };
  
  static struct uiWidgetColors wcol_toggle = {
        {255, 255, 255, 255},
  
        0,
 -      0, 0
 +      0, 0,
 +      0,
 +      0.25f,
  };
  
  static struct uiWidgetColors wcol_scroll = {
        {255, 255, 255, 255},
  
        1,
 -      5, -5
 +      5, -5,
 +      0,
 +      0.5f,
  };
  
  static struct uiWidgetColors wcol_progress = {
        {255, 255, 255, 255},
  
        0,
 -      0, 0
 +      0, 0,
 +      0,
 +      0.25f,
  };
  
  static struct uiWidgetColors wcol_list_item = {
        {255, 255, 255, 255},
  
        0,
 -      0, 0
 +      0, 0,
 +      0,
 +      0.2f,
 +};
 +
 +struct uiWidgetColors wcol_tab = {
 +      {60, 60, 60, 255},
 +      {83, 83, 83, 255},
 +      {114, 114, 114, 255},
 +      {90, 90, 90, 255},
 +
 +      {0, 0, 0, 255},
 +      {0, 0, 0, 255},
 +
 +      0,
 +      0, 0,
 +      0,
 +      0.25f,
  };
  
  /* free wcol struct to play with */
@@@ -2430,9 -1937,7 +2432,9 @@@ static struct uiWidgetColors wcol_tmp 
        {255, 255, 255, 255},
  
        0,
 -      0, 0
 +      0, 0,
 +      0,
 +      0.25f,
  };
  
  
@@@ -2441,10 -1946,8 +2443,10 @@@ void ui_widget_color_init(ThemeUI *tui
  {
        tui->wcol_regular = wcol_regular;
        tui->wcol_tool = wcol_tool;
 +      tui->wcol_toolbar_item = wcol_toolbar_item;
        tui->wcol_text = wcol_text;
        tui->wcol_radio = wcol_radio;
 +      tui->wcol_tab = wcol_tab;
        tui->wcol_option = wcol_option;
        tui->wcol_toggle = wcol_toggle;
        tui->wcol_num = wcol_num;
@@@ -2491,13 -1994,6 +2493,13 @@@ static void ui_widget_color_disabled(ui
        wt->wcol_theme = &wcol_theme_s;
  }
  
 +static void widget_active_color(char cp[3])
 +{
 +      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)
  {
                        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);
  
                        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);
                }
        }
  
@@@ -2584,8 -2078,6 +2586,8 @@@ static void widget_state_numslider(uiWi
                        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);
                        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);
        }
  }
  
@@@ -2717,23 -2207,22 +2719,23 @@@ static void widget_softshadow(const rct
        /* we draw a number of increasing size alpha quad strips */
        alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout;
  
 -      glEnableClientState(GL_VERTEX_ARRAY);
 +      unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_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)
        }
  
        glEnable(GL_BLEND);
 -      widget_softshadow(rect, roundboxalign, 0.25f * U.widget_unit);
 +      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);
  }
  
 -
  static void ui_hsv_cursor(float x, float y)
  {
 -      glPushMatrix();
 -      glTranslatef(x, y, 0.0f);
 +      unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
  
 -      glColor3f(1.0f, 1.0f, 1.0f);
 -      glutil_draw_filled_arc(0.0f, M_PI * 2.0, 3.0f * U.pixelsize, 8);
 +      immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +
 +      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);
 +      immUniformColor3f(0.0f, 0.0f, 0.0f);
 +      imm_draw_circle_wire_2d(pos, x, y, 3.0f * U.pixelsize, 12);
        glDisable(GL_BLEND);
        glDisable(GL_LINE_SMOOTH);
  
 -      glPopMatrix();
 +      immUnbindProgram();
  }
  
  void ui_hsvcircle_vals_from_pos(float *val_rad, float *val_dist, const rcti *rect,
@@@ -2823,17 -2312,18 +2825,17 @@@ void ui_hsvcircle_pos_from_vals(uiBut *
  
  static void ui_draw_but_HSVCIRCLE(uiBut *but, 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_color_picker_to_rgb(0.0f, 0.0f, hsv[2], colcent, colcent + 1, colcent + 2);
  
 -      glBegin(GL_TRIANGLE_FAN);
 -      glColor3fv(colcent);
 -      glVertex2f(centx, centy);
 +      Gwn_VertFormat *format = immVertexFormat();
 +      unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +      unsigned int color = GWN_vertformat_attr_add(format, "color", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
 +
 +      immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
 +
 +      immBegin(GWN_PRIM_TRI_FAN, tot + 2);
 +      immAttrib3fv(color, colcent);
 +      immVertex2f(pos, centx, centy);
  
 -      for (a = 0; a <= tot; a++, ang += radstep) {
 +      float ang = 0.0f;
 +      for (int a = 0; a <= tot; a++, ang += radstep) {
                float si = sinf(ang);
                float co = cosf(ang);
  
  
                ui_color_picker_to_rgb_v(hsv, col);
  
 -              glColor3fv(col);
 -              glVertex2f(centx + co * radius, centy + si * radius);
 +              immAttrib3fv(color, col);
 +              immVertex2f(pos, centx + co * radius, centy + si * radius);
        }
 -      glEnd();
 +      immEnd();
 +      immUnbindProgram();
  
        /* fully rounded outline */
 -      glPushMatrix();
 -      glTranslatef(centx, centy, 0.0f);
 +      format = immVertexFormat();
 +      pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +
 +      immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +
        glEnable(GL_BLEND);
        glEnable(GL_LINE_SMOOTH);
 -      glColor3ubv((unsigned char *)wcol->outline);
 -      glutil_draw_lined_arc(0.0f, M_PI * 2.0, radius, tot + 1);
 +
 +      immUniformColor3ubv((unsigned char *)wcol->outline);
 +      imm_draw_circle_wire_2d(pos, centx, centy, radius, tot);
 +
 +      immUnbindProgram();
 +
        glDisable(GL_BLEND);
        glDisable(GL_LINE_SMOOTH);
 -      glPopMatrix();
  
        /* cursor */
 +      float xpos, ypos;
        ui_hsvcircle_pos_from_vals(but, rect, hsvo, &xpos, &ypos);
 -
        ui_hsv_cursor(xpos, ypos);
  }
  
  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;
        }
  
        /* old below */
 +      Gwn_VertFormat *format = immVertexFormat();
 +      unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +      unsigned int col = GWN_vertformat_attr_add(format, "color", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
 +      immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
  
 +      immBegin(GWN_PRIM_TRIS, steps * 3 * 6);
        for (dx = 0.0f; dx < 0.999f; dx += color_step) { /* 0.999 = prevent float inaccuracy for steps */
                const float dx_next = dx + color_step;
  
                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);
 +                      immAttrib4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
 +                      immVertex2f(pos, sx1, sy);
 +
 +                      immAttrib4f(col, col1[a][0], col1[a][1], col1[a][2], alpha);
 +                      immVertex2f(pos, sx2, sy);
 +
 +                      immAttrib4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
 +                      immVertex2f(pos, sx2, sy + dy);
  
 -                      glColor4f(col1[a][0], col1[a][1], col1[a][2], alpha);
 -                      glVertex2f(sx2, sy);
 +                      immAttrib4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
 +                      immVertex2f(pos, sx1, sy);
  
 -                      glColor4f(col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
 -                      glVertex2f(sx2, sy + dy);
 +                      immAttrib4f(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);
 +                      immAttrib4f(col, col0[a + 1][0], col0[a + 1][1], col0[a + 1][2], alpha);
 +                      immVertex2f(pos, sx1, sy + dy);
                }
 -              glEnd();
        }
 +      immEnd();
 +
 +      immUnbindProgram();
  }
  
  bool ui_but_is_colorpicker_display_space(uiBut *but)
@@@ -3136,20 -2599,15 +3138,20 @@@ static void ui_draw_but_HSVCUBE(uiBut *
        ui_hsv_cursor(x, y);
  
        /* outline */
 -      glColor3ub(0,  0,  0);
 -      fdrawbox((rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
 +      unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_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;
  
        widgetbase_draw(&wtb, &wcol_tmp);
  
 +      /* We are drawing on top of widget bases. Flush cache. */
 +      glEnable(GL_BLEND);
 +      UI_widgetbase_draw_cache_flush();
 +      glDisable(GL_BLEND);
 +
        /* cursor */
        x = rect->xmin + 0.5f * BLI_rcti_size_x(rect);
        y = rect->ymin + v    * BLI_rcti_size_y(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. */
 +      glEnable(GL_BLEND);
 +      UI_widgetbase_draw_cache_flush();
 +      glDisable(GL_BLEND);
 +}
 +
  
  /* ************ separator, for menus etc ***************** */
  static void ui_draw_separator(const rcti *rect,  uiWidgetColors *wcol)
                30
        };
  
 +      unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +      immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +
        glEnable(GL_BLEND);
 -      glColor4ubv(col);
 +      immUniformColor4ubv(col);
        glLineWidth(1.0f);
 -      sdrawline(rect->xmin, y, rect->xmax, y);
 +
 +      immBegin(GWN_PRIM_LINES, 2);
 +      immVertex2f(pos, rect->xmin, y);
 +      immVertex2f(pos, rect->xmax, y);
 +      immEnd();
 +
        glDisable(GL_BLEND);
 +
 +      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);
        }
  
        /* 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;
@@@ -3361,6 -2724,53 +3363,6 @@@ static void widget_numbut_embossn(uiBu
        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)
  {
        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)
@@@ -3511,7 -2921,7 +3513,7 @@@ static void widget_progressbar(uiBut *b
  
        /* 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 */
        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;
@@@ -3643,12 -3064,13 +3645,12 @@@ static void widget_swatch(uiBut *but, u
  
        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)) {
                /* draw based on state - color for keyed etc */
                widgetbase_draw(&wtb, wcol);
  
        }
  
        widgetbase_draw(&wtb, wcol);
 -
        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;
  
                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. */
 +              glEnable(GL_BLEND);
 +              UI_widgetbase_draw_cache_flush();
 +              glDisable(GL_BLEND);
 +
 +              unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +              immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +
 +              immUniformColor3f(bw, bw, bw);
 +              immBegin(GWN_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();
        }
  }
  
@@@ -3720,7 -3133,8 +3722,7 @@@ static void widget_icon_has_anim(uiBut 
                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);
        }
@@@ -3742,7 -3156,8 +3744,7 @@@ static void widget_textbut(uiWidgetColo
  
        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);
@@@ -3756,13 -3171,12 +3758,13 @@@ static void widget_menubut(uiWidgetColo
  
        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);
  
@@@ -3777,7 -3191,8 +3779,7 @@@ static void widget_menuiconbut(uiWidget
  
        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 */
@@@ -3793,7 -3208,8 +3795,7 @@@ static void widget_menunodebut(uiWidget
  
        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);
@@@ -3812,7 -3228,7 +3814,7 @@@ static void widget_pulldownbut(uiWidget
  {
        if (state & UI_ACTIVE) {
                uiWidgetBase wtb;
 -              const float rad = 0.2f * U.widget_unit;
 +              const float rad = wcol->roundness * U.widget_unit;
  
                widget_init(&wtb);
  
@@@ -3846,7 -3262,7 +3848,7 @@@ static void widget_menu_radial_itembut(
  
        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;
@@@ -3866,9 -3282,9 +3868,9 @@@ static void widget_list_itembut(uiWidge
  
        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);
@@@ -3893,7 -3309,8 +3895,7 @@@ static void widget_optionbut(uiWidgetCo
        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 */
@@@ -3934,7 -3351,8 +3936,7 @@@ static void widget_radiobut(uiWidgetCol
  
        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);
@@@ -3957,7 -3375,8 +3959,7 @@@ static void widget_box(uiBut *but, uiWi
                wcol->inner[2] = but->col[2];
        }
  
 -      /* 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);
@@@ -3972,7 -3391,8 +3974,7 @@@ static void widget_but(uiWidgetColors *
  
        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);
  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);
  
  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);
  
        widgetbase_draw(&wtb, wcol);
  }
  
 +static void widget_tab(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
 +{
 +      const uiStyle *style = UI_style_get();
 +      const float rad = wcol->roundness * U.widget_unit;
 +      const int fontid = style->widget.uifont_id;
 +      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;
 +      unsigned char 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, (unsigned char *)wcol->inner_sel,
 +                                   (unsigned char *)wcol->outline, 0.2f);
 +      }
 +      else {
 +              interp_v3_v3v3_uchar(theme_col_tab_highlight, (unsigned char *)wcol->inner,
 +                                   (unsigned char *)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. */
 +      glEnable(GL_BLEND);
 +      UI_widgetbase_draw_cache_flush();
 +      glDisable(GL_BLEND);
 +
 +#ifdef USE_TAB_SHADED_HIGHLIGHT
 +      /* draw outline (3d look) */
 +      ui_draw_but_TAB_outline(rect, rad, theme_col_tab_highlight, (unsigned char *)wcol->inner);
 +#endif
 +
 +      /* text shadow */
 +      BLF_enable(fontid, BLF_SHADOW);
 +      BLF_shadow(fontid, 3, (const float[4]){1.0f, 1.0f, 1.0f, 0.25f});
 +      BLF_shadow_offset(fontid, 0, -1);
 +
 +#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;
        unsigned char col[4];
  
        /* state copy! */
                /* 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);
  
 +              unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_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 */
@@@ -4156,16 -3513,6 +4158,16 @@@ static uiWidgetType *widget_type(uiWidg
                        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;
@@@ -4288,7 -3635,7 +4290,7 @@@ static int widget_roundbox_set(uiBut *b
        /* 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))
        }
  
        /* 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);
@@@ -4350,10 -3697,6 +4352,10 @@@ void ui_draw_but(const bContext *C, ARe
        uiFontStyle *fstyle = &style->widget;
        uiWidgetType *wt = NULL;
  
 +#ifdef USE_POPOVER_ONCE
 +      const rcti rect_orig = *rect;
 +#endif
 +
        /* handle menus separately */
        if (but->dt == UI_EMBOSS_PULLDOWN) {
                switch (but->type) {
                                break;
  
                        case UI_BTYPE_BUT:
 +#ifdef USE_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:
                                        wt->wcol_theme = &btheme->tui.wcol_menu_back;
                                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:
  
                        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);
                                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;
                        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;
  
                if (disabled)
                        glEnable(GL_BLEND);
 +
 +#ifdef USE_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);
 +                              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);
        }
  }
  
 +static void ui_draw_clip_tri(uiBlock *block, rcti *rect, uiWidgetType *wt)
 +{
 +      if (block) {
 +              float draw_color[4];
 +              unsigned char *color = (unsigned char *)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);
        else
                wt->draw(&wt->wcol, rect, 0, 0);
  
 -      if (block) {
 -              if (block->flag & UI_BLOCK_CLIPTOP) {
 -                      /* XXX no scaling for UI here yet */
 -                      glColor3ubv((unsigned char *)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 mval_origin[2])
 +{
 +      /* tsk, this isn't nice. */
 +      const float unit_half = (BLI_rcti_size_x(rect) / UI_POPOVER_WIDTH_UNITS) / 2;
 +      const float cent_x = mval_origin ? mval_origin[0] : BLI_rcti_cent_x(rect);
 +      rect->ymax -= unit_half;
 +      rect->ymin += unit_half;
 +
 +      glEnable(GL_BLEND);
 +
 +      /* 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)) {
 +              unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +              immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +              immUniformColor4ubv((unsigned char *)wcol->inner);
 +              glEnable(GL_BLEND);
 +              immBegin(GWN_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((unsigned char *)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();
        }
 +
 +      glDisable(GL_BLEND);
 +}
 +
 +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, mval_origin);
 +      }
 +      else {
 +              wt->state(wt, 0);
 +              wt->draw(&wt->wcol, rect, 0, 0);
 +      }
 +
 +      ui_draw_clip_tri(block, rect, wt);
  }
  
  static void draw_disk_shaded(
        float y1, y2;
        float fac;
        unsigned char r_col[4];
 +      unsigned int pos, col;
  
 -      glBegin(GL_TRIANGLE_STRIP);
 -
 -      s = sinf(start);
 -      c = cosf(start);
 -
 -      y1 = s * radius_int;
 -      y2 = s * radius_ext;
 -
 +      Gwn_VertFormat *format = immVertexFormat();
 +      pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_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 = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 4, GWN_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((unsigned char *)col1);
        }
 -      glVertex2f(c * radius_ext, s * radius_ext);
  
 -      for (i = 1; i < subd; i++) {
 +      immBegin(GWN_PRIM_TRI_STRIP, subd * 2);
 +      for (i = 0; i < subd; i++) {
                float a;
  
                a = start + ((i) / (float)(subd - 1)) * angle;
                if (shaded) {
                        fac = (y1 + radius_ext) * radius_ext_scale;
                        round_box_shade_col4_r(r_col, col1, col2, fac);
 -
 -                      glColor4ubv(r_col);
 +                      immAttrib4ubv(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);
 +                      immAttrib4ubv(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)
        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);
 +      gpuPushMatrix();
 +      gpuTranslate2f(cx, cy);
  
        glEnable(GL_BLEND);
        if (btheme->tui.wcol_pie_menu.shaded) {
                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)) {
                        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);
 +      Gwn_VertFormat *format = immVertexFormat();
 +      unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +      immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +      immUniformColor4ubv((unsigned char *)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);
  
 -              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);
 +              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};
 +
 +              draw_disk_shaded(angle - range / 2.0f, range, pie_confirm_radius, pie_confirm_external, subd, col, NULL, false);
        }
  
        glDisable(GL_BLEND);
 -      glPopMatrix();
 +      gpuPopMatrix();
  }
  
  
@@@ -4967,7 -4196,8 +4969,7 @@@ void ui_draw_menu_item(uiFontStyle *fst
                        UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0');
                }
  
 -              glColor4ubv((unsigned char *)wt->wcol.text);
 -              UI_fontstyle_draw(fstyle, rect, drawstr);
 +              UI_fontstyle_draw(fstyle, rect, drawstr, (unsigned char *)wt->wcol.text);
        }
  
        /* part text right aligned */
                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, (unsigned char *)wt->wcol.text);
                        *cpoin = UI_SEP_CHAR;
                }
        }
@@@ -5033,7 -4263,8 +5035,7 @@@ void ui_draw_preview_item(uiFontStyle *
                BLI_strncpy(drawstr, name, sizeof(drawstr));
                UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0');
  
 -              glColor4ubv((unsigned char *)wt->wcol.text);
 -              UI_fontstyle_draw(fstyle, &trect, drawstr);
 +              UI_fontstyle_draw(fstyle, &trect, drawstr, (unsigned char *)wt->wcol.text);
        }
  }