The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
#############################################################################
## Name:        layout.pl
## Purpose:     Layout wxPerl sample
## Author:      Mattia Barbon
## Modified by:
## Created:      1/11/2000
## RCS-ID:      
## Copyright:   (c) 2000 Mattia Barbon
## Licence:     This program is free software; you can redistribute it and/or
##              modify it under the same terms as Perl itself
#############################################################################

use strict;
use Wx;

use vars qw($frame $menu_bar $layout_load_file $layout_test_sizer
 $layout_test_nb $layout_quit $layout_about $layout_test_plsizer);

( $layout_load_file, $layout_test_sizer, $layout_test_nb, $layout_quit,
  $layout_about, $layout_test_plsizer ) = ( 1 .. 10 );

package MyApp;

use vars qw(@ISA);

@ISA = qw(Wx::App);

sub OnInit {
  my( $this ) = @_;

  my( $frame ) = $main::frame =
    MyFrame->new( undef, 'wxPerl layout demo', 0, 0, 550, 500 );

  $frame->SetAutoLayout( 1 );
  $frame->CreateStatusBar( 2 );

  my( $file_menu ) = Wx::Menu->new();

  $file_menu->Append( $main::layout_load_file, '&Load file', 'Load a text file' );
  $file_menu->Append( $main::layout_test_sizer, '&Test sizers', 'Test sizer' );
  $file_menu->Append( $main::layout_test_nb, '&Test notebook sizers',
                      'Test notebook sizers' );
  $file_menu->Append( $main::layout_test_plsizer, '&Test Perl Sizers',
                      'Test Perl Sizers' );
  $file_menu->AppendSeparator();
  $file_menu->Append( $main::layout_quit, 'E&xit', 'Quit program' );

  my( $help_menu ) = Wx::Menu->new();

  $help_menu->Append( $main::layout_about, '&About', 'About layout demo' );

  my( $menu_bar ) = $main::menu_bar = Wx::MenuBar->new();

  $menu_bar->Append( $file_menu, '&File' );
  $menu_bar->Append( $help_menu, '&Help' );

  $frame->SetMenuBar( $menu_bar );

  use Wx qw(wxTAB_TRAVERSAL);

  $frame->{PANEL} = Wx::Panel->new( $frame, 0, [0, 0], [1000, 500], 
                                    wxTAB_TRAVERSAL );

  my( $btn1 ) = Wx::Button->new( $frame->panel, -1, 'A button (1)' );
  my( $b1 ) = Wx::LayoutConstraints->new();

  use Wx qw(:everything);

  $b1->centreX->SameAs( $frame->panel, wxCentreX );
  $b1->top->SameAs( $frame->panel, wxTop, 5 );
  $b1->width->PercentOf( $frame->panel, wxWidth, 80 );
  $b1->height->PercentOf( $frame->panel, wxHeight, 10 );
  $btn1->SetConstraints( $b1 );

  my( $list ) = Wx::ListBox->new( $frame->panel, -1, [-1, -1], [200, 100] );

  $list->Append( 'Apple' );
  $list->Append( 'Pear' );
  $list->Append( 'Orange' );
  $list->Append( 'Banana' );
  $list->Append( 'Fruit' );

  my( $b2 ) = Wx::LayoutConstraints->new();
  $b2->top->Below( $btn1, 5 );
  $b2->left->SameAs( $frame->panel, wxLeft, 5 );
  $b2->width->PercentOf( $frame->panel, wxWidth, 40 );
  $b2->bottom->SameAs( $frame->panel, wxBottom, 5 );
  $list->SetConstraints( $b2 );

  my( $mtext ) = Wx::TextCtrl->new( $frame->panel, -1, 'Some text',
                                    [-1, -1], [150, 100] );

  my( $b3 ) = Wx::LayoutConstraints->new();
  $b3->top->Below( $btn1, 5 );
  $b3->left->RightOf( $list, 5 );
  $b3->right->SameAs( $frame->panel, wxRight, 5 );
  $b3->bottom->SameAs( $frame->panel, wxBottom, 5 );
  $mtext->SetConstraints( $b3 );

  use Wx qw(wxRETAINED);

  $frame->{CANVAS} = MyWindow->new( $frame, 0, 0, 400, 400, wxRETAINED );
  $frame->{TEXT_WINDOW} = MyTextWindow->new( $frame, 0, 250, 400, 250 );

  my( $c1 ) = Wx::LayoutConstraints->new();
  $c1->left->SameAs( $frame, wxLeft );
  $c1->top->SameAs( $frame, wxTop );
  $c1->right->PercentOf( $frame, wxWidth, 50 );
  $c1->height->PercentOf( $frame, wxHeight, 50 );
  $frame->panel->SetConstraints( $c1 );

  my( $c2 ) = Wx::LayoutConstraints->new();
  $c2->left->SameAs( $frame->panel, wxRight );
  $c2->top->SameAs( $frame, wxTop );
  $c2->right->SameAs( $frame, wxRight );
  $c2->height->PercentOf( $frame, wxHeight, 50 );
  $frame->canvas->SetConstraints( $c2 );

  my( $c3 ) = Wx::LayoutConstraints->new();
  $c3->left->SameAs( $frame, wxLeft );
  $c3->top->Below( $frame->panel );
  $c3->right->SameAs( $frame, wxRight );
  $c3->bottom->SameAs( $frame, wxBottom );
  $frame->text_window->SetConstraints( $c3 );

  $frame->Show( 1 );
  $frame->SetStatusText( 'wxPerl layout demo' );

  $this->SetTopWindow( $frame );

  1;
}

package MyFrame;

use vars qw(@ISA);

@ISA = qw(Wx::Frame);

use Wx::Event qw(EVT_MENU);

sub new {
  my( $class ) = shift;
  my( $this ) = $class->SUPER::new( $_[0], -1, $_[1], [ @_[2, 3] ],
                                    [ @_[4, 5] ] );

  $this->SetIcon( Wx::GetWxPerlIcon() );

  EVT_MENU( $this, $main::layout_load_file, \&LoadFile );
  EVT_MENU( $this, $main::layout_quit, \&Quit );
  EVT_MENU( $this, $main::layout_test_sizer, \&TestSizers );
  EVT_MENU( $this, $main::layout_test_nb, \&TestNotebookSizers );
  EVT_MENU( $this, $main::layout_about, \&About );
  EVT_MENU( $this, $main::layout_test_plsizer, \&TestPlSizers );

  return $this;
}

sub LoadFile {
  my( $str ) = Wx::FileSelector( 'Load text file', undef, undef, undef, '*.txt' );

  $main::frame->text_window->LoadFile( $str ) if( length $str );
}

sub Quit {
  $_[0]->Close( 1 );
}

use Wx qw(wxOK wxCENTRE);

sub About {
  Wx::MessageBox( 'wxPerl layout demo', 'wxPerl Layout demo', wxOK|wxCENTRE );
}

sub TestSizers {
  my( $frame ) = MySizerFrame->new( undef, 'Sizer test frame', 50, 50 );
  $frame->Show( 1 );
}

sub TestPlSizers {
  my( $frame ) = MyPlSizerFrame->new( undef, 'PlSizer test frame', 100, 100 );
  $frame->Show( 1 );
}

use Wx qw(wxID_OK wxDefaultPosition wxDefaultSize wxTE_MULTILINE);
use Wx qw(:sizer);

sub TestNotebookSizers {
  my( $this, $event ) = @_;
  my( $dialog ) = Wx::Dialog->new( $this, -1, 'Notebook sizer test dialog' );
  my( $topsizer ) = Wx::BoxSizer->new( wxVERTICAL );
  my( $notebook ) = Wx::Notebook->new( $dialog, -1 );
  my( $nbs ) = Wx::NotebookSizer->new( $notebook );

  $topsizer->Add( $nbs, wxGROW );
  my( $button ) = Wx::Button->new( $dialog, wxID_OK, 'OK' );
  $topsizer->Add( $button, 0, wxALIGN_RIGHT|wxALL, 10 );
  my( $multi ) = Wx::TextCtrl->new( $notebook, -1, 'TextCtrl', wxDefaultPosition,
                                    wxDefaultSize, wxTE_MULTILINE );
  $notebook->AddPage( $multi, 'Page One' );

  my( $panel ) = Wx::Panel->new( $notebook, -1 );
  $notebook->AddPage( $panel, 'Page Two' );
  my( $panelsizer ) = Wx::BoxSizer->new( wxVERTICAL );
  my( $text ) = Wx::TextCtrl->new( $panel, -1, 'Textline 1', wxDefaultPosition,
                                   [250, -1] );
  $panelsizer->Add( $text, 0, wxGROW|wxALL, 30 );
  $text = Wx::TextCtrl->new( $panel, -1, 'Textline 2', wxDefaultPosition,
                             [250, -1] );
  $panelsizer->Add( $text, 0, wxGROW|wxALL, 30 );
  my( $button2 ) = Wx::Button->new( $panel, -1, 'Hello' );
  $panelsizer->Add( $button2, 0, wxALIGN_RIGHT|wxLEFT|wxRIGHT|wxBOTTOM, 30 );

  $panel->SetAutoLayout( 1 );
  $panel->SetSizer( $panelsizer );

  $dialog->SetAutoLayout( 1 );
  $dialog->SetSizer( $topsizer );
  $topsizer->Fit( $dialog );
  $topsizer->SetSizeHints( $dialog );

  $dialog->ShowModal();
  $dialog->Destroy();
}

use Wx qw(wxGREEN_PEN wxCYAN_BRUSH wxRED_PEN wxBLACK_PEN);
use Wx qw(wxPOINT);
sub Draw {
  my( $this, $dc ) = @_;

  $dc->SetPen( wxGREEN_PEN );
  $dc->DrawLine( 0, 0, 200, 200 );
  $dc->DrawLine( 200, 0, 0, 200 );

  $dc->SetBrush( wxCYAN_BRUSH );
  $dc->SetPen( wxRED_PEN );

  $dc->DrawRectangle( 100, 100, 100, 50 );
  $dc->DrawRoundedRectangle( 150, 150, 100, 50, 20 );

  $dc->DrawEllipse( 250, 250, 100, 50 );
  $dc->DrawSpline( [ wxPOINT(50, 200 ), [ 50, 100 ],
                   wxPOINT( 200, 10 ) ] );

  $dc->DrawLine( 50, 230, 200, 230 );
  $dc->SetPen( wxBLACK_PEN );
  $dc->DrawArc( 50, 300, 100, 250, 100, 300 );
}

sub panel {
  $_[0]->{PANEL};
}

sub canvas {
  $_[0]->{CANVAS};
}

sub text_window {
  $_[0]->{TEXT_WINDOW};
}

package MyWindow;

use vars qw(@ISA);

@ISA = qw(Wx::Window);

use Wx::Event qw(EVT_PAINT);

sub new {
  my( $class ) = shift;
  my( $this ) = $class->SUPER::new( $_[0], -1, [ @_[1, 2] ],
                                    [ @_[3, 4] ], $_[5] );

  EVT_PAINT( $this, \&OnPaint );

  return $this;
}

sub OnPaint {
  my( $this, $event ) = @_;
  my( $dc ) = Wx::PaintDC->new( $this );

  $main::frame->Draw( $dc, 1 );
}

package MyTextWindow;

use vars qw(@ISA);

@ISA = qw(Wx::TextCtrl);

use Wx qw(wxTE_MULTILINE);

sub new {
  my( $class ) = shift;
  my( $this ) = $class->SUPER::new( $_[0], -1, '', [ @_[1, 2] ],
                                    [ @_[3, 4] ], $_[5]|wxTE_MULTILINE );
  return $this;
}

package MySizerFrame;

use vars qw(@ISA);

@ISA = qw(Wx::Frame);

use Wx qw(:sizer wxTE_MULTILINE wxDefaultPosition);

sub new {
  my( $class ) = shift;
  my( $this ) = $class->SUPER::new( $_[0], -1, $_[1], [ @_[2, 3] ] );

  my( $topsizer ) = Wx::BoxSizer->new( wxVERTICAL );

  $topsizer->Add( Wx::StaticText->new( $this, -1, 'An explanation (wxALIGN_RIGHT)' ),
                  0, wxALIGN_RIGHT|wxTOP|wxLEFT|wxRIGHT, 5 );
  $topsizer->Add( Wx::TextCtrl->new( $this, -1, 'My text (wxEXPAND)', wxDefaultPosition,
                                [ 100, 60 ], wxTE_MULTILINE ),
                  1, wxEXPAND|wxALL, 5 );

  my( $statsizer ) = Wx::StaticBoxSizer->new( Wx::StaticBox->new( $this, -1,
                                                                  'A wxStaticBoxSizer' ),
                                              wxVERTICAL );
  $statsizer->Add( Wx::StaticText->new( $this, -1, 'And some TEXT inside it' ),
                   0, wxCENTER|wxALL, 30 );
  $topsizer->Add( $statsizer, 1, wxEXPAND|wxALL, 10 );

  use Wx qw(wxGROW wxALIGN_CENTER_VERTICAL);

  my( $gridsizer ) = Wx::GridSizer->new( 0, 2, 5, 5 );
  $gridsizer->Add( Wx::StaticText->new( $this, -1, 'Label' ), 0,
                   wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL );
  $gridsizer->Add( Wx::TextCtrl->new( $this, -1, 'Grid sizer demo' ), 1,
                   wxGROW|wxALIGN_CENTER_VERTICAL );
  $gridsizer->Add( Wx::StaticText->new( $this, -1, 'Another label' ), 0,
                   wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL );
  $gridsizer->Add( Wx::TextCtrl->new( $this, -1, 'More text' ), 0,
                   wxGROW|wxALIGN_CENTER_VERTICAL );
  $gridsizer->Add( Wx::StaticText->new( $this, -1, 'Final label' ), 0,
                   wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL );
  $gridsizer->Add( Wx::TextCtrl->new( $this, -1, 'And yet more text' ), 0,
                   wxGROW|wxALIGN_CENTER_VERTICAL );
  $topsizer->Add( $gridsizer, 1, wxGROW|wxALL, 10 );

  use Wx qw(wxHORIZONTAL);

  $topsizer->Add( Wx::StaticLine->new( $this, -1, wxDefaultPosition, [ 3, 3 ],
                                       wxHORIZONTAL ),
                  0, wxEXPAND|wxALL, 5 );

  my( $button_box ) = Wx::BoxSizer->new( wxHORIZONTAL );
  $button_box->Add( Wx::Button->new( $this, -1, 'Two buttons in a box' ),
                    0, wxALL, 7 );
  $button_box->Add( Wx::Button->new( $this, -1, '(wxCENTER)' ),
                    0, wxALL, 7 );
  $topsizer->Add( $button_box, 0, wxCENTER );

  $this->SetAutoLayout( 1 );
  $topsizer->Fit( $this );
  $topsizer->SetSizeHints( $this );
  $this->SetSizer( $topsizer );

  return $this;
}

package MyPlSizerFrame;

use strict;
use vars qw(@ISA);

@ISA = qw(Wx::Frame);

use Wx qw(:sizer);

sub new {
  my $class = shift;
  my $this = $class->SUPER::new( $_[0], -1, $_[1], [ @_[2,3] ] );

  my $panel = Wx::Panel->new( $this, -1 );
  my $s = MyBoxSizer->new( wxVERTICAL );

  $s->Add( Wx::TextCtrl->new( $panel, -1, "This is a test" ),
           2, wxGROW|wxALL, 20 );

  my $s2 = MyBoxSizer->new( wxHORIZONTAL );
  $s->Add( $s2, 1, wxGROW|wxALL, 10 );

  $s2->Add( Wx::Button->new( $panel, -1, "Button1" ), 1, wxGROW, 5 );
  $s2->Add( Wx::Button->new( $panel, -1, "Button2" ), 2, wxGROW, 10 );

  $panel->SetSizer( $s );
  $panel->SetAutoLayout( 1 );

  $this
}

package MyBoxSizer;

use strict;
use vars qw(@ISA);

@ISA = qw(Wx::PlSizer);

sub new {
  my $class = shift;
  my $this = $class->SUPER::new;

  $this->{ORIENT} = shift;

  $this;
}

use Wx qw(:sizer);

sub RecalcSizes {
  my $this = shift;

  return if $this->GetChildren == 0;

  my( $delta, $extra );
  if( $this->{STRETCHABLE} ) {
    my $t;

    if( $this->{ORIENT} == wxHORIZONTAL ) {
      $t = $this->GetSize->x - $this->{FIXEDWIDTH};
    } else {
      $t = $this->GetSize->y - $this->{FIXEDHEIGHT};
    }

    $delta = int( $t / $this->{STRETCHABLE} );
    $extra = $t % $this->{STRETCHABLE};
  }

  my $pt = $this->GetPosition;

  foreach my $item ( $this->GetChildren ) {
    my $weight = $item->GetOption ? $item->GetOption : 1;
    my $size = $item->CalcMin;

    if( $this->{ORIENT} == wxVERTICAL ) {
      my $height = $size->y;
      if( $item->GetOption ) {
        $height = $delta * $weight + $extra;
        $extra = 0;
      }

      my $child_pos = $pt;
      my $child_size = Wx::Size->new( $size->x, $height );

      if( $item->GetFlag & ( wxEXPAND | wxSHAPED ) ) {
        $child_size->x( $this->GetSize->x );
      } elsif( $item->GetFlag & wxALIGN_RIGHT ) {
        $child_pos->x( $child_pos->x + $this->GetSize->x - $size->x );
      } elsif( $item->GetFlag & ( wxCENTER | wxALIGN_CENTER_HORIZONTAL ) ) {
        $child_pos->x( $child_pos->x + ( $this->GetSize->x - $size->x ) / 2 );
      }

      $item->SetDimension( $child_pos, $child_size );

      $pt->y( $pt->y + $height );
    } else {
      my $width = $size->x;
      if( $item->GetOption ) {
        $width = $delta * $weight + $extra;
        $extra = 0;
      }

      my $child_pos = $pt;
      my $child_size = Wx::Size->new( $width, $size->y );

      if( $item->GetFlag & ( wxEXPAND | wxSHAPED ) ) {
        $child_size->y( $this->GetSize->y );
      } elsif( $item->GetFlag & wxALIGN_BOTTOM ) {
        $child_pos->y( $child_pos->y + $this->GetSize->y - $size->y );
      } elsif( $item->GetFlag & ( wxCENTER | wxALIGN_CENTER_VERTICAL ) ) {
        $child_pos->y( $child_pos->y + ( $this->GetSize->y - $size->y ) / 2 );
      }

      $item->SetDimension( $child_pos, $child_size );

      $pt->x( $pt->x + $width );
    }
  }
}

sub CalcMin {
  my $this = shift;

  return Wx::Size->new( 10, 10 ) if( $this->GetChildren == 0 );

  $this->{STRETCHABLE} = 0;
  $this->{MINWIDTH} = 0;
  $this->{MINHEIGHT} = 0;
  $this->{FIXEDWIDTH} = 0;
  $this->{FIXEDHEIGHT} = 0;

  foreach my $item ( $this->GetChildren ) {
    $this->{STRETCHABLE} += $item->GetOption;

    my $size = $item->CalcMin;

    if( $this->{ORIENT} == wxHORIZONTAL ) {
      $this->{MINWIDTH} += $size->x;
      $this->{MINHEIGHT} = $this->{MINHEIGHT} < $size->y ?
        $size->y : $this->{MINHEIGHT};
    } else {
      $this->{MINHEIGHT} += $size->y;
      $this->{MINWIDTH} = $this->{MINWIDTH} < $size->x ?
        $size->x : $this->{MINWIDTH};
    }

    if( $item->GetOption == 0 ) {
      if( $this->{ORIENT} == wxVERTICAL ) {
        $this->{FIXEDHEIGHT} += $size->y;
        $this->{FIXEDWIDTH} = $this->{FIXEDWIDTH} < $size->x ?
          $size->x : $this->{FIXEDWIDTH};
      } else {
        $this->{FIXEDWIDTH} += $size->x;
        $this->{FIXEDHEIGHT} = $this->{FIXEDHEIGHT} < $size->y ?
          $size->y : $this->{FIXEDHEIGHT};
      }
    }
  }

  return Wx::Size->new( $this->{MINWIDTH}, $this->{MINHEIGHT} );
}

package main;

my( $app ) = MyApp->new();
$app->MainLoop();

# Local variables: #
# mode: cperl #
# End: #