From fafd21b14c2337c2a5ca580d444d05cb1bb92566 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 22 Jul 2021 23:44:53 +0100 Subject: Animation: Generic Slider implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the slider gui implemented for the pose slide tools. Generalise it so it can be used by other tools as well. Reviewed by: Sybren A. Stüvel Differential Revision: https://developer.blender.org/D9314 Ref: D9314 --- source/blender/editors/armature/pose_slide.c | 528 ++++----------------------- source/blender/editors/include/ED_util.h | 19 + source/blender/editors/util/ed_draw.c | 460 +++++++++++++++++++++++ 3 files changed, 541 insertions(+), 466 deletions(-) diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 675af6eada9..e87d221058c 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -82,6 +82,7 @@ #include "ED_numinput.h" #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_util.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" @@ -137,8 +138,8 @@ typedef struct tPoseSlideOp { Scene *scene; /** area that we're operating in (needed for modal()) */ ScrArea *area; - /** Header of the region used for drawing the slider. */ - ARegion *region_header; + /** Region we're operating in (needed for modal()). */ + ARegion *region; /** len of the PoseSlideObject array. */ uint objects_len; @@ -168,26 +169,7 @@ typedef struct tPoseSlideOp { /** Axis-limits for transforms. */ ePoseSlide_AxisLock axislock; - /** Allow overshoot or clamp between 0% and 100%. */ - bool overshoot; - - /** Reduces factor delta from mouse movement. */ - bool precision; - - /** Move factor in 10% steps. */ - bool increments; - - /** Draw callback handler. */ - void *draw_handle; - - /** Accumulative, unclamped and unrounded factor. */ - float raw_factor; - - /** 0-1 value for determining the influence of whatever is relevant. */ - float factor; - - /** Last cursor position in screen space used for mouse movement delta calculation. */ - int last_cursor_x; + struct tSlider *slider; /** Numeric input. */ NumInput num; @@ -232,256 +214,6 @@ static const EnumPropertyItem prop_axis_lock_types[] = { /* ------------------------------------ */ -static void draw_overshoot_triangle(const uint8_t color[4], - const bool facing_right, - const float x, - const float y) -{ - const uint shdr_pos_2d = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(GPU_BLEND_ALPHA); - GPU_polygon_smooth(true); - immUniformColor3ubvAlpha(color, 225); - const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize; - const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize; - - immBegin(GPU_PRIM_TRIS, 3); - immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y); - immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2); - immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2); - immEnd(); - - GPU_polygon_smooth(false); - GPU_blend(GPU_BLEND_NONE); - immUnbindProgram(); -} - -static void draw_ticks(const float start_factor, - const float end_factor, - const float line_start[2], - const float base_tick_height, - const float line_width, - const uint8_t color_overshoot[4], - const uint8_t color_line[4]) -{ - /* Use factor represented as 0-100 int to avoid floating point precision problems. */ - const int tick_increment = 10; - - /* Round initial_tick_factor up to the next tick_increment. */ - int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment; - - while (tick_percentage <= (int)(end_factor * 100)) { - float tick_height; - /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit - * smaller and the rest is the minimum size. */ - if (tick_percentage % 100 == 0) { - tick_height = base_tick_height; - } - else if (tick_percentage % 50 == 0) { - tick_height = base_tick_height * 0.8; - } - else { - tick_height = base_tick_height * 0.5; - } - - const float x = line_start[0] + - (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE; - const rctf tick_rect = { - .xmin = x - (line_width / 2), - .xmax = x + (line_width / 2), - .ymin = line_start[1] - (tick_height / 2), - .ymax = line_start[1] + (tick_height / 2), - }; - - if (tick_percentage < 0 || tick_percentage > 100) { - UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255); - } - else { - UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255); - } - tick_percentage += tick_increment; - } -} - -static void draw_main_line(const rctf *main_line_rect, - const float factor, - const bool overshoot, - const uint8_t color_overshoot[4], - const uint8_t color_line[4]) -{ - if (overshoot) { - /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */ - const float line_zero_percent = main_line_rect->xmin - - ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) * - SLIDE_PIXEL_DISTANCE); - - const float clamped_line_zero_percent = clamp_f( - line_zero_percent, main_line_rect->xmin, main_line_rect->xmax); - const float clamped_line_hundred_percent = clamp_f( - line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax); - - const rctf left_overshoot_line_rect = { - .xmin = main_line_rect->xmin, - .xmax = clamped_line_zero_percent, - .ymin = main_line_rect->ymin, - .ymax = main_line_rect->ymax, - }; - const rctf right_overshoot_line_rect = { - .xmin = clamped_line_hundred_percent, - .xmax = main_line_rect->xmax, - .ymin = main_line_rect->ymin, - .ymax = main_line_rect->ymax, - }; - UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255); - UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255); - - const rctf non_overshoot_line_rect = { - .xmin = clamped_line_zero_percent, - .xmax = clamped_line_hundred_percent, - .ymin = main_line_rect->ymin, - .ymax = main_line_rect->ymax, - }; - UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255); - } - else { - UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255); - } -} - -static void draw_backdrop(const int fontid, - const rctf *main_line_rect, - const float color_bg[4], - const short region_y_size, - const float base_tick_height) -{ - float string_pixel_size[2]; - const char *percentage_string_placeholder = "000%%"; - BLF_width_and_height(fontid, - percentage_string_placeholder, - sizeof(percentage_string_placeholder), - &string_pixel_size[0], - &string_pixel_size[1]); - const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize}; - const rctf backdrop_rect = { - .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0], - .xmax = main_line_rect->xmax + pad[0], - .ymin = pad[1], - .ymax = region_y_size - pad[1], - }; - UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); -} - -/** - * Draw an on screen Slider for a Pose Slide Operator. - */ -static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg) -{ - tPoseSlideOp *pso = arg; - - /* Only draw in region from which the Operator was started. */ - if (region != pso->region_header) { - return; - } - - uint8_t color_text[4]; - uint8_t color_line[4]; - uint8_t color_handle[4]; - uint8_t color_overshoot[4]; - float color_bg[4]; - - /* Get theme colors. */ - UI_GetThemeColor4ubv(TH_TEXT, color_text); - UI_GetThemeColor4ubv(TH_TEXT, color_line); - UI_GetThemeColor4ubv(TH_TEXT, color_overshoot); - UI_GetThemeColor4ubv(TH_ACTIVE, color_handle); - UI_GetThemeColor3fv(TH_BACK, color_bg); - - color_bg[3] = 0.5f; - color_overshoot[0] = color_overshoot[0] * 0.7; - color_overshoot[1] = color_overshoot[1] * 0.7; - color_overshoot[2] = color_overshoot[2] * 0.7; - - /* Get the default font. */ - const uiStyle *style = UI_style_get(); - const uiFontStyle *fstyle = &style->widget; - const int fontid = fstyle->uifont_id; - BLF_color3ubv(fontid, color_text); - BLF_rotation(fontid, 0.0f); - - const float line_width = 1.5 * U.pixelsize; - const float base_tick_height = 12.0 * U.pixelsize; - const float line_y = region->winy / 2; - - rctf main_line_rect = { - .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2), - .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2), - .ymin = line_y - line_width / 2, - .ymax = line_y + line_width / 2, - }; - float line_start_factor = 0; - int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->factor; - - if (pso->overshoot) { - main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; - main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; - line_start_factor = pso->factor - 0.5f - OVERSHOOT_RANGE_DELTA; - handle_pos_x = region->winx / 2; - } - - draw_backdrop(fontid, &main_line_rect, color_bg, pso->region_header->winy, base_tick_height); - - draw_main_line(&main_line_rect, pso->factor, pso->overshoot, color_overshoot, color_line); - - const float factor_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1; - const float line_start_position[2] = {main_line_rect.xmin, line_y}; - draw_ticks(line_start_factor, - line_start_factor + factor_range, - line_start_position, - base_tick_height, - line_width, - color_overshoot, - color_line); - - /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100% - * range. */ - if (pso->overshoot) { - if (pso->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) { - draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y); - } - if (pso->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) { - draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y); - } - } - - char percentage_string[256]; - - /* Draw handle indicating current factor. */ - const rctf handle_rect = { - .xmin = handle_pos_x - (line_width), - .xmax = handle_pos_x + (line_width), - .ymin = line_y - (base_tick_height / 2), - .ymax = line_y + (base_tick_height / 2), - }; - - UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255); - BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->factor * 100); - - /* Draw percentage string. */ - float percentage_string_pixel_size[2]; - BLF_width_and_height(fontid, - percentage_string, - sizeof(percentage_string), - &percentage_string_pixel_size[0], - &percentage_string_pixel_size[1]); - - BLF_position(fontid, - main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2, - (region->winy / 2) - percentage_string_pixel_size[1] / 2, - 0.0f); - BLF_draw(fontid, percentage_string, sizeof(percentage_string)); -} - /** Operator custom-data initialization. */ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) { @@ -492,15 +224,13 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /* Get info from context. */ pso->scene = CTX_data_scene(C); - pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */ - pso->region_header = CTX_wm_region(C); /* Only really needed when doing modal(). */ + pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */ + pso->region = CTX_wm_region(C); /* Only really needed when doing modal(). */ pso->cframe = pso->scene->r.cfra; pso->mode = mode; /* Set range info from property values - these may get overridden for the invoke(). */ - pso->factor = RNA_float_get(op->ptr, "factor"); - pso->raw_factor = pso->factor; pso->prevFrame = RNA_int_get(op->ptr, "prev_frame"); pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); @@ -508,6 +238,9 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) pso->channels = RNA_enum_get(op->ptr, "channels"); pso->axislock = RNA_enum_get(op->ptr, "axis_lock"); + pso->slider = ED_slider_create(C); + ED_slider_factor_set(pso->slider, RNA_float_get(op->ptr, "factor")); + /* For each Pose-Channel which gets affected, get the F-Curves for that channel * and set the relevant transform flags. */ poseAnim_mapping_get(C, &pso->pfLinks); @@ -552,14 +285,6 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) pso->num.val_flag[0] |= NUM_NO_NEGATIVE; pso->num.unit_type[0] = B_UNIT_NONE; /* Percentages don't have any units. */ - /* Register UI drawing callback. */ - ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER); - if (region_header != NULL) { - pso->region_header = region_header; - pso->draw_handle = ED_region_draw_cb_activate( - region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL); - } - /* Return status is whether we've got all the data we were requested to get. */ return 1; } @@ -567,17 +292,16 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /** * Exiting the operator (free data). */ -static void pose_slide_exit(wmOperator *op) +static void pose_slide_exit(bContext *C, wmOperator *op) { tPoseSlideOp *pso = op->customdata; + ED_slider_destroy(C, pso->slider); + /* Hide Bone Overlay. */ View3D *v3d = pso->area->spacedata.first; v3d->overlay.flag = pso->overlay_flag; - /* Remove UI drawing callback. */ - ED_region_draw_cb_exit(pso->region_header->type, pso->draw_handle); - /* Free the temp pchan links and their data. */ poseAnim_mapping_free(&pso->pfLinks); @@ -655,8 +379,8 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo /* Calculate the relative weights of the endpoints. */ if (pso->mode == POSESLIDE_BREAKDOWN) { /* Get weights from the factor control. */ - w1 = pso->factor; /* This must come second. */ - w2 = 1.0f - w1; /* This must come first. */ + w1 = ED_slider_factor_get(pso->slider); /* This must come second. */ + w2 = 1.0f - w1; /* This must come first. */ } else { /* - these weights are derived from the relative distance of these @@ -682,13 +406,13 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo case POSESLIDE_PUSH: /* Make the current pose more pronounced. */ { /* Slide the pose away from the breakdown pose in the timeline */ - (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor; + (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * ED_slider_factor_get(pso->slider); break; } case POSESLIDE_RELAX: /* Make the current pose more like its surrounding ones. */ { /* Slide the pose towards the breakdown pose in the timeline */ - (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor; + (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * ED_slider_factor_get(pso->slider); break; } case POSESLIDE_BREAKDOWN: /* Make the current pose slide around between the endpoints. */ @@ -888,7 +612,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) normalize_qt(quat_prev); normalize_qt(quat_next); - interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->factor); + interp_qt_qtqt(quat_final, quat_prev, quat_next, ED_slider_factor_get(pso->slider)); } else { /* POSESLIDE_PUSH and POSESLIDE_RELAX. */ @@ -906,11 +630,12 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) normalize_qt(quat_curr); if (pso->mode == POSESLIDE_PUSH) { - interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->factor); + interp_qt_qtqt( + quat_final, quat_breakdown, quat_curr, 1.0f + ED_slider_factor_get(pso->slider)); } else { BLI_assert(pso->mode == POSESLIDE_RELAX); - interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->factor); + interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, ED_slider_factor_get(pso->slider)); } } @@ -931,11 +656,11 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo ((lock & PS_LOCK_Z) && (idx == 2))) { float diff_val = default_value - vec[idx]; if (pso->mode == POSESLIDE_RELAX_REST) { - vec[idx] += pso->factor * diff_val; + vec[idx] += ED_slider_factor_get(pso->slider) * diff_val; } else { /* Push */ - vec[idx] -= pso->factor * diff_val; + vec[idx] -= ED_slider_factor_get(pso->slider) * diff_val; } } } @@ -953,11 +678,11 @@ static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4] for (int idx = 0; idx < 4; idx++) { float diff_val = default_values[idx] - vec[idx]; if (pso->mode == POSESLIDE_RELAX_REST) { - vec[idx] += pso->factor * diff_val; + vec[idx] += ED_slider_factor_get(pso->slider) * diff_val; } else { /* Push */ - vec[idx] -= pso->factor * diff_val; + vec[idx] -= ED_slider_factor_get(pso->slider) * diff_val; } } } @@ -1130,9 +855,7 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) char limits_str[UI_MAX_DRAW_STR]; char axis_str[50]; char mode_str[32]; - char overshoot_str[50]; - char precision_str[50]; - char increments_str[50]; + char slider_str[UI_MAX_DRAW_STR]; char bone_vis_str[50]; switch (pso->mode) { @@ -1203,29 +926,10 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) break; } - if (pso->overshoot) { - STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot")); - } - else { - STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot")); - } - - if (pso->precision) { - STRNCPY(precision_str, TIP_("[Shift] - Precision active")); - } - else { - STRNCPY(precision_str, TIP_("Shift - Hold for precision")); - } - - if (pso->increments) { - STRNCPY(increments_str, TIP_("[Ctrl] - Increments active")); - } - else { - STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments")); - } - STRNCPY(bone_vis_str, TIP_("[H] - Toggle bone visibility")); + ED_slider_status_string_get(pso->slider, slider_str, sizeof(slider_str)); + if (hasNumInput(&pso->num)) { Scene *scene = pso->scene; char str_offs[NUM_STR_REP_LEN]; @@ -1237,12 +941,10 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) else { BLI_snprintf(status_str, sizeof(status_str), - "%s: %s | %s | %s | %s | %s", + "%s: %s | %s | %s", mode_str, limits_str, - overshoot_str, - precision_str, - increments_str, + slider_str, bone_vis_str); } @@ -1253,11 +955,15 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) /** * Common code for invoke() methods. */ -static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso) +static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent *event) { tPChanFCurveLink *pfl; wmWindow *win = CTX_wm_window(C); + tPoseSlideOp *pso = op->customdata; + + ED_slider_init(pso->slider, event); + /* For each link, add all its keyframes to the search tree. */ for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { LinkData *ld; @@ -1315,7 +1021,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p } else { BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -1348,29 +1054,6 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p return OPERATOR_RUNNING_MODAL; } -/** - * Calculate factor based on mouse movement, clamp or round to increments if - * enabled by the user. Store the new factor value. - */ -static void pose_slide_mouse_update_factor(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event) -{ - const float factor_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE)); - /* Reduced factor delta in precision mode (shift held). */ - pso->raw_factor += pso->precision ? (factor_delta / 8) : factor_delta; - pso->factor = pso->raw_factor; - pso->last_cursor_x = event->x; - - if (!pso->overshoot) { - pso->factor = clamp_f(pso->factor, 0, 1); - } - - if (pso->increments) { - pso->factor = round(pso->factor * 10) / 10; - } - - RNA_float_set(op->ptr, "factor", pso->factor); -} - /** * Handle an event to toggle channels mode. */ @@ -1434,6 +1117,8 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) const bool has_numinput = hasNumInput(&pso->num); + do_pose_update = ED_slider_modal(pso->slider, event); + switch (event->type) { case LEFTMOUSE: /* Confirm. */ case EVT_RETKEY: @@ -1449,7 +1134,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Insert keyframes as required. */ pose_slide_autoKeyframe(C, pso); - pose_slide_exit(op); + pose_slide_exit(C, op); /* Done! */ return OPERATOR_FINISHED; @@ -1472,7 +1157,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) pose_slide_refresh(C, pso); /* Clean up temp data. */ - pose_slide_exit(op); + pose_slide_exit(C, op); /* Canceled! */ return OPERATOR_CANCELLED; @@ -1485,9 +1170,6 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) { /* Only handle mouse-move if not doing numinput. */ if (has_numinput == false) { - /* Update factor based on position of mouse. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Update pose to reflect the new values (see below). */ do_pose_update = true; } @@ -1500,12 +1182,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Grab percentage from numeric input, and store this new value for redo * NOTE: users see ints, while internally we use a 0-1 float */ - value = pso->factor * 100.0f; + value = ED_slider_factor_get(pso->slider) * 100.0f; applyNumInput(&pso->num, &value); - pso->factor = value / 100.0f; - CLAMP(pso->factor, 0.0f, 1.0f); - RNA_float_set(op->ptr, "factor", pso->factor); + float factor = value / 100; + CLAMP(factor, 0.0f, 1.0f); + ED_slider_factor_set(pso->slider, factor); + RNA_float_set(op->ptr, "factor", ED_slider_factor_get(pso->slider)); /* Update pose to reflect the new values (see below) */ do_pose_update = true; @@ -1567,29 +1250,6 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } - /* Overshoot. */ - case EVT_EKEY: { - pso->overshoot = !pso->overshoot; - do_pose_update = true; - break; - } - - /* Precision mode. */ - case EVT_LEFTSHIFTKEY: - case EVT_RIGHTSHIFTKEY: { - pso->precision = true; - do_pose_update = true; - break; - } - - /* Increments mode. */ - case EVT_LEFTCTRLKEY: - case EVT_RIGHTCTRLKEY: { - pso->increments = true; - do_pose_update = true; - break; - } - /* Toggle Bone visibility. */ case EVT_HKEY: { View3D *v3d = pso->area->spacedata.first; @@ -1600,25 +1260,6 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } } - /* Precision and stepping only active while button is held. */ - else if (event->val == KM_RELEASE) { - switch (event->type) { - case EVT_LEFTSHIFTKEY: - case EVT_RIGHTSHIFTKEY: { - pso->precision = false; - do_pose_update = true; - break; - } - case EVT_LEFTCTRLKEY: - case EVT_RIGHTCTRLKEY: { - pso->increments = false; - do_pose_update = true; - break; - } - default: - break; - } - } else { /* Unhandled event - maybe it was some view manipulation? */ /* Allow to pass through. */ @@ -1652,10 +1293,10 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /** * Common code for cancel() */ -static void pose_slide_cancel(bContext *UNUSED(C), wmOperator *op) +static void pose_slide_cancel(bContext *C, wmOperator *op) { /* Cleanup and done. */ - pose_slide_exit(op); + pose_slide_exit(C, op); } /** @@ -1675,7 +1316,7 @@ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso pose_slide_autoKeyframe(C, pso); /* Cleanup and done. */ - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_FINISHED; } @@ -1743,23 +1384,14 @@ static void pose_slide_opdef_properties(wmOperatorType *ot) */ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Do common setup work. */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -1771,7 +1403,7 @@ static int pose_slide_push_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -1809,23 +1441,14 @@ void POSE_OT_push(wmOperatorType *ot) */ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Do common setup work. */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -1837,7 +1460,7 @@ static int pose_slide_relax_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -1874,23 +1497,14 @@ void POSE_OT_relax(wmOperatorType *ot) */ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* do common setup work */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -1902,7 +1516,7 @@ static int pose_slide_push_rest_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -1940,23 +1554,14 @@ void POSE_OT_push_rest(wmOperatorType *ot) */ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Do common setup work. */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -1968,7 +1573,7 @@ static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -2006,23 +1611,14 @@ void POSE_OT_relax_rest(wmOperatorType *ot) */ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Do common setup work. */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -2034,7 +1630,7 @@ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 953f26aa45f..0105af843bb 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -24,6 +24,7 @@ #pragma once #include "BLI_compiler_attrs.h" +#include "WM_types.h" #ifdef __cplusplus extern "C" { @@ -61,6 +62,24 @@ void ED_region_draw_mouse_line_cb(const struct bContext *C, void ED_region_image_metadata_draw( int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy); +/* Slider */ +struct tSlider; + +struct tSlider *ED_slider_create(struct bContext *C); +void ED_slider_init(struct tSlider *slider, const struct wmEvent *event); +bool ED_slider_modal(struct tSlider *slider, const struct wmEvent *event); +void ED_slider_destroy(struct bContext *C, struct tSlider *slider); + +void ED_slider_status_string_get(const struct tSlider *slider, + char *status_string, + const size_t size_of_status_string); + +float ED_slider_factor_get(struct tSlider *slider); +void ED_slider_factor_set(struct tSlider *slider, float factor); + +bool ED_slider_allow_overshoot_get(struct tSlider *slider); +void ED_slider_allow_overshoot_set(struct tSlider *slider, const bool value); + /* ************** XXX OLD CRUFT WARNING ************* */ void apply_keyb_grid( diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index 3b543cdf6ce..159d750da4e 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -33,6 +33,8 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_image.h" @@ -56,6 +58,464 @@ #include "WM_api.h" #include "WM_types.h" +/* -------------------------------------------------------------------- */ +/** \name Generic Slider + * + * The generic slider is supposed to be called during modal operations. It calculates a factor + * value based on mouse position and draws a visual representation. In order to use it, you need to + * store a reference to a tSlider in your operator which you get by calling "ED_slider_create". + * Then you need to update it during modal operations by calling "ED_slider_modal", which will + * update tSlider->factor for you to use. To remove drawing and free the memory, call + * "ED_slider_destroy". + * \{ */ + +#define SLIDE_PIXEL_DISTANCE (300.0f * U.dpi_fac) +#define OVERSHOOT_RANGE_DELTA 0.2f + +typedef struct tSlider { + struct Scene *scene; + struct ScrArea *area; + /* Header of the region used for drawing the slider. */ + struct ARegion *region_header; + + /* Draw callback handler. */ + void *draw_handle; + + /* Accumulative, unclamped and unrounded factor. */ + float raw_factor; + + /** 0-1 value for determining the influence of whatever is relevant. */ + float factor; + + /* Last mouse cursor position used for mouse movement delta calculation. */ + float last_cursor[2]; + + /* Enable range beyond 0-100%. */ + bool allow_overshoot; + + /* Allow overshoot or clamp between 0% and 100%. */ + bool overshoot; + + /* Move factor in 10% steps. */ + bool increments; + + /* Reduces factor delta from mouse movement. */ + bool precision; +} tSlider; + +static void draw_overshoot_triangle(const uint8_t color[4], + const bool facing_right, + const float x, + const float y) +{ + const uint shdr_pos_2d = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); + GPU_polygon_smooth(true); + immUniformColor3ubvAlpha(color, 225); + const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize; + const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize; + + immBegin(GPU_PRIM_TRIS, 3); + immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y); + immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2); + immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2); + immEnd(); + + GPU_polygon_smooth(false); + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); +} + +static void draw_ticks(const float start_factor, + const float end_factor, + const float line_start[2], + const float base_tick_height, + const float line_width, + const uint8_t color_overshoot[4], + const uint8_t color_line[4]) +{ + /* Use factor represented as 0-100 int to avoid floating point precision problems. */ + const int tick_increment = 10; + + /* Round initial_tick_factor up to the next tick_increment. */ + int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment; + + while (tick_percentage <= (int)(end_factor * 100)) { + float tick_height; + /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit + * smaller and the rest is the minimum size. */ + if (tick_percentage % 100 == 0) { + tick_height = base_tick_height; + } + else if (tick_percentage % 50 == 0) { + tick_height = base_tick_height * 0.8; + } + else { + tick_height = base_tick_height * 0.5; + } + + const float x = line_start[0] + + (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE; + const rctf tick_rect = { + .xmin = x - (line_width / 2), + .xmax = x + (line_width / 2), + .ymin = line_start[1] - (tick_height / 2), + .ymax = line_start[1] + (tick_height / 2), + }; + + if (tick_percentage < 0 || tick_percentage > 100) { + UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255); + } + else { + UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255); + } + tick_percentage += tick_increment; + } +} + +static void draw_main_line(const rctf *main_line_rect, + const float factor, + const bool overshoot, + const uint8_t color_overshoot[4], + const uint8_t color_line[4]) +{ + if (overshoot) { + /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */ + const float line_zero_percent = main_line_rect->xmin - + ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) * + SLIDE_PIXEL_DISTANCE); + + const float clamped_line_zero_percent = clamp_f( + line_zero_percent, main_line_rect->xmin, main_line_rect->xmax); + const float clamped_line_hundred_percent = clamp_f( + line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax); + + const rctf left_overshoot_line_rect = { + .xmin = main_line_rect->xmin, + .xmax = clamped_line_zero_percent, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; + const rctf right_overshoot_line_rect = { + .xmin = clamped_line_hundred_percent, + .xmax = main_line_rect->xmax, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; + UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255); + UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255); + + const rctf non_overshoot_line_rect = { + .xmin = clamped_line_zero_percent, + .xmax = clamped_line_hundred_percent, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; + UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255); + } + else { + UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255); + } +} + +static void draw_backdrop(const int fontid, + const rctf *main_line_rect, + const float color_bg[4], + const short region_y_size, + const float base_tick_height) +{ + float string_pixel_size[2]; + const char *percentage_string_placeholder = "000%%"; + BLF_width_and_height(fontid, + percentage_string_placeholder, + sizeof(percentage_string_placeholder), + &string_pixel_size[0], + &string_pixel_size[1]); + const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize}; + const rctf backdrop_rect = { + .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0], + .xmax = main_line_rect->xmax + pad[0], + .ymin = pad[1], + .ymax = region_y_size - pad[1], + }; + UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); +} + +/* Draw an on screen Slider for a Pose Slide Operator. */ +static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void *arg) +{ + tSlider *slider = arg; + + /* Only draw in region from which the Operator was started. */ + if (region != slider->region_header) { + return; + } + + uint8_t color_text[4]; + uint8_t color_line[4]; + uint8_t color_handle[4]; + uint8_t color_overshoot[4]; + float color_bg[4]; + + /* Get theme colors. */ + UI_GetThemeColor4ubv(TH_TEXT, color_text); + UI_GetThemeColor4ubv(TH_TEXT, color_line); + UI_GetThemeColor4ubv(TH_TEXT, color_overshoot); + UI_GetThemeColor4ubv(TH_ACTIVE, color_handle); + UI_GetThemeColor3fv(TH_BACK, color_bg); + + color_bg[3] = 0.5f; + color_overshoot[0] = color_overshoot[0] * 0.7; + color_overshoot[1] = color_overshoot[1] * 0.7; + color_overshoot[2] = color_overshoot[2] * 0.7; + + /* Get the default font. */ + const uiStyle *style = UI_style_get(); + const uiFontStyle *fstyle = &style->widget; + const int fontid = fstyle->uifont_id; + BLF_color3ubv(fontid, color_text); + BLF_rotation(fontid, 0.0f); + + const float line_width = 1.5 * U.pixelsize; + const float base_tick_height = 12.0 * U.pixelsize; + const float line_y = region->winy / 2; + + rctf main_line_rect = { + .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2), + .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2), + .ymin = line_y - line_width / 2, + .ymax = line_y + line_width / 2, + }; + float line_start_factor = 0; + int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * slider->factor; + + if (slider->overshoot) { + main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; + main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; + line_start_factor = slider->factor - 0.5f - OVERSHOOT_RANGE_DELTA; + handle_pos_x = region->winx / 2; + } + + draw_backdrop(fontid, &main_line_rect, color_bg, slider->region_header->winy, base_tick_height); + + draw_main_line(&main_line_rect, slider->factor, slider->overshoot, color_overshoot, color_line); + + const float factor_range = slider->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1; + const float line_start_position[2] = {main_line_rect.xmin, line_y}; + draw_ticks(line_start_factor, + line_start_factor + factor_range, + line_start_position, + base_tick_height, + line_width, + color_overshoot, + color_line); + + /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100% + * range. */ + if (slider->overshoot) { + if (slider->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) { + draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y); + } + if (slider->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) { + draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y); + } + } + + char percentage_string[256]; + + /* Draw handle indicating current factor. */ + const rctf handle_rect = { + .xmin = handle_pos_x - (line_width), + .xmax = handle_pos_x + (line_width), + .ymin = line_y - (base_tick_height / 2), + .ymax = line_y + (base_tick_height / 2), + }; + + UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255); + BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", slider->factor * 100); + + /* Draw percentage string. */ + float percentage_string_pixel_size[2]; + BLF_width_and_height(fontid, + percentage_string, + sizeof(percentage_string), + &percentage_string_pixel_size[0], + &percentage_string_pixel_size[1]); + + BLF_position(fontid, + main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2, + (region->winy / 2) - percentage_string_pixel_size[1] / 2, + 0.0f); + BLF_draw(fontid, percentage_string, sizeof(percentage_string)); +} + +static void slider_update_factor(tSlider *slider, const wmEvent *event) +{ + const float factor_delta = (event->x - slider->last_cursor[0]) / SLIDE_PIXEL_DISTANCE; + /* Reduced factor delta in precision mode (shift held). */ + slider->raw_factor += slider->precision ? (factor_delta / 8) : factor_delta; + slider->factor = slider->raw_factor; + slider->last_cursor[0] = event->x; + slider->last_cursor[1] = event->y; + + if (!slider->overshoot) { + slider->factor = clamp_f(slider->factor, 0, 1); + } + + if (slider->increments) { + slider->factor = round(slider->factor * 10) / 10; + } +} + +tSlider *ED_slider_create(struct bContext *C) +{ + tSlider *slider = MEM_callocN(sizeof(tSlider), "tSlider"); + slider->scene = CTX_data_scene(C); + slider->area = CTX_wm_area(C); + slider->region_header = CTX_wm_region(C); + + /* Default is true, caller needs to manually set to false. */ + slider->allow_overshoot = true; + + /* Set initial factor. */ + slider->raw_factor = 0.5f; + slider->factor = 0.5; + + /* Add draw callback. Always in header. */ + LISTBASE_FOREACH (ARegion *, region, &slider->area->regionbase) { + if (region->regiontype == RGN_TYPE_HEADER) { + slider->region_header = region; + slider->draw_handle = ED_region_draw_cb_activate( + region->type, slider_draw, slider, REGION_DRAW_POST_PIXEL); + } + } + + return slider; +} + +/* For modal operations so the percentage doesn't pop on the first mouse movement. */ +void ED_slider_init(struct tSlider *slider, const wmEvent *event) +{ + slider->last_cursor[0] = event->x; + slider->last_cursor[1] = event->y; +} + +/* Calculate slider factor based on mouse position. */ +bool ED_slider_modal(tSlider *slider, const wmEvent *event) +{ + bool event_handled = true; + /* Handle key presses. */ + switch (event->type) { + case EVT_EKEY: + if (slider->allow_overshoot) { + slider->overshoot = event->val == KM_PRESS ? !slider->overshoot : slider->overshoot; + slider_update_factor(slider, event); + } + break; + case EVT_LEFTSHIFTKEY: + case EVT_RIGHTSHIFTKEY: + slider->precision = event->val == KM_PRESS; + break; + case EVT_LEFTCTRLKEY: + case EVT_RIGHTCTRLKEY: + slider->increments = event->val == KM_PRESS; + break; + case MOUSEMOVE:; + /* Update factor. */ + slider_update_factor(slider, event); + break; + default: + event_handled = false; + break; + } + + ED_region_tag_redraw(slider->region_header); + + return event_handled; +} + +/* Return string based on the current state of the slider. */ +void ED_slider_status_string_get(const struct tSlider *slider, + char *status_string, + const size_t size_of_status_string) +{ + /* 50 characters is enough to fit the individual setting strings. Extend if message is longer. */ + char overshoot_str[50]; + char precision_str[50]; + char increments_str[50]; + + if (slider->allow_overshoot) { + if (slider->overshoot) { + STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot")); + } + else { + STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot")); + } + } + else { + STRNCPY(overshoot_str, TIP_("Overshoot disabled")); + } + + if (slider->precision) { + STRNCPY(precision_str, TIP_("[Shift] - Precision active")); + } + else { + STRNCPY(precision_str, TIP_("Shift - Hold for precision")); + } + + if (slider->increments) { + STRNCPY(increments_str, TIP_("[Ctrl] - Increments active")); + } + else { + STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments")); + } + + BLI_snprintf(status_string, + size_of_status_string, + "%s | %s | %s", + overshoot_str, + precision_str, + increments_str); +} + +void ED_slider_destroy(struct bContext *C, tSlider *slider) +{ + /* Remove draw callback. */ + ED_region_draw_cb_exit(slider->region_header->type, slider->draw_handle); + ED_area_status_text(slider->area, NULL); + ED_workspace_status_text(C, NULL); + MEM_freeN(slider); +} + +/* Setters & Getters */ + +float ED_slider_factor_get(struct tSlider *slider) +{ + return slider->factor; +} + +void ED_slider_factor_set(struct tSlider *slider, const float factor) +{ + slider->factor = factor; + if (!slider->overshoot) { + slider->factor = clamp_f(slider->factor, 0, 1); + } +} + +bool ED_slider_allow_overshoot_get(struct tSlider *slider) +{ + return slider->allow_overshoot; +} + +void ED_slider_allow_overshoot_set(struct tSlider *slider, const bool value) +{ + slider->allow_overshoot = value; +} + +/** \} */ + /** * Callback that draws a line between the mouse and a position given as the initial argument. */ -- cgit v1.2.3