use List::Util qw(min max) ;
#----------------------------------------------------------------------------------------------
register_action_handlers
(
'Align objects'=>
{
SHORTCUTS => '0A0-a',
'Align left' => ['000-l', \&align, 'left'],
'Align center' => ['000-c', \&align, 'center'],
'Align right' => ['000-r', \&align, 'right'],
'Align top' => ['000-t', \&align, 'top'],
'Align middle' => ['000-m', \&align, 'middle'],
'Align bottom' => ['000-b', \&align, 'bottom'],
# spread vertically # spread horizontally # adjacent vert # adjacent hor # stack
},
) ;
#----------------------------------------------------------------------------------------------
sub align
{
my ($self, $alignment) = @_ ;
$self->create_undo_snapshot() ;
my @elements_to_move = grep {my @connectors = $_->get_connector_points() ; @connectors == 0 } $self->get_selected_elements(1) ;
for ($alignment)
{
$_ eq 'left' and do
{
my $left = min( map{$_->{X}} @elements_to_move) ;
for my $element (@elements_to_move)
{
$self->move_elements($left - $element->{X},0, $element) ;
}
last ;
} ;
$_ eq 'center' and do
{
my $left = min( map{$_->{X}} @elements_to_move) ;
my $right = max
(
map
{
my ($w, $h) = $_->get_size() ;
$_->{X} + $w ;
} @elements_to_move
) ;
my $center = int(($left + $right) / 2) ;
# find element which center is closes to geometric center
my $closest_element = undef ;
my $closest_element_distance = 1_000_000 ;
my $closest_center ;
for my $element (@elements_to_move)
{
my ($w, $h) = $element->get_size() ;
my $element_center = $element->{X} + int($w / 2) ;
my $center_to_center_distance = abs($center - $element_center) ;
if($center_to_center_distance <$closest_element_distance)
{
$closest_element = $element ;
$closest_element_distance = $center_to_center_distance;
$closest_center = $element_center ;
}
}
for my $element (@elements_to_move)
{
next if $element == $closest_element ;
my ($w, $h) = $element->get_size() ;
my $element_center = $element->{X} + int($w / 2) ;
$self->move_elements($closest_center - $element_center, 0, $element) ;
}
last ;
} ;
$_ eq 'right' and do
{
my $right = max
(
map
{
my ($w, $h) = $_->get_size() ;
$_->{X} + $w ;
} @elements_to_move
) ;
for my $element (@elements_to_move)
{
my ($w, $h) = $element->get_size() ;
$self->move_elements($right - ($element->{X} + $w), 0, $element) ;
}
last ;
} ;
$_ eq 'top' and do
{
my $top = min( map{$_->{Y}} @elements_to_move) ;
for my$element (@elements_to_move)
{
$self->move_elements(0, $top - $element->{Y}, $element) ;
}
last ;
} ;
$_ eq 'middle' and do
{
my $top = min( map{$_->{Y}} @elements_to_move) ;
my $bottom = max
(
map
{
my ($w, $h) = $_->get_size() ;
$_->{Y} + $h ;
} @elements_to_move
) ;
my $center = int(($top + $bottom) / 2) ;
# find element which center is closes to geometric center
my $closest_element = undef ;
my $closest_element_distance = 1_000_000 ;
my $closest_center ;
for my $element (@elements_to_move)
{
my ($w, $h) = $element->get_size() ;
my $element_center = $element->{Y} + int($h / 2) ;
my $center_to_center_distance = abs($center - $element_center) ;
if($center_to_center_distance <$closest_element_distance)
{
$closest_element = $element ;
$closest_element_distance = $center_to_center_distance;
$closest_center = $element_center ;
}
}
for my $element (@elements_to_move)
{
next if $element == $closest_element ;
my ($w, $h) = $element->get_size() ;
my $element_center = $element->{Y} + int($h / 2) ;
$self->move_elements(0, $closest_center - $element_center, $element) ;
}
last ;
} ;
$_ eq 'bottom' and do
{
my $bottom = max
(
map
{
my ($w, $h) = $_->get_size() ;
$_->{Y} + $h ;
} @elements_to_move
) ;
for my $element (@elements_to_move)
{
my ($w, $h) = $element->get_size() ;
$self->move_elements(0, $bottom - ($element->{Y} + $h), $element) ;
}
last ;
} ;
}
$self->update_display() ;
}