Thursday, December 18, 2014

Broken gnocl::splashScreen now fixed

Whilst a splash screen widget has been in the core for some time now, the code has became unreliable and so some fixing was in order. The revised module, rather than using cairo to draw screen elements, uses items placed directly in GtkFixed container to create a gnocl megawidget. This has not only proved more easy to code, but also operates more smoothly. Updates available in the nightly build.

Thursday, November 20, 2014

Paned motion events now handled

I'm not quite certain why, but the GtkPaned API has no direct support for tracking the events associated with the mouse pointer. Signals are implemented, but only for keyboard operations. The docs give useful function to isolate the handle gdkwindow but why didn't they go the whole hog and implement a proper binding? Well we have it now in Gnocl!  

To test this feature get the latest nighly build.

set p [gnocl::paned \
-children [list $b1 $ebox] \
-resize 1 -shrink 1 \
-onHandleMotion {
## puts "MOVING w= %w x= %x X= %X p= %p s=%s"
if {"%s"== "256" } {
foreach {x y w h} [gnocl::winfo geometry %w] {}
_set_width $paned [expr $w - "%p"]
gnocl::update
}
}]

Sunday, November 16, 2014

Multipaned container implemented in Tcl/Gnocl

The Gtk+ widget set contains two widgets, GtkHPaned and GtkVPaned which only allow the insertion of two child widgets. One might suppose that if more items are needed then folding boxed in a scrolling window would be adequate. One alternative, as I have used up to date, is the use of nested double panes.

Implementing a new container widget in C using the Gtk+ libraries is possible, but implementing such an beast in Tcl/Gnocl is somewhat less complex.

Over the past couple of evenings I've put together a proof of concept script to get the bare bones of this this megawidget in place. All seems well, although a little bit of easing on the gnocl source code side of things has helped. To this end I've added the geometry subcommand to the gnocl::fixed widget. The effect of this to allowing a single command to return the position and size of child objects placed in a gnocl:fixed container rather than making two separate calls in the script, one for the (x y) position and the other for (width height) size of the child.

As can be seen from this screenshot my test script my paned is somewhat overpopulated.

To test this out locally, download and install the lastet nightly build from SourceForge.

A click and drag of the mouse pointer on the separator bars sees the height of all of the panes resize correctly in real-time.

The next step is to introduce some minium settings, overlap prevention and collapse-expand double click behavious on the separator.


# mypaned-2.tcl
#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl

set bar 15
set height 50
set width 200
set i 1
set handle_height 5


#---------------
#
#---------------
#
proc my_paned {} {

    set ::container [gnocl::fixed -onAdd {puts HI}]

    set wid [gnocl::eventBox]  ;#-background red]

    # add the first container
    $::container add $wid -x 0 -y 0 -width $::width -height $::height

    lappend ::panes $wid

    return $::container

}

#---------------
#
#---------------
#
proc _resize_above { handle } {

    set box [gnocl::winfo parent $handle]
    set i [lsearch $::handles $handle]
    set above [lindex $::panes $i]
   
    set w [lindex [gnocl::winfo geometry $box] 2]

    # get and process geometry
    foreach {ax ay aw ah} [$box geometry $above] {}
    foreach {hx hy} [$box position $handle] {}

if 0 {
        +---------------+ ay
        |               |
        |     ABOVE     | new height = hy - ay
        |               |
        +---------------+ hy
        |     HANDLE    |
        +---------------+  
}

    $box move $above -x $ax -y $ay -width $aw -height [expr $hy -$ay]
       
}

#---------------
#
#---------------
#
proc _resize_below { handle } {

    set box [gnocl::winfo parent $handle]
    set i [lsearch $::handles $handle]
    set below [lindex $::panes [incr i] ]

    # get and process geometry
    foreach {bx by bw bh} [$box geometry $below] {}
    foreach {hx hy hw hh} [$box geometry $handle] {}

if 0 {
        +---------------+ hy
        |     HANDLE    |
        +---------------+ hy+hh = by
        |               |
        |     BELOW     | new height = (by+bh) - (hy+hh)
        |               |
        +---------------+ by+bh 

}
    $box move $below -x $bx -y [expr $hy + $hh] -width $hw -height [expr ($by+$bh) - ($hy+$hh)]
       
}

#---------------
#
#---------------
#
proc move_handle { handle } {

    # get container
    set box [gnocl::winfo parent $handle]

    # move handle
    set hy1 [_pointer_ypos $handle]
    foreach {hw hh} [$box size $handle] {}
    $box move $handle -x 0 -y [_pointer_ypos $handle] -width $hw -height $hh

    _resize_above $handle
    _resize_below $handle   
       
}

#---------------
#
#---------------
#
proc _pointer_ypos { w } {
    return [expr [lindex [gnocl::winfo pointer [gnocl::winfo toplevel "$w"]] 1] -5]
}


#---------------
#
#---------------
#
proc adjust {w} {
    set i [lsearch $::handles $w]
    set l [expr [llength $::handles] -1]


    set ret "[expr $i] [expr $i+1]" 
       
    return $ret
}

#---------------
#
#---------------
#
proc add_handle { y } {
   
    set wid [ gnocl::eventBox ]
    $wid configure -child [ gnocl::separator ]
   
    set parent [gnocl::winfo parent $::container]
    foreach {a b width c} [gnocl::winfo geometry $parent] {}
   
    set y [expr $y -$::handle_height]
   
    $::container add $wid -x 0 -y $y -width $width -height $::handle_height   

    lappend ::handles $wid
   
    $wid configure -onMotion { move_handle %w }
       
}

#---------------
#
#---------------
#
proc add_pane { } {
   
    set lpane [lindex $::panes end]
   
    foreach {a y w h} [$::container geometry $lpane] {}
   
    set y [expr $y + $h +$::handle_height]
    set wid [gnocl::eventBox ]
   
    $::container add $wid -x 0 -y $y -width $w -height $h

    add_handle $y
   
    incr ::i
   
    lappend ::panes $wid
   
}

#---------------
# test-block
#---------------
#

set paned [my_paned]

gnocl::window -child $paned -height 600 -width 300

add_pane
add_pane
add_pane
add_pane
add_pane
add_pane
add_pane
add_pane

set txt(1) [gnocl::text -wrapMode word] ; $txt(1) lorem
set txt(2) [gnocl::text -wrapMode word] ; $txt(2) lorem
set txt(3) [gnocl::text -wrapMode word] ; $txt(3) lorem
set txt(4) [gnocl::text -wrapMode word] ; $txt(4) lorem

[lindex $panes 0] configure -child $txt(1)
[lindex $panes 1] configure -child [gnocl::label -text ONE]
[lindex $panes 2] configure -child [gnocl::label -text TWO]
[lindex $panes 3] configure -child $txt(2)
[lindex $panes 4] configure -child [gnocl::label -text FOUR]
[lindex $panes 5] configure -child [gnocl::label -text FIVE]
[lindex $panes 6] configure -child $txt(3)
[lindex $panes 7] configure -child [gnocl::label -text SIX]
[lindex $panes 8] configure -child $txt(4)

Thursday, November 06, 2014

gnocl::entry validation

Making headway now in implementing entry validation. As is always the case, the challenges don't arise from getting the code to work, but overcoming those instances when, theoretically, a block of code should work but doesn't. The most annoying aspect of all this is that such hindrances tyically occur in the simplest, most straight forward of tasks and those features which at first glance look daunting are implemented in a blink! Think I'm having a deja vu moment here.

Ah, such is life. Zen and the the art of C programming -- frustration arises when one's vision of an ideal and perfect world are shot to pieces by an abrupt clash with the imperfect reality of things.

The entry and parseoptions C modules now have all the necessary functionality coded into it, what now remains is to tidy it up, test and document it.

Sunday, November 02, 2014

Recent Changes and Enchancements

Admittedly, over the past few months things have been a little quiet on Gnocl development front. The summer has been very busy for me with most of my free time taken up with the preparation of some translation work for a forthcoming textbook on the history of east asian medicine. Needless to say, much of this has been produced using Tcl/Gnocl derived tools.

The past couple of weekends have seen me address a number of problems and limitations that have arisen. Firstly the Gnocl website failed to load -- this has now been fixed and all runs normally. Next, I've worked on a couple of issues around the gnocl::eventBox and gnocl::entry widgets. With the enhancements to the gnocl::entryBox it is now possible to remove child widgets or allow their replacement during runtime. The gnocl::entry has seen the addition of the -innerBorder option, which controls the packing between the text and the entry containing frame.

Some changes have taken place beneath the surface too. Previously it was not possible to access the parameters structures for widgets in event callback handlers as these simple return the calling GtkWidget pointers, this has been overcome through the introduction of a parameter list hash table.

Other modifications in progress include the introduction of entry validation for the gnocl::entry. Set using the new -validate option the following choices will be available: unicode (default), alnum, alpha, integer and float.

The lastest build is now available from Sourceforge.




Saturday, May 31, 2014

Dialog bindings completely re-written.

The legacy code to create a gnocl::dialog has always been problematic. The way the code was originally devised was to build up a set of control buttons using what Gtk libs describe as "activation widgets", that is: custom made widgets added to the bottom row of buttons. Apart from this approach going against the Gnome user interface guidelines, it always results in an application crash. Looking at other dialogs in the library indicates why. The idea is that the dialog 'runs' within its own loop which is only exited when a response button is activated. The dialog either gathers or gives information and then returns to the calling script a response decision obtained the user. Using activation widgets that call the -onResponse signal break this process. If programmers have used the activation widgets for something more complex then those widgets ought to be packed as children into the main body of the dialog.
I've now resolved this problem by completely re-writing the gnocl dialog bindings with the planned redundancy of the existing dialog code on the next incremental release.

The much simplified code has the following options:

-backgroundColor,
-backgroundImage
-buttons,
-child,
-extra,
-image,
-modal,
-parent,
-primary,
-resizeable,
-resizeGrip,
-secondary,
-setSize,
-title,
-tooltip,
-type,

Download and compile today's gnocl nightly build to test for yourselves. All comments and feedback most welcome especially regarding applications using glade or builder files.

The following test script shows how complex dialogs and alternative message dialogs can be made.

(In the development release the command gnocl::messageDialog is currently used to access this new  code, this will be changed to gnocl::dialog when the old code is retired.)

# test-dialog.tcl
#
#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl

set vbox [gnocl::vBox]
set but1 [gnocl::button -text Dialog1-Chooser -onClicked { dialog1 $win} ]
set but2 [gnocl::button -text Dialog2-Message -onClicked { dialog2 $win} ]

set txt [gnocl::text]

$vbox add $but1
$vbox add $but2
$vbox add $txt -fill {1 1} -expand 1

set win [gnocl::window -child $vbox -setSize 0.25 -onDelete {exit}]

#---------------
# Test Form-Chooser Dialog.
#---------------
#
proc dialog1 {win} {
   
    set table [gnocl::table -homogeneous 1]

    $table addRow [list [gnocl::button -text "1"] "" [gnocl::button -text "2"]]
    $table addColumn [list [gnocl::button -text "3"] "" [gnocl::button -text "4"]]
    $table add [gnocl::button -text "5"] 1 0 -rowSpan 3
    $table add [gnocl::button -text "6"] 2 1 -columnSpan 2
   
    set response [gnocl::messageDialog \
        -parent $win \
        -primary HELLO \
        -modal 1 \
        -setSize 0.4 \
        -child $table \
        -title "HOW NOW BROWN COW." ]

    switch $response {
        "#OK" {
            puts "${::apple}. Well, that's alright then!"
            }
        "#CANCEL" {
            puts "Oh, maybe next time then!"
        }
        default {
            puts $response
        }
    }

}

#---------------
# Test Message Dialog, with widget and optional graphic.
#---------------
#

set apple "Granny Smith"

proc dialog2 {win} {
   
    # NOTE: variables created/access in global namespace

    set tmp $::apple

    set ent [gnocl::entry -variable tmp -tooltip "Change your mind here."]
    set txt [gnocl::text -wrapMode word]
    $txt lorem

    set im [gnocl::image -image %#Index -stockSize dialog -tooltip "Pointy Finger."]
   
    # the "-type question" setting will be overidden by "image $im"
   
    set response [gnocl::messageDialog \
        -resizable 1 \
        -title "CHOOSE!" \
        -buttons ok-cancel \
        -child $txt \
        -type  other \
        -image $im \
        -parent $win \
        -primary "WE RECOMMEND:\n$::apple" \
        -secondary "What do you like?" \
        -backgroundColor #F2F4C0 \
        -tooltip "It's time to decide."]
   
    switch $response {
        "#OK" {
            set ::apple $::tmp
            puts "${::apple}. Well, that's alright then!"
            }
        "#CANCEL" {
            puts "Oh, maybe next time then!"
        }
        "#DELETE" {
            puts "Dialog delete! How rude!"
        }
    }
    unset ::tmp
}

Friday, May 09, 2014

Difficulties Over Setting Rollover Tag Attributes. -SOLVED

Setting individual rollover colours for tags seems a little more trouble some than I first imagined. After adding a whole pile of code that wasn't going anywhere it was time to rethink!

Occam's Razor time.

Whenever a solution is becoming overly complex to the point that it begins to fail, then the solution is wrong. After a while I thought that if rollovers were implemented in Gtk2+ how would it be listed in the docs? Ah! Styles -- a little voice said behind me. But, as no such style exists (if it does let me know), it seems right to add two pseudo styles: "rollover-fg" and "rollover-bg". These can be set using the familiar:

gnocl::setStyles GtkTextView "rollover-bg" blue

What is needed now is to remove the earlier tag options to set these colours.

Rollover effects

The text rollover effects are well implemented now in the core C code. At present changing the colours for a rollover event in any single text widget will affect all text widgets. So, the next step is to enable unique fg/bg colour combinations.

When using the rollover -tag setting some caution must be used as if any single rollover tag is used repeatedly in a text, then all those separated blocks of text marked with the tag will be highlighted.

Friday, March 28, 2014

New Megawidget Boilerplate

Haven't posted much to the blog recently. This isn't because things are quiet on the Tcl/Gnocl side, I've just been doing more applications coding. Today's posting is a result of this. I already use a bunch of gnocl megawidgets but there always seems to be slight differences between each module. I thought that I'd settle this once and for all by creatings a boiler plate module which can then be adapted to suit. Because I shift between the gnocl C sources and Tcl, I thought it wise to follow a similar design approach in both.

Individual procedures handle the construction, configuration and command handling for each widget instance and error checking is implemented too.

#---------------
# widget.tcl
#---------------
# Boilerplate object builder package for gnocl derive megawidgets.
# Based upon approach used in Gnocl source code.
# William J Giddings, 28-Mar-14.
#---------------
# Substitute keyword _widget_ for unique object type identifier.
#---------------
# !/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

package require Gnocl
package provide widget

namespace eval _widget_ {}

#---------------
# lists of valid widget options, commands and components
#---------------
#
set _widget_::opts { -text -onClicked -data}
set _widget_::cmds { configure cget type opts cmds}
set _widget_::components {}

#---------------
# implement widget commands
#---------------
#
proc _widget_::cmd {wid cmd args} {

    _widget_::check $cmd

    # apply the commands
    switch -- $cmd {
        opts -
        cmds {return [lsort [set _widget_::$cmd]] }
        type { return "_widget_"}
        configure -
        cget {
            eval "_widget_::$cmd $wid $args"
            }
        default { # shouldn't need to get here, but... }
    }

}

#---------------
# retrieve current component values
#---------------
#
proc _widget_::cget {wid args} {

    # get list of members
    foreach {w id} $_widget_::components {set $w $id}
   
    # obtain current settings
    foreach {a b} $args {
        # apply according to each component
        switch -- $a {
            -onClicked -
            -text { return [$but_1 cget $a] }
            -data { return [$wid cget $a] }
            default { # shouldn't need to get here, but... }
        }
    }
   
}

#---------------
# check options and commands for valid values
#---------------
#
proc _widget_::check { opts } {

    # test for a valid options
    if { [string first - $opts ] >= 0 } {
        foreach {opt val} $opts {
            if { [string first $opt $_widget_::opts] == -1 } {
                set errmsg "[string repeat - 17]\nERROR! Invalid option \"$opt\".\nShould be one of: [lsort $_widget_::opts]\n[string repeat - 17]"
                error $errmsg
            }
        }   
        return
    }
   
    # test for valid command
    foreach {opt } $opts {
    if { [string first $opt $_widget_::cmds] == -1 } {
        set errmsg "[string repeat - 17]\nERROR! Invalid command \"$opt\".\nShould be one of: [lsort $_widget_::cmds]\n[string repeat - 17]"
        error $errmsg
        }
    }

}

#---------------
# configure widget components
#---------------
#
proc _widget_::configure {wid args} {
   
    _widget_::check $args
   
    # recover list of widget components
    foreach {w id} $::_widget_::components {set $w $id}

    # apply new options
    foreach {a b} $args {
        # apply according to each component
        switch -- $a {
            -text -
            -onClicked { $but_1 configure $a $b }
            -data { $wid configure $a $b }
            default { # shouldn't need to get here, but... }
        }
    }
   
}

#---------------
# create and assemble widget components
#---------------
#
proc _widget_::construct {} {
   
    # create object container
    set vbox [gnocl::vBox]

    # create components
    set but_1 [gnocl::button -text BUTTON]

    # assemble components
    $vbox add $but_1

    # add to listing
    set ::_widget_::components [list but_1 $but_1]

    return $vbox
}

#---------------
# the widget command itself
#---------------
#
proc widget {args} {
    set wid [_widget_::construct]

    # configure
    eval "_widget_::configure $wid $args"
   
    # overload the box to add commands
    rename $wid _$wid

    # widget command
    proc $wid {cmd args} {
        set wid [lindex [::info level 0] 0]   
        eval "_widget_::cmd _$wid $cmd $args"          
    }

    return $wid
   
}


proc demo {} {
    set wid(1) [widget -text "HELLO CAMPERS!" -onClicked {puts "HO HI HO!"}]
    gnocl::window -child $wid(1)

    $wid(1) configure -text "HI DI HI" -data "leave my data alone!"
    puts [$wid(1) type]

    catch { $wid(1) configure -a AAA } {}
    catch { $wid(1) nonsuch } {}

    puts [$wid(1) cget -data]

    set wid(2) [widget -text "GOOD MORNING!" -onClicked {puts "GOOD AFTERNOON"} -data BYE-BYE]

    gnocl::window -child $wid(2)
    puts [$wid(2) cget -data]
    puts [$wid(1) opts]
    puts [$wid(2) cmds]

}

demo

Thursday, January 16, 2014

gnocl::richTextToolBar -working version ready...

Not working on any academic research over the past few days has given me time to focus on completing the implementation of a simple pango/html rich text edit widget. GtkHtml was one route to take but this is being phased out in favour of WebkitGtk (besides, GtkHtml documentation of any work simply doesn't exist). After hacking together an editor using the webKitGtk, which itself is something of a work around, I modified the existing gnocl webkit package to offer all the basic functionality of a simple html doc editor. But, this required implementing Javascript calls in C procs which were then called through a Tcl front end! Apart from the package taking a second or two to load, I found that webkitGtk will tend to crash after a while. Not much use to me! My aim is to create markup strings which can also be used for display within Gtk+ widgets during run-time, i.e. marked up labels or list entries. As a glance through previous blog postings will show, I had already put together some basics, exploring the task from both the Tcl and C side of things. At the end of the day, I coded the whole lot in C and it now works fine although some care needs to be taken when applying background colours. Needless to say, if there is an error in the pango markup then this won't cause a crash as the returned text will be stripped of any markup. Along with the editor, there's also a html-source viewer.

As the widget item name suggests, this is basically a toolbar, if the -text option is set, then any pre-existing text widget can be used.

Here's a snapshot and the script that created it.



#--------------------------
# test_gtk_richTextToolBar.tcl
#--------------------------
#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl

set txt [gnocl::text -wrapMode word -markupTags 1 -baseFont {Sans 14}]
set app(rtbar) [gnocl::richTextToolBar -text $txt -textAccelerators 1 ]

$txt configure -onChanged {
           $app(rtf,lab1) configure -text [$txt getMarkup start end]
           }
   
set vbox [gnocl::vBox]

set app(rtf,lab1) [gnocl::label -useMarkup 1 -justify left -align topLeft  -wrap 1 ]
set eb1 [gnocl::eventBox -child $app(rtf,lab1) -background white ]

$vbox add $eb1 -fill {1 1} -expand 1
$vbox add $app(rtbar)
$vbox add $txt -fill {1 1} -expand 1

gnocl::window -child $vbox -setSize 0.4


Sunday, January 05, 2014

New Year's Enhancements

Given some extra time to the implementation of the gnocl::toolBar items. Whilst is is possible to pack buttons, toggles and radio buttons into the toolbar, the custom items offer better formatting and are stylistically 'correct'. As the resulting module source code was stretching into thousands of lines, the toolbar.c module was split and the code for creating the 'items' shifted into separate files. Here's summary of the recent changes:

2014-01:
    gnocl::toolbar
        o separated source code for the creation of toolbar items into
            individual modules.
        o -onToggled functionality now working properly for relevant
            items.
    gnocl::radioButton
        o added %p substitution strings
    gnocl::toggleButton
        o added %p %d subtitution strings
        o new option -data

Updated code available from sourceforge.