forked from enlightenment/efl
912 lines
26 KiB
C
912 lines
26 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include "elementary_config.h"
|
|
#endif
|
|
|
|
#define ELM_LAYOUT_PROTECTED
|
|
#define EFL_PART_PROTECTED
|
|
|
|
#include <Elementary.h>
|
|
#include "elm_priv.h"
|
|
|
|
#include "elm_widget_layout.h"
|
|
#include "efl_ui_textpath_part.eo.h"
|
|
#include "elm_part_helper.h"
|
|
|
|
|
|
#define MY_CLASS EFL_UI_TEXTPATH_CLASS
|
|
#define MY_CLASS_NAME "Efl.Ui.Textpath"
|
|
|
|
#define SLICE_DEFAULT_NO 99
|
|
typedef struct _Efl_Ui_Textpath_Point Efl_Ui_Textpath_Point;
|
|
typedef struct _Efl_Ui_Textpath_Line Efl_Ui_Textpath_Line;
|
|
typedef struct _Efl_Ui_Textpath_Segment Efl_Ui_Textpath_Segment;
|
|
typedef struct _Efl_Ui_Textpath_Data Efl_Ui_Textpath_Data;
|
|
|
|
struct _Efl_Ui_Textpath_Point
|
|
{
|
|
double x;
|
|
double y;
|
|
};
|
|
|
|
struct _Efl_Ui_Textpath_Line
|
|
{
|
|
Efl_Ui_Textpath_Point start;
|
|
Efl_Ui_Textpath_Point end;
|
|
};
|
|
|
|
struct _Efl_Ui_Textpath_Segment
|
|
{
|
|
EINA_INLIST;
|
|
int length;
|
|
Efl_Gfx_Path_Command_Type type;
|
|
union
|
|
{
|
|
Eina_Bezier bezier;
|
|
Efl_Ui_Textpath_Line line;
|
|
};
|
|
};
|
|
|
|
/* If you need to draw slices using Evas Line,
|
|
* define the following debug flag manually. */
|
|
//#define EFL_UI_TEXTPATH_LINE_DEBUG
|
|
|
|
struct _Efl_Ui_Textpath_Data
|
|
{
|
|
Evas_Object *text_obj;
|
|
char *text;
|
|
Efl_Gfx_Path *path;
|
|
struct {
|
|
double x, y;
|
|
double radius;
|
|
double start_angle;
|
|
} circle;
|
|
Efl_Ui_Textpath_Direction direction;
|
|
int slice_no;
|
|
Eina_Bool ellipsis;
|
|
|
|
Eina_Inlist *segments;
|
|
int total_length;
|
|
Ecore_Job *draw_text_job;
|
|
#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
|
|
Eina_List *lines;
|
|
#endif
|
|
};
|
|
|
|
#define EFL_UI_TEXTPATH_DATA_GET(o, sd) \
|
|
Efl_Ui_Textpath_Data *sd = efl_data_scope_get(o, EFL_UI_TEXTPATH_CLASS)
|
|
|
|
static inline double
|
|
_rad_to_deg(double rad)
|
|
{
|
|
return 180 * rad / M_PI;
|
|
}
|
|
|
|
static inline double
|
|
_deg_to_rad(double angle)
|
|
{
|
|
return angle / 180 * M_PI;
|
|
}
|
|
|
|
static void
|
|
_segment_draw(Efl_Ui_Textpath_Data *pd, int slice_no, double dt, double dist,
|
|
int w1, int cmp, Evas_Map *map, Eina_Bezier bezier,
|
|
int *last_x1, int *last_y1, int *last_x2, int *last_y2)
|
|
{
|
|
int i;
|
|
double u0, u1, v0, v1;
|
|
double t;
|
|
double px, py, px2, py2;
|
|
double rad;
|
|
Eina_Rect r;
|
|
Eina_Vector2 vec, nvec, vec0, vec1, vec2, vec3;
|
|
Eina_Matrix2 mat;
|
|
#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
|
|
static Eina_Bool yello_color_flag = EINA_FALSE;
|
|
yello_color_flag = !yello_color_flag;
|
|
#endif
|
|
|
|
r = efl_gfx_entity_geometry_get(pd->text_obj);
|
|
|
|
rad = _deg_to_rad(90);
|
|
eina_matrix2_values_set(&mat, cos(rad), -sin(rad), sin(rad), cos(rad));
|
|
|
|
eina_bezier_values_get(&bezier, NULL, NULL, NULL, NULL, NULL, NULL, &px2, &py2);
|
|
t = 0;
|
|
eina_bezier_point_at(&bezier, t, &px, &py);
|
|
eina_bezier_point_at(&bezier, t + dt, &px2, &py2);
|
|
|
|
vec.x = (px2 - px);
|
|
vec.y = (py2 - py);
|
|
eina_vector2_normalize(&nvec, &vec);
|
|
|
|
eina_vector2_transform(&vec, &mat, &nvec);
|
|
eina_vector2_normalize(&nvec, &vec);
|
|
eina_vector2_scale(&vec, &nvec, ((double) r.h) * 0.5);
|
|
|
|
vec1.x = (vec.x + px);
|
|
vec1.y = (vec.y + py);
|
|
vec2.x = (-vec.x + px);
|
|
vec2.y = (-vec.y + py);
|
|
|
|
if (cmp == 0)
|
|
{
|
|
*last_x1 = (int) round(vec1.x + r.x);
|
|
*last_y1 = (int) round(vec1.y + r.y);
|
|
*last_x2 = (int) round(vec2.x + r.x);
|
|
*last_y2 = (int) round(vec2.y + r.y);
|
|
}
|
|
|
|
//add points to map
|
|
for (i = 0; i < slice_no; i++)
|
|
{
|
|
int mp0_x, mp0_y;
|
|
int mp1_x, mp1_y;
|
|
int mp2_x, mp2_y;
|
|
int mp3_x, mp3_y;
|
|
double next_dt = dt;
|
|
|
|
//v0, v3
|
|
vec0.x = vec1.x;
|
|
vec0.y = vec1.y;
|
|
vec3.x = vec2.x;
|
|
vec3.y = vec2.y;
|
|
|
|
//UV
|
|
u0 = w1 + i * dist;
|
|
u1 = u0 + dist;
|
|
if (u1 > r.w)
|
|
u1 = r.w;
|
|
v0 = (double) 0;
|
|
v1 = (double) r.h;
|
|
|
|
/* If u1 is modified not to exceed its end,
|
|
* modify next_dt according to changes of dist. */
|
|
if (u1 < u0 + dist)
|
|
next_dt = dt * (u1 - u0) / dist;
|
|
|
|
//v1, v2
|
|
t = (double) (i * dt) + next_dt;
|
|
eina_bezier_point_at(&bezier, t, &px, &py);
|
|
eina_bezier_point_at(&bezier, t + next_dt, &px2, &py2);
|
|
|
|
vec.x = (px2 - px);
|
|
vec.y = (py2 - py);
|
|
eina_vector2_normalize(&nvec, &vec);
|
|
eina_vector2_transform(&vec, &mat, &nvec);
|
|
eina_vector2_normalize(&nvec, &vec);
|
|
eina_vector2_scale(&vec, &nvec, ((double) r.h) * 0.5);
|
|
|
|
vec1.x = (vec.x + px);
|
|
vec1.y = (vec.y + py);
|
|
vec2.x = (-vec.x + px);
|
|
vec2.y = (-vec.y + py);
|
|
|
|
/* Set mp1, mp2 position according to difference between
|
|
* previous points and next points.
|
|
* It improves smoothness of curve's slope changing.
|
|
* But, it can cause huge differeces from actual positions. */
|
|
mp0_x = *last_x1;
|
|
mp0_y = *last_y1;
|
|
mp1_x = *last_x1 + (int) round(vec1.x - vec0.x);
|
|
mp1_y = *last_y1 + (int) round(vec1.y - vec0.y);
|
|
mp2_x = *last_x2 + (int) round(vec2.x - vec3.x);
|
|
mp2_y = *last_y2 + (int) round(vec2.y - vec3.y);
|
|
mp3_x = *last_x2;
|
|
mp3_y = *last_y2;
|
|
|
|
/* It reduces differences between actual position and modified position. */
|
|
mp1_x += (int)round(((double)vec1.x - mp1_x) / 2);
|
|
mp1_y += (int)round(((double)vec1.y - mp1_y) / 2);
|
|
mp2_x += (int)round(((double)vec2.x - mp2_x) / 2);
|
|
mp2_y += (int)round(((double)vec2.y - mp2_y) / 2);
|
|
|
|
evas_map_point_coord_set(map, cmp + i * 4, mp0_x, mp0_y, 0);
|
|
evas_map_point_coord_set(map, cmp + i * 4 + 1, mp1_x, mp1_y, 0);
|
|
evas_map_point_coord_set(map, cmp + i * 4 + 2, mp2_x, mp2_y, 0);
|
|
evas_map_point_coord_set(map, cmp + i * 4 + 3, mp3_x, mp3_y, 0);
|
|
|
|
evas_map_point_image_uv_set(map, cmp + i * 4, u0, v0);
|
|
evas_map_point_image_uv_set(map, cmp + i * 4 + 1, u1, v0);
|
|
evas_map_point_image_uv_set(map, cmp + i * 4 + 2, u1, v1);
|
|
evas_map_point_image_uv_set(map, cmp + i * 4 + 3, u0, v1);
|
|
|
|
*last_x1 = mp1_x;
|
|
*last_y1 = mp1_y;
|
|
*last_x2 = mp2_x;
|
|
*last_y2 = mp2_y;
|
|
|
|
#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
|
|
Evas_Object *line = evas_object_line_add(evas_object_evas_get(pd->text_obj));
|
|
pd->lines = eina_list_append(pd->lines, line);
|
|
if (yello_color_flag)
|
|
evas_object_color_set(line, 255, 255, 0, 255);
|
|
else
|
|
evas_object_color_set(line, 255, 0, 0, 255);
|
|
evas_object_line_xy_set(line,
|
|
mp0_x, mp0_y,
|
|
mp1_x, mp1_y);
|
|
evas_object_show(line);
|
|
|
|
line = evas_object_line_add(evas_object_evas_get(pd->text_obj));
|
|
pd->lines = eina_list_append(pd->lines, line);
|
|
if (yello_color_flag)
|
|
evas_object_color_set(line, 255, 255, 0, 255);
|
|
else
|
|
evas_object_color_set(line, 255, 0, 0, 255);
|
|
evas_object_line_xy_set(line,
|
|
mp1_x, mp1_y,
|
|
mp2_x, mp2_y);
|
|
evas_object_show(line);
|
|
|
|
line = evas_object_line_add(evas_object_evas_get(pd->text_obj));
|
|
pd->lines = eina_list_append(pd->lines, line);
|
|
if (yello_color_flag)
|
|
evas_object_color_set(line, 255, 255, 0, 255);
|
|
else
|
|
evas_object_color_set(line, 255, 0, 0, 255);
|
|
evas_object_line_xy_set(line,
|
|
mp2_x, mp2_y,
|
|
mp3_x, mp3_y);
|
|
evas_object_show(line);
|
|
|
|
line = evas_object_line_add(evas_object_evas_get(pd->text_obj));
|
|
pd->lines = eina_list_append(pd->lines, line);
|
|
if (yello_color_flag)
|
|
evas_object_color_set(line, 255, 255, 0, 255);
|
|
else
|
|
evas_object_color_set(line, 255, 0, 0, 255);
|
|
evas_object_line_xy_set(line,
|
|
mp3_x, mp3_y,
|
|
mp0_x, mp0_y);
|
|
evas_object_show(line);
|
|
#endif
|
|
|
|
if (u1 >= r.w) break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_text_on_line_draw(Efl_Ui_Textpath_Data *pd, int w1, int w2, int cmp, Evas_Map *map, Efl_Ui_Textpath_Line line)
|
|
{
|
|
double x1, x2, y1, y2;
|
|
double line_len, len, sina, cosa;
|
|
Eina_Rect r;
|
|
|
|
x1 = line.start.x;
|
|
y1 = line.start.y;
|
|
x2 = line.end.x;
|
|
y2 = line.end.y;
|
|
|
|
line_len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
|
|
len = w2 - w1;
|
|
if (line_len > len)
|
|
{
|
|
x2 = x1 + len * (x2 - x1) / line_len;
|
|
y2 = y1 + len * (y2 - y1) / line_len;
|
|
}
|
|
|
|
len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
|
|
sina = (y2 - y1) / len;
|
|
cosa = (x2 - x1) / len;
|
|
|
|
r = efl_gfx_entity_geometry_get(pd->text_obj);
|
|
r.h /= 2;
|
|
evas_map_point_coord_set(map, cmp + 3, x1 - r.h * sina, y1 + r.h * cosa, 0);
|
|
evas_map_point_coord_set(map, cmp + 2, x2 - r.h * sina, y2 + r.h * cosa, 0);
|
|
evas_map_point_coord_set(map, cmp + 1, x2 + r.h * sina, y2 - r.h * cosa, 0);
|
|
evas_map_point_coord_set(map, cmp + 0, x1 + r.h * sina, y1 - r.h * cosa, 0);
|
|
|
|
r.h *= 2;
|
|
evas_map_point_image_uv_set(map, cmp + 0, w1, 0);
|
|
evas_map_point_image_uv_set(map, cmp + 1, w2, 0);
|
|
evas_map_point_image_uv_set(map, cmp + 2, w2, r.h);
|
|
evas_map_point_image_uv_set(map, cmp + 3, w1, r.h);
|
|
}
|
|
|
|
static int
|
|
_map_point_calc(Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
int map_no = 0;
|
|
Efl_Ui_Textpath_Segment *seg;
|
|
|
|
EINA_INLIST_FOREACH(pd->segments, seg)
|
|
{
|
|
if (seg->type == EFL_GFX_PATH_COMMAND_TYPE_LINE_TO)
|
|
{
|
|
map_no++;
|
|
}
|
|
else if (seg->type == EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO)
|
|
{
|
|
int no = (int)ceil(pd->slice_no * seg->length / (double)pd->total_length);
|
|
if (no == 0) no = 1;
|
|
map_no += no;
|
|
}
|
|
}
|
|
map_no *= 4;
|
|
|
|
return map_no;
|
|
}
|
|
|
|
static void
|
|
_text_draw(void *data)
|
|
{
|
|
Efl_Ui_Textpath_Data *pd = data;
|
|
Efl_Ui_Textpath_Segment *seg;
|
|
Evas_Map *map;
|
|
int w1, w2;
|
|
int remained_w;
|
|
int cur_map_point = 0, map_point_no;
|
|
Eina_Size2D sz;
|
|
int last_x1, last_y1, last_x2, last_y2;
|
|
|
|
last_x1 = last_y1 = last_x2 = last_y2 = 0;
|
|
|
|
sz = efl_gfx_entity_size_get(pd->text_obj);
|
|
remained_w = sz.w;
|
|
|
|
map_point_no = _map_point_calc(pd);
|
|
if (map_point_no == 0)
|
|
{
|
|
evas_object_map_enable_set(pd->text_obj, EINA_FALSE);
|
|
return;
|
|
}
|
|
map = evas_map_new(map_point_no);
|
|
|
|
#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
|
|
Evas_Object *line;
|
|
EINA_LIST_FREE(pd->lines, line)
|
|
evas_object_del(line);
|
|
#endif
|
|
|
|
w1 = w2 = 0;
|
|
EINA_INLIST_FOREACH(pd->segments, seg)
|
|
{
|
|
int len = seg->length;
|
|
if (remained_w <= 0)
|
|
break;
|
|
w2 = w1 + len;
|
|
if (w2 > sz.w)
|
|
w2 = sz.w;
|
|
if (seg->type == EFL_GFX_PATH_COMMAND_TYPE_LINE_TO)
|
|
{
|
|
_text_on_line_draw(pd, w1, w2, cur_map_point, map, seg->line);
|
|
cur_map_point += 4;
|
|
}
|
|
else
|
|
{
|
|
double slice_value, dt, dist;
|
|
int slice_no;
|
|
|
|
slice_value = pd->slice_no * seg->length / (double)pd->total_length;
|
|
dt = (double)pd->total_length / (pd->slice_no * seg->length);
|
|
dist = (double)pd->total_length / (double)pd->slice_no;
|
|
|
|
slice_no = (int)ceil(slice_value);
|
|
dt = (double)slice_value * dt / (double)slice_no;
|
|
dist = (double)slice_value * dist / (double)slice_no;
|
|
|
|
_segment_draw(pd, slice_no, dt, dist,
|
|
w1, cur_map_point, map, seg->bezier,
|
|
&last_x1, &last_y1, &last_x2, &last_y2);
|
|
cur_map_point += slice_no * 4;
|
|
}
|
|
w1 = w2;
|
|
remained_w -= seg->length;
|
|
}
|
|
evas_object_map_enable_set(pd->text_obj, EINA_TRUE);
|
|
evas_object_anti_alias_set(pd->text_obj, EINA_TRUE);
|
|
evas_object_map_set(pd->text_obj, map);
|
|
evas_map_free(map);
|
|
|
|
pd->draw_text_job = NULL;
|
|
}
|
|
|
|
static void
|
|
_path_data_get(Eo *obj, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
const Efl_Gfx_Path_Command_Type *cmd;
|
|
const double *points;
|
|
Efl_Ui_Textpath_Segment *seg;
|
|
Eina_Position2D opos;
|
|
|
|
EINA_INLIST_FREE(pd->segments, seg)
|
|
{
|
|
pd->segments = eina_inlist_remove(pd->segments, EINA_INLIST_GET(seg));
|
|
free(seg);
|
|
}
|
|
|
|
opos = efl_gfx_entity_position_get(obj);
|
|
|
|
pd->total_length = 0;
|
|
efl_gfx_path_get(obj, &cmd, &points);
|
|
if (cmd)
|
|
{
|
|
int pos = -1;
|
|
double px0 = 0.0, py0 = 0.0, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1, px1, py1;
|
|
Eina_Rect rect = EINA_RECT_ZERO();
|
|
|
|
while (*cmd != EFL_GFX_PATH_COMMAND_TYPE_END)
|
|
{
|
|
if (*cmd == EFL_GFX_PATH_COMMAND_TYPE_MOVE_TO)
|
|
{
|
|
pos++;
|
|
px0 = points[pos] + opos.x;
|
|
pos++;
|
|
py0 = points[pos] + opos.y;
|
|
}
|
|
else if (*cmd == EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO)
|
|
{
|
|
Eina_Bezier bz;
|
|
double bx, by, bw, bh;
|
|
Eina_Rect brect;
|
|
|
|
pos++;
|
|
ctrl_x0 = points[pos] + opos.x;
|
|
pos++;
|
|
ctrl_y0 = points[pos] + opos.y;
|
|
pos++;
|
|
ctrl_x1 = points[pos] + opos.x;
|
|
pos++;
|
|
ctrl_y1 = points[pos] + opos.y;
|
|
pos++;
|
|
px1 = points[pos] + opos.x;
|
|
pos++;
|
|
py1 = points[pos] + opos.y;
|
|
|
|
eina_bezier_values_set(&bz, px0, py0, ctrl_x0, ctrl_y0, ctrl_x1, ctrl_y1, px1, py1);
|
|
seg = malloc(sizeof(Efl_Ui_Textpath_Segment));
|
|
if (!seg)
|
|
{
|
|
ERR("Failed to allocate segment");
|
|
px0 = px1;
|
|
py0 = py1;
|
|
continue;
|
|
}
|
|
seg->length = eina_bezier_length_get(&bz);
|
|
seg->bezier = bz;
|
|
seg->type = EFL_GFX_PATH_COMMAND_TYPE_CUBIC_TO;
|
|
pd->segments = eina_inlist_append(pd->segments, EINA_INLIST_GET(seg));
|
|
pd->total_length += seg->length;
|
|
|
|
//move points
|
|
px0 = px1;
|
|
py0 = py1;
|
|
|
|
eina_bezier_bounds_get(&bz, &bx, &by, &bw, &bh);
|
|
brect = EINA_RECT(bx, by, bw, bh);
|
|
eina_rectangle_union(&rect.rect, &brect.rect);
|
|
}
|
|
else if (*cmd == EFL_GFX_PATH_COMMAND_TYPE_LINE_TO)
|
|
{
|
|
Eina_Rect lrect;
|
|
|
|
pos++;
|
|
px1 = points[pos] + opos.x;
|
|
pos++;
|
|
py1 = points[pos] + opos.y;
|
|
|
|
seg = malloc(sizeof(Efl_Ui_Textpath_Segment));
|
|
if (!seg)
|
|
{
|
|
ERR("Failed to allocate segment");
|
|
px0 = px1;
|
|
py0 = py1;
|
|
continue;
|
|
}
|
|
seg->type = EFL_GFX_PATH_COMMAND_TYPE_LINE_TO;
|
|
seg->line.start.x = px0;
|
|
seg->line.start.y = py0;
|
|
seg->line.end.x = px1;
|
|
seg->line.end.y = py1;
|
|
seg->length = sqrt((px1 - px0)*(px1 - px0) + (py1 - py0)*(py1 - py0));
|
|
pd->segments = eina_inlist_append(pd->segments, EINA_INLIST_GET(seg));
|
|
pd->total_length += seg->length;
|
|
|
|
lrect = EINA_RECT(px0, py0, px1 - px0, py1 - py0);
|
|
eina_rectangle_union(&rect.rect, &lrect.rect);
|
|
}
|
|
cmd++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_sizing_eval(Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
ecore_job_del(pd->draw_text_job);
|
|
pd->draw_text_job = ecore_job_add(_text_draw, pd);
|
|
}
|
|
|
|
static void
|
|
_textpath_ellipsis_set(Efl_Ui_Textpath_Data *pd, Eina_Bool enabled)
|
|
{
|
|
edje_object_part_text_style_user_pop(pd->text_obj, "efl.text");
|
|
|
|
if (enabled)
|
|
edje_object_part_text_style_user_push(pd->text_obj, "efl.text",
|
|
"DEFAULT='ellipsis=1.0'");
|
|
}
|
|
|
|
static void
|
|
_ellipsis_set(Efl_Ui_Textpath_Data *pd, Eo *obj)
|
|
{
|
|
if (!pd->text_obj) return;
|
|
|
|
Evas_Coord w = 0, h = 0;
|
|
Eina_Bool is_ellipsis = EINA_FALSE;
|
|
const Evas_Object *tb;
|
|
|
|
if (elm_widget_is_legacy(obj))
|
|
tb = edje_object_part_object_get(pd->text_obj, "elm.text");
|
|
else
|
|
tb = edje_object_part_object_get(pd->text_obj, "efl.text");
|
|
|
|
evas_object_textblock_size_native_get(tb, &w, &h);
|
|
evas_object_size_hint_min_set(pd->text_obj, w, h);
|
|
if (pd->ellipsis)
|
|
{
|
|
if (w > pd->total_length)
|
|
{
|
|
is_ellipsis = EINA_TRUE;
|
|
w = pd->total_length;
|
|
}
|
|
}
|
|
efl_gfx_entity_size_set(pd->text_obj, EINA_SIZE2D(w, h));
|
|
_textpath_ellipsis_set(pd, is_ellipsis);
|
|
}
|
|
|
|
static void
|
|
_efl_ui_textpath_efl_gfx_path_commit(Eo *obj, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
_path_data_get(obj, pd);
|
|
_sizing_eval(pd);
|
|
}
|
|
|
|
static void
|
|
_path_start_angle_adjust(Eo *obj, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
Eina_Rect r;
|
|
Efl_Ui_Textpath_Segment *seg;
|
|
Eina_Vector2 first, last;
|
|
int remained_w, len;
|
|
double rad, t, offset_angle;
|
|
|
|
if (pd->direction != EFL_UI_TEXTPATH_DIRECTION_CW_CENTER &&
|
|
pd->direction != EFL_UI_TEXTPATH_DIRECTION_CCW_CENTER)
|
|
return;
|
|
|
|
r = efl_gfx_entity_geometry_get(pd->text_obj);
|
|
remained_w = r.w;
|
|
|
|
EINA_INLIST_FOREACH(pd->segments, seg)
|
|
{
|
|
if (remained_w <= 0)
|
|
break;
|
|
|
|
len = seg->length;
|
|
if (remained_w < len)
|
|
{
|
|
t = remained_w / (double)len;
|
|
eina_bezier_point_at(&seg->bezier, t, &last.x, &last.y);
|
|
}
|
|
|
|
if (remained_w == r.w)
|
|
eina_bezier_point_at(&seg->bezier, 0, &first.x, &first.y);
|
|
|
|
remained_w -= len;
|
|
}
|
|
|
|
first.x -= (pd->circle.x + r.x);
|
|
first.y -= (pd->circle.y + r.y);
|
|
last.x -= (pd->circle.x + r.x);
|
|
last.y -= (pd->circle.y + r.y);
|
|
|
|
eina_vector2_normalize(&first, &first);
|
|
eina_vector2_normalize(&last, &last);
|
|
rad = acos(eina_vector2_dot_product(&first, &last));
|
|
if (rad == 0) return;
|
|
|
|
offset_angle = _rad_to_deg(rad);
|
|
if (r.w > pd->total_length / 2)
|
|
offset_angle = 360 - offset_angle;
|
|
offset_angle /= 2.0;
|
|
|
|
efl_gfx_path_reset(obj);
|
|
if (pd->direction == EFL_UI_TEXTPATH_DIRECTION_CW_CENTER)
|
|
{
|
|
efl_gfx_path_append_arc(obj,
|
|
pd->circle.x - pd->circle.radius,
|
|
pd->circle.y - pd->circle.radius,
|
|
pd->circle.radius * 2,
|
|
pd->circle.radius * 2,
|
|
pd->circle.start_angle + offset_angle,
|
|
-360);
|
|
}
|
|
else
|
|
{
|
|
efl_gfx_path_append_arc(obj,
|
|
pd->circle.x - pd->circle.radius,
|
|
pd->circle.y - pd->circle.radius,
|
|
pd->circle.radius * 2,
|
|
pd->circle.radius * 2,
|
|
pd->circle.start_angle - offset_angle,
|
|
360);
|
|
}
|
|
_path_data_get(obj, pd);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_textpath_text_set_internal(Eo *obj, Efl_Ui_Textpath_Data *pd, const char *part, const char *text)
|
|
{
|
|
ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE);
|
|
Eina_Bool ret = EINA_TRUE;
|
|
|
|
if (!text) text = "";
|
|
ret = edje_object_part_text_set(pd->text_obj, part, text);
|
|
_ellipsis_set(pd, obj);
|
|
|
|
//Only if circlular textpath
|
|
if (pd->circle.radius > 0)
|
|
_path_start_angle_adjust(obj, pd);
|
|
|
|
_sizing_eval(pd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_efl_canvas_group_group_calculate(Eo *obj EINA_UNUSED, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
_sizing_eval(pd);
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Textpath_Data *priv)
|
|
{
|
|
ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
|
|
|
|
efl_canvas_group_add(efl_super(obj, MY_CLASS));
|
|
|
|
priv->text_obj = edje_object_add(evas_object_evas_get(obj));
|
|
elm_widget_theme_object_set(obj, priv->text_obj, "textpath", "base",
|
|
elm_widget_style_get(obj));
|
|
efl_gfx_hint_weight_set(priv->text_obj, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
|
|
efl_gfx_hint_align_set(priv->text_obj, EVAS_HINT_FILL, EVAS_HINT_FILL);
|
|
efl_gfx_entity_visible_set(priv->text_obj, EINA_TRUE);
|
|
|
|
evas_object_smart_member_add(priv->text_obj, obj);
|
|
elm_widget_sub_object_add(obj, priv->text_obj);
|
|
}
|
|
|
|
EOLIAN static Efl_Object *
|
|
_efl_ui_textpath_efl_object_constructor(Eo *obj, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
obj = efl_constructor(efl_super(obj, MY_CLASS));
|
|
pd->slice_no = SLICE_DEFAULT_NO;
|
|
pd->direction = EFL_UI_TEXTPATH_DIRECTION_CW;
|
|
|
|
return obj;
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_efl_object_destructor(Eo *obj, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
Efl_Ui_Textpath_Segment *seg;
|
|
|
|
if (pd->text) free(pd->text);
|
|
if (pd->text_obj) evas_object_del(pd->text_obj);
|
|
EINA_INLIST_FREE(pd->segments, seg)
|
|
{
|
|
pd->segments = eina_inlist_remove(pd->segments, EINA_INLIST_GET(seg));
|
|
free(seg);
|
|
}
|
|
ecore_job_del(pd->draw_text_job);
|
|
|
|
#ifdef EFL_UI_TEXTPATH_LINE_DEBUG
|
|
Evas_Object *line;
|
|
EINA_LIST_FREE(pd->lines, line)
|
|
evas_object_del(line);
|
|
#endif
|
|
|
|
efl_destructor(efl_super(obj, MY_CLASS));
|
|
}
|
|
|
|
EOLIAN static Eina_Bool
|
|
_efl_ui_textpath_text_set(Eo *obj, Efl_Ui_Textpath_Data *pd, const char *part, const char *text)
|
|
{
|
|
return _textpath_text_set_internal(obj, pd, part, text);
|
|
}
|
|
|
|
EOLIAN static const char *
|
|
_efl_ui_textpath_text_get(Eo *obj EINA_UNUSED, Efl_Ui_Textpath_Data *pd, const char *part)
|
|
{
|
|
return edje_object_part_text_get(pd->text_obj, part);
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_efl_text_text_set(Eo *obj, Efl_Ui_Textpath_Data *pd, const char *text)
|
|
{
|
|
_textpath_text_set_internal(obj, pd, "efl.text", text);
|
|
}
|
|
|
|
EOLIAN static const char *
|
|
_efl_ui_textpath_efl_text_text_get(const Eo *obj EINA_UNUSED, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
return edje_object_part_text_get(pd->text_obj, "efl.text");
|
|
}
|
|
|
|
EOLIAN static Eina_Error
|
|
_efl_ui_textpath_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
Eina_Error ret = EFL_UI_THEME_APPLY_ERROR_GENERIC;
|
|
|
|
ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
|
|
if (ret == EFL_UI_THEME_APPLY_ERROR_GENERIC) return EFL_UI_THEME_APPLY_ERROR_GENERIC;
|
|
|
|
elm_widget_theme_object_set(obj, pd->text_obj, "textpath", "base",
|
|
elm_widget_style_get(obj));
|
|
_ellipsis_set(pd, obj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_efl_gfx_entity_position_set(Eo *obj, Efl_Ui_Textpath_Data *pd, Eina_Position2D pos)
|
|
{
|
|
Eina_Position2D opos, diff;
|
|
Efl_Ui_Textpath_Segment *seg;
|
|
double sx, sy, csx, csy, cex, cey, ex, ey;
|
|
|
|
opos = efl_gfx_entity_position_get(obj);
|
|
|
|
diff.x = pos.x - opos.x;
|
|
diff.y = pos.y - opos.y;
|
|
|
|
efl_gfx_entity_position_set(efl_super(obj, MY_CLASS), pos);
|
|
|
|
EINA_INLIST_FOREACH(pd->segments, seg)
|
|
{
|
|
eina_bezier_values_get(&seg->bezier, &sx, &sy, &csx, &csy,
|
|
&cex, &cey, &ex, &ey);
|
|
sx += diff.x;
|
|
sy += diff.y;
|
|
csx += diff.x;
|
|
csy += diff.y;
|
|
cex += diff.x;
|
|
cey += diff.y;
|
|
ex += diff.x;
|
|
ey += diff.y;
|
|
|
|
eina_bezier_values_set(&seg->bezier, sx, sy, csx, csy,
|
|
cex, cey, ex, ey);
|
|
}
|
|
|
|
_text_draw(pd);
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_efl_gfx_entity_size_set(Eo *obj, Efl_Ui_Textpath_Data *pd EINA_UNUSED, Eina_Size2D sz)
|
|
{
|
|
Eina_Size2D psize = efl_gfx_entity_size_get(obj);
|
|
efl_gfx_entity_size_set(efl_super(obj, MY_CLASS), sz);
|
|
if (psize.w != sz.w || psize.h != sz.h) _text_draw(pd);
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_circle_set(Eo *obj, Efl_Ui_Textpath_Data *pd, double x, double y, double radius, double start_angle, Efl_Ui_Textpath_Direction direction)
|
|
{
|
|
double sweep_length;
|
|
|
|
if (pd->circle.x == x && pd->circle.y == y &&
|
|
pd->circle.radius == radius &&
|
|
pd->circle.start_angle == start_angle &&
|
|
pd->direction == direction &&
|
|
_map_point_calc(pd) > 0)
|
|
return;
|
|
pd->circle.x = x;
|
|
pd->circle.y = y;
|
|
pd->circle.radius = radius;
|
|
pd->circle.start_angle = start_angle;
|
|
pd->direction = direction;
|
|
|
|
efl_gfx_path_reset(obj);
|
|
|
|
if (direction == EFL_UI_TEXTPATH_DIRECTION_CW ||
|
|
direction == EFL_UI_TEXTPATH_DIRECTION_CW_CENTER)
|
|
sweep_length = -360;
|
|
else
|
|
sweep_length = 360;
|
|
|
|
efl_gfx_path_append_arc(obj, x - radius, y - radius, radius * 2,
|
|
radius * 2, start_angle, sweep_length);
|
|
|
|
_path_data_get(obj, pd);
|
|
_path_start_angle_adjust(obj, pd);
|
|
_sizing_eval(pd);
|
|
|
|
efl_gfx_hint_size_min_set(obj, EINA_SIZE2D(x * 2, y * 2));
|
|
}
|
|
|
|
EOLIAN static int
|
|
_efl_ui_textpath_slice_number_get(const Eo *obj EINA_UNUSED, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
return pd->slice_no;
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_slice_number_set(Eo *obj EINA_UNUSED, Efl_Ui_Textpath_Data *pd, int slice_no)
|
|
{
|
|
if (pd->slice_no == slice_no) return;
|
|
pd->slice_no = slice_no;
|
|
_sizing_eval(pd);
|
|
}
|
|
|
|
EOLIAN static void
|
|
_efl_ui_textpath_ellipsis_set(Eo *obj, Efl_Ui_Textpath_Data *pd, Eina_Bool ellipsis)
|
|
{
|
|
if (pd->ellipsis == ellipsis) return;
|
|
pd->ellipsis = ellipsis;
|
|
|
|
_ellipsis_set(pd, obj);
|
|
_sizing_eval(pd);
|
|
}
|
|
|
|
EOLIAN static Eina_Bool
|
|
_efl_ui_textpath_ellipsis_get(const Eo *obj EINA_UNUSED, Efl_Ui_Textpath_Data *pd)
|
|
{
|
|
return pd->ellipsis;
|
|
}
|
|
|
|
/* Efl.Part begin */
|
|
|
|
static Eina_Bool
|
|
_part_is_efl_ui_textpath_part(const Eo *obj EINA_UNUSED, const char *part)
|
|
{
|
|
//Use Efl.Ui.Widget's "background" and "shadow" parts
|
|
if (eina_streq(part, "background") || eina_streq(part, "shadow"))
|
|
return EINA_FALSE;
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
ELM_PART_OVERRIDE_PARTIAL(efl_ui_textpath, EFL_UI_TEXTPATH, Efl_Ui_Textpath_Data, _part_is_efl_ui_textpath_part)
|
|
ELM_PART_OVERRIDE_TEXT_SET(efl_ui_textpath, EFL_UI_TEXTPATH, Efl_Ui_Textpath_Data)
|
|
ELM_PART_OVERRIDE_TEXT_GET(efl_ui_textpath, EFL_UI_TEXTPATH, Efl_Ui_Textpath_Data)
|
|
#include "efl_ui_textpath_part.eo.c"
|
|
/* Efl.Part end */
|
|
|
|
#define EFL_UI_TEXTPATH_EXTRA_OPS \
|
|
EFL_CANVAS_GROUP_ADD_OPS(efl_ui_textpath)
|
|
|
|
#include "efl_ui_textpath.eo.c"
|
|
#include "efl_ui_textpath_eo.legacy.c"
|
|
|
|
#include "efl_ui_textpath_legacy_eo.h"
|
|
|
|
#define MY_CLASS_NAME_LEGACY "elm_textpath"
|
|
/* Legacy APIs */
|
|
|
|
static void
|
|
_efl_ui_textpath_legacy_class_constructor(Efl_Class *klass)
|
|
{
|
|
evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
|
|
}
|
|
|
|
EOLIAN static Eo *
|
|
_efl_ui_textpath_legacy_efl_object_constructor(Eo *obj, void *_pd EINA_UNUSED)
|
|
{
|
|
obj = efl_constructor(efl_super(obj, EFL_UI_TEXTPATH_LEGACY_CLASS));
|
|
efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY);
|
|
return obj;
|
|
}
|
|
|
|
EAPI Evas_Object *
|
|
elm_textpath_add(Evas_Object *parent)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
|
|
return elm_legacy_add(EFL_UI_TEXTPATH_LEGACY_CLASS, parent);
|
|
}
|
|
|
|
#include "efl_ui_textpath_legacy_eo.c"
|
|
|