VFONT: Text Selection Operator #106915

Merged
Harley Acheson merged 12 commits from Harley/blender:TextObjectSelection into main 2023-04-21 19:08:58 +02:00
8 changed files with 261 additions and 3 deletions

View File

@ -6780,6 +6780,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),)
@ -8193,6 +8204,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

@ -1259,6 +1259,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
@ -3028,6 +3042,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

@ -72,6 +72,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.
Harley marked this conversation as resolved
Review

Missed a closing /** \} */ comment - both before and when the group ends.

Missed a closing `/** \} */` comment - both before and when the group ends.
* \{ */
/* 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,
@ -1433,6 +1453,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];
@ -1730,13 +1779,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

@ -84,6 +84,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

@ -1796,6 +1796,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);
}
Harley marked this conversation as resolved
Review

The group can enclose the utility function (doesn't seem needed to define a new group).

The group can enclose the utility function (doesn't seem needed to define a new group).
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;
}
Harley marked this conversation as resolved
Review

May as well check value is release in this case.

May as well check value is release in this case.
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: