From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Michal Nazarewicz Newsgroups: gmane.emacs.devel Subject: [RFC] Add :invisible face attribute Date: Wed, 18 Dec 2024 17:08:12 +0100 Message-ID: <20241218160813.31108-1-mina86@mina86.com> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="10801"; mail-complaints-to="usenet@ciao.gmane.io" To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Wed Dec 18 17:09:19 2024 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1tNwbv-0002gc-Is for ged-emacs-devel@m.gmane-mx.org; Wed, 18 Dec 2024 17:09:19 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tNwbB-0006B9-Dm; Wed, 18 Dec 2024 11:08:33 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tNwb8-0006Au-Gh for emacs-devel@gnu.org; Wed, 18 Dec 2024 11:08:30 -0500 Original-Received: from mail-ej1-x631.google.com ([2a00:1450:4864:20::631]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tNwb5-0001Qa-Nn for emacs-devel@gnu.org; Wed, 18 Dec 2024 11:08:30 -0500 Original-Received: by mail-ej1-x631.google.com with SMTP id a640c23a62f3a-aa670ffe302so1222156666b.2 for ; Wed, 18 Dec 2024 08:08:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1734538103; x=1735142903; darn=gnu.org; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:sender:from:to:cc:subject:date:message-id:reply-to; bh=fFS5niP58eL2khZrWQX7i74bV41pgMF+igoyLeyeRmU=; b=IWgyt7UcU9Ge401zPDY0t2WHMxoDmGslZX0fhSYjuhESZPAbrZ025/X3qcWanyfKR6 Nv5FB4qnSq5uWdrjW6Ibnt9SKo6qxSdfXNDGnBC7kGx24HGGHASPLAJzQt1bOC/nI1w7 l63fyb45xCYxfSFZsdixKOeEZVnw4F0S2PZA8YdKtMSdnWhS4H5psT94fMWvNR1G1FDg G+JBwdltozVGyErBnc+NNxrUFnfLsNmDRs0AWfynuEIM2n4q+Av89jHicsubXYtExRew j+oOeXg0jsB5TWLFvY6mmpWsMz/HLQXPa0JAmk54cfteqINnIKlLAsm5xU9r+udqOJUu etjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1734538103; x=1735142903; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:sender:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=fFS5niP58eL2khZrWQX7i74bV41pgMF+igoyLeyeRmU=; b=Vm7V/WuXdnZ/BY218mRlp/D1ojy2ikjU77r9foSxhBGCzKY/neod3lFXS/V6eGTohM jGTSWP/LV98ONGpYa6twuvJN5HOE5qdcABZ6m/3fEZQRS47nhSInPGcTb2b57kcrcwTv JTf5/AnVtQP9lizzmthAnWFUt3/P48xoOvYc3Omh5r2lBYraxqxcCj2SI1N2xtvXIHdZ GU1KhXsgc6FwHsAdSQ4VNdURDKhKX0QlzXKyNhNbFKCy1JboxwE94HwQwJ8QJliYNJQx hgBd3Yfa8NH3QJzKteBtiOE6kye1F9GymJUxLz4HCbi4NcDKjU1b7UCYFHWvEuwQ+wX1 da3A== X-Gm-Message-State: AOJu0Yzfiv5YTZJfjHjkLDKOi3YjIf0/C3En2qfv2KhV+DwOprcdrj8+ F0rfeoiYOSr3+olCjKcVvcPQxAKXuKC8sJHF/uWnBFJu+QZ4PKmHTzXmAQ== X-Gm-Gg: ASbGncsJGCSY4nAVvM137C87q+Zk4YUTjOr9MONPMc6jKrwEaFs3GQL5WWG0VGdxVwY 0N5sABx5YJrw30DxiM0Fe9RZvaoBtHbZ7OXIm/mzpCXHcBM3v/YSs1cQXqlqdVV3OQG4s5Xpeq/ 7fruhRRI2f5siuafNilPRpAYNqnEu/05JVdpMHVxx2myvFw/UuJo1FIDjq77ba452buVgQk2zbh bWTrNwV6Hj+O9eWwjETR7qOnMsBk1otm3c6UGKrUzC15mURmAce7TGJS493T3i2vN6Qj7/7fVkI TQm6xnMETDVIzavrhASx X-Google-Smtp-Source: AGHT+IEq4UeqOb0FAoLSZqEy7sb5maeg8eVQsmdMun9DQQHRSDQEDy2lqGHwCPouOuNJQWjn3PVLcQ== X-Received: by 2002:a17:906:6a25:b0:aa6:6a52:970 with SMTP id a640c23a62f3a-aabf46feea8mr294162866b.1.1734538101566; Wed, 18 Dec 2024 08:08:21 -0800 (PST) Original-Received: from erwin.mina86.com (87-205-2-211.static.ip.netia.com.pl. [87.205.2.211]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-aab963599d9sm584394266b.107.2024.12.18.08.08.20 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2024 08:08:20 -0800 (PST) X-Mailer: git-send-email 2.45.2 Received-SPF: pass client-ip=2a00:1450:4864:20::631; envelope-from=mnazarewicz@gmail.com; helo=mail-ej1-x631.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FORGED_FROMDOMAIN=0.001, FREEMAIL_FROM=0.001, HEADER_FROM_DIFFERENT_DOMAINS=0.248, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:326661 Archived-At: Introduce :invisible face attribute which makes foreground to be the same as background rendering the text invisible; or when :invert-video is also in effect, background is the same as foreground. Use it in Org mode for org-hide face eliminting the need for org-find-invisible-foreground function. This also simplifies auto-dim-other-buffers NonGNU ELPA package removing the need to configure a separate hide face for org-hide remap. To observe the atribute in action, set ‘org-hide-leading-stars’ option, open NEWS file and enable org-mode. The initial stars in section headings are rendered using the new attribute rather than Org mode needing to explicitly match foregroun to background. --- lisp/cus-face.el | 6 ++++ lisp/faces.el | 32 +++++++++++++++-- lisp/org/org-faces.el | 4 +-- lisp/org/org.el | 18 ---------- src/dispextern.h | 1 + src/xfaces.c | 80 +++++++++++++++++++++++++++++++++++++------ 6 files changed, 108 insertions(+), 33 deletions(-) diff --git a/lisp/cus-face.el b/lisp/cus-face.el index 478092c30cb..b7000747393 100644 --- a/lisp/cus-face.el +++ b/lisp/cus-face.el @@ -251,6 +251,12 @@ custom-face-attributes (const :tag "Off" nil) (const :tag "On" t))) + (:invisible + (choice :tag "Invisible" + :help-echo "Control whether text should be rendered in the same color as background." + (const :tag "Off" nil) + (const :tag "On" t))) + (:foreground (color :tag "Foreground" :help-echo "Set foreground color (name or #RRGGBB hex spec).")) diff --git a/lisp/faces.el b/lisp/faces.el index 05df685c679..6451664d791 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -375,6 +375,7 @@ face-x-resources (:underline (".attributeUnderline" . "Face.AttributeUnderline")) (:inverse-video (".attributeInverse" . "Face.AttributeInverse")) (:extend (".attributeExtend" . "Face.AttributeExtend")) + (:invisible (".attributeInvisible" . "Face.AttributeInvisible")) (:stipple (".attributeStipple" . "Face.AttributeStipple") (".attributeBackgroundPixmap" . "Face.AttributeBackgroundPixmap")) @@ -615,6 +616,15 @@ face-inverse-video-p (eq (face-attribute face :inverse-video frame inherit) t)) +(defun face-invisible-video-p (face &optional frame inherit) + "Return non-nil if FACE specifies a non-nil invisible. +If the optional argument FRAME is given, report on face FACE in that frame. +If FRAME is t, report on the defaults for face FACE (for new frames). +If FRAME is omitted or nil, use the selected frame. +Optional argument INHERIT is passed to `face-attribute'." + (eq (face-attribute face :invisible frame inherit) t)) + + (defun face-bold-p (face &optional frame inherit) "Return non-nil if the font of FACE is bold on FRAME. If the optional argument FRAME is given, report on face FACE in that frame. @@ -822,6 +832,12 @@ set-face-attribute VALUE specifies whether characters in FACE should be displayed in inverse video. VALUE must be one of t or nil. +`:invisible' + +VALUE specifies whether characters in FACE should be displayed in +foreground color the same as background color rendering them +invisible. VALUE must be one of t or nil. + `:stipple' If VALUE is a string, it must be the name of a file of pixmap data. @@ -1038,6 +1054,17 @@ set-face-inverse-video (define-obsolete-function-alias 'set-face-inverse-video-p 'set-face-inverse-video "24.4") +(defun set-face-invisible (face invisible-p &optional frame) + "Specify whether face FACE is in invisible. +INVISIBLE-P non-nil means FACE displays explicitly in invisible. +INVISIBLE-P nil means FACE explicitly is not in invisible. +FRAME nil or not specified means change face on all frames. +Use `set-face-attribute' to \"unspecify\" the invisible attribute." + (interactive + (let ((list (read-face-and-attribute :invisible))) + (list (car list) (if (cadr list) t)))) + (set-face-attribute face frame :invisible invisible-p)) + (defun set-face-bold (face bold-p &optional frame) "Specify whether face FACE is bold. BOLD-P non-nil means FACE should explicitly display bold. @@ -1215,7 +1242,7 @@ face-valid-attribute-values (:slant (mapcar (lambda (x) (cons (symbol-name (aref x 1)) (aref x 1))) font-slant-table)) - ((or :inverse-video :extend) + ((or :inverse-video :extend :invisible) (mapcar (lambda (x) (cons (symbol-name x) x)) (internal-lisp-face-attribute-values attribute))) ((or :underline :overline :strike-through :box) @@ -1265,6 +1292,7 @@ face-attribute-name-alist (:strike-through . "strike-through") (:box . "box") (:inverse-video . "inverse-video display") + (:invisible . "invisible text") (:foreground . "foreground color") (:background . "background color") (:stipple . "background stipple") @@ -1668,7 +1696,7 @@ face-spec-reset-face (append '(:underline nil :overline nil :strike-through nil :box nil :inverse-video nil :stipple nil :inherit nil - :extend nil) + :extend nil :invisible nil) ;; `display-graphic-p' is unavailable when running ;; temacs, prior to loading frame.el. (when (fboundp 'display-graphic-p) diff --git a/lisp/org/org-faces.el b/lisp/org/org-faces.el index 21b23b641ca..ad6af62526d 100644 --- a/lisp/org/org-faces.el +++ b/lisp/org/org-faces.el @@ -40,9 +40,7 @@ org-default "Face used for default text." :group 'org-faces) -(defface org-hide - '((((background light)) (:foreground "white")) - (((background dark)) (:foreground "black"))) +(defface org-hide '((t :invisible t)) "Face used to hide leading stars in headlines. The foreground color of this face should be equal to the background color of the frame." diff --git a/lisp/org/org.el b/lisp/org/org.el index 4166738c162..782c2818c82 100644 --- a/lisp/org/org.el +++ b/lisp/org/org.el @@ -5100,10 +5100,6 @@ org-mode ;; Activate `org-table-header-line-mode' (when org-table-header-line-p (org-table-header-line-mode 1)) - ;; Try to set `org-hide' face correctly. - (let ((foreground (org-find-invisible-foreground))) - (when foreground - (set-face-foreground 'org-hide foreground))) ;; Set face extension as requested. (org--set-faces-extend '(org-block-begin-line org-block-end-line) org-fontify-whole-block-delimiter-line) @@ -5138,20 +5134,6 @@ org-mode-transpose-word-syntax-table (abbrev-table-put org-mode-abbrev-table :parents (list text-mode-abbrev-table))) -(defun org-find-invisible-foreground () - (let ((candidates (remove - "unspecified-bg" - (nconc - (list (face-background 'default) - (face-background 'org-default)) - (mapcar - (lambda (alist) - (when (boundp alist) - (cdr (assq 'background-color (symbol-value alist))))) - '(default-frame-alist initial-frame-alist window-system-default-frame-alist)) - (list (face-foreground 'org-hide)))))) - (car (remove nil candidates)))) - (defun org-current-time (&optional rounding-minutes past) "Current time, possibly rounded to ROUNDING-MINUTES. When ROUNDING-MINUTES is not an integer, fall back on the car of diff --git a/src/dispextern.h b/src/dispextern.h index 9df6eaf623a..2adb2686188 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -1700,6 +1700,7 @@ #define FONT_TOO_HIGH(ft) \ LFACE_FONTSET_INDEX, LFACE_DISTANT_FOREGROUND_INDEX, LFACE_EXTEND_INDEX, + LFACE_INVISIBLE_INDEX, LFACE_VECTOR_SIZE }; diff --git a/src/xfaces.c b/src/xfaces.c index f6264802fa4..1b89bd4eb04 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -1378,6 +1378,7 @@ load_face_colors (struct frame *f, struct face *face, { Lisp_Object fg, bg, dfg; Emacs_Color xfg, xbg; + bool invisible; bg = attrs[LFACE_BACKGROUND_INDEX]; fg = attrs[LFACE_FOREGROUND_INDEX]; @@ -1391,6 +1392,11 @@ load_face_colors (struct frame *f, struct face *face, bg = tmp; } + /* Set foreground to background if invisible. */ + invisible = EQ (attrs[LFACE_INVISIBLE_INDEX], Qt); + if (invisible) + fg = bg; + /* Check for support for foreground, not for background because face_color_supported_p is smart enough to know that grays are "supported" as background because we are supposed to use stipple @@ -1405,14 +1411,17 @@ load_face_colors (struct frame *f, struct face *face, face->background = load_color2 (f, face, bg, LFACE_BACKGROUND_INDEX, &xbg); face->foreground = load_color2 (f, face, fg, LFACE_FOREGROUND_INDEX, &xfg); - dfg = attrs[LFACE_DISTANT_FOREGROUND_INDEX]; - if (!NILP (dfg) && !UNSPECIFIEDP (dfg) - && color_distance (&xbg, &xfg) < face_near_same_color_threshold) + if (!invisible) { - if (EQ (attrs[LFACE_INVERSE_INDEX], Qt)) - face->background = load_color (f, face, dfg, LFACE_BACKGROUND_INDEX); - else - face->foreground = load_color (f, face, dfg, LFACE_FOREGROUND_INDEX); + dfg = attrs[LFACE_DISTANT_FOREGROUND_INDEX]; + if (!NILP (dfg) && !UNSPECIFIEDP (dfg) + && color_distance (&xbg, &xfg) < face_near_same_color_threshold) + { + if (EQ (attrs[LFACE_INVERSE_INDEX], Qt)) + face->background = load_color (f, face, dfg, LFACE_BACKGROUND_INDEX); + else + face->foreground = load_color (f, face, dfg, LFACE_FOREGROUND_INDEX); + } } } @@ -1803,6 +1812,7 @@ #define LFACE_FONTSET(LFACE) AREF (LFACE, LFACE_FONTSET_INDEX) #define LFACE_EXTEND(LFACE) AREF (LFACE, LFACE_EXTEND_INDEX) #define LFACE_DISTANT_FOREGROUND(LFACE) \ AREF (LFACE, LFACE_DISTANT_FOREGROUND_INDEX) +#define LFACE_INVISIBLE(LFACE) AREF (LFACE, LFACE_INVISIBLE_INDEX) /* True if LFACE is a Lisp face. A Lisp face is a vector of size LFACE_VECTOR_SIZE which has the symbol `face' in slot 0. */ @@ -1912,6 +1922,10 @@ check_lface_attrs (Lisp_Object attrs[LFACE_VECTOR_SIZE]) || RESET_P (attrs[LFACE_FONTSET_INDEX]) || NILP (attrs[LFACE_FONTSET_INDEX])); #endif + eassert (UNSPECIFIEDP (attrs[LFACE_INVISIBLE_INDEX]) + || IGNORE_DEFFACE_P (attrs[LFACE_INVISIBLE_INDEX]) + || RESET_P (attrs[LFACE_INVISIBLE_INDEX]) + || SYMBOLP (attrs[LFACE_INVISIBLE_INDEX])); } @@ -2911,6 +2925,13 @@ merge_face_ref (struct window *w, else err = true; } + else if (EQ (keyword, QCinvisible)) + { + if (EQ (value, Qt) || NILP (value)) + to[LFACE_INVISIBLE_INDEX] = value; + else + err = true; + } else err = true; @@ -3484,6 +3505,19 @@ DEFUN ("internal-set-lisp-face-attribute", Finternal_set_lisp_face_attribute, old_value = LFACE_EXTEND (lface); ASET (lface, LFACE_EXTEND_INDEX, value); } + else if (EQ (attr, QCinvisible)) + { + if (!UNSPECIFIEDP (value) + && !IGNORE_DEFFACE_P (value) + && !RESET_P (value)) + { + CHECK_SYMBOL (value); + if (!EQ (value, Qt) && !NILP (value)) + signal_error ("Invalid invisible face attribute value", value); + } + old_value = LFACE_INVISIBLE (lface); + ASET (lface, LFACE_INVISIBLE_INDEX, value); + } else if (EQ (attr, QCforeground)) { HANDLE_INVALID_NIL_VALUE (QCforeground, face); @@ -3977,7 +4011,8 @@ DEFUN ("internal-set-lisp-face-attribute-from-resource", else if (EQ (attr, QCweight) || EQ (attr, QCslant) || EQ (attr, QCwidth)) value = intern (SSDATA (value)); else if (EQ (attr, QCinverse_video) - || EQ (attr, QCextend)) + || EQ (attr, QCextend) + || EQ (attr, QCinvisible)) value = face_boolean_x_resource_value (value, true); else if (EQ (attr, QCunderline) || EQ (attr, QCoverline) @@ -4203,6 +4238,8 @@ DEFUN ("internal-get-lisp-face-attribute", Finternal_get_lisp_face_attribute, value = LFACE_INHERIT (lface); else if (EQ (keyword, QCextend)) value = LFACE_EXTEND (lface); + else if (EQ (keyword, QCinvisible)) + value = LFACE_INVISIBLE (lface); else if (EQ (keyword, QCfont)) value = LFACE_FONT (lface); else if (EQ (keyword, QCfontset)) @@ -4231,7 +4268,8 @@ DEFUN ("internal-lisp-face-attribute-values", if (EQ (attr, QCunderline) || EQ (attr, QCoverline) || EQ (attr, QCstrike_through) || EQ (attr, QCinverse_video) - || EQ (attr, QCextend)) + || EQ (attr, QCextend) + || EQ (attr, QCinvisible)) result = list2 (Qt, Qnil); return result; @@ -5360,7 +5398,10 @@ gui_supports_face_attributes_p (struct frame *f, def_attrs[LFACE_STRIKE_THROUGH_INDEX])) || (!UNSPECIFIEDP (lattrs[LFACE_BOX_INDEX]) && face_attr_equal_p (lattrs[LFACE_BOX_INDEX], - def_attrs[LFACE_BOX_INDEX]))) + def_attrs[LFACE_BOX_INDEX])) + || (!UNSPECIFIEDP (lattrs[LFACE_INVISIBLE_INDEX]) + && face_attr_equal_p (lattrs[LFACE_INVISIBLE_INDEX], + def_attrs[LFACE_INVISIBLE_INDEX]))) return false; /* Check font-related attributes, as those are the most commonly @@ -5549,6 +5590,17 @@ tty_supports_face_attributes_p (struct frame *f, test_caps |= TTY_CAP_STRIKE_THROUGH; } + /* invisible video */ + val = attrs[LFACE_INVERSE_INDEX]; + if (!UNSPECIFIEDP (val)) + { + if (face_attr_equal_p (val, def_attrs[LFACE_INVERSE_INDEX])) + return false; /* same as default */ + /* Always supported since it just copies bg to fg. XXX: Is this + true? I actually have no idea what this function is supposed + to do. */ + } + /* Color testing. */ /* Check if foreground color is close enough. */ @@ -5968,6 +6020,9 @@ realize_default_face (struct frame *f) if (UNSPECIFIEDP (LFACE_INVERSE (lface))) ASET (lface, LFACE_INVERSE_INDEX, Qnil); + if (UNSPECIFIEDP (LFACE_INVISIBLE (lface))) + ASET (lface, LFACE_INVISIBLE_INDEX, Qnil); + if (UNSPECIFIEDP (LFACE_FOREGROUND (lface))) { /* This function is called so early that colors are not yet @@ -6716,6 +6771,9 @@ realize_tty_face (struct face_cache *cache, face->background = tem; } + if (!NILP (attrs[LFACE_INVISIBLE_INDEX])) + face->foreground = face->background; + if (tty_suppress_bold_inverse_default_colors_p && face->tty_bold_p && face->background == FACE_TTY_DEFAULT_FG_COLOR @@ -7342,6 +7400,7 @@ init_xfaces (void) face_attr_sym[LFACE_FONTSET_INDEX] = QCfontset; face_attr_sym[LFACE_DISTANT_FOREGROUND_INDEX] = QCdistant_foreground; face_attr_sym[LFACE_EXTEND_INDEX] = QCextend; + face_attr_sym[LFACE_INVISIBLE_INDEX] = QCinvisible; } void @@ -7381,6 +7440,7 @@ syms_of_xfaces (void) DEFSYM (QCbox, ":box"); DEFSYM (QCinherit, ":inherit"); DEFSYM (QCextend, ":extend"); + DEFSYM (QCinvisible, ":invisible"); /* Symbols used for Lisp face attribute values. */ DEFSYM (QCcolor, ":color"); -- 2.45.2