# # Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # Created by: # Vadim Belman # # $Id: FrameSet.pm,v 1.9 2007/09/13 15:12:24 dk Exp $ use strict; use Prima; use Prima::Const; package fra; # Frame arragement constants. use constant Vertical => 0; use constant Horizontal => 1; package frr; # Frame resize method constants. use constant Simple => 0; use constant Proportional => 1; # XXX Yet to be implemented. package Prima::FrameSet::Frame; use strict; use vars qw(@ISA); @ISA = qw(Prima::Widget Prima::MouseScroller); # Initialization sub profile_default { return { %{ $_[0]-> SUPER::profile_default}, minFrameWidth => 5, maxFrameWidth => undef, }; } sub init { my $me = shift; my %profile = $me-> SUPER::init(@_); foreach my $key (qw(minFrameWidth maxFrameWidth)) { $me-> $key($profile{$key}); } return %profile; } # Properties sub minFrameWidth { my $me = shift; if (@_ > 0) { return if defined ($me-> {minFrameWidth}) && $me-> {minFrameWidth} == $_[0]; if (defined $_[0]) { $me-> {minFrameWidth} = $_[0] < 0 ? 0 : $_[0]; } else { undef $me-> {minFrameWidth}; } } else { return $me-> {minFrameWidth}; } } sub maxFrameWidth { my $me = shift; if (@_ > 0) { return if defined($me-> {maxFrameWidth}) && ($me-> {maxFrameWidth} == $_[0]); if (defined $_[0]) { $me-> {maxFrameWidth} = $_[0] < 0 ? 0 : $_[0]; } else { undef $me-> {maxFrameWidth}; } } else { return $me-> {maxFrameWidth}; } } package Prima::FrameSet::Slider; use strict; use vars qw(@ISA); @ISA = qw(Prima::Widget); # Initialization sub profile_default { return { %{$_[0]-> SUPER::profile_default}, vertical => 0, thickness => 4, growMode => gm::GrowHiX | gm::GrowLoX, frame1 => undef, frame2 => undef, sliderIndex => 0, }; } sub profile_check_in { my ($me, $p, $default) = @_; my $userPointer = exists $p-> {pointerType}; $me-> SUPER::profile_check_in($p, $default); if (exists $p-> {vertical} ? $p-> {vertical} : $default-> {vertical}) { $p-> {growMode} = gm::GrowHiY | gm::GrowLoY unless exists $p-> {growMode}; } } sub init { my $me = shift; my %profile = $me-> SUPER::init(@_); foreach my $prop (qw(thickness vertical frame1 frame2 sliderIndex)) { $me-> $prop($profile{$prop}); } $me-> adjust_sizes; return %profile; } # Event handlers sub on_paint { my ($me, $canvas) = @_; my @sz = $canvas-> size; unless ($me-> enabled) { $me-> color( $me-> disabledBackColor); $me-> bar( 0, 0, $me-> size); } else { $me-> rect3d( 0, 0, $sz[0]-1, $sz[1]-1, 1, $me-> {draggingMode} ? ( $me-> dark3DColor, $me-> light3DColor, ) : ( $me-> light3DColor, $me-> dark3DColor, ), $me-> backColor ); } } sub on_enable { $_[0]-> repaint; } sub on_disable { $_[0]-> repaint; } sub on_mousedown { my $me = shift; my ($btn, $mod, $x, $y) = @_; $me-> clear_event; if ($btn == mb::Left) { $me-> start_dragging; $me-> repaint; $me-> update_view; @{ $me}{qw(spotX spotY)} = ($x, $y); $me-> {traceRect} = [$me-> client_to_screen(0, 0), $me-> client_to_screen($me-> size)]; $me-> xorrect(@{$me-> {traceRect}}) unless $me-> owner-> opaqueResize; $me-> capture(1); } } sub on_mouseup { my $me = shift; my ($btn, $mod, $x, $y) = @_; $me-> clear_event; if ($btn == mb::Left) { $me-> stop_dragging; $me-> owner-> slider_moved( $me, $me-> get_delta($x, $y)); $me-> repaint; } } sub on_mousemove { my $me = shift; my ($mod, $x, $y) = @_; if ($me-> {draggingMode}) { $me-> clear_event; my $delta = $me-> get_delta($x, $y); my @sz = $me-> size; my ($fmin, $fmax); if ($delta > 0) { $fmin = $me-> frame2; $fmax = $me-> frame1; } else { $fmin = $me-> frame1; $fmax = $me-> frame2; } my ($fminWidth, $fmaxWidth); if ($me-> {vertical}) { $fminWidth = $fmin-> width; $fmaxWidth = $fmax-> width; } else { $fminWidth = $fmin-> height; $fmaxWidth = $fmax-> height; } $delta = ($fminWidth - $fmin-> {minFrameWidth}) * ($delta < 0 ? -1 : 1) if defined($fmin-> {minFrameWidth}) && (($fminWidth - abs($delta)) < $fmin-> {minFrameWidth}); $delta = ($fmax-> {maxFrameWidth} - $fmaxWidth) * ($delta < 0 ? -1 : 1) if defined($fmax-> {maxFrameWidth}) && (($fmaxWidth + abs($delta)) > $fmax-> {maxFrameWidth}); if ($me-> owner-> opaqueResize) { $me-> owner-> slider_moved($me, $delta); } else { my @oldrect = @{$me-> {traceRect}}; if ($me-> {vertical}) { $me-> {traceRect} = [$me-> client_to_screen($delta, 0), $me-> client_to_screen($sz[0] + $delta, $sz[1])]; } else { $me-> {traceRect} = [$me-> client_to_screen(0, $delta), $me-> client_to_screen($sz[0], $sz[1] + $delta)]; } my $different = 0; $different ||= $oldrect[$_] != $me-> {traceRect}-> [$_] foreach 0..3; if ($different) { # Don't redraw dragging rect if the old and the new rectangle are the same. $me-> xorrect(@oldrect); $me-> xorrect(@{$me-> {traceRect}}); } } } } sub on_keydown { my $me = shift; my ($code, $key, $mod) = @_; if (($key == kb::Esc) && $me-> {draggingMode}) { $me-> stop_dragging; $me-> repaint; } } # Helpers sub adjust_sizes { my $me = shift; my ($w, $h); my $owner = $me-> owner; if ($me-> {vertical}) { $h = $owner-> height; $w = $me-> {thickness}; } else { $h = $me-> {thickness}; $w = $owner-> width; } $me-> size($w, $h); } sub xorrect { my ( $self, @r) = @_; $r[2]--; $r[3]--; my $o = $::application; $o-> begin_paint; my $oo = $self-> clipOwner ? $self-> owner : $::application; $o-> clipRect( $oo-> client_to_screen( 0,0,$oo-> size)); $o-> set( fillPattern => fp::SimpleDots, color => cl::Set, backColor => cl::Clear, rop => rop::XorPut, ); $o-> bar( @r); $o-> end_paint; } sub get_delta { my $me = shift; my ($x, $y) = @_; return $me-> {vertical} ? $x - $me-> {spotX} : $y - $me-> {spotY}; } sub start_dragging { my $me = shift; $me-> {draggingMode} = 1; $me-> {oldFocus} = $::application-> get_focused_widget; $me-> focus; } sub stop_dragging { my $me = shift; $me-> xorrect(@{$me-> {traceRect}}) unless $me-> owner-> opaqueResize; $me-> {draggingMode} = 0; $me-> capture(0); $me-> {oldFocus}-> focus if defined $me-> {oldFocus}; } # Properties sub vertical { my $me = shift; if (@_ > 0) { return if exists($me-> {vertical}) && $me-> {vertical} == $_[0]; $me-> {vertical} = $_[0]; $me-> pointerType($_[0] ? cr::SizeWE : cr::SizeNS); $me-> adjust_sizes; $me-> repaint; } else { return $me-> {vertical}; } } sub thickness { my $me = shift; if (@_ > 0) { return if exists($me-> {thickness}) && $me-> {thickness} == $_[0]; $me-> {thickness} = $_[0]; $me-> adjust_sizes; $me-> repaint; } else { return $me-> {thickness}; } } sub frame1 { my $me = shift; if (@_ > 0) { return unless $_[0] && $_[0]-> isa('Prima::FrameSet::Frame'); $me-> {frame1} = $_[0]; } else { return $me-> {frame1}; } } sub frame2 { my $me = shift; if (@_ > 0) { return unless $_[0] && $_[0]-> isa('Prima::FrameSet::Frame'); $me-> {frame2} = $_[0]; } else { return $me-> {frame2}; } } sub sliderIndex { my $me = shift; if (@_ > 0) { $me-> {sliderIndex} = $_[0]; } else { return $me-> {sliderIndex}; } } package Prima::FrameSet; use strict; use vars qw(@ISA); @ISA = qw(Prima::Widget); # Initialization. sub profile_default { return { %{ $_[0]-> SUPER::profile_default}, arrangement => fra::Horizontal, # Vertical or horizontal insertion of frames. frameCount => 2, # Number of frames, no less than 2. flexible => 1, # Frame can be resized by user at any time at any place. sliders => [qw(1)], # Where resize sliders must be located. Array size depends on number of frames and must be (nframes+1) long. sliderWidth => 4, separatorWidth => 1, # Separator is an immovable slider. frameSizes => [qw(50% *)], # Sizes of frames in percents, pixel or '*' for automatic. growMode => gm::Client, origin => [0, 0], opaqueResize => 0, resizeMethod => frr::Simple, frameClass => 'Prima::FrameSet::Frame', frameProfile => {}, frameProfiles => [], sliderClass => 'Prima::FrameSet::Slider', sliderProfile => {}, sliderProfiles => [], }; } sub profile_check_in { my $me = shift; my ($profile, $default) = @_; $me-> SUPER::profile_check_in(@_); $profile-> {frameCount} = @{$profile-> {frameSizes}} unless exists $profile-> {frameCount}; $profile-> {frameCount} = 2 if $profile-> {frameCount} < 2; if (! exists($profile-> {sliders}) && ! exists($profile-> {flexible})) { $profile-> {sliders} = [(1) x ($profile-> {frameCount} - 1)]; } } sub init { my $me = shift; my %profile = $me-> SUPER::init(@_); $me-> {frameSizes} = $profile{frameSizes}; foreach my $prop (qw(frameCount arrangement sliderWidth separatorWidth flexible opaqueResize resizeMethod)) { $me-> $prop($profile{$prop}); } for (my $i = 0; $i < $profile{frameCount}; $i++) { my %xp = %{$profile{frameProfile}}; %xp = ( %xp, %{$profile{frameProfiles}-> [$i]}) if $profile{frameProfiles}-> [$i] && ref($profile{frameProfiles}-> [$i]) eq 'HASH'; my $frame = $me-> insert( $profile{frameClass} => name => "Frame$i", %xp, ); push @{$me-> {frames}}, $frame; } my $sn; for ($sn = 0; $sn < ($profile{frameCount} - 1); $sn++) { my $moveable = $profile{sliders}-> [$sn] ? 1 : 0; $moveable = $profile{flexible}; my %xp = %{$profile{sliderProfile}}; %xp = ( %xp, %{$profile{sliderProfiles}-> [$sn]}) if $profile{sliderProfiles}-> [$sn] && ref($profile{sliderProfiles}-> [$sn]) eq 'HASH'; my $slider = $me-> insert( $profile{sliderClass} => name => "Slider#$sn", $moveable ? ( thickness => $profile{sliderWidth}, enabled => 1, ) : ( thickness => 1, enabled => 0, ), # Horizontal arrangement of frames means we need a vertically oriented slider. vertical => $me-> {arrangement} == fra::Horizontal, frame1 => $me-> {frames}-> [$sn], frame2 => $me-> {frames}-> [$sn + 1], sliderIndex => $sn, %xp, ); push @{$me-> {sliders}}, $slider; } $me-> recalc_frames(initial => 1); $me-> reset; return %profile; } # Event handlers sub on_size { my $me = shift; if ($_[0] == 0 && $_[1] == 0) { # We get it when initial resize is performed. $me-> recalc_frames(initial => 1); } else { $me-> recalc_frames(resize => 1, sizes => \@_); } $me-> reset; } sub on_paint { my ($me, $canvas) = @_; $canvas-> fillPattern(fp::Interleave); $canvas-> bar(0, 0, $me-> size); } # Helpers sub recalc_frames { my $me = shift; my (%profile) = @_; return unless $me-> owner; my @sizes = @{$me-> {sizes} || []}; if ($profile{initial}) { my @frameSizes = @{$me-> {frameSizes}}; my $asterixCount = 0; my $percents = 0; my $pixels = 0; foreach my $fsz (@frameSizes) { if ($fsz eq '*') { $asterixCount++; next; } (my $nfsz = $fsz) =~ s/\%$//; $nfsz = 0 if $nfsz < 0; if ($fsz =~ /\%$/) { $percents += $nfsz; } else { $pixels += $nfsz; } } my $totalSize; # Total width or height depending on arrangement. if ($me-> {arrangement} == fra::Vertical) { $totalSize = $me-> height; } else { $totalSize = $me-> width; } my $size = $totalSize - $pixels; # Size left after discounting all pixel-based and sliders sizes. foreach my $slider (@{$me-> {sliders}}) { $size -= $slider-> thickness; } my $percentSize = ($size * $percents / 100.); my $autoSize = $asterixCount ? ($size - $percentSize) / $asterixCount : 0; # Size of an automaticly-sized frame. @sizes = (0) x $me-> {frameCount}; my $frac = 0; my $origSize; for (my $i = 0; $i < $me-> {frameCount}; $i++) { if (! defined($frameSizes[$i]) || ($frameSizes[$i] eq '*')) { $sizes[$i] = int($autoSize + .5); $frac += $autoSize - $sizes[$i]; } elsif ($frameSizes[$i] =~ /\%$/) { (my $nfsz = $frameSizes[$i]) =~ s/\%$//; $sizes[$i] = int( ($origSize = ($size * $nfsz) / 100.) + .5); $frac += $origSize - $sizes[$i]; } else { $sizes[$i] = int(($origSize = $frameSizes[$i]) + .5); $frac += $origSize - $sizes[$i]; } if (abs($frac) >= 1) { $sizes[$i] += int($frac); $frac = $frac - int($frac); } } $me-> {sizes} = [@sizes]; } elsif ($profile{resize}) { my $idx = $me-> {arrangement} == fra::Horizontal ? 0 : 1; my $old_size = @{$profile{sizes}}[$idx]; my $new_size = @{$profile{sizes}}[$idx + 2]; return if ($old_size == $new_size); if ($me-> {resizeMethod} == frr::Simple) { my $i; for ($i = 0; $i < ($me-> {frameCount} - 1); $i++) { $old_size -= $me-> {sliders}-> [$i]-> {thickness}; $new_size -= $me-> {sliders}-> [$i]-> {thickness}; } my @nsizes; my $newTotal = 0; my ($f, $ns); for ($i = 0; $i < ($me-> {frameCount} - 1); $i++) { $f = $me-> {frames}-> [$i]; $ns = int(($sizes[$i] * $new_size) / $old_size + .5); $ns = $f-> {minFrameWidth} if defined($f-> {minFrameWidth}) && ($ns < $f-> {minFrameWidth}); $ns = $f-> {maxFrameWidth} if defined($f-> {maxFrameWidth}) && ($ns > $f-> {maxFrameWidth}); $nsizes[$i] = $ns; $newTotal += $ns; } # Calculate for the last frame. $f = $me-> {frames}-> [$i]; $ns = $new_size - $newTotal; $ns = 0 if $ns < 0; $ns = $f-> {minFrameWidth} if defined($f-> {minFrameWidth}) && ($ns < $f-> {minFrameWidth}); $ns = $f-> {maxFrameWidth} if defined($f-> {maxFrameWidth}) && ($ns > $f-> {maxFrameWidth}); $nsizes[$i] = $ns; $me-> {sizes} = \@nsizes; } } return (wantarray ? @sizes : \@sizes); } sub reset { my $me = shift; return unless $me-> owner; my $origin = [0, 0]; my $end = [$me-> {arrangement} == fra::Vertical ? ($me-> width, 1) : (1, $me-> height)]; my $idx = $me-> {arrangement} == fra::Vertical ? 1 : 0; # What element of origin/size array we change. my @sliders = @{$me-> {sliders}}; for (my $i = 0; $i < $me-> {frameCount}; $i++) { $end-> [$idx] = $origin-> [$idx] + $me-> {sizes}-> [$i]; $me-> {frames}-> [$i]-> rect(@$origin, @$end); $origin-> [$idx] += $me-> {sizes}-> [$i]; if ($i < @{$me-> {sliders}}) { $sliders[$i]-> origin(@$origin); $origin-> [$idx] += $sliders[$i]-> thickness; } } } sub slider_moved { my $me = shift; my ($slider, $delta) = @_; return unless $delta; my $si = $slider-> sliderIndex; if ($me-> {resizeMethod} == frr::Simple) { my $frame1 = $slider-> frame1; my $frame2 = $slider-> frame2; my ($w1, $w2); if ($me-> {arrangement} == fra::Horizontal) { $w1 = $frame1-> width; $w2 = $frame2-> width; } else { $w1 = $frame1-> height; $w2 = $frame2-> height; } # Adjust delta in a way it doesn't clash with frame's min/max sizes. my $nw1 = $w1 + $delta; if (defined $frame1-> minFrameWidth && $nw1 < $frame1-> minFrameWidth) { $nw1 = $frame1-> minFrameWidth; } elsif (defined $frame1-> maxFrameWidth && $nw1 > $frame1-> maxFrameWidth) { $nw1 = $frame1-> maxFrameWidth; } $delta = $nw1 - $w1; my $nw2 = $w2 - $delta; if (defined $frame2-> minFrameWidth && $nw2 < $frame2-> minFrameWidth) { $nw2 = $frame2-> minFrameWidth; } elsif (defined $frame2-> maxFrameWidth && $nw2 > $frame2-> maxFrameWidth) { $nw2 = $frame2-> maxFrameWidth; } $delta = $w2 - $nw2; $nw1 = $w1 + $delta; my @rect; if ($me-> {arrangement} == fra::Horizontal) { $frame1-> width($nw1); @rect = $slider-> rect; $rect[0] += $delta; $rect[2] += $delta; $slider-> rect(@rect); @rect = $frame2-> rect; $rect[0] += $delta; $frame2-> rect(@rect); } else { $frame1-> height($nw1); @rect = $slider-> rect; $rect[1] += $delta; $rect[3] += $delta; $slider-> rect(@rect); @rect = $frame2-> rect; $rect[1] += $delta; $frame2-> rect(@rect); } $me-> {sizes}-> [$si] = $nw1; $me-> {sizes}-> [$si + 1] = $nw2; } } # Properties sub arrangement { my $me = shift; if (@_ > 0) { my $haveIt = exists($me-> {arrangement}); return if $haveIt && ($me-> {arrangement} == $_[0]); $me-> {arrangement} = $_[0]; if ($me-> {sliders}) { for (my $i = 0; $i < ($me-> {frameCount} - 1); $i++) { $me-> {sliders}-> [$i]-> vertical($_[0] == fra::Horizontal); } } if ($haveIt) { $me-> recalc_frames; $me-> reset; } } else { return $me-> {arrangement}; } } sub frameCount { # XXX frameCount property only sets number of frames. Needed adjustments # are too complicated and various to be implemented right here. my $me = shift; if (@_ > 0) { my $haveIt = exists $me-> {frameCount}; return if $haveIt && $me-> {frameCount} == $_[0]; my $oldval = ($haveIt ? $me-> {frameCount} : undef); $me-> {frameCount} = $_[0]; } else { return $me-> {frameCount}; } } sub sliderWidth { my $me = shift; if (@_ > 0) { my $haveIt = exists($me-> {sliderWidth}); return if ($haveIt && ($me-> {sliderWidth} == $_[0])) || ($_[0] < 0); $me-> {sliderWidth} = $_[0]; if ($haveIt) { for (my $i = 0; $i < ($me-> {frameCount} - 1); $i++) { if ($me-> {sliders}-> [$i]-> enabled) { $me-> {sliders}-> [$i]-> thickness($_[0]); } } $me-> recalc_frames; $me-> reset; } } else { return $me-> {sliderWidth}; } } sub separatorWidth { my $me = shift; if (@_ > 0) { my $haveIt = exists($me-> {separatorWidth}); return if ($haveIt && ($me-> {separatorWidth} == $_[0])) || ($_[0] < 0); $me-> {separatorWidth} = $_[0]; if ($haveIt) { for (my $i = 0; $i < ($me-> {frameCount} - 1); $i++) { if ($me-> {sliders}-> [$i]-> enabled) { $me-> {sliders}-> [$i]-> thickness($_[0]); } } $me-> recalc_frames; $me-> reset; } } else { return $me-> {separatorWidth}; } } sub flexible { my $me = shift; if (@_ > 0) { my $haveIt = exists($me-> {flexible}); return if $haveIt && ! ($me-> {flexible} xor $_[0]); $me-> {flexible} = $_[0] ? 1 : 0; if ($haveIt) { for (my $i = 0; $i < ($me-> {frameCount} - 1); $i++) { $me-> {sliders}-> [$i]-> thickness($me-> {flexible} ? $me-> {sliderWidth} : $me-> {separatorWidth}); $me-> {sliders}-> [$i]-> enabled( $me-> {flexible}); } $me-> recalc_frames; $me-> reset; } } else { return $me-> {flexible}; } } sub frameSizes { return [@{$_[0]-> {frameSizes}}] unless $#_; my $me = shift; my @fs = ( $_[0] && ref($_[0]) eq 'ARRAY' && 1 == scalar @_) ? @{$_[0]} : @_; $me-> {frameSizes} = \@fs; $me-> recalc_frames( initial => 1); $me-> reset; } sub opaqueResize { my $me = shift; if (@_ > 0) { return if exists($me-> {opaqueResize}) && $me-> {opaqueResize} == $_[0]; $me-> {opaqueResize} = $_[0]; } else { return $me-> {opaqueResize}; } } sub resizeMethod { my $me = shift; if (@_ > 0) { return if exists($me-> {resizeMethod}) && $me-> {resizeMethod} == $_[0]; $me-> {resizeMethod} = $_[0]; } else { return $me-> {resizeMethod}; } } # User interface sub firstFrame { my $me = shift; return $me-> {frames}-> [0]; } sub lastFrame { my $me = shift; return $me-> {frames}-> [-1]; } sub frames { my $me = shift; return wantarray ? @{$me-> {frames}} : $me-> {frames}; } sub frame { my ( $self, $frameIndex) = @_; return $self-> {frames}-> [$frameIndex]; } sub insert_to_frame { my $me = shift; my $frameIdx = shift; $frameIdx = $me-> {frameCount} - 1 if $frameIdx > ($me-> {frameCount} - 1); $me-> lock; my @ctrls = $me-> {frames}-> [$frameIdx]-> insert(@_); $me-> unlock; return wantarray ? @ctrls : $ctrls[0]; } 1; __DATA__ =pod =head1 NAME Prima::FrameSet - standard frameset widget =head1 SYNOPSIS use Prima::FrameSet; my $frame = Prima::FrameSet->create( frameSizes => [qw(211 20% 123 10% * 45% *)], opaqueResize => 0, frameProfiles => [ 0,0, { minFrameWidth => 123, maxFrameWidth => 123 }], ); $frame->insert_to_frame( 0, Button => text => '~Ok', ); =head1 DESCRIPTION Provides standard means of framesets manipulations. It includes sharing of common workspace among several widget groups; redistribution of space, occupied by frames; isolation of different frames from each other. This module defines C and C packages for constants, used by L and L properties, respectively. Two additional auxiliary packages are defined within this module: L and L. =head1 AUTHOR Vadim Belman, Evoland@lflat.orgE =head1 SEE ALSO L, L, F. =cut