May 21

In this article I explore the gtkdialog EventBox object and its uses by example. EventBox can be used, among other things, to provide background colour to other objects, intercept and handle events and add frames around gtkdialog objects to help visualise the space they occupy.

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.

The examples in this article demonstrate the following uses of the EventBox:

  1. Providing background colour to HBox / VBox objects
  2. Applying font styles to text within EventBox and its content
  3. Unsing the EventBox to intercept mouse clicks on objects contained inside the EventBox
  4. Using the EventBox and styles to add a colour border to a window
  5. Using the EventBox and styles to add a “right-click” menu to an application
  6. Using the EventBox and styles to add visible borders to any/all gtldialog components

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)

EventBox Object Model

gtkdialog, based as it is on the GTK object model, leverages the GTK object hierarchy. Consider the reference for the EventBox object at http://01micko.com/reference/eventbox.html.

The very first link points to a GtkEventBox – https://developer.gnome.org/gtk2/2.24/GtkEventBox.html.

In the GTK object hierarchy, https://developer.gnome.org/gtk2/2.24/GtkEventBox.html#GtkEventBox.object-hierarchy, the EventBox object is some levels below the GObject, the topmost object.

The GtkEventBox, and consequently the gtkdialog EventBox, object inherits from the objects higher up in the hierarchy. This will come into play when we explore attributes that can be set for an EventBox, and actions that can be configured for an EventBox.

EventBox Attributes

The gtkdialog EventBox object is a container, of a sort, to which styles can be applied, which can be “shown”/”hidden” and which can be “positioned” in the object hierarchy in such a way that all of the children which it contains are “beneath” it, so that the EventBox and not the children receives mouse clicks.

An example of an interface, which uses the EventBox object, and which will be developed in this article, is shown here.

Reference documentation for the gtkdialog EventBox object, http://01micko.com/reference/eventbox.html, provides a table of attributes specific to this gtkdialog object and makes a reference to ancestor class properties.

Attributes that can be configured for the eventbox widget are defined in the widget’s reference page see https://developer.gnome.org/gtk2/2.24/GtkEventBox.html, section Properties – https://developer.gnome.org/gtk2/2.24/GtkEventBox.html#GtkEventBox.properties

Some of the properties are inherited from the parent objects, like GtkContainer and GtkWidget. For example, border-width, which defines the amount of space between the notebook frame and the notebook content, is inherited from the GtkContainer.

Similarly, sensitive property is inherited from the GtkWidget and when set to true effectively disables everything inside the eventbox container.

See the reference page: file:///home/demo/gtkdialog-0.8.3/doc/reference/eventbox.html

To work out what attributes are actually supported, in this case by the eventbox widget, we need to do some exploration. Discussion below goes into this to an extent and I provided most of the useful attributes that can be set, both from GtkWindow itself and inherited from GtkContainer and GtkWidget. The “Works?” column indicates whether the attribute has a visible effect (Y), does not have a visible effect (N) or I have not figured out a way to test whether it works or not.

The table below summarised EventBox attributes/properties and indicates which work with the gtkdialog and which do not as far as I can tell. “Do not” may well be a function of me not seeing any visible difference with the property set to different values. Some ancestor attributes/properties which are not really useable from gtkdialog are omitted.

Name Description Value Default Works? In
EventBox
space-expand Pack widget expanding into space true or false Y gtkdialog
space-fill Pack widget filling space true or false Y gtkdialog
block-function-signals Block signal emissions from functions true or false gtkdialog
Inherited from GtkEventBox
above-child Whether the event-trapping window of the eventbox is above the window of the child widget as opposed to below it. If true no clicks will be delivered to the objects underneath the eventbox. true or false false Y GtkEventBox
visible-window Whether the event box is visible, as opposed to invisible and only used to trap events. true or false false Y GtkEventBox
Inherited from gtkContainer
border-width The width of the empty border outside the containers children <=65535 0 Y
can-default Whether the widget can be the default widget.

This does not seem to have any effect when applied to a Window object

true or false false Maybe

Makes no sense for this

GtkWidget
can-focus Whether the widget can accept the input focus.

This does not seem to have any effect when applied to a Window object

true or false false Maybe

Makes no sense for this

GtkWidget
has-default Whether the widget is the default widget.

This does not seem to have any effect when applied to a Window object

true or false false Maybe

Makes no sense for this

GtkWidget
has-focus Whether the widget has the input focus.

This does not seem to have any effect when applied to a Window object

true or false false Maybe

Makes no sense for this

GtkWidget
has-tooltip Whether this widget has a tooltip.

Disables tooltip display if tooltip is defined for the Window

true or false false Y GtkWidget
height-request Override for height request of the widget, or -1 if natural height should be used.

Has not effect of default-height is specified.

>=0 -1 Y GtkWidget
is-focus Whether the widget is the focus widget within the toplevel.

Does not seem to have any effect when applied to the Window object

true or false false Maybe

Makes no sense for this

GtkWidget
name The name of the widget.

It can be used to address styling directves to a named object

string NULL GtkWidget
receives-default If TRUE, the widget will receive the default action when it is focused.

I don’t know what that is supposed to accomplish. Nothing visible happens regardless of the property value.

true or false false Maybe

Makes no sense for this

GtkWidget
sensitive Whether the widget responds to input.

FALSE disables all components contained in the eventbox.

true or false TRUE Y GtkWidget
tooltip-markup The contents of the tooltip for this widget.

Simple markup can be used to call out parts of tooltip text in.

See http://www.murga-linux.com/puppy/viewtopic.php?t=40418 for a markup explorer tool.

Ineffective if tooltip-disabled is TRUE.

string NULL Y GtkWidget
tooltip-text The contents of the tooltip for this widget.

Ineffective if tooltip-disabled is TRUE.

string NULL Y GtkWidget
visible Whether the widget is visible.

It is somewhat self-defeating to make a window invisible on creation unless it is a “subsidiary” window and can be made visible form some other piece of logic.

true or false true Y GtkWidget
width-request Override for width request of the widget, or -1 if natural width should be used.

Has not effect if default-width is specified or if an outer container has a width-request set.

>=0 -1 Y GtkWidget

The following example includes just about every attrivute which can be set for the EventBox. We will explore what the various attribute settings do below.

First, let’s create an enumerations script which will supply human-readable versions of enumerations and other numeric constants that may be used in the scripts I show. See https://github.com/GNOME/gtk/blob/master/gdk/gdktypes.h, and elsewhere, for sources of these enumerations.

If you have this enumerations source because you worked through the Notebook example than you don’t need to re-create it. No new constants have been added.

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 defined a bunch
# see https://github.com/GNOME/gtk/blob/master/gdk/gdktypes.h for specifics
#
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

above-child and visible-window attributes

In this section we will explore how the above-child and visible-window attributes of the eventbox, combined with appropriate styles, affect the appearance and function of the eventbox and its contents.

To create and exercise the first example execute the following commands, noting that you will need to copy and paste the code into an editor window because it is too large for a “here document”.

mkdir /tmp/scripts
cd /tmp/scripts
> /tmp/scripts/ex34.sh
chmod ug+x /tmp/scripts/ex34.sh

geany /tmp/scripts/ex34.sh & # until ## EOSCRIPT
#!/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

# window title
WIN_TITLE="EventBox Example"

# Size of the notebook and all content panels
#
_WIDTH_REQUEST=400
export _DATE_FORMAT_RFC3339_NS="--rfc-3339=ns"
export _DATE_FORMAT_TIMESTAMP="+%Y%m%d-%H%M%S.%N%z"

# 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 width-request="${_WIDTH_REQUEST}">

        <hbox>
            <hbox homogenous="true" space-fill="true" space-expand="true">
                <text><label>Some content goes here</label></text>
            </hbox>
        </hbox>

        $(: visible-window="false" blocks delivery of clicks to the content objects, including inner eventbox objects )
        $(: but it does not prevent keystrokes from selecting buttons and pressing on them, for example )
        <eventbox above-child="false" visible-window="true">
            <frame  Outer Frame  >
                <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> _Refresh </label>
                        <input file icon="system-config-date"></input>
                        <width>16</width>
                        <action condition="command_is_true(fnDateFormatToggleNeeded '${_DATE_FORMAT_RFC3339_NS}' )">refresh:vDateFormatToggle</action>
                        <action function="refresh">vWindow</action>
                    </button>
                    <text xpad="1"><label>""</label></text>
                </hbox>

                $(: both above-child and visible-window have to be true for the event box to "block" clicks to the content )
                $(: both above-child and visible-window have to be true for the event box background colour to be seen )
                <eventbox above-child="false" visible-window="true" has-tooltip="true" tooltip-markup="This is <b>tooltip</b> for eventbox"
                          height-request="100" sensitive="true" visible="true" width-request="600">
                    <frame  Inner Frame  >
                        <hbox>
                            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true">
                                <label> _Update </label>
                                <input file icon="system-config-date"></input>
                                <width>16</width>
                                <action condition="command_is_true(fnDateFormatToggleNeeded '${_DATE_FORMAT_TIMESTAMP}')">refresh:vDateFormatToggle</action>
                                <action function="refresh">vWindow</action>
                            </button>
                        </hbox>
                    </frame> $(: inner frame)
                   
                    <variable>vEventBoxOuter</variable>
                </eventbox>

            </frame> $(: outer frame)

            <variable>vEventBoxOuter</variable>
        </eventbox>

        <hbox>
            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true"
              can-default="true" has-default="true">
                <label> _Quit </label>
                <input file icon="gtk-quit"></input>
                <width>16</width>
                <action function="exit">QUIT</action>
            </button>
            <text xpad="5"><label>""</label></text>
        </hbox>

        <entry visible="false">
            <default>false</default>
            <variable>vDateFormatToggle</variable>
            <input>fnDateFormatToggle</input>
        </entry>

    </vbox>


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

#== Styles ==========================================
export GTK2_RC_FILES=${LOCAL_DATA_DIR}/gtkrc
cat <<-'EOSTYLEDEF' > ${GTK2_RC_FILES}
style "outerFrameStyle" {
    bg[NORMAL]      = "slateblue"
    fg[NORMAL]      = "white"
    font_name="URW Gothic L Book 14"
}
style "innerFrameStyle" {
    bg[NORMAL]      = "white"
    fg[NORMAL]      = "navy"
    font_name="URW Gothic L Demi Oblique 10"
}

widget_class "<GtkWindow><GtkVBox><GtkEventBox>" style "outerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame>" style "outerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkLabel>" style "outerFrameStyle"
# below will not show background of the eventbox if eventbox attribute visible="false"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox>" style "innerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox><GtkFrame>" style "innerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox><GtkFrame><GtkLabel>" style "innerFrameStyle"
EOSTYLEDEF

## -- post UI construction ---------------------
# fnDateFormatToggleNeeded '--rfc-3339=ns' or '-R'
fnDateFormatToggleNeeded() {
    local vWhatFormat="${1}"
    [[ "${vDateFormatToggle}" == "${vWhatFormat}" ]] && echo "false" || echo "true"
}
export -f fnDateFormatToggleNeeded

# fnDateFormatToggle
fnDateFormatToggle() {
    [[ "${vDateFormatToggle}" == "${_DATE_FORMAT_RFC3339_NS}" ]] && echo "${_DATE_FORMAT_TIMESTAMP}" || echo "${_DATE_FORMAT_RFC3339_NS}"
}
export -f fnDateFormatToggle

# fnShowTimestamp
fnShowTimestamp() {
    date ${vDateFormatToggle}
}
export -f fnShowTimestamp

# -- 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

## EOSCRIPT

/tmp/scripts/ex34.sh

Executing this example unmodified produces the window which looks like that shown earlier – reproduced for completeness:

Because some of the attributes of the EventBox, such as the above-child and the visible-window do not produce visual effects but affect the behaviour of the application it will be necessary to describe the outcome rather than simply show a screenshot of it.

Click the Refresh and the Update buttons to see the window title change in accordance with the date formatting logic triggered by each. Note that the title window changes to display a timestamp and that the format of the timestamp changes depending on which of the two buttons is clicked.

Now change the value of the attribute above-child to “true” and click the two buttons as before. The code fragment here after this change is made is reproduced below:

<eventbox above-child="true" visible-window="true" has-tooltip="true"
           tooltip-markup="This is <b>tooltip</b> for eventbox"
           height-request="100" sensitive="true" visible="true" width-request="600">
                    <frame  Inner Frame  >
                        <hbox>

Note that clicking on the “Update” button produces no effect. The above-child=”true” places all elements contained inside the eventbox “beneath” it so that it is the EventBox object that receives mouse clicks. If the event box had a signal handler attached to it, as it does not in this case, this event handler would intercept the clicks. Since there is no handler the clicks are simply ignored. Note however that if you use the Tab key to “tab to” the “Update” button, and it is selected, then pressing the Enter key will cause the action associated with this button to be executed event when the above-child is set to true.

Now change the above-child back to false and visible-window to true, as shown in the code fragment below, then run the example.

<eventbox above-child="false" visible-window="false"
          has-tooltip="true" tooltip-markup="This is <b>tooltip</b> for eventbox"
          height-request="100" sensitive="true" visible="true" width-request="600">

Note that the appearance of the window has changed:

Because the inner EventBox’s visible-window property is set to false the visual effects of the EventBox are not there – the background of the EventBox, which was white when the visible-window property was true is no longer white. The area of the inner EventBox takes on the appearance of the outer EventBox with its background colour of slateblue. But, the inner eventbox still intercepts the clicks made inside its borders even though the above-child is set to false – click on the Update button and see.

Now set the outer eventbox visible-window attribute to false and see the effect on the background colour and on the clickability of the Refresh button and run the example – the Refresh button is no longer clickable and the entire area of the outer EventBox, with its contained inner EventBox, has the default window colour.

Now set the inner eventbox visible-window attribute to true and run the example. The background colour of the inner EventBox is white but the Update button is still not clickable even though both eventboxes have the above-child attribute set to false.

This behaviour is, to me at least, counterintuitive but that’s what it is. Go figure.

Using eventbox to create a framed “help window”

A handy trick, which nested eventbox elements afford, is the ability to create a coloured “frame” around arbitrary content. The example below takes advantage of this trick to create a framed “help window”.

The screenshot below shows what is meant:

The window with the red border and the white background is shown when the Help button is clicked. Clicking on it or anywhere else other than on the Help button makes this window disappear.

The red border and the white background to the content inside are accomplished with the aid of the nested eventbox objects.

Execute the following commands, including copying and pasting the body of the example between geany and EOSCRIPT.

Note the sections highlighted in bold. These are the major adifferences between this example and the previous example.

mkdir /tmp/scripts
cd /tmp/scripts
> /tmp/scripts/ex35.sh
chmod ug+x /tmp/scripts/ex35.sh

geany /tmp/scripts/ex35.sh & # until ## EOSCRIPT
#!/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"
export HELP_DIALOG_FILE="${LOCAL_DATA_DIR}/HELP_DIALOG_FILE_${USER}_${BASHPID}.gtkdialog"

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

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

# Size of the notebook and all content panels
#
_WIDTH_REQUEST=400
export _DATE_FORMAT_RFC3339_NS="--rfc-3339=ns"
export _DATE_FORMAT_TIMESTAMP="+%Y%m%d-%H%M%S.%N%z"

# construct UI
## Help UI
##
# window title
WIN_TITLE="Help on Event Box Example"

## =========================================================
## construct help panel
##
cat > ${HELP_DIALOG_FILE} <<-EODECK
<window 
    decorated="false" 
    skip_taskbar_hint="true"
    window_position="${GTK_WIN_POS_CENTER}"

    <eventbox xwidth-request="200" name="outer_colour" above-child="false" visible-window="true" visible="true">
        <vbox border-width="3">
            <eventbox name="inner_colour" above-child="false" visible-window="true" visible="true">
                <text xpad="6" ypad="6" xalign="${XALIGN_LEFT}" justify="${GTK_JUSTIFY_LEFT}" selectable="false" use-markup="true">
                    <label>
"<span size='large'><b>${WIN_TITLE}</b></span>

This text goes into the help popup. This text goes into the help popup. This text goes into the help popup.
This text goes into the help popup.
This text goes into the help popup.
This text goes into the help popup.
This text goes into the help popup.

<span size='x-small'>Enjoy.</span>"
                    </label>
                </text>
            </eventbox>
        </vbox>
    </eventbox>

    <variable>HELP_DIALOG</variable>
    <action signal="button-press-event" condition="command_is_true( [[ \$PTR_BTN == 1 ]] && echo true )">closewindow:HELP_DIALOG</action> 
    <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">closewindow:HELP_DIALOG</action> 
    <action signal="focus-out-event">closewindow:HELP_DIALOG</action>
</window>
EODECK
export HELP_DIALOG=$(cat ${HELP_DIALOG_FILE})

## main UI
##
# window title
WIN_TITLE="EventBox Example"

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 width-request="${_WIDTH_REQUEST}">

        <hbox>
            <hbox homogenous="true" space-fill="true" space-expand="true">
                <text><label>Some content goes here</label></text>
            </hbox>
        </hbox>

        $(: visible-window="false" blocks delivery of clicks to the content objects, including inner eventbox objects )
        $(: but it does not prevent keystrokes from selecting buttons and pressing on them, for example )
        <eventbox above-child="false" visible-window="true">
            <frame  Outer Frame  >
                <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> _Refresh </label>
                        <input file icon="system-config-date"></input>
                        <width>16</width>
                        <action condition="command_is_true(fnDateFormatToggleNeeded '${_DATE_FORMAT_RFC3339_NS}' )">refresh:vDateFormatToggle</action>
                        <action function="refresh">vWindow</action>
                    </button>
                    <text xpad="1"><label>""</label></text>
                </hbox>

                $(: both above-child and visible-window have to be true for the event box to "block" clicks to the content )
                $(: both above-child and visible-window have to be true for the event box background colour to be seen )
                <eventbox above-child="false" visible-window="true" has-tooltip="true" tooltip-markup="This is <b>tooltip</b> for eventbox"
                          height-request="100" sensitive="true" visible="true" width-request="600">
                    <frame  Inner Frame  >
                        <hbox>
                            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true">
                                <label> _Update </label>
                                <input file icon="system-config-date"></input>
                                <width>16</width>
                                <action condition="command_is_true(fnDateFormatToggleNeeded '${_DATE_FORMAT_TIMESTAMP}')">refresh:vDateFormatToggle</action>
                                <action function="refresh">vWindow</action>
                            </button>
                        </hbox>

                    </frame> $(: inner frame)
                 
                    <variable>vEventBoxOuter</variable>
                </eventbox>

            </frame> $(: outer frame)

            <variable>vEventBoxOuter</variable>
        </eventbox>

        <hbox>
            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true">
                <label> _Help </label>
                <input file icon="gtk-help"></input>
                <width>16</width>
                <action>launch:HELP_DIALOG</action>
            </button>

            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true"
              can-default="true" has-default="true">
                <label> _Quit </label>
                <input file icon="gtk-quit"></input>
                <width>16</width>
                <action function="exit">QUIT</action>
            </button>
            <text xpad="5"><label>""</label></text>
        </hbox>

        $(: 'invisible entry box for persisting the state of date format togge')
        <entry visible="false">
            <default>false</default>
            <variable>vDateFormatToggle</variable>
            <input>fnDateFormatToggle</input>
        </entry>

    </vbox>

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

#== Styles ==========================================
export GTK2_RC_FILES=${LOCAL_DATA_DIR}/gtkrc
cat <<-'EOSTYLEDEF' > ${GTK2_RC_FILES}
style "outerFrameStyle" {
    bg[NORMAL]      = "slateblue"
    fg[NORMAL]      = "white"
    font_name="URW Gothic L Book 14"
}
style "innerFrameStyle" {
    bg[NORMAL]      = "white"
    fg[NORMAL]      = "navy"
    font_name="URW Gothic L Demi Oblique 10"
}

widget_class "<GtkWindow><GtkVBox><GtkEventBox>" style "outerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame>" style "outerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkLabel>" style "outerFrameStyle"
# below will not show background of the eventbox if eventbox attribute visible="false"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox>" style "innerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox><GtkFrame>" style "innerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox><GtkFrame><GtkLabel>" style "innerFrameStyle"

style "outer_colour" { bg[NORMAL] = "#ff0000" }
widget "*outer_colour" style "outer_colour"
style "inner_colour" { bg[NORMAL] = "#ffffff" }
widget "*inner_colour" style "inner_colour"

EOSTYLEDEF

## -- post UI construction ---------------------
# fnDateFormatToggleNeeded '--rfc-3339=ns' or '-R'
fnDateFormatToggleNeeded() {
    local vWhatFormat="${1}"
    [[ "${vDateFormatToggle}" == "${vWhatFormat}" ]] && echo "false" || echo "true"
}
export -f fnDateFormatToggleNeeded

# fnDateFormatToggle
fnDateFormatToggle() {
    [[ "${vDateFormatToggle}" == "${_DATE_FORMAT_RFC3339_NS}" ]] && echo "${_DATE_FORMAT_TIMESTAMP}" || echo "${_DATE_FORMAT_RFC3339_NS}"
}
export -f fnDateFormatToggle

# fnShowTimestamp
fnShowTimestamp() {
    date ${vDateFormatToggle}
}
export -f fnShowTimestamp

# -- 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

##EOSCRIPT

 

/tmp/scripts/ex35.sh

There are a number of objects and patterns, apart from the eventbox, which this example introduces. They will not be discussed here. Of passing note are the use of the <eventbox><vbox><eventbox> construct to create the red border around the inner eventbox and its content, the escape key and focus-out-event handlers and the launch:HELP_WINDOW in the main window’s Help button, and closewindow:HELP_WINDOW in the help window itself.

Run the example and click the Help button. The application will launch the help window and make it disappear when the the escape key is pressed or when the mouse is clicked anywhere outside the boundaries of the Help button.

Using eventbox to create a Popup Menu

In this example we expand the use of the nested evenboxes to create a right-click popup menu.

The screenshot below shows what is meant:

The window with the red border and the white background, and the two checkboxes, is shown when the right mouse button is clicked. Setting and unsetting checkboxes is done as expected – click the checkbox or its label, use the keyboard to alternate between selecting one or the other checkbox, press enter or space to change the state of the selected checkbox. Clicking anywhere else outside the boundaries of the menu makes this menu disappear.

The menu and menu handling in this example was inspired by the example at http://blog.puppylinux.com/?viewDetailed=00030.

The red border and the white background to the content inside are accomplished with the aid of the nested eventbox objects. Checkboxes and their handling are the same regardless of what sor of window they are in.

In this example the state of the checkboxes must be persisted when the menu is closed. Since gtkdialog does not have the mechanism to have a signal handler directly programmatically set the value of a variable (push model) two functionas are introduced to set and get the value of the checkbox using a file in the file system for persistence. This was inspired by one of the gtkdialog examples and may be explored in another article. For now it should suffice to say that checking and unchecking one of the checkboxes casues the value of the checkbox (true/false, checked/unchecked) to be written to a corresponding file and the state of he checkboxes is read form these files whenever the popup menu is shown. The values of the checkboxes are read in the Refresh and Update button handler to control its logic.

Execute the following commands, including copying and pasting the body of the example between geany and EOSCRIPT.

Note the sections highlighted in bold. These are the major adifferences between this example and the previous example.

mkdir /tmp/scripts
cd /tmp/scripts
> /tmp/scripts/ex36.sh
chmod ug+x /tmp/scripts/ex36.sh

geany /tmp/scripts/ex36.sh & # until ## EOSCRIPT
#!/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"
export HELP_DIALOG_FILE="${LOCAL_DATA_DIR}/HELP_DIALOG_FILE_${USER}_${BASHPID}.gtkdialog"
export RCLK_DIALOG_FILE="${LOCAL_DATA_DIR}/RCLK_DIALOG_FILE_${USER}_${BASHPID}.gtkdialog"

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

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

# Size of the notebook and all content panels
#
_WIDTH_REQUEST=400
export _DATE_FORMAT_RFC3339_NS="--rfc-3339=ns"
export _DATE_FORMAT_TIMESTAMP="+%Y%m%d-%H%M%S.%N%z"

##### construct UI #############################
## pre-ui ccreate functions
# show right-click menu on right mouse button press in main window
#
fnAddRightClickActionsInline() {
echo "<action signal=\"button-press-event\" condition=\"command_is_true( [[ \$PTR_BTN == 3 ]] && echo true )\">\
launch:RIGHT_CLICK_DIALOG\
</action>"
}

:<<'NONFUNCTIONAL--------------------------------------------------------'
    fnUtlVarGet
    -----------
    Get a variable [maintained as a file] from the local data directory.
    On entry: $1 = name
    On exit: echoes value
NONFUNCTIONAL--------------------------------------------------------
fnUtlVarGet() {
    [[ ! -e $LOCAL_DATA_DIR/$1 ]] && touch $LOCAL_DATA_DIR/$1
    local input
    read -r input < $LOCAL_DATA_DIR/$1
    echo -n "$input"
}
export -f fnUtlVarGet
 
:<<'NONFUNCTIONAL--------------------------------------------------------'
    fnUtlVarSet
    -----------
    Set a variable [maintained as a file] within the local data directory.
    On entry: $1 = name
              $2 = value
NONFUNCTIONAL--------------------------------------------------------
fnUtlVarSet() {
    [[ ! -e $LOCAL_DATA_DIR/$1 ]] && touch $LOCAL_DATA_DIR/$1
    echo "$2" > $LOCAL_DATA_DIR/$1
}
export -f fnUtlVarSet
## right-click settings UI
##
 
## =========================================================
## construct right-click menu
##

fnUtlVarSet 'cbkShowRequest' "false"   # init right-click menu checkboxes to unset
fnUtlVarSet 'cbkShowResponse' "false"  # init right-click menu checkboxes to unset

cat > ${RCLK_DIALOG_FILE} <<-EODECK
<window 
    decorated="false" 
    skip_taskbar_hint="true"
    window_position="${GTK_WIN_POS_MOUSE}"


$(:<<'COMMENT--------------------------------------------------'
This stuff was inspired by Gtkdialog right-click menu - http://blog.puppylinux.com/?viewDetailed=00030

COMMENT--------------------------------------------------
)
    <eventbox xwidth-request="200" name="outer_colour" above-child="false" visible-window="true" visible="true">
        <vbox border-width="3">
            <eventbox name="inner_colour" above-child="false" visible-window="true" visible="true">
                <vbox border-width="10">
                    <vbox>
                        <checkbox tooltip-text="Selecting this option will display the Requests which will govern date format" label="Show Date Format Request?" xalign="${XALIGN_LEFT}">
                            <variable>cbkShowRequest</variable>
                            <action condition="active_is_true(cbkShowRequest)">fnUtlVarSet 'cbkShowRequest' 'true'</action>
                            <action condition="active_is_false(cbkShowRequest)">fnUtlVarSet 'cbkShowRequest' 'false'</action>
                            <input>fnUtlVarGet 'cbkShowRequest'</input>
                        </checkbox>
                    </vbox>
                    <vbox>
                        <checkbox tooltip-text="Selecting this option will display the formatted date" label="Show Formatted Date?" xalign="${XALIGN_LEFT}">
                            <variable>cbkShowResponse</variable>
                            <action condition="active_is_true(cbkShowResponse)">fnUtlVarSet 'cbkShowResponse' 'true'</action>
                            <action condition="active_is_false(cbkShowResponse)">fnUtlVarSet 'cbkShowResponse' 'false'</action>
                            <input>fnUtlVarGet 'cbkShowResponse'</input>
                        </checkbox>
                    </vbox>
                </vbox>
            </eventbox>
        </vbox>
    </eventbox>
 
    <variable>RIGHT_CLICK_DIALOG</variable>

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

export RIGHT_CLICK_DIALOG=$(cat ${RCLK_DIALOG_FILE})

## Help UI
##
# window title
WIN_TITLE="Help on Event Box Example"

## =========================================================
## construct help panel
##
cat > ${HELP_DIALOG_FILE} <<-EODECK
<window 
 decorated="false" 
 skip_taskbar_hint="true"
 window_position="${GTK_WIN_POS_CENTER}"
> 
 <eventbox xwidth-request="200" name="outer_colour" above-child="false" visible-window="true" visible="true">
 <vbox border-width="3">
 <eventbox name="inner_colour" above-child="false" visible-window="true" visible="true">
 <text xpad="6" ypad="6" xalign="${XALIGN_LEFT}" justify="${GTK_JUSTIFY_LEFT}" selectable="false" use-markup="true">
 <label>
"<span size='large'><b>${WIN_TITLE}</b></span>

This text goes into the help popup. This text goes into the help popup. This text goes into the help popup.
This text goes into the help popup.
This text goes into the help popup.
This text goes into the help popup.
This text goes into the help popup.

<span size='x-small'>Enjoy.</span>"
 </label>
 </text>
 </eventbox>
 </vbox>
 </eventbox>

 <variable>HELP_DIALOG</variable>
 <action signal="button-press-event" condition="command_is_true( [[ \$PTR_BTN == 1 ]] && echo true )">closewindow:HELP_DIALOG</action> 
 <action this-is-window="escape" signal="key-press-event" condition="command_is_true( [[ \$KEY_RAW = 0x9 ]] && echo true )">closewindow:HELP_DIALOG</action> 
 <action signal="focus-out-event">closewindow:HELP_DIALOG</action>
</window>
EODECK
export HELP_DIALOG=$(cat ${HELP_DIALOG_FILE})
## main UI
##
# window title
WIN_TITLE="EventBox Example"

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 width-request="${_WIDTH_REQUEST}">

        <hbox>
            <hbox homogenous="true" space-fill="true" space-expand="true">
                <text><label>Some content goes here</label></text>
            </hbox>
        </hbox>

        $(: visible-window="false" blocks delivery of clicks to the content objects, including inner eventbox objects )
        $(: but it does not prevent keystrokes from selecting buttons and pressing on them, for example )
        <eventbox above-child="false" visible-window="true">
            <frame  Outer Frame  >
                <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> _Refresh </label>
                        <input file icon="system-config-date"></input>
                        <width>16</width>
                        <action condition="command_is_true(fnDateFormatToggleNeeded '${_DATE_FORMAT_RFC3339_NS}' )">refresh:vDateFormatToggle</action>
                        <action function="refresh">vWindow</action>
                    </button>
                    <text xpad="1"><label>""</label></text>
                </hbox>

                $(: both above-child and visible-window have to be true for the event box to "block" clicks to the content )
                $(: both above-child and visible-window have to be true for the event box background colour to be seen )
                <eventbox above-child="false" visible-window="true" has-tooltip="true" tooltip-markup="This is <b>tooltip</b> for eventbox"
                          height-request="100" sensitive="true" visible="true" width-request="600">
                    <frame  Inner Frame  >
                        <hbox>
                            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true">
                                <label> _Update </label>
                                <input file icon="system-config-date"></input>
                                <width>16</width>
                                <action condition="command_is_true(fnDateFormatToggleNeeded '${_DATE_FORMAT_TIMESTAMP}')">refresh:vDateFormatToggle</action>
                                <action function="refresh">vWindow</action>
$(fnAddRightClickActionsInline eventbox)
                            </button>
                        </hbox>

                    </frame> $(: inner frame)

                    <variable>vEventBoxOuter</variable>
                </eventbox>
            </frame> $(: outer frame)

            <variable>vEventBoxOuter</variable>
$(fnAddRightClickActionsInline eventbox)
        </eventbox>

        <hbox>
            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true">
                <label> _Help </label>
                <input file icon="gtk-help"></input>
                <width>16</width>
                <action>launch:HELP_DIALOG</action>
            </button>

            <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true"
              can-default="true" has-default="true">
                <label> _Quit </label>
                <input file icon="gtk-quit"></input>
                <width>16</width>
                <action function="exit">QUIT</action>
            </button>
            <text xpad="5"><label>""</label></text>
        </hbox>

        $(: 'invisible entry box for persisting the state of date format togge')
        <entry visible="false">
            <default>false</default>
            <variable>vDateFormatToggle</variable>
            <input>fnDateFormatToggle</input>
        </entry>

    </vbox>

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

#== Styles ==========================================
export GTK2_RC_FILES=${LOCAL_DATA_DIR}/gtkrc
cat <<-'EOSTYLEDEF' > ${GTK2_RC_FILES}
style "outerFrameStyle" {
    bg[NORMAL]      = "slateblue"
    fg[NORMAL]      = "white"
    font_name="URW Gothic L Book 14"
}
style "innerFrameStyle" {
    bg[NORMAL]      = "white"
    fg[NORMAL]      = "navy"
    font_name="URW Gothic L Demi Oblique 10"
}

widget_class "<GtkWindow><GtkVBox><GtkEventBox>" style "outerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame>" style "outerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkLabel>" style "outerFrameStyle"
# below will not show background of the eventbox if eventbox attribute visible="false"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox>" style "innerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox><GtkFrame>" style "innerFrameStyle"
widget_class "<GtkWindow><GtkVBox><GtkEventBox><GtkFrame><GtkContainer><GtkEventBox><GtkFrame><GtkLabel>" style "innerFrameStyle"

style "outer_colour" { bg[NORMAL] = "#ff0000" }
widget "*outer_colour" style "outer_colour"
style "inner_colour" { bg[NORMAL] = "#ffffff" }
widget "*inner_colour" style "inner_colour"

EOSTYLEDEF

## -- post UI construction ---------------------
# fnDateFormatToggleNeeded '--rfc-3339=ns' or '-R'
fnDateFormatToggleNeeded() {
    local vWhatFormat="${1}"
    [[ "${vDateFormatToggle}" == "${vWhatFormat}" ]] && echo "false" || echo "true"
}
export -f fnDateFormatToggleNeeded

# fnDateFormatToggle
fnDateFormatToggle() {
    [[ "${vDateFormatToggle}" == "${_DATE_FORMAT_RFC3339_NS}" ]] && echo "${_DATE_FORMAT_TIMESTAMP}" || echo "${_DATE_FORMAT_RFC3339_NS}"
}
export -f fnDateFormatToggle

# fnShowTimestamp
fnShowTimestamp() {
    [[ "${cbkShowRequest}" = "true" ]] && yad \
        --center --width=400 --image="gtk-dialog-info" --window-icon="gtk-dialog-info" --title="Date format" --text="Requesting date format ${vDateFormatToggle}" \
        --button=" Dismiss!gtk-ok!Dismiss this dialogue:0"
    vDate="$(date ${vDateFormatToggle})"
    [[ "${cbkShowResponse}" = "true" ]] && yad \
        --center --width=400 --image="gtk-dialog-info" --window-icon="gtk-dialog-info" --title="Formatted Date" --text="Formatted date: ${vDate}" \
        --button=" Dismiss!gtk-ok!Dismiss this dialogue:0"
    echo ${vDate}
}
export -f fnShowTimestamp

# -- 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
## EOSCRIPT

/tmp/scripts/ex36.sh

The screenshot shown before illustrates the application appearance when the right-click menu is show. In the screenshot below one of the results of checking the checkbox is shown, as an illustration of what the button handler does when the checkboxes is checked.

Using eventboxes to add frames to objects

It may be hard to work out what space the various objects in a gtkdialog application actually occupy on the screen because most, like text labels, horizontal and vertical boxes, and others, do not have visible bounding boxes which could be used to gauge that. In this example I demonstrate hos nested event boxes can be used to add visible borders to all objects and how to quickly remove them / make them invisible. This use of eventboxes is somewhat off-field but somebody muight have a use for it, during development if not in an actual useful applications.

The following screenshot illustrates one of the manifestations of this.

Execute the following commands, including copying and pasting the body of the example between geany and EOSCRIPT.

mkdir /tmp/scripts
cd /tmp/scripts
> /tmp/scripts/ex37.sh
chmod ug+x /tmp/scripts/ex37.sh

geany /tmp/scripts/ex37.sh & # until ## EOSCRIPT
#!/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

# local constants
#
_WIDTH_REQUEST=400
_SPACING=0

##### construct UI #############################

## pre-ui create functions
##
#_beginFrame() { echo -n '<eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">'; }
#endFrame_() { echo -n '</hbox></eventbox></vbox></eventbox>'; }
_beginFrame() { echo -n ''; }
endFrame_() { echo -n ''; }

## right-click settings UI
##

## main UI
##
# window title
WIN_TITLE="EventBox Example $(basename $0)"

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">
$(_beginFrame)
    <vbox width-request="${_WIDTH_REQUEST}" spacing="${_SPACING}">
$(_beginFrame)
        <vbox spacing="${_SPACING}">
$(_beginFrame)
            <hbox spacing="${_SPACING}">
$(_beginFrame)
                <hbox spacing="${_SPACING}">
$(_beginFrame)
                    <text ypad="10" xpad="50" xalign="${XALIGN_LEFT}"><label>Some content goes here</label></text>
$(endFrame_)
$(_beginFrame)
                    <text xpad="50" label=""></text>
$(endFrame_)
                </hbox>
$(endFrame_)
            </hbox>
$(endFrame_)
        </vbox>
$(endFrame_)

$(_beginFrame)
        <vbox spacing="${_SPACING}">
$(_beginFrame)
            <hbox spacing="${_SPACING}">
$(_beginFrame)
                <hbox spacing="${_SPACING}">
$(_beginFrame)
                    <text xpad="30" label="CCCC"></text>
$(endFrame_)
$(_beginFrame)
                    <text xpad="30" label="BBBB"></text>
$(endFrame_)
$(_beginFrame)
                    <text xpad="30" label="AAAA"></text>
$(endFrame_)
$(_beginFrame)
                    <text xpad="45" label=""></text>
$(endFrame_)
                </hbox>
$(endFrame_)
            </hbox>
$(endFrame_)
        </vbox>
$(endFrame_)

$(_beginFrame)
        <vbox spacing="${_SPACING}">
$(_beginFrame)
            <hbox spacing="${_SPACING}">
$(_beginFrame)
                <button width-request="100" homogenous="true" image-position="${BTN_IMG_POS_LEFT}" use-underline="true"
                  can-default="true" has-default="true">
                    <label> _Quit </label>
                    <input file icon="gtk-quit"></input>
                    <width>16</width>
                    <action function="exit">QUIT</action>
                </button>
$(endFrame_)
$(_beginFrame)
                <text xpad="5"><label>""</label></text>
$(endFrame_)
            </hbox>
$(endFrame_)
        </vbox>

$(endFrame_)
    </vbox>
$(endFrame_)

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

#== Styles ==========================================
export GTK2_RC_FILES=${LOCAL_DATA_DIR}/gtkrc
cat <<-'EOSTYLEDEF' > ${GTK2_RC_FILES}
style "outer_colour" { bg[NORMAL] = "blue" }
widget "*outer_colour" style "outer_colour"
style "inner_colour" { bg[NORMAL] = "green" }
widget "*inner_colour" style "inner_colour"

EOSTYLEDEF

## -- post UI construction ---------------------
# fnDateFormatToggleNeeded '--rfc-3339=ns' or '-R'
fnDateFormatToggleNeeded() {
    local vWhatFormat="${1}"
    [[ "${vDateFormatToggle}" == "${vWhatFormat}" ]] && echo "false" || echo "true"
}
export -f fnDateFormatToggleNeeded

# fnDateFormatToggle
fnDateFormatToggle() {
    [[ "${vDateFormatToggle}" == "${_DATE_FORMAT_RFC3339_NS}" ]] && echo "${_DATE_FORMAT_TIMESTAMP}" || echo "${_DATE_FORMAT_RFC3339_NS}"
}
export -f fnDateFormatToggle

# fnShowTimestamp
fnShowTimestamp() {
    [[ "${cbkShowRequest}" = "true" ]] && yad \
        --center --width=400 --image="gtk-dialog-info" --window-icon="gtk-dialog-info" --title="Date format" --text="Requesting date format ${vDateFormatToggle}" \
        --button=" Dismiss!gtk-ok!Dismiss this dialogue:0"
    vDate="$(date ${vDateFormatToggle})"
    [[ "${cbkShowResponse}" = "true" ]] && yad \
        --center --width=400 --image="gtk-dialog-info" --window-icon="gtk-dialog-info" --title="Formatted Date" --text="Formatted date: ${vDate}" \
        --button=" Dismiss!gtk-ok!Dismiss this dialogue:0"
    echo ${vDate}
}
export -f fnShowTimestamp

# -- 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

## EOSCRIPT

 

/tmp/scripts/ex37.sh

Executing this script using the command above produces:

It is difficult to say, looking at the screenshot, what are the boundaries of the various objects which are present inside the window.

Let’s consider the following code snippet:

$(_beginFrame)
    <vbox width-request="${_WIDTH_REQUEST}" spacing="${_SPACING}">
$(_beginFrame)
        <vbox spacing="${_SPACING}">
$(_beginFrame)
            <hbox spacing="${_SPACING}">
$(_beginFrame)
                <hbox spacing="${_SPACING}">
$(_beginFrame)
                    <text ypad="10" xpad="50" xalign="${XALIGN_LEFT}"><label>Some content goes here</label></text>
$(endFrame_)
$(_beginFrame)
                    <text xpad="50" label=""></text>
$(endFrame_)
                </hbox>
$(endFrame_)
            </hbox>
$(endFrame_)
        </vbox>
$(endFrame_)

Note the $(_beginFrame) and $(endFrame_) constructs.

Bash functions with the names _beginFrame and endFrame_ are executed at the time the gtkdialog XML file is being written using the 'cat <<-EODECK > ${MAIN_DIALOG_FILE}' “here document” construct. Whatever these functions write to the stdout becomes injected into the here document at that place.

In the following code fragment in the source given above note that the first pair of _beginFrame and endFrame_ funcions is commented out and the second pair is “active”. The active functions write an empty string to their stdout consequently only a newline is injected into the gtkdialog definition at the places they are invoked.

## pre-ui create functions
##
#_beginFrame() { echo -n '<eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">'; }
#endFrame_() { echo -n '</hbox></eventbox></vbox></eventbox>'; }
_beginFrame() { echo -n ''; }
endFrame_() { echo -n ''; }

 Execute the following on the command line to see this. The following fragment illustrates this:

/tmp/scripts/ex37.sh --dump | cat -n | more

     5        <vbox width-request="400" spacing="0">
     6   
     7            <vbox spacing="0">
     8   
     9                <hbox spacing="0">
    10   
    11                    <hbox spacing="0">
    12   
    13                        <text ypad="10" xpad="50" xalign="0"><label>Some content goes here</label></text>
    14   
    15   
    16                        <text xpad="50" label=""></text>
    17   
    18                    </hbox>
    19   
    20                </hbox>
    21   
    22            </vbox>

Now comment out the active functions and uncomment the inactive functions as shown:

## pre-ui create functions
##
_beginFrame() { echo -n '<eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">'; }
endFrame_() { echo -n '</hbox></eventbox></vbox></eventbox>'; }
#_beginFrame() { echo -n ''; }
#endFrame_() { echo -n ''; }

Execute the same command as before and note the output.

     4    <eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">
     5        <vbox width-request="400" spacing="0">
     6    <eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">
     7            <vbox spacing="0">
     8    <eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">
     9                <hbox spacing="0">
    10    <eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">
    11                    <hbox spacing="0">
    12    <eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">
    13                        <text ypad="10" xpad="50" xalign="0"><label>Some content goes here</label></text>
    14    </hbox></eventbox></vbox></eventbox>
    15    <eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">
    16                        <text xpad="50" label=""></text>
    17    </hbox></eventbox></vbox></eventbox>
    18                    </hbox>
    19    </hbox></eventbox></vbox></eventbox>
    20                </hbox>
    21    </hbox></eventbox></vbox></eventbox>
    22            </vbox>
    23    </hbox></eventbox></vbox></eventbox>

 Text in bold highlights the code which was injected into the definition of the UI.

Executing the example produces the visual appearance shown in the earlier screenshot, repeated here:

To understand what this code does let’s have a look at the artefacts which are “injected”.

The _beginFrame function emits the following code, here distributed over multiple lines for ease of reading:

<eventbox name="outer_colour" border-width="1">
    <vbox border-width="1">
        <eventbox name="inner_colour" border-width="1">
            <hbox border-width="1">

The outer eventbox sets the background colour of all it contains to whatever the style defines for the “outer_colour”, which in the example happens to be blue.

The vbox with the width of 1 causes a 1 pixel wide border to be shown. That border’s colour is inherited from the outer eventbox and is of the colour that eventbox’s style defines.

The inner eventbox sets the background colour of all of its content to the inner_colour, which happens to be green.

The innermost hbox adds a 1 pixel border around anything it contains and that border inherits the inner eventbox’s background colour.

Not the green 1 pixel border around the Quit button.

The injected _beginFrame code “starts” definitions of 4 containers. These containers must be “finished”/”closed” so the endFrame_ function supplied the closures:

            </hbox>
        </eventbox>
    </vbox>
</eventbox>

Now change the code for the _beginFrame so that it reads as follows and execute the example:

## pre-ui create functions
##
_beginFrame() { echo -n '<eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="0">'; }
endFrame_() { echo -n '</hbox></eventbox></vbox></eventbox>'; }

The ending hbox no longer provides the 1 pixel green inner border so the application window now looks like:

Note that, for all intents and purposes the green 1 pixel border around the Quit button is gone. The 1 pixel green borders around all the other components is also gone, though it is harder to see. Scroll back to the previous screenshot and compare them.

Now change the definitno so that the vbox border is 0 pixels and execute the example:

## pre-ui create functions
##
_beginFrame() { echo -n '<eventbox name="outer_colour" border-width="1"><vbox border-width="0"><eventbox name="inner_colour" border-width="1"><hbox border-width="0">'; }
endFrame_() { echo -n '</hbox></eventbox></vbox></eventbox>'; }

This results in the following appearance:

The blue lines are even closer together.

Now change the border-width of the outer eventbox to 0 and execute the example:

_beginFrame() { echo -n '<eventbox name="outer_colour" border-width="0"><vbox border-width="0"><eventbox name="inner_colour" border-width="1"><hbox border-width="0">'; }
endFrame_() { echo -n '</hbox></eventbox></vbox></eventbox>'; }

Now change the border-width of the inner eventbox to 0 and execute the example:

_beginFrame() { echo -n '<eventbox name="outer_colour" border-width="0"><vbox border-width="0"><eventbox name="inner_colour" border-width="0"><hbox border-width="0">'; }
endFrame_() { echo -n '</hbox></eventbox></vbox></eventbox>'; }

The remaining blue areas are there because:

  1. The first text element definition adds 10 pixels padding above and below the text, ‘<text ypad=”10″ ‘ but the second does not
  2. The button is “taller” than the text to the right of it

Reset the values of the 4 border-width attribtes back to 1.

Notice that the vbox and hbox objects which are injected by the _beginFrame function carry the spacing=”${_SPACING}” attribute. In the original source the value of this attribute is defined as 0:

_SPACING=0

Change the value to 10 and execute the example:

_SPACING=10

Notice that extra space was added between the vboxes and between the hboxes.

Comment out the _beginFrame and endFrame_ functions which inject the extra code, uncomment the _beginFrame and endFrame_ functions which do not inject the code and execute the example.

###_beginFrame() { echo -n '<eventbox name="outer_colour" border-width="1"><vbox border-width="1"><eventbox name="inner_colour" border-width="1"><hbox border-width="1">'; }
###endFrame_() { echo -n '</hbox></eventbox></vbox></eventbox>'; }
_beginFrame() { echo -n ''; }
endFrame_() { echo -n ''; }

Compare the original windows and the window with the extra spacing added by the hbox and vbox elements.

If one does not have a way to see the bounding boxes of the various elements it is pretty much impossible to see what adds spaces, where and how much.

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.

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.

May 06

In this article I explore the gtkdialog Notebook object, its attributes and actions, through increasingly more sophisticated examples. In passing, other gtkdialog objects are used without much elaboration.

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.

gtkdialog, based as it is on the GTK object model, leverages the GTK object hierarchy. Consider the reference for the Notebook object at http://01micko.com/reference/notebook.html.

The very first link points to a GtkNotebook – https://developer.gnome.org/gtk2/2.24/GtkNotebook.html.

In the GTK object hierarchy, https://developer.gnome.org/gtk2/2.24/GtkNotebook.html#GtkNotebook.object-hierarchy, the Notebook object is some levels below the GObject, the topmost object.

As briefly discussed in the previous article the GtkNotebook, and consequently the gtkdialog Notebook object inherit, as I understand it, from the objects higher up in the hierarchy. This will come into play when we explore attributes that can be set for a Notebook, and actions that can be configured for a Notebook.

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)

Notebook Attributes

The gtkdialog Notebook object is a visible container of a sort. Its purpose it to provide a tabbed container for collections of other  objects.

An example of a tabbed interface, which uses the Notebook object and which will be developed in this article, is shown here as a bit of a teaser.

Reference documentation for the gtkdialog Notebook object, http://01micko.com/reference/notebook.html, provides a table of attributes specific to this gtkdialog object and makes a reference to ancestor class properties.

Attributes that can be configured for the notebook widget are defined in the widget’s reference page see https://developer.gnome.org/gtk2/2.24/GtkNotebook.html, section Properties – https://developer.gnome.org/gtk2/2.24/GtkNotebook.html#GtkNotebook.properties

Some of them are useful and some of them are not, as far as I am concerned or can empirically determine. Changing values of some of them does not have visible consequences.

Not all attributes/properties are recognised and acted upon by  gtkdialog. Some of the properties are inherited from the parent objects, like GtkContainer and GtkWidget. For example, border-width, which defines the amount of space between the notebook frame and the notebook content, is inherited from the GtkContainer.

Similarly, sensitive property is inherited from the GtkWidget and when set to false effectively disables everything inside the notebook container.

See the reference page: file:///home/demo/gtkdialog-0.8.3/doc/reference/notebook.html

To work out what attributes are actually supported, in this case by the notebook widget, we need to do some exploration. Discussion below goes into this to an extent and I provided most of the useful attributes that can be set, both from GtkWindow itself and inherited from GtkContainer and GtkWidget. The “Works?” column indicates whether the attribute has a visible effect (Y), does not have a visible effect (N) or I have not figured out a way to test whether it works or not.

The table below summarised Notebook attributes/properties and indicates which work with the gtkdialog and which do not as far as I can tell. “Do not” may well be a function of me not seeing any visible difference with the property set to different values. Some ancestor attributes/properties which are not really useable from gtkdialog are omitted.

Name Description Value Default Works? In
Notebook
space-expand Pack widget expanding into space true or false Y gtkdialog
space-fill Pack widget filling space true or false Y gtkdialog
block-function-signals Block signal emissions from functions true or false gtkdialog
file-monitor Emit signal when input file(s) change true or false gtkdialog
auto-refresh Auto refresh when input file(s) change true or false gtkdialog
tab-base-index Tab label start page number Integer N

 

gtkdialog
tab-labels Tab labels label0 | label1 | … Y gtkdialog
tab-prefix Tab label prefix N gtkdialog
tab-suffix Tab label suffix N gtkdialog
Inherited from GtkNotebook
enable-popup If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page true or false false Y GtkNotebook
group-id Group ID for tabs drag and drop >= -1 -1 GtkNotebook
group-name Group name for tabs drag and drop String NULL GtkNotebook
homogeneous Whether tabs should have homogeneous sizes true or false false Y GtkNotebook
page The index of the current page >= -1 -1 Y GtkNotebook
scrollable If TRUE, scroll arrows are added if there are too many tabs to fit true or false false Y GtkNotebook
show-border Whether the border should be shown or not true or false true N GtkNotebook
show-tabs Whether tabs should be shown or not true or false true Y GtkNotebook
tab-border Width of the border around the tab labels Int 2 y GtkNotebook
tab-hborder Width of the horizontal border of tab labels Int 2 Y GtkNotebook
tab-pos Which side of the notebook holds the tabs GtkPositionType

0 GTK_POS_LEFT

1 GTK_POS_RIGHT

2 GTK_POS_TOP

3 GTK_POS_BOTTOM

GTK_POS_TOP Y GtkNotebook
tab-vborder Width of the vertical border of tab labels Int 2 Y GtkNotebook
GtkNotebook – child – there is no such thing in dtkdialog, it seems
detachable Whether the tab is detachable true or false false
menu-label The string displayed in the child’s menu entry String NULL N
position The index of the child in the parent >=-1 -1 N
reorderable Whether the tab is reorderable by user action or not true or false false N
tab-expand Whether to expand the child’s tab or not true or false false N
tab-fill Whether the child’s tab should fill the allocated area or not true or false true N
tab-label The string displayed on the child’s tab label String NULL N
GtkNotebook – Style – no such thing?
arrow-spacing spacing between the scroll arrows and the tabs >=0 0 N
has-backward-stepper determines whether the standard backward arrow button is displayed true or false true N
has-forward-stepper determines whether the standard forward arrow button is displayed true or false true N
has-secondary-backward-stepper determines whether a second backward arrow button is displayed on the opposite end of the tab area true or false false N
has-secondary-forward-stepper determines whether a second forward arrow button is displayed on the opposite end of the tab area true or false false N
tab-curvature defines size of tab curvature >=0 1 N
tab-overlap defines size of tab overlap area Int 2 N
Inherited from gtkContainer
border-width The width of the empty border outside the containers children <=65535 0 Y
can-default Whether the widget can be the default widget.

This does not seem to have any effect when applied to a Window object

true or false false GtkWidget
can-focus Whether the widget can accept the input focus.

This does not seem to have any effect when applied to a Window object

true or false false GtkWidget
has-default Whether the widget is the default widget.

This does not seem to have any effect when applied to a Window object

true or false false GtkWidget
has-focus Whether the widget has the input focus.

This does not seem to have any effect when applied to a Window object

true or false false GtkWidget
has-tooltip Whether this widget has a tooltip.

Disables tooltip display if tooltip is defined for the Window

true or false false Y GtkWidget
height-request Override for height request of the widget, or -1 if natural height should be used.

Has not effect of default-height is specified.

>=0 -1 Y GtkWidget
is-focus Whether the widget is the focus widget within the toplevel.

Does not seem to have any effect when applied to the Window object

true or false false GtkWidget
name The name of the widget.

It can be used to address styling directves to a named object

string NULL GtkWidget
receives-default If TRUE, the widget will receive the default action when it is focused.

I don’t know what that is supposed to accomplish. Nothing visible happens regardless of the property value.

true or false false GtkWidget
sensitive Whether the widget responds to input.

FALSE disables all components contained in the window.

true or false TRUE Y GtkWidget
tooltip-markup The contents of the tooltip for this widget.

Simple markup can be used to call out parts of tooltip text in.

See http://www.murga-linux.com/puppy/viewtopic.php?t=40418 for a markup explorer tool.

Ineffective if tooltip-disabled is TRUE.

string NULL Y GtkWidget
tooltip-text The contents of the tooltip for this widget.

Ineffective if tooltip-disabled is TRUE.

string NULL Y GtkWidget
visible Whether the widget is visible.

It is somewhat self-defeating to make a window invisible on creation unless it is a “subsidiary” window and can be made visible form some other piece of logic.

true or false true Y GtkWidget
width-request Override for width request of the widget, or -1 if natural width should be used.

Has not effect if default-width is specified.

>=0 -1 Y GtkWidget

The following example includes just about every attribute that can be defined for a Notebook object. Experiment with the various settings to see what you can see.

First, let’s create an enumerations script which will supply human-readable versions of enumerations and other numeric constants that may be used in the scripts I show. See https://github.com/GNOME/gtk/blob/master/gdk/gdktypes.h, and elsewhere, for sources of these enumerations.

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 defined a bunch
# see https://github.com/GNOME/gtk/blob/master/gdk/gdktypes.h for specifics
#
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

To create and exercise the first example execute the following commands, noting that you will need to copy and paste the code into an editor window because it is too large for a copy/paste as a “here document”. Explore the effects of changing the various attributes of the Notebook object.

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

geany ex30.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" xx-tab-labels="which will be shown on tabs"
                    page="1" xx-page="0 based tab number which will be initially shown"
                    enable-popup="true" xx-enable-popup="right-click will bring a list of tabs to select from"
                    show-border="true" xx-show-border="does not seem to do anything visible"
                    tab-border="2" xx-tab-border="how much space around text of tab label"
                    tab-hborder="2" xx-tab-hborder="how much space to the left and right of the tab label text"
                    tab-vborder="2" xx-tab-vborder="how much space to above and below 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" xx-homogeneous="make the width of tabs the same"
                    space-fill="true"
                    space-expand="true"
                    scrollable="false" xx-scrollable="if notebook container width less than the width of all tabs adds left/right arrows to navigate between tabs"
                    tab-pos="${TAB_POS_ABOVE}"
                    border-width="0"
                    sensitive="true"
                    tooltip-markup="<span size='small'>This is an example of a <b>Notebook</b></span>"
                    has-tooltip="true"
                    visible="true"
                    >

$(: TAB 1 ------------------------------------)
                    <vbox xx-notbook-tab="Tab 1">
                        <vbox spacing="2">
                            <text><label>Requests Panels Go Here</label></text>
                        </vbox>
                    </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">
                        <vbox>
                            <text><label>Settings Panels Go Here</label></text>
                        </vbox>
                    </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

# -- 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/ex30.sh

Exercising this example unmodified presents the following initial screen:

Changing the following attributes to the following values will produce a somewhat different UI:

page="0" xx-page="0 based tab number which will be initially shown"
tab-hborder="12" xx-tab-hborder="how much space to the left and right of the tab label text"
tab-pos="${TAB_POS_LEFT}"
border-width="10"

Note that the right-click list of tab names, which can be used for navigation between the tabs just as clicking on the tabs can, is enabled by setting the enable-popup attribute to true.

page=”0″ causes the first tab to be the tab shown by default.

tab-border=”12″ adds space on either side of the label text.

tab-pos determines where the tabs will be shown. The enumeration is:

TAB_POS_LEFT=0
TAB_POS_RIGHT=1
TAB_POS_ABOVE=2
TAB_POS_BELOW=3

border-width adds whitespace on all sides of the notebook.

Explore other values of the various attributes and see what you can see.

Click the tabs, hover the mouse over the area of the notebook to see the tooltip, etc..

Showing and Hiding Notebook Tabs

The example in the previous section showed a pretty ordinary use of the Notebook. More sophisticated applications can be created by for example hiding notebook tabs until they are needed, showing them on demand and hiding them again when not required.

This example explores exactly this use case but it also shows how the “last visible tab” can be “remembered” and activated when the visible-on-demand tab is hidden again.

The example uses variables associated with the various objects like hbox, vbox, notebook and button to manipulate their associated objects dynamically to accomplish the desired effects.

The following screenshots show the various states of this application to illustrate what is expected.

This is the initial state of the application.

Clicking the cogwheel button will show the Settings tab, which is initially invisible.

Clicking the cogwheel button again hides the Settings tab and makes the tab which was active before the button was clicked and the settings tab was shown and activated, active again.

Recreate the application and convince yourself that this is indeed how the application behaves.

The source code and commands to recreate the example are shown below. Specific techniques will be discussed following the example code.

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

geany ex31.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

WIN_TITLE="XDS.b Registry Browser"

# set initial notebook page - this determines which tab is initially shown
echo 0 > ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE

# Common size of all notebook 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 container definition )

                 <notebook $(: begin notebook definition )
                    height-request="${_HEIGHT_REQUEST}"
                    width-request="${_WIDTH_REQUEST}"
                    tab-labels="Requests|Patients|Settings" xx-tab-labels="which will be shown on tabs"
                    page="1" xx-page="0 based tab number which will be initially shown"
                    enable-popup="true" xx-enable-popup="right-click will bring a list of tabs to select from"
                    show-border="true" xx-show-border="does not seem to do anything visible"
                    tab-border="2" xx-tab-border="how much space around text of tab label"
                    tab-hborder="2" xx-tab-hborder="how much space to the left and right of the tab label text"
                    tab-vborder="2" xx-tab-vborder="how much space to above and below 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" xx-homogeneous="make the width of tabs the same"
                    space-fill="true"
                    space-expand="true"
                    scrollable="false" xx-scrollable="if notebook container width less than the width of all tabs adds left/right arrows to navigate between tabs"
                    tab-pos="${TAB_POS_ABOVE}"
                    border-width="0"
                    sensitive="true"
                    tooltip-markup="<span size='small'>This is an example of a <b>Notebook</b></span>"
                    has-tooltip="true"
                    visible="true"
                    >

$(: TAB 1 ------------------------------------)
                    <vbox xx-notbook-tab="Tab 1">
                        <vbox spacing="2">
                            <text><label>Requests Panels Go Here</label></text>
                        </vbox>
                    </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 ------------------------------------)
                    $(: make this tab initially invisible by setting visible="false")
                    <vbox xx-notbook-tab="Tab 3" visible="false">
                        <vbox>
                            <text><label>Settings Panels Go Here</label></text>
                        </vbox>
                        $(: Variable name of the vbox/tab. )
                        $(: It is used to explicitly show/hide the vbox and its content )
                        <variable>vTab3</variable>
                    </vbox> $(: end-notbook-tab="Tab 3" )

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

                    <sensitive>true</sensitive>
                    $(: variable name of the notebook and all it contains )
                    $(: used to explicitly manage the notebook - like refreshing it )
                    <variable>vNotebook</variable>
                    $(: which tab to show when the control is first created and page=x attribute is not set )
                    $(: which tab to show when the control is refreshed )
                    <input file>${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</input>
                    $(: save the tab number when tabs are switched )
                    $(: used to resore the "last seen tab" when showing/hiding the settings tab )
                    <output file>${LOCAL_DATA_DIR}/NOTEBOOK_PAGE_OUT</output>
                    <action signal="focus-out-event" x-save-last-tab-number-in="output file">save:vNotebook</action>
                </notebook> $(: end Notebook definition )

$(: End Notebook definition -------------------)

        </hbox> $(: end Notebook container definition )
 
$(: Common object - shown below the notebook --)

        <hbox>

            $(: invisible entry for persisting the state of the toggle )
            $(: fnToggleSettingsTab toggles the value )
            <entry visible="false">
                <default>false</default>
                <variable>vSettingsToggle</variable>
                <input>fnToggleSettingsTab</input>
            </entry>
            $(: button which toggles between displying and hiding the settings tab )
            <button width-request="35" homogenous="true" image-position="0" use-underline="true"
              tooltip-text="Configure URLs and Credentials">
                <input file icon="ibus-setup"></input>
                <width>16</width>
                <variable>vSettingsButton</variable>
                $(: set toggle to opposite settings )
                <action>refresh:vSettingsToggle</action> 
                $(: hide the settings tab and restore the "last tab" value so that the tab shown before the button is clicked )
                $(: is shown when the settings tab is hidden )
                <action condition="command_is_false(echo \$vSettingsToggle)">hide:vTab3</action>
                <action condition="command_is_false(echo \$vSettingsToggle)">cp ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE_OUT ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</action> 
                $(: show the settings tab and save the "last tab" value so that the tab shown when the button is clicked )
                $(: is shown whent he settings are hidden again )
                <action condition="command_is_true(echo \$vSettingsToggle)">show:vTab3</action>
                <action condition="command_is_true(echo \$vSettingsToggle)">echo 2 > ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</action> 
                $(: cause the changes to be reflected in the UI )
                <action>refresh:vNotebook</action>
            </button>

            <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

# -- Post UI Assembly Functions - used from UI after it is assembled ------------------------------------
#
# --------------------
# fnToggleSettingsTab - flips settings toggle
# controls whether the settings tab is visible or hidden 
# switches value of the entry to which it is hooked between true and false each time it is invoked
#
fnToggleSettingsTab() {
    [[ "${vSettingsToggle}" == "true" ]] && echo "false" || echo "true"
}
export -f fnToggleSettingsTab

# -- 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/ex31.sh

Note the lines highlighted in bold. These are the lines by which the two examples differ.

Text in $(: … ) provides brief comments.

The following code fragment:

$(: make this tab initially invisible by setting visible="false")
<vbox xx-notbook-tab="Tab 3" visible="false">
    <vbox>
        <text><label>Settings Panels Go Here</label></text>
    </vbox>
    $(: Variable name of the vbox/tab. )
    $(: It is used to explicitly show/hide the vbox and its content )
    <variable>vTab3</variable>
</vbox> $(: end-notbook-tab="Tab 3" )

sets the visible attribute to false. This makes the Settings tab invisible when the application starts.

Adding the variable to the definition makes it possible to refer to this vbox object as an entity and is used to show and hide the vbox and its content.

The following code fragment:

                    <sensitive>true</sensitive>
                    $(: variable name of the notebook and all it contains )
                    $(: used to explicitly manage the notebook - like refreshing it )
                    <variable>vNotebook</variable>
                    $(: which tab to show when the control is first created and page=x attribute is not set )
                    $(: which tab to show when the control is refreshed )
                    <input file>${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</input>
                    $(: save the tab number when tabs are switched )
                    $(: used to restore the "last seen tab" when showing/hiding the settings tab )
                    <output file>${LOCAL_DATA_DIR}/NOTEBOOK_PAGE_OUT</output>
                    <action signal="focus-out-event" x-save-last-tab-number-in="output file">save:vNotebook</action>
                </notebook> $(: end Notebook definition )

$(: End Notebook definition -------------------)

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

defines the variable for the entire notebook object – vNotebook. This variable is used to trigger the “save” action, which gets executed whenever tabs are changed, and to refresh the notebook, which causes the focus-out-event to be delivered to the notebook object and thus cause the currently active tab number to be written to the output file.

Elsewhere the content of the output file is copied to the input file so that the correct tab is activated.

Consider the code below.

            $(: invisible entry for persisting the state of the toggle )
            $(: fnToggleSettingsTab toggles the value )
            <entry visible="false">
                <default>false</default>
                <variable>vSettingsToggle</variable>
                <input>fnToggleSettingsTab</input>
            </entry>

 First, we define an invisible “entry” object which we use to persist the “state” of the settings tab – hidden or shown.

Consider the related code below:

# -- Post UI Assembly Functions - used from UI after it is assembled ------------------------------------
#
# --------------------
# fnToggleSettingsTab - flips settings toggle
# controls whether the settings tab is visible or hidden 
# switches value of the entry to which it is hooked between true and false each time it is invoked
#
fnToggleSettingsTab() {
    [[ "${vSettingsToggle}" == "true" ]] && echo "false" || echo "true"
}
export -f fnToggleSettingsTab

Here we define a function which cases the value of the variable vSettingsToggle to be true or false, toggling between one and the other value each time the function is executed.

Consider the following code, which makes use of the foregoing:

            $(: button which toggles between displying and hiding the settings tab )
            <button width-request="35" homogenous="true" image-position="0" use-underline="true"
              tooltip-text="Configure URLs and Credentials">
                <input file icon="ibus-setup"></input>
                <width>16</width>
                <variable>vSettingsButton</variable>
                $(: set toggle to opposite settings )
                <action>refresh:vSettingsToggle</action> 
                $(: hide the settings tab and restore the "last tab" value so that the tab shown before the button is clicked )
                $(: is shown when the settings tab is hidden )
                <action condition="command_is_false(echo \$vSettingsToggle)">hide:vTab3</action>
                <action condition="command_is_false(echo \$vSettingsToggle)">cp ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE_OUT ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</action> 
                $(: show the settings tab and save the "last tab" value so that the tab shown when the button is clicked )
                $(: is shown whent he settings are hidden again )
                <action condition="command_is_true(echo \$vSettingsToggle)">show:vTab3</action>
                <action condition="command_is_true(echo \$vSettingsToggle)">echo 2 > ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</action> 
                $(: cause the changes to be reflected in the UI )
                <action>refresh:vNotebook</action>
            </button>

 Here we define a button with no label and with the icon image somewhat indicative of its function. The button has a variable so that it can be manipulated.

Each time the button is clicked the “refresh:vSettingsToggle” action causes the vSettingsToggle invisible entry to execute the function associated with its “input” attribute. This function alternates between emitting the literals true and the literal false to stdout, which sets the variable to the corresponding value.

The action_is_false condition checks the value of the vSettingsToggle and causes its action to be executed if the value is false. In this case the result is that the settings tab gets hidden and the saved value of the last active tab before the settings button was clicked is copied to the file used as input to the notebook object.

The action_is_true condition checks the value of the vSettingsToggle and causes its action to be executed if the value is true. In this case the result is that the settings tab gets shown and the value of the active tab is explicitly set to 2 (the third tab – 0-based numbering) in the file used as input to the notebook object.

Finally the refresh:vNotebook action is executed and as a consequence the UI is updated to show the results of the manipulations discussed above.

This example shows how notebook tabs can be shown and hidden on demand, how a button can be instrumented to conditionally execute actions on other objects and how active tab can be explicitly set at runtime depending on the needs of the UI and the logic it implements.

Showing and Hiding the entire Notebook

In this example the entire notebook is alternately hidden and replaced with a completely different object, of the same size, and then shown again. The example shows how a “toggle” button can be simulated with two buttons of the same size which are alternately shown and hidden.

This is not so much an example of Notebook use as it is an example of dynamically replacing content of the window with different content of the same size, simulating dynamic screen refresh.

Consider the screenshots:

Click the “Show Info” button and see the entire Notebook disappear and get replaced with different content, and the text of the “Show Info” button label apparently changing to “Hide Info”. Notice, too, the “settings” button getting disabled and re-enabled depending on whether the Notbook content is visible.

Execute the following commands to recreate the application.

mkdir -pv /tmp/scripts
cd /tmp/scripts
> ex32.sh
chmod ug+x ex32.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

WIN_TITLE="XDS.b Registry Browser"

# set initial notebook page - this determines which tab is initially shown
echo 0 > ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE

# Common size of all notebook 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 container definition )

                 <notebook $(: begin notebook definition )
                    height-request="${_HEIGHT_REQUEST}"
                    width-request="${_WIDTH_REQUEST}"
                    tab-labels="Requests|Patients|Settings" xx-tab-labels="which will be shown on tabs"
                    page="1" xx-page="0 based tab number which will be initially shown"
                    enable-popup="true" xx-enable-popup="right-click will bring a list of tabs to select from"
                    show-border="true" xx-show-border="does not seem to do anything visible"
                    tab-border="2" xx-tab-border="how much space around text of tab label"
                    tab-hborder="2" xx-tab-hborder="how much space to the left and right of the tab label text"
                    tab-vborder="2" xx-tab-vborder="how much space to above and below 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" xx-homogeneous="make the width of tabs the same"
                    space-fill="true"
                    space-expand="true"
                    scrollable="false" xx-scrollable="if notebook container width less than the width of all tabs adds left/right arrows to navigate between tabs"
                    tab-pos="${TAB_POS_ABOVE}"
                    border-width="0"
                    sensitive="true"
                    tooltip-markup="<span size='small'>This is an example of a <b>Notebook</b></span>"
                    has-tooltip="true"
                    visible="true"
                    >

$(: TAB 1 ------------------------------------)
                    <vbox xx-notbook-tab="Tab 1">
                        <vbox spacing="2">
                            <text><label>Requests Panels Go Here</label></text>
                        </vbox>
                    </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 ------------------------------------)
                    $(: make this tab initially invisible by setting visible="false")
                    <vbox xx-notbook-tab="Tab 3" visible="false">
                        <vbox>
                            <text><label>Settings Panels Go Here</label></text>
                        </vbox>
                        $(: Variable name of the vbox/tab. )
                        $(: It is used to explicitly show/hide the vbox and its content )
                        <variable>vTab3</variable>
                    </vbox> $(: end-notbook-tab="Tab 3" )

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

                    <sensitive>true</sensitive>
                    $(: variable name of the notebook and all it contains )
                    $(: used to explicitly manage the notebook - like refreshing it )
                    <variable>vNotebook</variable>
                    $(: which tab to show when the control is first created and page=x attribute is not set )
                    $(: which tab to show when the control is refreshed )
                    <input file>${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</input>
                    $(: save the tab number when tabs are switched )
                    $(: used to resore the "last seen tab" when showing/hiding the settings tab )
                    <output file>${LOCAL_DATA_DIR}/NOTEBOOK_PAGE_OUT</output>
                    <action signal="focus-out-event" x-save-last-tab-number-in="output file">save:vNotebook</action>
                </notebook> $(: end Notebook definition )

$(: End Notebook definition -------------------)

            <variable>vHBoxNotebook</variable>
        </hbox> $(: end Notebook container definition )

$(: alternative content - replaces the Notebook when shown - initially invisible )
        <hbox 
            height-request="${_HEIGHT_REQUEST}"
            width-request="${_WIDTH_REQUEST}"
            homogeneous="true" sensitive="true" visible="false" 
            >
            <vbox width-request="${_WIDTH_REQUEST}">
                <text><label>Patient Details Go Here</label></text>
            </vbox>
            <variable>vHBoxInfo</variable>
        </hbox> $(: vHBoxInfo)

$(: Common object - shown below the notebook and alternate content --)

        <hbox>

            $(: the two buttons below work as a toggle and switch between the notebook object and the text object )
            $(: onlt one button is visible at a time - Both occupy the same space giving is the "toggle" effect )
            $(: This button hides the notebook and the "show info " button, and shows the info panel in its place )
            <button visible="true" sensitive="true" width-request="100" homogenous="true" image-position="0"use-underline="true">
                <label> _Show Info </label>
                <input file icon="gtk-apply"></input>
                <width>16</width>
                <variable>vBtnShowInfo</variable>
                <action>hide:vHBoxNotebook</action> $(: hide notebook )
                <action>hide:vBtnShowInfo</action>  $(: hide self [button] )
                <action>show:vHBoxInfo</action>     $(: show info panel )
                <action>show:vBtnHideInfo</action>  $(: show button that will hide info panel again )
                <action>disable:vSettingsButton</action> $(: disable show settings button - notebook is hidden )
            </button>
            $(: This button hides the info panel and the "hide info " button, and shows the notebook back again )
            <button visible="false" sensitive="true" width-request="100" homogenous="true" image-position="0"use-underline="true">
                <label> _Hide Info </label>
                <input file icon="gtk-apply"></input>
                <width>16</width>
                <variable>vBtnHideInfo</variable>
                <action>hide:vHBoxInfo</action>
                <action>hide:vBtnHideInfo</action>
                <action>show:vHBoxNotebook</action>
                <action>show:vBtnShowInfo</action>
                <action>enable:vSettingsButton</action> $(: enable show settings button - notebook is visible )
            </button>

            $(: invisible entry for persisting the state of the toggle )
            $(: fnToggleSettingsTab toggles the value )
            <entry visible="false">
                <default>false</default>
                <variable>vSettingsToggle</variable>
                <input>fnToggleSettingsTab</input>
            </entry>
            $(: button which toggles between displying and hiding the settings tab )
            <button width-request="35" homogenous="true" image-position="0" use-underline="true"
              tooltip-text="Configure URLs and Credentials">
                <input file icon="ibus-setup"></input>
                <width>16</width>
                <variable>vSettingsButton</variable>
                $(: set toggle to opposite settings )
                <action>refresh:vSettingsToggle</action>
                $(: hide the settings tab and restore the "last tab" value so that the tab shown before the button is clicked )
                $(: is shown when the settings tab is hidden )
                <action condition="command_is_false(echo \$vSettingsToggle)">hide:vTab3</action>
                <action condition="command_is_false(echo \$vSettingsToggle)">cp ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE_OUT ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</action>
                $(: show the settings tab and save the "last tab" value so that the tab shown when the button is clicked )
                $(: is shown whent he settings are hidden again )
                <action condition="command_is_true(echo \$vSettingsToggle)">show:vTab3</action>
                <action condition="command_is_true(echo \$vSettingsToggle)">echo 2 > ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</action>
                $(: cause the changes to be reflected in the UI )
                <action>refresh:vNotebook</action>
            </button>

            <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

# -- Post UI Assembly Functions - used from UI after it is assembled ------------------------------------
#

# --------------------
# fnToggleSettingsTab - flips settings toggle
# controls whether the settings tab is visible or hidden
# switches value of the entry to which it is hooked between true and false each time it is invoked
#
fnToggleSettingsTab() {
    [[ "${vSettingsToggle}" == "true" ]] && echo "false" || echo "true"
}
export -f fnToggleSettingsTab

# -- 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/ex32.sh

In this example an extra hbox with alternate content and two buttons were added to the example ex31 to accomplish what was required. Inspect the code and figure out what gets done. The embedded comments provide guidance.

More sophisticated example

The code for example whose screenshot was presented at the beginning of this article as a tease is provided below. Explore the example to see how the notebook object and the techniques discussed in this article can be put to use in developing a useful application. The code related to interaction with the backends was removed and replaced with data stubs as not relevant to the gtkdialog discussion.

mkdir -pv /tmp/scripts
cd /tmp/scripts
> notebook.sh
chmod ug+x notebook.sh
geany notebook.sh & # until ## EOSOURCE
#!/bin/bash

:<<'COMMENT == ====================================================='
    XDS.b Registry Browser
COMMENT == =====================================================

appVersion=1.0.4
echo "Loading ${BASH_SOURCE[0]} version ${appVersion} ..." 1>&2
echo "Starting ${0} version ${appVersion} ..." 1>&2

# make bash strict
set -e
set -u
set -o pipefail

# this idiom adds a --debug flag and defines the expansion of dbg - if expanded to : rest of command is ignored
[ "--debug" = "${1:-}" ] && export dbg="" || export dbg=:

# make ERR trap global
set -o errtrace

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

fnHlrRemoveTmporaryFilesOnExit() {
    # remove all temporary files by blowing away the directory which contains them
    verify="" # -v
    mkdir -p ${verify} ${LOCAL_DATA_DIR}
    rm ${verify} -R -f ${LOCAL_DATA_DIR}
}
# Trap the signal "zero"
trap fnHlrRemoveTmporaryFilesOnExit 0 # clean up after any program end.

fnHlrProcessERR(){
    MYSELF="$0"   # equals to my script name
    LASTLINE="$1" # argument 1: last line of error occurence
    LASTERR="$2"  # argument 2: error code of last command
    echo "${MYSELF}: ${FUNCNAME} : line ${LASTLINE}: exit status of last command: ${LASTERR}" 1>&2
    local frame=0
    while caller $frame; do
      ((frame++));
    done
    echo "$*"
    fnHlrRemoveTmporaryFilesOnExit
}
export -f fnHlrProcessERR
# Trap the ERR signal - not as usefull as all that, but
trap 'fnHlrProcessERR 859 4175' ERR

export LC_ALL=C # disable unicode support
export oldPOSIXLY_CORRECT=${POSIXLY_CORRECT:-}
$dbg echo '${POSIXLY_CORRECT}-->>'"${POSIXLY_CORRECT:-}"'<<<---' 1>&2
unset POSIXLY_CORRECT   # disable posix compliance - it prevents process substitution working

# name temporary files
#
export MAIN_DIALOG_FILE="${LOCAL_DATA_DIR}/MAIN_DIALOG_FILE_${USER}_${BASHPID}.gtkdialog"
export baseSOAPExchangeFileName="rls_browser_${USER}_${BASHPID}"
export SOAPRequestFileName="${LOCAL_DATA_DIR}/${baseSOAPExchangeFileName}_request.xml"
export SOAPResponseFileName="${LOCAL_DATA_DIR}/${baseSOAPExchangeFileName}_response.xml"
export SOAPErrorFileName="${LOCAL_DATA_DIR}/${baseSOAPExchangeFileName}_response.error"
export SOAPResponseFormattedFileName="${LOCAL_DATA_DIR}/${baseSOAPExchangeFileName}_response.xml.xml"

$dbg echo '$MAIN_DIALOG_FILE--->>>'"$MAIN_DIALOG_FILE"'<<<---'
$dbg echo '$baseSOAPExchangeFileName--->>>'"$baseSOAPExchangeFileName"'<<<---'
$dbg echo '$SOAPRequestFileName--->>>'"$SOAPRequestFileName"'<<<---'
$dbg echo '$SOAPResponseFileName--->>>'"$SOAPResponseFileName"'<<<---'
$dbg echo '$SOAPErrorFileName--->>>'"$SOAPErrorFileName"'<<<---'
$dbg echo '$SOAPResponseFormattedFileName--->>>'"$SOAPResponseFormattedFileName"'<<<---'

# store config properties in the same directory as the script - not checking for permissions
#
export selfConfigPath="${0%%.sh}.properties"
export patientXSLPath="${0%%.sh}.xsl"

# -- Pre UI Assembly Functions - used to provide UI text ------------------------------------
#
fnUtlVarGet() {
    [[ ! -e $LOCAL_DATA_DIR/$1 ]] && touch $LOCAL_DATA_DIR/$1
    local input
    read -r input < $LOCAL_DATA_DIR/$1
    echo -n "$input"
}
export -f fnUtlVarGet

fnUtlVarSet() {
    [[ ! -e $LOCAL_DATA_DIR/$1 ]] && touch $LOCAL_DATA_DIR/$1
    echo "$2" > $LOCAL_DATA_DIR/$1
}
export -f fnUtlVarSet

evbb(){ echo '<eventbox>'; }
evbe(){ echo '</eventbox>'; }
#evbb(){ echo ''; }
#evbe(){ echo ''; }

# --
fnLoadConfiguration() {
    export gvDBHost="localhost"
    export gvDBPort="1521"
    export gvDBService="adtdb"
    export gvLOGDBUsername="LOG"
    export gvLOGDBPassword="password"
    export gvPIXServiceURL="http://pixservice:7654/PIXService"
    export gvOHMPIDomainOID="1.2.3.4.5.6.7.8.9"
}
fnLoadConfiguration

## first time init
fnUtlVarSet "PATIENT_DB_QUERY_FIRST_TIME" "TRUE"
fnUtlVarSet "REQUEST_DB_QUERY_FIRST_TIME" "TRUE"

# patient info file - start empty
echo '' > ${LOCAL_DATA_DIR}/PATIENT_INFO

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

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

WIN_TITLE="XDS.b Registry Browser"

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

_HEIGHT_REQUEST=375 # 425
_WIDTH_REQUEST=570 # 490

# 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" xx-homogeneous="make the width of tabs the same"
            sensitive="true"
            visible="true"
            >
                <notebook $(: start notebook )
                    height-request="${_HEIGHT_REQUEST}" 
                    width-request="${_WIDTH_REQUEST}" 
                    tab-labels="Requests|Patients|Settings" xx-tab-labels="works"
                    xxxpage="0" xx-page="works, 0 based tab number which will be initially shown"
                    enable-popup="true" xx-enable-popup="right-click will bring a list of tabs to select from"
                    show-border="true" xx-show-border="does not seem to do anything visible"
                    tab-border="2" xx-tab-border="how much space around text of tab label"
                    tab-hborder="2" xx-tab-hborder="how much space to the left and right of the tab label text"
                    tab-vborder="2" xx-tab-vborder="how much space to above and below 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" xx-homogeneous="make the width of tabs the same"
                    space-fill="true"
                    space-expand="true"
                    scrollable="false" xx-scrollable="if notebook container width less than the width of all tabs adds left/right arrows to navigate between tabs"
                    tab-pos="${TAB_POS_ABOVE}"
                    border-width="0"
                    sensitive="true"
                    xx-tooltip-markup="<span size='small'>This is a <b>Tooltip</b></span>"
                    xx-has-tooltip="true"
                    visible="true"
                    >

$(: TAB 1 ------------------------------------)

                    <vbox begin-notbook-tab="Tab 1">
                        <vbox spacing="2">
                            <hbox>
                                <button width-request="100" xalign="${XALIGN_LEFT}" can-default="true" has-default="true"
                                space-expand="false" space-fill="false" use-underline="true"
                                tooltip-text="Find requests received by XDS Registry" can-default="true"
                                has-default="true">
                                    <width>16</width>
                                    <input file icon="gtk-find"></input>
                                    <label> _Find </label>
                                    <variable export="false">btnFind</variable>
                                    <action type="clear">treeResultsRequestsList</action>
                                    <action type="refresh">treeResultsRequestsList</action>
                                </button>
                                <text xpad="3">
                                    <label>""</label>
                                </text>
                            </hbox>

                            <frame  Registry Requests  >
                                <hbox homogenous="true" space-expand="true" space-fill="true" space-expand="true"
                                  selectable="true" can-focus="true"
                                  height-request="280">
                                    <tree homogeneous="true" space-expand="true" space-fill="true" rules-hint="true"
                                      xalign="${XALIGN_CENTRE}" headers-visible="true" headers-clickable="true"
                                      hover-expand="false" hover-selection="false"
                                      selection-mode="${TREE_SELECTION_MODE_SINGLE}" vscrollbar-policy="${VSCROLLBAR_AUTOMATIC}"
                                      selectable="true" can-focus="true"
                                      exported-column="2" stock-id="gtk-dialog-question" auto-refresh="true"
                                      column-type="string|string|string|string|string"
                                      column-visible="true|true|false|true|true"
                                      column-sizing="1|1|1|1|0"
                                      >
                                        <label>"Request|From IP|Message Id|Timestamp|Pass"</label>
                                        <variable>treeResultsRequestsList</variable>
                                        <input>fnDoGetRequests</input>
                                        <action function="clear">vPatientInfo</action>
                                        <action>fnShowRequestInfo "\$treeResultsRequestsList"</action>
                                    </tree>
                                </hbox>
                            </frame>
                        </vbox>
                        <variable>vTab1</variable>
                    </vbox> $(: end-notbook-tab="Tab 1" )

$(: TAB 2 -----------------------------------)

                    <vbox xx-notbook-tab="Tab 2">
                        <vbox spacing="2">
                            <hbox>
                                <button width-request="100" xalign="${XALIGN_LEFT}" can-default="true" has-default="true" space-expand="false" space-fill="false" use-underline="true"
                                tooltip-text="Find Patients registered in XDS Registry" can-default="true" has-default="true">
                                    <width>16</width>
                                    <input file icon="gtk-find"></input>
                                    <label> _Find </label>
                                    <variable export="false">btnFind</variable>
                                    <action type="clear">treeResultsIDsList</action>
                                    <action type="refresh">treeResultsIDsList</action>
                                </button>
                                <text xpad="3">
                                    <label>""</label>
                                </text>
                            </hbox>

                            <frame  Registered Patients  >
                                <hbox homogenous="true" space-expand="true" space-fill="true" selectable="true" can-focus="true"
                                  height-request="280">
                                    <tree homogeneous="true" space-expand="true" space-fill="true" rules-hint="true"
                                      xalign="${XALIGN_CENTRE}" headers-visible="true" headers-clickable="true"
                                      hover-expand="false" hover-selection="false"
                                      selection-mode="${TREE_SELECTION_MODE_SINGLE}" vscrollbar-policy="${VSCROLLBAR_AUTOMATIC}"
                                      selectable="true" can-focus="true"
                                      exported-column="1" stock-id="gtk-dialog-question" auto-refresh="true"
                                      column-type="string|string|string|string"
                                      column-visible="false|true|false|true"
                                      column-sizing="1|1|1|0"
                                      >
                                        <label>"UUID|Patient Id|LastUpdated|Status"</label>
                                        <variable>treeResultsIDsList</variable>
                                        <input>fnDoGetPatientIDs</input>
                                        <action>fnShowPatientInfo "\$treeResultsIDsList"</action>

                                        <action>disable:vSettingsButton</action>
                                        <action>hide:vHBoxNotebook</action>

                                        <action>show:vHBoxInfo</action>
                                        <action>show:vBtnHideInfo</action>

                                        <action>refresh:vPatientInfo</action>
                                    </tree>
                                </hbox>
                            </frame>
                        </vbox>
                        <variable>vTab2</variable>
                    </vbox>

$(: TAB 3 ------------------------------------)

                    <vbox xx-notbook-tab="Tab 3" visible="false" enabled="false" comment="This is where applicaiton properties go">
                        <hbox>
                            <vbox homogenous="true" space-fill="true" space-expand="true">
                                <frame   XDS Registry Database   >
                                    <vbox height-request="85" x-width-request="220">
                                        <hbox spacing="2">
                                            $(evbb)<text><label>Hostname</label></text>$(evbe)
                                            $(evbb)<entry can-focus="true" has-focus="true" has-frame="false">
                                                <input>echo \$gvDBHost</input>
                                                <variable>vDBHost</variable>
                                                <width>120</width>
                                                <height>22</height>
                                            </entry>$(evbe)
                                        </hbox>
                                        <hbox spacing="2">
                                            $(evbb)<text><label>Port Number</label></text>$(evbe)
                                            $(evbb)<entry has-frame="false">
                                                <input>echo \$gvDBPort</input>
                                                <variable>vDBPort</variable>
                                                <width>60</width>
                                                <height>22</height>
                                            </entry>$(evbe)
                                            $(evbb)<text width-request="58"><label>""</label></text>$(evbe)
                                        </hbox>
                                        <hbox spacing="2">
                                            $(evbb)<text><label>Service Name</label></text>$(evbe)
                                            $(evbb)<entry has-frame="false">
                                                <input>echo \$gvDBService</input>
                                                <variable>vDBService</variable>
                                                <width>70</width>
                                                <height>22</height>
                                            </entry>$(evbe)
                                            $(evbb)<text width-request="48"><label>""</label></text>$(evbe)
                                        </hbox>
                                    </vbox>
                                </frame>
                            </vbox>
                           
                            <vbox homogenous="true" space-fill="true" space-expand="true">
                                <frame   LOG Database Schema   >
                                    <vbox height-request="85" x-width-request="220">
                                        <hbox spacing="2">
                                            $(evbb)<text><label>Username</label></text>$(evbe)
                                            $(evbb)<entry has-frame="false">
                                                <input>echo \$gvLOGDBUsername</input>
                                                <variable>vLOGDBUsername</variable>
                                                <width>120</width>
                                                <height>22</height>
                                            </entry>$(evbe)
                                        </hbox>
                                        <hbox spacing="2">
                                            $(evbb)<text><label>Password</label></text>$(evbe)
                                            $(evbb)<entry caps-lock-warning="true" has-frame="false">
                                                <input>echo \$gvLOGDBPassword</input>
                                                <variable>vLOGDBPassword</variable>
                                                <width>120</width>
                                                <height>22</height>
                                                <sensitive>password</sensitive>
                                            </entry>$(evbe)
                                        </hbox>
                                        <hbox>
                                            <button image-position="0" x="valid 0-3 - left, right, above, below"
                                  use-underline="true"
                                  >
                                                <label> T_est Connectivity </label>
                                                <input file icon="gtk-network"></input>
                                                <width>16</width>
                                                <action>fnTestDBConnectivity "true" "\$vDBHost" "\$vDBPort" "\$vDBService" "\$vLOGDBUsername" "\$vLOGDBPassword"</action>
                                            </button>
                                        </hbox>
                                    </vbox>
                                </frame>
                            </vbox>
                        </hbox>

                        <frame   PIX Service  >
                            <vbox height-request="60">
                                <hbox spacing="2">
                                    $(evbb)<text><label>OHMPI Domain OID</label></text>$(evbe)
                                    $(evbb)<entry caps-lock-warning="true" has-frame="false">
                                        <input>echo \$gvOHMPIDomainOID</input>
                                        <variable>vOHMPIDomainOID</variable>
                                        <width>340</width>
                                        <height>22</height>
                                    </entry>$(evbe)
                                </hbox>
                                <hbox spacing="2">
                                    $(evbb)<text><label>URL</label></text>$(evbe)
                                    $(evbb)<entry caps-lock-warning="true" has-frame="false">
                                        <input>echo \$gvPIXServiceURL</input>
                                        <variable>vPIXServiceURL</variable>
                                        <width>440</width>
                                        <height>22</height>
                                    </entry>$(evbe)
                                </hbox>
                                <hbox>
                                    <button image-position="0" x="valid 0-3 - left, right, above, below"
                                  use-underline="true"
                                  >
                                        <label> Test C_onnectivity </label>
                                        <input file icon="gtk-network"></input>
                                        <width>16</width>
                                        <action>fnTestURLConnectivity "true" "\$vPIXServiceURL"</action>
                                    </button>
                                </hbox>
                            </vbox>
                        </frame>
                        <variable>vTab3</variable>
                    </vbox>

                    <sensitive>true</sensitive>
                    <variable>vNotebook</variable>
                    <input file>${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</input>
                    <output file>${LOCAL_DATA_DIR}/NOTEBOOK_PAGE_OUT</output>
                    <action signal="focus-out-event" x-save-last-tab-number-in="output file">save:vNotebook</action>
                </notebook>

            <variable>vHBoxNotebook</variable>
        </hbox> $(: vHBoxNotebook )

        <hbox height-request="${_HEIGHT_REQUEST}"
            width-request="${_WIDTH_REQUEST}"
            show-border="true" xx-show-border="does not seem to do anything visible"
            homogeneous="true" xx-homogeneous="make the width of tabs the same"
            sensitive="true"
            visible="false"
            >
            <vbox width-request="${_WIDTH_REQUEST}">
                <text><label>Patient Details</label></text>
              <frame>
                <tree file-monitor="false" auto-refresh="false" vscrollbar-policy="${VSCROLLBAR_NEVER}"
                      homogeneous="true" space-expand="true" space-fill="true" rules-hint="true" headers-visible="false"
                      headers-clickable="false" hover-expand="false" hover-selection="false" selection-mode="0"
                      >
                    <label>a|b</label>
                    <variable>vPatientInfo</variable>
                    <input file>${LOCAL_DATA_DIR}/PATIENT_INFO</input>
                    <sensitive>true</sensitive>
                </tree>
              </frame>
            </vbox>
            <variable>vHBoxInfo</variable>
        </hbox> $(: vHBoxInfo)

        <hbox>
            <button visible="false" sensitive="true" width-request="100" homogenous="true" image-position="0"use-underline="true">
                <label> _Dismiss </label>
                <input file icon="gtk-apply"></input>
                <width>16</width>
                <variable>vBtnHideInfo</variable>
                <action>hide:vHBoxInfo</action>
                <action>show:vHBoxNotebook</action>
                <action>hide:vBtnHideInfo</action>
                <action>enable:vSettingsButton</action>
            </button>

            <button width-request="35" homogenous="true" image-position="0" use-underline="true"
              tooltip-text="Configure URLs and Credentials">
                <input file icon="ibus-setup"></input>
                <width>16</width>
                <variable>vSettingsButton</variable>
                <action>refresh:vSettingsToggle</action> $(: set toggle to opposite settings )
                <action condition="command_is_false(echo \$vSettingsToggle)">hide:vTab3</action>
                <action condition="command_is_false(echo \$vSettingsToggle)">cp ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE_OUT ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</action>
                <action condition="command_is_true(echo \$vSettingsToggle)">show:vTab3</action>
                <action condition="command_is_true(echo \$vSettingsToggle)">echo 2 > ${LOCAL_DATA_DIR}/NOTEBOOK_PAGE</action>
                <action>refresh:vNotebook</action>
            </button>

            <button width-request="100"
              homogenous="true"
              image-position="0" x="valid 0-3 - left, right, above, below"
              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 bot be true for default to work"
              xxx-default="if entry has focus default setting for button does not work"
             >
                <label> _Quit </label>
                <input file icon="gtk-quit"></input>
                <width>16</width>
                <action function="exit">QUIT</action>
            </button>
            <text xpad="3">
                <label>""</label>
            </text>
            <entry visible="false">
                <default>false</default>
                <variable>vSettingsToggle</variable>
                <input>fnToggleSettingsTab</input>
            </entry>
        </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 ==========================================
export GTK2_RC_FILES=${LOCAL_DATA_DIR}/gtkrc
cat <<-'EOSTYLEDEF' > ${GTK2_RC_FILES}
style "tab3TextBoxes" {
    font_name="Sans Regular 10"
}
style "notebook" {
    GtkNotebook::tab-overlap = -2
    GtkNotebook::tab-curvature = 0
    xthickness = 1
    ythickness = 1
}
style "frameTitleStyle" {
    fg[NORMAL]      = "navy"
    font_name="CM Roman CE Regular 10"
}
style "entryStyle" {
    font_name="Monospace Regular 10"
    fg[NORMAL]="black"
}
style "headerStyle" = "frame" {
    fg[NORMAL]      = "navy"
    font_name="CM Roman CE Regular 12"
}

widget_class "*<GtkNotebook>" style "notebook"
widget_class "*.<GtkFrame>.<GtkLabel>" style "frameTitleStyle"
widget_class "*.<GtkEntry>*" style "entryStyle"
class "GtkEntry" style "entryStyle"

widget_class "<GtkWindow><GtkVBox><GtkNotebook><GtkVBox><GtkHBox><GtkEventBox>" style "tab3TextBoxes"
widget_class "<GtkWindow><GtkVBox><GtkNotebook><GtkVBox><GtkHBox><GtkEventBox><GtkLabel>" style "tab3TextBoxes"

widget_class "*<GtkNotebook>*<GtkEventBox>" style "tab3TextBoxes"
widget_class "*<GtkNotebook>*<GtkEventBox><GtkLabel>" style "tab3TextBoxes"

widget "win0.GtkVBox.GtkHBox.GtkVBox.GtkLabel" style "headerStyle"
EOSTYLEDEF

# -- Post UI Assembly Functions - used from UI after it is assembled ------------------------------------
#
# --------------------
# fnToggleSettingsTab
fnToggleSettingsTab() {
    [[ "${vSettingsToggle}" == "true" ]] && echo "false" || echo "true"
}
export -f fnToggleSettingsTab

# --------------------
# fnTestDBConnectivity "true/false" "\$vDBHost" "\$vDBPort" "\$vDBService" "\$vADTDBUsername" "\$vADTDBPassword"
fnTestDBConnectivity() {
    lvShowOKDialog="${1}"
    lvDBHost="${2}"
    lvDBPort="${3}"
    lvDBService="${4}"
    lvDBUsername="${5}"
    lvDBPassword="${6}"
    [[ "${lvShowOKDialog}" = "true" ]] && {
        yad \
            --center --width=400 --image="gtk-dialog-authentication" --window-icon="gtk-dialog-authentication" \
            --title="Failed connection" \
            --text="Failed to connect to database schema ${lvDBUsername}\nConnection: ${lvDBHost}:${lvDBPort}/${lvDBService}" \
            --button=" Dismiss!gtk-ok!Dismiss this dialogue:0"
    } || :
    return 0;
}
export -f fnTestDBConnectivity

# -----------------------------------------------
# fnTestURLConnectivity "true/false" "\$vPIXServiceURL"
fnTestURLConnectivity() {
    local lvShowOKDialog="${1}"
    local lvPIXServiceURL="${2}"
    fnUtlInvalidSomethingAlert "No Service" "PIX/PDQ Service is not available\nConnectivity test failed";
}
export -f fnTestURLConnectivity

# -----------------------------
# fnDoGetPatientIDs
fnDoGetPatientIDs() {
    [[ "$(fnUtlVarGet 'PATIENT_DB_QUERY_FIRST_TIME')" = "TRUE" ]] && {
        fnUtlVarSet "PATIENT_DB_QUERY_FIRST_TIME" "FALSE"
        return 0;
    } || :
    echo 'urn:uuid:7e762cc0-209b-406e-a752-5072a0ada6b5|0000000005^^^&1.2.3.4.5.6.7.8.9&ISO|05-MAY-17|A'
    echo 'urn:uuid:ab0c5276-8df6-487d-96a4-1a531afa28a4|0000000004^^^&1.2.3.4.5.6.7.8.9&ISO|05-MAY-17|A'
    echo 'urn:uuid:a2c7fe79-b830-40ff-8199-0db747873b31|0000000003^^^&1.2.3.4.5.6.7.8.9&ISO|05-MAY-17|A'
    echo 'urn:uuid:a61c5ae9-366e-4e1f-955d-ff05571a37ee|0000000001^^^&1.2.3.4.5.6.7.8.9&ISO|05-MAY-17|I'
    echo 'urn:uuid:ec3f4416-d3c8-4c48-90a1-068aaa06a6db|0000000002^^^&1.2.3.4.5.6.7.8.9&ISO|05-MAY-17|A'
    echo 'urn:uuid:ae82e878-e5f0-4c4e-950b-22576de0a002|0000000000^^^&1.2.3.4.5.6.7.8.9&ISO|05-MAY-17|A'
}
export -f fnDoGetPatientIDs

# ---------------------------------
# fnDoGetRequests
fnDoGetRequests() {
#dbg=

    [[ "$(fnUtlVarGet 'REQUEST_DB_QUERY_FIRST_TIME')" = "TRUE" ]] && {
        fnUtlVarSet "REQUEST_DB_QUERY_FIRST_TIME" "FALSE"
        return 0;
    } || :
    echo 'PIDFEED.Add|localhost|b2e6eabb-7b32-409e-b6b2-eebddfdb56b6|20170505104319294|T'
    echo 'PIDFEED.Merge|localhost|1bb4afcb-8934-447a-9bdb-6c9066add528|20170505104424232|T'
    echo 'PIDFEED.Add|localhost|15b837e2-4963-441a-94b9-d7dc5861961a|20170505105033304|T'
    echo 'SubmitObjectsRequest.b|localhost|3c1c0037-0e20-4dd4-8c1b-819c2f58c301|20170505105141384|T'
    echo 'SQ.b FindDocuments|localhost|87010111-7513-44cd-ae1e-55324d1f4d64|20170505105249668|T'
    echo 'SQ.b GetSubmissionSetAndContents|localhost|b8616a98-3ca6-4cfe-98f0-0c4cd2209237|20170505105346116|T'
    echo 'SQ.b GetDocuments|localhost|0666024e-a16e-4edc-9bf1-51a5320308f5|20170505105426666|T'
    echo 'SQ.b FindDocuments|localhost|b0e67663-2f10-457a-a69d-22a0fb83d88a|20170505105519734|T'
    echo 'PIDFEED.Add|localhost|5e199a85-a105-4d86-b513-64a41580fc02|20170505115447445|T'
    echo 'SubmitObjectsRequest.b|localhost|1774af74-5332-49ac-a4bc-4a490ba8e8d5|20170505115740370|T'
    echo 'SQ.b FindDocuments|localhost|2e22762c-aa82-41a5-82eb-7fbbcff532ee|20170505115933070|T'
    echo ""
#dbg=:
}
export -f fnDoGetRequests

# ---------------------------------
# fnShowRequestInfo \$treeResultsRequestsList
fnShowRequestInfo() {
    local lvMessageId="${1}"
    fnUtlInvalidSomethingAlert "No Service" "XDS.b Regsitry is not able to supply data\nCannot show details for message ${lvMessageId}";
}
export -f fnShowRequestInfo

# --------------------------------
# fnShowPatientInfo "\$treeResultsIDsList"
fnShowPatientInfo() {
    local lvPatientId="${1}"
#dbg=
    echo "Patient Identifier:|${lvPatientId}" > ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Status|active'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo '|'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Demographics:'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Title|'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Give Name|John'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Family Name|Doe'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Gender|M'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Date of Birth|10/11/1999'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Social Security Number|200809101'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo '|'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Address:'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Street Address|33 Berry Street'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'City/Town|North Sydney'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'State|NSW'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo 'Postal Code|2060'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
    echo '|'  >> ${LOCAL_DATA_DIR}/PATIENT_INFO
#dbg=:
}
export -f fnShowPatientInfo

## --------------------------------------
function fnUtlInvalidSomethingAlert() {
invTitle=${1:-}
invMessage=${2:-}
    yad \
        --center --width=400 --image="gtk-dialog-error" --window-icon="gtk-dialog-error" --title="${invTitle}" --text="${invMessage}" \
        --button=" Dismiss!gtk-ok!Dismiss this dialogue:0"
}
export -f fnUtlInvalidSomethingAlert

# -- 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/notebook.sh

I hope that this article helps you work with the gtkdialog notebook object.

preload preload preload