elm_code: Bring in line numbers and a left gutter

This styling makes it easier to see that symbols are not part of the text
but also makes line numbers part of the widget and they can be easily
switched on or off.
This commit is contained in:
Andy Williams 2015-02-03 13:59:50 +00:00
commit d47a6181cb
12 changed files with 210 additions and 46 deletions

View File

@ -53,16 +53,16 @@ _elm_code_test_welcome_setup(Evas_Object *parent)
code = elm_code_create();
widget = eo_add(ELM_CODE_WIDGET_CLASS, parent);
eo_do(widget,
elm_code_widget_code_set(code);
elm_code_widget_font_size_set(14);
elm_code_widget_editable_set(EINA_TRUE);
elm_code_widget_code_set(code),
elm_code_widget_font_size_set(14),
elm_code_widget_editable_set(EINA_TRUE),
elm_code_widget_line_numbers_set(EINA_TRUE),
eo_event_callback_add(ELM_CODE_WIDGET_EVENT_LINE_CLICKED, _elm_code_test_line_cb, code));
_append_line(code->file, "Hello World, Elm Code!");
elm_code_file_line_token_add(code->file, 1, 14, 21, ELM_CODE_TOKEN_TYPE_COMMENT);
_append_line(code->file, "");
_append_line(code->file, "This is a demo of elm_code's capabilities.");
_append_line(code->file, "*** Currently experimental ***");
elm_code_file_line_status_set(code->file, 4, ELM_CODE_STATUS_TYPE_ERROR);

View File

@ -37,6 +37,7 @@
#include "elm_code_file.h"
#include "elm_code_parse.h"
#include "elm_code_widget.eo.h"
#include "elm_code_widget_text.h"
#include "elm_code_diff_widget.h"
#ifdef __cplusplus

View File

@ -20,6 +20,7 @@ includesdir = $(includedir)/edi-@VMAJ@
libelm_code_la_SOURCES = \
elm_code_file.c \
elm_code_parse.c \
elm_code_widget_text.c \
elm_code_widget.c \
elm_code_diff_widget.c \
elm_code.c \

View File

@ -26,12 +26,10 @@ typedef enum {
ELM_CODE_TOKEN_TYPE_DEFAULT = ELM_CODE_STATUS_TYPE_COUNT,
ELM_CODE_TOKEN_TYPE_COMMENT,
ELM_CODE_TOKEN_TYPE_ADDED,
ELM_CODE_TOKEN_TYPE_REMOVED,
ELM_CODE_TOKEN_TYPE_CHANGED,
ELM_CODE_TOKEN_TYPE_CURSOR, // a pseudo type used for styling but may not be set on a cell
ELM_CODE_TOKEN_TYPE_COUNT
} Elm_Code_Token_Type;

View File

@ -25,3 +25,17 @@ extern int _elm_code_lib_log_dom;
#define DBG(...) EINA_LOG_DOM_DBG(_elm_code_lib_log_dom, __VA_ARGS__)
#endif
typedef struct
{
Elm_Code *code;
Evas_Object *grid, *scroller;
Evas_Font_Size font_size;
double gravity_x, gravity_y;
unsigned int cursor_line, cursor_col;
Eina_Bool cursor_move_vetoed;
Eina_Bool editable, focussed;
Eina_Bool show_line_numbers;
} Elm_Code_Widget_Data;

View File

@ -5,18 +5,13 @@
#include <Elm_Code.h>
#include "elm_code_private.h"
typedef struct
{
Elm_Code *code;
Evas_Object *grid, *scroller;
typedef enum {
ELM_CODE_WIDGET_COLOR_GUTTER_BG = ELM_CODE_TOKEN_TYPE_COUNT,
ELM_CODE_WIDGET_COLOR_GUTTER_FG,
ELM_CODE_WIDGET_COLOR_CURSOR,
Evas_Font_Size font_size;
double gravity_x, gravity_y;
unsigned int cursor_line, cursor_col;
Eina_Bool cursor_move_vetoed;
Eina_Bool editable, focussed;
} Elm_Code_Widget_Data;
ELM_CODE_WIDGET_COLOR_COUNT
} Elm_Code_Widget_Colors;
Eina_Unicode status_icons[] = {
' ',
@ -69,10 +64,11 @@ _elm_code_widget_resize(Elm_Code_Widget *widget)
Elm_Code_Line *line;
Eina_List *item;
Evas_Coord ww, wh, old_width, old_height;
int w, h, cw, ch;
int w, h, cw, ch, gutter;
Elm_Code_Widget_Data *pd;
pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
gutter = elm_code_widget_text_left_gutter_width_get(widget);
if (!pd->code)
return EINA_FALSE;
@ -85,8 +81,8 @@ _elm_code_widget_resize(Elm_Code_Widget *widget)
w = 0;
h = elm_code_file_lines_get(pd->code->file);
EINA_LIST_FOREACH(pd->code->file->lines, item, line)
if (line->length + 2 > w)
w = line->length + 2;
if (line->length + gutter + 1 > w)
w = line->length + gutter + 1;
if (w*cw > ww)
ww = w*cw;
@ -116,24 +112,26 @@ _elm_code_widget_fill_line_token(Evas_Textgrid_Cell *cells, int count, int start
}
static void
_elm_code_widget_fill_line_tokens(Evas_Textgrid_Cell *cells, unsigned int count, Elm_Code_Line *line)
_elm_code_widget_fill_line_tokens(Elm_Code_Widget *widget, Evas_Textgrid_Cell *cells,
unsigned int count, Elm_Code_Line *line)
{
Eina_List *item;
Elm_Code_Token *token;
int start, length;
int start, length, offset;
start = 1;
length = line->length;
offset = elm_code_widget_text_left_gutter_width_get(widget) - 1;
start = offset + 1;
length = line->length + offset;
EINA_LIST_FOREACH(line->tokens, item, token)
{
_elm_code_widget_fill_line_token(cells, count, start, token->start, ELM_CODE_TOKEN_TYPE_DEFAULT);
_elm_code_widget_fill_line_token(cells, count, start, token->start + offset, ELM_CODE_TOKEN_TYPE_DEFAULT);
// TODO handle a token starting before the previous finishes
_elm_code_widget_fill_line_token(cells, count, token->start, token->end, token->type);
_elm_code_widget_fill_line_token(cells, count, token->start + offset, token->end + offset, token->type);
start = token->end + 1;
start = token->end + offset + 1;
}
_elm_code_widget_fill_line_token(cells, count, start, length, ELM_CODE_TOKEN_TYPE_DEFAULT);
@ -142,12 +140,13 @@ _elm_code_widget_fill_line_tokens(Evas_Textgrid_Cell *cells, unsigned int count,
static void
_elm_code_widget_fill_line(Elm_Code_Widget *widget, Evas_Textgrid_Cell *cells, Elm_Code_Line *line)
{
char *chr;
char *chr, *number;
unsigned int length, x;
int w;
int w, gutter, g;
Elm_Code_Widget_Data *pd;
pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
gutter = elm_code_widget_text_left_gutter_width_get(widget);
if (!_elm_code_widget_resize(widget))
return;
@ -155,16 +154,30 @@ _elm_code_widget_fill_line(Elm_Code_Widget *widget, Evas_Textgrid_Cell *cells, E
length = line->length;
evas_object_textgrid_size_get(pd->grid, &w, NULL);
cells[0].codepoint = status_icons[line->status];
cells[0].bold = 1;
cells[0].fg = ELM_CODE_TOKEN_TYPE_DEFAULT;
cells[0].bg = line->status;
cells[gutter-1].codepoint = status_icons[line->status];
cells[gutter-1].bold = 1;
cells[gutter-1].fg = ELM_CODE_WIDGET_COLOR_GUTTER_FG;
cells[gutter-1].bg = (line->status == ELM_CODE_STATUS_TYPE_DEFAULT) ? ELM_CODE_WIDGET_COLOR_GUTTER_BG : line->status;
if (pd->show_line_numbers)
{
number = malloc(sizeof(char) * gutter);
snprintf(number, gutter, "%*d", gutter - 1, line->number);
for (g = 0; g < gutter - 1; g++)
{
cells[g].codepoint = *(number + g);
cells[g].fg = ELM_CODE_WIDGET_COLOR_GUTTER_FG;
cells[g].bg = ELM_CODE_WIDGET_COLOR_GUTTER_BG;
}
free(number);
}
if (line->modified)
chr = line->modified;
else
chr = (char *)line->content;
for (x = 1; x < (unsigned int) w && x <= length; x++)
for (x = gutter; x < (unsigned int) w && x < length + gutter; x++)
{
cells[x].codepoint = *chr;
cells[x].bg = line->status;
@ -177,11 +190,11 @@ _elm_code_widget_fill_line(Elm_Code_Widget *widget, Evas_Textgrid_Cell *cells, E
cells[x].bg = line->status;
}
_elm_code_widget_fill_line_tokens(cells, w, line);
_elm_code_widget_fill_line_tokens(widget, cells, w, line);
if (pd->editable && pd->focussed && pd->cursor_line == line->number)
{
if (pd->cursor_col < (unsigned int) w)
cells[pd->cursor_col].bg = ELM_CODE_TOKEN_TYPE_CURSOR;
if (pd->cursor_col + gutter - 1 < (unsigned int) w)
cells[pd->cursor_col + gutter - 1].bg = ELM_CODE_WIDGET_COLOR_CURSOR;
}
evas_object_textgrid_update_add(pd->grid, 0, line->number - 1, w, 1);
@ -269,7 +282,7 @@ _elm_code_widget_clicked_editable_cb(Elm_Code_Widget *widget, Evas_Coord x, Evas
pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
evas_object_textgrid_cell_size_get(pd->grid, &cw, &ch);
col = ((double) x / cw) + 2;
col = ((double) x / cw) - elm_code_widget_text_left_gutter_width_get(widget) + 1;
row = ((double) y / ch) + 1;
line = elm_code_file_line_get(pd->code->file, row);
@ -277,8 +290,8 @@ _elm_code_widget_clicked_editable_cb(Elm_Code_Widget *widget, Evas_Coord x, Evas
{
pd->cursor_line = row;
if (col <= (unsigned int) line->length + 2)
pd->cursor_col = col - 2;
if (col <= (unsigned int) line->length + 1)
pd->cursor_col = col;
else
pd->cursor_col = line->length + 1;
}
@ -571,6 +584,18 @@ _elm_code_widget_editable_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
return pd->editable;
}
EOLIAN static void
_elm_code_widget_line_numbers_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, Eina_Bool line_numbers)
{
pd->show_line_numbers = line_numbers;
}
EOLIAN static Eina_Bool
_elm_code_widget_line_numbers_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
{
return pd->show_line_numbers;
}
static void
_elm_code_widget_setup_palette(Evas_Object *o)
{
@ -605,9 +630,13 @@ _elm_code_widget_setup_palette(Evas_Object *o)
evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_CHANGED,
54, 54, 255, 255);
// the style for a cursor - this is a special token and will be applied to the background
evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_TOKEN_TYPE_CURSOR,
// other styles that the widget uses
evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_WIDGET_COLOR_CURSOR,
205, 205, 54, 255);
evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_WIDGET_COLOR_GUTTER_BG,
75, 75, 75, 255);
evas_object_textgrid_palette_set(o, EVAS_TEXTGRID_PALETTE_STANDARD, ELM_CODE_WIDGET_COLOR_GUTTER_FG,
139, 139, 139, 255);
}
EOLIAN static void

View File

@ -84,6 +84,26 @@ class Elm_Code_Widget (Elm_Box, Elm_Interface_Scrollable,
Eina_Bool editable; /*@ The editable state of the widget */
}
}
line_numbers {
set {
/*@
Set whether line numbers should be displayed in the left gutter.
Passing EINA_TRUE will reserve a space for showing line numbers,
EINA_FALSE will turn this off.
@ingroup Features */
}
get {
/*@
Get the status of line number display for this widget.
@ingroup Features */
}
values {
Eina_Bool line_numbers; /*@ Whether or not line numbers (or their placeholder) should be shown */
}
}
}
methods {
}

View File

@ -33,6 +33,14 @@ Eina_Bool _elm_code_widget_editable_get(Eo *obj, Elm_Code_Widget_Data *pd);
EOAPI EO_FUNC_BODY(elm_code_widget_editable_get, Eina_Bool, 0);
void _elm_code_widget_line_numbers_set(Eo *obj, Elm_Code_Widget_Data *pd, Eina_Bool line_numbers);
EOAPI EO_VOID_FUNC_BODYV(elm_code_widget_line_numbers_set, EO_FUNC_CALL(line_numbers), Eina_Bool line_numbers);
Eina_Bool _elm_code_widget_line_numbers_get(Eo *obj, Elm_Code_Widget_Data *pd);
EOAPI EO_FUNC_BODY(elm_code_widget_line_numbers_get, Eina_Bool, 0);
void _elm_code_widget_eo_base_constructor(Eo *obj, Elm_Code_Widget_Data *pd);
@ -58,6 +66,8 @@ static Eo_Op_Description _elm_code_widget_op_desc[] = {
EO_OP_FUNC(elm_code_widget_gravity_get, _elm_code_widget_gravity_get, "Get the current x and y gravity of the widget's scroller"),
EO_OP_FUNC(elm_code_widget_editable_set, _elm_code_widget_editable_set, "Set whether this widget allows editing"),
EO_OP_FUNC(elm_code_widget_editable_get, _elm_code_widget_editable_get, "Get the current editable state of this widget"),
EO_OP_FUNC(elm_code_widget_line_numbers_set, _elm_code_widget_line_numbers_set, "Set whether line numbers should be displayed in the left gutter."),
EO_OP_FUNC(elm_code_widget_line_numbers_get, _elm_code_widget_line_numbers_get, "Get the status of line number display for this widget."),
EO_OP_SENTINEL
};

View File

@ -63,8 +63,7 @@ EOAPI Evas_Font_Size elm_code_widget_font_size_get(void);
*
* Set how this widget's scroller should respond to new lines being added.
*
* An x value of 0.0 will maintain the distance from the left edge, 1.0
will ensure the rightmost edge (of the longest line) is respected
* An x value of 0.0 will maintain the distance from the left edge, 1.0 will ensure the rightmost edge (of the longest line) is respected
* With 0.0 for y the view will keep it's position relative to the top whereas 1.0 will scroll downward as lines are added.
*
* @ingroup Layout
@ -81,7 +80,7 @@ EOAPI void elm_code_widget_gravity_set(double x, double y);
*
* @ingroup Layout
*
* @param[out] x The horizontal value of the scroller gravity, currently ignored
* @param[out] x The horizontal value of the scroller gravity - valid values are 0.0 and 1.0
* @param[out] y The vertical gravity of the widget's scroller - valid values are 0.0 and 1.0
*
*/
@ -117,6 +116,30 @@ EOAPI void elm_code_widget_editable_set(Eina_Bool editable);
*/
EOAPI Eina_Bool elm_code_widget_editable_get(void);
/**
*
* Set whether line numbers should be displayed in the left gutter.
*
* Passing EINA_TRUE will reserve a space for showing line numbers,
* EINA_FALSE will turn this off.
*
* @ingroup Features
*
* @param[in] line_numbers Whether or not line numbers (or their placeholder) should be shown
*
*/
EOAPI void elm_code_widget_line_numbers_set(Eina_Bool line_numbers);
/**
*
* Get the status of line number display for this widget.
*
* @ingroup Features
*
*
*/
EOAPI Eina_Bool elm_code_widget_line_numbers_get(void);
EOAPI extern const Eo_Event_Description _ELM_CODE_WIDGET_EVENT_LINE_CLICKED;
/**

View File

@ -0,0 +1,38 @@
#ifdef HAVE_CONFIG
# include "config.h"
#endif
#include "Elm_Code.h"
#include "elm_code_private.h"
EAPI int
elm_code_widget_text_line_number_width_get(Elm_Code_Widget *widget)
{
Elm_Code_Widget_Data *pd;
int max;
pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
max = elm_code_file_lines_get(pd->code->file);
if (max < 1)
max = 1;
return floor(log10(max)) + 1;
}
EAPI int
elm_code_widget_text_left_gutter_width_get(Elm_Code_Widget *widget)
{
Elm_Code_Widget_Data *pd;
int width = 1; // the status icon, for now
if (!widget)
return width;
pd = eo_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
if (pd->show_line_numbers)
width += elm_code_widget_text_line_number_width_get(widget);
return width;
}

View File

@ -0,0 +1,30 @@
#ifndef ELM_CODE_WIDGET_TEXT_H_
# define ELM_CODE_WIDGET_TEXT_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Text layout handling functions.
* @defgroup Managing the complexities of layout out text in an Elm_Code_Widget
*
* @{
*
* Functions for text layout handling
*
*/
EAPI int elm_code_widget_text_left_gutter_width_get(Elm_Code_Widget *widget);
EAPI int elm_code_widget_text_line_number_width_get(Elm_Code_Widget *widget);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ELM_CODE_WIDGET_TEXT_H_ */

View File

@ -29,7 +29,7 @@ START_TEST (elm_code_widget_token_render_simple_test)
elm_code_file_line_token_add(file, 1, 6+1, 17+1, ELM_CODE_TOKEN_TYPE_COMMENT);
elm_code_file_line_token_add(file, 1, 21+1, 22+1, ELM_CODE_TOKEN_TYPE_COMMENT);
_elm_code_widget_fill_line_tokens(cells, length+1, line);
_elm_code_widget_fill_line_tokens(NULL, cells, length+1, line);
_assert_cell_type(cells[1], ELM_CODE_TOKEN_TYPE_DEFAULT, 1);
_assert_cell_type(cells[4], ELM_CODE_TOKEN_TYPE_DEFAULT, 4);
_assert_cell_type(cells[6], ELM_CODE_TOKEN_TYPE_DEFAULT, 6);