VFONT: Text Selection Operator
An operator to allow interactive text selection for 3D Text Objects. This is from the code of Yash Dabhade (yashdabhade) for GSoC 2022 with corrections and simplifications. Also includes double-click for word selection. Pull Request: https://projects.blender.org/blender/blender/pulls/106915
This commit is contained in:
parent
29a4903eb8
commit
68f8253c71
|
@ -6848,6 +6848,17 @@ def km_3d_view_tool_cursor(params):
|
|||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_text_select(params):
|
||||
return (
|
||||
"3D View Tool: Edit Text, Select Text",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("font.selection_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
|
||||
("font.select_word", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_select(params, *, fallback):
|
||||
if params.use_tweak_select_passthrough:
|
||||
operator_props = (("vert_without_handles", True),)
|
||||
|
@ -8264,6 +8275,7 @@ def generate_keymaps(params=None):
|
|||
*(km_node_editor_tool_select_circle(params, fallback=fallback) for fallback in (False, True)),
|
||||
km_node_editor_tool_links_cut(params),
|
||||
km_3d_view_tool_cursor(params),
|
||||
km_3d_view_tool_text_select(params),
|
||||
*(km_3d_view_tool_select(params, fallback=fallback) for fallback in (False, True)),
|
||||
*(km_3d_view_tool_select_box(params, fallback=fallback) for fallback in (False, True)),
|
||||
*(km_3d_view_tool_select_circle(params, fallback=fallback) for fallback in (False, True)),
|
||||
|
|
|
@ -1284,6 +1284,20 @@ class _defs_edit_curve:
|
|||
)
|
||||
|
||||
|
||||
class _defs_edit_text:
|
||||
|
||||
@ToolDef.from_fn
|
||||
def select_text():
|
||||
return dict(
|
||||
idname="builtin.select_text",
|
||||
label="Select Text",
|
||||
cursor='TEXT',
|
||||
icon="ops.generic.select_box",
|
||||
widget=None,
|
||||
keymap=(),
|
||||
)
|
||||
|
||||
|
||||
class _defs_pose:
|
||||
|
||||
@ToolDef.from_fn
|
||||
|
@ -2980,6 +2994,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
_defs_transform.shear,
|
||||
],
|
||||
'EDIT_TEXT': [
|
||||
_defs_edit_text.select_text,
|
||||
_defs_view3d_generic.cursor,
|
||||
None,
|
||||
*_tools_annotate,
|
||||
|
|
|
@ -89,6 +89,9 @@ bool BKE_vfont_to_curve_ex(struct Object *ob,
|
|||
bool *r_text_free,
|
||||
struct CharTrans **r_chartransdata);
|
||||
bool BKE_vfont_to_curve_nubase(struct Object *ob, int mode, struct ListBase *r_nubase);
|
||||
|
||||
int BKE_vfont_cursor_to_text_index(struct Object *ob, float cursor_location[2]);
|
||||
|
||||
/**
|
||||
* \warning Expects to have access to evaluated data (i.e. passed object should be evaluated one).
|
||||
*/
|
||||
|
|
|
@ -734,6 +734,25 @@ typedef struct VFontToCurveIter {
|
|||
int status;
|
||||
} VFontToCurveIter;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VFont Mouse Cursor to Text Offset
|
||||
*
|
||||
* This is an optional argument to `vfont_to_curve` for getting the text
|
||||
* offset into the string at a mouse cursor location. Used for getting
|
||||
* text cursor (caret) position or selection range.
|
||||
* \{ */
|
||||
/* Used when translating a mouse cursor location to a position within the string. */
|
||||
typedef struct VFontCursor_Params {
|
||||
/* Mouse cursor location in Object coordinate space as input. */
|
||||
float cursor_location[2];
|
||||
/* Character position within EditFont::textbuf as output. */
|
||||
int r_string_offset;
|
||||
} VFontCursor_Params;
|
||||
|
||||
/** \} */
|
||||
|
||||
enum {
|
||||
VFONT_TO_CURVE_INIT = 0,
|
||||
VFONT_TO_CURVE_BISECT,
|
||||
|
@ -774,6 +793,7 @@ static bool vfont_to_curve(Object *ob,
|
|||
Curve *cu,
|
||||
int mode,
|
||||
VFontToCurveIter *iter_data,
|
||||
struct VFontCursor_Params *cursor_params,
|
||||
ListBase *r_nubase,
|
||||
const char32_t **r_text,
|
||||
int *r_text_len,
|
||||
|
@ -1441,6 +1461,35 @@ static bool vfont_to_curve(Object *ob,
|
|||
}
|
||||
}
|
||||
|
||||
if (cursor_params) {
|
||||
cursor_params->r_string_offset = -1;
|
||||
for (i = 0; i <= slen; i++, ct++) {
|
||||
info = &custrinfo[i];
|
||||
ascii = mem[i];
|
||||
if (info->flag & CU_CHINFO_SMALLCAPS_CHECK) {
|
||||
ascii = towupper(ascii);
|
||||
}
|
||||
ct = &chartransdata[i];
|
||||
che = find_vfont_char(vfd, ascii);
|
||||
float charwidth = char_width(cu, che, info);
|
||||
float charhalf = (charwidth / 2.0f);
|
||||
if (cursor_params->cursor_location[1] >= ct->yof - (0.25f * linedist) &&
|
||||
cursor_params->cursor_location[1] <= (ct->yof + (0.75f * linedist))) {
|
||||
/* On this row. */
|
||||
if (cursor_params->cursor_location[0] >= (ct->xof) &&
|
||||
cursor_params->cursor_location[0] <= (ct->xof + charhalf)) {
|
||||
/* Left half of character. */
|
||||
cursor_params->r_string_offset = i;
|
||||
}
|
||||
else if (cursor_params->cursor_location[0] >= (ct->xof + charhalf) &&
|
||||
cursor_params->cursor_location[0] <= (ct->xof + charwidth)) {
|
||||
/* Right half of character. */
|
||||
cursor_params->r_string_offset = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN) &&
|
||||
iter_data->status == VFONT_TO_CURVE_INIT) {
|
||||
ct = &chartransdata[ef->pos];
|
||||
|
@ -1738,13 +1787,49 @@ bool BKE_vfont_to_curve_ex(Object *ob,
|
|||
};
|
||||
|
||||
do {
|
||||
data.ok &= vfont_to_curve(
|
||||
ob, cu, mode, &data, r_nubase, r_text, r_text_len, r_text_free, r_chartransdata);
|
||||
data.ok &= vfont_to_curve(ob,
|
||||
cu,
|
||||
mode,
|
||||
&data,
|
||||
NULL,
|
||||
r_nubase,
|
||||
r_text,
|
||||
r_text_len,
|
||||
r_text_free,
|
||||
r_chartransdata);
|
||||
} while (data.ok && ELEM(data.status, VFONT_TO_CURVE_SCALE_ONCE, VFONT_TO_CURVE_BISECT));
|
||||
|
||||
return data.ok;
|
||||
}
|
||||
|
||||
int BKE_vfont_cursor_to_text_index(Object *ob, float cursor_location[2])
|
||||
{
|
||||
Curve *cu = (Curve *)ob->data;
|
||||
ListBase *r_nubase = &cu->nurb;
|
||||
|
||||
/* TODO: iterating to calculate the scale can be avoided. */
|
||||
|
||||
VFontToCurveIter data = {
|
||||
.iteraction = cu->totbox * FONT_TO_CURVE_SCALE_ITERATIONS,
|
||||
.scale_to_fit = 1.0f,
|
||||
.word_wrap = true,
|
||||
.ok = true,
|
||||
.status = VFONT_TO_CURVE_INIT,
|
||||
};
|
||||
|
||||
VFontCursor_Params cursor_params = {
|
||||
.cursor_location = {cursor_location[0], cursor_location[1]},
|
||||
.r_string_offset = -1,
|
||||
};
|
||||
|
||||
do {
|
||||
data.ok &= vfont_to_curve(
|
||||
ob, cu, FO_CURS, &data, &cursor_params, r_nubase, NULL, NULL, NULL, NULL);
|
||||
} while (data.ok && ELEM(data.status, VFONT_TO_CURVE_SCALE_ONCE, VFONT_TO_CURVE_BISECT));
|
||||
|
||||
return cursor_params.r_string_offset;
|
||||
}
|
||||
|
||||
#undef FONT_TO_CURVE_SCALE_ITERATIONS
|
||||
#undef FONT_TO_CURVE_SCALE_THRESHOLD
|
||||
|
||||
|
|
|
@ -86,6 +86,9 @@ void FONT_OT_text_cut(struct wmOperatorType *ot);
|
|||
void FONT_OT_text_paste(struct wmOperatorType *ot);
|
||||
void FONT_OT_text_paste_from_file(struct wmOperatorType *ot);
|
||||
|
||||
void FONT_OT_selection_set(struct wmOperatorType *ot);
|
||||
void FONT_OT_select_word(struct wmOperatorType *ot);
|
||||
|
||||
void FONT_OT_move(struct wmOperatorType *ot);
|
||||
void FONT_OT_move_select(struct wmOperatorType *ot);
|
||||
void FONT_OT_delete(struct wmOperatorType *ot);
|
||||
|
|
|
@ -40,6 +40,9 @@ void ED_operatortypes_curve(void)
|
|||
WM_operatortype_append(FONT_OT_text_paste);
|
||||
WM_operatortype_append(FONT_OT_text_paste_from_file);
|
||||
|
||||
WM_operatortype_append(FONT_OT_selection_set);
|
||||
WM_operatortype_append(FONT_OT_select_word);
|
||||
|
||||
WM_operatortype_append(FONT_OT_move);
|
||||
WM_operatortype_append(FONT_OT_move_select);
|
||||
WM_operatortype_append(FONT_OT_delete);
|
||||
|
|
|
@ -1805,6 +1805,143 @@ void FONT_OT_text_insert(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Font Selection Operator
|
||||
* \{ */
|
||||
|
||||
static int font_cursor_text_index_from_event(bContext *C, Object *obedit, const wmEvent *event)
|
||||
{
|
||||
Curve *cu = obedit->data;
|
||||
EditFont *ef = cu->editfont;
|
||||
|
||||
/* Calculate a plane from the text object's orientation. */
|
||||
float plane[4];
|
||||
plane_from_point_normal_v3(plane, obedit->object_to_world[3], obedit->object_to_world[2]);
|
||||
|
||||
/* Convert Mouse location in region to 3D location in world space. */
|
||||
float mal_fl[2] = {(float)event->mval[0], (float)event->mval[1]};
|
||||
float mouse_loc[3];
|
||||
ED_view3d_win_to_3d_on_plane(CTX_wm_region(C), plane, mal_fl, true, mouse_loc);
|
||||
|
||||
/* Convert to object space and scale by font size. */
|
||||
mul_m4_v3(obedit->world_to_object, mouse_loc);
|
||||
|
||||
float curs_loc[2] = {mouse_loc[0] / cu->fsize, mouse_loc[1] / cu->fsize};
|
||||
return BKE_vfont_cursor_to_text_index(obedit, curs_loc);
|
||||
}
|
||||
|
||||
static void font_cursor_set_apply(bContext *C, const wmEvent *event)
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
Object *ob = DEG_get_evaluated_object(depsgraph, CTX_data_active_object(C));
|
||||
Curve *cu = ob->data;
|
||||
EditFont *ef = cu->editfont;
|
||||
BLI_assert(ef->len >= 0);
|
||||
|
||||
const int string_offset = font_cursor_text_index_from_event(C, ob, event);
|
||||
|
||||
if (string_offset > ef->len || string_offset < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0];
|
||||
|
||||
if (ob->totcol > 0) {
|
||||
ob->actcol = cu->curinfo.mat_nr;
|
||||
if (ob->actcol < 1) {
|
||||
ob->actcol = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ef->selboxes && (ef->selstart == 0)) {
|
||||
if (ef->pos == 0) {
|
||||
ef->selstart = ef->selend = 1;
|
||||
}
|
||||
else {
|
||||
ef->selstart = ef->selend = string_offset + 1;
|
||||
}
|
||||
}
|
||||
ef->selend = string_offset;
|
||||
ef->pos = string_offset;
|
||||
|
||||
DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
||||
}
|
||||
|
||||
static int font_selection_set_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
Object *obedit = CTX_data_active_object(C);
|
||||
Curve *cu = obedit->data;
|
||||
EditFont *ef = cu->editfont;
|
||||
|
||||
font_cursor_set_apply(C, event);
|
||||
ef->selstart = 0;
|
||||
ef->selend = 0;
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int font_selection_set_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
|
||||
{
|
||||
switch (event->type) {
|
||||
case LEFTMOUSE:
|
||||
if (event->val == KM_RELEASE) {
|
||||
font_cursor_set_apply(C, event);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
break;
|
||||
case MIDDLEMOUSE:
|
||||
case RIGHTMOUSE:
|
||||
return OPERATOR_FINISHED;
|
||||
case MOUSEMOVE:
|
||||
font_cursor_set_apply(C, event);
|
||||
break;
|
||||
}
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
void FONT_OT_selection_set(struct wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Set Selection";
|
||||
ot->idname = "FONT_OT_selection_set";
|
||||
ot->description = "Set cursor selection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = font_selection_set_invoke;
|
||||
ot->modal = font_selection_set_modal;
|
||||
ot->poll = ED_operator_editfont;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Select Word Operator
|
||||
* \{ */
|
||||
|
||||
static int font_select_word_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
move_cursor(C, NEXT_CHAR, false);
|
||||
move_cursor(C, PREV_WORD, false);
|
||||
move_cursor(C, NEXT_WORD, true);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void FONT_OT_select_word(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select Word";
|
||||
ot->idname = "FONT_OT_select_word";
|
||||
ot->description = "Select word under cursor";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = font_select_word_exec;
|
||||
ot->poll = ED_operator_editfont;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Text-Box Add Operator
|
||||
* \{ */
|
||||
|
|
|
@ -729,7 +729,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
|
|||
case CTX_MODE_PARTICLE:
|
||||
return "builtin_brush.Comb";
|
||||
case CTX_MODE_EDIT_TEXT:
|
||||
return "builtin.cursor";
|
||||
return "builtin.select_text";
|
||||
}
|
||||
break;
|
||||
case SPACE_IMAGE:
|
||||
|
|
Loading…
Reference in New Issue