Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Mon, 7 May 2018 16:02:48 +0000 (18:02 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 7 May 2018 16:02:48 +0000 (18:02 +0200)
15 files changed:
1  2 
source/blender/alembic/intern/abc_mesh.cc
source/blender/blenkernel/intern/dynamicpaint.c
source/blender/blenlib/intern/math_color.c
source/blender/collada/MeshImporter.cpp
source/blender/editors/interface/interface_templates.c
source/blender/editors/interface/interface_widgets.c
source/blender/editors/interface/resources.c
source/blender/editors/sculpt_paint/paint_image_2d.c
source/blender/editors/sculpt_paint/paint_image_proj.c
source/blender/gpu/intern/gpu_buffers.c
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_image.c
source/blender/modifiers/intern/MOD_particleinstance.c
source/blender/render/intern/source/multires_bake.c

index 52d14f847285dca13d0e2234e85f7f5e90ad2ff5,ef0a823ac3e7efe96b9024ec83a56f6cc821e491..56fa0a93532f36e74f5b0366d9bf7930e497fd84
@@@ -38,6 -38,7 +38,6 @@@ extern "C" 
  #include "BLI_string.h"
  
  #include "BKE_cdderivedmesh.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_main.h"
  #include "BKE_material.h"
  #include "BKE_mesh.h"
@@@ -286,13 -287,12 +286,13 @@@ static ModifierData *get_liquid_sim_mod
  
  /* ************************************************************************** */
  
 -AbcMeshWriter::AbcMeshWriter(Scene *scene,
 +AbcMeshWriter::AbcMeshWriter(Depsgraph *depsgraph,
 +                             Scene *scene,
                               Object *ob,
                               AbcTransformWriter *parent,
                               uint32_t time_sampling,
                               ExportSettings &settings)
 -    : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
 +    : AbcObjectWriter(depsgraph, scene, ob, time_sampling, settings, parent)
  {
        m_is_animated = isAnimated();
        m_subsurf_mod = NULL;
@@@ -525,7 -525,7 +525,7 @@@ DerivedMesh *AbcMeshWriter::getFinalMes
                m_subsurf_mod->mode |= eModifierMode_DisableTemporary;
        }
  
 -      DerivedMesh *dm = mesh_create_derived_render(m_scene, m_object, CD_MASK_MESH);
 +      DerivedMesh *dm = mesh_create_derived_render(m_depsgraph, m_scene, m_object, CD_MASK_MESH);
  
        if (m_subsurf_mod) {
                m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
@@@ -923,6 -923,12 +923,6 @@@ static void *add_customdata_cb(void *us
        numloops = dm->getNumLoops(dm);
        cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT,
                                            NULL, numloops, name);
 -      if (cd_data_type == CD_MLOOPUV) {
 -              CustomData_add_layer_named(dm->getPolyDataLayout(dm),
 -                                         CD_MTEXPOLY, CD_DEFAULT,
 -                                         NULL, numloops, name);
 -      }
 -
        return cd_ptr;
  }
  
@@@ -1325,7 -1331,7 +1325,7 @@@ void AbcSubDReader::readObjectData(Mai
                        MEdge *edge = find_edge(edges, mesh->totedge, (*indices)[i], (*indices)[i + 1]);
  
                        if (edge) {
-                               edge->crease = FTOCHAR((*sharpnesses)[s]);
+                               edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]);
                        }
                }
  
index c91d3b32e75b5c07fd57d592c915a4f322860c22,c34bd2493eb57dd5252fea72a51406cc7c6f9372..0135abc7edbd53a2bc4578635423d5317ecd5527
@@@ -74,9 -74,6 +74,9 @@@
  #include "BKE_pointcache.h"
  #include "BKE_scene.h"
  
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_query.h"
 +
  /* for image output   */
  #include "IMB_imbuf_types.h"
  #include "IMB_imbuf.h"
@@@ -489,27 -486,36 +489,27 @@@ static void scene_setSubframe(Scene *sc
        scene->r.subframe = subframe;
  }
  
 -static int surface_getBrushFlags(DynamicPaintSurface *surface, const Scene *scene)
 +static int surface_getBrushFlags(DynamicPaintSurface *surface, const ViewLayer *view_layer)
  {
        Base *base = NULL;
 -      GroupObject *go = NULL;
        Object *brushObj = NULL;
        ModifierData *md = NULL;
  
        int flags = 0;
  
        if (surface->brush_group)
 -              go = surface->brush_group->gobject.first;
 +              base = FIRSTBASE(surface->brush_group->view_layer);
        else
 -              base = scene->base.first;
 +              base = FIRSTBASE(view_layer);
  
 -      while (base || go) {
 +      while (base) {
                brushObj = NULL;
  
                /* select object */
 -              if (surface->brush_group) {
 -                      if (go->ob)
 -                              brushObj = go->ob;
 -              }
 -              else {
 -                      brushObj = base->object;
 -              }
 +              brushObj = base->object;
  
 -              if (surface->brush_group)
 -                      go = go->next;
 -              else
 -                      base = base->next;
 +              /* next item */
 +              base = base->next;
  
                if (!brushObj) {
                        continue;
        return flags;
  }
  
 -static int brush_usesMaterial(const DynamicPaintBrushSettings *brush, const Scene *scene)
 -{
 -      return ((brush->flags & MOD_DPAINT_USE_MATERIAL) && (!BKE_scene_use_new_shading_nodes(scene)));
 -}
 -
  /* check whether two bounds intersect */
  static bool boundsIntersect(Bounds3D *b1, Bounds3D *b2)
  {
@@@ -1091,6 -1102,7 +1091,6 @@@ bool dynamicPaint_createType(struct Dyn
                        brush->flags = MOD_DPAINT_ABS_ALPHA | MOD_DPAINT_RAMP_ALPHA;
                        brush->collision = MOD_DPAINT_COL_VOLUME;
  
 -                      brush->mat = NULL;
                        brush->r = 0.15f;
                        brush->g = 0.4f;
                        brush->b = 0.8f;
@@@ -1230,6 -1242,7 +1230,6 @@@ void dynamicPaint_Modifier_copy(struct 
                t_brush->flags = brush->flags;
                t_brush->collision = brush->collision;
  
 -              t_brush->mat = brush->mat;
                t_brush->r = brush->r;
                t_brush->g = brush->g;
                t_brush->b = brush->b;
@@@ -1810,7 -1823,7 +1810,7 @@@ static void dynamic_paint_apply_surface
                }
                /* apply wetness */
                if (mloopcol_wet) {
-                       const char c = FTOCHAR(pPoint[v_index].wetness);
+                       const char c = unit_float_to_uchar_clamp(pPoint[v_index].wetness);
                        mloopcol_wet[l_index].r = c;
                        mloopcol_wet[l_index].g = c;
                        mloopcol_wet[l_index].b = c;
                                rgb_float_to_uchar((unsigned char *)&mloopcol_preview[l_index].r, c);
                        }
                        else {
-                               const char c = FTOCHAR(pPoint[v_index].wetness);
+                               const char c = unit_float_to_uchar_clamp(pPoint[v_index].wetness);
                                mloopcol_preview[l_index].r = c;
                                mloopcol_preview[l_index].g = c;
                                mloopcol_preview[l_index].b = c;
@@@ -2058,9 -2071,7 +2058,9 @@@ static void canvas_copyDerivedMesh(Dyna
  /*
   *    Updates derived mesh copy and processes dynamic paint step / caches.
   */
 -static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene, Object *ob, DerivedMesh *dm)
 +static void dynamicPaint_frameUpdate(
 +        DynamicPaintModifierData *pmd, struct Depsgraph *depsgraph, Scene *scene,
 +        Object *ob, DerivedMesh *dm)
  {
        if (pmd->canvas) {
                DynamicPaintCanvasSettings *canvas = pmd->canvas;
                                else if (can_simulate) {
                                        /* calculate surface frame */
                                        canvas->flags |= MOD_DPAINT_BAKING;
 -                                      dynamicPaint_calculateFrame(surface, scene, ob, current_frame);
 +                                      dynamicPaint_calculateFrame(surface, depsgraph, scene, ob, current_frame);
                                        canvas->flags &= ~MOD_DPAINT_BAKING;
  
                                        /* restore canvas derivedmesh if required */
  }
  
  /* Modifier call. Processes dynamic paint modifier step. */
 -DerivedMesh *dynamicPaint_Modifier_do(DynamicPaintModifierData *pmd, Scene *scene, Object *ob, DerivedMesh *dm)
 +DerivedMesh *dynamicPaint_Modifier_do(
 +        DynamicPaintModifierData *pmd, struct Depsgraph *depsgraph, Scene *scene,
 +        Object *ob, DerivedMesh *dm)
  {
        if (pmd->canvas) {
                DerivedMesh *ret;
  
                /* Update canvas data for a new frame */
 -              dynamicPaint_frameUpdate(pmd, scene, ob, dm);
 +              dynamicPaint_frameUpdate(pmd, depsgraph, scene, ob, dm);
  
                /* Return output mesh */
                ret = dynamicPaint_Modifier_apply(pmd, ob, dm);
        }
        else {
                /* Update canvas data for a new frame */
 -              dynamicPaint_frameUpdate(pmd, scene, ob, dm);
 +              dynamicPaint_frameUpdate(pmd, depsgraph, scene, ob, dm);
  
                /* Return output mesh */
                return dynamicPaint_Modifier_apply(pmd, ob, dm);
@@@ -3329,6 -3338,91 +3329,6 @@@ void dynamicPaint_outputSurfaceImage(Dy
  }
  
  
 -/***************************** Material / Texture Sampling ******************************/
 -
 -/* stores a copy of required materials to allow doing adjustments
 - *  without interfering the render/preview */
 -typedef struct BrushMaterials {
 -      Material *mat;
 -      Material **ob_mats;
 -      int tot;
 -} BrushMaterials;
 -
 -/* Initialize materials for brush object:
 - *  Calculates inverse matrices for linked objects, updates
 - *  volume caches etc. */
 -static void dynamicPaint_updateBrushMaterials(Object *brushOb, Material *ui_mat, Scene *scene, BrushMaterials *bMats)
 -{
 -      /* Calculate inverse transformation matrix
 -       *  for this object */
 -      invert_m4_m4(brushOb->imat, brushOb->obmat);
 -      copy_m4_m4(brushOb->imat_ren, brushOb->imat);
 -
 -      /* Now process every material linked to this brush object */
 -      if ((ui_mat == NULL) && brushOb->mat && brushOb->totcol) {
 -              int i, tot = (*give_totcolp(brushOb));
 -
 -              /* allocate material pointer array */
 -              if (tot) {
 -                      bMats->ob_mats = MEM_callocN(sizeof(Material *) * (tot), "BrushMaterials");
 -                      for (i = 0; i < tot; i++) {
 -                              bMats->ob_mats[i] = RE_sample_material_init(give_current_material(brushOb, (i + 1)), scene);
 -                      }
 -              }
 -              bMats->tot = tot;
 -      }
 -      else {
 -              bMats->mat = RE_sample_material_init(ui_mat, scene);
 -      }
 -}
 -
 -/* free all data allocated by dynamicPaint_updateBrushMaterials() */
 -static void dynamicPaint_freeBrushMaterials(BrushMaterials *bMats)
 -{
 -      /* Now process every material linked to this brush object */
 -      if (bMats->ob_mats) {
 -              int i;
 -              for (i = 0; i < bMats->tot; i++) {
 -                      RE_sample_material_free(bMats->ob_mats[i]);
 -              }
 -              MEM_freeN(bMats->ob_mats);
 -      }
 -      else if (bMats->mat) {
 -              RE_sample_material_free(bMats->mat);
 -      }
 -}
 -
 -/*
 - *    Get material diffuse color and alpha (including linked textures) in given coordinates
 - */
 -static void dynamicPaint_doMaterialTex(
 -        const BrushMaterials *bMats, float color[3], float *alpha, Object *brushOb,
 -        const float volume_co[3], const float surface_co[3],
 -        int triIndex, DerivedMesh *orcoDm)
 -{
 -      Material *mat = bMats->mat;
 -
 -      const MLoopTri *mlooptri = orcoDm->getLoopTriArray(orcoDm);
 -      const MPoly *mpoly = orcoDm->getPolyArray(orcoDm);
 -
 -      /* If no material defined, use the one assigned to the mesh face */
 -      if (mat == NULL) {
 -              if (bMats->ob_mats) {
 -                      int mat_nr = mpoly[mlooptri[triIndex].poly].mat_nr;
 -                      if (mat_nr >= (*give_totcolp(brushOb)))
 -                              return;
 -                      mat = bMats->ob_mats[mat_nr];
 -                      if (mat == NULL)
 -                              return;    /* No material assigned */
 -              }
 -              else {
 -                      return;
 -              }
 -      }
 -      RE_sample_material_color(mat, color, alpha, volume_co, surface_co, triIndex, orcoDm, brushOb);
 -}
 -
 -
  /***************************** Ray / Nearest Point Utils ******************************/
  
  
@@@ -3649,8 -3743,7 +3649,8 @@@ static void dynamic_paint_brush_velocit
  }
  
  static void dynamicPaint_brushMeshCalculateVelocity(
 -        Scene *scene, Object *ob, DynamicPaintBrushSettings *brush, Vec3f **brushVel, float timescale)
 +        struct Depsgraph *depsgraph, Scene *scene,
 +        Object *ob, DynamicPaintBrushSettings *brush, Vec3f **brushVel, float timescale)
  {
        float prev_obmat[4][4];
        DerivedMesh *dm_p, *dm_c;
        scene->r.subframe = prev_sfra;
  
        BKE_object_modifier_update_subframe(
 -                  scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
 +                  depsgraph, scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
        dm_p = CDDM_copy(brush->dm);
        numOfVerts_p = dm_p->getNumVerts(dm_p);
        mvert_p = dm_p->getVertArray(dm_p);
        scene->r.subframe = cur_sfra;
  
        BKE_object_modifier_update_subframe(
 -                  scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
 +                  depsgraph, scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
        dm_c = brush->dm;
        numOfVerts_c = dm_c->getNumVerts(dm_c);
        mvert_c = dm_p->getVertArray(dm_c);
  }
  
  /* calculate velocity for object center point */
 -static void dynamicPaint_brushObjectCalculateVelocity(Scene *scene, Object *ob, Vec3f *brushVel, float timescale)
 +static void dynamicPaint_brushObjectCalculateVelocity(struct Depsgraph *depsgraph, Scene *scene, Object *ob, Vec3f *brushVel, float timescale)
  {
        float prev_obmat[4][4];
        float cur_loc[3] = {0.0f}, prev_loc[3] = {0.0f};
        scene->r.cfra = prev_fra;
        scene->r.subframe = prev_sfra;
        BKE_object_modifier_update_subframe(
 -                  scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
 +                  depsgraph, scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
        copy_m4_m4(prev_obmat, ob->obmat);
  
        /* current frame dm */
        scene->r.cfra = cur_fra;
        scene->r.subframe = cur_sfra;
        BKE_object_modifier_update_subframe(
 -                  scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
 +                  depsgraph, scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
  
        /* calculate speed */
        mul_m4_v3(prev_obmat, prev_loc);
@@@ -3754,6 -3847,7 +3754,6 @@@ typedef struct DynamicPaintPaintData 
        const DynamicPaintSurface *surface;
        const DynamicPaintBrushSettings *brush;
        Object *brushOb;
 -      const BrushMaterials *bMats;
        const Scene *scene;
        const float timescale;
        const int c_index;
@@@ -3790,10 -3884,14 +3790,10 @@@ static void dynamic_paint_paint_mesh_ce
        VolumeGrid *grid = bData->grid;
  
        const DynamicPaintBrushSettings *brush = data->brush;
 -      Object *brushOb = data->brushOb;
 -      const BrushMaterials *bMats = data->bMats;
  
 -      const Scene *scene = data->scene;
        const float timescale = data->timescale;
        const int c_index = data->c_index;
  
 -      DerivedMesh *dm = data->dm;
        const MVert *mvert = data->mvert;
        const MLoop *mloop = data->mloop;
        const MLoopTri *mlooptri = data->mlooptri;
                        sampleColor[1] = brush->g;
                        sampleColor[2] = brush->b;
  
 -                      /* Get material+textures color on hit point if required */
 -                      if (brush_usesMaterial(brush, scene)) {
 -                              dynamicPaint_doMaterialTex(bMats, sampleColor, &alpha_factor, brushOb,
 -                                                         bData->realCoord[bData->s_pos[index] + ss].v,
 -                                                         hitCoord, hitTri, dm);
 -                      }
 -
                        /* Sample proximity colorband if required       */
                        if ((hit_found == HIT_PROXIMITY) &&
                            (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP))
        }
  }
  
 -static int dynamicPaint_paintMesh(DynamicPaintSurface *surface,
 +static int dynamicPaint_paintMesh(struct Depsgraph *depsgraph, DynamicPaintSurface *surface,
                                    DynamicPaintBrushSettings *brush,
                                    Object *brushOb,
 -                                  BrushMaterials *bMats,
                                    Scene *scene,
                                    float timescale)
  {
        const MLoop *mloop = NULL;
  
        if (brush->flags & MOD_DPAINT_USES_VELOCITY)
 -              dynamicPaint_brushMeshCalculateVelocity(scene, brushOb, brush, &brushVelocity, timescale);
 +              dynamicPaint_brushMeshCalculateVelocity(depsgraph, scene, brushOb, brush, &brushVelocity, timescale);
  
        if (!brush->dm)
                return 0;
                                        /* loop through cell points and process brush */
                                        DynamicPaintPaintData data = {
                                            .surface = surface,
 -                                          .brush = brush, .brushOb = brushOb, .bMats = bMats,
 +                                          .brush = brush, .brushOb = brushOb,
                                            .scene = scene, .timescale = timescale, .c_index = c_index,
                                            .dm = dm, .mvert = mvert, .mloop = mloop, .mlooptri = mlooptri,
                                            .brush_radius = brush_radius, .avg_brushNor = avg_brushNor, .brushVelocity = brushVelocity,
@@@ -4505,9 -4611,13 +4505,9 @@@ static void dynamic_paint_paint_single_
        const PaintBakeData *bData = sData->bData;
  
        const DynamicPaintBrushSettings *brush = data->brush;
 -      Object *brushOb = data->brushOb;
 -      const BrushMaterials *bMats = data->bMats;
  
 -      const Scene *scene = data->scene;
        const float timescale = data->timescale;
  
 -      const MVert *mvert = data->mvert;
        const float brush_radius = data->brush_radius;
        const Vec3f *brushVelocity = data->brushVelocity;
  
                float depth = 0.0f;
                float velocity_val = 0.0f;
  
 -              /* material */
 -              if (brush_usesMaterial(brush, scene)) {
 -                      float alpha_factor = 1.0f;
 -                      float hit_coord[3];
 -                      /* use dummy coord of first vertex */
 -                      mul_v3_m4v3(hit_coord, brushOb->obmat, mvert[0].co);
 -
 -                      dynamicPaint_doMaterialTex(bMats, paintColor, &alpha_factor, brushOb,
 -                                                 bData->realCoord[bData->s_pos[index]].v, hit_coord, 0, brush->dm);
 -              }
 -
                /* color ramp */
                if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP &&
                    BKE_colorband_evaluate(brush->paint_ramp, (1.0f - strength), colorband))
                                paintColor[2] = colorband[2];
                        }
                        else {
 -                              if (!brush_usesMaterial(brush, scene)) {
 -                                      paintColor[0] = brush->r;
 -                                      paintColor[1] = brush->g;
 -                                      paintColor[2] = brush->b;
 -                              }
 +                              paintColor[0] = brush->r;
 +                              paintColor[1] = brush->g;
 +                              paintColor[2] = brush->b;
                        }
                }
                else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) {
  }
  
  static int dynamicPaint_paintSinglePoint(
 -        DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush,
 -        Object *brushOb, BrushMaterials *bMats, Scene *scene, float timescale)
 +        struct Depsgraph *depsgraph, DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush,
 +        Object *brushOb, Scene *scene, float timescale)
  {
        PaintSurfaceData *sData = surface->data;
        float brush_radius = brush->paint_distance * surface->radius_scale;
        Vec3f brushVel;
  
        if (brush->flags & MOD_DPAINT_USES_VELOCITY)
 -              dynamicPaint_brushObjectCalculateVelocity(scene, brushOb, &brushVel, timescale);
 +              dynamicPaint_brushObjectCalculateVelocity(depsgraph, scene, brushOb, &brushVel, timescale);
  
        const MVert *mvert = brush->dm->getVertArray(brush->dm);
  
         */
        DynamicPaintPaintData data = {
            .surface = surface,
 -          .brush = brush, .brushOb = brushOb, .bMats = bMats,
 +          .brush = brush, .brushOb = brushOb,
            .scene = scene, .timescale = timescale,
            .mvert = mvert,
            .brush_radius = brush_radius, .brushVelocity = &brushVel,
@@@ -4918,7 -5041,7 +4918,7 @@@ static void dynamic_paint_prepare_effec
  }
  
  static int dynamicPaint_prepareEffectStep(
 -        DynamicPaintSurface *surface, Scene *scene, Object *ob, float **force, float timescale)
 +        struct Depsgraph *depsgraph, DynamicPaintSurface *surface, Scene *scene, Object *ob, float **force, float timescale)
  {
        double average_force = 0.0f;
        float shrink_speed = 0.0f, spread_speed = 0.0f;
  
        /* Init force data if required */
        if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
 -              ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true);
 +              ListBase *effectors = pdInitEffectors(depsgraph, scene, ob, NULL, surface->effector_weights, true);
  
                /* allocate memory for force data (dir vector + strength) */
                *force = MEM_mallocN(sData->total_points * 4 * sizeof(float), "PaintEffectForces");
@@@ -5761,7 -5884,7 +5761,7 @@@ static void dynamic_paint_generate_bake
        }
  }
  
 -static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Scene *scene, Object *ob)
 +static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const ViewLayer *view_layer, Object *ob)
  {
        PaintSurfaceData *sData = surface->data;
        PaintBakeData *bData = sData->bData;
        int index;
        bool new_bdata = false;
        const bool do_velocity_data = ((surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) ||
 -                                     (surface_getBrushFlags(surface, scene) & BRUSH_USES_VELOCITY));
 +                                     (surface_getBrushFlags(surface, view_layer) & BRUSH_USES_VELOCITY));
        const bool do_accel_data = (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) != 0;
  
        int canvasNumOfVerts = dm->getNumVerts(dm);
  /*
   * Do Dynamic Paint step. Paints scene brush objects of current state/frame to the surface.
   */
 -static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *surface, float timescale, float subframe)
 +static int dynamicPaint_doStep(struct Depsgraph *depsgraph, Scene *scene, Object *ob, DynamicPaintSurface *surface, float timescale, float subframe)
  {
        PaintSurfaceData *sData = surface->data;
        PaintBakeData *bData = sData->bData;
         */
        {
                Base *base = NULL;
 -              GroupObject *go = NULL;
                Object *brushObj = NULL;
                ModifierData *md = NULL;
 +              ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
  
                /* backup current scene frame */
                int scene_frame = scene->r.cfra;
  
                /* either from group or from all objects */
                if (surface->brush_group)
 -                      go = surface->brush_group->gobject.first;
 +                      base = FIRSTBASE(surface->brush_group->view_layer);
                else
 -                      base = scene->base.first;
 +                      base = FIRSTBASE(view_layer);
  
 -              while (base || go) {
 +              while (base) {
                        brushObj = NULL;
                        /* select object */
 -                      if (surface->brush_group) {
 -                              if (go->ob)
 -                                      brushObj = go->ob;
 -                      }
 -                      else
 -                              brushObj = base->object;
 +                      brushObj = base->object;
  
                        /* next item */
 -                      if (surface->brush_group)
 -                              go = go->next;
 -                      else
 -                              base = base->next;
 +                      base = base->next;
  
                        if (!brushObj) {
                                /* skip item */
                                /* make sure we're dealing with a brush */
                                if (pmd2->brush) {
                                        DynamicPaintBrushSettings *brush = pmd2->brush;
 -                                      BrushMaterials bMats = {NULL};
  
                                        /* calculate brush speed vectors if required */
                                        if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && brush->flags & MOD_DPAINT_DO_SMUDGE) {
                                        /* update object data on this subframe */
                                        if (subframe) {
                                                scene_setSubframe(scene, subframe);
 -                                              BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION,
 +                                              BKE_object_modifier_update_subframe(depsgraph, scene, brushObj, true, SUBFRAME_RECURSION,
                                                                                    BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
                                        }
 -                                      /* Prepare materials if required        */
 -                                      if (brush_usesMaterial(brush, scene))
 -                                              dynamicPaint_updateBrushMaterials(brushObj, brush->mat, scene, &bMats);
 +
  
                                        /* Apply brush on the surface depending on it's collision type */
 -                                      /* Particle brush: */
 -                                      if (brush->collision == MOD_DPAINT_COL_PSYS) {
 -                                              if (brush->psys && brush->psys->part &&
 -                                                  ELEM(brush->psys->part->type, PART_EMITTER, PART_FLUID) &&
 -                                                  psys_check_enabled(brushObj, brush->psys, G.is_rendering))
 -                                              {
 -                                                      /* Paint a particle system */
 -                                                      BKE_animsys_evaluate_animdata(scene, &brush->psys->part->id, brush->psys->part->adt,
 -                                                                                    BKE_scene_frame_get(scene), ADT_RECALC_ANIM);
 -                                                      dynamicPaint_paintParticles(surface, brush->psys, brush, timescale);
 -                                              }
 +                                      if (brush->psys && brush->psys->part &&
 +                                          ELEM(brush->psys->part->type, PART_EMITTER, PART_FLUID) &&
 +                                          psys_check_enabled(brushObj, brush->psys, G.is_rendering))
 +                                      {
 +                                              /* Paint a particle system */
 +                                              BKE_animsys_evaluate_animdata(scene, &brush->psys->part->id, brush->psys->part->adt,
 +                                                                            BKE_scene_frame_get(scene), ADT_RECALC_ANIM);
 +                                              dynamicPaint_paintParticles(surface, brush->psys, brush, timescale);
                                        }
                                        /* Object center distance: */
 -                                      else if (brush->collision == MOD_DPAINT_COL_POINT && brushObj != ob) {
 -                                              dynamicPaint_paintSinglePoint(surface, brushObj->loc, brush, brushObj, &bMats, scene, timescale);
 +                                      if (brush->collision == MOD_DPAINT_COL_POINT && brushObj != ob) {
 +                                              dynamicPaint_paintSinglePoint(depsgraph, surface, brushObj->loc, brush, brushObj, scene, timescale);
                                        }
                                        /* Mesh volume/proximity: */
                                        else if (brushObj != ob) {
 -                                              dynamicPaint_paintMesh(surface, brush, brushObj, &bMats, scene, timescale);
 +                                              dynamicPaint_paintMesh(depsgraph, surface, brush, brushObj, scene, timescale);
                                        }
  
 -                                      /* free temp material data */
 -                                      if (brush_usesMaterial(brush, scene))
 -                                              dynamicPaint_freeBrushMaterials(&bMats);
                                        /* reset object to it's original state */
                                        if (subframe) {
                                                scene->r.cfra = scene_frame;
                                                scene->r.subframe = scene_subframe;
 -                                              BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION,
 +                                              BKE_object_modifier_update_subframe(depsgraph, scene, brushObj, true, SUBFRAME_RECURSION,
                                                                                    BKE_scene_frame_get(scene), eModifierType_DynamicPaint);
                                        }
  
                                return setError(canvas, N_("Not enough free memory"));
  
                        /* Prepare effects and get number of required steps */
 -                      steps = dynamicPaint_prepareEffectStep(surface, scene, ob, &force, timescale);
 +                      steps = dynamicPaint_prepareEffectStep(depsgraph, surface, scene, ob, &force, timescale);
                        for (s = 0; s < steps; s++) {
                                dynamicPaint_doEffectStep(surface, force, prevPoint, timescale, (float)steps);
                        }
  /*
   * Calculate a single frame and included subframes for surface
   */
 -int dynamicPaint_calculateFrame(DynamicPaintSurface *surface, Scene *scene, Object *cObject, int frame)
 +int dynamicPaint_calculateFrame(
 +        DynamicPaintSurface *surface, struct Depsgraph *depsgraph,
 +        Scene *scene, Object *cObject, int frame)
  {
        float timescale = 1.0f;
  
                dynamicPaint_applySurfaceDisplace(surface, surface->canvas->dm);
  
        /* update bake data */
 -      dynamicPaint_generateBakeData(surface, scene, cObject);
 +      ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
 +      dynamicPaint_generateBakeData(surface, view_layer, cObject);
  
        /* don't do substeps for first frame */
        if (surface->substeps && (frame != surface->start_frame)) {
  
                for (st = 1; st <= surface->substeps; st++) {
                        float subframe = ((float) st) / (surface->substeps + 1);
 -                      if (!dynamicPaint_doStep(scene, cObject, surface, timescale, subframe))
 +                      if (!dynamicPaint_doStep(depsgraph, scene, cObject, surface, timescale, subframe))
                                return 0;
                }
        }
  
 -      return dynamicPaint_doStep(scene, cObject, surface, timescale, 0.0f);
 +      return dynamicPaint_doStep(depsgraph, scene, cObject, surface, timescale, 0.0f);
  }
index 2a6f28ef6eea1638416e222de4f694c2689bd901,ef9e46fc62dce05e625e7790268a097555519f4d..9399646bedf968c402585a1b5a40e55297f9b4ae
@@@ -380,10 -380,8 +380,10 @@@ void xyz_to_rgb(float xc, float yc, flo
        }
  }
  
 -/* we define a 'cpack' here as a (3 byte color code) number that can be expressed like 0xFFAA66 or so.
 - * for that reason it is sensitive for endianness... with this function it works correctly
 +/**
 + * We define a 'cpack' here as a (3 byte color code) number that can be expressed like 0xFFAA66 or so.
 + * for that reason it is sensitive for endianness... with this function it works correctly.
 + * \see #imm_cpack
   */
  
  unsigned int hsv_to_cpack(float h, float s, float v)
@@@ -441,12 -439,12 +441,12 @@@ void rgba_uchar_to_float(float r_col[4]
  
  void rgb_float_to_uchar(unsigned char r_col[3], const float col_f[3])
  {
-       F3TOCHAR3(col_f, r_col);
+       unit_float_to_uchar_clamp_v3(r_col, col_f);
  }
  
  void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
  {
-       F4TOCHAR4(col_f, r_col);
+       unit_float_to_uchar_clamp_v4(r_col, col_f);
  }
  
  /* ********************************* color transforms ********************************* */
@@@ -677,80 -675,135 +677,80 @@@ void rgb_to_lab(float r, float g, floa
        xyz_to_lab(x, y, z, ll, la, lb);
  }
  
 -static void xyz_to_lms(float x, float y, float z, float *l, float *m, float *s)
 -{
 -      *l =  0.3897f * x + 0.6890f * y - 0.0787f * z;
 -      *m = -0.2298f * x + 1.1834f * y + 0.0464f * z;
 -      *s = z;
 -}
 -
 -static void lms_to_xyz(float l, float m, float s, float *x, float *y, float *z)
 -{
 -      *x =  1.9102f * l - 1.1121f * m + 0.2019f * s;
 -      *y =  0.3709f * l + 0.6290f * m + 0.0000f * s;
 -      *z = s;
 -}
 -
 -static void normalize_rgb(float rgb[3])
 -{
 -      const float max = max_fff(rgb[0], rgb[1], rgb[2]);
 -
 -      if (max > 0.0f) {
 -              mul_v3_fl(rgb, 1.0f / max);
 -      }
 -}
 +/* ****************************** blackbody ******************************** */
  
 -/* Color rendering of spectra, adapted from public domain code by John Walker,
 - * http://www.fourmilab.ch/
 +/* Calculate color in range 800..12000 using an approximation
 + * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B
 + * Max absolute error for RGB is (0.00095, 0.00077, 0.00057),
 + * which is enough to get the same 8 bit/channel color.
   */
  
 -static void spectrum_to_xyz(float temperature, float xyz[3])
 -{
 -      int i;
 -      float lambda, x = 0.0f, y = 0.0f, z = 0.0f, xyz_sum;
 -
 -      /* CIE colour matching functions xBar, yBar, and zBar for wavelengths from
 -       * 380 through 780 nanometers, every 5 nanometers.
 -       * For a wavelength lambda in this range:
 -       *
 -       * cie_colour_match[(lambda - 380) / 5][0] = xBar
 -       * cie_colour_match[(lambda - 380) / 5][1] = yBar
 -       * cie_colour_match[(lambda - 380) / 5][2] = zBar
 -       */
 -
 -      const float cie_colour_match[81][3] = {
 -          {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f},
 -          {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f},
 -          {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f},
 -          {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f},
 -          {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f},
 -          {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f},
 -          {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f},
 -          {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f},
 -          {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f},
 -          {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f},
 -          {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f},
 -          {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f},
 -          {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f},
 -          {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f},
 -          {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f},
 -          {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f},
 -          {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f},
 -          {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f},
 -          {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f},
 -          {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f},
 -          {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f},
 -          {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f},
 -          {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f},
 -          {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f},
 -          {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f},
 -          {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f},
 -          {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}
 -      };
 -
 -      for (i = 0, lambda = 380.0f; lambda < 780.1f; i++, lambda += 5.0f) {
 -              /* wavelength in meter */
 -              const float wlm = lambda * 1e-9f;
 -              const float Me = (3.74183e-16f * powf(wlm, -5.0f)) / (expf(1.4388e-2f / (wlm * temperature)) - 1.0f);
 -
 -              x += Me * cie_colour_match[i][0];
 -              y += Me * cie_colour_match[i][1];
 -              z += Me * cie_colour_match[i][2];
 +static const float blackbody_table_r[6][3] = {
 +      {  2.52432244e+03f, -1.06185848e-03f, 3.11067539e+00f },
 +      {  3.37763626e+03f, -4.34581697e-04f, 1.64843306e+00f },
 +      {  4.10671449e+03f, -8.61949938e-05f, 6.41423749e-01f },
 +      {  4.66849800e+03f,  2.85655028e-05f, 1.29075375e-01f },
 +      {  4.60124770e+03f,  2.89727618e-05f, 1.48001316e-01f },
 +      {  3.78765709e+03f,  9.36026367e-06f, 3.98995841e-01f },
 +};
 +
 +static const float blackbody_table_g[6][3] = {
 +      { -7.50343014e+02f,  3.15679613e-04f, 4.73464526e-01f },
 +      { -1.00402363e+03f,  1.29189794e-04f, 9.08181524e-01f },
 +      { -1.22075471e+03f,  2.56245413e-05f, 1.20753416e+00f },
 +      { -1.42546105e+03f, -4.01730887e-05f, 1.44002695e+00f },
 +      { -1.18134453e+03f, -2.18913373e-05f, 1.30656109e+00f },
 +      { -5.00279505e+02f, -4.59745390e-06f, 1.09090465e+00f },
 +};
 +
 +static const float blackbody_table_b[6][4] = {
 +      { 0.0f, 0.0f, 0.0f, 0.0f },
 +      { 0.0f, 0.0f, 0.0f, 0.0f },
 +      { 0.0f, 0.0f, 0.0f, 0.0f },
 +      { -2.02524603e-11f,  1.79435860e-07f, -2.60561875e-04f, -1.41761141e-02f },
 +      { -2.22463426e-13f, -1.55078698e-08f,  3.81675160e-04f, -7.30646033e-01f },
 +      {  6.72595954e-13f, -2.73059993e-08f,  4.24068546e-04f, -7.52204323e-01f },
 +};
 +
 +static void blackbody_temperature_to_rgb(float rgb[3], float t)
 +{
 +      if (t >= 12000.0f) {
 +              rgb[0] = 0.826270103f;
 +              rgb[1] = 0.994478524f;
 +              rgb[2] = 1.56626022f;
 +      }
 +      else if (t < 965.0f) {
 +              rgb[0] = 4.70366907f;
 +              rgb[1] = 0.0f;
 +              rgb[2] = 0.0f;
 +      }
 +      else {
 +              int i = (t >= 6365.0f) ? 5 :
 +                      (t >= 3315.0f) ? 4 :
 +                      (t >= 1902.0f) ? 3 :
 +                      (t >= 1449.0f) ? 2 :
 +                      (t >= 1167.0f) ? 1 : 0;
 +
 +              const float *r = blackbody_table_r[i];
 +              const float *g = blackbody_table_g[i];
 +              const float *b = blackbody_table_b[i];
 +
 +              const float t_inv = 1.0f / t;
 +              rgb[0] = r[0] * t_inv + r[1] * t + r[2];
 +              rgb[1] = g[0] * t_inv + g[1] * t + g[2];
 +              rgb[2] = ((b[0] * t + b[1]) * t + b[2]) * t + b[3];
        }
 -
 -      xyz_sum = (x + y + z);
 -
 -      xyz[0] = x / xyz_sum;
 -      xyz[1] = y / xyz_sum;
 -      xyz[2] = z / xyz_sum;
  }
  
  void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, float max)
  {
 -      int i, j = 0, dj = 1;
 -      float rgb[3], xyz[3], lms[3], lms_w[3];
 -      float bb_temp;
 -
 -      if (min < max) {
 -              SWAP(float, min, max);
 -              j = width - 1;
 -              dj = -1;
 -      }
 +      for (int i = 0; i < width; i++) {
 +              float temperature = min + (max - min) / (float)width * (float)i;
  
 -      for (i = 0; i < width; i++, j += dj) {
 -              bb_temp = min + (max - min) / (float)width * (float)i;
 +              float rgb[3];
 +              blackbody_temperature_to_rgb(rgb, temperature);
  
 -              /* integrate blackbody radiation spectrum to XYZ */
 -              spectrum_to_xyz(bb_temp, xyz);
 -
 -              /* normalize highest temperature to white (in LMS system) */
 -              xyz_to_lms(xyz[0], xyz[1], xyz[2], &lms[0], &lms[1], &lms[2]);
 -
 -              if (i == 0) {
 -                      lms_w[0] = 1.0f / lms[0];
 -                      lms_w[1] = 1.0f / lms[1];
 -                      lms_w[2] = 1.0f / lms[2];
 -              }
 -
 -              mul_v3_v3(lms, lms_w);
 -
 -              lms_to_xyz(lms[0], lms[1], lms[2], &xyz[0], &xyz[1], &xyz[2]);
 -
 -              /* convert to RGB */
 -              xyz_to_rgb(xyz[0], xyz[1], xyz[2], &rgb[0], &rgb[1], &rgb[2], BLI_XYZ_CIE);
 -              constrain_rgb(&rgb[0], &rgb[1], &rgb[2]);
 -              normalize_rgb(rgb);
 -
 -              copy_v3_v3(&r_table[(j << 2)], rgb);
 -
 -              if (rgb[2] > 0.1f)
 -                      r_table[(j << 2) + 3] = rgb[2];
 -              else
 -                      r_table[(j << 2) + 3] = 0.0f;
 +              copy_v3_v3(&r_table[i * 4], rgb);
 +              r_table[i * 4 + 3] = 0.0f;
        }
  }
index 10b9745a6b9423a92c831a16cd23b92155f2faa6,da85038bf909835a62187493ecaae136e24572ec..4c6d00a79cb7c5f764a53a57b868a8c2f4560968
@@@ -185,9 -185,9 +185,9 @@@ void VCOLDataWrapper::get_vcol(int v_in
                        COLLADAFW::ArrayPrimitiveType<float> *values = mVData->getFloatValues();
                        if (values->empty() || values->getCount() <= (v_index * stride + 2)) return;  // xxx need to create an eror instead
  
-                       mloopcol->r = FTOCHAR((*values)[v_index * stride]);
-                       mloopcol->g = FTOCHAR((*values)[v_index * stride + 1]);
-                       mloopcol->b = FTOCHAR((*values)[v_index * stride + 2]);
+                       mloopcol->r = unit_float_to_uchar_clamp((*values)[v_index * stride]);
+                       mloopcol->g = unit_float_to_uchar_clamp((*values)[v_index * stride + 1]);
+                       mloopcol->b = unit_float_to_uchar_clamp((*values)[v_index * stride + 2]);
                }
                break;
  
                        COLLADAFW::ArrayPrimitiveType<double> *values = mVData->getDoubleValues();
                        if (values->empty() || values->getCount() <= (v_index * stride + 2)) return; // xxx need to create an eror instead
  
-                       mloopcol->r = FTOCHAR((*values)[v_index * stride]);
-                       mloopcol->g = FTOCHAR((*values)[v_index * stride + 1]);
-                       mloopcol->b = FTOCHAR((*values)[v_index * stride + 2]);
+                       mloopcol->r = unit_float_to_uchar_clamp((*values)[v_index * stride]);
+                       mloopcol->g = unit_float_to_uchar_clamp((*values)[v_index * stride + 1]);
+                       mloopcol->b = unit_float_to_uchar_clamp((*values)[v_index * stride + 2]);
                }
                break;
                default:
  
  }
  
 -MeshImporter::MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce) : unitconverter(unitconv), scene(sce), armature_importer(arm) {
 +MeshImporter::MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce, ViewLayer *view_layer):
 +      unitconverter(unitconv), 
 +      scene(sce),
 +      view_layer(view_layer),
 +      armature_importer(arm) {
  }
  
  bool MeshImporter::set_poly_indices(MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count)
@@@ -476,9 -472,11 +476,9 @@@ void MeshImporter::allocate_poly_data(C
                                COLLADAFW::MeshVertexData::InputInfos *info = collada_mesh->getUVCoords().getInputInfosArray()[i];
                                COLLADAFW::String &uvname = info->mName;
                                // Allocate space for UV_data
 -                              CustomData_add_layer_named(&me->pdata, CD_MTEXPOLY, CD_DEFAULT, NULL, me->totpoly, uvname.c_str());
                                CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, uvname.c_str());
                        }
                        // activate the first uv map
 -                      me->mtpoly  = (MTexPoly *)CustomData_get_layer_n(&me->pdata, CD_MTEXPOLY, 0);
                        me->mloopuv = (MLoopUV *) CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, 0);
                }
  
@@@ -879,6 -877,48 +879,6 @@@ std::string *MeshImporter::get_geometry
        return NULL;
  }
  
 -MTex *MeshImporter::assign_textures_to_uvlayer(COLLADAFW::TextureCoordinateBinding &ctexture,
 -                                               Mesh *me, TexIndexTextureArrayMap& texindex_texarray_map,
 -                                               MTex *color_texture)
 -{
 -      const COLLADAFW::TextureMapId texture_index = ctexture.getTextureMapId();
 -      size_t setindex = ctexture.getSetIndex();
 -      std::string uvname = ctexture.getSemantic();
 -      
 -      if (setindex == -1) return NULL;
 -      
 -      const CustomData *data = &me->fdata;
 -      int layer_index = CustomData_get_layer_index(data, CD_MTFACE);
 -
 -      if (layer_index == -1) return NULL;
 -
 -      CustomDataLayer *cdl = &data->layers[layer_index + setindex];
 -      
 -      /* set uvname to bind_vertex_input semantic */
 -      BLI_strncpy(cdl->name, uvname.c_str(), sizeof(cdl->name));
 -
 -      if (texindex_texarray_map.find(texture_index) == texindex_texarray_map.end()) {
 -              
 -              fprintf(stderr, "Cannot find texture array by texture index.\n");
 -              return color_texture;
 -      }
 -      
 -      std::vector<MTex *> textures = texindex_texarray_map[texture_index];
 -      
 -      std::vector<MTex *>::iterator it;
 -      
 -      for (it = textures.begin(); it != textures.end(); it++) {
 -              
 -              MTex *texture = *it;
 -              
 -              if (texture) {
 -                      BLI_strncpy(texture->uvname, uvname.c_str(), sizeof(texture->uvname));
 -                      if (texture->mapto == MAP_COL) color_texture = texture;
 -              }
 -      }
 -      return color_texture;
 -}
 -
  /**
   * this function checks if both objects have the same
   * materials assigned to Object (in the same order)
@@@ -1012,19 -1052,21 +1012,19 @@@ void MeshImporter::optimize_material_as
   * which materials shall be moved to the created geometries. Also see
   * optimize_material_assignements() above.
   */
 -MTFace *MeshImporter::assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial,
 -                                              std::map<COLLADAFW::UniqueId, Material *>& uid_material_map,
 -                                              Object *ob, const COLLADAFW::UniqueId *geom_uid,
 -                                              char *layername, MTFace *texture_face,
 -                                              std::map<Material *, TexIndexTextureArrayMap>& material_texture_mapping_map, short mat_index)
 +void MeshImporter::assign_material_to_geom(
 +        COLLADAFW::MaterialBinding cmaterial,
 +        std::map<COLLADAFW::UniqueId, Material *>& uid_material_map,
 +        Object *ob, const COLLADAFW::UniqueId *geom_uid,
 +        short mat_index)
  {
 -      MTex *color_texture = NULL;
 -      Mesh *me = (Mesh *)ob->data;
        const COLLADAFW::UniqueId& ma_uid = cmaterial.getReferencedMaterial();
        
        // do we know this material?
        if (uid_material_map.find(ma_uid) == uid_material_map.end()) {
                
                fprintf(stderr, "Cannot find material by UID.\n");
 -              return NULL;
 +              return;
        }
  
        // first time we get geom_uid, ma_uid pair. Save for later check.
        ob->actcol=0;
        assign_material(ob, ma, mat_index + 1, BKE_MAT_ASSIGN_OBJECT); 
        
 -      COLLADAFW::TextureCoordinateBindingArray& tex_array = 
 -          cmaterial.getTextureCoordinateBindingArray();
 -      TexIndexTextureArrayMap texindex_texarray_map = material_texture_mapping_map[ma];
 -      unsigned int i;
 -      // loop through <bind_vertex_inputs>
 -      for (i = 0; i < tex_array.getCount(); i++) {
 -              
 -              color_texture = assign_textures_to_uvlayer(tex_array[i], me, texindex_texarray_map,
 -                                                          color_texture);
 -      }
 -      
 -      // set texture face
 -      if (color_texture &&
 -          strlen((color_texture)->uvname) &&
 -          !STREQ(layername, color_texture->uvname))
 -      {
 -              texture_face = (MTFace *)CustomData_get_layer_named(&me->fdata, CD_MTFACE,
 -                                                                  color_texture->uvname);
 -              strcpy(layername, color_texture->uvname);
 -      }
 -      
        MaterialIdPrimitiveArrayMap& mat_prim_map = geom_uid_mat_mapping_map[*geom_uid];
        COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId();
        
                        Primitive& prim = *it;
                        MPoly *mpoly = prim.mpoly;
  
 -                      for (i = 0; i < prim.totpoly; i++, mpoly++) {
 +                      for (int i = 0; i < prim.totpoly; i++, mpoly++) {
                                mpoly->mat_nr = mat_index;
 -                              // bind texture images to faces
 -                              if (texture_face && color_texture) {
 -                                      texture_face->tpage = (Image *)color_texture->tex->ima;
 -                                      texture_face++;
 -                              }
                        }
                }
        }       
 -      return texture_face;
  }
  
  Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::InstanceGeometry *geom,
                                           bool isController,
 -                                         std::map<COLLADAFW::UniqueId, Material *>& uid_material_map,
 -                                         std::map<Material *, TexIndexTextureArrayMap>& material_texture_mapping_map)
 +                                         std::map<COLLADAFW::UniqueId, Material *>& uid_material_map)
  {
        const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId();
        
        const char *name = (id.length()) ? id.c_str() : NULL;
        
        // add object
 -      Object *ob = bc_add_object(scene, OB_MESH, name);
 +      Object *ob = bc_add_object(scene, view_layer, OB_MESH, name);
        bc_set_mark(ob); // used later for material assignement optimization
  
  
        id_us_plus(&old_mesh->id);  /* Because BKE_mesh_assign_object would have already decreased it... */
        BKE_libblock_free_us(G.main, old_mesh);
  
 -      char layername[100];
 -      layername[0] = '\0';
 -      MTFace *texture_face = NULL;
 -      
        COLLADAFW::MaterialBindingArray& mat_array =
            geom->getMaterialBindings();
        
        for (unsigned int i = 0; i < mat_array.getCount(); i++) {
                
                if (mat_array[i].getReferencedMaterial().isValid()) {
 -                      texture_face = assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid,
 -                                                             layername, texture_face,
 -                                                             material_texture_mapping_map, i);
 +                      assign_material_to_geom(
 +                              mat_array[i], uid_material_map, ob, geom_uid,
 +                              i);
                }
                else {
                        fprintf(stderr, "invalid referenced material for %s\n", mat_array[i].getName().c_str());
index 9167bc43f4ae4e8142ba885b8a11a4ba457484d4,5431f2104415818a576321234276e84c19043add..857ca0a9d96885a88984787dbe757c9a6a0141e3
  #include "BKE_colorband.h"
  #include "BKE_colortools.h"
  #include "BKE_context.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_global.h"
  #include "BKE_idcode.h"
  #include "BKE_idprop.h"
 +#include "BKE_layer.h"
  #include "BKE_library.h"
 +#include "BKE_library_override.h"
  #include "BKE_linestyle.h"
  #include "BKE_main.h"
  #include "BKE_modifier.h"
  #include "BKE_particle.h"
  #include "BKE_paint.h"
  #include "BKE_report.h"
 -#include "BKE_sca.h"
  #include "BKE_screen.h"
  
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_build.h"
 +
  #include "ED_screen.h"
  #include "ED_object.h"
  #include "ED_render.h"
  
  // #define USE_OP_RESET_BUT  // we may want to make this optional, disable for now.
  
 +/* defines for templateID/TemplateSearch */
 +#define TEMPLATE_SEARCH_TEXTBUT_WIDTH  (UI_UNIT_X * 6)
 +#define TEMPLATE_SEARCH_TEXTBUT_HEIGHT  UI_UNIT_Y
 +
  
  void UI_template_fix_linking(void)
  {
  }
  
 +/**
 + * Add a block button for the search menu for templateID and templateSearch.
 + */
 +static void template_add_button_search_menu(
 +        const bContext *C, uiLayout *layout, uiBlock *block,
 +        PointerRNA *ptr, PropertyRNA *prop,
 +        uiBlockCreateFunc block_func, void *block_argN, const char * const tip,
 +        const bool use_previews, const bool editable)
 +{
 +      PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop);
 +      ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL;
 +      const ID *idfrom = ptr->id.data;
 +      const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop);
 +      uiBut *but;
 +
 +      if (use_previews) {
 +              ARegion *region = CTX_wm_region(C);
 +              ScrArea *area = CTX_wm_area(C);
 +              /* XXX ugly top-bar exception */
 +              const bool use_big_size = (region->regiontype != RGN_TYPE_HEADER) && (area->spacetype != SPACE_TOPBAR); /* silly check, could be more generic */
 +              /* Ugly exception for screens here, drawing their preview in icon size looks ugly/useless */
 +              const bool use_preview_icon = use_big_size || (id && (GS(id->name) != ID_SCR));
 +              const short width = UI_UNIT_X * (use_big_size ? 6 : 1.6f);
 +              const short height = UI_UNIT_Y * (use_big_size ? 6 : 1);
 +
 +              but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, width, height, tip);
 +              if (use_preview_icon) {
 +                      int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type);
 +                      ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
 +              }
 +              else {
 +                      ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
 +                      UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
 +              }
 +
 +              if ((idfrom && idfrom->lib) || !editable)
 +                      UI_but_flag_enable(but, UI_BUT_DISABLED);
 +              if (use_big_size) {
 +                      uiLayoutRow(layout, true);
 +              }
 +      }
 +      else {
 +              but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip);
 +              ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
 +              if (id) {
 +                      /* default dragging of icon for id browse buttons */
 +                      UI_but_drag_set_id(but, id);
 +              }
 +              UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
 +
 +              if ((idfrom && idfrom->lib) || !editable)
 +                      UI_but_flag_enable(but, UI_BUT_DISABLED);
 +      }
 +}
 +
 +static uiBlock *template_common_search_menu(
 +        const bContext *C, ARegion *region,
 +        uiButSearchFunc search_func, void *search_arg,
 +        uiButHandleFunc handle_func, void *active_item,
 +        const int preview_rows, const int preview_cols)
 +{
 +      static char search[256];
 +      wmWindow *win = CTX_wm_window(C);
 +      uiBlock *block;
 +      uiBut *but;
 +
 +      /* clear initial search string, then all items show */
 +      search[0] = 0;
 +
 +      block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
 +      UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_SEARCH_MENU);
 +
 +      /* preview thumbnails */
 +      if (preview_rows > 0 && preview_cols > 0) {
 +              const int w = 4 * U.widget_unit * preview_cols;
 +              const int h = 5 * U.widget_unit * preview_rows;
 +
 +              /* fake button, it holds space for search items */
 +              uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL);
 +
 +              but = uiDefSearchBut(
 +                      block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, w, UI_UNIT_Y,
 +                      preview_rows, preview_cols, "");
 +      }
 +      /* list view */
 +      else {
 +              const int searchbox_width  = UI_searchbox_size_x();
 +              const int searchbox_height = UI_searchbox_size_y();
 +
 +              /* fake button, it holds space for search items */
 +              uiDefBut(
 +                      block, UI_BTYPE_LABEL, 0, "", 10, 15, searchbox_width, searchbox_height,
 +                      NULL, 0, 0, 0, 0, NULL);
 +              but = uiDefSearchBut(
 +                      block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0,
 +                      searchbox_width, UI_UNIT_Y - 1, 0, 0, "");
 +      }
 +      UI_but_func_search_set(
 +              but, ui_searchbox_create_generic, search_func,
 +              search_arg, handle_func, active_item);
 +
 +
 +      UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
 +      UI_block_direction_set(block, UI_DIR_DOWN);
 +
 +      /* give search-field focus */
 +      UI_but_focus_on_enter_event(win, but);
 +      /* this type of search menu requires undo */
 +      but->flag |= UI_BUT_UNDO;
 +
 +      return block;
 +}
 +
  /********************** Header Template *************************/
  
  void uiTemplateHeader(uiLayout *layout, bContext *C)
@@@ -240,7 -120,7 +240,7 @@@ typedef struct TemplateID 
  } TemplateID;
  
  /* Search browse menu, assign  */
 -static void id_search_call_cb(bContext *C, void *arg_template, void *item)
 +static void template_ID_set_property_cb(bContext *C, void *arg_template, void *item)
  {
        TemplateID *template_ui = (TemplateID *)arg_template;
  
@@@ -351,25 -231,30 +351,25 @@@ static void id_search_cb_objects_from_s
        }
  
        BKE_main_id_flag_listbase(lb, LIB_TAG_DOIT, false);
 -      for (Base *base = scene->base.first; base; base = base->next) {
 -              base->object->id.tag |= LIB_TAG_DOIT;
 +
 +      FOREACH_SCENE_OBJECT_BEGIN(scene, ob_iter)
 +      {
 +              ob_iter->id.tag |= LIB_TAG_DOIT;
        }
 +      FOREACH_SCENE_OBJECT_END;
        id_search_cb_tagged(C, arg_template, str, items);
  }
  
  /* ID Search browse menu, open */
  static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem)
  {
 -      static char search[256];
        static TemplateID template_ui;
 -      PointerRNA idptr;
 -      wmWindow *win = CTX_wm_window(C);
 -      uiBlock *block;
 -      uiBut *but;
 +      PointerRNA active_item_ptr;
        void (*id_search_cb_p)(const bContext *, void *, const char *, uiSearchItems *) = id_search_cb;
  
 -      /* clear initial search string, then all items show */
 -      search[0] = 0;
        /* arg_litem is malloced, can be freed by parent button */
        template_ui = *((TemplateID *)arg_litem);
 -      
 -      /* get active id for showing first item */
 -      idptr = RNA_property_pointer_get(&template_ui.ptr, template_ui.prop);
 +      active_item_ptr = RNA_property_pointer_get(&template_ui.ptr, template_ui.prop);
  
        if (template_ui.filter) {
                /* Currently only used for objects. */
                }
        }
  
 -      block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
 -      UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_SEARCH_MENU);
 -      
 -      /* preview thumbnails */
 -      if (template_ui.prv_rows > 0 && template_ui.prv_cols > 0) {
 -              int w = 4 * U.widget_unit * template_ui.prv_cols;
 -              int h = 5 * U.widget_unit * template_ui.prv_rows;
 -              
 -              /* fake button, it holds space for search items */
 -              uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL);
 -              
 -              but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, w, UI_UNIT_Y,
 -                                   template_ui.prv_rows, template_ui.prv_cols, "");
 -              UI_but_func_search_set(
 -                      but, ui_searchbox_create_generic, id_search_cb_p,
 -                      &template_ui, id_search_call_cb, idptr.data);
 -      }
 -      /* list view */
 -      else {
 -              const int searchbox_width  = UI_searchbox_size_x();
 -              const int searchbox_height = UI_searchbox_size_y();
 -              
 -              /* fake button, it holds space for search items */
 -              uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 15, searchbox_width, searchbox_height, NULL, 0, 0, 0, 0, NULL);
 -              but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, searchbox_width, UI_UNIT_Y - 1, 0, 0, "");
 -              UI_but_func_search_set(
 -                      but, ui_searchbox_create_generic, id_search_cb_p,
 -                      &template_ui, id_search_call_cb, idptr.data);
 -      }
 -              
 -      
 -      UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
 -      UI_block_direction_set(block, UI_DIR_DOWN);
 -      
 -      /* give search-field focus */
 -      UI_but_focus_on_enter_event(win, but);
 -      /* this type of search menu requires undo */
 -      but->flag |= UI_BUT_UNDO;
 -      
 -      return block;
 +      return template_common_search_menu(
 +                     C, ar, id_search_cb_p, &template_ui, template_ID_set_property_cb, active_item_ptr.data,
 +                     template_ui.prv_rows, template_ui.prv_cols);
  }
  
  /************************ ID Template ***************************/
@@@ -460,34 -382,14 +460,34 @@@ static void template_id_cb(bContext *C
                case UI_ID_LOCAL:
                        if (id) {
                                Main *bmain = CTX_data_main(C);
 -                              if (id_make_local(bmain, id, false, false)) {
 -                                      BKE_main_id_clear_newpoins(bmain);
 +                              if (CTX_wm_window(C)->eventstate->shift) {
 +                                      ID *override_id = BKE_override_static_create_from_id(bmain, id);
 +                                      if (override_id != NULL) {
 +                                              BKE_main_id_clear_newpoins(bmain);
  
 -                                      /* reassign to get get proper updates/notifiers */
 -                                      idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
 -                                      RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr);
 -                                      RNA_property_update(C, &template_ui->ptr, template_ui->prop);
 +                                              /* Assign new pointer, takes care of updates/notifiers */
 +                                              RNA_id_pointer_create(override_id, &idptr);
 +                                      }
                                }
 +                              else {
 +                                      if (id_make_local(bmain, id, false, false)) {
 +                                              BKE_main_id_clear_newpoins(bmain);
 +
 +                                              /* reassign to get get proper updates/notifiers */
 +                                              idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
 +                                      }
 +                              }
 +                              RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr);
 +                              RNA_property_update(C, &template_ui->ptr, template_ui->prop);
 +                      }
 +                      break;
 +              case UI_ID_OVERRIDE:
 +                      if (id && id->override_static) {
 +                              BKE_override_static_free(&id->override_static);
 +                              /* reassign to get get proper updates/notifiers */
 +                              idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
 +                              RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr);
 +                              RNA_property_update(C, &template_ui->ptr, template_ui->prop);
                        }
                        break;
                case UI_ID_ALONE:
                                        Scene *scene = CTX_data_scene(C);
                                        ED_object_single_user(bmain, scene, (struct Object *)id);
                                        WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
 -                                      DAG_relations_tag_update(bmain);
 +                                      DEG_relations_tag_update(bmain);
                                }
                                else {
                                        if (id) {
                                                Main *bmain = CTX_data_main(C);
                                                id_single_user(C, id, &template_ui->ptr, template_ui->prop);
 -                                              DAG_relations_tag_update(bmain);
 +                                              DEG_relations_tag_update(bmain);
                                        }
                                }
                        }
        }
  }
  
 -static const char *template_id_browse_tip(StructRNA *type)
 +static const char *template_id_browse_tip(const StructRNA *type)
  {
        if (type) {
                switch (RNA_type_to_ID_code(type)) {
                        case ID_PAL: return N_("Browse Palette Data to be linked");
                        case ID_PC:  return N_("Browse Paint Curve Data to be linked");
                        case ID_CF:  return N_("Browse Cache Files to be linked");
 +                      case ID_WS:  return N_("Browse Workspace to be linked");
 +                      case ID_LP:  return N_("Browse LightProbe to be linked");
                }
        }
        return N_("Browse ID data to be linked");
@@@ -572,68 -472,6 +572,68 @@@ static const char *template_id_context(
  }
  #endif
  
 +static uiBut *template_id_def_new_but(
 +        uiBlock *block, const ID *id, const TemplateID *template_ui, StructRNA *type,
 +        const char * const newop, const bool editable, const bool id_open, const bool use_tab_but,
 +        int but_height)
 +{
 +      ID *idfrom = template_ui->ptr.id.data;
 +      uiBut *but;
 +      const int w = id ? UI_UNIT_X : id_open ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
 +      const int but_type = use_tab_but ? UI_BTYPE_TAB : UI_BTYPE_BUT;
 +
 +      /* i18n markup, does nothing! */
 +      BLT_I18N_MSGID_MULTI_CTXT("New", BLT_I18NCONTEXT_DEFAULT,
 +                                       BLT_I18NCONTEXT_ID_SCENE,
 +                                       BLT_I18NCONTEXT_ID_OBJECT,
 +                                       BLT_I18NCONTEXT_ID_MESH,
 +                                       BLT_I18NCONTEXT_ID_CURVE,
 +                                       BLT_I18NCONTEXT_ID_METABALL,
 +                                       BLT_I18NCONTEXT_ID_MATERIAL,
 +                                       BLT_I18NCONTEXT_ID_TEXTURE,
 +                                       BLT_I18NCONTEXT_ID_IMAGE,
 +                                       BLT_I18NCONTEXT_ID_LATTICE,
 +                                       BLT_I18NCONTEXT_ID_LAMP,
 +                                       BLT_I18NCONTEXT_ID_CAMERA,
 +                                       BLT_I18NCONTEXT_ID_WORLD,
 +                                       BLT_I18NCONTEXT_ID_SCREEN,
 +                                       BLT_I18NCONTEXT_ID_TEXT,
 +      );
 +      BLT_I18N_MSGID_MULTI_CTXT("New", BLT_I18NCONTEXT_ID_SPEAKER,
 +                                       BLT_I18NCONTEXT_ID_SOUND,
 +                                       BLT_I18NCONTEXT_ID_ARMATURE,
 +                                       BLT_I18NCONTEXT_ID_ACTION,
 +                                       BLT_I18NCONTEXT_ID_NODETREE,
 +                                       BLT_I18NCONTEXT_ID_BRUSH,
 +                                       BLT_I18NCONTEXT_ID_PARTICLESETTINGS,
 +                                       BLT_I18NCONTEXT_ID_GPENCIL,
 +                                       BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
 +                                       BLT_I18NCONTEXT_ID_WORKSPACE,
 +                                       BLT_I18NCONTEXT_ID_LIGHTPROBE,
 +      );
 +
 +      if (newop) {
 +              but = uiDefIconTextButO(block, but_type, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN,
 +                                      (id) ? "" : CTX_IFACE_(template_id_context(type), "New"), 0, 0, w, but_height, NULL);
 +              UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_ADD_NEW));
 +      }
 +      else {
 +              but = uiDefIconTextBut(block, but_type, 0, ICON_ZOOMIN, (id) ? "" : CTX_IFACE_(template_id_context(type), "New"),
 +                                     0, 0, w, but_height, NULL, 0, 0, 0, 0, NULL);
 +              UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_ADD_NEW));
 +      }
 +
 +      if ((idfrom && idfrom->lib) || !editable) {
 +              UI_but_flag_enable(but, UI_BUT_DISABLED);
 +      }
 +
 +#ifndef WITH_INTERNATIONAL
 +      UNUSED_VARS(type);
 +#endif
 +
 +      return but;
 +}
 +
  static void template_ID(
          bContext *C, uiLayout *layout, TemplateID *template_ui, StructRNA *type, int flag,
          const char *newop, const char *openop, const char *unlinkop)
        // ListBase *lb; // UNUSED
        ID *id, *idfrom;
        const bool editable = RNA_property_editable(&template_ui->ptr, template_ui->prop);
 +      const bool use_previews = template_ui->preview = (flag & UI_ID_PREVIEWS) != 0;
  
        idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
        id = idptr.data;
        if (idptr.type)
                type = idptr.type;
  
 -      if (flag & UI_ID_PREVIEWS) {
 -              template_ui->preview = true;
 -
 -              but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template_ui), "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6,
 -                                   TIP_(template_id_browse_tip(type)));
 -              ui_def_but_icon(but, id ? ui_id_icon_get(C, id, true) : RNA_struct_ui_icon(type),
 -                              UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
 -
 -              if ((idfrom && idfrom->lib) || !editable)
 -                      UI_but_flag_enable(but, UI_BUT_DISABLED);
 -              
 -              uiLayoutRow(layout, true);
 -      }
 -      else if (flag & UI_ID_BROWSE) {
 -              but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template_ui), "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y,
 -                                   TIP_(template_id_browse_tip(type)));
 -              ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON);
 -              /* default dragging of icon for id browse buttons */
 -              UI_but_drag_set_id(but, id);
 -              UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
 -
 -              if ((idfrom && idfrom->lib) || !editable)
 -                      UI_but_flag_enable(but, UI_BUT_DISABLED);
 +      if (flag & UI_ID_BROWSE) {
 +              template_add_button_search_menu(
 +                      C, layout, block, &template_ui->ptr, template_ui->prop,
 +                      id_search_menu, MEM_dupallocN(template_ui), TIP_(template_id_browse_tip(type)),
 +                      use_previews, editable);
        }
  
        /* text button with name */
  
                //text_idbutton(id, name);
                name[0] = '\0';
 -              but = uiDefButR(block, UI_BTYPE_TEXT, 0, name, 0, 0, UI_UNIT_X * 6, UI_UNIT_Y,
 -                              &idptr, "name", -1, 0, 0, -1, -1, RNA_struct_ui_description(type));
 +              but = uiDefButR(
 +                      block, UI_BTYPE_TEXT, 0, name, 0, 0, TEMPLATE_SEARCH_TEXTBUT_WIDTH, TEMPLATE_SEARCH_TEXTBUT_HEIGHT,
 +                      &idptr, "name", -1, 0, 0, -1, -1, RNA_struct_ui_description(type));
                UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_RENAME));
                if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT);
  
                                UI_but_flag_enable(but, UI_BUT_DISABLED);
                        }
                        else {
 +                              const bool disabled = (!id_make_local(CTX_data_main(C), id, true /* test */, false) ||
 +                                                     (idfrom && idfrom->lib));
                                but = uiDefIconBut(block, UI_BTYPE_BUT, 0, ICON_LIBRARY_DATA_DIRECT, 0, 0, UI_UNIT_X, UI_UNIT_Y,
 -                                                 NULL, 0, 0, 0, 0, TIP_("Direct linked library data-block, click to make local"));
 -                              if (!id_make_local(CTX_data_main(C), id, true /* test */, false) || (idfrom && idfrom->lib))
 +                                                 NULL, 0, 0, 0, 0,
 +                                                 TIP_("Direct linked library data-block, click to make local, "
 +                                                      "Shift + Click to create a static override"));
 +                              if (disabled) {
                                        UI_but_flag_enable(but, UI_BUT_DISABLED);
 +                              }
 +                              else {
 +                                      UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_LOCAL));
 +                              }
                        }
 -
 -                      UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_LOCAL));
 +              }
 +              else if (ID_IS_STATIC_OVERRIDE(id)) {
 +                      but = uiDefIconBut(block, UI_BTYPE_BUT, 0, ICON_LIBRARY_DATA_OVERRIDE, 0, 0, UI_UNIT_X, UI_UNIT_Y,
 +                                         NULL, 0, 0, 0, 0,
 +                                         TIP_("Static override of linked library data-block, click to make fully local"));
 +                      UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_OVERRIDE));
                }
  
                if (id->us > 1) {
        
                if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT);
                
 -              if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB))) {
 +              if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) {
                        uiDefButR(block, UI_BTYPE_TOGGLE, 0, "F", 0, 0, UI_UNIT_X, UI_UNIT_Y, &idptr, "use_fake_user", -1, 0, 0, -1, -1, NULL);
                }
        }
        
        if (flag & UI_ID_ADD_NEW) {
 -              int w = id ? UI_UNIT_X : (flag & UI_ID_OPEN) ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
 -              
 -              /* i18n markup, does nothing! */
 -              BLT_I18N_MSGID_MULTI_CTXT("New", BLT_I18NCONTEXT_DEFAULT,
 -                                               BLT_I18NCONTEXT_ID_SCENE,
 -                                               BLT_I18NCONTEXT_ID_OBJECT,
 -                                               BLT_I18NCONTEXT_ID_MESH,
 -                                               BLT_I18NCONTEXT_ID_CURVE,
 -                                               BLT_I18NCONTEXT_ID_METABALL,
 -                                               BLT_I18NCONTEXT_ID_MATERIAL,
 -                                               BLT_I18NCONTEXT_ID_TEXTURE,
 -                                               BLT_I18NCONTEXT_ID_IMAGE,
 -                                               BLT_I18NCONTEXT_ID_LATTICE,
 -                                               BLT_I18NCONTEXT_ID_LAMP,
 -                                               BLT_I18NCONTEXT_ID_CAMERA,
 -                                               BLT_I18NCONTEXT_ID_WORLD,
 -                                               BLT_I18NCONTEXT_ID_SCREEN,
 -                                               BLT_I18NCONTEXT_ID_TEXT,
 -              );
 -              BLT_I18N_MSGID_MULTI_CTXT("New", BLT_I18NCONTEXT_ID_SPEAKER,
 -                                               BLT_I18NCONTEXT_ID_SOUND,
 -                                               BLT_I18NCONTEXT_ID_ARMATURE,
 -                                               BLT_I18NCONTEXT_ID_ACTION,
 -                                               BLT_I18NCONTEXT_ID_NODETREE,
 -                                               BLT_I18NCONTEXT_ID_BRUSH,
 -                                               BLT_I18NCONTEXT_ID_PARTICLESETTINGS,
 -                                               BLT_I18NCONTEXT_ID_GPENCIL,
 -                                               BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
 -              );
 -              
 -              if (newop) {
 -                      but = uiDefIconTextButO(block, UI_BTYPE_BUT, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN,
 -                                              (id) ? "" : CTX_IFACE_(template_id_context(type), "New"), 0, 0, w, UI_UNIT_Y, NULL);
 -                      UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_ADD_NEW));
 -              }
 -              else {
 -                      but = uiDefIconTextBut(block, UI_BTYPE_BUT, 0, ICON_ZOOMIN, (id) ? "" : CTX_IFACE_(template_id_context(type), "New"),
 -                                             0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
 -                      UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), SET_INT_IN_POINTER(UI_ID_ADD_NEW));
 -              }
 -
 -              if ((idfrom && idfrom->lib) || !editable)
 -                      UI_but_flag_enable(but, UI_BUT_DISABLED);
 +              template_id_def_new_but(block, id, template_ui, type, newop, editable, flag & UI_ID_OPEN, false, UI_UNIT_X);
        }
  
        /* Due to space limit in UI - skip the "open" icon for packed data, and allow to unpack.
        UI_block_align_end(block);
  }
  
 +static void template_ID_tabs(
 +        bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, int flag,
 +        const char *newop, const char *UNUSED(openop), const char *unlinkop)
 +{
 +      const ARegion *region = CTX_wm_region(C);
 +      const PointerRNA active_ptr = RNA_property_pointer_get(&template->ptr, template->prop);
 +      const int but_align = (region->alignment == RGN_ALIGN_TOP) ? UI_BUT_ALIGN_DOWN : UI_BUT_ALIGN_TOP;
 +      const int but_height = UI_UNIT_Y * 1.1;
 +
 +      uiBlock *block = uiLayoutGetBlock(layout);
 +      uiStyle *style = UI_style_get_dpi();
 +
 +
 +      for (ID *id = template->idlb->first; id; id = id->next) {
 +              wmOperatorType *unlink_ot = WM_operatortype_find(unlinkop, false);
 +              const bool is_active = active_ptr.data == id;
 +              const unsigned int but_width = UI_fontstyle_string_width(&style->widgetlabel, id->name + 2) + UI_UNIT_X +
 +                                             (is_active ? ICON_DEFAULT_WIDTH_SCALE : 0);
 +              uiButTab *tab;
 +
 +              tab = (uiButTab *)uiDefButR_prop(
 +                      block, UI_BTYPE_TAB, 0, "", 0, 0, but_width, UI_UNIT_Y * 1.1,
 +                      &template->ptr, template->prop, 0, 0.0f,
 +                      sizeof(id->name) - 2, 0.0f, 0.0f, "");
 +              UI_but_funcN_set(&tab->but, template_ID_set_property_cb, MEM_dupallocN(template), id);
 +              tab->but.custom_data = (void *)id;
 +              tab->unlink_ot = unlink_ot;
 +
 +              if (is_active) {
 +                      UI_but_flag_enable(&tab->but, UI_BUT_VALUE_CLEAR);
 +              }
 +              UI_but_drawflag_enable(&tab->but, but_align);
 +      }
 +
 +      if (flag & UI_ID_ADD_NEW) {
 +              const bool editable = RNA_property_editable(&template->ptr, template->prop);
 +              uiBut *but;
 +
 +              if (active_ptr.type) {
 +                      type = active_ptr.type;
 +              }
 +
 +              but = template_id_def_new_but(block, active_ptr.data, template, type, newop, editable, flag & UI_ID_OPEN, true, but_height);
 +              UI_but_drawflag_enable(but, but_align);
 +      }
 +}
 +
  static void ui_template_id(
 -        uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop,
 -        const char *openop, const char *unlinkop, int flag, int prv_rows, int prv_cols, int filter)
 +        uiLayout *layout, bContext *C,
 +        PointerRNA *ptr, const char *propname,
 +        const char *newop, const char *openop, const char *unlinkop,
 +        int flag, int prv_rows, int prv_cols, int filter, bool use_tabs)
  {
        TemplateID *template_ui;
        PropertyRNA *prop;
         *      - template_ID makes a copy of the template data and assigns it to the relevant buttons
         */
        if (template_ui->idlb) {
 -              uiLayoutRow(layout, true);
 -              template_ID(C, layout, template_ui, type, flag, newop, openop, unlinkop);
 +              if (use_tabs) {
 +                      uiLayoutRow(layout, true);
 +                      template_ID_tabs(C, layout, template_ui, type, flag, newop, openop, unlinkop);
 +              }
 +              else {
 +                      uiLayoutRow(layout, true);
 +                      template_ID(C, layout, template_ui, type, flag, newop, openop, unlinkop);
 +              }
        }
  
        MEM_freeN(template_ui);
@@@ -914,49 -743,23 +914,49 @@@ void uiTemplateID
          uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop,
          const char *openop, const char *unlinkop, int filter)
  {
 -      ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop,
 -                     UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, 0, 0, filter);
 +      ui_template_id(
 +              layout, C, ptr, propname,
 +              newop, openop, unlinkop,
 +              UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE,
 +              0, 0, filter, false);
  }
  
  void uiTemplateIDBrowse(
          uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop,
          const char *openop, const char *unlinkop, int filter)
  {
 -      ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME, 0, 0, filter);
 +      ui_template_id(
 +              layout, C, ptr, propname,
 +              newop, openop, unlinkop,
 +              UI_ID_BROWSE | UI_ID_RENAME,
 +              0, 0, filter, false);
  }
  
  void uiTemplateIDPreview(
          uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop,
          const char *openop, const char *unlinkop, int rows, int cols, int filter)
  {
 -      ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop,
 -                     UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS, rows, cols, filter);
 +      ui_template_id(
 +              layout, C, ptr, propname,
 +              newop, openop, unlinkop,
 +              UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS,
 +              rows, cols, filter, false);
 +}
 +
 +/**
 + * Version of #uiTemplateID using tabs.
 + */
 +void uiTemplateIDTabs(
 +        uiLayout *layout, bContext *C,
 +        PointerRNA *ptr, const char *propname,
 +        const char *newop, const char *openop, const char *unlinkop,
 +        int filter)
 +{
 +      ui_template_id(
 +              layout, C, ptr, propname,
 +              newop, openop, unlinkop,
 +              UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE,
 +              0, 0, filter, true);
  }
  
  /************************ ID Chooser Template ***************************/
@@@ -1018,208 -821,6 +1018,208 @@@ void uiTemplateAnyID
        uiItemFullR(sub, ptr, propID, 0, 0, 0, "", ICON_NONE);
  }
  
 +/********************* Search Template ********************/
 +
 +typedef struct TemplateSearch {
 +      uiRNACollectionSearch search_data;
 +
 +      bool use_previews;
 +      int preview_rows, preview_cols;
 +} TemplateSearch;
 +
 +static void template_search_handle_cb(bContext *C, void *arg_template, void *item)
 +{
 +      TemplateSearch *template_search = arg_template;
 +      uiRNACollectionSearch *coll_search = &template_search->search_data;
 +      StructRNA *type = RNA_property_pointer_type(&coll_search->target_ptr, coll_search->target_prop);
 +      PointerRNA item_ptr;
 +
 +      RNA_pointer_create(NULL, type, item, &item_ptr);
 +      RNA_property_pointer_set(&coll_search->target_ptr, coll_search->target_prop, item_ptr);
 +      RNA_property_update(C, &coll_search->target_ptr, coll_search->target_prop);
 +}
 +
 +static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_template)
 +{
 +      static TemplateSearch template_search;
 +      PointerRNA active_ptr;
 +
 +      /* arg_template is malloced, can be freed by parent button */
 +      template_search = *((TemplateSearch *)arg_template);
 +      active_ptr = RNA_property_pointer_get(&template_search.search_data.target_ptr,
 +                                            template_search.search_data.target_prop);
 +
 +      return template_common_search_menu(
 +                     C, region, ui_rna_collection_search_cb, &template_search,
 +                     template_search_handle_cb, active_ptr.data,
 +                     template_search.preview_rows, template_search.preview_cols);
 +}
 +
 +static void template_search_add_button_searchmenu(
 +        const bContext *C, uiLayout *layout, uiBlock *block,
 +        TemplateSearch *template_search, const bool editable)
 +{
 +      const char *ui_description = RNA_property_ui_description(template_search->search_data.target_prop);
 +
 +      template_add_button_search_menu(
 +              C, layout, block,
 +              &template_search->search_data.target_ptr, template_search->search_data.target_prop,
 +              template_search_menu, MEM_dupallocN(template_search), ui_description,
 +              template_search->use_previews, editable);
 +}
 +
 +static void template_search_add_button_name(
 +        uiBlock *block, PointerRNA *active_ptr, const StructRNA *type)
 +{
 +      uiDefAutoButR(
 +              block, active_ptr, RNA_struct_name_property(type), 0, "", ICON_NONE,
 +              0, 0, TEMPLATE_SEARCH_TEXTBUT_WIDTH, TEMPLATE_SEARCH_TEXTBUT_HEIGHT);
 +}
 +
 +static void template_search_add_button_operator(
 +        uiBlock *block, const char * const operator_name,
 +        const int opcontext, const int icon, const bool editable)
 +{
 +      if (!operator_name) {
 +              return;
 +      }
 +
 +      uiBut *but = uiDefIconButO(
 +              block, UI_BTYPE_BUT, operator_name, opcontext, icon,
 +              0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
 +
 +      if (!editable) {
 +              UI_but_drawflag_enable(but, UI_BUT_DISABLED);
 +      }
 +}
 +
 +static void template_search_buttons(
 +        const bContext *C, uiLayout *layout, TemplateSearch *template_search,
 +        const char *newop, const char *unlinkop)
 +{
 +      uiBlock *block = uiLayoutGetBlock(layout);
 +      uiRNACollectionSearch *search_data = &template_search->search_data;
 +      StructRNA *type = RNA_property_pointer_type(&search_data->target_ptr, search_data->target_prop);
 +      const bool editable = RNA_property_editable(&search_data->target_ptr, search_data->target_prop);
 +      PointerRNA active_ptr = RNA_property_pointer_get(&search_data->target_ptr, search_data->target_prop);
 +
 +      if (active_ptr.type) {
 +              /* can only get correct type when there is an active item */
 +              type = active_ptr.type;
 +      }
 +
 +      uiLayoutRow(layout, true);
 +      UI_block_align_begin(block);
 +
 +      template_search_add_button_searchmenu(C, layout, block, template_search, editable);
 +      template_search_add_button_name(block, &active_ptr, type);
 +      template_search_add_button_operator(block, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, editable);
 +      template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable);
 +
 +      UI_block_align_end(block);
 +}
 +
 +static PropertyRNA *template_search_get_searchprop(
 +        PointerRNA *targetptr, PropertyRNA *targetprop,
 +        PointerRNA *searchptr, const char * const searchpropname)
 +{
 +      PropertyRNA *searchprop;
 +
 +      if (searchptr && !searchptr->data) {
 +              searchptr = NULL;
 +      }
 +
 +      if (!searchptr && !searchpropname) {
 +              /* both NULL means we don't use a custom rna collection to search in */
 +      }
 +      else if (!searchptr && searchpropname) {
 +              RNA_warning("searchpropname defined (%s) but searchptr is missing", searchpropname);
 +      }
 +      else if (searchptr && !searchpropname) {
 +              RNA_warning("searchptr defined (%s) but searchpropname is missing", RNA_struct_identifier(searchptr->type));
 +      }
 +      else if (!(searchprop = RNA_struct_find_property(searchptr, searchpropname))) {
 +              RNA_warning("search collection property not found: %s.%s",
 +                          RNA_struct_identifier(searchptr->type), searchpropname);
 +      }
 +      else if (RNA_property_type(searchprop) != PROP_COLLECTION) {
 +              RNA_warning("search collection property is not a collection type: %s.%s",
 +                          RNA_struct_identifier(searchptr->type), searchpropname);
 +      }
 +      /* check if searchprop has same type as targetprop */
 +      else if (RNA_property_pointer_type(searchptr, searchprop) != RNA_property_pointer_type(targetptr, targetprop)) {
 +              RNA_warning("search collection items from %s.%s are not of type %s",
 +                          RNA_struct_identifier(searchptr->type), searchpropname,
 +                          RNA_struct_identifier(RNA_property_pointer_type(targetptr, targetprop)));
 +      }
 +      else {
 +              return searchprop;
 +      }
 +
 +      return NULL;
 +}
 +
 +static TemplateSearch *template_search_setup(
 +        PointerRNA *ptr, const char * const propname,
 +        PointerRNA *searchptr, const char * const searchpropname)
 +{
 +      TemplateSearch *template_search;
 +      PropertyRNA *prop, *searchprop;
 +
 +      prop = RNA_struct_find_property(ptr, propname);
 +
 +      if (!prop || RNA_property_type(prop) != PROP_POINTER) {
 +              RNA_warning("pointer property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
 +              return NULL;
 +      }
 +      searchprop = template_search_get_searchprop(ptr, prop, searchptr, searchpropname);
 +
 +      template_search = MEM_callocN(sizeof(*template_search), __func__);
 +      template_search->search_data.target_ptr = *ptr;
 +      template_search->search_data.target_prop = prop;
 +      template_search->search_data.search_ptr = *searchptr;
 +      template_search->search_data.search_prop = searchprop;
 +
 +      return template_search;
 +}
 +
 +/**
 + * Search menu to pick an item from a collection.
 + * A version of uiTemplateID that works for non-ID types.
 + */
 +void uiTemplateSearch(
 +        uiLayout *layout, bContext *C,
 +        PointerRNA *ptr, const char *propname,
 +        PointerRNA *searchptr, const char *searchpropname,
 +        const char *newop, const char *unlinkop)
 +{
 +      TemplateSearch *template_search = template_search_setup(ptr, propname, searchptr, searchpropname);
 +      if (template_search != NULL) {
 +              template_search_buttons(C, layout, template_search, newop, unlinkop);
 +              MEM_freeN(template_search);
 +      }
 +}
 +
 +void uiTemplateSearchPreview(
 +        uiLayout *layout, bContext *C,
 +        PointerRNA *ptr, const char *propname,
 +        PointerRNA *searchptr, const char *searchpropname,
 +        const char *newop, const char *unlinkop,
 +        const int rows, const int cols)
 +{
 +      TemplateSearch *template_search = template_search_setup(ptr, propname, searchptr, searchpropname);
 +
 +      if (template_search != NULL) {
 +              template_search->use_previews = true;
 +              template_search->preview_rows = rows;
 +              template_search->preview_cols = cols;
 +
 +              template_search_buttons(C, layout, template_search, newop, unlinkop);
 +
 +              MEM_freeN(template_search);
 +      }
 +}
 +
  /********************* RNA Path Builder Template ********************/
  
  /* ---------- */
@@@ -1273,7 -874,7 +1273,7 @@@ static void modifiers_convertToReal(bCo
        ob->partype = PAROBJECT;
  
        WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
 -      DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +      DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
  
        ED_undo_push(C, "Modifier convert to real");
  }
@@@ -1416,7 -1017,8 +1416,7 @@@ static uiLayout *draw_modifier
                UI_block_emboss_set(block, UI_EMBOSS_NONE);
                /* When Modifier is a simulation, show button to switch to context rather than the delete button. */
                if (modifier_can_delete(md) &&
 -                  (!modifier_is_simulation(md) ||
 -                   STREQ(scene->r.engine, RE_engine_id_BLENDER_GAME)))
 +                  !modifier_is_simulation(md))
                {
                        uiItemO(row, "", ICON_X, "OBJECT_OT_modifier_remove");
                }
@@@ -1530,70 -1132,6 +1530,70 @@@ uiLayout *uiTemplateModifier(uiLayout *
        return NULL;
  }
  
 +
 +/************************ Redo Buttons Template *************************/
 +
 +static bool template_operator_redo_property_buts_poll(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
 +{
 +      return (RNA_property_tags(prop) & OP_PROP_TAG_ADVANCED) == 0;
 +}
 +
 +static void template_operator_redo_property_buts_draw(
 +        const bContext *C, wmOperator *op,
 +        uiLayout *layout, int layout_flags,
 +        bool *r_has_advanced)
 +{
 +      if (op->type->flag & OPTYPE_MACRO) {
 +              for (wmOperator *macro_op = op->macro.first; macro_op; macro_op = macro_op->next) {
 +                      template_operator_redo_property_buts_draw(C, macro_op, layout, layout_flags, r_has_advanced);
 +              }
 +      }
 +      else {
 +              /* Might want to make label_align adjustable somehow. */
 +              eAutoPropButsReturn return_info = uiTemplateOperatorPropertyButs(
 +                      C, layout, op, r_has_advanced ? template_operator_redo_property_buts_poll : NULL,
 +                      UI_BUT_LABEL_ALIGN_NONE, layout_flags);
 +              if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) {
 +                      if (r_has_advanced) {
 +                              *r_has_advanced = true;
 +                      }
 +              }
 +      }
 +}
 +
 +void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
 +{
 +      wmOperator *op = WM_operator_last_redo(C);
 +      uiBlock *block = uiLayoutGetBlock(layout);
 +
 +      if (op == NULL) {
 +              return;
 +      }
 +
 +      /* Disable for now, doesn't fit well in popover. */
 +#if 0
 +      /* Repeat button with operator name as text. */
 +      uiItemFullO(layout, "SCREEN_OT_repeat_last", RNA_struct_ui_name(op->type->srna),
 +                  ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, NULL);
 +#endif
 +
 +      if (WM_operator_repeat_check(C, op)) {
 +#if 0
 +              bool has_advanced = false;
 +#endif
 +
 +              UI_block_func_set(block, ED_undo_operator_repeat_cb, op, NULL);
 +              template_operator_redo_property_buts_draw(C, op, layout, UI_TEMPLATE_OP_PROPS_COMPACT, NULL /* &has_advanced */ );
 +              UI_block_func_set(block, NULL, NULL, NULL); /* may want to reset to old state instead of NULLing all */
 +
 +#if 0
 +              if (has_advanced) {
 +                      uiItemO(layout, IFACE_("More..."), ICON_NONE, "SCREEN_OT_redo_last");
 +              }
 +#endif
 +      }
 +}
 +
  /************************ Constraint Template *************************/
  
  #include "DNA_constraint_types.h"
@@@ -1617,7 -1155,7 +1617,7 @@@ static void do_constraint_panels(bConte
                        Main *bmain = CTX_data_main(C);
                        if (ob->pose)
                                BKE_pose_tag_recalc(bmain, ob->pose); /* checks & sorts pose channels */
 -                      DAG_relations_tag_update(bmain);
 +                      DEG_relations_tag_update(bmain);
                        break;
                }
  #endif
         * object_test_constraints(ob);
         * if (ob->pose) BKE_pose_update_constraint_flags(ob->pose); */
        
 -      if (ob->type == OB_ARMATURE) DAG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB);
 -      else DAG_id_tag_update(&ob->id, OB_RECALC_OB);
 +      if (ob->type == OB_ARMATURE) DEG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB);
 +      else DEG_id_tag_update(&ob->id, OB_RECALC_OB);
  
        WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
  }
@@@ -3090,6 -2628,77 +3090,6 @@@ void uiTemplateLayers
        }
  }
  
 -void uiTemplateGameStates(
 -        uiLayout *layout, PointerRNA *ptr, const char *propname,
 -        PointerRNA *used_ptr, const char *used_propname, int active_state)
 -{
 -      uiLayout *uRow, *uCol;
 -      PropertyRNA *prop, *used_prop = NULL;
 -      int groups, cols, states;
 -      int group, col, state, row;
 -      int cols_per_group = 5;
 -      Object *ob = (Object *)ptr->id.data;
 -
 -      prop = RNA_struct_find_property(ptr, propname);
 -      if (!prop) {
 -              RNA_warning("states property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
 -              return;
 -      }
 -      
 -      /* the number of states determines the way we group them 
 -       *      - we want 2 rows only (for now)
 -       *      - the number of columns (cols) is the total number of buttons per row
 -       *        the 'remainder' is added to this, as it will be ok to have first row slightly wider if need be
 -       *      - for now, only split into groups if group will have at least 5 items
 -       */
 -      states = RNA_property_array_length(ptr, prop);
 -      cols = (states / 2) + (states % 2);
 -      groups = ((cols / 2) < cols_per_group) ? (1) : (cols / cols_per_group);
 -
 -      if (used_ptr && used_propname) {
 -              used_prop = RNA_struct_find_property(used_ptr, used_propname);
 -              if (!used_prop) {
 -                      RNA_warning("used layers property not found: %s.%s", RNA_struct_identifier(ptr->type), used_propname);
 -                      return;
 -              }
 -
 -              if (RNA_property_array_length(used_ptr, used_prop) < states)
 -                      used_prop = NULL;
 -      }
 -      
 -      /* layers are laid out going across rows, with the columns being divided into groups */
 -      
 -      for (group = 0; group < groups; group++) {
 -              uCol = uiLayoutColumn(layout, true);
 -              
 -              for (row = 0; row < 2; row++) {
 -                      uiBlock *block;
 -                      uiBut *but;
 -
 -                      uRow = uiLayoutRow(uCol, true);
 -                      block = uiLayoutGetBlock(uRow);
 -                      state = groups * cols_per_group * row + cols_per_group * group;
 -                      
 -                      /* add layers as toggle buts */
 -                      for (col = 0; (col < cols_per_group) && (state < states); col++, state++) {
 -                              int icon = 0;
 -                              int butlay = 1 << state;
 -
 -                              if (active_state & butlay)
 -                                      icon = ICON_LAYER_ACTIVE;
 -                              else if (used_prop && RNA_property_boolean_get_index(used_ptr, used_prop, state))
 -                                      icon = ICON_LAYER_USED;
 -                              
 -                              but = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, icon, 0, 0, UI_UNIT_X / 2, UI_UNIT_Y / 2, ptr, prop,
 -                                                       state, 0, 0, -1, -1, sca_state_name_get(ob, state));
 -                              UI_but_func_set(but, handle_layer_buttons, but, SET_INT_IN_POINTER(state));
 -                              but->type = UI_BTYPE_TOGGLE;
 -                      }
 -              }
 -      }
 -}
 -
 -
  /************************* List Template **************************/
  static void uilist_draw_item_default(
          struct uiList *ui_list, struct bContext *UNUSED(C), struct uiLayout *layout,
@@@ -3881,14 -3490,11 +3881,14 @@@ static void ui_layout_operator_buts__re
   * Draw Operator property buttons for redoing execution with different settings.
   * This function does not initialize the layout, functions can be called on the layout before and after.
   */
 -void uiTemplateOperatorPropertyButs(
 +eAutoPropButsReturn uiTemplateOperatorPropertyButs(
          const bContext *C, uiLayout *layout, wmOperator *op,
          bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *),
 -        const char label_align, const short flag)
 +        const eButLabelAlign label_align, const short flag)
  {
 +      uiBlock *block = uiLayoutGetBlock(layout);
 +      eAutoPropButsReturn return_info = 0;
 +
        if (!op->properties) {
                IDPropertyTemplate val = {0};
                op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
        /* poll() on this operator may still fail, at the moment there is no nice feedback when this happens
         * just fails silently */
        if (!WM_operator_repeat_check(C, op)) {
 -              UI_block_lock_set(uiLayoutGetBlock(layout), true, "Operator can't' redo");
 +              UI_block_lock_set(block, true, "Operator can't' redo");
  
                /* XXX, could give some nicer feedback or not show redo panel at all? */
                uiItemL(layout, IFACE_("* Redo Unsupported *"), ICON_NONE);
        }
        else {
                /* useful for macros where only one of the steps can't be re-done */
 -              UI_block_lock_clear(uiLayoutGetBlock(layout));
 +              UI_block_lock_clear(block);
        }
  
        /* menu */
                PointerRNA op_ptr;
                uiLayout *row;
  
 -              uiLayoutGetBlock(layout)->ui_operator = op;
 +              block->ui_operator = op;
  
                row = uiLayoutRow(layout, true);
                uiItemM(row, (bContext *)C, "WM_MT_operator_presets", NULL, ICON_NONE);
                op->type->ui((bContext *)C, op);
                op->layout = NULL;
  
 -              /* UI_LAYOUT_OP_SHOW_EMPTY ignored */
 +              /* UI_LAYOUT_OP_SHOW_EMPTY ignored. return_info is ignored too. We could
 +               * allow ot.ui callback to return this, but not needed right now. */
        }
        else {
                wmWindowManager *wm = CTX_wm_manager(C);
                PointerRNA ptr;
 -              int empty;
  
                RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
  
                /* main draw call */
 -              empty = uiDefAutoButsRNA(layout, &ptr, check_prop, label_align) == 0;
 +              return_info = uiDefAutoButsRNA(layout, &ptr, check_prop, label_align, (flag & UI_TEMPLATE_OP_PROPS_COMPACT));
  
 -              if (empty && (flag & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY)) {
 +              if ((return_info & UI_PROP_BUTS_NONE_ADDED) && (flag & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY)) {
                        uiItemL(layout, IFACE_("No Properties"), ICON_NONE);
                }
        }
         * but this is not so important if this button is drawn in those cases
         * (which isn't all that likely anyway) - campbell */
        if (op->properties->len) {
 -              uiBlock *block;
                uiBut *but;
                uiLayout *col; /* needed to avoid alignment errors with previous buttons */
  
  
        /* set various special settings for buttons */
        {
 -              uiBlock *block = uiLayoutGetBlock(layout);
                const bool is_popup = (block->flag & UI_BLOCK_KEEP_OPEN) != 0;
                uiBut *but;
  
                        }
                }
        }
 +
 +      return return_info;
  }
  
  /************************* Running Jobs Template **************************/
@@@ -4241,7 -3847,7 +4241,7 @@@ void uiTemplateReportsBanner(uiLayout *
  
        but = uiDefBut(block, UI_BTYPE_ROUNDBOX, 0, "", UI_UNIT_X + 10, 0, UI_UNIT_X + width, UI_UNIT_Y,
                       NULL, 0.0f, 0.0f, 0, 0, "");
-       but->col[0] = but->col[1] = but->col[2] = FTOCHAR(rti->grayscale);
+       but->col[0] = but->col[1] = but->col[2] = unit_float_to_uchar_clamp(rti->grayscale);
        but->col[3] = 255;
  
        UI_block_align_end(block);
@@@ -4329,7 -3935,6 +4329,7 @@@ void uiTemplateKeymapItemProperties(uiL
        if (propptr.data) {
                uiBut *but = uiLayoutGetBlock(layout)->buttons.last;
  
 +              WM_operator_properties_sanitize(&propptr, false);
                template_keymap_item_properties(layout, NULL, &propptr);
  
                /* attach callbacks to compensate for missing properties update,
        }
  }
  
 +/********************************* Overrides *************************************/
 +
 +void uiTemplateOverrideProperty(
 +        uiLayout *layout, PointerRNA *collection_props_ptr, PointerRNA *scene_props_ptr, const char *propname,
 +        const char *name, const char *text_ctxt, int translate, int icon,
 +        const char *custom_template)
 +{
 +      bool is_set = false;
 +      uiLayout *row, *col;
 +
 +      PointerRNA *ptr;
 +      PropertyRNA *prop;
 +
 +      IDProperty *collection_props = collection_props_ptr->data;
 +
 +      if (IDP_GetPropertyFromGroup(collection_props, propname)) {
 +              prop = RNA_struct_find_property(collection_props_ptr, propname);
 +              ptr = collection_props_ptr;
 +              is_set = RNA_property_is_set(ptr, prop);
 +      }
 +      else {
 +              /* property doesn't exist yet */
 +              prop = RNA_struct_find_property(scene_props_ptr, propname);
 +              ptr = scene_props_ptr;
 +      }
 +
 +      /* Get translated name (label). */
 +      name = RNA_translate_ui_text(name, text_ctxt, NULL, prop, translate);
 +
 +      row = uiLayoutRow(layout, false);
 +      col = uiLayoutColumn(row, false);
 +
 +      uiLayoutSetEnabled(col, is_set);
 +
 +      if (custom_template && STREQ(custom_template, "icon_view")) {
 +              uiTemplateIconView(col, ptr, propname, false, 5.0f);
 +      }
 +      else {
 +              uiItemFullR(col, ptr, prop, -1, 0, 0, name, icon);
 +      }
 +
 +      col = uiLayoutColumn(row, false);
 +      uiBut *but;
 +      uiBlock *block = uiLayoutGetBlock(col);
 +      UI_block_emboss_set(block, UI_EMBOSS_NONE);
 +
 +      if (is_set) {
 +              but = uiDefIconButO(block, UI_BTYPE_BUT, "UI_OT_unuse_property_button", WM_OP_EXEC_DEFAULT, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
 +      }
 +      else {
 +              but = uiDefIconButO(block, UI_BTYPE_BUT, "UI_OT_use_property_button", WM_OP_EXEC_DEFAULT, ICON_ZOOMIN, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
 +              /* XXX - Using existing data struct to pass another RNAPointer */
 +              but->rnasearchpoin = *scene_props_ptr;
 +      }
 +
 +      but->rnapoin = *collection_props_ptr;
 +      but->rnaprop = prop;
 +      UI_block_emboss_set(block, UI_EMBOSS);
 +}
 +
  /********************************* Color management *************************************/
  
  void uiTemplateColorspaceSettings(uiLayout *layout, PointerRNA *ptr, const char *propname)
index 60f555a61886e875a3b2decea9c0387377a88957,8c868b81239ef3eb54eb9cbd1ebb25d1b0241691..b1cacdc5db6970aa4edd0c6f464d059547b348d3
  #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 */
@@@ -686,18 -371,7 +686,18 @@@ 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;
@@@ -987,7 -635,7 +987,7 @@@ static void shadecolors4(char coltop[4]
  
  static void round_box_shade_col4_r(unsigned char r_col[4], const char col1[4], const char col2[4], const float fac)
  {
-       const int faci = FTOCHAR(fac);
+       const int faci = unit_float_to_uchar_clamp(fac);
        const int facm = 255 - faci;
  
        r_col[0] = (faci * col1[0] + facm * col2[0]) / 256;
@@@ -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])
 +static void widgetbase_outline(uiWidgetBase *wtb, unsigned int pos)
  {
 -      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;
 +      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);
 +
 +      widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2);
 +}
 +
 +static void widgetbase_set_uniform_alpha_discard(
 +        uiWidgetBase *wtb,
 +        const bool alpha_check,
 +        const float discard_factor)
 +{
 +      if (alpha_check) {
 +              wtb->uniform_params.alpha_discard = -discard_factor;
 +      }
 +      else {
 +              wtb->uniform_params.alpha_discard = discard_factor;
        }
  }
  
 -static void widgetbase_outline(uiWidgetBase *wtb)
 +static void widgetbase_set_uniform_alpha_check(
 +        uiWidgetBase *wtb,
 +        const bool alpha_check)
  {
 -      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);
 +      const float discard_factor = fabs(wtb->uniform_params.alpha_discard);
 +      widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor);
 +}
  
 -      glEnableClientState(GL_VERTEX_ARRAY);
 -      glVertexPointer(2, GL_FLOAT, 0, triangle_strip);
 -      glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2);
 -      glDisableClientState(GL_VERTEX_ARRAY);
 +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);
  }
  
 -static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol)
 +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)
  {
 -      int j, a;
 -      
 -      glEnable(GL_BLEND);
 +      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]);
 +}
  
 -      /* 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 */
 +/* keep in sync with shader */
 +#define MAX_WIDGET_BASE_BATCH 6
 +#define MAX_WIDGET_PARAMETERS 11
  
 -                              /* 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);
 +struct {
 +      Gwn_Batch *batch; /* Batch type */
 +      uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH];
 +      int count;
 +      bool enabled;
 +} g_widget_base_batch = {0};
  
 -                              /* 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);
 +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};
  
 -                              glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v);
 -                              glDrawArrays(GL_POLYGON, 0, wtb->totvert);
 +      if (g_widget_base_batch.count == 0)
 +              return;
  
 -                              GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
 +      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;
 +}
  
 -                              /* alpha fill */
 -                              glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 +void UI_widgetbase_draw_cache_begin(void)
 +{
 +      BLI_assert(g_widget_base_batch.enabled == false);
 +      g_widget_base_batch.enabled = true;
 +}
  
 -                              glColor4ubv((unsigned char *)wcol->inner);
 +void UI_widgetbase_draw_cache_end(void)
 +{
 +      BLI_assert(g_widget_base_batch.enabled == true);
 +      g_widget_base_batch.enabled = false;
  
 -                              for (a = 0; a < wtb->totvert; a++) {
 -                                      x_mid += wtb->inner_v[a][0];
 -                              }
 -                              x_mid /= wtb->totvert;
 +      glEnable(GL_BLEND);
  
 -                              glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v);
 -                              glDrawArrays(GL_POLYGON, 0, wtb->totvert);
 +      UI_widgetbase_draw_cache_flush();
  
 -                              /* 1/2 solid color */
 -                              glColor4ub(wcol->inner[0], wcol->inner[1], wcol->inner[2], 255);
 +      glDisable(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];
 -                              }
 +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);
 +              }
  
 -                              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);
 +              /* 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;
 +              }
  
 -                              glEnableClientState(GL_VERTEX_ARRAY);
 -                              glVertexPointer(2, GL_FLOAT, 0, wtb->inner_v);
 -                              glDrawArrays(GL_POLYGON, 0, wtb->totvert);
 -                              glDisableClientState(GL_VERTEX_ARRAY);
 -                      }
 +              /* 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;
                }
 -              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]);
 -                      }
 +              g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params;
 +              g_widget_base_batch.count++;
  
 -                      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);
 +              if (g_widget_base_batch.count == MAX_WIDGET_BASE_BATCH) {
 +                      UI_widgetbase_draw_cache_flush();
                }
        }
 -      
 -      /* 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 */
 +      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);
 +      }
 +}
  
 -              const unsigned char tcol[4] = {wcol->outline[0],
 -                                             wcol->outline[1],
 -                                             wcol->outline[2],
 -                                             wcol->outline[3] / WIDGET_AA_JITTER};
 -              unsigned char emboss[4];
 +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));
  
 -              widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip);
 +      glEnable(GL_BLEND);
  
 -              if (wtb->draw_emboss) {
 -                      widget_verts_to_triangle_strip_open(wtb, wtb->halfwayvert, triangle_strip_emboss);
 -                      UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss);
 +      /* 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);
                }
 +      }
  
 -              glEnableClientState(GL_VERTEX_ARRAY);
 +      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;
  
 -              for (j = 0; j < WIDGET_AA_JITTER; j++) {
 -                      glTranslate2fv(jit[j]);
 -                      
 -                      /* outline */
 -                      glColor4ubv(tcol);
 -
 -                      glVertexPointer(2, GL_FLOAT, 0, triangle_strip);
 -                      glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2);
 -
 -                      /* 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);
 -                              }
 -                      }
 -                      
 -                      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);
 -                      }
  
 -                      glTranslatef(-jit[j][0], -jit[j][1], 0.0f);
 -              }
 +      /* 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);
 +
 +              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;
                        xs = (int)(xs + 0.1f);
                        ys = (int)(ys + 0.1f);
                }
 -              
 +
                /* to indicate draggable */
                if (but->dragpoin && (but->flag & UI_ACTIVE)) {
                        float rgb[3] = {1.25f, 1.25f, 1.25f};
                        UI_icon_draw_aspect_color(xs, ys, icon, aspect, rgb);
                }
 -              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
  
 -      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);
 +
 +                                      if (fstyle->kerning == 1) {
 +                                              BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
 +                                      }
  
 -                              fixedbuf[ul_index] = '\0';
 -                              ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index);
 +                                      fixedbuf[ul_index] = '\0';
 +                                      ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index);
  
 -                              BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f);
 -                              BLF_draw(fstyle->uifont_id, "_", 2);
 +                                      BLF_position(fstyle->uifont_id, rect->xmin + font_xofs + ul_advance, rect->ymin + font_yofs, 0.0f);
 +                                      BLF_color4ubv(fstyle->uifont_id, (unsigned char *)wcol->text);
 +                                      BLF_draw(fstyle->uifont_id, "_", 2);
  
 -                              if (fstyle->kerning == 1) {
 -                                      BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
 +                                      if (fstyle->kerning == 1) {
 +                                              BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
 +                                      }
                                }
                        }
                }
        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 -1557,7 +1966,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 -1680,6 +2106,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 -1693,7 +2121,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 -1746,7 +2182,9 @@@ static struct uiWidgetColors wcol_menu 
        {204, 204, 204, 255},
        
        1,
 -      15, -15
 +      15, -15,
 +      0,
 +      0.2f,
  };
  
  /* button that starts pulldown */
@@@ -2198,9 -1760,7 +2198,9 @@@ static struct uiWidgetColors wcol_pulld
        {0, 0, 0, 255},
        
        0,
 -      25, -20
 +      25, -20,
 +      0,
 +      0.2f,
  };
  
  /* button inside menu */
@@@ -2214,9 -1774,7 +2214,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 -1788,7 +2230,9 @@@ static struct uiWidgetColors wcol_menu_
        {255, 255, 255, 255},
        
        0,
 -      25, -20
 +      25, -20,
 +      0,
 +      0.25f,
  };
  
  /* pie menus */
@@@ -2246,9 -1802,7 +2246,9 @@@ static struct uiWidgetColors wcol_pie_m
        {255, 255, 255, 255},
  
        1,
 -      10, -10
 +      10, -10,
 +      0,
 +      0.5f,
  };
  
  
@@@ -2263,9 -1817,7 +2263,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 = {0xb4, 0xb4, 0xb4, 0xff},
 +      .item = {0x19, 0x19, 0x19, 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 -1935,7 +2430,9 @@@ static struct uiWidgetColors wcol_tmp 
        {255, 255, 255, 255},
        
        0,
 -      0, 0
 +      0, 0,
 +      0,
 +      0.25f,
  };
  
  
@@@ -2441,10 -1944,8 +2441,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 -1992,6 +2491,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,9 -2076,7 +2584,9 @@@ 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);
        }
  }
  
@@@ -2716,24 -2204,23 +2716,24 @@@ 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);
 -      
 -      glColor3f(1.0f, 1.0f, 1.0f);
 -      glutil_draw_filled_arc(0.0f, M_PI * 2.0, 3.0f * U.pixelsize, 8);
 +      unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +
 +      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 -2310,18 +2823,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;
                        copy_v3_v3(col1[3], col1[2]);
                        break;
        }
 -      
 +
        /* 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);
                        
 -                      glColor4f(col1[a][0], col1[a][1], col1[a][2], alpha);
 -                      glVertex2f(sx2, sy);
 +                      immAttrib4f(col, col1[a][0], col1[a][1], col1[a][2], alpha);
 +                      immVertex2f(pos, sx2, sy);
  
 -                      glColor4f(col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
 -                      glVertex2f(sx2, sy + dy);
 +                      immAttrib4f(col, col1[a + 1][0], col1[a + 1][1], col1[a + 1][2], alpha);
 +                      immVertex2f(pos, sx2, sy + dy);
 +
 +                      immAttrib4f(col, col0[a][0], col0[a][1], col0[a][2], alpha);
 +                      immVertex2f(pos, sx1, sy);
 +
 +                      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 -2597,15 +3136,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);
@@@ -3215,30 -2666,20 +3215,30 @@@ static void ui_draw_separator(const rct
                wcol->text[2],
                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;
@@@ -3347,6 -2722,53 +3347,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)
@@@ -3497,7 -2919,7 +3497,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;
 -              
 -              /* slider part */
 +              int roundboxalign_slider = roundboxalign;
 +
                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;
 -              
 -              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);
 +              float factor, factor_ui;
 +              float factor_discard = 1.0f; /* No discard. */
 +              float value = (float)ui_but_value_get(but);
 +
 +              if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
 +                      factor = value / but->softmax;
                }
 -              
 -              /* 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);
 -              
 -              if (rect1.xmax + offs > rect->xmax) {
 -                      roundboxalign_slider = roundboxalign & ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
 -                      offs *= (rect1.xmax + offs - rect->xmax) / offs;
 +              else {
 +                      factor = (value - but->softmin) / (but->softmax - but->softmin);
 +              }
 +
 +              float width = (float)BLI_rcti_size_x(rect);
 +              factor_ui = factor * width;
 +
 +              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);
 -              
 +              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;
@@@ -3629,12 -3062,13 +3629,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;
                float bw = rgb_to_grayscale(col);
  
                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();
        }
  }
  
@@@ -3706,7 -3131,8 +3706,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);
        }
@@@ -3728,7 -3154,8 +3728,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);
@@@ -3742,13 -3169,12 +3742,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);
        
@@@ -3763,7 -3189,8 +3763,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 */
@@@ -3779,7 -3206,8 +3779,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);
@@@ -3798,7 -3226,7 +3798,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);
  
@@@ -3832,7 -3260,7 +3832,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;
@@@ -3852,9 -3280,9 +3852,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);
@@@ -3879,7 -3307,8 +3879,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 */
@@@ -3920,7 -3349,8 +3920,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);
@@@ -3943,7 -3373,8 +3943,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);
@@@ -3958,7 -3389,8 +3958,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! */
        wt->wcol = *(wt->wcol_theme);
        
                /* 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 */
@@@ -4142,16 -3511,6 +4142,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;
                        wt.wcol_theme = &btheme->tui.wcol_menu_back;
                        wt.draw = widget_menu_back;
                        break;
 -                      
 +
                /* specials */
                case UI_WTYPE_ICON:
                        wt.custom = widget_icon_has_anim;
@@@ -4274,7 -3633,7 +4274,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);
@@@ -4386,16 -3745,7 +4386,16 @@@ void ui_draw_but(const bContext *C, ARe
                                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:
                                if (but->block->flag & UI_BLOCK_LOOP)
                                        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);
                                        }
                                }
                                break;
 -                              
 +
                        case UI_BTYPE_PULLDOWN:
                                wt = widget_type(UI_WTYPE_PULLDOWN);
                                break;
                        case UI_BTYPE_LISTBOX:
                                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;
@@@ -4630,97 -3973,21 +4630,97 @@@ void ui_draw_menu_back(uiStyle *UNUSED(
                wt->draw(&wt->wcol, rect, block->flag, block->direction);
        else
                wt->draw(&wt->wcol, rect, 0, 0);
 -      
 +
        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 */
 -                      glColor3ubv((unsigned char *)wt->wcol.text);
 -                      UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymax - 8, 't');
 +                      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 */
 -                      glColor3ubv((unsigned char *)wt->wcol.text);
 -                      UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymin + 10, 'v');
 +                      UI_draw_icon_tri(BLI_rcti_cent_x(rect), rect->ymin + 10, 'v', draw_color);
                }
        }
  }
  
 +/**
 + * 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);
 +              }
 +              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)
 +{
 +      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(&wcol_menu_back, rect, block->direction, mval_origin);
 +      }
 +      else {
 +              uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK);
 +
 +              wt->state(wt, 0);
 +              wt->draw(&wt->wcol, rect, 0, 0);
 +      }
 +}
 +
  static void draw_disk_shaded(
          float start, float angle,
          float radius_int, float radius_ext, int subd,
        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();
  }
  
  
@@@ -4923,7 -4194,8 +4923,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;
                }
        }
@@@ -4989,7 -4261,8 +4989,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);
        }
  }
  
index 602a88daa14a6fff1b13454d9fefe53d00f9fc69,ce25817c1474f89743e9e2395d03fbea1b0c2053..c78342a0713708d975772aa6203346f6bfd5c13f
@@@ -54,8 -54,6 +54,8 @@@
  
  #include "BIF_gl.h"
  
 +#include "BLF_api.h"
 +
  #include "UI_interface.h"
  #include "UI_interface_icons.h"
  
@@@ -156,15 -154,18 +156,15 @@@ const unsigned char *UI_ThemeGetColorPt
                                case SPACE_CONSOLE:
                                        ts = &btheme->tconsole;
                                        break;
 -                              case SPACE_TIME:
 -                                      ts = &btheme->ttime;
 -                                      break;
                                case SPACE_NODE:
                                        ts = &btheme->tnode;
                                        break;
 -                              case SPACE_LOGIC:
 -                                      ts = &btheme->tlogic;
 -                                      break;
                                case SPACE_CLIP:
                                        ts = &btheme->tclip;
                                        break;
 +                              case SPACE_TOPBAR:
 +                                      ts = &btheme->ttopbar;
 +                                      break;
                                default:
                                        ts = &btheme->tv3d;
                                        break;
                                        headerdesel[0] = cp[0] > 10 ? cp[0] - 10 : 0;
                                        headerdesel[1] = cp[1] > 10 ? cp[1] - 10 : 0;
                                        headerdesel[2] = cp[2] > 10 ? cp[2] - 10 : 0;
 +                                      headerdesel[3] = cp[3];
                                        cp = headerdesel;
                                        break;
                                case TH_HEADER_TEXT:
                                case TH_WIDGET_EMBOSS:
                                        cp = btheme->tui.widget_emboss; break;
  
 +                              case TH_EDITOR_OUTLINE:
 +                                      cp = btheme->tui.editor_outline;
 +                                      break;
                                case TH_AXIS_X:
                                        cp = btheme->tui.xaxis; break;
                                case TH_AXIS_Y:
                                case TH_AXIS_Z:
                                        cp = btheme->tui.zaxis; break;
  
 +                              case TH_MANIPULATOR_HI:
 +                                      cp = btheme->tui.manipulator_hi; break;
 +                              case TH_MANIPULATOR_PRIMARY:
 +                                      cp = btheme->tui.manipulator_primary; break;
 +                              case TH_MANIPULATOR_SECONDARY:
 +                                      cp = btheme->tui.manipulator_secondary; break;
 +                              case TH_MANIPULATOR_A:
 +                                      cp = btheme->tui.manipulator_a; break;
 +                              case TH_MANIPULATOR_B:
 +                                      cp = btheme->tui.manipulator_b; break;
 +
                                case TH_INFO_SELECTED:
                                        cp = ts->info_selected;
                                        break;
@@@ -850,15 -836,6 +850,15 @@@ static void ui_theme_space_init_handles
        rgba_char_args_set(theme_space->act_spline, 0xdb, 0x25, 0x12, 255);
  }
  
 +static void ui_theme_space_init_manipulator_colors(bTheme *btheme)
 +{
 +      rgba_char_args_set(btheme->tui.manipulator_hi, 255, 255, 255, 255);
 +      rgba_char_args_set(btheme->tui.manipulator_primary, 222, 255, 13, 255);
 +      rgba_char_args_set(btheme->tui.manipulator_secondary, 0, 255, 255, 255);
 +      rgba_char_args_set(btheme->tui.manipulator_a, 23, 127, 23, 255);
 +      rgba_char_args_set(btheme->tui.manipulator_b, 127, 23, 23, 255);
 +}
 +
  /**
   * initialize default theme
   * \note: when you add new colors, created & saved themes need initialized
@@@ -885,7 -862,6 +885,7 @@@ void ui_theme_init_default(void
        btheme->tui.iconfile[0] = 0;
        rgba_char_args_set(btheme->tui.wcol_tooltip.text, 255, 255, 255, 255);
        rgba_char_args_set_fl(btheme->tui.widget_emboss, 1.0f, 1.0f, 1.0f, 0.02f);
 +      rgba_char_args_set_fl(btheme->tui.editor_outline, 0.25f, 0.25f, 0.25f, 1.0f);
  
        rgba_char_args_set(btheme->tui.xaxis, 220,   0,   0, 255);
        rgba_char_args_set(btheme->tui.yaxis,   0, 220,   0, 255);
        /* common (new) variables */
        ui_theme_init_new(btheme);
        
 +      /* Manipulator. */
 +      ui_theme_space_init_manipulator_colors(btheme);
 +
        /* space view3d */
        rgba_char_args_set_fl(btheme->tv3d.back,       0.225, 0.225, 0.225, 1.0);
        rgba_char_args_set(btheme->tv3d.text,       0, 0, 0, 255);
        rgba_char_args_set(btheme->tnode.console_output, 223, 202, 53, 255);  /* interface nodes */
        btheme->tnode.noodle_curving = 5;
  
 -      /* space logic */
 -      btheme->tlogic = btheme->tv3d;
 -      rgba_char_args_set(btheme->tlogic.back, 100, 100, 100, 255);
 -      
        /* space clip */
        btheme->tclip = btheme->tv3d;
  
        rgba_char_args_set(btheme->tclip.strip_select, 0xff, 0x8c, 0x00, 0xff);
        btheme->tclip.handle_vertex_size = 5;
        ui_theme_space_init_handles_color(&btheme->tclip);
 +
 +      /* space topbar */
 +      char tmp[4];
 +      btheme->ttopbar = btheme->tv3d;
 +      /* swap colors */
 +      copy_v4_v4_char(tmp, btheme->ttopbar.header);
 +      copy_v4_v4_char(btheme->ttopbar.header, btheme->ttopbar.tab_inactive);
 +      copy_v4_v4_char(btheme->ttopbar.back, tmp);
  }
  
  void ui_style_init_default(void)
@@@ -1304,35 -1273,25 +1304,35 @@@ void UI_ThemeColor4(int colorid
        
        cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
        glColor4ubv(cp);
 -
  }
  
  /* set the color with offset for shades */
  void UI_ThemeColorShade(int colorid, int offset)
  {
 -      int r, g, b;
 +      unsigned char col[4];
 +      UI_GetThemeColorShade4ubv(colorid, offset, col);
 +      glColor4ubv(col);
 +}
 +
 +void UI_ThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset)
 +{
 +      int r, g, b, a;
        const unsigned char *cp;
        
        cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
 -      r = offset + (int) cp[0];
 +      r = coloffset + (int) cp[0];
        CLAMP(r, 0, 255);
 -      g = offset + (int) cp[1];
 +      g = coloffset + (int) cp[1];
        CLAMP(g, 0, 255);
 -      b = offset + (int) cp[2];
 +      b = coloffset + (int) cp[2];
        CLAMP(b, 0, 255);
 -      glColor4ub(r, g, b, cp[3]);
 +      a = alphaoffset + (int) cp[3];
 +      CLAMP(a, 0, 255);
 +
 +      glColor4ub(r, g, b, a);
  }
 -void UI_ThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset)
 +
 +void UI_GetThemeColorShadeAlpha4ubv(int colorid, int coloffset, int alphaoffset, unsigned char col[4])
  {
        int r, g, b, a;
        const unsigned char *cp;
        CLAMP(b, 0, 255);
        a = alphaoffset + (int) cp[3];
        CLAMP(a, 0, 255);
 -      glColor4ub(r, g, b, a);
 +
 +      col[0] = r;
 +      col[1] = g;
 +      col[2] = b;
 +      col[3] = a;
  }
  
  void UI_GetThemeColorBlend3ubv(int colorid1, int colorid2, float fac, unsigned char col[3])
        col[2] = floorf((1.0f - fac) * cp1[2] + fac * cp2[2]);
  }
  
 +void UI_GetThemeColorBlend3f(int colorid1, int colorid2, float fac, float r_col[3])
 +{
 +      const unsigned char *cp1, *cp2;
 +
 +      cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1);
 +      cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2);
 +
 +      CLAMP(fac, 0.0f, 1.0f);
 +      r_col[0] = ((1.0f - fac) * cp1[0] + fac * cp2[0]) / 255.0f;
 +      r_col[1] = ((1.0f - fac) * cp1[1] + fac * cp2[1]) / 255.0f;
 +      r_col[2] = ((1.0f - fac) * cp1[2] + fac * cp2[2]) / 255.0f;
 +}
 +
  /* blend between to theme colors, and set it */
  void UI_ThemeColorBlend(int colorid1, int colorid2, float fac)
  {
@@@ -1431,12 -1373,6 +1431,12 @@@ void UI_ThemeColorBlendShadeAlpha(int c
        glColor4ub(r, g, b, a);
  }
  
 +void UI_FontThemeColor(int fontid, int colorid)
 +{
 +      unsigned char color[4];
 +      UI_GetThemeColor4ubv(colorid, color);
 +      BLF_color4ubv(fontid, color);
 +}
  
  /* get individual values, not scaled */
  float UI_GetThemeValuef(int colorid)
@@@ -1535,111 -1471,6 +1535,111 @@@ void UI_GetThemeColorShade3ubv(int colo
        col[2] = b;
  }
  
-       F3TOCHAR3(blend, col);
 +void UI_GetThemeColorBlendShade3ubv(int colorid1, int colorid2, float fac, int offset, unsigned char col[3])
 +{
 +      const unsigned char *cp1, *cp2;
 +
 +      cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1);
 +      cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2);
 +
 +      CLAMP(fac, 0.0f, 1.0f);
 +
 +      float blend[3];
 +      blend[0] = offset + floorf((1.0f - fac) * cp1[0] + fac * cp2[0]);
 +      blend[1] = offset + floorf((1.0f - fac) * cp1[1] + fac * cp2[1]);
 +      blend[2] = offset + floorf((1.0f - fac) * cp1[2] + fac * cp2[2]);
 +
++      unit_float_to_uchar_clamp_v3(col, blend);
 +}
 +
 +void UI_GetThemeColorShade4ubv(int colorid, int offset, unsigned char col[4])
 +{
 +      int r, g, b;
 +      const unsigned char *cp;
 +
 +      cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
 +      r = offset + (int) cp[0];
 +      CLAMP(r, 0, 255);
 +      g = offset + (int) cp[1];
 +      CLAMP(g, 0, 255);
 +      b = offset + (int) cp[2];
 +      CLAMP(b, 0, 255);
 +
 +      col[0] = r;
 +      col[1] = g;
 +      col[2] = b;
 +      col[3] = cp[3];
 +}
 +
 +void UI_GetThemeColorShadeAlpha4fv(int colorid, int coloffset, int alphaoffset, float col[4])
 +{
 +      int r, g, b, a;
 +      const unsigned char *cp;
 +
 +      cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
 +
 +      r = coloffset + (int) cp[0];
 +      CLAMP(r, 0, 255);
 +      g = coloffset + (int) cp[1];
 +      CLAMP(g, 0, 255);
 +      b = coloffset + (int) cp[2];
 +      CLAMP(b, 0, 255);
 +      a = alphaoffset + (int) cp[3];
 +      CLAMP(b, 0, 255);
 +
 +      col[0] = ((float)r) / 255.0f;
 +      col[1] = ((float)g) / 255.0f;
 +      col[2] = ((float)b) / 255.0f;
 +      col[3] = ((float)a) / 255.0f;
 +}
 +
 +void UI_GetThemeColorBlendShade3fv(int colorid1, int colorid2, float fac, int offset, float col[3])
 +{
 +      int r, g, b;
 +      const unsigned char *cp1, *cp2;
 +
 +      cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1);
 +      cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2);
 +
 +      CLAMP(fac, 0.0f, 1.0f);
 +
 +      r = offset + floorf((1.0f - fac) * cp1[0] + fac * cp2[0]);
 +      CLAMP(r, 0, 255);
 +      g = offset + floorf((1.0f - fac) * cp1[1] + fac * cp2[1]);
 +      CLAMP(g, 0, 255);
 +      b = offset + floorf((1.0f - fac) * cp1[2] + fac * cp2[2]);
 +      CLAMP(b, 0, 255);
 +
 +      col[0] = ((float)r) / 255.0f;
 +      col[1] = ((float)g) / 255.0f;
 +      col[2] = ((float)b) / 255.0f;
 +}
 +
 +void UI_GetThemeColorBlendShade4fv(int colorid1, int colorid2, float fac, int offset, float col[4])
 +{
 +      int r, g, b, a;
 +      const unsigned char *cp1, *cp2;
 +
 +      cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1);
 +      cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2);
 +
 +      CLAMP(fac, 0.0f, 1.0f);
 +
 +      r = offset + floorf((1.0f - fac) * cp1[0] + fac * cp2[0]);
 +      CLAMP(r, 0, 255);
 +      g = offset + floorf((1.0f - fac) * cp1[1] + fac * cp2[1]);
 +      CLAMP(g, 0, 255);
 +      b = offset + floorf((1.0f - fac) * cp1[2] + fac * cp2[2]);
 +      CLAMP(b, 0, 255);
 +      a = offset + floorf((1.0f - fac) * cp1[3] + fac * cp2[3]);
 +      CLAMP(a, 0, 255);
 +
 +      col[0] = ((float)r) / 255.0f;
 +      col[1] = ((float)g) / 255.0f;
 +      col[2] = ((float)b) / 255.0f;
 +      col[3] = ((float)a) / 255.0f;
 +}
 +
  /* get the color, in char pointer */
  void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
  {
@@@ -1828,9 -1659,11 +1828,9 @@@ void init_userdef_do_versions(void
                U.savetime = 1;
  // XXX                error(STRINGIFY(BLENDER_STARTUP_FILE)" is buggy, please consider removing it.\n");
        }
 -      /* transform widget settings */
 -      if (U.tw_hotspot == 0) {
 -              U.tw_hotspot = 14;
 -              U.tw_size = 25;          /* percentage of window size */
 -              U.tw_handlesize = 16;    /* percentage of widget radius */
 +      if (U.manipulator_size == 0) {
 +              U.manipulator_size = 75;
 +              U.manipulator_flag |= USER_MANIPULATOR_DRAW;
        }
        if (U.pad_rot_angle == 0.0f)
                U.pad_rot_angle = 15.0f;
  
                        if (btheme->tui.wcol_num.outline[3] == 0)
                                ui_widget_color_init(&btheme->tui);
 -                      
 -                      /* Logic editor theme, check for alpha==0 is safe, then color was never set */
 -                      if (btheme->tlogic.syntaxn[3] == 0) {
 -                              /* re-uses syntax color storage */
 -                              btheme->tlogic = btheme->tv3d;
 -                              rgba_char_args_set(btheme->tlogic.back, 100, 100, 100, 255);
 -                      }
  
                        rgba_char_args_set_fl(btheme->tinfo.back, 0.45, 0.45, 0.45, 1.0);
                        rgba_char_args_set_fl(btheme->tuserpref.back, 0.45, 0.45, 0.45, 1.0);
                                strcpy(km->idname, "3D View Generic");
                        else if (STREQ(km->idname, "EditMesh"))
                                strcpy(km->idname, "Mesh");
 -                      else if (STREQ(km->idname, "TimeLine"))
 -                              strcpy(km->idname, "Timeline");
                        else if (STREQ(km->idname, "UVEdit"))
                                strcpy(km->idname, "UV Editor");
                        else if (STREQ(km->idname, "Animation_Channels"))
                                strcpy(km->idname, "Property Editor");
                }
        }
 -      if (!USER_VERSION_ATLEAST(250, 16)) {
 -              if (U.wmdrawmethod == USER_DRAW_TRIPLE)
 -                      U.wmdrawmethod = USER_DRAW_AUTOMATIC;
 -      }
        
        if (!USER_VERSION_ATLEAST(252, 3)) {
                if (U.flag & USER_LMOUSESELECT) 
        
        if (!USER_VERSION_ATLEAST(269, 9)) {
                bTheme *btheme;
 -              
 -              U.tw_size = U.tw_size * 5.0f;
 -              
                /* Action Editor (and NLA Editor) - Keyframe Colors */
                /* Graph Editor - larger vertex size defaults */
                for (btheme = U.themes.first; btheme; btheme = btheme->next) {
                        rgba_char_args_set(btheme->tnode.gp_vertex, 0, 0, 0, 255);
                        rgba_char_args_set(btheme->tnode.gp_vertex_select, 255, 133, 0, 255);
                        btheme->tnode.gp_vertex_size = 3;
 -                      
 -                      /* Timeline Keyframe Indicators */
 -                      rgba_char_args_set(btheme->ttime.time_keyframe, 0xDD, 0xD7, 0x00, 0xFF);
 -                      rgba_char_args_set(btheme->ttime.time_gp_keyframe, 0xB5, 0xE6, 0x1D, 0xFF);
                }
        }
  
                U.transopts &= ~(
                    USER_TR_DEPRECATED_2 | USER_TR_DEPRECATED_3 | USER_TR_DEPRECATED_4 |
                    USER_TR_DEPRECATED_6 | USER_TR_DEPRECATED_7);
 -              U.gameflags &= ~(
 -                  USER_GL_RENDER_DEPRECATED_0 | USER_GL_RENDER_DEPRECATED_1 |
 -                  USER_GL_RENDER_DEPRECATED_3 | USER_GL_RENDER_DEPRECATED_4);
  
                U.uiflag |= USER_LOCK_CURSOR_ADJUST;
        }
  
 +      if (!USER_VERSION_ATLEAST(280, 9)) {
 +              /* interface_widgets.c */
 +              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
 +              };
 +
 +              for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) {
 +                      char tmp[4];
 +
 +                      btheme->tui.wcol_tab = wcol_tab;
 +                      btheme->ttopbar = btheme->tv3d;
 +                      /* swap colors */
 +                      copy_v4_v4_char(tmp, btheme->ttopbar.header);
 +                      copy_v4_v4_char(btheme->ttopbar.header, btheme->ttopbar.tab_inactive);
 +                      copy_v4_v4_char(btheme->ttopbar.back, tmp);
 +              }
 +      }
 +      
 +      if (!USER_VERSION_ATLEAST(280, 9)) {
 +              /* Timeline removal */
 +              for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) {
 +                      if (btheme->tipo.anim_active[3] == 0) {
 +                              rgba_char_args_set(btheme->tipo.anim_active,    204, 112, 26, 102);
 +                      }
 +                      if (btheme->tseq.anim_active[3] == 0) {
 +                              rgba_char_args_set(btheme->tseq.anim_active,    204, 112, 26, 102);     
 +                      }
 +              }
 +      }
 +
 +      if (!USER_VERSION_ATLEAST(280, 10)) {
 +              /* Roundness */
 +              for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) {
 +                      btheme->tui.wcol_regular.roundness = 0.25f;
 +                      btheme->tui.wcol_tool.roundness = 0.2f;
 +                      btheme->tui.wcol_text.roundness = 0.2f;
 +                      btheme->tui.wcol_radio.roundness = 0.2f;
 +                      btheme->tui.wcol_option.roundness = 0.333333f;
 +                      btheme->tui.wcol_toggle.roundness = 0.25f;
 +                      btheme->tui.wcol_num.roundness = 0.5f;
 +                      btheme->tui.wcol_numslider.roundness = 0.5f;
 +                      btheme->tui.wcol_tab.roundness = 0.25f;
 +                      btheme->tui.wcol_menu.roundness = 0.2f;
 +                      btheme->tui.wcol_pulldown.roundness = 0.2f;
 +                      btheme->tui.wcol_menu_back.roundness = 0.25f;
 +                      btheme->tui.wcol_menu_item.roundness = 0.25f;
 +                      btheme->tui.wcol_tooltip.roundness = 0.25f;
 +                      btheme->tui.wcol_box.roundness = 0.2f;
 +                      btheme->tui.wcol_scroll.roundness = 0.5f;
 +                      btheme->tui.wcol_progress.roundness = 0.25f;
 +                      btheme->tui.wcol_list_item.roundness = 0.2f;
 +                      btheme->tui.wcol_pie_menu.roundness = 0.5f;
 +                      rgba_char_args_set_fl(btheme->tui.editor_outline, 0.25f, 0.25f, 0.25f, 1.0f);
 +              }
 +      }
 +
 +      if (((bTheme *)U.themes.first)->tui.wcol_toolbar_item.text[3] == 0) {
 +              struct uiWidgetColors wcol_toolbar_item = {
 +                      .outline = {0x19, 0x19, 0x19, 0xff},
 +                      .inner = {0x46, 0x46, 0x46, 0xff},
 +                      .inner_sel = {0xb4, 0xb4, 0xb4, 0xff},
 +                      .item = {0x19, 0x19, 0x19, 0xff},
 +
 +                      .text = {0xff, 0xff, 0xff, 0xff},
 +                      .text_sel = {0x33, 0x33, 0x33, 0xff},
 +
 +                      .shaded = 0,
 +                      .shadetop = 0,
 +                      .shadedown = 0,
 +                      .alpha_check = 0,
 +                      .roundness = 0.3f,
 +              };
 +              for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) {
 +                      btheme->tui.wcol_toolbar_item = wcol_toolbar_item;
 +                      btheme->tui.icon_saturation = 0.4f;
 +              }
 +      }
 +
        /**
         * Include next version bump.
         */
                /* (keep this block even if it becomes empty). */
        }
  
 +      if (((bTheme *)U.themes.first)->tui.manipulator_hi[3] == 0) {
 +              for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) {
 +                      ui_theme_space_init_manipulator_colors(btheme);
 +              }
 +      }
 +
        if (U.pixelsize == 0.0f)
                U.pixelsize = 1.0f;
        
        if (U.image_draw_method == 0)
                U.image_draw_method = IMAGE_DRAW_METHOD_2DTEXTURE;
        
 -      // keep the following until the new audaspace is default to be built with
 -#ifdef WITH_SYSTEM_AUDASPACE
        // we default to the first audio device
        U.audiodevice = 0;
 -#endif
  
        /* Not versioning, just avoid errors. */
  #ifndef WITH_CYCLES
index 83a5a0d0b1ba6dd2fd3d28c86811e621a6faedfa,37096f9e660197cc79b4ecc60df9433c4547c9fd..c2ce9d8378288eda490ffbd53d2b7c0e5f979e7a
  
  #include "BKE_colorband.h"
  #include "BKE_context.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_brush.h"
  #include "BKE_image.h"
  #include "BKE_paint.h"
  #include "BKE_report.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "ED_paint.h"
  #include "ED_screen.h"
  
@@@ -433,7 -432,7 +433,7 @@@ static ImBuf *brush_painter_imbuf_new(B
                                unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4;
  
                                rgb_float_to_uchar(dst, rgba);
-                               dst[3] = FTOCHAR(rgba[3]);
+                               dst[3] = unit_float_to_uchar_clamp(rgba[3]);
                        }
                }
        }
@@@ -1368,7 -1367,7 +1368,7 @@@ void paint_2d_redraw(const bContext *C
  
                /* compositor listener deals with updating */
                WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image);
 -              DAG_id_tag_update(&s->image->id, 0);
 +              DEG_id_tag_update(&s->image->id, 0);
        }
        else {
                if (!s->sima || !s->sima->lock)
index cacb75b26689dda3147824d15f75971d4651bed4,7784926128f6282a3511d815bddf778c69e01d8d..8c148dcb313c5c52f24b9537df1143b0c17246db
@@@ -65,6 -65,7 +65,6 @@@
  #include "BKE_colorband.h"
  #include "BKE_context.h"
  #include "BKE_colortools.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_DerivedMesh.h"
  #include "BKE_idprop.h"
  #include "BKE_brush.h"
@@@ -80,8 -81,6 +80,8 @@@
  #include "BKE_scene.h"
  #include "BKE_texture.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "UI_interface.h"
  
  #include "ED_mesh.h"
  static void partial_redraw_array_init(ImagePaintPartialRedraw *pr);
  
  /* Defines and Structs */
- /* FTOCHAR as inline function */
+ /* unit_float_to_uchar_clamp as inline function */
  BLI_INLINE unsigned char f_to_char(const float val)
  {
-       return FTOCHAR(val);
+       return unit_float_to_uchar_clamp(val);
  }
  
  /* ProjectionPaint defines */
@@@ -227,7 -226,6 +227,7 @@@ typedef struct ProjPaintState 
        View3D *v3d;
        RegionView3D *rv3d;
        ARegion *ar;
 +      Depsgraph *depsgraph;
        Scene *scene;
        int source; /* PROJ_SRC_**** */
  
        bool  do_backfacecull;          /* ignore faces with normals pointing away, skips a lot of raycasts if your normals are correctly flipped */
        bool  do_mask_normal;           /* mask out pixels based on their normals */
        bool  do_mask_cavity;           /* mask out pixels based on cavity */
 -      bool  do_new_shading_nodes;     /* cache BKE_scene_use_new_shading_nodes value */
        float normal_angle;             /* what angle to mask at */
        float normal_angle__cos;         /* cos(normal_angle), faster to compare */
        float normal_angle_inner;
@@@ -2491,7 -2490,8 +2491,7 @@@ static bool IsectPoly2Df_twoside(const 
  static void project_paint_face_init(
          const ProjPaintState *ps,
          const int thread_index, const int bucket_index, const int tri_index, const int image_index,
 -        const rctf *clip_rect, const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf,
 -        const bool clamp_u, const bool clamp_v)
 +        const rctf *clip_rect, const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf)
  {
        /* Projection vars, to get the 3D locations into screen space  */
        MemArena *arena = ps->arena_mt[thread_index];
  #endif
  
                if (pixel_bounds_array(uv_clip, &bounds_px, ibuf->x, ibuf->y, uv_clip_tot)) {
 -
 -                      if (clamp_u) {
 -                              CLAMP(bounds_px.xmin, 0, ibuf->x);
 -                              CLAMP(bounds_px.xmax, 0, ibuf->x);
 -                      }
 -
 -                      if (clamp_v) {
 -                              CLAMP(bounds_px.ymin, 0, ibuf->y);
 -                              CLAMP(bounds_px.ymax, 0, ibuf->y);
 -                      }
 -
  #if 0
                        project_paint_undo_tiles_init(&bounds_px, ps->projImages + image_index, tmpibuf,
                                                      tile_width, threaded, ps->do_masking);
@@@ -2914,16 -2925,19 +2914,16 @@@ static void project_bucket_init
        int tri_index, image_index = 0;
        ImBuf *ibuf = NULL;
        Image *tpage_last = NULL, *tpage;
 -      Image *ima = NULL;
        ImBuf *tmpibuf = NULL;
  
        if (ps->image_tot == 1) {
                /* Simple loop, no context switching */
                ibuf = ps->projImages[0].ibuf;
 -              ima = ps->projImages[0].ima;
  
                for (node = ps->bucketFaces[bucket_index]; node; node = node->next) {
                        project_paint_face_init(
                                ps, thread_index, bucket_index, GET_INT_FROM_POINTER(node->link), 0,
 -                              clip_rect, bucket_bounds, ibuf, &tmpibuf,
 -                              (ima->tpageflag & IMA_CLAMP_U) != 0, (ima->tpageflag & IMA_CLAMP_V) != 0);
 +                              clip_rect, bucket_bounds, ibuf, &tmpibuf);
                }
        }
        else {
                                for (image_index = 0; image_index < ps->image_tot; image_index++) {
                                        if (ps->projImages[image_index].ima == tpage_last) {
                                                ibuf = ps->projImages[image_index].ibuf;
 -                                              ima = ps->projImages[image_index].ima;
                                                break;
                                        }
                                }
  
                        project_paint_face_init(
                                ps, thread_index, bucket_index, tri_index, image_index,
 -                              clip_rect, bucket_bounds, ibuf, &tmpibuf,
 -                              (ima->tpageflag & IMA_CLAMP_U) != 0, (ima->tpageflag & IMA_CLAMP_V) != 0);
 +                              clip_rect, bucket_bounds, ibuf, &tmpibuf);
                }
        }
  
@@@ -3117,7