/usr/local/CPAN/XUL-Node/XUL/Node/MVC.pm
package XUL::Node::MVC;
use strict;
use warnings;
use Carp;
use Aspect;
use Aspect::Library::Listenable;
use XUL::Node;
use XUL::Node::Model::Value;
# node value model support ----------------------------------------------------
before {
# TODO: no way to remove model
my $context = shift;
my $view = $context->self;
my $key = $context->params_ref->[1];
my $value = $context->params_ref->[2];
my $tied = tied $context->params_ref->[2];
my $model = get_models($view) && get_models($view)->{$key};
my $is_simple_value =
(!$tied && !is_value_model($value)) ||
($tied && !is_value_model($tied));
if ($is_simple_value) {
return unless $model;
$context->return_value($value);
$model->value($value);
return;
}
if ($model) { remove_model($view, $key) }
else { init_models($view) }
$model = $tied || $value;
$context->params($view, $key, $model->value);
set_model($view, $key, $model);
} call 'XUL::Node::set_attribute';
before { remove_all_models(shift->self) } call 'XUL::Node::destroy';
sub init_models ($) {
my $view = shift;
return if get_models($view);
$view->{get_models_key()} = {};
}
# bind a view attribute to a model
sub set_model ($$$) {
my ($view, $key, $model) = @_;
# when model fires Change, call _$key on view, with one param:
# the value of the model
add_listener $model, Change => ["_$key", $view, [qw(value)]];
get_models($view)->{$key} = $model;
}
sub remove_model ($$) {
my ($view, $key) = @_;
my $models = get_models($view);
my $old_model = $models->{$key};
return unless $old_model;
remove_listener $old_model, Change => $view;
delete $models->{$key};
}
sub remove_all_models ($) {
my $view = shift;
my $models = get_models($view);
remove_model($view, $_) for keys %$models;
}
sub get_models ($) { shift->{get_models_key()} }
sub get_models_key () { __PACKAGE__. '_value_models' }
sub is_value_model ($) { UNIVERSAL::isa(shift, 'XUL::Node::Model::Value') }
# exporting -------------------------------------------------------------------
my @MODEL_CLASSES = qw(Value);
sub import {
my $class = shift;
my $package = caller();
my @widgets = @_;
no strict 'refs';
# export model factories and attributes
for my $name (@MODEL_CLASSES) {
my $model_class = "XUL::Node::Model::$name";
my $import_name = "${package}::$name";
# import the model attribtue
eval qq{
use Attribute::Handlers autotieref =>
{'$import_name', '$model_class'}
};
croak "cannot import attributes: [$@]" if $@;
# import model factory subs
*{"${import_name}Model"} = sub {
my %params = @_;
if (exists $params{tie}) {
delete $params{tie};
my $index;
for my $i (0..@_ - 1) { if($_[$i] eq 'tie') { $index = $i; last } }
my $tied = \$_[$index + 1];
return tie $$tied, $model_class, %params;
}
return $model_class->new(%params);
}
}
# add all XUL::Node stuff so YOU don't have to import it
for my $name (@XUL::Node::EXPORT) { *{"${package}::$name"} = *{"$name"} }
# export all custom widgets
XUL::Node::import_widgets($package, @widgets);
}
1;