[vte] widget: Add support for increased line and character spacing
- From: Egmont Koblinger <egmontkob src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] widget: Add support for increased line and character spacing
- Date: Mon, 4 Dec 2017 22:20:38 +0000 (UTC)
commit 26c79c6dc93aae4d4f787f36b594cac151df0f07
Author: Egmont Koblinger <egmont gmail com>
Date: Mon Dec 4 23:08:44 2017 +0100
widget: Add support for increased line and character spacing
New API methods vte_terminal_set_cell_{height,width}_scale take values
from 1.0 (default) to 2.0 (double spacing) to push the letters further
apart from each other, without affecting the font size.
https://bugzilla.gnome.org/show_bug.cgi?id=781479
https://bugzilla.gnome.org/show_bug.cgi?id=738781
doc/reference/vte-sections.txt | 4 +
src/app/app.cc | 8 ++
src/vte.cc | 193 +++++++++++++++++++++++++++++-----------
src/vte/vteterminal.h | 12 +++
src/vtedefines.hh | 2 +
src/vtedraw.cc | 124 ++++++++++++++++++--------
src/vtedraw.hh | 11 ++-
src/vtegtk.cc | 130 ++++++++++++++++++++++++++-
src/vtegtk.hh | 2 +
src/vteinternal.hh | 35 ++++++--
10 files changed, 416 insertions(+), 105 deletions(-)
---
diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt
index 0b6cd47..f73b3da 100644
--- a/doc/reference/vte-sections.txt
+++ b/doc/reference/vte-sections.txt
@@ -33,6 +33,10 @@ vte_terminal_set_scroll_on_keystroke
vte_terminal_get_scroll_on_keystroke
vte_terminal_set_rewrap_on_resize
vte_terminal_get_rewrap_on_resize
+vte_terminal_set_cell_height_scale
+vte_terminal_get_cell_height_scale
+vte_terminal_set_cell_width_scale
+vte_terminal_get_cell_width_scale
vte_terminal_set_color_bold
vte_terminal_set_color_foreground
vte_terminal_set_color_background
diff --git a/src/app/app.cc b/src/app/app.cc
index e882681..2770477 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -88,6 +88,8 @@ public:
int scrollback_lines{-1 /* infinite */};
int transparency_percent{-1};
int verbosity{0};
+ double cell_height_scale{1.0};
+ double cell_width_scale{1.0};
VteCursorBlinkMode cursor_blink_mode{VTE_CURSOR_BLINK_SYSTEM};
VteCursorShape cursor_shape{VTE_CURSOR_SHAPE_BLOCK};
@@ -338,6 +340,10 @@ public:
"Set background image extend", "EXTEND" },
{ "background-operator", 0, 0, G_OPTION_ARG_CALLBACK,
(void*)parse_background_operator,
"Set background draw operator", "OPERATOR" },
+ { "cell-height-scale", 0, 0, G_OPTION_ARG_DOUBLE, &cell_height_scale,
+ "Add extra line spacing", "1.0..2.0" },
+ { "cell-width-scale", 0, 0, G_OPTION_ARG_DOUBLE, &cell_width_scale,
+ "Add extra letter spacing", "1.0..2.0" },
{ "cjk-width", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_cjk_width,
"Specify the cjk ambiguous width to use for UTF-8 encoding", "NARROW|WIDE" },
{ "cursor-blink", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_cursor_blink,
@@ -1830,6 +1836,8 @@ vteapp_window_constructed(GObject *object)
vte_terminal_set_allow_hyperlink(window->terminal, !options.no_hyperlink);
vte_terminal_set_audible_bell(window->terminal, options.audible_bell);
+ vte_terminal_set_cell_height_scale(window->terminal, options.cell_height_scale);
+ vte_terminal_set_cell_width_scale(window->terminal, options.cell_width_scale);
vte_terminal_set_cjk_ambiguous_width(window->terminal, options.cjk_ambiguous_width);
vte_terminal_set_cursor_blink_mode(window->terminal, options.cursor_blink_mode);
vte_terminal_set_cursor_shape(window->terminal, options.cursor_shape);
diff --git a/src/vte.cc b/src/vte.cc
index d16b580..9976cf3 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -635,12 +635,10 @@ VteTerminalPrivate::invalidate_cell(vte::grid::column_t col,
}
columns = cell->attr.columns;
style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
- if (cell->c != 0 &&
- _vte_draw_get_char_width (
- m_draw,
- cell->c, columns, style) >
- m_cell_width * columns) {
- columns++;
+ if (cell->c != 0) {
+ int right;
+ _vte_draw_get_char_edges(m_draw, cell->c, columns, style, NULL, &right);
+ columns = MAX(columns, howmany(right, m_cell_width));
}
}
}
@@ -679,13 +677,10 @@ VteTerminalPrivate::invalidate_cursor_once(bool periodic)
if (cell != NULL) {
columns = cell->attr.columns;
auto style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
- if (cell->c != 0 &&
- _vte_draw_get_char_width (
- m_draw,
- cell->c,
- columns, style) >
- m_cell_width * columns) {
- columns++;
+ if (cell->c != 0) {
+ int right;
+ _vte_draw_get_char_edges(m_draw, cell->c, columns, style, NULL, &right);
+ columns = MAX(columns, howmany(right, m_cell_width));
}
}
columns = MAX(columns, preedit_width);
@@ -7526,45 +7521,63 @@ VteTerminalPrivate::widget_visibility_notify(GdkEventVisibility *event)
}
}
-/* Apply the changed metrics, and queue a resize if need be. */
+/* Apply the changed metrics, and queue a resize if need be.
+ *
+ * The cell's height consists of 4 parts, from top to bottom:
+ * - char_spacing.top: half of the extra line spacing,
+ * - char_ascent: the font's ascent,
+ * - char_descent: the font's descent,
+ * - char_spacing.bottom: the other half of the extra line spacing.
+ * Extra line spacing is typically 0, beef up cell_height_scale to get actual pixels
+ * here. Similarly, increase cell_width_scale to get nonzero char_spacing.{left,right}.
+ */
void
-VteTerminalPrivate::apply_font_metrics(int width,
- int height,
- int ascent,
- int descent)
+VteTerminalPrivate::apply_font_metrics(int cell_width,
+ int cell_height,
+ int char_ascent,
+ int char_descent,
+ GtkBorder char_spacing)
{
+ int char_height;
bool resize = false, cresize = false;
/* Sanity check for broken font changes. */
- width = MAX(width, 1);
- height = MAX(height, 2);
- ascent = MAX(ascent, 1);
- descent = MAX(descent, 1);
+ cell_width = MAX(cell_width, 1);
+ cell_height = MAX(cell_height, 2);
+ char_ascent = MAX(char_ascent, 1);
+ char_descent = MAX(char_descent, 1);
+
+ /* For convenience only. */
+ char_height = char_ascent + char_descent;
/* Change settings, and keep track of when we've changed anything. */
- if (width != m_cell_width) {
+ if (cell_width != m_cell_width) {
resize = cresize = true;
- m_cell_width = width;
+ m_cell_width = cell_width;
}
- if (height != m_cell_height) {
+ if (cell_height != m_cell_height) {
resize = cresize = true;
- m_cell_height = height;
+ m_cell_height = cell_height;
}
- if (ascent != m_char_ascent) {
+ if (char_ascent != m_char_ascent) {
resize = true;
- m_char_ascent = ascent;
+ m_char_ascent = char_ascent;
}
- if (descent != m_char_descent) {
+ if (char_descent != m_char_descent) {
resize = true;
- m_char_descent = descent;
+ m_char_descent = char_descent;
}
- m_line_thickness = MAX (MIN ((height - ascent) / 2, height / 14), 1);
+ if (memcmp(&char_spacing, &m_char_padding, sizeof(GtkBorder)) != 0) {
+ resize = true;
+ m_char_padding = char_spacing;
+ }
+ m_line_thickness = MAX (MIN (char_descent / 2, char_height / 14), 1);
/* FIXME take these from pango_font_metrics_get_{underline,strikethrough}_{position,thickness} */
- m_underline_position = MIN (ascent + m_line_thickness, height - m_line_thickness);
+ m_underline_position = MIN (char_spacing.top + char_ascent + m_line_thickness, cell_height -
m_line_thickness);
m_underline_thickness = m_line_thickness;
- m_strikethrough_position = ascent - height / 4;
+ m_strikethrough_position = char_spacing.top + char_ascent - char_height / 4;
m_strikethrough_thickness = m_line_thickness;
- m_regex_underline_position = height - 1; /* FIXME */
+ m_regex_underline_position = char_spacing.top + char_height - 1; /* FIXME */
m_regex_underline_thickness = 1; /* FIXME */
/* Queue a resize if anything's changed. */
@@ -7590,14 +7603,22 @@ VteTerminalPrivate::ensure_font()
set_font_desc(m_unscaled_font_desc);
}
if (m_fontdirty) {
- gint width, height, ascent;
+ int cell_width, cell_height;
+ int char_ascent, char_descent;
+ GtkBorder char_spacing;
m_fontdirty = FALSE;
_vte_draw_set_text_font (m_draw,
m_widget,
- m_fontdesc);
+ m_fontdesc,
+ m_cell_width_scale,
+ m_cell_height_scale);
_vte_draw_get_text_metrics (m_draw,
- &width, &height, &ascent);
- apply_font_metrics(width, height, ascent, height - ascent);
+ &cell_width, &cell_height,
+ &char_ascent, &char_descent,
+ &char_spacing);
+ apply_font_metrics(cell_width, cell_height,
+ char_ascent, char_descent,
+ char_spacing);
}
}
}
@@ -7704,6 +7725,40 @@ VteTerminalPrivate::set_font_scale(gdouble scale)
return true;
}
+bool
+VteTerminalPrivate::set_cell_width_scale(double scale)
+{
+ /* FIXME: compare old and new scale in pixel space */
+ if (_vte_double_equal(scale, m_cell_width_scale))
+ return false;
+
+ m_cell_width_scale = scale;
+ /* Set the drawing font. */
+ m_fontdirty = TRUE;
+ if (widget_realized()) {
+ ensure_font();
+ }
+
+ return true;
+}
+
+bool
+VteTerminalPrivate::set_cell_height_scale(double scale)
+{
+ /* FIXME: compare old and new scale in pixel space */
+ if (_vte_double_equal(scale, m_cell_height_scale))
+ return false;
+
+ m_cell_height_scale = scale;
+ /* Set the drawing font. */
+ m_fontdirty = TRUE;
+ if (widget_realized()) {
+ ensure_font();
+ }
+
+ return true;
+}
+
/* Read and refresh our perception of the size of the PTY. */
void
VteTerminalPrivate::refresh_size()
@@ -8034,6 +8089,7 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
m_cell_height = 1;
m_char_ascent = 1;
m_char_descent = 1;
+ m_char_padding = {0, 0, 0, 0};
m_line_thickness = 1;
m_underline_position = 1;
m_underline_thickness = 1;
@@ -8162,6 +8218,8 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
m_unscaled_font_desc = nullptr;
m_fontdesc = nullptr;
m_font_scale = 1.;
+ m_cell_width_scale = 1.;
+ m_cell_height_scale = 1.;
m_has_fonts = FALSE;
m_allow_hyperlink = FALSE;
@@ -9574,7 +9632,8 @@ VteTerminalPrivate::paint_cursor()
struct _vte_draw_text_request item;
vte::grid::row_t drow;
vte::grid::column_t col;
- long width, height, cursor_width;
+ int width, height, cursor_width;
+ guint style = 0;
guint fore, back;
vte::color::rgb bg;
int x, y;
@@ -9614,14 +9673,8 @@ VteTerminalPrivate::paint_cursor()
item.columns = item.c == '\t' ? 1 : cell ? cell->attr.columns : 1;
item.x = col * width;
item.y = row_to_pixel(drow);
- cursor_width = item.columns * width;
if (cell && cell->c != 0) {
- guint style;
- gint cw;
style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
- cw = _vte_draw_get_char_width (m_draw, cell->c,
- cell->attr.columns, style);
- cursor_width = MAX(cursor_width, cw);
}
selected = cell_is_selected(col, drow);
@@ -9634,33 +9687,65 @@ VteTerminalPrivate::paint_cursor()
switch (decscusr_cursor_shape()) {
case VTE_CURSOR_SHAPE_IBEAM: {
+ /* Draw at the very left of the cell (before the spacing), even in case of CJK.
+ * IMO (egmont) not overrunning the letter improves readability, vertical movement
+ * looks good (no zigzag even when a somewhat wider glyph that starts filling up
+ * the left spacing, or CJK that begins further to the right is encountered),
+ * and also this is where it looks good if background colors change, including
+ * Shift+arrows highlighting experience in some editors. As per the behavior of
+ * word processors, don't increase the height by the line spacing. */
int stem_width;
- stem_width = (int) (((float) height) * m_cursor_aspect_ratio + 0.5);
- stem_width = CLAMP (stem_width, VTE_LINE_WIDTH, cursor_width);
+ stem_width = (int) (((float) (m_char_ascent + m_char_descent)) *
m_cursor_aspect_ratio + 0.5);
+ stem_width = CLAMP (stem_width, VTE_LINE_WIDTH, m_cell_width);
_vte_draw_fill_rectangle(m_draw,
- x, y, stem_width, height,
+ x, y + m_char_padding.top, stem_width, m_char_ascent +
m_char_descent,
&bg, VTE_DRAW_OPAQUE);
break;
}
case VTE_CURSOR_SHAPE_UNDERLINE: {
- int line_height;
+ /* The width is at least the overall width of the cell (or two cells) minus the two
+ * half spacings on the two edges. That is, underlines under a CJK are more than
twice
+ * as wide as narrow characters in case of letter spacing. Plus, if necessary, the
width
+ * is increased to span under the entire glyph. Vertical position is not affected by
+ * line spacing. */
+
+ int line_height, left, right;
/* use height (not width) so underline and ibeam will
* be equally visible */
- line_height = (int) (((float) height) * m_cursor_aspect_ratio + 0.5);
- line_height = CLAMP (line_height, VTE_LINE_WIDTH, height);
+ line_height = (int) (((float) (m_char_ascent + m_char_descent)) *
m_cursor_aspect_ratio + 0.5);
+ line_height = CLAMP (line_height, VTE_LINE_WIDTH, m_char_ascent + m_char_descent);
+
+ left = m_char_padding.left;
+ right = item.columns * m_cell_width - m_char_padding.right;
+
+ if (cell && cell->c != 0 && cell->c != ' ' && cell->c != '\t') {
+ int l, r;
+ _vte_draw_get_char_edges (m_draw, cell->c, cell->attr.columns, style, &l,
&r);
+ left = MIN(left, l);
+ right = MAX(right, r);
+ }
_vte_draw_fill_rectangle(m_draw,
- x, y + height - line_height,
- cursor_width, line_height,
+ x + left, y + m_cell_height - m_char_padding.bottom -
line_height,
+ right - left, line_height,
&bg, VTE_DRAW_OPAQUE);
break;
}
case VTE_CURSOR_SHAPE_BLOCK:
+ /* Include the spacings in the cursor, see bug 781479 comments 39-44.
+ * Make the cursor even wider if the glyph is wider. */
+
+ cursor_width = item.columns * width;
+ if (cell && cell->c != 0 && cell->c != ' ' && cell->c != '\t') {
+ int r;
+ _vte_draw_get_char_edges (m_draw, cell->c, cell->attr.columns, style, NULL,
&r);
+ cursor_width = MAX(cursor_width, r);
+ }
if (focus) {
/* just reverse the character under the cursor */
@@ -9669,7 +9754,7 @@ VteTerminalPrivate::paint_cursor()
cursor_width, height,
&bg, VTE_DRAW_OPAQUE);
- if (cell && cell->c != 0 && cell->c != ' ') {
+ if (cell && cell->c != 0 && cell->c != ' ' && cell->c != '\t') {
draw_cells(
&item, 1,
fore, back, TRUE, FALSE,
diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h
index 2cb3024..755dc8e 100644
--- a/src/vte/vteterminal.h
+++ b/src/vte/vteterminal.h
@@ -209,6 +209,18 @@ void vte_terminal_set_font_scale(VteTerminal *terminal,
_VTE_PUBLIC
gdouble vte_terminal_get_font_scale(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+_VTE_PUBLIC
+void vte_terminal_set_cell_width_scale(VteTerminal *terminal,
+ double scale) _VTE_GNUC_NONNULL(1);
+_VTE_PUBLIC
+double vte_terminal_get_cell_width_scale(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+
+_VTE_PUBLIC
+void vte_terminal_set_cell_height_scale(VteTerminal *terminal,
+ double scale) _VTE_GNUC_NONNULL(1);
+_VTE_PUBLIC
+double vte_terminal_get_cell_height_scale(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+
/* Set various on-off settings. */
_VTE_PUBLIC
void vte_terminal_set_audible_bell(VteTerminal *terminal,
diff --git a/src/vtedefines.hh b/src/vtedefines.hh
index 3afc532..89e6416 100644
--- a/src/vtedefines.hh
+++ b/src/vtedefines.hh
@@ -95,6 +95,8 @@
#define VTE_FONT_SCALE_MIN (.25)
#define VTE_FONT_SCALE_MAX (4.)
+#define VTE_CELL_SCALE_MIN (1.)
+#define VTE_CELL_SCALE_MAX (2.)
/* Maximum length of a URI in the OSC 8 escape sequences. There's no de jure limit,
* 2000-ish the de facto standard, and Internet Explorer supports 2083.
diff --git a/src/vtedraw.cc b/src/vtedraw.cc
index c713d44..c99d02e 100644
--- a/src/vtedraw.cc
+++ b/src/vtedraw.cc
@@ -241,7 +241,7 @@ struct font_info {
struct unistr_info ascii_unistr_info[128];
GHashTable *other_unistr_info;
- /* cell metrics */
+ /* cell metrics as taken from the font, not yet scaled by cell_{width,height}_scale */
gint width, height, ascent;
/* reusable string for UTF-8 conversion */
@@ -399,7 +399,6 @@ font_info_measure_font (struct font_info *info)
* info for them */
font_info_cache_ascii (info);
-
if (info->height == 0) {
info->height = PANGO_PIXELS_CEIL (logical.height);
}
@@ -412,7 +411,6 @@ font_info_measure_font (struct font_info *info)
info, info->width, info->height, info->ascent);
}
-
static struct font_info *
font_info_allocate (PangoContext *context)
{
@@ -755,6 +753,9 @@ guint _vte_draw_get_style(gboolean bold, gboolean italic) {
struct _vte_draw {
struct font_info *fonts[4];
+ /* cell metrics, already adjusted by cell_{width,height}_scale */
+ int cell_width, cell_height;
+ GtkBorder char_spacing;
cairo_t *cr;
};
@@ -836,7 +837,9 @@ _vte_draw_clear (struct _vte_draw *draw, gint x, gint y, gint width, gint height
void
_vte_draw_set_text_font (struct _vte_draw *draw,
GtkWidget *widget,
- const PangoFontDescription *fontdesc)
+ const PangoFontDescription *fontdesc,
+ double cell_width_scale,
+ double cell_height_scale)
{
PangoFontDescription *bolddesc = NULL;
PangoFontDescription *italicdesc = NULL;
@@ -896,33 +899,73 @@ _vte_draw_set_text_font (struct _vte_draw *draw,
font_info_destroy (draw->fonts[bold]);
draw->fonts[bold] = draw->fonts[normal];
}
+
+ /* Apply letter spacing and line spacing. */
+ draw->cell_width = draw->fonts[VTE_DRAW_NORMAL]->width * cell_width_scale;
+ draw->char_spacing.left = (draw->cell_width - draw->fonts[VTE_DRAW_NORMAL]->width) / 2;
+ draw->char_spacing.right = (draw->cell_width - draw->fonts[VTE_DRAW_NORMAL]->width + 1) / 2;
+ draw->cell_height = draw->fonts[VTE_DRAW_NORMAL]->height * cell_height_scale;
+ draw->char_spacing.top = (draw->cell_height - draw->fonts[VTE_DRAW_NORMAL]->height + 1) / 2;
+ draw->char_spacing.bottom = (draw->cell_height - draw->fonts[VTE_DRAW_NORMAL]->height) / 2;
}
void
_vte_draw_get_text_metrics(struct _vte_draw *draw,
- gint *width, gint *height, gint *ascent)
+ int *cell_width, int *cell_height,
+ int *char_ascent, int *char_descent,
+ GtkBorder *char_spacing)
{
g_return_if_fail (draw->fonts[VTE_DRAW_NORMAL] != NULL);
- if (width)
- *width = draw->fonts[VTE_DRAW_NORMAL]->width;
- if (height)
- *height = draw->fonts[VTE_DRAW_NORMAL]->height;
- if (ascent)
- *ascent = draw->fonts[VTE_DRAW_NORMAL]->ascent;
+ if (cell_width)
+ *cell_width = draw->cell_width;
+ if (cell_height)
+ *cell_height = draw->cell_height;
+ if (char_ascent)
+ *char_ascent = draw->fonts[VTE_DRAW_NORMAL]->ascent;
+ if (char_descent)
+ *char_descent = draw->fonts[VTE_DRAW_NORMAL]->height - draw->fonts[VTE_DRAW_NORMAL]->ascent;
+ if (char_spacing)
+ *char_spacing = draw->char_spacing;
}
-int
-_vte_draw_get_char_width (struct _vte_draw *draw, vteunistr c, int columns,
- guint style)
+/* Stores the left and right edges of the given glyph, relative to the cell's left edge. */
+void
+_vte_draw_get_char_edges (struct _vte_draw *draw, vteunistr c, int columns, guint style,
+ int *left, int *right)
{
- struct unistr_info *uinfo;
+ int l, w, normal_width, fits_width;
+
+ if (G_UNLIKELY (draw->fonts[VTE_DRAW_NORMAL] == NULL)) {
+ if (left)
+ *left = 0;
+ if (right)
+ *right = 0;
+ return;
+ }
- g_return_val_if_fail (draw->fonts[VTE_DRAW_NORMAL] != NULL, 0);
+ w = font_info_get_unistr_info (draw->fonts[style], c)->width;
+ normal_width = draw->fonts[VTE_DRAW_NORMAL]->width * columns;
+ fits_width = draw->cell_width * columns;
+
+ if (G_LIKELY (w <= normal_width)) {
+ /* The regular case: The glyph is not wider than one (CJK: two) regular character(s).
+ * Align to the left, after applying half (CJK: one) letter spacing. */
+ l = draw->char_spacing.left + (columns == 2 ? draw->char_spacing.right : 0);
+ } else if (G_UNLIKELY (w <= fits_width)) {
+ /* Slightly wider glyph, but still fits in the cell (spacing included). This case can
+ * only happen with nonzero letter spacing. Center the glyph in the cell(s). */
+ l = (fits_width - w) / 2;
+ } else {
+ /* Even wider glyph: doesn't fit in the cell. Align at left and overflow on the right. */
+ l = 0;
+ }
- uinfo = font_info_get_unistr_info (draw->fonts[style], c);
- return uinfo->width;
+ if (left)
+ *left = l;
+ if (right)
+ *right = l + w;
}
gboolean
@@ -947,9 +990,9 @@ _vte_draw_unichar_is_local_graphic(vteunistr c)
static void
_vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color::rgb const* fg,
gint x, gint y,
- gint column_width, gint columns, gint row_height)
+ gint font_width, gint columns, gint font_height)
{
- gint width, xcenter, xright, ycenter, ybottom;
+ gint width, height, xcenter, xright, ycenter, ybottom;
int upper_half, lower_half, left_half, right_half;
int light_line_width, heavy_line_width;
double adjust;
@@ -957,9 +1000,10 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
cairo_save (cr);
- width = column_width * columns;
- upper_half = row_height / 2;
- lower_half = row_height - upper_half;
+ width = draw->cell_width * columns;
+ height = draw->cell_height;
+ upper_half = height / 2;
+ lower_half = height - upper_half;
left_half = width / 2;
right_half = width - left_half;
@@ -971,7 +1015,8 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
k_eights; \
})
- light_line_width = column_width / 5;
+ /* Exclude the spacing for line width computation. */
+ light_line_width = font_width / 5;
light_line_width = MAX (light_line_width, 1);
if (c >= 0x2550 && c <= 0x256c) {
@@ -983,7 +1028,7 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
xcenter = x + left_half;
ycenter = y + upper_half;
xright = x + width;
- ybottom = y + row_height;
+ ybottom = y + height;
switch (c) {
@@ -1110,7 +1155,7 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
upper_half - light_line_width / 2,
upper_half - light_line_width / 2 + light_line_width,
upper_half - heavy_line_width / 2 + heavy_line_width,
- row_height};
+ height};
int xi, yi;
cairo_set_line_width(cr, 0);
for (yi = 4; yi >= 0; yi--) {
@@ -1145,7 +1190,7 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
const guint v = c - 0x2500;
int size, line_width;
- size = (v & 2) ? row_height : width;
+ size = (v & 2) ? height : width;
switch (v >> 2) {
case 1: /* triple dash */
@@ -1178,7 +1223,7 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
if (v & 2) {
cairo_move_to(cr, xcenter + adjust, y);
- cairo_line_to(cr, xcenter + adjust, y + row_height);
+ cairo_line_to(cr, xcenter + adjust, y + height);
} else {
cairo_move_to(cr, x, ycenter + adjust);
cairo_line_to(cr, x + width, ycenter + adjust);
@@ -1202,7 +1247,7 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
adjust = (line_width & 1) ? .5 : 0.;
cairo_set_line_width(cr, line_width);
- radius = (column_width + 2) / 3;
+ radius = (font_width + 2) / 3;
radius = MAX(radius, heavy_line_width);
if (v & 2) {
@@ -1273,9 +1318,9 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
/* Use the number of eights from the top, so that
* U+2584 aligns with U+2596..U+259f.
*/
- const int h = EIGHTS (row_height, 8 - v);
+ const int h = EIGHTS (height, 8 - v);
- cairo_rectangle(cr, x, y + h, width, row_height - h);
+ cairo_rectangle(cr, x, y + h, width, height - h);
cairo_fill (cr);
break;
}
@@ -1295,13 +1340,13 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
*/
const int w = EIGHTS (width, 8 - v);
- cairo_rectangle(cr, x, y, w, row_height);
+ cairo_rectangle(cr, x, y, w, height);
cairo_fill (cr);
break;
}
case 0x2590: /* right half block */
- cairo_rectangle(cr, x + left_half, y, right_half, row_height);
+ cairo_rectangle(cr, x + left_half, y, right_half, height);
cairo_fill (cr);
break;
@@ -1313,13 +1358,13 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
fg->green / 65535.,
fg->blue / 65535.,
(c - 0x2590) / 4.);
- cairo_rectangle(cr, x, y, width, row_height);
+ cairo_rectangle(cr, x, y, width, height);
cairo_fill (cr);
break;
case 0x2594: /* upper one eighth block */
{
- const int h = EIGHTS (row_height, 1); /* Align with U+2587 */
+ const int h = EIGHTS (height, 1); /* Align with U+2587 */
cairo_rectangle(cr, x, y, width, h);
cairo_fill (cr);
break;
@@ -1328,7 +1373,7 @@ _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, vte::color:
case 0x2595: /* right one eighth block */
{
const int w = EIGHTS (width, 7); /* Align with U+2589 */
- cairo_rectangle(cr, x + w, y, width - w, row_height);
+ cairo_rectangle(cr, x + w, y, width - w, height);
cairo_fill (cr);
break;
}
@@ -1421,10 +1466,13 @@ _vte_draw_text_internal (struct _vte_draw *draw,
for (i = 0; i < n_requests; i++) {
vteunistr c = requests[i].c;
- int x = requests[i].x;
- int y = requests[i].y + font->ascent;
struct unistr_info *uinfo = font_info_get_unistr_info (font, c);
union unistr_font_info *ufi = &uinfo->ufi;
+ int x, y;
+
+ _vte_draw_get_char_edges(draw, c, requests[i].columns, style, &x, NULL);
+ x += requests[i].x;
+ y = requests[i].y + draw->char_spacing.top + font->ascent;
if (_vte_draw_unichar_is_local_graphic(c)) {
_vte_draw_terminal_draw_graphic(draw, c, color,
diff --git a/src/vtedraw.hh b/src/vtedraw.hh
index c45cb53..505d71b 100644
--- a/src/vtedraw.hh
+++ b/src/vtedraw.hh
@@ -63,11 +63,14 @@ void _vte_draw_clear(struct _vte_draw *draw,
void _vte_draw_set_text_font(struct _vte_draw *draw,
GtkWidget *widget,
- const PangoFontDescription *fontdesc);
+ const PangoFontDescription *fontdesc,
+ double cell_width_scale, double cell_height_scale);
void _vte_draw_get_text_metrics(struct _vte_draw *draw,
- gint *width, gint *height, gint *ascent);
-int _vte_draw_get_char_width(struct _vte_draw *draw, vteunistr c, int columns,
- guint style);
+ int *cell_width, int *cell_height,
+ int *char_ascent, int *char_descent,
+ GtkBorder *char_spacing);
+void _vte_draw_get_char_edges (struct _vte_draw *draw, vteunistr c, int columns, guint style,
+ int *left, int *right);
gboolean _vte_draw_has_bold (struct _vte_draw *draw, guint style);
void _vte_draw_text(struct _vte_draw *draw,
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index 6e9924b..1098bf5 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -436,6 +436,12 @@ vte_terminal_get_property (GObject *object,
case PROP_BACKSPACE_BINDING:
g_value_set_enum (value, impl->m_backspace_binding);
break;
+ case PROP_CELL_HEIGHT_SCALE:
+ g_value_set_double (value, vte_terminal_get_cell_height_scale (terminal));
+ break;
+ case PROP_CELL_WIDTH_SCALE:
+ g_value_set_double (value, vte_terminal_get_cell_width_scale (terminal));
+ break;
case PROP_CJK_AMBIGUOUS_WIDTH:
g_value_set_int (value, vte_terminal_get_cjk_ambiguous_width (terminal));
break;
@@ -537,6 +543,12 @@ vte_terminal_set_property (GObject *object,
case PROP_BACKSPACE_BINDING:
vte_terminal_set_backspace_binding (terminal, (VteEraseBinding)g_value_get_enum
(value));
break;
+ case PROP_CELL_HEIGHT_SCALE:
+ vte_terminal_set_cell_height_scale (terminal, g_value_get_double (value));
+ break;
+ case PROP_CELL_WIDTH_SCALE:
+ vte_terminal_set_cell_width_scale (terminal, g_value_get_double (value));
+ break;
case PROP_CJK_AMBIGUOUS_WIDTH:
vte_terminal_set_cjk_ambiguous_width (terminal, g_value_get_int (value));
break;
@@ -889,8 +901,10 @@ vte_terminal_class_init(VteTerminalClass *klass)
* @width: the new character cell width
* @height: the new character cell height
*
- * Emitted whenever selection of a new font causes the values of the
- * %char_width or %char_height fields to change.
+ * Emitted whenever the cell size changes, e.g. due to a change in
+ * font, font-scale or cell-width/height-scale.
+ *
+ * Note that this signal should rather be called "cell-size-changed".
*/
signals[SIGNAL_CHAR_SIZE_CHANGED] =
g_signal_new(I_("char-size-changed"),
@@ -1302,6 +1316,34 @@ vte_terminal_class_init(VteTerminalClass *klass)
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY));
/**
+ * VteTerminal:cell-height-scale:
+ *
+ * Scale factor for the cell height, to increase line spacing. (The font's height is not affected.)
+ *
+ * Since: 0.52
+ */
+ pspecs[PROP_CELL_HEIGHT_SCALE] =
+ g_param_spec_double ("cell-height-scale", NULL, NULL,
+ VTE_CELL_SCALE_MIN,
+ VTE_CELL_SCALE_MAX,
+ 1.,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY));
+
+ /**
+ * VteTerminal:cell-width-scale:
+ *
+ * Scale factor for the cell width, to increase letter spacing. (The font's width is not affected.)
+ *
+ * Since: 0.52
+ */
+ pspecs[PROP_CELL_WIDTH_SCALE] =
+ g_param_spec_double ("cell-width-scale", NULL, NULL,
+ VTE_CELL_SCALE_MIN,
+ VTE_CELL_SCALE_MAX,
+ 1.,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY));
+
+ /**
* VteTerminal:cjk-ambiguous-width:
*
* This setting controls whether ambiguous-width characters are narrow or wide
@@ -2971,6 +3013,9 @@ vte_terminal_set_backspace_binding(VteTerminal *terminal,
* @terminal: a #VteTerminal
*
* Returns: the height of a character cell
+ *
+ * Note that this method should rather be called vte_terminal_get_cell_height,
+ * because the return value takes cell-height-scale into account.
*/
glong
vte_terminal_get_char_height(VteTerminal *terminal)
@@ -2984,6 +3029,9 @@ vte_terminal_get_char_height(VteTerminal *terminal)
* @terminal: a #VteTerminal
*
* Returns: the width of a character cell
+ *
+ * Note that this method should rather be called vte_terminal_get_cell_width,
+ * because the return value takes cell-width-scale into account.
*/
glong
vte_terminal_get_char_width(VteTerminal *terminal)
@@ -3511,6 +3559,84 @@ vte_terminal_set_font_scale(VteTerminal *terminal,
g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_FONT_SCALE]);
}
+/**
+ * vte_terminal_get_cell_height_scale:
+ * @terminal: a #VteTerminal
+ *
+ * Returns: the terminal's cell height scale
+ *
+ * Since: 0.52
+ */
+double
+vte_terminal_get_cell_height_scale(VteTerminal *terminal)
+{
+ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), 1.);
+
+ return IMPL(terminal)->m_cell_height_scale;
+}
+
+/**
+ * vte_terminal_set_cell_height_scale:
+ * @terminal: a #VteTerminal
+ * @scale: the cell height scale
+ *
+ * Sets the terminal's cell height scale to @scale.
+ *
+ * This can be used to increase the line spacing. (The font's height is not affected.)
+ * Valid values go from 1.0 (default) to 2.0 ("double spacing").
+ *
+ * Since: 0.52
+ */
+void
+vte_terminal_set_cell_height_scale(VteTerminal *terminal,
+ double scale)
+{
+ g_return_if_fail(VTE_IS_TERMINAL(terminal));
+
+ scale = CLAMP(scale, VTE_CELL_SCALE_MIN, VTE_CELL_SCALE_MAX);
+ if (IMPL(terminal)->set_cell_height_scale(scale))
+ g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_CELL_HEIGHT_SCALE]);
+}
+
+/**
+ * vte_terminal_get_cell_width_scale:
+ * @terminal: a #VteTerminal
+ *
+ * Returns: the terminal's cell width scale
+ *
+ * Since: 0.52
+ */
+double
+vte_terminal_get_cell_width_scale(VteTerminal *terminal)
+{
+ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), 1.);
+
+ return IMPL(terminal)->m_cell_width_scale;
+}
+
+/**
+ * vte_terminal_set_cell_width_scale:
+ * @terminal: a #VteTerminal
+ * @scale: the cell width scale
+ *
+ * Sets the terminal's cell width scale to @scale.
+ *
+ * This can be used to increase the letter spacing. (The font's width is not affected.)
+ * Valid values go from 1.0 (default) to 2.0.
+ *
+ * Since: 0.52
+ */
+void
+vte_terminal_set_cell_width_scale(VteTerminal *terminal,
+ double scale)
+{
+ g_return_if_fail(VTE_IS_TERMINAL(terminal));
+
+ scale = CLAMP(scale, VTE_CELL_SCALE_MIN, VTE_CELL_SCALE_MAX);
+ if (IMPL(terminal)->set_cell_width_scale(scale))
+ g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_CELL_WIDTH_SCALE]);
+}
+
/* Just some arbitrary minimum values */
#define MIN_COLUMNS (16)
#define MIN_ROWS (2)
diff --git a/src/vtegtk.hh b/src/vtegtk.hh
index d1ade94..ea970a4 100644
--- a/src/vtegtk.hh
+++ b/src/vtegtk.hh
@@ -67,6 +67,8 @@ enum {
PROP_ALLOW_HYPERLINK,
PROP_AUDIBLE_BELL,
PROP_BACKSPACE_BINDING,
+ PROP_CELL_HEIGHT_SCALE,
+ PROP_CELL_WIDTH_SCALE,
PROP_CJK_AMBIGUOUS_WIDTH,
PROP_CURSOR_BLINK_MODE,
PROP_CURSOR_SHAPE,
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index e9c18b4..bff62e0 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -573,9 +573,27 @@ public:
PangoFontDescription *m_fontdesc;
gdouble m_font_scale;
gboolean m_fontdirty;
- glong m_char_ascent;
- glong m_char_descent;
- /* dimensions of character cells */
+
+ /* First, the dimensions of ASCII characters are measured. The result
+ * could probably be called char_{width,height} or font_{width,height}
+ * but these aren't stored directly here, not to accidentally be confused
+ * with m_cell_{width_height}. The values are stored in vtedraw's font_info.
+ *
+ * Then in case of nondefault m_cell_{width,height}_scale an additional
+ * m_char_padding is added, resulting in m_cell_{width,height} which are
+ * hence potentially larger than the characters. This is to implement
+ * line spacing and letter spacing, primarly for accessibility (bug 781479).
+ *
+ * Char width/height, if really needed, can be computed by subtracting
+ * the char padding from the cell dimensions. Char height can also be
+ * reconstructed from m_char_{ascent,descent}, one of which is redundant,
+ * stored for convenience only.
+ */
+ long m_char_ascent;
+ long m_char_descent;
+ double m_cell_width_scale;
+ double m_cell_height_scale;
+ GtkBorder m_char_padding;
glong m_cell_width;
glong m_cell_height;
@@ -1002,10 +1020,11 @@ public:
void ensure_font();
void update_font();
- void apply_font_metrics(int width,
- int height,
- int ascent,
- int descent);
+ void apply_font_metrics(int cell_width,
+ int cell_height,
+ int char_ascent,
+ int char_descent,
+ GtkBorder char_spacing);
void refresh_size();
void screen_set_size(VteScreen *screen_,
@@ -1197,6 +1216,8 @@ public:
bool set_allow_hyperlink(bool setting);
bool set_backspace_binding(VteEraseBinding binding);
bool set_background_alpha(double alpha);
+ bool set_cell_height_scale(double scale);
+ bool set_cell_width_scale(double scale);
bool set_cjk_ambiguous_width(int width);
void set_color_background(vte::color::rgb const &color);
void set_color_bold(vte::color::rgb const& color);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]