From 4f50d964e51bbe5219f40df4353f4314c7ade985 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 10 Jan 2022 19:26:46 +0800 Subject: [PATCH] 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'. --- doc/lispref/display.texi | 7 +++++-- etc/NEWS | 6 ++++++ lisp/cus-face.el | 20 ++++++++++++++------ src/dispextern.h | 9 +++++++++ src/haikuterm.c | 14 +++++++++++--- src/nsterm.m | 15 +++++++++++---- src/w32term.c | 20 ++++++++++++++++---- src/xfaces.c | 16 ++++++++++++++++ src/xterm.c | 25 ++++++++++++++++++------- 9 files changed, 106 insertions(+), 26 deletions(-) diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index f191c400abc..2183d35e7e1 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -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 diff --git a/etc/NEWS b/etc/NEWS index 4ff2325ff08..d256342f439 100644 --- a/etc/NEWS +++ b/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 diff --git a/lisp/cus-face.el b/lisp/cus-face.el index fa6d940d7c1..e905a455570 100644 --- a/lisp/cus-face.el +++ b/lisp/cus-face.el @@ -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" diff --git a/src/dispextern.h b/src/dispextern.h index 954992a0ec2..368507732ce 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -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; diff --git a/src/haikuterm.c b/src/haikuterm.c index b6d105bf2b6..2304f718c31 100644 --- a/src/haikuterm.c +++ b/src/haikuterm.c @@ -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 diff --git a/src/nsterm.m b/src/nsterm.m index a15dc47a226..4f60cc737da 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -3265,7 +3265,11 @@ ns_draw_text_decoration (struct glyph_string *s, struct face *face, /* 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 @@ ns_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)); + 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 @@ ns_draw_text_decoration (struct glyph_string *s, struct face *face, /* 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 @@ ns_draw_text_decoration (struct glyph_string *s, struct face *face, 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) diff --git a/src/w32term.c b/src/w32term.c index 700c492cc37..78777f153c0 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -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. */ diff --git a/src/xfaces.c b/src/xfaces.c index 3fd31b7f225..8064d47c947 100644 --- a/src/xfaces.c +++ b/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"); diff --git a/src/xterm.c b/src/xterm.c index 321b4c5ab66..e2b09938a27 100644 --- a/src/xterm.c +++ b/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; -- 2.39.2