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:
parent
e36f076eb7
commit
4f50d964e5
9 changed files with 106 additions and 26 deletions
|
@ -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
|
||||
|
|
6
etc/NEWS
6
etc/NEWS
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
15
src/nsterm.m
15
src/nsterm.m
|
@ -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)
|
||||
|
|
|
@ -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. */
|
||||
|
|
16
src/xfaces.c
16
src/xfaces.c
|
@ -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");
|
||||
|
|
25
src/xterm.c
25
src/xterm.c
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue