MapTools.org

[Chameleon-dev] Major CVS commit

Paul Spencer spencer@dmsolutions.ca
Thu, 22 Jan 2004 13:47:31 -0500
This is a multi-part message in MIME format.
--------------040807010900090106090904
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

All,

I have finally committed all my changes related to handling of buttons 
in Chameleon.  It is a very extensive change and has affected basically 
every widget and popup dialog in Chameleon.

Attached is a document that attempts to describe the changes that I've 
made and the changes that you will have to make to your application 
templates to make it compatible with the new buttons.

There are bound to be some problems as a result of this.  I have done 
quite a bit of testing, and I do know of some problems:

* Pan widget - if you select the pan widget button in the user 
interface, it is difficult or impossible to select another tool.  Try 
the zoom in tool, that one seems to work for some reason.  This will be 
fixed by mid-February

* Ruler widget - same problem as Pan widget (the problem is they 
interfer with the new button javascript architecture - but they are 
going to be rewritten soon so I didn't bother to work around it)

* CWCJSAPI - not tested at all, I would expect that it would more or 
less work as is, but I am not yet ready to test it.

Please send reports of any other problems to the mailing list or create 
bugs in bugzilla.  And ask if anything in the attached doc is unclear or 
doesn't work the way I say it should.

Cheers,

Paul
-- 
  -----------------------------------------------------------------
|Paul Spencer                           spencer@dmsolutions.ca    |
|-----------------------------------------------------------------|
|Applications & Software Development                              |
|DM Solutions Group Inc                 http://www.dmsolutions.ca/|
  -----------------------------------------------------------------

--------------040807010900090106090904
Content-Type: text/plain;
 name="button_changes.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="button_changes.txt"

Chameleon Button Architecture

The architecture of Chameleon buttons has undergone a significant overhaul to unify and standardize how buttons are created and represented in applications.

Prior to this update, there were two separate types of buttons.  Each type had advantages and disadvantages.  The purpose of this update is to provide a consolidated architecture that has the advantages of both and the disadvantages of neither.

Background

The two existing button types were TextButtons and NavButtons.

TextButton

TextButtons are generated on the fly from configuration information provided in the application template and have significant flexibility.  They were designed to provide a convenient mechanism for creating customizable and multilingual interfaces without requiring a graphic artist to constantly be regenerating buttons for the application.  TextButtons use a common utility called the buttonizer.  The buttonizer generates a single image from an array of input parameters.  A buttonizer button consists of a border, a background color or image, an image and a label.  Most of the parameters are optional, and many are very flexible.  For a full description of the capabilities of the buttonizer, see buttonizer.php in common/buttonizer folder.

TextButtons use the services of buttonizer to pre-generate a button image from settings in an application template, and these images are cached in a web-accessible location for performance.  Once a TextButton with specific characteristics is generated, it will normally not be regenerated unless caching is specifically disabled or the cache directory is emptied.

The original implementation of TextButtons were used to represent pretty much every widget that used a button in the application, except Navigational buttons (discussed below).  TextButtons ended up as a single clickable image with no fancy javascript image swapping effects, which is a limitation compared to the NavButtons, but also an advantage when designing javascript-minimized applications.  The same TextButtons are used in all popup dialog boxes via the services of the CWC2ButtonCache.php file.  

The original implementation of the caching mechanism for TextButtons was relatively poor and did not take advantage of session management to maximize performance.

NavButton

NavButtons use Button.php to manage multi-state 'clickable' buttons from a set of pre-generated images.  This provides a more user-friendly interface, and can look very nice.  However, this approach suffered from a number of drawbacks.  The javascript for the buttons was embedded in the application page rather than being loaded via a (browser cachable) .js file.  The button images had to be pre-rendered by a graphic artist, so even small changes to the interface required generating several images for each navigation tool.  Finally, there was no way to not use javascript with the buttons.

The NavButton did have one additional useful trait, it was possible to group NavButtons using a toolset attribute to make them work as 'radio' buttons.

Button Requirements

>From the existing capabilities of TextButton and NavButton, the following requirements were derived:

* All buttons need to use the buttonizer
* All buttons need to be multi-state capable
* All buttons need to be able to minimize javascript
* Multi-state code needs to be moved into a separate js file
* Buttons need to work in popup dialogs
* Buttons in popup dialogs need to be able to be styled from the application template.
* Buttons need to support multiple types of buttons, including radio (single button selected in a group), toggle (the button is on or off and remembers it's state), and click (a single use button).

Button Implementation Impact on Application Developers

The implementation of the new Buttons necessitated changes to pretty much every widget in Chameleon (there were a few widgets that didn't change because they have no user interface).  The biggest change from the previous version are primarily in the TextButton SharedResource and changes to the widget attributes relating to Buttons.

Buttons now understand the notion of 'button state', and every button can have either a single state (the default state, called 'normal') or multiple states.  The multiple states that are allowed are:

* normal - the style of the button when it is not activated and the user is not hovering over the button
* hover - the style of the button when the user moves the mouse over the button
* selected - the style of the button when it is activated (clicked in)
* disabled - the style of the button when it cannot be used.

Buttons can be of several types, including:

* radio - the button is part of a toolset.  All widgets supporting buttons now get a 'toolset' attribute and if it is set, then the button is automatically turned into a radio button regardless of the widget's built in type.  A radio button is a sticky button that stays selected until a different button in the same group is activated.  A radio button that is activate cannot be re-activated until it has been first deactivated by clicking another button.  If there is only a single button in a toolset, this may cause problems because the tool will only be selectable once.  A future enhancement may address this issue.
* toggle - a toggle button is a sticky button that remembers its state when clicked.  Clicking a selected toggle button 'turns it off' by returning the button to its normal state.
* normal

TextButton SharedResource

The TextButton SharedResource has changed significantly.  First of all, it is no longer necessary to name the TextButton SharedResource "TextButton".  Every widget now refers to a SharedResource by name, so different buttons can easily use different TextButton SharedResources.  The structure of the SharedResource has also changed significantly.  The existing structure will still work without modification, but in order to support multi-state buttons, a new sub-eleemnt called 'state' has been added.  The new structure of a TextButton SharedResource looks like:

<cwc2 type="SharedResource" name="TextButton">  <!-- note this doesn't have to be called TextButton -->
    <textbuttonbackgroundimage value=""/> <!-- this section is the same -->
    <imagewidth value="100"/>
    <imageheight value="24"/>
    <textbuttonpadding value="2"/>
    <textbuttonnudge value="0"/>
    <labelcolor value="111111"/>
    <labelfont value="../etc/fritqat.ttf"/>
    <labelalign value="left"/>
    <labelfontsize value="8"/>
    <labelantialias value="true"/>
    <usetextbuttoncache value="true"/>
    <state value="normal"> <!-- this is new -->
        <textbuttoncolor value="CCCCCC"/>
        <textbuttonborder_topleft_image value="images/tl_1.png"/>
        <textbuttonborder_top_image value="images/t_1.png"/>
        <textbuttonborder_topright_image value="images/tr_1.png"/>
        <textbuttonborder_right_image value="images/r_1.png"/>
        <textbuttonborder_bottomright_image value="images/br_1.png"/>
        <textbuttonborder_bottom_image value="images/b_1.png"/>
        <textbuttonborder_bottomleft_image value="images/bl_1.png"/>
        <textbuttonborder_left_image value="images/l_1.png"/>
    </state>
    <state value="hover">
        <textbuttoncolor value="EEEEEE"/>
        <textbuttonborder_topleft_image value="images/tl_2.png"/>
        <textbuttonborder_top_image value="images/t_2.png"/>
        <textbuttonborder_topright_image value="images/tr_2.png"/>
        <textbuttonborder_right_image value="images/r_2.png"/>
        <textbuttonborder_bottomright_image value="images/br_2.png"/>
        <textbuttonborder_bottom_image value="images/b_2.png"/>
        <textbuttonborder_bottomleft_image value="images/bl_2.png"/>
        <textbuttonborder_left_image value="images/l_2.png"/>
    </state>
    <state value="selected">
        <textbuttoncolor value="AAAAAA"/>
        <textbuttonborder_topleft_image value="images/tl_3.png"/>
        <textbuttonborder_top_image value="images/t_3.png"/>
        <textbuttonborder_topright_image value="images/tr_3.png"/>
        <textbuttonborder_right_image value="images/r_3.png"/>
        <textbuttonborder_bottomright_image value="images/br_3.png"/>
        <textbuttonborder_bottom_image value="images/b_3.png"/>
        <textbuttonborder_bottomleft_image value="images/bl_3.png"/>
        <textbuttonborder_left_image value="images/l_3.png"/>
    </state>
    <state value="disabled">
        <textbuttoncolor value="CCCCCC"/>
        <textbuttonborder_topleft_image value="images/tl_1.png"/>
        <textbuttonborder_top_image value="images/t_1.png"/>
        <textbuttonborder_topright_image value="images/tr_1.png"/>
        <textbuttonborder_right_image value="images/r_1.png"/>
        <textbuttonborder_bottomright_image value="images/br_1.png"/>
        <textbuttonborder_bottom_image value="images/b_1.png"/>
        <textbuttonborder_bottomleft_image value="images/bl_1.png"/>
        <textbuttonborder_left_image value="images/l_1.png"/>
    </state>
</cwc2>

The values from the main block of the SharedResource form the default values for all states.  If no <state> tags are included then a default "normal" state is created from the default values.  Otherwise, states are created from each <state> tag and initialized using the defaults then overloaded using the state-specific settings.  Any tag can go in either.

Widget Attributes

The attributes for widgets have changed.  All widget attributes relating to button resources are provided by Button.php (with one exception, for popups, described below).  The new attributes are:

* styleresource - the name of a SharedResource to use for styling TextButtons.  If not set or empty or invalid, then the Button will not be generated using the buttonizer.  This means that any 'image' attribute for the widget will be used as is.

* image - the name of an image to use for the widget.  If 'styleresource' is valid, then the image will be placed in the generated button, otherwise it will be used directly.  *NOTE* that for buttonizer, all images MUST be PNGs.  GIF images are not supported.  If the 'styleresource' is not valid or empty or missing, then the image will be output as is, so any browser-supported format would be okay.

* imagewidth - the width of the final image.  If the styleresource is valid, then this is the width of the buttonized image.  If it is not valid, then it is the width of the IMAGE attribute.  In either case, it will be output in the WIDTH attribute of an IMG tag.

* imageheight - the height of the final image.  If the styleresource is valid, then this is the height of the buttonized image.  If it is not valid, then it is the height of the IMAGE attribute.  In either case, it will be output in the HEIGHT attribute of an IMG tag.

* imagetip - the 'tooltip' text to be displayed when the mouse is over the image, using the ALT and TITLE attributes of the IMG tag.

* labelalign - the alignment of the label, overrides the styleresource setting for labelalign.

* toolset - the name of a group of radio buttons.  If not set, or set to an empty string, then the button acts as specified by the widget designer, otherwise it becomes a radio button within this group.

* default - for radio buttons and toggle buttons, this determines if a button will start selected or not.  Only one widget can be considered the default for a group.  Setting to false or empty or omitting entirely means the button will not be selected.  If more than one button in the same toolset has the default attribute set to true, one of them will end up being selected but it is not generally possible to determine which one because it depends on widget priorities and order in the template.

* onclick - the javascript function to call when the button is clicked.  This must be a standalone javascript function.  Methods on objects will not work (i.e. window.close) will not work.  The javascript function does not include the parentheses () or any parameters

There is one additional attribute that applies to widgets with Popup windows.  This is:

* popupstyleresource - the name of a SharedResource to use for styling Buttons in Popup dialogs.  If not set, it will use the styleresource.  If this isn't set, then the buttons will appear as plain text, pretty ugly !!!

Button Implementation Impact on Widget Developers

This section is for people who are building or maintaining widgets and want to know how the button implementation changes affect widget internal code.  There are four sections:

* Includes - what you need to include() in your widget code

* Initialization Phase - what needs to be done to create and initialize a button for the widget.

* Processing Phase - what needs to be done in the ParseURL function

* Rendering Phase - what needs to be done for widgets in the various Get*** functions and DrawPublish()

Includes

All widgets can now access the new style of buttons (in fact, must access the new style of buttons) by includeing Button.php.  This provides the class definition of CWCButton.

include( "Button.php" );

Initialization Phase

In order to represent a widget using a button, the widget must create an instance of the CWCButton class as a member variable of the widget and properly initialize the button instance.

The widget should (this is not mandatory, but is part of the coding style for Chameleon) declare a member variable in the widget class definition as follows:

class MyWidget
{
    var $moButton;
    
    ...

In the widget constructor, a new button instance can be created using:

    function MyWidget()
    {
        $this->moButton = new CWCButton($this);
    }

You can optionally set the button type using $this->moButton->mnType.  mnType can be one of 

* CWCBUTTONTYPE_CLICK -  a normal clickable button
* CWCBUTTONTYPE_TOGGLE - a button that remembers its state (in or out) and is sticky
* CWCBUTTONTYPE_RADIO - a button that is part of a group of buttons.  Normally, it is not necessary to set this as a button that becomes part of a group of buttons is changed to a radio button automatically.

In the widget's InitDefaults() method, the button must be given a chance to initialize itself.  This involves calling the InitDefaults method of the button object and (normally) setting the onclick event of the button.  The SetOnClick function must be called either during Initialization or during Processing.  Previously, TextButtons could set the onclick event just before the DrawPublish method was called.  This no longer works!!!

The onclick function is called with a javascript object representing the button as its first parameter.  There is currently no way to pass parameters to javascript functions by widget attributes, but if this is implemented, they will be passed as the second (and subsequent) parameters.

    function InitDefaults()
    {
        $this->moButton->InitDefaults();
        $this->moButton->SetOnClick( 'myJSFunction', 'myParam' );
    }

Processing Phase

The processing phase of widgets is all done in the ParseURL function.  If a button is either a toggle button or part of a toolset, this is actually managed via HTML form variables and the button code needs access to the URL in order to determine which buttons should be pre-selected when the page is loaded.  Because any button can be part of a toolset (whether it makes sense or not), every widget that has a button object should add the following to its ParseURL function:

    function ParseURL()
    {
        $this->moButton->ParseURL();
    }

Rendering Phase

During the rendering phase, the Chameleon core calls several functions for every widget to get HTML variables, javascript code, and the final HTML representation of the widget.  Widgets that include buttons must pass some of these calls on to the button instance to allow it to properly represent itself in the page. In particular, the widget must implement 


    function GetJavascriptInitFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptInitFunctions();
        
        // add other init functions here if necessary
        
        return $aReturn;
    }

    function GetJavascriptVariables()
    {
        $aReturn = $this->moButton->GetJavascriptVariables();
        
        //add other variables here if necessary
        
        return $aReturn;
    }

    function GetJavascriptOnLoadFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptOnLoadFunctions();
        
        //add other OnLoad functions here if necessary
        
        return $aReturn;
    }

    function GetJavascriptIncludeFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptIncludeFunctions();
        
        //add other Include functions here if necessary
        
        return $aReturn;
    }

    function GetHTMLHiddenVariables()
    {
        $aReturn = $this->moButton->GetHTMLHiddenVariables();
        
        //add other HTML hidden variables here if necessary
        
        return $aReturn;
    }
    
    function GetJavascriptFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptFunctions();
        
        //add other Javascript functions here
        
        return $aReturn;
    }

    function DrawPublish()
    {
        $szResult = $this->moButton->DrawPublish();

        //add other rendering code as required

        return $szResult;
    }
}

Complete Example

The following is a complete example of a widget that does nothing but is properly configured to represent itself as a button:

include( "Widget.php" );
include( "Button.php" );

class MyWidget extends CWCWidget
{
    var $moButton;
    
    function MyWidget()
    {
        $this->moButton = new CWCButton($this);
    }

    function InitDefaults()
    {
        $this->moButton->InitDefaults();
        $this->moButton->SetOnClick( 'myJSFunction' );
    }


    function ParseURL()
    {
        $this->moButton->ParseURL();
    }

    function GetJavascriptInitFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptInitFunctions();
        
        // add other init functions here if necessary
        
        return $aReturn;
    }

    function GetJavascriptVariables()
    {
        $aReturn = $this->moButton->GetJavascriptVariables();
        
        //add other variables here if necessary
        
        return $aReturn;
    }

    function GetJavascriptOnLoadFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptOnLoadFunctions();
        
        //add other OnLoad functions here if necessary
        
        return $aReturn;
    }

    function GetJavascriptIncludeFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptIncludeFunctions();
        
        //add other Include functions here if necessary
        
        return $aReturn;
    }

    function GetHTMLHiddenVariables()
    {
        $aReturn = $this->moButton->GetHTMLHiddenVariables();
        
        //add other HTML hidden variables here if necessary
        
        return $aReturn;
    }
    
    function GetJavascriptFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptFunctions();
        
        //add other Javascript functions here
        
        $szFunctionName = "myJSFunction";
        $szFunction = <<<EOT
function {$szFunctionName}()
{
    alert( "hey, you clicked me" );
}

        $aReturn[$szFunctionName] = $szFunction;
        
        return $aReturn;
    }

    function DrawPublish()
    {
        $szResult = $this->moButton->DrawPublish();

        //add other rendering code as required

        return $szResult;
    }
}

Popup Dialogs

Widgets that provide a Popup dialog as part of the widget (i.e. clicking a button to open a dialog) need some modifications to work correctly.  Popup support is provided by Popup.php.  This document does not describe how to set up a widget to provide popup services, just what changes are required in relation to Buttons to get Popups to work correctly.  The changes are in two separate parts, first in the widget code and second in the code for the popup dialog.

Widget Changes

In order to get Popups to launch correctly, it is necessary to create a javascript function to open the Popup dialog.  The Popup object handles most of this for the developer, but in any particular widget, you will need to output a javascript function that encapsulates the javascript provided by the Popup object.  It is this function that should be set as the Button's onclick event.  The previous example could be modified as follows:

    function GetJavascriptFunctions()
    {
        $aReturn = $this->moButton->GetJavascriptFunctions();
        
        $this->moPopup->mszLink = "widgets/myWidgetPopup.phtml";
        $szFunctionName = "myJSFunction";
        $szButtonJS = $this->moPopup->DrawPublish();
        $szFunction = <<<EOT
/**
 * {$szFunctionName}
 * popup a Bounding Box dialog
 */
function {$szJsFunctionName}()
{
    {$szButtonJS}
    return;
}
EOT;

        $aReturn[$szFunctionName] = $szFunction;
    }

This will cause a page called myWidgetPopup.phtml to be opened with the attributes specified in the widget tag's popup attributes.

Popup Dialog Changes

Popup dialogs do not need to change directly.  However, if they include buttons (from the previous TextButton stuff) then you will need to make a few changes.  If you are designing a new Popup, then the following applies also.

To access button functionality, include the CWC2ButtonCache.php file as follows (or in another block of php code before you need buttons)

<?php  include_once("CWC2ButtonCache.php"); ?>

To enable javascript buttons (multi-state), you will need to include the js files as follows somewhere in the <HEAD> section of the page.

<script language="JavaScript" src="<?php echo $_SESSION['gszCoreWebPath']; ?>/widgets/js/cwc_dhtml.js"></script>
<script language="JavaScript" src="<?php echo $_SESSION['gszCoreWebPath']; ?>/widgets/js/cwc_button.js"></script>

To output a button on the page at any location (in the BODY section), break to php (using the <?php ?> tags) and echo the results of a call to the makeButton function provided by CWC2ButtonCache.  This function gets the information about how to render buttons from the PHP session, and uses the values from the original SharedResource specified as the popupstyleresource.  The makeButton function takes the following parameters:

* jsFunction - the name of a javascript function to call.  It must be a function.  If you want to close the window, you cannot call window.close, you must have a javascript function that calls window.close and then pass the name of the function to makeButton.  Note that this is the name of the function only and cannot include the parentheses ().

* jsParams - the parameters to pass to the javascript function.  The javascript object representing the button is passed as the first parameter, the remaining params are passed second.  It is only possible to pass a single parameter.  If you need to pass multiple parameters, pass them as an array ... 'new Array( param, param2 )'

* widgetname - the name of the widget for which this is a popup.  This is very important and is used to retrieve the style resources for the buttons.

* image - the image to put on the button

* label - the label to put on the button, should be obtained from an MLT object to ensure that multilingual interfaces are respected.

* imagetip - the tooltip to display when the mouse is over the button

* extraparams - an array of buttonizer parameters to override the values obtained from the SharedResource values.  Often this will just be the image with.

A complete example follows:

<?php echo makeButton( 'applyZoomBox', '', 'BoundingBoxPopup', 'images/icon_zoom.png', trim($oCommonMLT->get("Zoom", "Zoom")), trim($oCommonMLT->get("ZoomRectTip", "Zoom to Bounding Coordinates")), array( 'width' => 75 )); ?>
--------------040807010900090106090904--



This archive was generated by Pipermail.