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=(),
|
||||
)
|
||||
|
||||
|
||||
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]);
|
||||
|
||||
/**
|
||||
* \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.
|
||||
Harley marked this conversation as resolved
|
||||
* \{ */
|
||||
/* 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)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \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
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));
|
||||
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:
|
||||
|
|
Loading…
Reference in New Issue
Missed a closing
/** \} */
comment - both before and when the group ends.