diff options
Diffstat (limited to 'src/interface-gtk/teco-gtk-label.gob')
-rw-r--r-- | src/interface-gtk/teco-gtk-label.gob | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/interface-gtk/teco-gtk-label.gob b/src/interface-gtk/teco-gtk-label.gob new file mode 100644 index 0000000..2167dbc --- /dev/null +++ b/src/interface-gtk/teco-gtk-label.gob @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012-2021 Robin Haberkorn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +requires 2.0.20 + +%ctop{ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include <glib/gprintf.h> + +#include <gdk/gdk.h> + +#include "sciteco.h" +#include "string-utils.h" + +#define GDK_TO_PANGO_COLOR(X) ((guint16)((X) * G_MAXUINT16)) +%} + +%h{ +#include <gtk/gtk.h> +%} + +class Teco:Gtk:Label from Gtk:Label { + private PangoColor fg; + private guint16 fg_alpha; + private PangoColor bg; + private guint16 bg_alpha; + + private teco_string_t string + destroy { + teco_string_clear(&VAR); + }; + + override (Gtk:Widget) void + style_updated(Gtk:Widget *widget) + { + Self *self = SELF(widget); + + PARENT_HANDLER(widget); + GtkStyleContext *style = gtk_widget_get_style_context(widget); + + GdkRGBA normal_color; + gtk_style_context_get_color(style, GTK_STATE_NORMAL, &normal_color); + selfp->bg.red = GDK_TO_PANGO_COLOR(normal_color.red); + selfp->bg.green = GDK_TO_PANGO_COLOR(normal_color.green); + selfp->bg.blue = GDK_TO_PANGO_COLOR(normal_color.blue); + selfp->bg_alpha = GDK_TO_PANGO_COLOR(normal_color.alpha); + + /* + * If Pango does not support transparent foregrounds, + * it will at least use a high-contrast foreground. + * + * NOTE: It would be very hard to get an appropriate background + * color even if Gtk supports it since the label itself may + * not have one but one of its parents. + * + * FIXME: We may want to honour the background color, + * so we can at least get decent reverse text when setting + * the background color in the CSS. + */ + selfp->fg.red = normal_color.red > 0.5 ? 0 : G_MAXUINT16; + selfp->fg.green = normal_color.green > 0.5 ? 0 : G_MAXUINT16; + selfp->fg.blue = normal_color.blue > 0.5 ? 0 : G_MAXUINT16; + /* try hard to get a transparent foreground anyway */ + selfp->fg_alpha = 0; + + /* + * The widget might be styled after the text has been set on it, + * we must recreate the Pango attributes. + */ + if (selfp->string.len > 0) { + PangoAttrList *attribs = NULL; + g_autofree gchar *plaintext = NULL; + + self_parse_string(selfp->string.data, selfp->string.len, + &selfp->fg, selfp->fg_alpha, + &selfp->bg, selfp->bg_alpha, + &attribs, &plaintext); + + gtk_label_set_attributes(GTK_LABEL(self), attribs); + pango_attr_list_unref(attribs); + + g_assert(!g_strcmp0(plaintext, gtk_label_get_text(GTK_LABEL(self)))); + } + } + + public GtkWidget * + new(const gchar *str, gssize len) + { + Self *widget = GET_NEW; + + self_set_text(widget, str, len); + + return GTK_WIDGET(widget); + } + + private void + add_highlight_attribs(Pango:Attr:List *attribs, + Pango:Color *fg, guint16 fg_alpha, + Pango:Color *bg, guint16 bg_alpha, + guint index, gsize len) + { + PangoAttribute *attr; + + /* + * NOTE: Transparent foreground do not seem to work, + * even in Pango v1.38. + * Perhaps, this has been fixed in later versions. + */ +#if PANGO_VERSION_CHECK(1,38,0) + attr = pango_attr_foreground_alpha_new(fg_alpha); + attr->start_index = index; + attr->end_index = index + len; + pango_attr_list_insert(attribs, attr); + + attr = pango_attr_background_alpha_new(bg_alpha); + attr->start_index = index; + attr->end_index = index + len; + pango_attr_list_insert(attribs, attr); +#endif + + attr = pango_attr_foreground_new(fg->red, fg->green, fg->blue); + attr->start_index = index; + attr->end_index = index + len; + pango_attr_list_insert(attribs, attr); + + attr = pango_attr_background_new(bg->red, bg->green, bg->blue); + attr->start_index = index; + attr->end_index = index + len; + pango_attr_list_insert(attribs, attr); + } + + public void + parse_string(const gchar *str, gssize len, + Pango:Color *fg, guint16 fg_alpha, + Pango:Color *bg, guint16 bg_alpha, + Pango:Attr:List **attribs, gchar **text) + { + if (len < 0) + len = strlen(str); + + /* + * Approximate size of unformatted text. + */ + gsize text_len = 1; /* for trailing 0 */ + for (gint i = 0; i < len; i++) + text_len += TECO_IS_CTL(str[i]) ? 3 : 1; + + *attribs = pango_attr_list_new(); + *text = g_malloc(text_len); + + gint index = 0; + while (len > 0) { + /* + * NOTE: This mapping is similar to + * teco_view_set_presentations() + */ + switch (*str) { + case '\e': + self_add_highlight_attribs(*attribs, + fg, fg_alpha, + bg, bg_alpha, + index, 1); + (*text)[index++] = '$'; + break; + case '\r': + self_add_highlight_attribs(*attribs, + fg, fg_alpha, + bg, bg_alpha, + index, 2); + (*text)[index++] = 'C'; + (*text)[index++] = 'R'; + break; + case '\n': + self_add_highlight_attribs(*attribs, + fg, fg_alpha, + bg, bg_alpha, + index, 2); + (*text)[index++] = 'L'; + (*text)[index++] = 'F'; + break; + case '\t': + self_add_highlight_attribs(*attribs, + fg, fg_alpha, + bg, bg_alpha, + index, 3); + (*text)[index++] = 'T'; + (*text)[index++] = 'A'; + (*text)[index++] = 'B'; + break; + default: + if (TECO_IS_CTL(*str)) { + self_add_highlight_attribs(*attribs, + fg, fg_alpha, + bg, bg_alpha, + index, 2); + (*text)[index++] = '^'; + (*text)[index++] = TECO_CTL_ECHO(*str); + } else { + (*text)[index++] = *str; + } + break; + } + + str++; + len--; + } + + /* null-terminate generated text */ + (*text)[index] = '\0'; + } + + public void + set_text(self, const gchar *str, gssize len) + { + teco_string_clear(&selfp->string); + teco_string_init(&selfp->string, str, len < 0 ? strlen(str) : len); + + g_autofree gchar *plaintext = NULL; + + if (selfp->string.len > 0) { + PangoAttrList *attribs = NULL; + + self_parse_string(selfp->string.data, selfp->string.len, + &selfp->fg, selfp->fg_alpha, + &selfp->bg, selfp->bg_alpha, + &attribs, &plaintext); + + gtk_label_set_attributes(GTK_LABEL(self), attribs); + pango_attr_list_unref(attribs); + } + + gtk_label_set_text(GTK_LABEL(self), plaintext); + } +} |