VFONT: Text Selection Operator #106915
|
@ -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)),
|
||||
|
|
|
@ -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=(),
|
||||
Harley marked this conversation as resolved
Outdated
|
||||
)
|
||||
|
||||
|
||||
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,
|
||||
|
|
|
@ -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]);
|
||||
Harley marked this conversation as resolved
Outdated
Campbell Barton
commented
Naming could still be clearer, both input and output are cursors and both can have offsets. The term Suggest: Naming could still be clearer, both input and output are cursors and both can have offsets.
The term `string` could be text renamed to `text`, as this is what the edit-font member is called.
Suggest: `BKE_vfont_cursor_to_text_index` to make it clear the output is an index in an array (any coordinate space can have an offset).
|
||||
|
||||
/**
|
||||
* \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;
|
||||
|
||||
/** \} */
|
||||
Harley marked this conversation as resolved
Outdated
Campbell Barton
commented
picky Prefer doxy-comments ( *picky* Prefer doxy-comments (`/** ... */` for struct members too), write in full sentences.
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
Harley marked this conversation as resolved
Outdated
Campbell Barton
commented
Note what coordinate space this is in. Note what coordinate space this is in.
Campbell Barton
commented
Note what coordinate space this is in. Note what coordinate space this is in.
|
||||
/** \name VFont Mouse Cursor to Text Offset
|
||||
*
|
||||
Harley marked this conversation as resolved
Outdated
Campbell Barton
commented
Note what the offset is applied to ( Note what the offset is applied to (`EditFont::textbuf` , typically applied to `EditFont::pos` & selection range).
|
||||
* 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
Campbell Barton
commented
Missed a closing 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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1796,6 +1796,143 @@ void FONT_OT_text_insert(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
Harley marked this conversation as resolved
Outdated
Campbell Barton
commented
This can return the index from This can return the index from `BKE_vfont_cursor_to_text_index` as the caller doesn't need to know the intermediate value.
|
||||
/** \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];
|
||||
Harley marked this conversation as resolved
Outdated
Campbell Barton
commented
A utility function would be clearer, A utility function would be clearer, `font_cursor_text_index_from_event(...)` for e.g. to split out the plane-projection from the text offset logic.
|
||||
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
Campbell Barton
commented
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));
|
||||
Harley marked this conversation as resolved
Outdated
Campbell Barton
commented
Early exit in the case input motion sets the cursor motion to the same position it was set to previously (avoids excessive updates for small mouse-motions). Early exit in the case input motion sets the cursor motion to the same position it was set to previously (avoids excessive updates for small mouse-motions).
Campbell Barton
commented
picky can set a *picky* can set a `const int` here.
|
||||
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
Campbell Barton
commented
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
|
||||
* \{ */
|
||||
|
|
|
@ -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:
|
||||
|
|
This should be left an empty tuple for the name to be generated (as is done with other tools).