[devhelp] Copy TeplInfoBar from the Tepl library
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [devhelp] Copy TeplInfoBar from the Tepl library
- Date: Sat, 10 Feb 2018 15:45:44 +0000 (UTC)
commit d3b2d7ead21482cb441dbac7670c196bf79ef4b6
Author: Sébastien Wilmet <swilmet gnome org>
Date: Sat Feb 10 15:26:51 2018 +0100
Copy TeplInfoBar from the Tepl library
It will be used internally by DhTab.
The code is copied from Tepl:
https://wiki.gnome.org/Projects/Tepl
(it's a library above GtkSourceView, so it's not great to depend on it
in Devhelp).
Modifications:
- in tepl-info-bar.h to remove
the check "Only <tepl/tepl.h> can be included directly."
- add a message on top of both files to say that it's a copy (if it was
not already obvious).
- in tepl-info-bar.c change the first /** to /* to avoid a warning when
gtk-doc compiles the doc.
Not all the functions will be used, but I think it's better to keep all
the code, to make it easier to update it to a new version.
docs/reference/Makefile.am | 3 +-
po/POTFILES.in | 1 +
src/Makefile.am | 2 +
src/tepl-info-bar.c | 450 ++++++++++++++++++++++++++++++++++++++++++++
src/tepl-info-bar.h | 70 +++++++
5 files changed, 525 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 89b27a2..54daeb3 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -57,7 +57,8 @@ IGNORE_HFILES = \
dh-tab.h \
dh-util.h \
dh-web-view.h \
- dh-window.h
+ dh-window.h \
+ tepl-info-bar.h
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8e890f0..04f5e20 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -26,3 +26,4 @@ src/dh-window.c
src/dh-window.ui
src/help-overlay.ui
src/menus.ui
+src/tepl-info-bar.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 6dabb3c..e84e3c9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -72,6 +72,7 @@ app_headers = \
dh-tab.h \
dh-web-view.h \
dh-window.h \
+ tepl-info-bar.h \
$(NULL)
app_c_files = \
@@ -81,6 +82,7 @@ app_c_files = \
dh-tab.c \
dh-web-view.c \
dh-window.c \
+ tepl-info-bar.c \
$(NULL)
BUILT_SOURCES = \
diff --git a/src/tepl-info-bar.c b/src/tepl-info-bar.c
new file mode 100644
index 0000000..af09fda
--- /dev/null
+++ b/src/tepl-info-bar.c
@@ -0,0 +1,450 @@
+/* Code copied from Tepl:
+ * https://wiki.gnome.org/Projects/Tepl
+ * Do not modify, modify upstream and then copy it back here.
+ */
+
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2016, 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * Tepl 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tepl-info-bar.h"
+
+/*
+ * SECTION:info-bar
+ * @Short_description: Subclass of GtkInfoBar
+ * @Title: TeplInfoBar
+ *
+ * #TeplInfoBar is a subclass of #GtkInfoBar with a vertical action area and
+ * functions to ease the creation of info bars.
+ */
+
+typedef struct _TeplInfoBarPrivate TeplInfoBarPrivate;
+
+struct _TeplInfoBarPrivate
+{
+ /* Left: icon. Right: content_vgrid. */
+ GtkGrid *content_hgrid;
+
+ /* Contains primary/secondary messages. */
+ GtkGrid *content_vgrid;
+
+ guint close_button_added : 1;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (TeplInfoBar, tepl_info_bar, GTK_TYPE_INFO_BAR)
+
+static void
+tepl_info_bar_response (GtkInfoBar *gtk_info_bar,
+ gint response_id)
+{
+ TeplInfoBar *info_bar = TEPL_INFO_BAR (gtk_info_bar);
+ TeplInfoBarPrivate *priv = tepl_info_bar_get_instance_private (info_bar);
+
+ if (response_id == GTK_RESPONSE_CLOSE &&
+ priv->close_button_added)
+ {
+ gtk_widget_destroy (GTK_WIDGET (info_bar));
+
+ /* No need to chain up, the widget is destroyed. */
+ return;
+ }
+
+ if (GTK_INFO_BAR_CLASS (tepl_info_bar_parent_class)->response != NULL)
+ {
+ GTK_INFO_BAR_CLASS (tepl_info_bar_parent_class)->response (gtk_info_bar,
+ response_id);
+ }
+}
+
+static void
+tepl_info_bar_class_init (TeplInfoBarClass *klass)
+{
+ GtkInfoBarClass *info_bar_class = GTK_INFO_BAR_CLASS (klass);
+
+ info_bar_class->response = tepl_info_bar_response;
+}
+
+static void
+tepl_info_bar_init (TeplInfoBar *info_bar)
+{
+ TeplInfoBarPrivate *priv;
+ GtkWidget *action_area;
+ GtkWidget *content_area;
+
+ priv = tepl_info_bar_get_instance_private (info_bar);
+
+ _tepl_info_bar_set_size_request (GTK_INFO_BAR (info_bar));
+
+ /* Change the buttons orientation to be vertical.
+ *
+ * With a small window, if 3 or more buttons are shown horizontally,
+ * there is a ridiculous amount of space for the text. And it can get
+ * worse since the button labels are translatable, in other languages it
+ * can take even more place. If the buttons are packed vertically, there
+ * is no problem.
+ *
+ * The GtkInfoBar implementation comes originally from gedit, and the
+ * action area was vertical. Then IIRC a GNOME designer decided that the
+ * action area must be horizontal, making the gedit info bars look
+ * weird... So, come back to the original design.
+ */
+ action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar));
+ if (GTK_IS_ORIENTABLE (action_area))
+ {
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (action_area),
+ GTK_ORIENTATION_VERTICAL);
+ }
+ else
+ {
+ g_warning ("Failed to set vertical orientation to the GtkInfoBar action area.");
+ }
+
+ /* hgrid */
+ priv->content_hgrid = GTK_GRID (gtk_grid_new ());
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->content_hgrid),
+ GTK_ORIENTATION_HORIZONTAL);
+ gtk_grid_set_column_spacing (priv->content_hgrid, 16);
+ gtk_widget_show (GTK_WIDGET (priv->content_hgrid));
+
+ content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
+ gtk_container_add (GTK_CONTAINER (content_area),
+ GTK_WIDGET (priv->content_hgrid));
+
+ /* vgrid */
+ priv->content_vgrid = GTK_GRID (gtk_grid_new ());
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->content_vgrid),
+ GTK_ORIENTATION_VERTICAL);
+ gtk_grid_set_row_spacing (priv->content_vgrid, 6);
+ gtk_widget_show (GTK_WIDGET (priv->content_vgrid));
+
+ gtk_container_add (GTK_CONTAINER (priv->content_hgrid),
+ GTK_WIDGET (priv->content_vgrid));
+}
+
+/**
+ * tepl_info_bar_new:
+ *
+ * Returns: a new #TeplInfoBar.
+ * Since: 1.0
+ */
+TeplInfoBar *
+tepl_info_bar_new (void)
+{
+ return g_object_new (TEPL_TYPE_INFO_BAR, NULL);
+}
+
+/**
+ * tepl_info_bar_new_simple:
+ * @msg_type: the message type.
+ * @primary_msg: the primary message.
+ * @secondary_msg: (nullable): the secondary message, or %NULL.
+ *
+ * Creates a new #TeplInfoBar with an icon (depending on @msg_type), a primary
+ * message and a secondary message.
+ *
+ * Returns: a new #TeplInfoBar.
+ * Since: 2.0
+ */
+TeplInfoBar *
+tepl_info_bar_new_simple (GtkMessageType msg_type,
+ const gchar *primary_msg,
+ const gchar *secondary_msg)
+{
+ TeplInfoBar *info_bar;
+
+ g_return_val_if_fail (primary_msg != NULL, NULL);
+
+ info_bar = tepl_info_bar_new ();
+
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), msg_type);
+ tepl_info_bar_add_icon (info_bar);
+ tepl_info_bar_add_primary_message (info_bar, primary_msg);
+
+ if (secondary_msg != NULL)
+ {
+ tepl_info_bar_add_secondary_message (info_bar, secondary_msg);
+ }
+
+ return info_bar;
+}
+
+static const gchar *
+get_icon_name (TeplInfoBar *info_bar)
+{
+ GtkMessageType msg_type;
+
+ msg_type = gtk_info_bar_get_message_type (GTK_INFO_BAR (info_bar));
+
+ switch (msg_type)
+ {
+ case GTK_MESSAGE_INFO:
+ return "dialog-information";
+
+ case GTK_MESSAGE_WARNING:
+ return "dialog-warning";
+
+ case GTK_MESSAGE_QUESTION:
+ return "dialog-question";
+
+ case GTK_MESSAGE_ERROR:
+ return "dialog-error";
+
+ case GTK_MESSAGE_OTHER:
+ default:
+ /* No icon */
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * tepl_info_bar_add_icon:
+ * @info_bar: a #TeplInfoBar.
+ *
+ * Adds an icon on the left, determined by the message type. So before calling
+ * this function, gtk_info_bar_set_message_type() must have been called.
+ *
+ * The icon is not updated when the message type changes. Another #TeplInfoBar
+ * must be created in that case.
+ *
+ * Since: 2.0
+ */
+void
+tepl_info_bar_add_icon (TeplInfoBar *info_bar)
+{
+ TeplInfoBarPrivate *priv;
+ const gchar *icon_name;
+ GtkWidget *image;
+
+ g_return_if_fail (TEPL_IS_INFO_BAR (info_bar));
+
+ priv = tepl_info_bar_get_instance_private (info_bar);
+
+ icon_name = get_icon_name (info_bar);
+ if (icon_name == NULL)
+ {
+ return;
+ }
+
+ image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_DIALOG);
+ gtk_widget_set_valign (image, GTK_ALIGN_START);
+ gtk_widget_show (image);
+
+ gtk_grid_attach_next_to (priv->content_hgrid,
+ image,
+ GTK_WIDGET (priv->content_vgrid),
+ GTK_POS_LEFT,
+ 1,
+ 1);
+}
+
+/**
+ * tepl_info_bar_add_primary_message:
+ * @info_bar: a #TeplInfoBar.
+ * @primary_msg: a primary message.
+ *
+ * Adds a primary message.
+ * Since: 2.0
+ */
+void
+tepl_info_bar_add_primary_message (TeplInfoBar *info_bar,
+ const gchar *primary_msg)
+{
+ TeplInfoBarPrivate *priv;
+ gchar *primary_msg_escaped;
+ gchar *primary_markup;
+ GtkLabel *primary_label;
+
+ g_return_if_fail (TEPL_IS_INFO_BAR (info_bar));
+ g_return_if_fail (primary_msg != NULL);
+
+ priv = tepl_info_bar_get_instance_private (info_bar);
+
+ primary_msg_escaped = g_markup_escape_text (primary_msg, -1);
+ primary_markup = g_strdup_printf ("<b>%s</b>", primary_msg_escaped);
+ primary_label = tepl_info_bar_create_label ();
+ gtk_label_set_markup (primary_label, primary_markup);
+ g_free (primary_markup);
+ g_free (primary_msg_escaped);
+
+ gtk_widget_show (GTK_WIDGET (primary_label));
+ gtk_container_add (GTK_CONTAINER (priv->content_vgrid),
+ GTK_WIDGET (primary_label));
+}
+
+/**
+ * tepl_info_bar_add_secondary_message:
+ * @info_bar: a #TeplInfoBar.
+ * @secondary_msg: a secondary message.
+ *
+ * Adds a secondary message.
+ * Since: 2.0
+ */
+void
+tepl_info_bar_add_secondary_message (TeplInfoBar *info_bar,
+ const gchar *secondary_msg)
+{
+ TeplInfoBarPrivate *priv;
+ gchar *secondary_msg_escaped;
+ gchar *secondary_markup;
+ GtkLabel *secondary_label;
+
+ g_return_if_fail (TEPL_IS_INFO_BAR (info_bar));
+ g_return_if_fail (secondary_msg != NULL);
+
+ priv = tepl_info_bar_get_instance_private (info_bar);
+
+ secondary_msg_escaped = g_markup_escape_text (secondary_msg, -1);
+ secondary_markup = g_strdup_printf ("<small>%s</small>", secondary_msg_escaped);
+ secondary_label = tepl_info_bar_create_label ();
+ gtk_label_set_markup (secondary_label, secondary_markup);
+ g_free (secondary_markup);
+ g_free (secondary_msg_escaped);
+
+ gtk_widget_show (GTK_WIDGET (secondary_label));
+ gtk_container_add (GTK_CONTAINER (priv->content_vgrid),
+ GTK_WIDGET (secondary_label));
+}
+
+/**
+ * tepl_info_bar_add_content_widget:
+ * @info_bar: a #TeplInfoBar.
+ * @content: a #GtkWidget.
+ *
+ * Adds @content to @info_bar.
+ *
+ * #TeplInfoBar has an internal container, to be able to add the icon and add
+ * primary or secondary messages. The internal container is added to the content
+ * area, as returned by gtk_info_bar_get_content_area(). So if you use a
+ * #TeplInfoBar and you need to add a custom #GtkWidget, it is better to use
+ * this function instead of adding the #GtkWidget directly to the content area.
+ *
+ * Since: 2.0
+ */
+void
+tepl_info_bar_add_content_widget (TeplInfoBar *info_bar,
+ GtkWidget *content)
+{
+ TeplInfoBarPrivate *priv;
+
+ g_return_if_fail (TEPL_IS_INFO_BAR (info_bar));
+ g_return_if_fail (GTK_IS_WIDGET (content));
+
+ priv = tepl_info_bar_get_instance_private (info_bar);
+
+ gtk_container_add (GTK_CONTAINER (priv->content_vgrid), content);
+}
+
+/**
+ * tepl_info_bar_add_close_button:
+ * @info_bar: a #TeplInfoBar.
+ *
+ * Calls gtk_info_bar_set_show_close_button(), and additionnally closes the
+ * @info_bar when the #GtkInfoBar::response signal is received with the
+ * @response_id %GTK_RESPONSE_CLOSE.
+ *
+ * Since: 2.0
+ */
+void
+tepl_info_bar_add_close_button (TeplInfoBar *info_bar)
+{
+ TeplInfoBarPrivate *priv;
+
+ g_return_if_fail (TEPL_IS_INFO_BAR (info_bar));
+
+ priv = tepl_info_bar_get_instance_private (info_bar);
+
+ gtk_info_bar_set_show_close_button (GTK_INFO_BAR (info_bar), TRUE);
+
+ priv->close_button_added = TRUE;
+}
+
+/**
+ * tepl_info_bar_create_label:
+ *
+ * Utility function to create a #GtkLabel suitable for a #GtkInfoBar. The
+ * wrapping and alignment is configured. The label is also set as selectable,
+ * for example to copy an error message and search an explanation on the web.
+ *
+ * Returns: (transfer floating): a new #GtkLabel suitable for a #GtkInfoBar.
+ * Since: 1.0
+ */
+GtkLabel *
+tepl_info_bar_create_label (void)
+{
+ GtkLabel *label;
+
+ label = GTK_LABEL (gtk_label_new (NULL));
+ gtk_widget_set_halign (GTK_WIDGET (label), GTK_ALIGN_START);
+ gtk_label_set_xalign (label, 0.0);
+ gtk_label_set_line_wrap (label, TRUE);
+ gtk_label_set_line_wrap_mode (label, PANGO_WRAP_WORD_CHAR);
+ gtk_label_set_selectable (label, TRUE);
+
+ /* Since the wrapping is enabled, we need to set a minimum width.
+ *
+ * If a minimum width is not set, adding an info bar to a container
+ * (e.g. a TeplTab) can make the GtkWindow height to grow. Because
+ * without a minimum width (and without ellipsization), when the user
+ * resizes the window (e.g. reducing the width) the widgets inside the
+ * window must be able to be drawn. When the info bar must be drawn with
+ * a width of e.g. 20 pixels, it takes a huge height because of the text
+ * wrapping. So by setting a minimum width to the label, the maximum
+ * height that the info bar can take is limited, so in most cases the
+ * GtkWindow current height is sufficient to draw the info bar with its
+ * maximum height.
+ *
+ * See:
+ * https://wiki.gnome.org/HowDoI/Labels
+ *
+ * There is also a safety net in tepl_tab_add_info_bar() which calls
+ * gtk_widget_set_size_request() on the GtkInfoBar, to set a minimum
+ * width.
+ */
+ gtk_label_set_width_chars (label, 30);
+
+ return label;
+}
+
+void
+_tepl_info_bar_set_size_request (GtkInfoBar *info_bar)
+{
+ gint min_width;
+ gint min_height;
+
+ g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
+
+ gtk_widget_get_size_request (GTK_WIDGET (info_bar), &min_width, &min_height);
+
+ /* If min_width != -1, gtk_widget_set_size_request() has already been
+ * called, so don't change the value.
+ */
+ if (min_width == -1)
+ {
+ /* Safety net to avoid in most cases the GtkWindow height to
+ * grow.
+ *
+ * The gtk_label_set_width_chars() call in
+ * tepl_info_bar_create_label() fixes the problem at the root,
+ * but we cannot enforce all GtkLabel of @info_bar to be created
+ * with tepl_info_bar_create_label(), so a safety net is better.
+ */
+ gtk_widget_set_size_request (GTK_WIDGET (info_bar), 300, min_height);
+ }
+}
diff --git a/src/tepl-info-bar.h b/src/tepl-info-bar.h
new file mode 100644
index 0000000..0ada12d
--- /dev/null
+++ b/src/tepl-info-bar.h
@@ -0,0 +1,70 @@
+/* Code copied from Tepl:
+ * https://wiki.gnome.org/Projects/Tepl
+ * Do not modify, modify upstream and then copy it back here.
+ */
+
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2016, 2017 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * Tepl 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_INFO_BAR_H
+#define TEPL_INFO_BAR_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TEPL_TYPE_INFO_BAR (tepl_info_bar_get_type ())
+G_DECLARE_DERIVABLE_TYPE (TeplInfoBar, tepl_info_bar,
+ TEPL, INFO_BAR,
+ GtkInfoBar)
+
+struct _TeplInfoBarClass
+{
+ GtkInfoBarClass parent_class;
+
+ gpointer padding[12];
+};
+
+TeplInfoBar * tepl_info_bar_new (void);
+
+TeplInfoBar * tepl_info_bar_new_simple (GtkMessageType msg_type,
+ const gchar *primary_msg,
+ const gchar *secondary_msg);
+
+void tepl_info_bar_add_icon (TeplInfoBar *info_bar);
+
+void tepl_info_bar_add_primary_message (TeplInfoBar *info_bar,
+ const gchar *primary_msg);
+
+void tepl_info_bar_add_secondary_message (TeplInfoBar *info_bar,
+ const gchar *secondary_msg);
+
+void tepl_info_bar_add_content_widget (TeplInfoBar *info_bar,
+ GtkWidget *content);
+
+void tepl_info_bar_add_close_button (TeplInfoBar *info_bar);
+
+GtkLabel * tepl_info_bar_create_label (void);
+
+G_GNUC_INTERNAL
+void _tepl_info_bar_set_size_request (GtkInfoBar *info_bar);
+
+G_END_DECLS
+
+#endif /* TEPL_INFO_BAR_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]