Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/interface')
-rw-r--r--source/blender/editors/interface/interface_draw.c232
-rw-r--r--source/blender/editors/interface/interface_handlers.c335
-rw-r--r--source/blender/editors/interface/interface_intern.h5
-rw-r--r--source/blender/editors/interface/interface_query.c3
-rw-r--r--source/blender/editors/interface/interface_templates.c608
-rw-r--r--source/blender/editors/interface/interface_widgets.c4
6 files changed, 1184 insertions, 3 deletions
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 72c31c7b39e..499842a570b 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -27,16 +27,21 @@
#include "DNA_color_types.h"
#include "DNA_screen_types.h"
#include "DNA_movieclip_types.h"
+#include "DNA_curveprofile_types.h"
#include "BLI_math.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLI_polyfill_2d.h"
+
+#include "MEM_guardedalloc.h"
#include "BKE_colorband.h"
#include "BKE_colortools.h"
#include "BKE_node.h"
#include "BKE_tracking.h"
+#include "BKE_curveprofile.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -1814,6 +1819,8 @@ static void ui_draw_but_curve_grid(
immVertex2f(pos, rect->xmax, fy);
fy += dy;
}
+ /* Note: Assertion fails with here when the view is moved farther below the center.
+ * Missing two points from the number given with immBegin. */
immEnd();
}
@@ -2113,6 +2120,231 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, const uiWidgetColors *wcol, cons
immUnbindProgram();
}
+/** Used to draw a curve profile widget. Somewhat similar to ui_draw_but_CURVE */
+void ui_draw_but_CURVEPROFILE(ARegion *ar,
+ uiBut *but,
+ const uiWidgetColors *wcol,
+ const rcti *rect)
+{
+ uint i;
+ float fx, fy;
+ CurveProfile *profile;
+ if (but->editprofile) {
+ profile = but->editprofile;
+ }
+ else {
+ profile = (CurveProfile *)but->poin;
+ }
+
+ /* Calculate offset and zoom */
+ float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect);
+ float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect);
+ float offsx = profile->view_rect.xmin - (1.0f / zoomx);
+ float offsy = profile->view_rect.ymin - (1.0f / zoomy);
+
+ /* Exit early if too narrow */
+ if (zoomx == 0.0f) {
+ return;
+ }
+
+ /* Test needed because path can draw outside of boundary */
+ int scissor[4];
+ GPU_scissor_get_i(scissor);
+ rcti scissor_new = {
+ .xmin = rect->xmin,
+ .ymin = rect->ymin,
+ .xmax = rect->xmax,
+ .ymax = rect->ymax,
+ };
+ rcti scissor_region = {0, ar->winx, 0, ar->winy};
+ BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new);
+ GPU_scissor(scissor_new.xmin,
+ scissor_new.ymin,
+ BLI_rcti_size_x(&scissor_new),
+ BLI_rcti_size_y(&scissor_new));
+
+ GPU_line_width(1.0f);
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ /* Backdrop */
+ float color_backdrop[4] = {0, 0, 0, 1};
+ if (profile->flag & PROF_USE_CLIP) {
+ gl_shaded_color_get_fl((uchar *)wcol->inner, -20, color_backdrop);
+ immUniformColor3fv(color_backdrop);
+ immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+ immUniformColor3ubv((uchar *)wcol->inner);
+ immRectf(pos,
+ rect->xmin + zoomx * (profile->clip_rect.xmin - offsx),
+ rect->ymin + zoomy * (profile->clip_rect.ymin - offsy),
+ rect->xmin + zoomx * (profile->clip_rect.xmax - offsx),
+ rect->ymin + zoomy * (profile->clip_rect.ymax - offsy));
+ }
+ else {
+ rgb_uchar_to_float(color_backdrop, (uchar *)wcol->inner);
+ immUniformColor3fv(color_backdrop);
+ immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+ }
+
+ /* 0.25 step grid */
+ gl_shaded_color((uchar *)wcol->inner, -16);
+ ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.25f);
+ /* 1.0 step grid */
+ gl_shaded_color((uchar *)wcol->inner, -24);
+ ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 1.0f);
+
+ /* Draw the path's fill */
+ if (profile->table == NULL) {
+ BKE_curveprofile_update(profile, false);
+ }
+ CurveProfilePoint *pts = profile->table;
+ /* Also add the last points on the right and bottom edges to close off the fill polygon */
+ bool add_left_tri = profile->view_rect.xmin < 0.0f;
+ bool add_bottom_tri = profile->view_rect.ymin < 0.0f;
+ uint tot_points = (uint)PROF_N_TABLE(profile->path_len) + 1 + add_left_tri + add_bottom_tri;
+ uint tot_triangles = tot_points - 2;
+
+ /* Create array of the positions of the table's points */
+ float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords");
+ for (i = 0; i < (uint)PROF_N_TABLE(profile->path_len);
+ i++) { /* Only add the points from the table here */
+ table_coords[i][0] = pts[i].x;
+ table_coords[i][1] = pts[i].y;
+ }
+ if (add_left_tri && add_bottom_tri) {
+ /* Add left side, bottom left corner, and bottom side points */
+ table_coords[tot_points - 3][0] = profile->view_rect.xmin;
+ table_coords[tot_points - 3][1] = 1.0f;
+ table_coords[tot_points - 2][0] = profile->view_rect.xmin;
+ table_coords[tot_points - 2][1] = profile->view_rect.ymin;
+ table_coords[tot_points - 1][0] = 1.0f;
+ table_coords[tot_points - 1][1] = profile->view_rect.ymin;
+ }
+ else if (add_left_tri) {
+ /* Add the left side and bottom left corner points */
+ table_coords[tot_points - 2][0] = profile->view_rect.xmin;
+ table_coords[tot_points - 2][1] = 1.0f;
+ table_coords[tot_points - 1][0] = profile->view_rect.xmin;
+ table_coords[tot_points - 1][1] = 0.0f;
+ }
+ else if (add_bottom_tri) {
+ /* Add the bottom side and bottom left corner points */
+ table_coords[tot_points - 2][0] = 0.0f;
+ table_coords[tot_points - 2][1] = profile->view_rect.ymin;
+ table_coords[tot_points - 1][0] = 1.0f;
+ table_coords[tot_points - 1][1] = profile->view_rect.ymin;
+ }
+ else {
+ /* Just add the bottom corner point. Side points would be redundant anyway */
+ table_coords[tot_points - 1][0] = 0.0f;
+ table_coords[tot_points - 1][1] = 0.0f;
+ }
+
+ /* Calculate the table point indices of the triangles for the profile's fill */
+ uint(*tri_indices)[3] = MEM_mallocN(sizeof(*tri_indices) * tot_triangles, "return tri indices");
+ BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices);
+
+ /* Draw the triangles for the profile fill */
+ immUniformColor3ubvAlpha((const uchar *)wcol->item, 128);
+ GPU_blend(true);
+ GPU_polygon_smooth(false);
+ immBegin(GPU_PRIM_TRIS, 3 * tot_triangles);
+ for (i = 0; i < tot_triangles; i++) {
+ for (uint j = 0; j < 3; j++) {
+ uint *tri = tri_indices[i];
+ fx = rect->xmin + zoomx * (table_coords[tri[j]][0] - offsx);
+ fy = rect->ymin + zoomy * (table_coords[tri[j]][1] - offsy);
+ immVertex2f(pos, fx, fy);
+ }
+ }
+ immEnd();
+ MEM_freeN(tri_indices);
+
+ /* Draw the profile's path so the edge stands out a bit */
+ tot_points -= (add_left_tri + add_left_tri);
+ GPU_line_width(1.0f);
+ immUniformColor3ubvAlpha((const uchar *)wcol->item, 255);
+ GPU_line_smooth(true);
+ immBegin(GPU_PRIM_LINE_STRIP, tot_points - 1);
+ for (i = 0; i < tot_points - 1; i++) {
+ fx = rect->xmin + zoomx * (table_coords[i][0] - offsx);
+ fy = rect->ymin + zoomy * (table_coords[i][1] - offsy);
+ immVertex2f(pos, fx, fy);
+ }
+ immEnd();
+ immUnbindProgram();
+ MEM_freeN(table_coords);
+
+ /* New GPU instructions for control points and sampled points. */
+ format = immVertexFormat();
+ pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+
+ /* Calculate vertex colors based on text theme. */
+ float color_vert[4], color_vert_select[4], color_sample[4];
+ UI_GetThemeColor4fv(TH_TEXT_HI, color_vert);
+ UI_GetThemeColor4fv(TH_TEXT, color_vert_select);
+ color_sample[0] = (float)wcol->item[0] / 255.0f;
+ color_sample[1] = (float)wcol->item[1] / 255.0f;
+ color_sample[2] = (float)wcol->item[2] / 255.0f;
+ color_sample[3] = (float)wcol->item[3] / 255.0f;
+ if (len_squared_v3v3(color_vert, color_vert_select) < 0.1f) {
+ interp_v3_v3v3(color_vert, color_vert_select, color_backdrop, 0.75f);
+ }
+ if (len_squared_v3(color_vert) > len_squared_v3(color_vert_select)) {
+ /* Ensure brightest text color is used for selection. */
+ swap_v3_v3(color_vert, color_vert_select);
+ }
+
+ /* Draw the control points. */
+ pts = profile->path;
+ tot_points = (uint)profile->path_len;
+ GPU_line_smooth(false);
+ GPU_blend(false);
+ GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f)));
+ immBegin(GPU_PRIM_POINTS, tot_points);
+ for (i = 0; i < tot_points; i++) {
+ fx = rect->xmin + zoomx * (pts[i].x - offsx);
+ fy = rect->ymin + zoomy * (pts[i].y - offsy);
+ immAttr4fv(col, (pts[i].flag & PROF_SELECT) ? color_vert_select : color_vert);
+ immVertex2f(pos, fx, fy);
+ }
+ immEnd();
+
+ /* Draw the sampled points in addition to the control points if they have been created */
+ pts = profile->segments;
+ tot_points = (uint)profile->segments_len;
+ if (tot_points > 0 && pts) {
+ GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 3.0f, 3.0f)));
+ immBegin(GPU_PRIM_POINTS, tot_points);
+ for (i = 0; i < tot_points; i++) {
+ fx = rect->xmin + zoomx * (pts[i].x - offsx);
+ fy = rect->ymin + zoomy * (pts[i].y - offsy);
+ immAttr4fv(col, color_sample);
+ immVertex2f(pos, fx, fy);
+ }
+ immEnd();
+ }
+
+ immUnbindProgram();
+
+ /* restore scissortest */
+ GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
+
+ /* Outline */
+ format = immVertexFormat();
+ pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ immUniformColor3ubv((const uchar *)wcol->outline);
+ imm_draw_box_wire_2d(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+
+ immUnbindProgram();
+}
+
void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(ar),
uiBut *but,
const uiWidgetColors *UNUSED(wcol),
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 806b5789df1..673e4c1a8da 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -32,7 +32,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
-
+#include "DNA_curveprofile_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -57,6 +57,7 @@
#include "BKE_tracking.h"
#include "BKE_unit.h"
#include "BKE_paint.h"
+#include "BKE_curveprofile.h"
#include "IMB_colormanagement.h"
@@ -442,6 +443,8 @@ static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut
static ColorBand but_copypaste_coba = {0};
static CurveMapping but_copypaste_curve = {0};
static bool but_copypaste_curve_alive = false;
+static CurveProfile but_copypaste_profile = {0};
+static bool but_copypaste_profile_alive = false;
/** \} */
@@ -1080,6 +1083,13 @@ static void ui_apply_but_CURVE(bContext *C, uiBut *but, uiHandleButtonData *data
data->applied = true;
}
+static void ui_apply_but_CURVEPROFILE(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_apply_but_func(C, but);
+ data->retval = but->retval;
+ data->applied = true;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1979,6 +1989,7 @@ static void ui_apply_but(
float *editvec;
ColorBand *editcoba;
CurveMapping *editcumap;
+ CurveProfile *editprofile;
data->retval = 0;
@@ -2036,11 +2047,13 @@ static void ui_apply_but(
editvec = but->editvec;
editcoba = but->editcoba;
editcumap = but->editcumap;
+ editprofile = but->editprofile;
but->editstr = NULL;
but->editval = NULL;
but->editvec = NULL;
but->editcoba = NULL;
but->editcumap = NULL;
+ but->editprofile = NULL;
/* handle different types */
switch (but->type) {
@@ -2100,6 +2113,9 @@ static void ui_apply_but(
case UI_BTYPE_CURVE:
ui_apply_but_CURVE(C, but, data);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ ui_apply_but_CURVEPROFILE(C, but, data);
+ break;
case UI_BTYPE_KEY_EVENT:
case UI_BTYPE_HOTKEY_EVENT:
ui_apply_but_BUT(C, but, data);
@@ -2147,6 +2163,7 @@ static void ui_apply_but(
but->editvec = editvec;
but->editcoba = editcoba;
but->editcumap = editcumap;
+ but->editprofile = editprofile;
}
/** \} */
@@ -2446,6 +2463,29 @@ static void ui_but_paste_curvemapping(bContext *C, uiBut *but)
}
}
+static void ui_but_copy_CurveProfile(uiBut *but)
+{
+ if (but->poin != NULL) {
+ but_copypaste_profile_alive = true;
+ BKE_curveprofile_free_data(&but_copypaste_profile);
+ BKE_curveprofile_copy_data(&but_copypaste_profile, (CurveProfile *)but->poin);
+ }
+}
+
+static void ui_but_paste_CurveProfile(bContext *C, uiBut *but)
+{
+ if (but_copypaste_profile_alive) {
+ if (!but->poin) {
+ but->poin = MEM_callocN(sizeof(CurveProfile), "CurveProfile");
+ }
+
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ BKE_curveprofile_free_data((CurveProfile *)but->poin);
+ BKE_curveprofile_copy_data((CurveProfile *)but->poin, &but_copypaste_profile);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+}
+
static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_len_max)
{
PointerRNA *opptr;
@@ -2540,6 +2580,10 @@ static void ui_but_copy(bContext *C, uiBut *but, const bool copy_array)
ui_but_copy_curvemapping(but);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ ui_but_copy_CurveProfile(but);
+ break;
+
case UI_BTYPE_BUT:
if (!but->optype) {
break;
@@ -2623,6 +2667,10 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons
ui_but_paste_curvemapping(C, but);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ ui_but_paste_CurveProfile(C, but);
+ break;
+
default:
break;
}
@@ -2633,6 +2681,7 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons
void ui_but_clipboard_free(void)
{
BKE_curvemapping_free_data(&but_copypaste_curve);
+ BKE_curveprofile_free_data(&but_copypaste_profile);
}
/** \} */
@@ -3757,6 +3806,9 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
if (but->type == UI_BTYPE_CURVE) {
but->editcumap = (CurveMapping *)but->poin;
}
+ if (but->type == UI_BTYPE_CURVEPROFILE) {
+ but->editprofile = (CurveProfile *)but->poin;
+ }
else if (but->type == UI_BTYPE_COLORBAND) {
data->coba = (ColorBand *)but->poin;
but->editcoba = data->coba;
@@ -3847,6 +3899,7 @@ static void ui_numedit_end(uiBut *but, uiHandleButtonData *data)
but->editvec = NULL;
but->editcoba = NULL;
but->editcumap = NULL;
+ but->editprofile = NULL;
data->dragstartx = 0;
data->draglastx = 0;
@@ -6803,6 +6856,281 @@ static int ui_do_but_CURVE(
return WM_UI_HANDLER_CONTINUE;
}
+/* Same as ui_numedit_but_CURVE with some smaller changes. */
+static bool ui_numedit_but_CURVEPROFILE(uiBlock *block,
+ uiBut *but,
+ uiHandleButtonData *data,
+ int evtx,
+ int evty,
+ bool snap,
+ const bool shift)
+{
+ CurveProfile *profile = (CurveProfile *)but->poin;
+ CurveProfilePoint *pts = profile->path;
+ float fx, fy, zoomx, zoomy;
+ int mx, my, dragx, dragy;
+ int a;
+ bool changed = false;
+
+ /* evtx evty and drag coords are absolute mousecoords,
+ * prevents errors when editing when layout changes */
+ mx = evtx;
+ my = evty;
+ ui_window_to_block(data->region, block, &mx, &my);
+ dragx = data->draglastx;
+ dragy = data->draglasty;
+ ui_window_to_block(data->region, block, &dragx, &dragy);
+
+ zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect);
+ zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect);
+
+ if (snap) {
+ float d[2];
+
+ d[0] = mx - data->dragstartx;
+ d[1] = my - data->dragstarty;
+
+ if (len_squared_v2(d) < (3.0f * 3.0f)) {
+ snap = false;
+ }
+ }
+
+ fx = (mx - dragx) / zoomx;
+ fy = (my - dragy) / zoomy;
+
+ if (data->dragsel != -1) {
+ CurveProfilePoint *point_last = NULL;
+ const float mval_factor = ui_mouse_scale_warp_factor(shift);
+ bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */
+
+ fx *= mval_factor;
+ fy *= mval_factor;
+
+ /* Move all the points that aren't the last or the first */
+ for (a = 1; a < profile->path_len - 1; a++) {
+ if (pts[a].flag & PROF_SELECT) {
+ float origx = pts[a].x, origy = pts[a].y;
+ pts[a].x += fx;
+ pts[a].y += fy;
+ if (snap) {
+ pts[a].x = 0.125f * roundf(8.0f * pts[a].x);
+ pts[a].y = 0.125f * roundf(8.0f * pts[a].y);
+ }
+ if (!moved_point && (pts[a].x != origx || pts[a].y != origy)) {
+ moved_point = true;
+ }
+
+ point_last = &pts[a];
+ }
+ }
+
+ BKE_curveprofile_update(profile, false);
+
+ if (moved_point) {
+ data->draglastx = evtx;
+ data->draglasty = evty;
+ changed = true;
+#ifdef USE_CONT_MOUSE_CORRECT
+ /* note: using 'cmp_last' is weak since there may be multiple points selected,
+ * but in practice this isnt really an issue */
+ if (ui_but_is_cursor_warp(but)) {
+ /* OK but can go outside bounds */
+ data->ungrab_mval[0] = but->rect.xmin + ((point_last->x - profile->view_rect.xmin) * zoomx);
+ data->ungrab_mval[1] = but->rect.ymin + ((point_last->y - profile->view_rect.ymin) * zoomy);
+ BLI_rctf_clamp_pt_v(&but->rect, data->ungrab_mval);
+ }
+#endif
+ }
+ data->dragchange = true; /* mark for selection */
+ }
+ else {
+ /* clamp for clip */
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.xmin - fx < profile->clip_rect.xmin) {
+ fx = profile->view_rect.xmin - profile->clip_rect.xmin;
+ }
+ else if (profile->view_rect.xmax - fx > profile->clip_rect.xmax) {
+ fx = profile->view_rect.xmax - profile->clip_rect.xmax;
+ }
+ if (profile->view_rect.ymin - fy < profile->clip_rect.ymin) {
+ fy = profile->view_rect.ymin - profile->clip_rect.ymin;
+ }
+ else if (profile->view_rect.ymax - fy > profile->clip_rect.ymax) {
+ fy = profile->view_rect.ymax - profile->clip_rect.ymax;
+ }
+ }
+
+ profile->view_rect.xmin -= fx;
+ profile->view_rect.ymin -= fy;
+ profile->view_rect.xmax -= fx;
+ profile->view_rect.ymax -= fy;
+
+ data->draglastx = evtx;
+ data->draglasty = evty;
+
+ changed = true;
+ }
+
+ return changed;
+}
+
+/** Interaction for curve profile widget.
+ * \note Uses hardcoded keys rather than the keymap. */
+static int ui_do_but_CURVEPROFILE(
+ bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+{
+ int mx, my, i;
+
+ mx = event->x;
+ my = event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ /* Move selected control points. */
+ if (event->type == GKEY && event->val == KM_RELEASE) {
+ data->dragstartx = mx;
+ data->dragstarty = my;
+ data->draglastx = mx;
+ data->draglasty = my;
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ CurveProfile *profile = (CurveProfile *)but->poin;
+
+ /* Delete selected control points. */
+ if (event->type == XKEY && event->val == KM_RELEASE) {
+ BKE_curveprofile_remove_by_flag(profile, PROF_SELECT);
+ BKE_curveprofile_update(profile, false);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ /* Selecting, adding, and starting point movements. */
+ if (data->state == BUTTON_STATE_HIGHLIGHT) {
+ if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
+ CurveProfilePoint *pts; /* Path or table. */
+ const float m_xy[2] = {mx, my};
+ float dist_min_sq;
+ int i_selected = -1;
+
+ if (event->ctrl) {
+ float f_xy[2];
+ BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
+
+ BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]);
+ BKE_curveprofile_update(profile, false);
+ }
+
+ /* Check for selecting of a point by finding closest point in radius. */
+ dist_min_sq = SQUARE(U.dpi_fac * 14.0f); /* 14 pixels radius for selecting points. */
+ pts = profile->path;
+ for (i = 0; i < profile->path_len; i++) {
+ float f_xy[2];
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x);
+ const float dist_sq = len_squared_v2v2(m_xy, f_xy);
+ if (dist_sq < dist_min_sq) {
+ i_selected = i;
+ dist_min_sq = dist_sq;
+ }
+ }
+
+ /* Add a point if the click was close to the path but not a control point. */
+ if (i_selected == -1) { /* No control point selected. */
+ float f_xy[2], f_xy_prev[2];
+ pts = profile->table;
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[0].x);
+
+ dist_min_sq = SQUARE(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */
+
+ /* Loop through the path's high resolution table and find what's near the click. */
+ for (i = 1; i <= PROF_N_TABLE(profile->path_len); i++) {
+ copy_v2_v2(f_xy_prev, f_xy);
+ BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x);
+
+ if (dist_squared_to_line_segment_v2(m_xy, f_xy_prev, f_xy) < dist_min_sq) {
+ BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy);
+
+ CurveProfilePoint *new_pt = BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]);
+ BKE_curveprofile_update(profile, false);
+
+ /* reset pts back to the control points. */
+ pts = profile->path;
+
+ /* Get the index of the newly added point. */
+ for (i = 0; i < profile->path_len; i++) {
+ if (&pts[i] == new_pt) {
+ i_selected = i;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /* Change the flag for the point(s) if one was selected. */
+ if (i_selected != -1) {
+ /* Deselect all if this one is deselected, except if we hold shift. */
+ if (!event->shift) {
+ for (i = 0; i < profile->path_len; i++) {
+ pts[i].flag &= ~PROF_SELECT;
+ }
+ pts[i_selected].flag |= PROF_SELECT;
+ }
+ else {
+ pts[i_selected].flag ^= PROF_SELECT;
+ }
+ }
+ else {
+ /* Move the view. */
+ data->cancel = true;
+ }
+
+ data->dragsel = i_selected;
+
+ data->dragstartx = mx;
+ data->dragstarty = my;
+ data->draglastx = mx;
+ data->draglasty = my;
+
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if (data->state == BUTTON_STATE_NUM_EDITING) { /* Do control point movement. */
+ if (event->type == MOUSEMOVE) {
+ if (mx != data->draglastx || my != data->draglasty) {
+ if (ui_numedit_but_CURVEPROFILE(
+ block, but, data, mx, my, event->ctrl != 0, event->shift != 0)) {
+ ui_numedit_apply(C, block, but, data);
+ }
+ }
+ }
+ else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
+ /* Finish move. */
+ if (data->dragsel != -1) {
+ CurveProfilePoint *pts = profile->path;
+
+ if (data->dragchange == false) {
+ /* Deselect all, select one. */
+ if (!event->shift) {
+ for (i = 0; i < profile->path_len; i++) {
+ pts[i].flag &= ~PROF_SELECT;
+ }
+ pts[data->dragsel].flag |= PROF_SELECT;
+ }
+ }
+ else {
+ BKE_curveprofile_update(profile, true); /* Remove doubles after move. */
+ }
+ }
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx, int my)
{
Histogram *hist = (Histogram *)but->poin;
@@ -7208,6 +7536,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_CURVE:
retval = ui_do_but_CURVE(C, block, but, data, event);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ retval = ui_do_but_CURVEPROFILE(C, block, but, data, event);
+ break;
case UI_BTYPE_HSVCUBE:
retval = ui_do_but_HSVCUBE(C, block, but, data, event);
break;
@@ -7599,7 +7930,7 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA
copy_v2_fl(data->ungrab_mval, FLT_MAX);
#endif
- if (ELEM(but->type, UI_BTYPE_CURVE, UI_BTYPE_SEARCH_MENU)) {
+ if (ELEM(but->type, UI_BTYPE_CURVE, UI_BTYPE_CURVEPROFILE, UI_BTYPE_SEARCH_MENU)) {
/* XXX curve is temp */
}
else {
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 81979b1fc8f..3fe2750e070 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -274,6 +274,7 @@ struct uiBut {
float *editvec;
void *editcoba;
void *editcumap;
+ void *editprofile;
uiButPushedStateFunc pushed_state_func;
void *pushed_state_arg;
@@ -740,6 +741,10 @@ void ui_draw_but_CURVE(ARegion *ar,
uiBut *but,
const struct uiWidgetColors *wcol,
const rcti *rect);
+void ui_draw_but_CURVEPROFILE(ARegion *ar,
+ uiBut *but,
+ const struct uiWidgetColors *wcol,
+ const rcti *rect);
void ui_draw_but_IMAGE(ARegion *ar,
uiBut *but,
const struct uiWidgetColors *wcol,
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 34b1070f8b4..ab20b5ac520 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -422,7 +422,8 @@ bool ui_but_is_cursor_warp(const uiBut *but)
UI_BTYPE_HSVCIRCLE,
UI_BTYPE_TRACK_PREVIEW,
UI_BTYPE_HSVCUBE,
- UI_BTYPE_CURVE)) {
+ UI_BTYPE_CURVE,
+ UI_BTYPE_CURVEPROFILE)) {
return true;
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 021d7733fae..b205572ba06 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -35,6 +35,7 @@
#include "DNA_texture_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_shader_fx_types.h"
+#include "DNA_curveprofile_types.h"
#include "BLI_utildefines.h"
#include "BLI_alloca.h"
@@ -69,6 +70,7 @@
#include "BKE_packedFile.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
+#include "BKE_curveprofile.h"
#include "BKE_report.h"
#include "BKE_screen.h"
#include "BKE_shader_fx.h"
@@ -4505,6 +4507,612 @@ void uiTemplateCurveMapping(uiLayout *layout,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Curve Profile Template
+ * \{ */
+
+static void CurveProfile_presets_dofunc(bContext *C, void *profile_v, int event)
+{
+ CurveProfile *profile = profile_v;
+
+ profile->preset = event;
+ BKE_curveprofile_reset(profile);
+ BKE_curveprofile_update(profile, false);
+
+ ED_undo_push(C, "CurveProfile tools");
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static uiBlock *CurveProfile_presets_func(bContext *C, ARegion *ar, CurveProfile *profile)
+{
+ uiBlock *block;
+ short yco = 0;
+ short menuwidth = 12 * UI_UNIT_X;
+ menuwidth = 0;
+
+ block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
+ UI_block_func_butmenu_set(block, CurveProfile_presets_dofunc, profile);
+
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Default"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_LINE,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Support Loops"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_SUPPORTS,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Cornice Moulding"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_CORNICE,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Crown Moulding"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_CROWN,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Steps"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ PROF_PRESET_STEPS,
+ "");
+
+ UI_block_direction_set(block, UI_DIR_DOWN);
+ UI_block_bounds_set_text(block, (int)(3.0f * UI_UNIT_X));
+
+ return block;
+}
+
+static uiBlock *CurveProfile_buttons_presets(bContext *C, ARegion *ar, void *profile_v)
+{
+ return CurveProfile_presets_func(C, ar, (CurveProfile *)profile_v);
+}
+
+/* Only for CurveProfile tools block */
+enum {
+ UIPROFILE_FUNC_RESET,
+ UIPROFILE_FUNC_RESET_VIEW,
+};
+
+static void CurveProfile_tools_dofunc(bContext *C, void *profile_v, int event)
+{
+ CurveProfile *profile = profile_v;
+
+ switch (event) {
+ case UIPROFILE_FUNC_RESET: /* reset */
+ BKE_curveprofile_reset(profile);
+ BKE_curveprofile_update(profile, false);
+ break;
+ case UIPROFILE_FUNC_RESET_VIEW: /* reset view to clipping rect */
+ profile->view_rect = profile->clip_rect;
+ break;
+ }
+ ED_undo_push(C, "CurveProfile tools");
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static uiBlock *CurveProfile_tools_func(bContext *C, ARegion *ar, CurveProfile *profile)
+{
+ uiBlock *block;
+ short yco = 0;
+ short menuwidth = 10 * UI_UNIT_X;
+
+ block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
+ UI_block_func_butmenu_set(block, CurveProfile_tools_dofunc, profile);
+
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Reset View"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ UIPROFILE_FUNC_RESET_VIEW,
+ "");
+ uiDefIconTextBut(block,
+ UI_BTYPE_BUT_MENU,
+ 1,
+ ICON_BLANK1,
+ IFACE_("Reset Curve"),
+ 0,
+ yco -= UI_UNIT_Y,
+ menuwidth,
+ UI_UNIT_Y,
+ NULL,
+ 0.0,
+ 0.0,
+ 0,
+ UIPROFILE_FUNC_RESET,
+ "");
+
+ UI_block_direction_set(block, UI_DIR_DOWN);
+ UI_block_bounds_set_text(block, (int)(3.0f * UI_UNIT_X));
+
+ return block;
+}
+
+static uiBlock *CurveProfile_buttons_tools(bContext *C, ARegion *ar, void *profile_v)
+{
+ return CurveProfile_tools_func(C, ar, (CurveProfile *)profile_v);
+}
+
+static void CurveProfile_buttons_zoom_in(bContext *C, void *profile_v, void *UNUSED(arg))
+{
+ CurveProfile *profile = profile_v;
+ float d;
+
+ /* we allow 20 times zoom */
+ if (BLI_rctf_size_x(&profile->view_rect) > 0.04f * BLI_rctf_size_x(&profile->clip_rect)) {
+ d = 0.1154f * BLI_rctf_size_x(&profile->view_rect);
+ profile->view_rect.xmin += d;
+ profile->view_rect.xmax -= d;
+ d = 0.1154f * BLI_rctf_size_y(&profile->view_rect);
+ profile->view_rect.ymin += d;
+ profile->view_rect.ymax -= d;
+ }
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static void CurveProfile_buttons_zoom_out(bContext *C, void *profile_v, void *UNUSED(arg))
+{
+ CurveProfile *profile = profile_v;
+ float d, d1;
+
+ /* Allow 20 times zoom, but don't view outside clip */
+ if (BLI_rctf_size_x(&profile->view_rect) < 20.0f * BLI_rctf_size_x(&profile->clip_rect)) {
+ d = d1 = 0.15f * BLI_rctf_size_x(&profile->view_rect);
+
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.xmin - d < profile->clip_rect.xmin) {
+ d1 = profile->view_rect.xmin - profile->clip_rect.xmin;
+ }
+ }
+ profile->view_rect.xmin -= d1;
+
+ d1 = d;
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.xmax + d > profile->clip_rect.xmax) {
+ d1 = -profile->view_rect.xmax + profile->clip_rect.xmax;
+ }
+ }
+ profile->view_rect.xmax += d1;
+
+ d = d1 = 0.15f * BLI_rctf_size_y(&profile->view_rect);
+
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.ymin - d < profile->clip_rect.ymin) {
+ d1 = profile->view_rect.ymin - profile->clip_rect.ymin;
+ }
+ }
+ profile->view_rect.ymin -= d1;
+
+ d1 = d;
+ if (profile->flag & PROF_USE_CLIP) {
+ if (profile->view_rect.ymax + d > profile->clip_rect.ymax) {
+ d1 = -profile->view_rect.ymax + profile->clip_rect.ymax;
+ }
+ }
+ profile->view_rect.ymax += d1;
+ }
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static void CurveProfile_clipping_toggle(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ profile->flag ^= PROF_USE_CLIP;
+
+ BKE_curveprofile_update(profile, false);
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_reverse(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ BKE_curveprofile_reverse(profile);
+ BKE_curveprofile_update(profile, false);
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_delete(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ BKE_curveprofile_remove_by_flag(profile, SELECT);
+ BKE_curveprofile_update(profile, false);
+
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_setsharp(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ BKE_curveprofile_selected_handle_set(profile, HD_VECT, HD_VECT);
+ BKE_curveprofile_update(profile, false);
+
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_setcurved(bContext *C, void *cb_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+
+ BKE_curveprofile_selected_handle_set(profile, HD_AUTO, HD_AUTO);
+ BKE_curveprofile_update(profile, false);
+
+ rna_update_cb(C, cb_v, NULL);
+}
+
+static void CurveProfile_buttons_update(bContext *C, void *arg1_v, void *profile_v)
+{
+ CurveProfile *profile = profile_v;
+ BKE_curveprofile_update(profile, true);
+ rna_update_cb(C, arg1_v, NULL);
+}
+
+static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUpdateCb *cb)
+{
+ CurveProfile *profile = ptr->data;
+ CurveProfilePoint *point = NULL;
+ uiLayout *row, *sub;
+ uiBlock *block;
+ uiBut *bt;
+ int i, icon, path_width, path_height;
+ bool point_last_or_first = false;
+ rctf bounds;
+
+ block = uiLayoutGetBlock(layout);
+
+ UI_block_emboss_set(block, UI_EMBOSS);
+
+ uiLayoutRow(layout, false);
+
+ /* Preset selector */
+ /* There is probably potential to use simpler "uiItemR" functions here, but automatic updating
+ * after a preset is selected would be more complicated. */
+ bt = uiDefBlockBut(
+ block, CurveProfile_buttons_presets, profile, "Preset", 0, 0, UI_UNIT_X, UI_UNIT_X, "");
+ UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
+
+ row = uiLayoutRow(layout, false);
+
+ /* (Left aligned) */
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT);
+
+ /* Zoom in */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_ZOOM_IN,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Zoom in"));
+ UI_but_func_set(bt, CurveProfile_buttons_zoom_in, profile, NULL);
+
+ /* Zoom out */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_ZOOM_OUT,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Zoom out"));
+ UI_but_func_set(bt, CurveProfile_buttons_zoom_out, profile, NULL);
+
+ /* (Right aligned) */
+ sub = uiLayoutRow(row, true);
+ uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT);
+
+ /* Reset view, reset curve */
+ bt = uiDefIconBlockBut(
+ block, CurveProfile_buttons_tools, profile, 0, 0, 0, 0, UI_UNIT_X, UI_UNIT_X, TIP_("Tools"));
+ UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
+
+ /* Flip path */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_ARROW_LEFTRIGHT,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Reverse Path"));
+ UI_but_funcN_set(bt, CurveProfile_buttons_reverse, MEM_dupallocN(cb), profile);
+
+ /* Clipping toggle */
+ icon = (profile->flag & PROF_USE_CLIP) ? ICON_CLIPUV_HLT : ICON_CLIPUV_DEHLT;
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ icon,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Toggle Profile Clipping"));
+ UI_but_funcN_set(bt, CurveProfile_clipping_toggle, MEM_dupallocN(cb), profile);
+
+ UI_block_funcN_set(block, rna_update_cb, MEM_dupallocN(cb), NULL);
+
+ /* The path itself */
+ path_width = max_ii(uiLayoutGetWidth(layout), UI_UNIT_X);
+ path_width = min_ii(path_width, (int)(16.0f * UI_UNIT_X));
+ path_height = path_width;
+ uiLayoutRow(layout, false);
+ uiDefBut(block,
+ UI_BTYPE_CURVEPROFILE,
+ 0,
+ "",
+ 0,
+ 0,
+ (short)path_width,
+ (short)path_height,
+ profile,
+ 0.0f,
+ 1.0f,
+ -1,
+ 0,
+ "");
+
+ /* Position sliders for (first) selected point */
+ for (i = 0; i < profile->path_len; i++) {
+ if (profile->path[i].flag & PROF_SELECT) {
+ point = &profile->path[i];
+ break;
+ }
+ }
+ if (i == 0 || i == profile->path_len - 1) {
+ point_last_or_first = true;
+ }
+
+ /* Selected point data */
+ if (point) {
+ if (profile->flag & PROF_USE_CLIP) {
+ bounds = profile->clip_rect;
+ }
+ else {
+ bounds.xmin = bounds.ymin = -1000.0;
+ bounds.xmax = bounds.ymax = 1000.0;
+ }
+
+ uiLayoutRow(layout, true);
+ UI_block_funcN_set(block, CurveProfile_buttons_update, MEM_dupallocN(cb), profile);
+
+ /* Sharp / Smooth */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_LINCURVE,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Set the point's handle type to sharp."));
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+ UI_but_funcN_set(bt, CurveProfile_buttons_setsharp, MEM_dupallocN(cb), profile);
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_SMOOTHCURVE,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Set the point's handle type to sharp."));
+ UI_but_funcN_set(bt, CurveProfile_buttons_setcurved, MEM_dupallocN(cb), profile);
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+
+ /* Position */
+ bt = uiDefButF(block,
+ UI_BTYPE_NUM,
+ 0,
+ "X:",
+ 0,
+ 2 * UI_UNIT_Y,
+ UI_UNIT_X * 10,
+ UI_UNIT_Y,
+ &point->x,
+ bounds.xmin,
+ bounds.xmax,
+ 1,
+ 5,
+ "");
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+
+ bt = uiDefButF(block,
+ UI_BTYPE_NUM,
+ 0,
+ "Y:",
+ 0,
+ 1 * UI_UNIT_Y,
+ UI_UNIT_X * 10,
+ UI_UNIT_Y,
+ &point->y,
+ bounds.ymin,
+ bounds.ymax,
+ 1,
+ 5,
+ "");
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+
+ /* Delete points */
+ bt = uiDefIconBut(block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_X,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_X,
+ NULL,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Delete points"));
+ UI_but_funcN_set(bt, CurveProfile_buttons_delete, MEM_dupallocN(cb), profile);
+ if (point_last_or_first) {
+ UI_but_flag_enable(bt, UI_BUT_DISABLED);
+ }
+ }
+
+ uiItemR(layout, ptr, "use_sample_straight_edges", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_sample_even_lengths", 0, NULL, ICON_NONE);
+
+ UI_block_funcN_set(block, NULL, NULL, NULL);
+}
+
+/** Template for a path creation widget intended for custom bevel profiles.
+ * This section is quite similar to uiTemplateCurveMapping, but with reduced complexity */
+void uiTemplateCurveProfile(uiLayout *layout, PointerRNA *ptr, const char *propname)
+{
+ RNAUpdateCb *cb;
+ PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
+ PointerRNA cptr;
+ ID *id;
+ uiBlock *block = uiLayoutGetBlock(layout);
+
+ if (!prop) {
+ RNA_warning(
+ "Curve Profile property not found: %s.%s", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ if (RNA_property_type(prop) != PROP_POINTER) {
+ RNA_warning(
+ "Curve Profile is not a pointer: %s.%s", RNA_struct_identifier(ptr->type), propname);
+ return;
+ }
+
+ cptr = RNA_property_pointer_get(ptr, prop);
+ if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_CurveProfile)) {
+ return;
+ }
+
+ /* Share update functionality with the CurveMapping widget template. */
+ cb = MEM_callocN(sizeof(RNAUpdateCb), "RNAUpdateCb");
+ cb->ptr = *ptr;
+ cb->prop = prop;
+
+ id = cptr.owner_id;
+ UI_block_lock_set(block, (id && ID_IS_LINKED(id)), ERROR_LIBDATA_MESSAGE);
+
+ CurveProfile_buttons_layout(layout, &cptr, cb);
+
+ UI_block_lock_clear(block);
+
+ MEM_freeN(cb);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name ColorPicker Template
* \{ */
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 4cef9ace66e..7f7352517c8 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -4774,6 +4774,10 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct
ui_draw_but_CURVE(ar, but, &tui->wcol_regular, rect);
break;
+ case UI_BTYPE_CURVEPROFILE:
+ ui_draw_but_CURVEPROFILE(ar, but, &tui->wcol_regular, rect);
+ break;
+
case UI_BTYPE_PROGRESS_BAR:
wt = widget_type(UI_WTYPE_PROGRESSBAR);
fstyle = &style->widgetlabel;