Source

Kephra / lib / Kephra / App / Sizer.pm

use strict;
use warnings;
use Kephra::API;
use Kephra::App::Util;

package Kephra::App::Sizer;
#our @ISA = 'Wx::Sizer';
use base 'Wx::Sizer';
my ($is_widget, $is_sizer) = Kephra::App::Util::get('is_widget', 'is_sizer');
use Scalar::Util qw(blessed looks_like_number);


sub new {
	my $class = shift;
	my $orient = shift;
	if    (lc substr($orient, 0, 1) eq 'v') { $orient = &Wx::wxVERTICAL }
	elsif (lc substr($orient, 0, 1) eq 'h') { $orient = &Wx::wxHORIZONTAL }
	#elsif (not defined $orient)             { $orient = &Wx::wxHORIZONTAL }
	else {
		return Kephra::Log::error
			("need h|horizontal or v|vertical as first parameter, not $orient", 1)
	}
	my $self = $class->SUPER::new( $orient );
	$self->append(@_);
	$self;
}


# syntax sugar for insert method, unless insert can take list of child items
# all these methods are ment to be chained, thatswhy the $self at the end
sub prepend         { shift->insert(  0, 0, @_ ) }
sub append          { shift->insert( -1, 0, @_ ) }
sub append_expanded { shift->insert( -1, 1, @_ ) }
sub insert_before   { my($self)=shift; $self->insert( $self->get_position(shift)  , 1, @_ ) }
sub insert_after    { my($self)=shift; $self->insert( $self->get_position(shift)+1, 1, @_ ) }

#sub insert_before   {
	#my ($self) = shift;
	#my ($mark_child) = shift;
	#my $pos = $self->get_position($mark_child);
	#return Kephra::Log::error("bookmark child $mark_child not found", 1) unless $pos > -1;
	#$self->insert($pos, 0, @_);
#}
#sub insert_after   {
	#my ($self) = shift;
	#my ($mark_child) = shift;
	#my $pos = $self->get_position($mark_child);
	#return Kephra::Log::error("bookmark child $mark_child not found", 1) unless $pos > -1;
	#$self->insert($pos+1, 0, @_);
#}
sub insert          {
	my ($self) = shift;
	my ($position) = shift;
	my ($proportion)  = shift;
	for (@_) {
		$self->_insert( $self->_build_item($position, $proportion, $_) );
		$position++ unless $position == -1;
	}
	$self->Layout;
	$self;
}

# accepted items : 
# - arrayref: sizer with crossing orientation holding the list of items in the array
# - widget: add widget without proportion
# - widgetref: add with proportion
# - int: spacer
# - intref: stretchspacer
sub _build_item {
	my ($self, $position, $proportion, $child) = @_;
	my $refcount;
	while (ref $child eq 'REF'){ $refcount++; $child = $$child; }

	if (ref $child eq 'ARRAY'){
		my $child_orient = 
			$refcount % 2                              ? $self->GetOrientation
		  : $self->GetOrientation == &Wx::wxHORIZONTAL ? &Wx::wxVERTICAL
		  :                                              &Wx::wxHORIZONTAL;
		my @subsizer_children = @$child;
		$child = __PACKAGE__->new( $child_orient, @subsizer_children );
	}

	my %item;
	if    ($is_widget->($child) or $is_sizer->($child))       { $item{'child'} = $child }
	elsif (looks_like_number($child) and int $child == $child){ $item{'child'} = 'space'}
	elsif (ref $child eq 'HASH'){ %item = %$child }           # already assembled
	else  { return Kephra::Log::error("got no proper widget, but $child", 2) }

	$item{'position'} = $position;
	$item{'proportion'} = $proportion if $proportion;
	$item{'proportion'} += $refcount if $refcount;

	my %presets = (
		position => -1, proportion => 0, style => &Wx::wxGROW, border => 0,
	);
	# fill with default settings (presets)
	for (qw/position proportion style border/)
		{ $item{$_} = $presets{$_} unless exists $item{$_} }
	$item{position} = $self->GetChildren || 0 if $item{position} == -1;

	return \%item;
}

sub _insert { # only one item
	my ($self, $item) = @_;
	return Kephra::Log::error('got hash as item def') unless ref $item eq 'HASH';

	if ($item->{'child'} eq 'space'){
		if ($item->{'proportion'}){
			$self->InsertStretchSpacer( $item->{'position'},  $item->{'proportion'} )
		} else {
			$self->InsertSpacer(        $item->{'position'},  $item->{'border'} )
		}
	} else {
		$self->Insert(
			$item->{'position'},  $item->{'child'},  $item->{'proportion'},
			$item->{'style'},     $item->{'border'}
		);
	}
	$item->{'position'};
}



sub show     {shift->_relayout( sub { $_[0]->Show(  $_[1],1) }, @_) }
sub hide     {shift->_relayout( sub { $_[0]->Hide(   $_[1] ) }, @_) }
sub detach   {shift->_relayout( sub { $_[0]->Detach( $_[1] ) }, @_) }
sub remove   {shift->_relayout( sub { $_[0]->Remove( $_[1] ) }, @_) } # del spacer & sizer
sub _relayout{
	my ($self) = shift;
	my ($call) = shift;
	return Kephra::Log::error('need a coderef, not $call', 2) unless ref $call eq 'CODE';
	$call->($self, $_) for @_;
	$self->Layout;
	$self;
}


# getter
sub get_position {                                      # number of that child
	my ($self, $child) = @_;
	my $pos = 0;
	if ($is_widget->($child)){
		for ($self->GetChildren){
			return $pos if $_->IsWindow and $_->GetWindow eq $child;
			$pos++;
		}
	}
	elsif ($is_sizer->($child)){
		for ($self->GetChildren){
			return $pos if $_->IsSizer and $_->GetSizer eq $child;
			$pos++;
		}
	} 
	else { return Kephra::Log::error("got no proper widget or sizer, but $child", 1) }
	Kephra::Log::error("$child is no child of $self", 1);
	return -1;
}
sub is_child   { $_[0]->get_position( $_[1] ) > -1 ? 1 : 0 }
sub child_item { $_[0]->GetItem($_[1]) }                # child with that number
sub childsizer { 
	my ($self) = shift;
	my @sizer;
	for ($self->GetChildren){ push @sizer, $_->GetSizer if $_->IsSizer }
	@sizer;
}
sub widgets    {
	my ($self) = shift;
	my @widgets;
	for ($self->GetChildren){ push @widgets, $_->GetWindow if $_->IsWindow }
	@widgets;
}

sub sizer_items  { shift->_items( sub{ $_[0]->IsSizer  } ) }
sub space_items  { shift->_items( sub{ $_[0]->IsSpacer } ) }
sub widget_items { shift->_items( sub{ $_[0]->IsWindow } ) }
sub _items       {
	my ($self, $check) = @_;
	return Kephra::Log::error("no checker coderef as first parameter, only $check", 1)
		unless ref $check eq 'CODE';

	my @item;
	for ($self->GetChildren){ push @item, $_ if $check->($_) }
	@item;
}

1;