Allow controlling the underline position of faces

* doc/lispref/display.texi (Face Attributes): Document new
`:position' property of the `:underline' attribute.
* etc/NEWS: Announce new property.
* lisp/cus-face.el (custom-face-attributes): Implement
customization for new face attribute.
* src/dispextern.h (struct face): New fields
`underline_pixels_above_descent_line' and
`underline_at_descent_line_p'.
* src/haikuterm.c (haiku_draw_text_decoration):
* src/nsterm.m (ns_draw_text_decoration):
* src/w32term.c (w32_draw_glyph_string):
* src/xterm.c (x_draw_glyph_string): Respect new face fields.

* src/xfaces.c (realize_gui_face): Handle new `:position'
keyword.
(syms_of_xfaces): New symbol `:position'.
This commit is contained in:
Po Lu 2022-01-10 19:26:46 +08:00
parent e36f076eb7
commit 4f50d964e5
9 changed files with 106 additions and 26 deletions

View file

@ -2555,13 +2555,16 @@ Underline with the foreground color of the face.
@item @var{color}
Underline in color @var{color}, a string specifying a color.
@item @code{(:color @var{color} :style @var{style})}
@item @code{(:color @var{color} :style @var{style} :position @var{position}}
@var{color} is either a string, or the symbol @code{foreground-color},
meaning the foreground color of the face. Omitting the attribute
@code{:color} means to use the foreground color of the face.
@var{style} should be a symbol @code{line} or @code{wave}, meaning to
use a straight or wavy line. Omitting the attribute @code{:style}
means to use a straight line.
means to use a straight line. @var{position}, if non-nil, means to
display the underline at the descent of the text, instead of at the
baseline level. If it is a number, then it specifies the amount of
pixels above the descent to display the underline.
@end table
@cindex overlined text

View file

@ -1278,6 +1278,12 @@ This is a subcategory of 'file-error', and is signaled when some file
operation fails because the OS doesn't allow Emacs to access a file or
a directory.
+++
** The ':underline' face attribute now accepts a new property.
The property ':position' now specifies the position of the underline
when used as part of a property list specification for the
':underline' attribute.
* Changes in Emacs 29.1 on Non-Free Operating Systems

View file

@ -141,7 +141,12 @@
(const :format "" :value :style)
(choice :tag "Style"
(const :tag "Line" line)
(const :tag "Wave" wave))))
(const :tag "Wave" wave))
(const :format "" :value :position)
(choice :tag "Position"
(const :tag "At Default Position" nil)
(const :tag "At Bottom Of Text" t)
(integer :tag "Pixels Above Bottom Of Text"))))
;; filter to make value suitable for customize
(lambda (real-value)
(and real-value
@ -151,18 +156,21 @@
'foreground-color))
(style
(or (and (consp real-value) (plist-get real-value :style))
'line)))
(list :color color :style style))))
'line))
(position (and (consp real-value)
(plist-get real-value :style))))
(list :color color :style style :position position))))
;; filter to make customized-value suitable for storing
(lambda (cus-value)
(and cus-value
(let ((color (plist-get cus-value :color))
(style (plist-get cus-value :style)))
(cond ((eq style 'line)
(style (plist-get cus-value :style))
(position (plist-get cus-value :position)))
(cond ((and (eq style 'line) (not position))
;; Use simple value for default style
(if (eq color 'foreground-color) t color))
(t
`(:color ,color :style ,style)))))))
`(:color ,color :style ,style :position ,position)))))))
(:overline
(choice :tag "Overline"

View file

@ -1720,6 +1720,12 @@ struct face
int box_vertical_line_width;
int box_horizontal_line_width;
/* The amount of pixels above the descent line the underline should
be displayed. It does not take effect unless
`underline_at_descent_line_p` is t. */
int underline_pixels_above_descent_line;
/* Type of box drawn. A value of FACE_NO_BOX means no box is drawn
around text in this face. A value of FACE_SIMPLE_BOX means a box
of width box_line_width is drawn in color box_color. A value of
@ -1753,6 +1759,9 @@ struct face
bool_bf strike_through_color_defaulted_p : 1;
bool_bf box_color_defaulted_p : 1;
/* True means the underline should be drawn at the descent line. */
bool_bf underline_at_descent_line_p : 1;
/* TTY appearances. Colors are found in `lface' with empty color
string meaning the default color of the TTY. */
bool_bf tty_bold_p : 1;

View file

@ -613,7 +613,12 @@ haiku_draw_text_decoration (struct glyph_string *s, struct face *face,
unsigned long thickness, position;
int y;
if (s->prev && s->prev && s->prev->hl == DRAW_MOUSE_FACE)
if (s->prev
&& s->prev->face->underline == FACE_UNDER_LINE
&& (s->prev->face->underline_at_descent_line_p
== s->face->underline_at_descent_line_p)
&& (s->prev->face->underline_pixels_above_descent_line
== s->face->underline_pixels_above_descent_line))
{
struct face *prev_face = s->prev->face;
@ -644,7 +649,8 @@ haiku_draw_text_decoration (struct glyph_string *s, struct face *face,
val = (WINDOW_BUFFER_LOCAL_VALUE
(Qx_underline_at_descent_line, s->w));
underline_at_descent_line
= !(NILP (val) || EQ (val, Qunbound));
= (!(NILP (val) || EQ (val, Qunbound))
|| s->face->underline_at_descent_line_p);
val = (WINDOW_BUFFER_LOCAL_VALUE
(Qx_use_underline_position_properties, s->w));
@ -657,7 +663,9 @@ haiku_draw_text_decoration (struct glyph_string *s, struct face *face,
else
thickness = 1;
if (underline_at_descent_line)
position = (s->height - thickness) - (s->ybase - s->y);
position = ((s->height - thickness)
- (s->ybase - s->y)
- s->face->underline_pixels_above_descent_line);
else
{
/* Get the underline position. This is the

View file

@ -3265,7 +3265,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
/* If the prev was underlined, match its appearance. */
if (s->prev
&& s->prev->face->underline == FACE_UNDER_LINE
&& s->prev->underline_thickness > 0)
&& s->prev->underline_thickness > 0
&& (s->prev->face->underline_at_descent_line_p
== s->face->underline_at_descent_line_p)
&& (s->prev->face->underline_pixels_above_descent_line
== s->face->underline_pixels_above_descent_line))
{
thickness = s->prev->underline_thickness;
position = s->prev->underline_position;
@ -3286,7 +3290,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
val = (WINDOW_BUFFER_LOCAL_VALUE
(Qx_underline_at_descent_line, s->w));
underline_at_descent_line = !(NILP (val) || EQ (val, Qunbound));
underline_at_descent_line = (!(NILP (val) || EQ (val, Qunbound))
|| s->face->underline_at_descent_line_p);
val = (WINDOW_BUFFER_LOCAL_VALUE
(Qx_use_underline_position_properties, s->w));
@ -3299,7 +3304,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
/* Determine the offset of underlining from the baseline. */
if (underline_at_descent_line)
position = descent - thickness;
position = (descent - thickness
- s->face->underline_pixels_above_descent_line);
else if (use_underline_position_properties
&& font && font->underline_position >= 0)
position = font->underline_position;
@ -3308,7 +3314,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
else
position = minimum_offset;
position = max (position, minimum_offset);
if (!s->face->underline_pixels_above_descent_line)
position = max (position, minimum_offset);
/* Ensure underlining is not cropped. */
if (descent <= position)

View file

@ -2564,7 +2564,11 @@ w32_draw_glyph_string (struct glyph_string *s)
int y;
if (s->prev
&& s->prev->face->underline == FACE_UNDER_LINE)
&& s->prev->face->underline == FACE_UNDER_LINE
&& (s->prev->face->underline_at_descent_line_p
== s->face->underline_at_descent_line_p)
&& (s->prev->face->underline_pixels_above_descent_line
== s->face->underline_pixels_above_descent_line))
{
/* We use the same underline style as the previous one. */
thickness = s->prev->underline_thickness;
@ -2587,7 +2591,8 @@ w32_draw_glyph_string (struct glyph_string *s)
val = (WINDOW_BUFFER_LOCAL_VALUE
(Qx_underline_at_descent_line, s->w));
underline_at_descent_line
= !(NILP (val) || EQ (val, Qunbound));
= (!(NILP (val) || EQ (val, Qunbound))
|| s->face->underline_at_descent_line_p);
val = (WINDOW_BUFFER_LOCAL_VALUE
(Qx_use_underline_position_properties, s->w));
@ -2601,7 +2606,9 @@ w32_draw_glyph_string (struct glyph_string *s)
thickness = 1;
if (underline_at_descent_line
|| !font)
position = (s->height - thickness) - (s->ybase - s->y);
position = ((s->height - thickness)
- (s->ybase - s->y)
- s->face->underline_pixels_above_descent_line);
else
{
/* Get the underline position. This is the
@ -2619,7 +2626,12 @@ w32_draw_glyph_string (struct glyph_string *s)
else
position = (font->descent + 1) / 2;
}
position = max (position, minimum_offset);
if (!(s->face->underline_at_descent_line_p
/* Ignore minimum_offset if the amount of pixels
was explictly specified. */
&& s->face->underline_pixels_above_descent_line))
position = max (position, minimum_offset);
}
/* Check the sanity of thickness and position. We should
avoid drawing underline out of the current line area. */

View file

@ -6041,6 +6041,8 @@ realize_gui_face (struct face_cache *cache, Lisp_Object attrs[LFACE_VECTOR_SIZE]
face->underline = FACE_UNDER_LINE;
face->underline_defaulted_p = true;
face->underline_color = 0;
face->underline_at_descent_line_p = false;
face->underline_pixels_above_descent_line = 0;
}
else if (STRINGP (underline))
{
@ -6050,12 +6052,16 @@ realize_gui_face (struct face_cache *cache, Lisp_Object attrs[LFACE_VECTOR_SIZE]
face->underline_color
= load_color (f, face, underline,
LFACE_UNDERLINE_INDEX);
face->underline_at_descent_line_p = false;
face->underline_pixels_above_descent_line = 0;
}
else if (NILP (underline))
{
face->underline = FACE_NO_UNDERLINE;
face->underline_defaulted_p = false;
face->underline_color = 0;
face->underline_at_descent_line_p = false;
face->underline_pixels_above_descent_line = 0;
}
else if (CONSP (underline))
{
@ -6064,6 +6070,8 @@ realize_gui_face (struct face_cache *cache, Lisp_Object attrs[LFACE_VECTOR_SIZE]
face->underline = FACE_UNDER_LINE;
face->underline_color = 0;
face->underline_defaulted_p = true;
face->underline_at_descent_line_p = false;
face->underline_pixels_above_descent_line = 0;
/* FIXME? This is also not robust about checking the precise form.
See comments in Finternal_set_lisp_face_attribute. */
@ -6100,6 +6108,13 @@ realize_gui_face (struct face_cache *cache, Lisp_Object attrs[LFACE_VECTOR_SIZE]
else if (EQ (value, Qwave))
face->underline = FACE_UNDER_WAVE;
}
else if (EQ (keyword, QCposition))
{
face->underline_at_descent_line_p = !NILP (value);
if (FIXNATP (value))
face->underline_pixels_above_descent_line = XFIXNAT (value);
}
}
}
@ -6915,6 +6930,7 @@ syms_of_xfaces (void)
DEFSYM (QCcolor, ":color");
DEFSYM (QCline_width, ":line-width");
DEFSYM (QCstyle, ":style");
DEFSYM (QCposition, ":position");
DEFSYM (Qline, "line");
DEFSYM (Qwave, "wave");
DEFSYM (Qreleased_button, "released-button");

View file

@ -4144,8 +4144,12 @@ x_draw_glyph_string (struct glyph_string *s)
unsigned long thickness, position;
int y;
if (s->prev &&
s->prev->face->underline == FACE_UNDER_LINE)
if (s->prev
&& s->prev->face->underline == FACE_UNDER_LINE
&& (s->prev->face->underline_at_descent_line_p
== s->face->underline_at_descent_line_p)
&& (s->prev->face->underline_pixels_above_descent_line
== s->face->underline_pixels_above_descent_line))
{
/* We use the same underline style as the previous one. */
thickness = s->prev->underline_thickness;
@ -4168,7 +4172,8 @@ x_draw_glyph_string (struct glyph_string *s)
val = (WINDOW_BUFFER_LOCAL_VALUE
(Qx_underline_at_descent_line, s->w));
underline_at_descent_line
= !(NILP (val) || EQ (val, Qunbound));
= (!(NILP (val) || EQ (val, Qunbound))
|| s->face->underline_at_descent_line_p);
val = (WINDOW_BUFFER_LOCAL_VALUE
(Qx_use_underline_position_properties, s->w));
@ -4181,7 +4186,9 @@ x_draw_glyph_string (struct glyph_string *s)
else
thickness = 1;
if (underline_at_descent_line)
position = (s->height - thickness) - (s->ybase - s->y);
position = ((s->height - thickness)
- (s->ybase - s->y)
- s->face->underline_pixels_above_descent_line);
else
{
/* Get the underline position. This is the
@ -4201,12 +4208,16 @@ x_draw_glyph_string (struct glyph_string *s)
else
position = minimum_offset;
}
position = max (position, minimum_offset);
/* Ignore minimum_offset if the amount of pixels was
explictly specified. */
if (!s->face->underline_pixels_above_descent_line)
position = max (position, minimum_offset);
}
/* Check the sanity of thickness and position. We should
avoid drawing underline out of the current line area. */
if (s->y + s->height <= s->ybase + position)
position = (s->height - 1) - (s->ybase - s->y);
if (s->y + s->height <= s->ybase + position)
position = (s->height - 1) - (s->ybase - s->y);
if (s->y + s->height < s->ybase + position + thickness)
thickness = (s->y + s->height) - (s->ybase + position);
s->underline_thickness = thickness;