From d0a287a4e57b299cec22c94b028e3553d5b36fcd Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Wed, 23 Jan 2019 21:16:14 +0100
Subject: Prepared a toolbar for actions on basic blocks.

---
 pixmaps/collapse.png              | Bin 0 -> 206 bytes
 pixmaps/collapse_dark.png         | Bin 0 -> 216 bytes
 pixmaps/expand.png                | Bin 0 -> 156 bytes
 pixmaps/expand_dark.png           | Bin 0 -> 161 bytes
 pixmaps/palette.png               | Bin 0 -> 565 bytes
 src/gtkext/Makefile.am            |  18 +++++
 src/gtkext/blockbar.ui            |  97 ++++++++++++++++++++++++
 src/gtkext/gresource.xml          |   6 ++
 src/gtkext/gtkblockdisplay.c      |   2 +
 src/gtkext/gtkbufferdisplay-int.h |   3 +
 src/gtkext/gtkbufferdisplay.c     | 156 ++++++++++++++++++++++++++++++++++++++
 src/gtkext/gtkbufferdisplay.h     |   8 ++
 src/gtkext/gtkdisplaypanel-int.h  |   4 +
 src/gtkext/gtkdisplaypanel.c      |   4 -
 src/gui/core/Makefile.am          |  22 ++++++
 src/gui/core/gresource.xml        |  10 +++
 themes/Adwaita/Makefile.am        |   3 +
 themes/Adwaita/core.css           |  16 ++++
 themes/Adwaita/widgets-dark.css   |  18 +++++
 themes/Adwaita/widgets.css        |  18 +++++
 tools/icons/collapse.py           |  46 +++++++++++
 tools/icons/expand.py             |  46 +++++++++++
 tools/icons/gen.sh                |   9 +++
 tools/icons/palette.py            |  53 +++++++++++++
 24 files changed, 535 insertions(+), 4 deletions(-)
 create mode 100644 pixmaps/collapse.png
 create mode 100644 pixmaps/collapse_dark.png
 create mode 100644 pixmaps/expand.png
 create mode 100644 pixmaps/expand_dark.png
 create mode 100644 pixmaps/palette.png
 create mode 100644 src/gtkext/blockbar.ui
 create mode 100644 src/gtkext/gresource.xml
 create mode 100644 src/gui/core/gresource.xml
 create mode 100644 themes/Adwaita/core.css
 create mode 100644 themes/Adwaita/widgets-dark.css
 create mode 100644 themes/Adwaita/widgets.css
 create mode 100644 tools/icons/collapse.py
 create mode 100644 tools/icons/expand.py
 create mode 100755 tools/icons/gen.sh
 create mode 100644 tools/icons/palette.py

diff --git a/pixmaps/collapse.png b/pixmaps/collapse.png
new file mode 100644
index 0000000..3f3d93c
Binary files /dev/null and b/pixmaps/collapse.png differ
diff --git a/pixmaps/collapse_dark.png b/pixmaps/collapse_dark.png
new file mode 100644
index 0000000..4961a78
Binary files /dev/null and b/pixmaps/collapse_dark.png differ
diff --git a/pixmaps/expand.png b/pixmaps/expand.png
new file mode 100644
index 0000000..793b3f9
Binary files /dev/null and b/pixmaps/expand.png differ
diff --git a/pixmaps/expand_dark.png b/pixmaps/expand_dark.png
new file mode 100644
index 0000000..4179f38
Binary files /dev/null and b/pixmaps/expand_dark.png differ
diff --git a/pixmaps/palette.png b/pixmaps/palette.png
new file mode 100644
index 0000000..d4ee16d
Binary files /dev/null and b/pixmaps/palette.png differ
diff --git a/src/gtkext/Makefile.am b/src/gtkext/Makefile.am
index 158e591..24df0c3 100644
--- a/src/gtkext/Makefile.am
+++ b/src/gtkext/Makefile.am
@@ -1,6 +1,11 @@
 
+BUILT_SOURCES = resources.h resources.c
+
 noinst_LTLIBRARIES = libgtkext.la
 
+UI_FILES =								\
+	blockbar.ui
+
 libgtkext_la_SOURCES =						\
 	diagram.h diagram.c						\
 	easygtk.h easygtk.c						\
@@ -16,6 +21,7 @@ libgtkext_la_SOURCES =						\
 	gtkgraphdisplay.h gtkgraphdisplay.c		\
 	gtkstatusstack.h gtkstatusstack.c		\
 	rendering.h rendering.c					\
+	resources.h resources.c					\
 	support.h support.c						\
 	tiledgrid.h tiledgrid.c					\
 	tmgt.h tmgt.c
@@ -36,3 +42,15 @@ AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS)
 AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS)
 
 SUBDIRS = graph
+
+
+resources.c: gresource.xml $(UI_FILES)
+	glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gtkext gresource.xml
+
+resources.h: gresource.xml
+	glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name gtkext gresource.xml
+
+
+CLEANFILES = resources.h resources.c
+
+EXTRA_DIST = gresource.xml $(UI_FILES)
diff --git a/src/gtkext/blockbar.ui b/src/gtkext/blockbar.ui
new file mode 100644
index 0000000..b8f07cd
--- /dev/null
+++ b/src/gtkext/blockbar.ui
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.21.0 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkOffscreenWindow">
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkGrid" id="blockbar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="focus_on_click">False</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Define a background color for the whole block</property>
+            <property name="opacity">0.59999999999999998</property>
+            <property name="relief">none</property>
+            <signal name="enter-notify-event" handler="on_block_bar_enter_notify" swapped="no"/>
+            <signal name="leave-notify-event" handler="on_block_bar_leave_notify" swapped="no"/>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+            </child>
+            <style>
+              <class name="tinybutton"/>
+              <class name="block-palette"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="focus_on_click">False</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Collapse the block with all its dominated blocks</property>
+            <property name="opacity">0.59999999999999998</property>
+            <property name="relief">none</property>
+            <signal name="enter-notify-event" handler="on_block_bar_enter_notify" swapped="no"/>
+            <signal name="leave-notify-event" handler="on_block_bar_leave_notify" swapped="no"/>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+            </child>
+            <style>
+              <class name="tinybutton"/>
+              <class name="block-collapse"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton">
+            <property name="can_focus">True</property>
+            <property name="focus_on_click">False</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">Expand the collapses block</property>
+            <property name="opacity">0.59999999999999998</property>
+            <property name="relief">none</property>
+            <signal name="enter-notify-event" handler="on_block_bar_enter_notify" swapped="no"/>
+            <signal name="leave-notify-event" handler="on_block_bar_leave_notify" swapped="no"/>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+            </child>
+            <style>
+              <class name="tinybutton"/>
+              <class name="block-expand"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child type="titlebar">
+      <placeholder/>
+    </child>
+  </object>
+</interface>
diff --git a/src/gtkext/gresource.xml b/src/gtkext/gresource.xml
new file mode 100644
index 0000000..0250484
--- /dev/null
+++ b/src/gtkext/gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+    <gresource prefix="/org/chrysalide/gtkext">
+        <file compressed="true">blockbar.ui</file>
+    </gresource>
+</gresources>
diff --git a/src/gtkext/gtkblockdisplay.c b/src/gtkext/gtkblockdisplay.c
index 97136c9..546c1aa 100644
--- a/src/gtkext/gtkblockdisplay.c
+++ b/src/gtkext/gtkblockdisplay.c
@@ -429,4 +429,6 @@ void gtk_block_display_override_view_index(GtkBlockDisplay *display, unsigned in
 
     GTK_BUFFER_DISPLAY(display)->offsets.max_widths[BLC_ASSEMBLY_LABEL] = 0;
 
+    gtk_buffer_display_add_block_bar(GTK_BUFFER_DISPLAY(display));
+
 }
diff --git a/src/gtkext/gtkbufferdisplay-int.h b/src/gtkext/gtkbufferdisplay-int.h
index c84f6d5..b4b64d0 100644
--- a/src/gtkext/gtkbufferdisplay-int.h
+++ b/src/gtkext/gtkbufferdisplay-int.h
@@ -49,6 +49,9 @@ struct _GtkBufferDisplay
     guint caret_timer;                      /* Identifiant du chronomètre  */
     bool show_caret;                        /* Bascule entre les affichages*/
 
+    GtkBuilder *builder;                    /* Constructeur à manipuler    */
+    GtkWidget *bar;                         /* Barre d'outils intégrée     */
+
 };
 
 /* Composant d'affichage de tampon de lignes (classe) */
diff --git a/src/gtkext/gtkbufferdisplay.c b/src/gtkext/gtkbufferdisplay.c
index 1b452f1..5ad808f 100644
--- a/src/gtkext/gtkbufferdisplay.c
+++ b/src/gtkext/gtkbufferdisplay.c
@@ -108,6 +108,20 @@ static void gtk_buffer_display_draw_caret(GtkBufferDisplay *, cairo_t *);
 
 
 
+/* ------------------------- INCLUSION D'UNE BARRE D'OUTILS ------------------------- */
+
+
+/* Place correctement la barre d'outils pour bloc. */
+static void gtk_buffer_display_move_block_bar(GtkBufferDisplay *);
+
+/* Accompagne le début du survol d'un élément de barre d'outils. */
+static gboolean on_block_bar_enter_notify(GtkWidget *, GdkEventCrossing *, GtkBufferDisplay *);
+
+/* Accompagne la fin du survol d'un élément de barre d'outils. */
+static gboolean on_block_bar_leave_notify(GtkWidget *, GdkEventCrossing *, GtkBufferDisplay *);
+
+
+
 /* ---------------------------------------------------------------------------------- */
 /*                            INTERACTION DIRECTE AVEC GTK                            */
 /* ---------------------------------------------------------------------------------- */
@@ -223,6 +237,9 @@ static void gtk_buffer_display_dispose(GtkBufferDisplay *display)
 
     g_clear_object(&display->cursor);
 
+    g_clear_object(&display->builder);
+    g_clear_object(&display->bar);
+
     G_OBJECT_CLASS(gtk_buffer_display_parent_class)->dispose(G_OBJECT(display));
 
 }
@@ -466,6 +483,10 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr)
 
     cairo_restore(cr);
 
+    /* Dessin des composants contenus */
+
+    GTK_WIDGET_CLASS(gtk_buffer_display_parent_class)->draw(widget, cr);
+
     return FALSE;
 
 }
@@ -565,10 +586,22 @@ static gboolean gtk_buffer_display_key_press(GtkWidget *widget, GdkEventKey *eve
 
 static void gtk_buffer_display_compute_requested_size(GtkBufferDisplay *display, gint *width, gint *height)
 {
+    gint extra;                             /* Eventuel supplément largeur */
+
     if (width != NULL)
     {
         if (display->view != NULL)
+        {
             *width = g_buffer_view_get_width(display->view, GTK_DISPLAY_PANEL(display)->options);
+
+            if (display->bar != NULL)
+            {
+                gtk_widget_get_preferred_width(display->bar, NULL, &extra);
+                *width += extra;
+            }
+
+        }
+
         else
             *width = 0;
     }
@@ -1183,3 +1216,126 @@ static void gtk_buffer_display_draw_caret(GtkBufferDisplay *display, cairo_t *cr
     }
 
 }
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                           INCLUSION D'UNE BARRE D'OUTILS                           */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : display = panneau d'affichage concerné.                      *
+*                                                                             *
+*  Description : Ajoute une nouvelle barre d'outils pour bloc au composant.   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void gtk_buffer_display_add_block_bar(GtkBufferDisplay *display)
+{
+    GtkWidget *bar;                         /* Barre d'outils à intégrer   */
+
+    assert(display->builder == NULL);
+
+    display->builder = gtk_builder_new_from_resource("/org/chrysalide/gtkext/blockbar.ui");
+
+    bar = GTK_WIDGET(gtk_builder_get_object(display->builder, "blockbar"));
+
+    display->bar = bar;
+    g_object_ref(G_OBJECT(bar));
+
+    g_object_ref(G_OBJECT(bar));
+    gtk_widget_unparent(bar);
+
+    gtk_fixed_put(GTK_FIXED(display), bar, 0, 0);
+
+    gtk_builder_add_callback_symbols(display->builder,
+                                     "on_block_bar_enter_notify", G_CALLBACK(on_block_bar_enter_notify),
+                                     "on_block_bar_leave_notify", G_CALLBACK(on_block_bar_leave_notify),
+                                     NULL);
+
+    gtk_builder_connect_signals(display->builder, display);
+
+    gtk_buffer_display_move_block_bar(display);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : display = panneau d'affichage concerné.                      *
+*                                                                             *
+*  Description : Place correctement la barre d'outils pour bloc.              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void gtk_buffer_display_move_block_bar(GtkBufferDisplay *display)
+{
+    GtkWidget *bar;                         /* Barre d'outils courante     */
+    gint width;                             /* Largeur requise à vide      */
+
+    bar = display->bar;
+    display->bar = NULL;
+
+    gtk_buffer_display_compute_requested_size(display, &width, NULL);
+
+    display->bar = bar;
+
+    gtk_fixed_move(GTK_FIXED(display), bar, width, (int)BORDER_CORNER_RADIUS / 2);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : widget  = composant graphique concerné par l'opération.      *
+*                event   = informations liées à l'événement.                  *
+*                display = panneau d'affichage impliqué par l'action.         *
+*                                                                             *
+*  Description : Accompagne le début du survol d'un élément de barre d'outils.*
+*                                                                             *
+*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static gboolean on_block_bar_enter_notify(GtkWidget *widget, GdkEventCrossing *event, GtkBufferDisplay *display)
+{
+    gtk_widget_set_opacity(widget, 1.0);
+
+    return FALSE;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : widget  = composant graphique concerné par l'opération.      *
+*                event   = informations liées à l'événement.                  *
+*                display = panneau d'affichage impliqué par l'action.         *
+*                                                                             *
+*  Description : Accompagne la fin du survol d'un élément de barre d'outils.  *
+*                                                                             *
+*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static gboolean on_block_bar_leave_notify(GtkWidget *widget, GdkEventCrossing *event, GtkBufferDisplay *display)
+{
+    gtk_widget_set_opacity(widget, 0.6);
+
+    return FALSE;
+
+}
diff --git a/src/gtkext/gtkbufferdisplay.h b/src/gtkext/gtkbufferdisplay.h
index 73fbbde..6c6c8b5 100644
--- a/src/gtkext/gtkbufferdisplay.h
+++ b/src/gtkext/gtkbufferdisplay.h
@@ -67,4 +67,12 @@ bool gtk_buffer_display_move_caret_to(GtkBufferDisplay *, bool, gint *);
 
 
 
+/* ------------------------- INCLUSION D'UNE BARRE D'OUTILS ------------------------- */
+
+
+/* Ajoute une nouvelle barre d'outils pour bloc au composant. */
+void gtk_buffer_display_add_block_bar(GtkBufferDisplay *);
+
+
+
 #endif  /* _GTKEXT_GTKBUFFER_DISPLAY_H */
diff --git a/src/gtkext/gtkdisplaypanel-int.h b/src/gtkext/gtkdisplaypanel-int.h
index 7d64925..62c990e 100644
--- a/src/gtkext/gtkdisplaypanel-int.h
+++ b/src/gtkext/gtkdisplaypanel-int.h
@@ -37,6 +37,10 @@
 
 
 
+/* Amplitude de l'arrondi pour les coins */
+#define BORDER_CORNER_RADIUS 10.0
+
+
 /* Indique les dimensions de travail du composant d'affichage. */
 typedef void (* compute_requested_size_fc) (GtkDisplayPanel *, gint *, gint *);
 
diff --git a/src/gtkext/gtkdisplaypanel.c b/src/gtkext/gtkdisplaypanel.c
index e548bf7..010d648 100644
--- a/src/gtkext/gtkdisplaypanel.c
+++ b/src/gtkext/gtkdisplaypanel.c
@@ -33,10 +33,6 @@
 
 
 
-/* Amplitude de l'arrondi pour les coins */
-#define BORDER_CORNER_RADIUS 10.0
-
-
 /* Procède à l'initialisation de l'afficheur générique. */
 static void gtk_display_panel_class_init(GtkDisplayPanelClass *);
 
diff --git a/src/gui/core/Makefile.am b/src/gui/core/Makefile.am
index d83d63e..d23dc07 100644
--- a/src/gui/core/Makefile.am
+++ b/src/gui/core/Makefile.am
@@ -1,11 +1,21 @@
 
+BUILT_SOURCES = resources.h resources.c
+
 noinst_LTLIBRARIES = libguicore.la
 
+RES_FILES =								\
+	../../../pixmaps/palette.png		\
+	../../../pixmaps/collapse.png		\
+	../../../pixmaps/collapse_dark.png	\
+	../../../pixmaps/expand.png			\
+	../../../pixmaps/expand_dark.png
+
 libguicore_la_SOURCES =					\
 	core.h core.c						\
 	global.h global.c					\
 	items.h items.c						\
 	panels.h panels.c					\
+	resources.h resources.c				\
 	theme.h theme.c
 
 libguicore_la_LDFLAGS = $(LIBGTK_LIBS) $(LIBXML_LIBS)
@@ -21,3 +31,15 @@ AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS)
 AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS)
 
 SUBDIRS = 
+
+
+resources.c: gresource.xml $(RES_FILES)
+	glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_core gresource.xml
+
+resources.h: gresource.xml
+	glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name gui_core gresource.xml
+
+
+CLEANFILES = resources.h resources.c
+
+EXTRA_DIST = gresource.xml $(RES_FILES)
diff --git a/src/gui/core/gresource.xml b/src/gui/core/gresource.xml
new file mode 100644
index 0000000..ad3688c
--- /dev/null
+++ b/src/gui/core/gresource.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+    <gresource prefix="/org/chrysalide/gui/core/images">
+        <file compressed="true" alias="palette.png">../../../pixmaps/palette.png</file>
+        <file compressed="true" alias="collapse.png">../../../pixmaps/collapse.png</file>
+        <file compressed="true" alias="collapse_dark.png">../../../pixmaps/collapse_dark.png</file>
+        <file compressed="true" alias="expand.png">../../../pixmaps/expand.png</file>
+        <file compressed="true" alias="expand_dark.png">../../../pixmaps/expand_dark.png</file>
+    </gresource>
+</gresources>
diff --git a/themes/Adwaita/Makefile.am b/themes/Adwaita/Makefile.am
index 4712702..cf4d6a3 100644
--- a/themes/Adwaita/Makefile.am
+++ b/themes/Adwaita/Makefile.am
@@ -1,8 +1,11 @@
 
 GTK3_CSS =								\
+	core.css							\
 	display.css							\
 	icons.css							\
 	portions.css						\
+	widgets.css							\
+	widgets-dark.css					\
 	clean.png							\
 	redo.png							\
 	undo.png
diff --git a/themes/Adwaita/core.css b/themes/Adwaita/core.css
new file mode 100644
index 0000000..a7760a1
--- /dev/null
+++ b/themes/Adwaita/core.css
@@ -0,0 +1,16 @@
+
+.tinybutton {
+
+    min-width: 12px;
+    min-height: 12px;
+
+    padding: 2px;
+
+}
+
+.tinybutton > image {
+
+    background-position: center center;
+    background-repeat: no-repeat;
+
+}
diff --git a/themes/Adwaita/widgets-dark.css b/themes/Adwaita/widgets-dark.css
new file mode 100644
index 0000000..37d856f
--- /dev/null
+++ b/themes/Adwaita/widgets-dark.css
@@ -0,0 +1,18 @@
+
+.block-palette > image {
+
+    background-image: url('resource:///org/chrysalide/gui/core/images/palette.png');
+
+}
+
+.block-collapse > image {
+
+    background-image: url('resource:///org/chrysalide/gui/core/images/collapse_dark.png');
+
+}
+
+.block-expand > image {
+
+    background-image: url('resource:///org/chrysalide/gui/core/images/expand_dark.png');
+
+}
diff --git a/themes/Adwaita/widgets.css b/themes/Adwaita/widgets.css
new file mode 100644
index 0000000..de84f34
--- /dev/null
+++ b/themes/Adwaita/widgets.css
@@ -0,0 +1,18 @@
+
+.block-palette > image {
+
+    background-image: url('resource:///org/chrysalide/gui/core/images/palette.png');
+
+}
+
+.block-collapse > image {
+
+    background-image: url('resource:///org/chrysalide/gui/core/images/collapse.png');
+
+}
+
+.block-expand > image {
+
+    background-image: url('resource:///org/chrysalide/gui/core/images/expand.png');
+
+}
diff --git a/tools/icons/collapse.py b/tools/icons/collapse.py
new file mode 100644
index 0000000..c3fe508
--- /dev/null
+++ b/tools/icons/collapse.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+
+import cairo
+import sys
+
+
+WIDTH, HEIGHT = 256, 256
+WIDTH, HEIGHT = 12, 12
+
+surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
+ctx = cairo.Context(surface)
+
+ctx.scale(WIDTH, HEIGHT)
+
+if len(sys.argv) > 1 and sys.argv[1] == '--dark':
+    ctx.set_source_rgb(1.0, 1.0, 1.0)
+    dark = '_dark'
+else:
+    ctx.set_source_rgb(0.0, 0.0, 0.0)
+    dark = ''
+
+ctx.move_to(0.4, 0.4)
+ctx.line_to(0.4, 0.0)
+ctx.line_to(0.0, 0.4)
+
+ctx.fill()
+
+ctx.move_to(0.6, 0.4)
+ctx.line_to(0.6, 0.0)
+ctx.line_to(1.0, 0.4)
+
+ctx.fill()
+
+ctx.move_to(0.6, 0.6)
+ctx.line_to(1.0, 0.6)
+ctx.line_to(0.6, 1.0)
+
+ctx.fill()
+
+ctx.move_to(0.4, 0.6)
+ctx.line_to(0.4, 1.0)
+ctx.line_to(0.0, 0.6)
+
+ctx.fill()
+
+surface.write_to_png('../../pixmaps/collapse%s.png' % dark)
diff --git a/tools/icons/expand.py b/tools/icons/expand.py
new file mode 100644
index 0000000..854bbc6
--- /dev/null
+++ b/tools/icons/expand.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+
+import cairo
+import sys
+
+
+WIDTH, HEIGHT = 256, 256
+WIDTH, HEIGHT = 12, 12
+
+surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
+ctx = cairo.Context(surface)
+
+ctx.scale(WIDTH, HEIGHT)
+
+if len(sys.argv) > 1 and sys.argv[1] == '--dark':
+    ctx.set_source_rgb(1.0, 1.0, 1.0)
+    dark = '_dark'
+else:
+    ctx.set_source_rgb(0.0, 0.0, 0.0)
+    dark = ''
+
+ctx.move_to(0.0, 0.0)
+ctx.line_to(0.4, 0.0)
+ctx.line_to(0.0, 0.4)
+
+ctx.fill()
+
+ctx.move_to(1.0, 0.0)
+ctx.line_to(0.6, 0.0)
+ctx.line_to(1.0, 0.4)
+
+ctx.fill()
+
+ctx.move_to(1.0, 1.0)
+ctx.line_to(1.0, 0.6)
+ctx.line_to(0.6, 1.0)
+
+ctx.fill()
+
+ctx.move_to(0.0, 1.0)
+ctx.line_to(0.4, 1.0)
+ctx.line_to(0.0, 0.6)
+
+ctx.fill()
+
+surface.write_to_png('../../pixmaps/expand%s.png' % dark)
diff --git a/tools/icons/gen.sh b/tools/icons/gen.sh
new file mode 100755
index 0000000..a09bacb
--- /dev/null
+++ b/tools/icons/gen.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+python3 ./palette.py
+
+python3 ./expand.py
+python3 ./expand.py --dark
+
+python3 ./collapse.py
+python3 ./collapse.py --dark
diff --git a/tools/icons/palette.py b/tools/icons/palette.py
new file mode 100644
index 0000000..4227964
--- /dev/null
+++ b/tools/icons/palette.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+import math
+import cairo
+
+
+WIDTH, HEIGHT = 256, 256
+WIDTH, HEIGHT = 12, 12
+
+surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
+ctx = cairo.Context(surface)
+
+ctx.scale(WIDTH, HEIGHT)
+
+ctx.arc(0.5, 0.5, 0.40, 0, 2 * math.pi)
+ctx.set_source_rgb(1.0, 1.0, 1.0)
+ctx.fill()
+
+colors = [
+    'fefe33',
+    'fabc02',
+    'fb9902',
+    'fd5308',
+    'fe2712',
+    'a7194b',
+    '8601af',
+    '3d01a4',
+    '0247fe',
+    '0391ce',
+    '66b032',
+    'd0ea2b'
+]
+
+angle = (2 * math.pi) / len(colors)
+
+for i in range(len(colors)):
+
+    hval = int(colors[i], 16)
+
+    red   = ((hval & 0xff0000) >> 16) / 256
+    green = ((hval & 0x00ff00) >>  8) / 256
+    blue  = ((hval & 0x0000ff) >>  0) / 256
+
+    start = angle / 2 - i * angle
+
+    ctx.arc(0.5, 0.5, 0.5, start - angle, start)
+
+    ctx.arc_negative(0.5, 0.5, 0.15, start, start - angle)
+
+    ctx.set_source_rgb(red, green, blue)
+    ctx.fill()
+
+surface.write_to_png('../../pixmaps/palette.png')
-- 
cgit v0.11.2-87-g4458