May 07

In this article I explore the application of styles to gtkdialog Notebook object components and content. GtkNotebook was discussed, and examples were provided in the previous article. In this article one of the examples presented earlier will expanded to include styling of label text, tab backgrounds and content. Discussion of widget_class paths and how they can be worked out for specific components in the gtkdialog UI containment hierarchy is also provided.

This is the next article in a series of articles on gtkdialog, “gtkdialog Exploration – articles and examples”, which can be found at https://blogs.czapski.id.au/2017/04/gtkdialog-exploration.

Pre-Requisites

This article assumes that the Virtual Box Machine Image created in accordance with the instructions in the blog article to be found at https://blogs.czapski.id.au/2016/10/configure-virtual-box-virtual-machine-and-install-centos-6-8-base-image is available but it is expected that pretty much any Linux environment will do just as well so long as it supports yad and gtkdialog. For convenience I posted the export of the VirtualBox image which would have been built if the reader followed all the articles in the series “Build a Linux-based Infrastructure Solution Demonstration Series” to date, that is to the 8th of March 2017. The link to the main article is https://blogs.czapski.id.au/2017/04/virtualbox-image-with-content-as-built-so-far.

It also assumes that yad and gtkdialog are installed, as discussed in the article “Install yad and gtkdialog” (https://blogs.czapski.id.au/2017/04/gtkdialog-for-rapid-prototyping-of-linux-applications-install-gtkialog-and-yad)

Example Application with No Styling

Create the constants file and the example code using instructions provided below, then execute the example application to see what an “unadorned” application UI looks like.

First, create the source with the constants, some of which are used in this example. If you worked through the previous article on the Notebook objects you should have this file already available in /tmp/scripts or wherever you created it.

mkdir -pv /tmp/scripts
cd /tmp/scripts
cat <<-'EOSOURCE' > gtkdialog.constants
# gtkdialog.constants
#
cstVersion=1.0.0
echo "Loading ${BASH_SOURCE[0]} version ${cstVersion} ..." 1>&2
 
# I prefer symbolic constants to numeric literals so I define a bunch
#
BTN_IMG_POS_LEFT=0
BTN_IMG_POS_RIGHT=1
BTN_IMG_POS_ABOVE=2
BTN_IMG_POS_BELOW=3
WIN_ICON=access
WIN_DECORATED=true
WIN_RESIZABLE=true
GTK_WIN_POS_CENTER=1
GTK_WIN_POS_MOUSE=2
WIN_POS=${GTK_WIN_POS_CENTER}
WIN_SKIP_TASKBAR_HINT=false
WIN_ALLOW_GROW=true
XALIGN_LEFT=0
XALIGN_RIGHT=1
XALIGN_CENTRE=0.5
YALIGN_TOP=0
YALIGN_BOTTOM=1
YALIGN_CENTRE=0.5
VSCROLLBAR_ALWAYS=0
VSCROLLBAR_AUTOMATIC=1
VSCROLLBAR_NEVER=2
HSCROLLBAR_ALWAYS=0
HSCROLLBAR_AUTOMATIC=1
HSCROLLBAR_NEVER=2
GTK_JUSTIFY_LEFT=0
GTK_JUSTIFY_RIGHT=1
GTK_JUSTIFY_CENTRE=2
TREE_SELECTION_MODE_NONE=0
TREE_SELECTION_MODE_SINGLE=1
TREE_SELECTION_MODE_BROWSE=2
TREE_SELECTION_MODE_MULTIPLE=3
TAB_POS_LEFT=0
TAB_POS_RIGHT=1
TAB_POS_ABOVE=2
TAB_POS_BELOW=3
 
EOSOURCE

Now create and execute the example.

mkdir -pv /tmp/scripts
cd /tmp/scripts
> ex33.sh
chmod ug+x ex33.sh

geany ex32.sh & # copy and paste the code blow until ## EOSOURCE
#!/bin/bash

# make directory for temporary files and have it and the content blown away at exit
#
export LOCAL_DATA_DIR="/dev/shm/$(basename ${0})"
mkdir -p ${LOCAL_DATA_DIR}

fnHlrRemoveTmporaryFilesOnExit() {
    mkdir -p ${LOCAL_DATA_DIR}
    rm -R -f ${LOCAL_DATA_DIR}
}
trap fnHlrRemoveTmporaryFilesOnExit 0 # clean up after any program end.

# name temporary files and place them in the directory which will get deleted on termination of the script (most of the time)
export MAIN_DIALOG_FILE="${LOCAL_DATA_DIR}/MAIN_DIALOG_FILE_${USER}_${BASHPID}.gtkdialog"

##====================================================
[ -z ${GTKDIALOG:-} ] && GTKDIALOG=gtkdialog

# load symbolic constants I use instead of numeric values
source $(dirname ${0})/gtkdialog.constants

# set initial notebook page
echo 1 > ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE

# window title
WIN_TITLE="XDS.b Registry Browser"

# Size of the notebook and all content panels
#
_HEIGHT_REQUEST=375
_WIDTH_REQUEST=570

# construct UI
cat <<-EODECK > ${MAIN_DIALOG_FILE}
<window title="${WIN_TITLE}" icon-name="${WIN_ICON}" decorated="${WIN_DECORATED}" resizable="${WIN_RESIZABLE}"
        window_position="${WIN_POS}" skip_taskbar_hint="${WIN_SKIP_TASKBAR_HINT}" allow-grow="${WIN_ALLOW_GROW}"
        auto-refresh="true" name="win0">
    <vbox>

        <hbox homogeneous="true" sensitive="true" visible="true">

                $(: begin Notebook definition )
                <notebook height-request="${_HEIGHT_REQUEST}" width-request="${_WIDTH_REQUEST}"
                    tab-labels="Requests|Patients|Settings"
                    page="2"
                    tab-hborder="12" xx-tab-hborder="how much space to the left and right of the tab label text"
                    show-tabs="true" xx-show-tabs="as the name says - if false only the content of the 'page=x' tab will show"
                    homogeneous="true" space-fill="true" space-expand="true"
                    tab-pos="${TAB_POS_ABOVE}">

$(: TAB 1 ------------------------------------)
                    <vbox xx-notbook-tab="Tab 1" spacing="2">
                        <text><label>Requests Panels Go Here</label></text>
                    </vbox> $(: end-notbook-tab="Tab 1" )

$(: TAB 2 -----------------------------------)
                    <vbox xx-notbook-tab="Tab 2">
                        <vbox spacing="2">
                            <text><label>Patient Panels Go Here</label></text>
                        </vbox>
                    </vbox> $(: end-notbook-tab="Tab 2" )

$(: TAB 3 ------------------------------------)
                    <vbox xx-notbook-tab="Tab 3" homogenous="true" space-fill="true" space-expand="true">
                    <eventbox>
                        <vbox>
                            <text><label>Settings Panels Go Here</label></text>
                        </vbox>
                    </eventbox>
                    </vbox> $(: end-notbook-tab="Tab 3" )

$(: End of tab content definitions -----------)

                </notebook>
        </hbox> $(: end Notebook definition )

$(: Common object - shown below the notebook --)

        <hbox>
            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true"
              can-default="true" xx-info="has-default must have can-default"
              has-default="true" xx-info="has-default and can-default must both be true for default to work"
              >
                <label> _Quit </label>
                <input file icon="gtk-quit"></input>
                <width>16</width>
                <action function="exit">QUIT</action>
            </button>
        </hbox>
    </vbox>

    <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">EXIT:exit</action>
</window>
EODECK

#== Styles ==========================================
:<<'WIDGET CONTAINMENT HIERARCHY  -------------------------------------'

Consider the following widget containment hierarchy when deriving paths
for styling objects.
Remember that some objects, like hbox and vbox, do not have visible
foreground or background to which styling can be usefully applied.
Remember too that in gtkdialog <text> is not a container/object - it is
syntactic sugar, and certain objects, like buttons, have implicit
containers which do appear in the XML we write for the UI.

window
    <vbox>
        <hbox                           - notebook outer container
            <notebook
                <vbox                   - tab 1
                    <text
                        <label>
                <vbox                   - tab 2
                    <vbox
                        <text>
                            <label
                <vbox                   - tab 3
                    <eventbox>
                        <vbox>
                            <text>
                                <label
        <hbox                           - bottom buttons outer container
            <button

Assuming that we want to change the font of specific labels,
following are the widget_class paths to use:

window.vbox.hbox.notebook.vbox.label
window.vbox.hbox.notebook.vbox.vbox.label
window.vbox.hbox.notebook.vbox.eventbox.vbox.label
window.vbox.hbox.button.hbox.hbox.label
- this one is counterintuitive - we don't have explicit hboxes in our code, right?
- see http://www.murga-linux.com/puppy/viewtopic.php?t=69188&start=2, which says specifically
- - <button> containing only a label is a GtkButton with GtkLabel
- - <button> containing only an image is a GtkButton containing a GtkImage
- - <button> containing a label and an image is a GtkButton containing a GtkHBox or GtkVBox itself containing a GtkLabel and GtkImage
- though even this does not really reflect the actual label containment hierarchy for the button label in this example

WIDGET CONTAINMENT HIERARCHY  -------------------------------------

export GTK2_RC_FILES=${LOCAL_DATA_DIR}/gtkrc
cat <<-'EOSTYLEDEF' > ${GTK2_RC_FILES}
style "notebookStyle" {
    GtkNotebook::tab-overlap = -2
    GtkNotebook::tab-curvature = 0
    xthickness = 1
    ythickness = 1
    font_name="URW Gothic L Book 14"
    fg[NORMAL]="SaddleBrown"        # colours the tab label font
    bg[NORMAL]="powderblue"         # background colour to the notebook
}
style "settingsTabStyle" {
    bg[NORMAL]      = "navy"
    fg[NORMAL]      = "white"
    font_name="URW Gothic L Oblique 18"
}
style "buttonStyle" {
    bg[NORMAL]      = "white"
    fg[NORMAL]      = "navy"
    font_name="URW Gothic L Demi Oblique 10"
}

#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkLabel>" style "notebookStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkVBox><GtkLabel>" style "notebookStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook>" style "notebookStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkEventBox><GtkVBox><GtkLabel>" style "settingsTabStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkEventBox>" style "settingsTabStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkButton><GtkHBox><GtkHBox><GtkLabel>" style "buttonStyle"
EOSTYLEDEF

# -- run the UI -----------------------------------------------------------------------------------------------

case ${1:-} in
    -d | --dump) more "${MAIN_DIALOG_FILE}" ;;
    -f | --file) $GTKDIALOG --center --file=${MAIN_DIALOG_FILE} ;;
    -v | --variable) export MAIN_DIALOG=$(cat ${MAIN_DIALOG_FILE}); $GTKDIALOG --center --program=MAIN_DIALOG ;;
    *) $GTKDIALOG --file=${MAIN_DIALOG_FILE} ;;
esac
## EOSOURCE

/tmp/scripts/ex33.sh

The “unadorned” application will look like that the illustration below.

widget_class Paths

As discussed in the article on styling gtkdialog objects, https://blogs.czapski.id.au/2017/04/gtkdialog-applying-styles-to-gtkdialog-applications, there are three ways in which the specific object to which a style is to be applied can be identified. In this article we use the widget_class and the paths widget_class recognises.

widget_class excepts a path to the object(s) to which to apply a style. The path must reflect the containment hierarchy of the target object(s), that is where in the nesting hierarchy of UI elements this object is located. The path is a dot-separated list of all the ancestor objects all the way from the Window object.

Consider the following widget containment hierarchy when deriving paths for styling objects.

window
    <vbox>
        <hbox                           - notebook outer container
            <notebook
                <vbox                   - tab 1
                    <text
                        <label>
                <vbox                   - tab 2
                    <vbox
                        <text>
                            <label
                <vbox                   - tab 3
                    <eventbox>
                        <vbox>
                            <text>
                                <label
        <hbox                           - bottom buttons outer container
            <button

Remember that some objects, like hbox and vbox, do not have visible foreground or background to which styling can be usefully applied.

Remember too that in gtkdialog <text> is not a container/object – it is syntactic sugar – and certain objects, like buttons, have implicit containers which do not appear in the XML we write for the UI.

Assuming that we want to change the font of specific labels, following are the widget_class paths to use:

window.vbox.hbox.notebook.vbox.label
window.vbox.hbox.notebook.vbox.vbox.label
window.vbox.hbox.notebook.vbox.eventbox.vbox.label
window.vbox.hbox.button.hbox.hbox.label

The path that leads to the label inside the button is counterintuitive – we don’t have explicit hboxes in our code, right?  For reasons why this might be the case see http://www.murga-linux.com/puppy/viewtopic.php?t=69188&start=2, which says:

  • <button> containing only a label is a GtkButton with GtkLabel
  • <button> containing only an image is a GtkButton containing a GtkImage
  • <button> containing a label and an image is a GtkButton containing a GtkHBox or GtkVBox itself containing a GtkLabel and GtkImage

In our case we have a button with a label and an icon, so the third bullet would apply, though even this does not really reflect the actual label containment hierarchy for the button label in this example. Trial and error demonstrates that the containment hierarchy for the label inside the button in his example is actually:

window.vbox.hbox.button.hbox.hbox.label

There are two implicit hbox objects between the button and the its label.

Applying Styles by Example

Locate the commented out section of the style definition in the source:

#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkLabel>" style "notebookStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkVBox><GtkLabel>" style "notebookStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook>" style "notebookStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkEventBox><GtkVBox><GtkLabel>" style "settingsTabStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkEventBox>" style "settingsTabStyle"
#widget_class "<GtkWindow><GtkVBox><GtkHBox><GtkButton><GtkHBox><GtkHBox><GtkLabel>" style "buttonStyle"

The first line applies the “notebookStyle” to the labels of the notebook, changing their font and font colour.

The notebookStyle is defined as follows:

style "notebookStyle" {
    GtkNotebook::tab-overlap = -2
    GtkNotebook::tab-curvature = 0
    xthickness = 1
    ythickness = 1
    font_name="URW Gothic L Book 14"
    fg[NORMAL]="SaddleBrown"        # colours the tab label font
    bg[NORMAL]="powderblue"         # background colour to the notebook
}

Uncomment the first commented out line, save and execute the example to see the effect:

Because the path of the widget_class is “<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkLabel>” the style is applied to the labels of the notbook tabs only. Since the labels don’t have background to which background colour could be applied the bg[NORMAL] directive is ignored.

Uncomment the second commented out line, save and execute the example to see the effect. You will not see the difference until you click on the “Patients” tab:

Because the path of the widget_class is “<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkVBox><GtkLabel>” style “notebookStyle” the style is applied to the label contained inside the text, inside two nested VBox objects inside the notebook. Since the labels don’t have background to which background colour could be applied the bg[NORMAL] directive is ignored.

Uncomment the third commented out line, save and execute the example to see the effect. You will not see the difference until you click on the “Patients” or “Requests” tab:

Because the path of the widget_class is “<GtkWindow><GtkVBox><GtkHBox><GtkNotebook>” the style “notebookStyle” is applied to the background of the notebook tabs.

Before you ask why the style was applied to the Requests and patients, but not to Settings – I have don’t have an answer. It is puzzling.

Consider the style “settingsTabStyle”:

style "settingsTabStyle" {
    bg[NORMAL]      = "navy"
    fg[NORMAL]      = "white"
    font_name="URW Gothic L Oblique 18"
}

The object to which this style is applied should have a navy background, white foreground and specific font face, size and slant.

Uncomment the fourth commented out line, save and execute the example to see the effect:

Because the path of the widget_class is “<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkEventBox><GtkVBox><GtkLabel>” the style “settingsTabStyle” is applied to the label of the content of the settings tab.

Uncomment the fifth commented out line, save and execute the example to see the effect:

Because the path of the widget_class is “<GtkWindow><GtkVBox><GtkHBox><GtkNotebook><GtkVBox><GtkEventBox>” the style “settingsTabStyle” is applied to the <eventbox> object the content of the settings tab.

The eventbox object is an interesting object in that unlike the hbox or vbox it does have a background to which colour can be applied. This property of the eventbox is what is used in the example to give the settings tab background colour different from that of the other tabs.

Now consider the style named “buttonStyle”.

style "buttonStyle" {
    bg[NORMAL]      = "white"
    fg[NORMAL]      = "navy"
    font_name="URW Gothic L Demi Oblique 10"
}

Uncomment the sixth and last commented out line, save and execute the example to see the effect:

That text of the button label was changed from default.

Note that the widget_class path, “<GtkWindow><GtkVBox><GtkHBox><GtkButton><GtkHBox><GtkHBox><GtkLabel>”, has the two extra implicit hbox components. I worked this out by successively trying:

"<GtkWindow><GtkVBox><GtkHBox><GtkButton><GtkLabel>" – did not work

"<GtkWindow><GtkVBox><GtkHBox><GtkButton>*<GtkLabel>" – worked

"<GtkWindow><GtkVBox><GtkHBox><GtkButton>*<GtkHBox>*<GtkLabel>" – worked

"<GtkWindow><GtkVBox><GtkHBox><GtkButton><GtkHBox>*<GtkLabel>" – worked

"<GtkWindow><GtkVBox><GtkHBox><GtkButton><GtkHBox><GtkLabel>" – did not work

"<GtkWindow><GtkVBox><GtkHBox><GtkButton><GtkHBox><GtkHBox><GtkLabel>" – worked

This was a bit tedious but not excessively so.

 Summary

We explored the styling of the notebook object and its content, reviewing in the process how one can derive paths to various UI components which the widget_class requires to apply styles.

Leave a Reply

preload preload preload