This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository efl.
View the commit online.
commit 91586e6c4ad0ca431be481f87fd9ab292bd8d21c
Author: Alastair Poole <[email protected]>
AuthorDate: Tue Apr 14 05:18:20 2026 +0100
elm_code_widget: Implement bounded viewport-based render.
The existing code was rendering as many lines as had been viewed.
With a 10k line=file the viewport would have to keep track
of 100000 objects and it became very unresponsive.
Changes:
* Added render window state to widget private data.
* Only render necessary grid rows.
* Switch line to grid lookup to a base-offset mapping.
* Update cursor geometry to compute positions without
using a grid index.
@fix
---
src/lib/elementary/elm_code_widget.c | 230 ++++++++++++++++-----------
src/lib/elementary/elm_code_widget_private.h | 3 +-
2 files changed, 139 insertions(+), 94 deletions(-)
diff --git a/src/lib/elementary/elm_code_widget.c b/src/lib/elementary/elm_code_widget.c
index 149d604946..c1c115eba4 100644
--- a/src/lib/elementary/elm_code_widget.c
+++ b/src/lib/elementary/elm_code_widget.c
@@ -3,6 +3,7 @@
#endif
#include <Elementary.h>
+#include <stdint.h>
#include "elm_priv.h"
@@ -57,6 +58,7 @@ static Eina_Unicode status_icons[] = {
} while (0)
static void _elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline);
+static Evas_Object *_elm_code_widget_grid_for_row_get(Elm_Code_Widget_Data *pd, unsigned int row);
#include "elm_code_widget_legacy_eo.h"
@@ -77,6 +79,7 @@ _elm_code_widget_efl_object_constructor(Eo *obj, Elm_Code_Widget_Data *pd)
pd->cursor_line = 1;
pd->cursor_col = 1;
+ pd->grid_row_base = 1;
pd->tabstop = 8;
@@ -101,6 +104,18 @@ _elm_code_widget_class_constructor(Efl_Class *klass EINA_UNUSED)
}
+static Evas_Object *
+_elm_code_widget_grid_for_row_get(Elm_Code_Widget_Data *pd, unsigned int row)
+{
+ unsigned int index;
+
+ if (row < pd->grid_row_base || !pd->grids)
+ return NULL;
+
+ index = row - pd->grid_row_base;
+ return eina_list_nth(pd->grids, index);
+}
+
void
_elm_code_widget_cell_size_get(Elm_Code_Widget *widget, Evas_Coord *width, Evas_Coord *height)
{
@@ -403,16 +418,31 @@ _elm_code_widget_fill_line(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, El
unsigned int length, x, charwidth, i, w;
int chrpos, gutter;
Evas_Object *grid;
+ Evas_Object *status;
Evas_Textgrid_Cell *cells;
+ void *grid_line;
EINA_SAFETY_ON_NULL_RETURN(line);
gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
- if (eina_list_count(pd->grids) < line->number)
+ w = elm_code_widget_columns_get(widget);
+ grid = _elm_code_widget_grid_for_row_get(pd, line->number);
+ if (!grid)
return;
- w = elm_code_widget_columns_get(widget);
- grid = eina_list_nth(pd->grids, line->number - 1);
+ grid_line = evas_object_data_get(grid, "line");
+ if (((unsigned int)(uintptr_t)grid_line) != line->number)
+ {
+ status = evas_object_data_get(grid, "status");
+ if (status)
+ {
+ elm_box_unpack(pd->gridbox, status);
+ evas_object_hide(status);
+ evas_object_data_set(grid, "status", NULL);
+ }
+ evas_object_data_set(grid, "line", (void *)(uintptr_t)line->number);
+ }
+
cells = evas_object_textgrid_cellrow_get(grid, 0);
length = elm_code_widget_line_text_column_width_get(widget, line);
chrpos = 0;
@@ -566,6 +596,7 @@ _elm_code_widget_clear(Elm_Code_Widget *widget)
{
evas_object_del(grid);
}
+ pd->grid_row_base = 1;
}
static void
@@ -576,6 +607,8 @@ _elm_code_widget_fill(Elm_Code_Widget *widget)
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
height = elm_code_widget_lines_visible_get(widget);
+ if (height < 1 && elm_code_file_lines_get(pd->code->file) > 0)
+ height = 1;
if (height > elm_code_file_lines_get(pd->code->file))
height = elm_code_file_lines_get(pd->code->file);
@@ -673,23 +706,18 @@ static void
_elm_code_widget_cursor_ensure_visible(Elm_Code_Widget *widget)
{
Evas_Coord viewx, viewy, vieww, viewh, cellw = 0, cellh = 0;
- Evas_Coord curx, cury, oy, rowy;
- Evas_Object *grid;
+ Evas_Coord curx, cury;
Elm_Code_Widget_Data *pd;
int gutter;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
- evas_object_geometry_get(widget, NULL, &oy, NULL, NULL);
elm_scroller_region_get(pd->scroller, &viewx, &viewy, &vieww, &viewh);
_elm_code_widget_cell_size_get(widget, &cellw, &cellh);
- grid = eina_list_data_get(eina_list_nth_list(pd->grids, pd->cursor_line - 1));
- evas_object_geometry_get(grid, NULL, &rowy, NULL, NULL);
-
gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
curx = (pd->cursor_col + gutter - 1) * cellw;
- cury = rowy + viewy - oy;
+ cury = (pd->cursor_line - 1) * cellh;
if (curx >= viewx && cury >= viewy && curx + cellw <= viewx + vieww && cury + cellh <= viewy + viewh)
return;
@@ -703,8 +731,7 @@ _elm_code_widget_cursor_move(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
Elm_Code *code;
Elm_Code_Line *line_obj;
const char *text;
- unsigned int oldrow, position, length, first_row, last_row;
- int cw = 0, ch = 0;
+ unsigned int oldrow, position, length;
oldrow = pd->cursor_line;
@@ -714,15 +741,6 @@ _elm_code_widget_cursor_move(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
if (line > elm_code_file_lines_get(pd->code->file))
return;
- if ((line > eina_list_count(pd->grids)) && (!pd->selection))
- {
- if (_elm_code_widget_viewport_get(widget, pd, &first_row, &last_row))
- {
- _elm_code_widget_cell_size_get(widget, &cw, &ch);
- _elm_code_widget_scroll_by(widget, 0, ch * (line - last_row));
- }
- }
-
code = pd->code;
line_obj = elm_code_file_line_get(code->file, line);
position = elm_code_widget_line_text_position_for_column_get(widget, line_obj, col);
@@ -748,12 +766,10 @@ _elm_code_widget_position_at_coordinates_get(Eo *obj, Elm_Code_Widget_Data *pd,
unsigned int *row, int *col)
{
Elm_Code_Widget *widget;
- Eina_List *item;
Elm_Code_Line *line;
- Evas_Coord ox = 0, oy = 0, sx = 0, sy = 0, rowy = 0;
- Evas_Object *grid;
+ Evas_Coord ox = 0, oy = 0, sx = 0, sy = 0;
int cw = 0, ch = 0, gutter, retcol;
- unsigned int guess = 1, number = 1;
+ unsigned int number = 1;
widget = (Elm_Code_Widget *)obj;
evas_object_geometry_get(widget, &ox, &oy, NULL, NULL);
@@ -765,24 +781,7 @@ _elm_code_widget_position_at_coordinates_get(Eo *obj, Elm_Code_Widget_Data *pd,
gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
if (y >= 0 && ch > 0)
- guess = ((double) y / ch) + 1;
- if (guess > 1)
- {
- number = guess;
-
- // unfortunately EINA_LIST_REVERSE_FOREACH skips to the end of the list...
- for (item = eina_list_nth_list(pd->grids, number - 1), grid = eina_list_data_get(item);
- number > 1 && item;
- item = eina_list_prev(item), grid = eina_list_data_get(item))
- {
- evas_object_geometry_get(grid, NULL, &rowy, NULL, NULL);
-
- if (rowy + sy - oy - 1 <= y)
- break;
-
- number--;
- }
- }
+ number = ((double) y / ch) + 1;
if (col)
{
@@ -808,8 +807,8 @@ _elm_code_widget_geometry_for_position_get(Elm_Code_Widget *widget, Elm_Code_Wid
Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h)
{
Elm_Code_Line *line;
- Evas_Object *grid;
- Evas_Coord cellw = 0;
+ Evas_Coord cellw = 0, cellh = 0;
+ Evas_Coord ox = 0, oy = 0, sx = 0, sy = 0;
unsigned int length = 0;
int gutter;
@@ -818,16 +817,19 @@ _elm_code_widget_geometry_for_position_get(Elm_Code_Widget *widget, Elm_Code_Wid
return EINA_FALSE;
elm_code_line_text_get(line, &length);
- _elm_code_widget_cell_size_get(widget, &cellw, h);
+ _elm_code_widget_cell_size_get(widget, &cellw, &cellh);
gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
-
- grid = eina_list_nth(pd->grids, row - 1);
- evas_object_geometry_get(grid, x, y, NULL, NULL);
+ evas_object_geometry_get(widget, &ox, &oy, NULL, NULL);
+ elm_scroller_region_get(pd->scroller, &sx, &sy, NULL, NULL);
if (x)
- *x += (col - 1 + gutter) * cellw;
+ *x = ox - sx + ((col - 1 + gutter) * cellw);
+ if (y)
+ *y = oy - sy + ((row - 1) * cellh);
if (w)
*w = cellw;
+ if (h)
+ *h = cellh;
return !!line && col <= (int) length;
}
@@ -841,7 +843,9 @@ _elm_code_widget_line_status_toggle(Elm_Code_Widget *widget EINA_UNUSED, Elm_Cod
char *text;
// add a status below the line if needed (and remove those no longer needed)
- grid = eina_list_nth(pd->grids, line->number - 1);
+ grid = _elm_code_widget_grid_for_row_get(pd, line->number);
+ if (!grid)
+ return;
status = evas_object_data_get(grid, "status");
if (status)
@@ -2005,6 +2009,50 @@ _elm_code_widget_setup_palette(Evas_Object *o, Evas_Object *layout, float fade)
_elm_code_widget_setup_palette_item(o, ELM_CODE_WIDGET_COLOR_WHITESPACE, "/bg/normal/text-whitespace", fade, edje);
}
+static void
+_elm_code_widget_render_window_get(Elm_Code_Widget *widget,
+ Elm_Code_Widget_Data *pd,
+ unsigned int *render_first_row,
+ unsigned int *render_last_row)
+{
+ unsigned int first_row = 1, last_row = 1, total_lines, preload;
+ Eina_Bool viewport = EINA_FALSE;
+
+ total_lines = elm_code_file_lines_get(pd->code->file);
+ if (!total_lines)
+ {
+ *render_first_row = 1;
+ *render_last_row = 0;
+ return;
+ }
+
+ if (_elm_code_widget_viewport_get(widget, pd, &first_row, &last_row))
+ viewport = EINA_TRUE;
+
+ if (!viewport || last_row < first_row)
+ {
+ first_row = 1;
+ last_row = efl_ui_code_widget_lines_visible_get(widget);
+ if (last_row < 1)
+ last_row = 1;
+ if (last_row > total_lines)
+ last_row = total_lines;
+ }
+
+ preload = last_row - first_row + 1;
+ if (preload < 64)
+ preload = 64;
+
+ if (first_row > preload)
+ *render_first_row = first_row - preload;
+ else
+ *render_first_row = 1;
+
+ *render_last_row = last_row + preload;
+ if (*render_last_row > total_lines)
+ *render_last_row = total_lines;
+}
+
static void
_elm_code_widget_ensure_n_grid_rows(Elm_Code_Widget *widget, int rows)
{
@@ -2015,22 +2063,22 @@ _elm_code_widget_ensure_n_grid_rows(Elm_Code_Widget *widget, int rows)
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
existing = eina_list_count(pd->grids);
- // trim unneeded rows in our rendering
- if (rows < existing)
+ if (rows < 0)
+ rows = 0;
+
+ /* trim unneeded rows in our rendering */
+ while (existing > rows)
{
- for (i = existing - rows; i > 0; i--)
- {
- grid = eina_list_data_get(eina_list_last(pd->grids));
- evas_object_del(grid);
- elm_box_unpack(pd->gridbox, grid);
- pd->grids = eina_list_remove_list(pd->grids, eina_list_last(pd->grids));
- }
- rows = existing;
+ Eina_List *last = eina_list_last(pd->grids);
+
+ grid = eina_list_data_get(last);
+ elm_box_unpack(pd->gridbox, grid);
+ evas_object_del(grid);
+ pd->grids = eina_list_remove_list(pd->grids, last);
+ existing--;
}
- if (rows == existing)
- return;
-
+ if (rows == existing) return;
for (i = existing; i < rows; i++)
{
grid = evas_object_textgrid_add(evas_object_evas_get(pd->gridbox));
@@ -2039,7 +2087,7 @@ _elm_code_widget_ensure_n_grid_rows(Elm_Code_Widget *widget, int rows)
evas_object_show(grid);
_elm_code_widget_setup_palette(grid, widget, (double) pd->alpha / 255);
- elm_box_pack_end(pd->gridbox, grid);
+ elm_box_pack_before(pd->gridbox, grid, pd->expander);
pd->grids = eina_list_append(pd->grids, grid);
evas_object_event_callback_add(grid, EVAS_CALLBACK_MOUSE_DOWN, _elm_code_widget_mouse_down_cb, widget);
@@ -2051,16 +2099,17 @@ _elm_code_widget_ensure_n_grid_rows(Elm_Code_Widget *widget, int rows)
}
static void
-_elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline)
+_elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline EINA_UNUSED)
{
Eina_List *item, *lines;
Elm_Code_Widget_Data *pd;
Elm_Code_Line *line;
Evas_Object *grid;
Evas_Coord ww, wh, old_width, old_height;
- int w = 0, h, cw = 0, ch = 0, gutter;
- unsigned int i, n, line_width, first_row = 1, last_row = 256;
- Eina_Bool viewport = EINA_FALSE;
+ int w = 0, cw = 0, ch = 0, gutter;
+ unsigned int i, h, line_width;
+ unsigned int first_row, last_row;
+ unsigned int rows_to_render;
pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
@@ -2076,19 +2125,17 @@ _elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline)
old_width = ww;
old_height = wh;
- n = h = elm_code_file_lines_get(pd->code->file);
-
- if (_elm_code_widget_viewport_get(widget, pd, &first_row, &last_row))
- viewport = EINA_TRUE;
-
- /* Grow by one page at a time where possible. */
- n = (last_row + (last_row - first_row)) < n ?
- last_row + (last_row - first_row) : n;
+ h = elm_code_file_lines_get(pd->code->file);
+ _elm_code_widget_render_window_get(widget, pd, &first_row, &last_row);
+ if (last_row < first_row)
+ rows_to_render = 0;
+ else
+ rows_to_render = last_row - first_row + 1;
/* Calculate the maximum width of our lines. */
lines = eina_list_nth_list(pd->code->file->lines, first_row - 1);
- for (i = 0; i < n; i++)
+ for (i = 0; i < rows_to_render; i++)
{
line = eina_list_data_get(lines);
if (!line) break;
@@ -2100,13 +2147,14 @@ _elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline)
lines = eina_list_next(lines);
}
- _elm_code_widget_ensure_n_grid_rows(widget, n);
+ _elm_code_widget_ensure_n_grid_rows(widget, rows_to_render);
+ pd->grid_row_base = first_row;
_elm_code_widget_cell_size_get(widget, &cw, &ch);
if (w*cw > ww)
ww = w*cw;
- if (h*ch > wh)
- wh = h*ch;
+ if ((Evas_Coord)(h * ch) > wh)
+ wh = h * ch;
if (cw > 0 && ww/cw > w)
pd->col_count = ww/cw;
@@ -2119,17 +2167,8 @@ _elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline)
evas_object_size_hint_min_set(grid, ww, ch);
}
- /* Here we expand our scroller when there are less grids than lines of text. */
- elm_box_unpack(pd->gridbox, pd->expander);
- evas_object_size_hint_min_set(pd->expander, ww, (h * ch) - (eina_list_count(pd->grids) * ch));
- elm_box_pack_end(pd->gridbox, pd->expander);
-
- if (!newline && viewport)
- {
- /* Where possible render additional lines to the viewport. */
- _elm_code_widget_fill_range(widget, pd, first_row, last_row + 64, NULL);
- return;
- }
+ evas_object_size_hint_min_set(pd->expander_top, ww, (first_row - 1) * ch);
+ evas_object_size_hint_min_set(pd->expander, ww, (h - (first_row + rows_to_render - 1)) * ch);
if (EINA_DBL_EQ(pd->gravity_x, 1.0) || EINA_DBL_EQ(pd->gravity_y, 1.0))
_elm_code_widget_scroll_by(widget,
@@ -2189,7 +2228,7 @@ _elm_code_widget_font_set(Eo *obj, Elm_Code_Widget_Data *pd,
evas_object_textgrid_font_set(grid, face, size * elm_config_scale_get());
}
- if (pd->cursor_rect && (eina_list_count(pd->grids) >= pd->cursor_line))
+ if (pd->cursor_rect)
_elm_code_widget_cursor_update(obj, pd);
if (pd->font_name)
@@ -2478,7 +2517,12 @@ _elm_code_widget_efl_canvas_group_group_add(Eo *obj, Elm_Code_Widget_Data *pd)
elm_object_content_set(scroller, gridrows);
pd->gridbox = gridrows;
+ pd->expander_top = evas_object_rectangle_add(evas_object_evas_get(scroller));
+ evas_object_show(pd->expander_top);
+ elm_box_pack_end(pd->gridbox, pd->expander_top);
+
pd->expander = evas_object_rectangle_add(evas_object_evas_get(scroller));
+ evas_object_show(pd->expander);
elm_box_pack_end(pd->gridbox, pd->expander);
_elm_code_widget_efl_ui_widget_theme_apply(obj, pd);
diff --git a/src/lib/elementary/elm_code_widget_private.h b/src/lib/elementary/elm_code_widget_private.h
index d64e5d67c7..fdc52d21c5 100644
--- a/src/lib/elementary/elm_code_widget_private.h
+++ b/src/lib/elementary/elm_code_widget_private.h
@@ -22,9 +22,10 @@ typedef struct
{
Elm_Code *code;
Eina_List *grids;
+ unsigned int grid_row_base;
unsigned int col_count;
Evas_Object *scroller, *gridbox, *background;
- Evas_Object *expander;
+ Evas_Object *expander, *expander_top;
const char *font_name;
Evas_Font_Size font_size;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.