NAME

POE::XUL::Node - XUL element

SYNOPSIS

use POE::XUL::Node;

# Flexible way of creating an element
my $box = POE::XUL::Node->new( tag => 'HBox', 
                               Description( "Something" ),
                               class => 'css-class',
                               style => $css,
                               Click => $poe_event  
                             );

# DWIM way
$window = Window(                            # window with a header,
   HTML_H1(textNode => 'a heading'),         # a label, and a button
   $label = Label(FILL, value => 'a label'),
   Button(label => 'a button'),
);

# attributes
$window->width( 800 );
$window->height( 600 );

$label->value('a value');
$label->style('color:red');
print $label->flex;

# compositing
print $window->child_count;                  # prints 2
$window->Label(value => 'another label');    # add a label to window
$window->appendChild(Label);                 # same but takes child as param
$button = $window->get_child(1);             # navigate the widget tree
$window->add_child(Label, 0);                # add a child at an index

# events
$window->Button(Click => sub { $label->value('clicked!') });
$window->MenuList(
   MenuPopup(map { MenuItem( label => "item #$_", ) } 1..10 ),
   Select => sub { $label->value( $_[0]->selectedIndex ) },
);

# disposing
$window->removeChild($button);                # remove child widget
$window->remove_child(1);                     # remove child by index

DESCRIPTION

POE::XUL::Node is a DOM-like object that encapsulates a XUL element. It uses POE::XUL::ChangeManager to make sure all changes are mirrored in the browser's DOM.

Elements

To create a UI, an application must create a Window with some elements in it. Elements are created by calling a function or method named after their tag:

$button = Button;                           # orphan button with no label
$box->Button;                               # another, but added to a box
$widget = POE::XUL::Node->new(tag => $tag); # using dynamic tag

After creating a widget, you must add it to a parent. The widget will show when there is a containment path between it and a window. There are multiple ways to set an elements parent:

$parent->appendChild($button);              # DOM-like
$parent->replaceChild( $old, $new );        # DOM-like
$parent->add_child($button);                # left over from XUL-Node
$parent->add_child($button, 1);             # at an index
$parent->Button(label => 'hi!');            # create and add in one shot
$parent = Box(style => 'color:red', $label);# add in parent constructor

Elements can be removed from the document by removing them from their parent:

$parent->removeChild($button);           # DOM-like
$parent->remove_child(0);                 # index
$parent->replaceChild( $old, $new );        # DOM-like

Elements have attributes. These can be set in the constructor, or via a method of the same name:

my $button = Button( value => 'one button' );
$button->value('a button');
print $button->value;                       # prints 'a button'

You can configure all attributes, event handlers, and children of a element, in the constructor. There are also constants for commonly used attributes. This allows for some nice code:

Window( SIZE_TO_CONTENT,
   Grid( FLEX,
      Columns( Column(FLEX), Column(FLEX) ),
      Rows(
         Row(
            Button( label => "cell 1", Click => $poe_event ),
            Button( label => "cell 2", Click => $poe_event ),
         ),
         Row(
            Button( label => "cell 3", Click => $poe_event ),
            Button( label => "cell 4", Click => $poe_event ),
         ),
      ),
   ),
);

Check out the XUL references (http://developer.mozilla.org/en/docs/XUL) for an explanation of available elements and their attributes.

The id attribute

POE::XUL requires each node to have a unique identifier. If you do not set the id attribute of an node, it will assigned one. A node's id attribute must be globally to the application, including across windows in the same application. This is contrary to how the DOM works, where elements in different windows may share an id, may even not have one.

Use <POE::XUL::Window/getElementById> to find a node by its id.

Events

Elements receive events from their client halves, and pass them on to attached listeners in the application. You attach a listener to a widget so:

# listening to existing widget
$textbox->attach( Change => sub { print 'clicked!' } );

# listening to widget in constructor
TextBox( Change => $poe_event );

You attach events by providing an event name and a listener. Possible event names are Click, Change, Select, and Pick. Different widgets fire different events. These are listed in POE::XUL::Event.

Listener are either the name of a POE event, or a callbacks that receives a single argument: the event object (POE::XUL::Event). POE events are called on the application session, NOT the current session when an event is defined. If you want to post to another session, use "callback" in POE::Session.

You can query the Event object for information about the event: name, source, and depending on the event type: checked, value, color, and selectedIndex.

Here is an example of listening to the Select event of a list box:

Window(
   VBox(FILL,
      $label = Label(value => 'select item from list'),
      ListBox(FILL, selectedIndex => 2,
         (map { ListItem(label => "item #$_") } 1..10),
         Select => sub {
            $label->value
               ("selected item #${\( shift->selectedIndex + 1 )}");
         },
      ),
   ),
);

Events are removed with the "detach" method:

$button->detach( 'Click' );

Style

An element's style property is implemented by a POE::XUL::Style object, which allows DOM-like manipulation of the element's style declaration.

my $button = Button( style=>'color: red' );
$button->style->color( 'puce' );

XUL-Node API vs. the XUL DOM

The XUL-Node API is different in the following ways:

  • Booleans are Perl booleans, not true and false.

  • All nodes must have an id attribute. If you do not specify one, it will be automatically generated by POE::XUL::Node.

  • There is little difference between attributes, properties, and methods. They are all attributes on the POE::XUL::Node object. However, the javascript client library handles them differently.

    This means that to call a method or a property, you have to specify at least one parameter:

    $node->blur( 0 );           # Equiv to node.blur() in JS
  • While all attribute and properties are mirrored from the Perl object to the DOM object, only a select few are mirrored back (value, selected, selectedIndex).

  • You currently can not move nodes around in the DOM.

    my $node = $parent->getChild( 3 );
    my $new_node = Description( content => $node );
    $parent->removeChild( 3 );
    $parent->appendChild( $new_node );      # FAIL!

ELEMENT CONSTRUCTORS

To make life funner, a bunch of constructor functions have been defined for the most commonly used elements. These functions are exported into any package that uses POE::XUL::Node.

XUL Elements

ArrowScrollBox, Box, Button, Caption, CheckBox, ColorPicker, Column, Columns, Deck, Description, Grid, Grippy, GroupBox, HBox, Image, Label, ListBox, ListCell, ListCol, ListCols, ListHead, ListHeader, ListItem, Menu, MenuBar, MenuItem, MenuList, MenuPopup, MenuSeparator, ProgressMeter, Radio, RadioGroup, Row, Rows, Seperator, Spacer, Splitter, Stack, StatusBar, StatusBarPanel, Tab, TabBox, TabPanel, TabPanels, Tabs, TextBox, ToolBar, ToolBarButton, ToolBarSeperator, ToolBox, VBox, Window.

It is of course possible to create any other XUL element with:

POE::XUL::Node->new( tag => $tag );

HTML Elements

HTML_Pre, HTML_H1, HTML_H2, HTML_H3, HTML_H4, HTML_A, HTML_Div, HTML_Br, HTML_Span.

It is of course possible to create any other HTML element with:

POE::XUL::Node->new( tag => "html:$tag" );

SPECIAL ELEMENTS

There are 4 special elements:

Script

Script( $JS );

Creates a script element, with type="text/javascript", and a single POE::XUL::CDATA child. The client library will eval() the script.

Boot

Boot( $text );

Sends the boot command to the client library. Currently, the client library calls $status.title( $text );, if the $status object exists. Your application must create $status.

RawCmd

RawCmd( \@cmd );

Allows you to send a raw command to the Javascript client library. Use at your own risk.

pxInstructions

pxInstructions( @instructions );

Send instructions to the ChangeManager. This is a slightly higher-level form of "RawCmd". Its presence indicates the immaturity of POE::XUL as a whole. These instructions are subject to change/removal in the future.

@instructions is an array instructions for the ChangeManager. See "instrction" in POE::XUL::ChangeManager for details.

METHODS

createTextNode

Creates and populates a POE::XUL::TextNode. Returns the new node.

my $tn = window->createTextNode( 'Some text' );

textNode

Sets or changes the text of a node, such as description. If the node has multiple children (aka <i>mixed-mode</i>) then it will replace the first textNode it finds. If there are none, it will append a new text node. See POE::XUL::TextNode.

my $d = Description( textNode => 'Hello world!' );
$d->textNode( 'This is different' );

children

Find a given node's child nodes. Returns array in array context, an array reference in scalar context. Modifying the arrayref will NOT modify the node's list of children.

foreach my $node ( $box->children ) {
    # ...
}

child_count

Returns the number of child nodes of an node.

hasChildNodes

Returns true if a node has child nodes.

add_child

$parent->add_child( $node, $index );

appendChild

$parent->appendChild( $node );

firstChild / first_child

my $node = $parent->firstChild;

get_child

my $node = $parent->get_child( $index );

Use <POE::XUL::Window/getElementById> to find a node by its id.

getItemAtIndex / get_item

my $node = $menu->getItemAtIndex( $index );

Like "get_child", but works for menulist and menupopup.

lastChild / last_child

my $node = $parent->lastChild;

removeChild / remove_child

$parent->removeChild( $node );
$parent->removeChild( $index );

replaceChild

$parent->replaceChild( $old, $new );

attributes

my %hash = $node->attributes;
my $hashref = $node->attributes;

Note that even if you manipulate $hashref directly, changes will not be mirrored in the node.

getAttribute / get_attribute

my $value = $node->getAttribute( $name );

setAttribute / set_attribute

$node->setAttribute( $name => $value );

removeAttribute / remove_attribute

$node->removeAttribute( $name );

hide

$node->hide;

Syntatic sugar that does the following:

$node->style->display( 'none' );

show

$node->show;

Syntatic sugar that does the following:

$node->style->display( '' );

attach

$node->attach( $Event => $listener );
$node->attach( $Event => $coderef );
$node->attach( $Event );

Attaches an event listener to a node. When $Event happens (normaly in response to a DOM event) the $poe_event is posted to the application session. Alternatively, the $coderef is called. In both cases, an POE::XUL::Event object is passed as the first parameter. $poe_event defaults to $Event.

attach() will auto-create handlers for POE::XUL::Application.

detach

$node->detach( $Event );

Removes the event listener for $Event. Auto-created handlers are currently not removed.

event

my $listener = $node->event( $Event );

Gets the node's event listener for $Event. A listener is either a coderef, or the name of a POE event handler in the application's session. Application code will rarely need to call this method.

dispose / distroy

Calls dispose on all the child nodes, and drops all events.

as_xml

Returns this element and all its child elements as an unindented XML string. Useful for debuging.

LIMITATIONS

  • Some elements are not supported yet: tree, popup.

  • Some DOM features are not supported yet:

    * multiple selections
    * node disposal
    * color picker will not fire events if type is set to button
    * equalsize attribute will not work
    * menus with no popups may not show
  • Some XUL properties are implemented with XBL. The front-end attempts to wait for the XBL to be created before setting the property. If the object takes too long, the attribute is set instead.

    What this means is that you can't reliably set the properties of freshly created nodes.

SEE ALSO

POE::XUL. POE::XUL::Event presents the list of all possible events.

http://developer.mozilla.org/en/docs/XUL has a good XUL reference.

AUTHOR

Philip Gwyn <gwyn-at-cpan.org>

CREDITS

Based on work by Ran Eilam.

COPYRIGHT AND LICENSE

Copyright 2007-2009 by Philip Gwyn. All rights reserved;

Copyright 2003-2004 Ran Eilam. All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.