Support units in modal numinput

Summary:
This completly changes the way modal numinput is handled. Now, edited expression is a string, which then gets unit- and py-evaluated to get a float value.

We gain many power and flexibility, but lose a few "shortcuts" like '-' to negate, or '/' to inverse (if they are really needed, we still can add them with modifiers, like e.g. ctrl-/ or so).

Features:
- units (cm, ", deg, etc.).
- basic operations from python/BKE_unit (+, *, **, etc.), and math constants and functions (pi, sin, etc.).
- you can navigate in edited value (left/right key, ctrl to move by block) and insert/delete chars, e.g. to fix a typo without having to rewrite everything.
- you can go to next/previous value with (ctrl-)TAB key.
- As before, hitting backspace after having deleted all leading chars will first reset the edited value to init state, and on second press, the whole "modal numinput" editing will be cancelled, going back to usual transform with mouse.

Notes:
- Did not touch to how values are shown in header when modal numinput is not enabled (would do that in another commit), so this is still quite inconsistent.
- Added back radian support in BKE_unit.
- Added arcminute/arcsecond to BKE_unit.
(those unit changes affect all angle UI controls, btw, so you can now enter radians or longitude/latitude values when in degrees units).

Related to T37600.

Reviewers: brecht, campbellbarton, carter2422

Reviewed By: brecht, campbellbarton, carter2422
Thanks everybody!

Differential Revision: http://developer.blender.org/D61
This commit is contained in:
Bastien Montagne 2013-12-21 17:11:43 +01:00
parent f72b86da8c
commit 87cc890aef
Notes: blender-bot 2023-02-14 14:37:35 +01:00
Referenced by issue #38314, Negative rotation (With minus) after degree to reverse
Referenced by issue #38252, Transforms = invalid
Referenced by issue #28293, Cannot enter decimal point when scaling
11 changed files with 1299 additions and 968 deletions

View File

@ -34,7 +34,7 @@ extern "C" {
/* in all cases the value is assumed to be scaled by the user preference */
/* humanly readable representation of a value in units (used for button drawing) */
void bUnit_AsString(char *str, int len_max, double value, int prec, int system, int type, int split, int pad);
size_t bUnit_AsString(char *str, int len_max, double value, int prec, int system, int type, int split, int pad);
/* replace units with values, used before python button evaluation */
int bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type);

View File

@ -260,9 +260,12 @@ static struct bUnitCollection buNaturalTimeCollection = {buNaturalTimeDef, 3, 0,
static struct bUnitDef buNaturalRotDef[] = {
{"degree", "degrees", "°", NULL, "Degrees", M_PI / 180.0, 0.0, B_UNIT_DEF_NONE},
// {"radian", "radians", "r", NULL, "Radians", 1.0, 0.0, B_UNIT_DEF_NONE},
// {"turn", "turns", "t", NULL, "Turns", 1.0/(M_PI*2.0), 0.0,B_UNIT_DEF_NONE},
{"degree", "degrees", "°", "d", "Degrees", M_PI / 180.0, 0.0, B_UNIT_DEF_NONE},
/* arcminutes/arcseconds are used in Astronomy/Navigation areas... */
{"arcminute", "arcminutes", "'", NULL, "Arcminutes", (M_PI / 180.0) / 60.0, 0.0, B_UNIT_DEF_SUPPRESS},
{"arcsecond", "arcseconds", "\"", NULL, "Arcseconds", (M_PI / 180.0) / 3600.0, 0.0, B_UNIT_DEF_SUPPRESS},
{"radian", "radians", "r", NULL, "Radians", 1.0, 0.0, B_UNIT_DEF_NONE},
// {"turn", "turns", "t", NULL, "Turns", 1.0 / (M_PI * 2.0), 0.0, B_UNIT_DEF_NONE},
{NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
};
static struct bUnitCollection buNaturalRotCollection = {buNaturalRotDef, 0, 0, sizeof(buNaturalRotDef) / sizeof(bUnitDef)};
@ -340,12 +343,12 @@ static void unit_dual_convert(double value, bUnitCollection *usys, bUnitDef **un
*unit_b = unit_best_fit(*value_b, usys, *unit_a, 1);
}
static int unit_as_string(char *str, int len_max, double value, int prec, bUnitCollection *usys,
/* non exposed options */
bUnitDef *unit, char pad)
static size_t unit_as_string(char *str, int len_max, double value, int prec, bUnitCollection *usys,
/* non exposed options */
bUnitDef *unit, char pad)
{
double value_conv;
int len, i;
size_t len, i;
if (unit) {
/* use unit without finding the best one */
@ -413,8 +416,10 @@ static int unit_as_string(char *str, int len_max, double value, int prec, bUnitC
return i;
}
/* Used for drawing number buttons, try keep fast */
void bUnit_AsString(char *str, int len_max, double value, int prec, int system, int type, int split, int pad)
/* Used for drawing number buttons, try keep fast.
* Return the length of the generated string.
*/
size_t bUnit_AsString(char *str, int len_max, double value, int prec, int system, int type, int split, int pad)
{
bUnitCollection *usys = unit_get_system(system, type);
@ -430,20 +435,21 @@ void bUnit_AsString(char *str, int len_max, double value, int prec, int system,
/* check the 2 is a smaller unit */
if (unit_b > unit_a) {
int i = unit_as_string(str, len_max, value_a, prec, usys, unit_a, '\0');
size_t i;
i = unit_as_string(str, len_max, value_a, prec, usys, unit_a, '\0');
/* is there enough space for at least 1 char of the next unit? */
if (i + 2 < len_max) {
str[i++] = ' ';
/* use low precision since this is a smaller unit */
unit_as_string(str + i, len_max - i, value_b, prec ? 1 : 0, usys, unit_b, '\0');
i += unit_as_string(str + i, len_max - i, value_b, prec ? 1 : 0, usys, unit_b, '\0');
}
return;
return i;
}
}
unit_as_string(str, len_max, value, prec, usys, NULL, pad ? ' ' : '\0');
return unit_as_string(str, len_max, value, prec, usys, NULL, pad ? ' ' : '\0');
}
BLI_INLINE int isalpha_or_utf8(const int ch)
@ -606,15 +612,8 @@ int bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double sca
return 0;
}
{ /* make lowercase */
int i;
char *ch = str;
for (i = 0; (i < len_max) && (*ch != '\0'); i++, ch++) {
if ((*ch >= 'A') && (*ch <= 'Z'))
*ch += ('a' - 'A');
}
}
/* make lowercase */
BLI_ascii_strtolower(str, len_max);
for (unit = usys->units; unit->name; unit++) {
/* in case there are multiple instances */

View File

@ -45,6 +45,7 @@
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_unit.h"
#include "RNA_access.h"
#include "RNA_define.h"
@ -603,6 +604,7 @@ typedef struct MarkerMove {
/* return 0 if not OK */
static int ed_marker_move_init(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
ListBase *markers = ED_context_get_markers(C);
MarkerMove *mm;
TimeMarker *marker;
@ -623,8 +625,10 @@ static int ed_marker_move_init(bContext *C, wmOperator *op)
initNumInput(&mm->num);
mm->num.idx_max = 0; /* one axis */
mm->num.flag |= NUM_NO_FRACTION;
mm->num.increment = 1.0f;
mm->num.val_flag[0] |= NUM_NO_FRACTION;
mm->num.unit_sys = scene->unit.system;
/* No time unit supporting frames currently... */
mm->num.unit_type[0] = B_UNIT_NONE;
for (a = 0, marker = markers->first; marker; marker = marker->next) {
if (marker->flag & SELECT) {
@ -832,7 +836,7 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even
}
if (event->val == KM_PRESS) {
if (handleNumInput(&mm->num, event)) {
if (handleNumInput(C, &mm->num, event)) {
char str_tx[NUM_STR_REP_LEN];
float value = RNA_int_get(op->ptr, "frames");
applyNumInput(&mm->num, &value);

View File

@ -27,40 +27,49 @@
#ifndef __ED_NUMINPUT_H__
#define __ED_NUMINPUT_H__
/*
* The ctrl value has different meaning:
* 0 : No value has been typed
*
* otherwise, |value| - 1 is where the cursor is located after the period
* Positive : number is positive
* Negative : number is negative
*/
#define NUM_STR_REP_LEN 64
#define NUM_MAX_ELEMENTS 3
typedef struct NumInput {
short idx;
short idx_max;
short flag; /* Different flags to indicate different behaviors */
char inv[3]; /* If the value is inverted or not */
float val[3]; /* Direct value of the input */
int ctrl[3]; /* Control to indicate what to do with the numbers that are typed */
float increment;
short idx_max; /* idx_max < NUM_MAX_ELEMENTS */
int unit_sys;
int unit_type[NUM_MAX_ELEMENTS]; /* Each value can have a different type */
bool unit_use_radians;
short flag; /* Flags affecting all values' behavior */
short val_flag[NUM_MAX_ELEMENTS]; /* Per-value flags */
float val[NUM_MAX_ELEMENTS]; /* Direct value of the input */
float val_org[NUM_MAX_ELEMENTS]; /* Original value of the input, for reset */
float val_inc[NUM_MAX_ELEMENTS]; /* Increment steps */
short idx; /* Active element/value */
char str[NUM_STR_REP_LEN]; /* String as typed by user for edited value (we assume ASCII world!) */
/* Current position of cursor in edited value str (first byte of "current" letter, so 0 for an empty str) */
int str_cur;
} NumInput;
/* NUMINPUT FLAGS */
#define NUM_NULL_ONE 2
#define NUM_NO_NEGATIVE 4
#define NUM_NO_ZERO 8
#define NUM_NO_FRACTION 16
#define NUM_AFFECT_ALL 32
/* NumInput.flag */
enum {
NUM_AFFECT_ALL = (1 << 0),
};
/* NumInput.val_flag[] */
enum {
/* Public! */
NUM_NULL_ONE = (1 << 0),
NUM_NO_NEGATIVE = (1 << 1),
NUM_NO_ZERO = (1 << 2),
NUM_NO_FRACTION = (1 << 3),
/* (1 << 9) and above are reserved for internal flags! */
};
/*********************** NumInput ********************************/
void initNumInput(NumInput *n);
#define NUM_STR_REP_LEN 20 /* str must be NUM_STR_LEN * (idx_max + 1) length. */
void outputNumInput(NumInput *n, char *str);
bool hasNumInput(const NumInput *n);
void applyNumInput(NumInput *n, float *vec);
bool handleNumInput(NumInput *n, const struct wmEvent *event);
bool handleNumInput(struct bContext *C, NumInput *n, const struct wmEvent *event);
#define NUM_MODAL_INCREMENT_UP 18
#define NUM_MODAL_INCREMENT_DOWN 19

View File

@ -36,6 +36,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_editmesh.h"
#include "BKE_unit.h"
#include "RNA_define.h"
#include "RNA_access.h"
@ -74,18 +75,23 @@ typedef struct {
static void edbm_bevel_update_header(wmOperator *op, bContext *C)
{
const char *str = IFACE_("Confirm: Enter/LClick, Cancel: (Esc/RMB), Offset: %s, Segments: %d");
const char *str = IFACE_("Confirm: (Enter/LMB), Cancel: (Esc/RMB), Offset: %s, Segments: %d");
char msg[HEADER_LENGTH];
ScrArea *sa = CTX_wm_area(C);
if (sa) {
BevelData *opdata = op->customdata;
char offset_str[NUM_STR_REP_LEN];
BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "offset"));
BLI_snprintf(msg, HEADER_LENGTH, str,
offset_str,
RNA_int_get(op->ptr, "segments")
);
if (hasNumInput(&opdata->num_input)) {
outputNumInput(&opdata->num_input, offset_str);
}
else {
BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "offset"));
}
BLI_snprintf(msg, HEADER_LENGTH, str, offset_str, RNA_int_get(op->ptr, "segments"));
ED_area_headerprint(sa, msg);
}
@ -94,6 +100,7 @@ static void edbm_bevel_update_header(wmOperator *op, bContext *C)
static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
{
Object *obedit = CTX_data_edit_object(C);
Scene *scene = CTX_data_scene(C);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BevelData *opdata;
@ -108,7 +115,10 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
opdata->shift_factor = -1.0f;
initNumInput(&opdata->num_input);
opdata->num_input.flag = NUM_NO_NEGATIVE;
opdata->num_input.idx_max = 0;
opdata->num_input.val_flag[0] |= NUM_NO_NEGATIVE;
opdata->num_input.unit_sys = scene->unit.system;
opdata->num_input.unit_type[0] = B_UNIT_NONE; /* Not sure this is a factor or a unit? */
/* avoid the cost of allocating a bm copy */
if (is_modal) {
@ -306,10 +316,9 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
BevelData *opdata = op->customdata;
int segments = RNA_int_get(op->ptr, "segments");
if (event->val == KM_PRESS) {
/* Try to handle numeric inputs... */
if (handleNumInput(&opdata->num_input, event)) {
if (event->val == KM_PRESS && hasNumInput(&opdata->num_input)) {
/* Modal numinput active, try to handle numeric inputs first... */
if (handleNumInput(C, &opdata->num_input, event)) {
float value = RNA_float_get(op->ptr, "offset");
applyNumInput(&opdata->num_input, &value);
RNA_float_set(op->ptr, "offset", value);
@ -318,51 +327,73 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
}
else {
bool handled = false;
switch (event->type) {
case ESCKEY:
case RIGHTMOUSE:
edbm_bevel_cancel(C, op);
return OPERATOR_CANCELLED;
switch (event->type) {
case ESCKEY:
case RIGHTMOUSE:
edbm_bevel_cancel(C, op);
return OPERATOR_CANCELLED;
case MOUSEMOVE:
if (!hasNumInput(&opdata->num_input)) {
const float factor = edbm_bevel_mval_factor(op, event);
RNA_float_set(op->ptr, "offset", factor);
case MOUSEMOVE:
if (!hasNumInput(&opdata->num_input)) {
const float factor = edbm_bevel_mval_factor(op, event);
RNA_float_set(op->ptr, "offset", factor);
edbm_bevel_calc(op);
edbm_bevel_update_header(op, C);
handled = true;
}
break;
case LEFTMOUSE:
case PADENTER:
case RETKEY:
edbm_bevel_calc(op);
edbm_bevel_exit(C, op);
return OPERATOR_FINISHED;
/* Note this will prevent padplus and padminus to ever activate modal numinput.
* This is not really an issue though, as we only expect positive values here...
* Else we could force them to only modify segments number when shift is pressed, or so.
*/
case WHEELUPMOUSE: /* change number of segments */
case PADPLUSKEY:
if (event->val == KM_RELEASE)
break;
segments++;
RNA_int_set(op->ptr, "segments", segments);
edbm_bevel_calc(op);
edbm_bevel_update_header(op, C);
handled = true;
break;
case WHEELDOWNMOUSE: /* change number of segments */
case PADMINUS:
if (event->val == KM_RELEASE)
break;
segments = max_ii(segments - 1, 1);
RNA_int_set(op->ptr, "segments", segments);
edbm_bevel_calc(op);
edbm_bevel_update_header(op, C);
handled = true;
break;
}
if (!handled && event->val == KM_PRESS) {
/* Modal numinput inactive, try to handle numeric inputs last... */
if (handleNumInput(C, &opdata->num_input, event)) {
float value = RNA_float_get(op->ptr, "offset");
applyNumInput(&opdata->num_input, &value);
RNA_float_set(op->ptr, "offset", value);
edbm_bevel_calc(op);
edbm_bevel_update_header(op, C);
return OPERATOR_RUNNING_MODAL;
}
break;
case LEFTMOUSE:
case PADENTER:
case RETKEY:
edbm_bevel_calc(op);
edbm_bevel_exit(C, op);
return OPERATOR_FINISHED;
case WHEELUPMOUSE: /* change number of segments */
case PADPLUSKEY:
if (event->val == KM_RELEASE)
break;
segments++;
RNA_int_set(op->ptr, "segments", segments);
edbm_bevel_calc(op);
edbm_bevel_update_header(op, C);
break;
case WHEELDOWNMOUSE: /* change number of segments */
case PADMINUS:
if (event->val == KM_RELEASE)
break;
segments = max_ii(segments - 1, 1);
RNA_int_set(op->ptr, "segments", segments);
edbm_bevel_calc(op);
edbm_bevel_update_header(op, C);
break;
}
}
return OPERATOR_RUNNING_MODAL;

View File

@ -36,6 +36,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_editmesh.h"
#include "BKE_unit.h"
#include "RNA_define.h"
#include "RNA_access.h"
@ -110,6 +111,7 @@ static void edbm_inset_update_header(wmOperator *op, bContext *C)
static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal)
{
InsetData *opdata;
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@ -129,6 +131,9 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal)
initNumInput(&opdata->num_input);
opdata->num_input.idx_max = 1; /* Two elements. */
opdata->num_input.unit_sys = scene->unit.system;
opdata->num_input.unit_type[0] = B_UNIT_LENGTH;
opdata->num_input.unit_type[1] = B_UNIT_LENGTH;
if (is_modal) {
View3D *v3d = CTX_wm_view3d(C);
@ -291,10 +296,9 @@ static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
InsetData *opdata = op->customdata;
if (event->val == KM_PRESS) {
/* Try to handle numeric inputs... */
if (handleNumInput(&opdata->num_input, event)) {
if (event->val == KM_PRESS && hasNumInput(&opdata->num_input)) {
/* Modal numinput active, try to handle numeric inputs first... */
if (handleNumInput(C, &opdata->num_input, event)) {
float amounts[2] = {RNA_float_get(op->ptr, "thickness"),
RNA_float_get(op->ptr, "depth")};
applyNumInput(&opdata->num_input, amounts);
@ -312,134 +316,163 @@ static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
}
else {
bool handled = false;
switch (event->type) {
case ESCKEY:
case RIGHTMOUSE:
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
switch (event->type) {
case ESCKEY:
case RIGHTMOUSE:
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
case MOUSEMOVE:
if (!hasNumInput(&opdata->num_input)) {
float mdiff[2];
float amount;
case MOUSEMOVE:
if (!hasNumInput(&opdata->num_input)) {
float mdiff[2];
float amount;
mdiff[0] = opdata->mcenter[0] - event->mval[0];
mdiff[1] = opdata->mcenter[1] - event->mval[1];
mdiff[0] = opdata->mcenter[0] - event->mval[0];
mdiff[1] = opdata->mcenter[1] - event->mval[1];
if (opdata->modify_depth)
amount = opdata->old_depth + ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size);
else
amount = opdata->old_thickness - ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size);
if (opdata->modify_depth)
amount = opdata->old_depth + ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size);
else
amount = opdata->old_thickness - ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size);
/* Fake shift-transform... */
if (opdata->shift)
amount = (amount - opdata->shift_amount) * 0.1f + opdata->shift_amount;
/* Fake shift-transform... */
if (opdata->shift)
amount = (amount - opdata->shift_amount) * 0.1f + opdata->shift_amount;
if (opdata->modify_depth)
RNA_float_set(op->ptr, "depth", amount);
else {
amount = max_ff(amount, 0.0f);
RNA_float_set(op->ptr, "thickness", amount);
}
if (opdata->modify_depth)
RNA_float_set(op->ptr, "depth", amount);
else {
amount = max_ff(amount, 0.0f);
RNA_float_set(op->ptr, "thickness", amount);
if (edbm_inset_calc(op))
edbm_inset_update_header(op, C);
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
}
handled = true;
}
break;
if (edbm_inset_calc(op))
edbm_inset_update_header(op, C);
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
case LEFTMOUSE:
case PADENTER:
case RETKEY:
edbm_inset_calc(op);
edbm_inset_exit(C, op);
return OPERATOR_FINISHED;
case LEFTSHIFTKEY:
case RIGHTSHIFTKEY:
if (event->val == KM_PRESS) {
if (opdata->modify_depth)
opdata->shift_amount = RNA_float_get(op->ptr, "depth");
else
opdata->shift_amount = RNA_float_get(op->ptr, "thickness");
opdata->shift = true;
handled = true;
}
else {
opdata->shift_amount = 0.0f;
opdata->shift = false;
handled = true;
}
break;
case LEFTCTRLKEY:
case RIGHTCTRLKEY:
{
float mlen[2];
mlen[0] = opdata->mcenter[0] - event->mval[0];
mlen[1] = opdata->mcenter[1] - event->mval[1];
if (event->val == KM_PRESS) {
opdata->old_thickness = RNA_float_get(op->ptr, "thickness");
if (opdata->shift)
opdata->shift_amount = opdata->old_thickness;
opdata->modify_depth = true;
}
else {
opdata->old_depth = RNA_float_get(op->ptr, "depth");
if (opdata->shift)
opdata->shift_amount = opdata->old_depth;
opdata->modify_depth = false;
}
opdata->initial_length = len_v2(mlen);
edbm_inset_update_header(op, C);
handled = true;
break;
}
break;
case LEFTMOUSE:
case PADENTER:
case RETKEY:
edbm_inset_calc(op);
edbm_inset_exit(C, op);
return OPERATOR_FINISHED;
case LEFTSHIFTKEY:
case RIGHTSHIFTKEY:
if (event->val == KM_PRESS) {
if (opdata->modify_depth)
opdata->shift_amount = RNA_float_get(op->ptr, "depth");
else
opdata->shift_amount = RNA_float_get(op->ptr, "thickness");
opdata->shift = true;
}
else {
opdata->shift_amount = 0.0f;
opdata->shift = false;
}
break;
case LEFTCTRLKEY:
case RIGHTCTRLKEY:
{
float mlen[2];
mlen[0] = opdata->mcenter[0] - event->mval[0];
mlen[1] = opdata->mcenter[1] - event->mval[1];
if (event->val == KM_PRESS) {
opdata->old_thickness = RNA_float_get(op->ptr, "thickness");
if (opdata->shift)
opdata->shift_amount = opdata->old_thickness;
opdata->modify_depth = true;
}
else {
opdata->old_depth = RNA_float_get(op->ptr, "depth");
if (opdata->shift)
opdata->shift_amount = opdata->old_depth;
opdata->modify_depth = false;
}
opdata->initial_length = len_v2(mlen);
edbm_inset_update_header(op, C);
break;
case OKEY:
if (event->val == KM_PRESS) {
const bool use_outset = RNA_boolean_get(op->ptr, "use_outset");
RNA_boolean_set(op->ptr, "use_outset", !use_outset);
if (edbm_inset_calc(op)) {
edbm_inset_update_header(op, C);
}
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
}
handled = true;
}
break;
case BKEY:
if (event->val == KM_PRESS) {
const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
RNA_boolean_set(op->ptr, "use_boundary", !use_boundary);
if (edbm_inset_calc(op)) {
edbm_inset_update_header(op, C);
}
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
}
handled = true;
}
break;
case IKEY:
if (event->val == KM_PRESS) {
const bool use_individual = RNA_boolean_get(op->ptr, "use_individual");
RNA_boolean_set(op->ptr, "use_individual", !use_individual);
if (edbm_inset_calc(op)) {
edbm_inset_update_header(op, C);
}
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
}
handled = true;
}
break;
}
case OKEY:
if (event->val == KM_PRESS) {
const bool use_outset = RNA_boolean_get(op->ptr, "use_outset");
RNA_boolean_set(op->ptr, "use_outset", !use_outset);
if (edbm_inset_calc(op)) {
edbm_inset_update_header(op, C);
}
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
}
}
break;
case BKEY:
if (event->val == KM_PRESS) {
const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
RNA_boolean_set(op->ptr, "use_boundary", !use_boundary);
if (edbm_inset_calc(op)) {
edbm_inset_update_header(op, C);
}
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
}
}
break;
case IKEY:
if (event->val == KM_PRESS) {
const bool use_individual = RNA_boolean_get(op->ptr, "use_individual");
RNA_boolean_set(op->ptr, "use_individual", !use_individual);
if (edbm_inset_calc(op)) {
edbm_inset_update_header(op, C);
}
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
}
}
break;
if (!handled && event->val == KM_PRESS) {
/* Modal numinput inactive, try to handle numeric inputs last... */
if (handleNumInput(C, &opdata->num_input, event)) {
float amounts[2] = {RNA_float_get(op->ptr, "thickness"),
RNA_float_get(op->ptr, "depth")};
applyNumInput(&opdata->num_input, amounts);
amounts[0] = max_ff(amounts[0], 0.0f);
RNA_float_set(op->ptr, "thickness", amounts[0]);
RNA_float_set(op->ptr, "depth", amounts[1]);
if (edbm_inset_calc(op)) {
edbm_inset_update_header(op, C);
return OPERATOR_RUNNING_MODAL;
}
else {
edbm_inset_cancel(C, op);
return OPERATOR_CANCELLED;
}
}
}
}
return OPERATOR_RUNNING_MODAL;

View File

@ -43,6 +43,7 @@
#include "BKE_report.h"
#include "BKE_editmesh.h"
#include "BKE_DerivedMesh.h"
#include "BKE_unit.h"
#include "BIF_gl.h"
@ -392,6 +393,7 @@ static void ringsel_exit(bContext *UNUSED(C), wmOperator *op)
static int ringsel_init(bContext *C, wmOperator *op, bool do_cut)
{
RingSelOpData *lcd;
Scene *scene = CTX_data_scene(C);
/* alloc new customdata */
lcd = op->customdata = MEM_callocN(sizeof(RingSelOpData), "ringsel Modal Op Data");
@ -405,8 +407,12 @@ static int ringsel_init(bContext *C, wmOperator *op, bool do_cut)
lcd->do_cut = do_cut;
initNumInput(&lcd->num);
lcd->num.idx_max = 0;
lcd->num.flag |= NUM_NO_NEGATIVE | NUM_NO_FRACTION;
lcd->num.idx_max = 1;
lcd->num.val_flag[0] |= NUM_NO_NEGATIVE | NUM_NO_FRACTION;
/* No specific flags for smoothness. */
lcd->num.unit_sys = scene->unit.system;
lcd->num.unit_type[0] = B_UNIT_NONE;
lcd->num.unit_type[1] = B_UNIT_NONE;
/* XXX, temp, workaround for [# ] */
EDBM_mesh_ensure_valid_dm_hack(CTX_data_scene(C), lcd->em);
@ -529,126 +535,163 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event)
view3d_operator_needs_opengl(C);
switch (event->type) {
case RETKEY:
case PADENTER:
case LEFTMOUSE: /* confirm */ // XXX hardcoded
if (event->val == KM_PRESS) {
/* finish */
ED_region_tag_redraw(lcd->ar);
ED_area_headerprint(CTX_wm_area(C), NULL);
if (lcd->eed) {
/* set for redo */
BM_mesh_elem_index_ensure(lcd->em->bm, BM_EDGE);
RNA_int_set(op->ptr, "edge_index", BM_elem_index_get(lcd->eed));
/* execute */
ringsel_finish(C, op);
ringsel_exit(C, op);
}
else {
ringcut_cancel(C, op);
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
ED_region_tag_redraw(lcd->ar);
break;
case RIGHTMOUSE: /* abort */ // XXX hardcoded
ED_region_tag_redraw(lcd->ar);
ringsel_exit(C, op);
ED_area_headerprint(CTX_wm_area(C), NULL);
return OPERATOR_FINISHED;
case ESCKEY:
if (event->val == KM_RELEASE) {
/* cancel */
ED_region_tag_redraw(lcd->ar);
ED_area_headerprint(CTX_wm_area(C), NULL);
ringcut_cancel(C, op);
return OPERATOR_CANCELLED;
}
ED_region_tag_redraw(lcd->ar);
break;
case PADPLUSKEY:
case PAGEUPKEY:
case WHEELUPMOUSE: /* change number of cuts */
if (event->val == KM_RELEASE)
break;
if (event->alt == 0) {
cuts++;
cuts = CLAMPIS(cuts, 0, SUBD_CUTS_MAX);
RNA_int_set(op->ptr, "number_cuts", cuts);
ringsel_find_edge(lcd, cuts);
show_cuts = true;
}
else {
smoothness = min_ff(smoothness + 0.05f, SUBD_SMOOTH_MAX);
RNA_float_set(op->ptr, "smoothness", smoothness);
show_cuts = true;
}
ED_region_tag_redraw(lcd->ar);
break;
case PADMINUS:
case PAGEDOWNKEY:
case WHEELDOWNMOUSE: /* change number of cuts */
if (event->val == KM_RELEASE)
break;
if (event->alt == 0) {
cuts = max_ii(cuts - 1, 1);
RNA_int_set(op->ptr, "number_cuts", cuts);
ringsel_find_edge(lcd, cuts);
show_cuts = true;
}
else {
smoothness = max_ff(smoothness - 0.05f, -SUBD_SMOOTH_MAX);
RNA_float_set(op->ptr, "smoothness", smoothness);
show_cuts = true;
}
ED_region_tag_redraw(lcd->ar);
break;
case MOUSEMOVE: /* mouse moved somewhere to select another loop */
{
lcd->vc.mval[0] = event->mval[0];
lcd->vc.mval[1] = event->mval[1];
loopcut_mouse_move(lcd, cuts);
ED_region_tag_redraw(lcd->ar);
break;
}
}
/* using the keyboard to input the number of cuts */
if (event->val == KM_PRESS) {
/* init as zero so backspace clears */
if (handleNumInput(&lcd->num, event)) {
float value = RNA_int_get(op->ptr, "number_cuts");
applyNumInput(&lcd->num, &value);
if (event->val == KM_PRESS && hasNumInput(&lcd->num)) {
/* Modal numinput active, try to handle numeric inputs first... */
if (handleNumInput(C, &lcd->num, event)) {
float values[2] = {(float)cuts, smoothness};
applyNumInput(&lcd->num, values);
/* allow zero so you can backspace and type in a value
* otherwise 1 as minimum would make more sense */
cuts = CLAMPIS(value, 0, SUBD_CUTS_MAX);
cuts = CLAMPIS(values[0], 0, SUBD_CUTS_MAX);
smoothness = CLAMPIS(values[1], -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX);
RNA_int_set(op->ptr, "number_cuts", cuts);
ringsel_find_edge(lcd, cuts);
show_cuts = true;
RNA_float_set(op->ptr, "smoothness", smoothness);
ED_region_tag_redraw(lcd->ar);
}
}
else {
bool handled = false;
switch (event->type) {
case RETKEY:
case PADENTER:
case LEFTMOUSE: /* confirm */ // XXX hardcoded
if (event->val == KM_PRESS) {
/* finish */
ED_region_tag_redraw(lcd->ar);
ED_area_headerprint(CTX_wm_area(C), NULL);
if (lcd->eed) {
/* set for redo */
BM_mesh_elem_index_ensure(lcd->em->bm, BM_EDGE);
RNA_int_set(op->ptr, "edge_index", BM_elem_index_get(lcd->eed));
/* execute */
ringsel_finish(C, op);
ringsel_exit(C, op);
}
else {
ringcut_cancel(C, op);
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
ED_region_tag_redraw(lcd->ar);
handled = true;
break;
case RIGHTMOUSE: /* abort */ // XXX hardcoded
ED_region_tag_redraw(lcd->ar);
ringsel_exit(C, op);
ED_area_headerprint(CTX_wm_area(C), NULL);
return OPERATOR_FINISHED;
case ESCKEY:
if (event->val == KM_RELEASE) {
/* cancel */
ED_region_tag_redraw(lcd->ar);
ED_area_headerprint(CTX_wm_area(C), NULL);
ringcut_cancel(C, op);
return OPERATOR_CANCELLED;
}
ED_region_tag_redraw(lcd->ar);
handled = true;
break;
case PADPLUSKEY:
case PAGEUPKEY:
case WHEELUPMOUSE: /* change number of cuts */
if (event->val == KM_RELEASE)
break;
if (event->alt == 0) {
cuts++;
cuts = CLAMPIS(cuts, 0, SUBD_CUTS_MAX);
RNA_int_set(op->ptr, "number_cuts", cuts);
ringsel_find_edge(lcd, cuts);
show_cuts = true;
}
else {
smoothness = min_ff(smoothness + 0.05f, SUBD_SMOOTH_MAX);
RNA_float_set(op->ptr, "smoothness", smoothness);
show_cuts = true;
}
ED_region_tag_redraw(lcd->ar);
handled = true;
break;
case PADMINUS:
case PAGEDOWNKEY:
case WHEELDOWNMOUSE: /* change number of cuts */
if (event->val == KM_RELEASE)
break;
if (event->alt == 0) {
cuts = max_ii(cuts - 1, 1);
RNA_int_set(op->ptr, "number_cuts", cuts);
ringsel_find_edge(lcd, cuts);
show_cuts = true;
}
else {
smoothness = max_ff(smoothness - 0.05f, -SUBD_SMOOTH_MAX);
RNA_float_set(op->ptr, "smoothness", smoothness);
show_cuts = true;
}
ED_region_tag_redraw(lcd->ar);
handled = true;
break;
case MOUSEMOVE: /* mouse moved somewhere to select another loop */
{
lcd->vc.mval[0] = event->mval[0];
lcd->vc.mval[1] = event->mval[1];
loopcut_mouse_move(lcd, cuts);
ED_region_tag_redraw(lcd->ar);
handled = true;
break;
}
}
if (!handled && event->val == KM_PRESS) {
/* Modal numinput inactive, try to handle numeric inputs last... */
if (handleNumInput(C, &lcd->num, event)) {
float values[2] = {(float)cuts, smoothness};
applyNumInput(&lcd->num, values);
/* allow zero so you can backspace and type in a value
* otherwise 1 as minimum would make more sense */
cuts = CLAMPIS(values[0], 0, SUBD_CUTS_MAX);
smoothness = CLAMPIS(values[1], -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX);
RNA_int_set(op->ptr, "number_cuts", cuts);
ringsel_find_edge(lcd, cuts);
show_cuts = true;
RNA_float_set(op->ptr, "smoothness", smoothness);
ED_region_tag_redraw(lcd->ar);
}
}
}
if (show_cuts) {
char buf[64];
BLI_snprintf(buf, sizeof(buf), IFACE_("Number of Cuts: %d, Smooth: %.2f (Alt)"), cuts, smoothness);
char buf[64 + NUM_STR_REP_LEN * 2];
char str_rep[NUM_STR_REP_LEN * 2];
if (hasNumInput(&lcd->num)) {
outputNumInput(&lcd->num, str_rep);
}
else {
BLI_snprintf(str_rep, NUM_STR_REP_LEN, "%d", cuts);
BLI_snprintf(str_rep + NUM_STR_REP_LEN, NUM_STR_REP_LEN, "%.2f", smoothness);
}
BLI_snprintf(buf, sizeof(buf), IFACE_("Number of Cuts: %s, Smooth: %s (Alt)"),
str_rep, str_rep + NUM_STR_REP_LEN);
ED_area_headerprint(CTX_wm_area(C), buf);
}

File diff suppressed because it is too large Load Diff

View File

@ -98,4 +98,9 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
set(INC ${INC} ../../python)
endif()
blender_add_lib(bf_editor_util "${SRC}" "${INC}" "${INC_SYS}")

View File

@ -46,4 +46,8 @@ incs = [
if env['WITH_BF_INTERNATIONAL']:
defs.append('WITH_INTERNATIONAL')
if env['WITH_BF_PYTHON']:
defs.append('WITH_PYTHON')
incs.append('../../python')
env.BlenderLib ( 'bf_editors_util', sources, incs, defines=defs, libtype=['core','player'], priority=[330,210] )

View File

@ -24,93 +24,97 @@
* \ingroup edutil
*/
#include <math.h> /* fabs */
#include <stdio.h> /* for size_t */
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_string_cursor_utf8.h"
#include "BKE_context.h"
#include "BKE_unit.h"
#include "DNA_scene_types.h"
#include "WM_api.h"
#include "WM_types.h"
#ifdef WITH_PYTHON
#include "BPY_extern.h"
#endif
#include "ED_numinput.h"
/* NumInput.val_flag[] */
enum {
/* (1 << 8) and below are reserved for public flags! */
NUM_EDITED = (1 << 9), /* User has edited this value somehow. */
NUM_INVALID = (1 << 10), /* Current expression for this value is invalid. */
};
/* ************************** Functions *************************** */
/* ************************** NUMINPUT **************************** */
void initNumInput(NumInput *n)
{
n->flag =
n->idx =
n->idx_max =
n->inv[0] =
n->inv[1] =
n->inv[2] =
n->ctrl[0] =
n->ctrl[1] =
n->ctrl[2] = 0;
n->val[0] =
n->val[1] =
n->val[2] = 0.0f;
n->unit_sys = USER_UNIT_NONE;
n->unit_type[0] = n->unit_type[1] = n->unit_type[2] = B_UNIT_NONE;
n->idx = 0;
n->idx_max = 0;
n->flag = 0;
n->val_flag[0] = n->val_flag[1] = n->val_flag[2] = 0;
zero_v3(n->val_org);
zero_v3(n->val);
n->str[0] = '\0';
n->str_cur = 0;
copy_v3_fl(n->val_inc, 1.0f);
}
/* str must be NUM_STR_REP_LEN * (idx_max + 1) length. */
void outputNumInput(NumInput *n, char *str)
{
char cur;
char inv[] = "1/";
short i, j;
const int ln = NUM_STR_REP_LEN;
const int prec = 4; /* draw-only, and avoids too much issues with radian->degrees conversion. */
for (j = 0; j <= n->idx_max; j++) {
/* if AFFECTALL and no number typed and cursor not on number, use first number */
if (n->flag & NUM_AFFECT_ALL && n->idx != j && n->ctrl[j] == 0)
i = 0;
else
i = j;
i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
if (n->idx != i)
cur = ' ';
else
cur = '|';
if (n->inv[i])
inv[0] = '1';
else
inv[0] = 0;
if (n->val[i] > 1e10f || n->val[i] < -1e10f)
BLI_snprintf(&str[j * ln], ln, "%s%.4e%c", inv, n->val[i], cur);
else
switch (n->ctrl[i]) {
case 0:
BLI_snprintf(&str[j * ln], ln, "%sNONE%c", inv, cur);
break;
case 1:
case -1:
BLI_snprintf(&str[j * ln], ln, "%s%.0f%c", inv, n->val[i], cur);
break;
case 10:
case -10:
BLI_snprintf(&str[j * ln], ln, "%s%.f.%c", inv, n->val[i], cur);
break;
case 100:
case -100:
BLI_snprintf(&str[j * ln], ln, "%s%.1f%c", inv, n->val[i], cur);
break;
case 1000:
case -1000:
BLI_snprintf(&str[j * ln], ln, "%s%.2f%c", inv, n->val[i], cur);
break;
case 10000:
case -10000:
BLI_snprintf(&str[j * ln], ln, "%s%.3f%c", inv, n->val[i], cur);
break;
default:
BLI_snprintf(&str[j * ln], ln, "%s%.4e%c", inv, n->val[i], cur);
break;
if (n->val_flag[i] & NUM_EDITED) {
if (i == n->idx && n->str[0]) {
char before_cursor[NUM_STR_REP_LEN];
char val[16];
if (n->val_flag[i] & NUM_INVALID) {
BLI_strncpy(val, "Invalid", sizeof(val));
}
else {
bUnit_AsString(val, sizeof(val), (double)n->val[i], prec,
n->unit_sys, n->unit_type[i], true, false);
}
BLI_strncpy(before_cursor, n->str, n->str_cur + 1); /* +1 because of trailing '\0' */
BLI_snprintf(&str[j * ln], ln, "[%s|%s] = %s", before_cursor, &n->str[n->str_cur], val);
}
else {
const char *cur = (i == n->idx) ? "|" : "";
if (n->unit_use_radians && n->unit_type[i] == B_UNIT_ROTATION) {
/* Radian exception... */
BLI_snprintf(&str[j * ln], ln, "%s%.6gr%s", cur, n->val[i], cur);
}
else {
char tstr[NUM_STR_REP_LEN];
bUnit_AsString(tstr, ln, (double)n->val[i], prec, n->unit_sys, n->unit_type[i], true, false);
BLI_snprintf(&str[j * ln], ln, "%s%s%s", cur, tstr, cur);
}
}
}
else {
const char *cur = (i == n->idx) ? "|" : "";
BLI_snprintf(&str[j * ln], ln, "%sNONE%s", cur, cur);
}
}
}
@ -119,8 +123,9 @@ bool hasNumInput(const NumInput *n)
short i;
for (i = 0; i <= n->idx_max; i++) {
if (n->ctrl[i])
if (n->val_flag[i] & NUM_EDITED) {
return true;
}
}
return false;
@ -132,177 +137,247 @@ bool hasNumInput(const NumInput *n)
void applyNumInput(NumInput *n, float *vec)
{
short i, j;
float val;
if (hasNumInput(n)) {
for (j = 0; j <= n->idx_max; j++) {
/* if AFFECTALL and no number typed and cursor not on number, use first number */
if (n->flag & NUM_AFFECT_ALL && n->idx != j && n->ctrl[j] == 0)
i = 0;
else
i = j;
i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j;
val = (!(n->val_flag[i] & NUM_EDITED) && n->val_flag[i] & NUM_NULL_ONE) ? 1.0f : n->val[i];
if (n->ctrl[i] == 0 && n->flag & NUM_NULL_ONE) {
vec[j] = 1.0f;
if (n->val_flag[i] & NUM_NO_NEGATIVE && val < 0.0f) {
val = 0.0f;
}
else if (n->val[i] == 0.0f && n->flag & NUM_NO_ZERO) {
vec[j] = 0.0001f;
if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
val = 0.0001f;
}
if (n->val_flag[i] & NUM_NO_FRACTION && val != floorf(val)) {
val = floorf(val + 0.5f);
if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) {
val = 1.0f;
}
}
vec[j] = val;
}
}
}
static void value_to_editstr(NumInput *n, int idx)
{
const int prec = 6; /* editing, higher precision needed. */
bUnit_AsString(n->str, NUM_STR_REP_LEN, (double)n->val[idx], prec,
n->unit_sys, n->unit_type[idx], true, false);
n->str_cur = strlen(n->str);
}
static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf_len)
{
int cur = n->str_cur;
int len = strlen(&n->str[cur]) + 1; /* +1 for the trailing '\0'. */
int n_cur = cur + buf_len;
if (n_cur + len >= NUM_STR_REP_LEN) {
return false;
}
memmove(&n->str[n_cur], &n->str[cur], len);
memcpy(&n->str[cur], buf, sizeof(char) * buf_len);
n->str_cur = n_cur;
return true;
}
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
{
const char *utf8_buf = NULL;
char ascii[2] = {'\0', '\0'};
bool updated = false;
short idx = n->idx, idx_max = n->idx_max;
short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
int cur;
double val;
switch (event->type) {
case EVT_MODAL_MAP:
if (ELEM(event->val, NUM_MODAL_INCREMENT_UP, NUM_MODAL_INCREMENT_DOWN)) {
n->val[idx] += (event->val == NUM_MODAL_INCREMENT_UP) ? n->val_inc[idx] : -n->val_inc[idx];
value_to_editstr(n, idx);
n->val_flag[idx] |= NUM_EDITED;
updated = true;
}
else {
if (n->inv[i]) {
vec[j] = 1.0f / n->val[i];
/* might be a char too... */
utf8_buf = event->utf8_buf;
ascii[0] = event->ascii;
}
break;
case BACKSPACEKEY:
/* Part specific to backspace... */
if (!(n->val_flag[idx] & NUM_EDITED)) {
copy_v3_v3(n->val, n->val_org);
n->val_flag[0] &= ~NUM_EDITED;
n->val_flag[1] &= ~NUM_EDITED;
n->val_flag[2] &= ~NUM_EDITED;
updated = true;
break;
}
else if (event->shift || !n->str[0]) {
n->val[idx] = n->val_org[idx];
n->val_flag[idx] &= ~NUM_EDITED;
n->str[0] = '\0';
n->str_cur = 0;
updated = true;
break;
}
/* Else, common behavior with DELKEY, only difference is remove char(s) before/after the cursor. */
dir = STRCUR_DIR_PREV;
/* fall-through */
case DELKEY:
if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
int t_cur = cur = n->str_cur;
if (event->ctrl) {
mode = STRCUR_JUMP_DELIM;
}
else {
vec[j] = n->val[i];
BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
if (t_cur != cur) {
if (t_cur < cur) {
SWAP(int, t_cur, cur);
n->str_cur = cur;
}
memmove(&n->str[cur], &n->str[t_cur], strlen(&n->str[t_cur]) + 1); /* +1 for trailing '\0'. */
updated = true;
}
}
}
}
}
else {
return false;
}
break;
case LEFTARROWKEY:
dir = STRCUR_DIR_PREV;
/* fall-through */
case RIGHTARROWKEY:
cur = n->str_cur;
if (event->ctrl) {
mode = STRCUR_JUMP_DELIM;
}
BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
if (cur != n->str_cur) {
n->str_cur = cur;
return true;
}
return false;
case HOMEKEY:
if (n->str[0]) {
n->str_cur = 0;
return true;
}
return false;
case ENDKEY:
if (n->str[0]) {
n->str_cur = strlen(n->str);
return true;
}
return false;
case TABKEY:
n->val_org[idx] = n->val[idx];
bool handleNumInput(NumInput *n, const wmEvent *event)
{
float Val = 0;
short idx = n->idx, idx_max = n->idx_max;
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case NUM_MODAL_INCREMENT_UP:
if (!n->ctrl[idx])
n->ctrl[idx] = 1;
n->val[idx] += n->increment;
idx += event->ctrl ? -1 : 1;
idx %= idx_max + 1;
n->idx = idx;
n->val[idx] = n->val_org[idx];
if (n->val_flag[idx] & NUM_EDITED) {
value_to_editstr(n, idx);
}
else {
n->str[0] = '\0';
n->str_cur = 0;
}
return true;
case CKEY:
if (event->ctrl) {
/* Copy current str to the copypaste buffer. */
WM_clipboard_text_set(n->str, 0);
updated = true;
break;
case NUM_MODAL_INCREMENT_DOWN:
if (!n->ctrl[idx])
n->ctrl[idx] = 1;
}
/* fall-through */
case VKEY:
if (event->ctrl) {
/* extract the first line from the clipboard */
char *pbuf = WM_clipboard_text_get(0);
n->val[idx] -= n->increment;
break;
default:
return 0;
}
}
else {
switch (event->type) {
case BACKSPACEKEY:
if (n->ctrl[idx] == 0) {
n->val[0] =
n->val[1] =
n->val[2] = 0.0f;
n->ctrl[0] =
n->ctrl[1] =
n->ctrl[2] = 0;
n->inv[0] =
n->inv[1] =
n->inv[2] = 0;
}
else {
n->val[idx] = 0.0f;
n->ctrl[idx] = 0;
n->inv[idx] = 0;
}
break;
case PERIODKEY: case PADPERIOD:
if (n->flag & NUM_NO_FRACTION)
return 0;
if (pbuf) {
bool success;
/* Only copy string until first of this char. */
char *cr = strchr(pbuf, '\r');
char *cn = strchr(pbuf, '\n');
if (cn && cn < cr) cr = cn;
if (cr) *cr = '\0';
switch (n->ctrl[idx]) {
case 0:
case 1:
n->ctrl[idx] = 10;
break;
case -1:
n->ctrl[idx] = -10;
break;
}
break;
case PADMINUS:
if (event->alt)
break;
/* fall-through */
case MINUSKEY:
if (n->flag & NUM_NO_NEGATIVE)
return 0;
success = editstr_insert_at_cursor(n, pbuf, strlen(pbuf));
if (n->ctrl[idx]) {
n->ctrl[idx] *= -1;
n->val[idx] *= -1;
}
else
n->ctrl[idx] = -1;
break;
case PADSLASHKEY: case SLASHKEY:
if (n->flag & NUM_NO_FRACTION)
return 0;
n->inv[idx] = !n->inv[idx];
break;
case TABKEY:
if (idx_max == 0)
return 0;
idx++;
if (idx > idx_max)
idx = 0;
n->idx = idx;
break;
case PAD9: case NINEKEY:
Val += 1.0f;
/* fall-through */
case PAD8: case EIGHTKEY:
Val += 1.0f;
/* fall-through */
case PAD7: case SEVENKEY:
Val += 1.0f;
/* fall-through */
case PAD6: case SIXKEY:
Val += 1.0f;
/* fall-through */
case PAD5: case FIVEKEY:
Val += 1.0f;
/* fall-through */
case PAD4: case FOURKEY:
Val += 1.0f;
/* fall-through */
case PAD3: case THREEKEY:
Val += 1.0f;
/* fall-through */
case PAD2: case TWOKEY:
Val += 1.0f;
/* fall-through */
case PAD1: case ONEKEY:
Val += 1.0f;
/* fall-through */
case PAD0: case ZEROKEY:
if (!n->ctrl[idx])
n->ctrl[idx] = 1;
if (fabsf(n->val[idx]) > 9999999.0f) {
/* pass */
}
else if (n->ctrl[idx] == 1) {
n->val[idx] *= 10;
n->val[idx] += Val;
}
else if (n->ctrl[idx] == -1) {
n->val[idx] *= 10;
n->val[idx] -= Val;
}
else {
/* float resolution breaks when over six digits after comma */
if (ABS(n->ctrl[idx]) < 10000000) {
n->val[idx] += Val / (float)n->ctrl[idx];
n->ctrl[idx] *= 10;
MEM_freeN(pbuf);
if (!success) {
return false;
}
n->val_flag[idx] |= NUM_EDITED;
}
updated = true;
break;
default:
return 0;
}
}
/* fall-through */
default:
utf8_buf = event->utf8_buf;
ascii[0] = event->ascii;
break;
}
if (utf8_buf && !utf8_buf[0] && ascii[0]) {
/* Fallback to ascii. */
utf8_buf = ascii;
}
if (utf8_buf && utf8_buf[0]) {
if (!editstr_insert_at_cursor(n, utf8_buf, BLI_str_utf8_size(utf8_buf))) {
return false;
}
n->val_flag[idx] |= NUM_EDITED;
}
else if (!updated) {
return false;
}
/* At this point, our value has changed, try to interpret it with python (if str is not empty!). */
if (n->str[0]) {
#ifdef WITH_PYTHON
char str_unit_convert[NUM_STR_REP_LEN * 6]; /* Should be more than enough! */
const char *default_unit = NULL;
/* Make radian default unit when needed. */
if (n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION)
default_unit = "r";
BLI_strncpy(str_unit_convert, n->str, sizeof(str_unit_convert));
bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, 1.0,
n->unit_sys, n->unit_type[idx]);
/* Note: with angles, we always get values as radians here... */
if (BPY_button_exec(C, str_unit_convert, &val, false) != -1) {
n->val[idx] = (float)val;
n->val_flag[idx] &= ~NUM_INVALID;
}
else {
n->val_flag[idx] |= NUM_INVALID;
}
#else /* Very unlikely, but does not harm... */
n->val[idx] = (float)atof(n->str);
#endif /* WITH_PYTHON */
}
// printf("%f\n", n->val[idx]);
/* REDRAW SINCE NUMBERS HAVE CHANGED */
return 1;
return true;
}