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:
Harley Acheson 2023-04-21 19:08:44 +02:00 committed by Harley Acheson
parent 29a4903eb8
commit 68f8253c71
8 changed files with 261 additions and 3 deletions

View File

@ -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)),

View File

@ -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,

View File

@ -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).
*/

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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
* \{ */

View File

@ -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: