Create generic modal functions from GRAPH_OT_decimate

This patch extracts the modal functions from GRAPH_OT_decimate
and makes them generic so they can be reused by future operators

Reviewed by: Sybren A. Stüvel
Differential Revision: https://developer.blender.org/D9326
Ref: D9326
This commit is contained in:
Christoph Lendenfeld 2021-12-03 22:24:41 +00:00
parent 56ed4c14d3
commit d5920744f4
Notes: blender-bot 2023-02-14 01:07:44 +01:00
Referenced by issue #93565, Spot light no longer projecting an image texture in Blender 3
Referenced by issue #81785, Implementation: Modal Key Manipulation Operators
1 changed files with 161 additions and 142 deletions

View File

@ -82,6 +82,9 @@ typedef struct tGraphSliderOp {
struct tSlider *slider;
/* Each operator has a specific update function. */
void (*modal_update)(struct bContext *, struct wmOperator *);
NumInput num;
} tGraphSliderOp;
@ -176,6 +179,154 @@ static void reset_bezts(tGraphSliderOp *gso)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Common Modal Functions
* \{ */
static void graph_slider_exit(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = op->customdata;
wmWindow *win = CTX_wm_window(C);
/* If data exists, clear its data and exit. */
if (gso == NULL) {
return;
}
ScrArea *area = gso->area;
LinkData *link;
ED_slider_destroy(C, gso->slider);
for (link = gso->bezt_arr_list.first; link != NULL; link = link->next) {
tBeztCopyData *copy = link->data;
MEM_freeN(copy->bezt);
MEM_freeN(link->data);
}
BLI_freelistN(&gso->bezt_arr_list);
MEM_freeN(gso);
/* Return to normal cursor and header status. */
WM_cursor_modal_restore(win);
ED_area_status_text(area, NULL);
/* cleanup */
op->customdata = NULL;
}
static int graph_slider_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tGraphSliderOp *gso = op->customdata;
const bool has_numinput = hasNumInput(&gso->num);
ED_slider_modal(gso->slider, event);
switch (event->type) {
/* Confirm */
case LEFTMOUSE:
case EVT_RETKEY:
case EVT_PADENTER: {
if (event->val == KM_PRESS) {
graph_slider_exit(C, op);
return OPERATOR_FINISHED;
}
break;
}
/* Cancel */
case EVT_ESCKEY:
case RIGHTMOUSE: {
if (event->val == KM_PRESS) {
reset_bezts(gso);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
graph_slider_exit(C, op);
return OPERATOR_CANCELLED;
}
break;
}
/* When the mouse is moved, the percentage and the keyframes update. */
case MOUSEMOVE: {
if (has_numinput == false) {
/* Do the update as specified by the operator. */
gso->modal_update(C, op);
}
break;
}
default: {
if ((event->val == KM_PRESS) && handleNumInput(C, &gso->num, event)) {
float value;
float percentage = RNA_property_float_get(op->ptr, gso->percentage_prop);
/* 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 = percentage * 100.0f;
applyNumInput(&gso->num, &value);
percentage = value / 100.0f;
ED_slider_factor_set(gso->slider, percentage);
RNA_property_float_set(op->ptr, gso->percentage_prop, percentage);
gso->modal_update(C, op);
break;
}
/* Unhandled event - maybe it was some view manip? */
/* Allow to pass through. */
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
}
}
return OPERATOR_RUNNING_MODAL;
}
/* Allocate tGraphSliderOp and assign to op->customdata. */
static int graph_slider_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tGraphSliderOp *gso;
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EW_SCROLL);
/* Init slide-op data. */
gso = op->customdata = MEM_callocN(sizeof(tGraphSliderOp), "tGraphSliderOp");
/* Get editor data. */
if (ANIM_animdata_get_context(C, &gso->ac) == 0) {
graph_slider_exit(C, op);
return OPERATOR_CANCELLED;
}
gso->percentage_prop = RNA_struct_find_property(op->ptr, "remove_ratio");
gso->scene = CTX_data_scene(C);
gso->area = CTX_wm_area(C);
gso->region = CTX_wm_region(C);
store_original_bezt_arrays(gso);
gso->slider = ED_slider_create(C);
ED_slider_init(gso->slider, event);
if (gso->bezt_arr_list.first == NULL) {
WM_report(RPT_WARNING,
"Fcurve Slider: Can't work on baked channels. Unbake them and try again.");
graph_slider_exit(C, op);
return OPERATOR_CANCELLED;
}
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Decimate Keyframes Operator
* \{ */
@ -207,38 +358,6 @@ static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float erro
ANIM_animdata_freelist(&anim_data);
}
static void decimate_exit(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = op->customdata;
wmWindow *win = CTX_wm_window(C);
/* If data exists, clear its data and exit. */
if (gso == NULL) {
return;
}
ScrArea *area = gso->area;
LinkData *link;
ED_slider_destroy(C, gso->slider);
for (link = gso->bezt_arr_list.first; link != NULL; link = link->next) {
tBeztCopyData *copy = link->data;
MEM_freeN(copy->bezt);
MEM_freeN(link->data);
}
BLI_freelistN(&gso->bezt_arr_list);
MEM_freeN(gso);
/* Return to normal cursor and header status. */
WM_cursor_modal_restore(win);
ED_area_status_text(area, NULL);
/* Cleanup. */
op->customdata = NULL;
}
/* Draw a percentage indicator in workspace footer. */
static void decimate_draw_status(bContext *C, tGraphSliderOp *gso)
{
@ -264,46 +383,6 @@ static void decimate_draw_status(bContext *C, tGraphSliderOp *gso)
ED_workspace_status_text(C, status_str);
}
static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tGraphSliderOp *gso;
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EW_SCROLL);
/* Init slide-op data. */
gso = op->customdata = MEM_callocN(sizeof(tGraphSliderOp), "tGraphSliderOp");
/* Get editor data. */
if (ANIM_animdata_get_context(C, &gso->ac) == 0) {
decimate_exit(C, op);
return OPERATOR_CANCELLED;
}
gso->percentage_prop = RNA_struct_find_property(op->ptr, "remove_ratio");
gso->scene = CTX_data_scene(C);
gso->area = CTX_wm_area(C);
gso->region = CTX_wm_region(C);
store_original_bezt_arrays(gso);
gso->slider = ED_slider_create(C);
ED_slider_init(gso->slider, event);
ED_slider_allow_overshoot_set(gso->slider, false);
decimate_draw_status(C, gso);
if (gso->bezt_arr_list.first == NULL) {
WM_report(RPT_WARNING,
"Fcurve Decimate: Can't decimate baked channels. Unbake them and try again.");
decimate_exit(C, op);
return OPERATOR_CANCELLED;
}
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static void graphkeys_decimate_modal_update(bContext *C, wmOperator *op)
{
/* Perform decimate updates - in response to some user action
@ -324,79 +403,19 @@ static void graphkeys_decimate_modal_update(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
/* This assumes that we are in "DECIM_RATIO" mode. This is because the error margin is very hard
* and finicky to control with this modal mouse grab method. Therefore, it is expected that the
* error margin mode is not adjusted by the modal operator but instead tweaked via the redo
* panel. */
tGraphSliderOp *gso = op->customdata;
const int invoke_result = graph_slider_invoke(C, op, event);
const bool has_numinput = hasNumInput(&gso->num);
ED_slider_modal(gso->slider, event);
switch (event->type) {
case LEFTMOUSE: /* Confirm */
case EVT_RETKEY:
case EVT_PADENTER: {
if (event->val == KM_PRESS) {
decimate_exit(C, op);
return OPERATOR_FINISHED;
}
break;
}
case EVT_ESCKEY: /* Cancel */
case RIGHTMOUSE: {
if (event->val == KM_PRESS) {
reset_bezts(gso);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
decimate_exit(C, op);
return OPERATOR_CANCELLED;
}
break;
}
/* Percentage Change... */
case MOUSEMOVE: /* Calculate new position. */
{
if (has_numinput == false) {
/* Update pose to reflect the new values. */
graphkeys_decimate_modal_update(C, op);
}
break;
}
default: {
if ((event->val == KM_PRESS) && handleNumInput(C, &gso->num, event)) {
float value;
float percentage = RNA_property_float_get(op->ptr, gso->percentage_prop);
/* 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 = percentage * 100.0f;
applyNumInput(&gso->num, &value);
percentage = value / 100.0f;
RNA_property_float_set(op->ptr, gso->percentage_prop, percentage);
/* Update decimate output to reflect the new values. */
graphkeys_decimate_modal_update(C, op);
break;
}
/* Unhandled event - maybe it was some view manipulation? */
/* Allow to pass through. */
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
}
if (invoke_result == OPERATOR_CANCELLED) {
return OPERATOR_CANCELLED;
}
return OPERATOR_RUNNING_MODAL;
tGraphSliderOp *gso = op->customdata;
gso->modal_update = graphkeys_decimate_modal_update;
ED_slider_allow_overshoot_set(gso->slider, false);
return invoke_result;
}
static int graphkeys_decimate_exec(bContext *C, wmOperator *op)
@ -500,7 +519,7 @@ void GRAPH_OT_decimate(wmOperatorType *ot)
ot->poll_property = graphkeys_decimate_poll_property;
ot->get_description = graphkeys_decimate_desc;
ot->invoke = graphkeys_decimate_invoke;
ot->modal = graphkeys_decimate_modal;
ot->modal = graph_slider_modal;
ot->exec = graphkeys_decimate_exec;
ot->poll = graphop_editable_keyframes_poll;