Support moving objects using keyboard arrows in the 2d plater view. (#4104)

* * Add nudge_instance to 2D.pm to move the selected instance.
* Add menu items ->, -<, ^, v keyboards shortcuts.

* Add Set nudge value as a config option in Preferences.

* Add Move instance submenu to Plater menu.

* Add Move instance icon.

* Remove move instance menu item and use EVT_KEY_DOWN instead in 2D.pm.

* Fix selecting objects between 3D and 2D platers.

* Fix Ubuntu keyboard focus error in 2D plater.

* Correct the keycodes in 2D.pm keyboard event.

* Fix Windows keyboard focus error.

* Remove Todo line.

* Adding a minimum value for 2d plater nudege variable and improving keyboard focus in 2D plater.
This commit is contained in:
Ahmed Samir 2018-01-06 04:16:04 +02:00 committed by Joseph Lenox
parent b6209c4ee3
commit 1190a74f4e
4 changed files with 101 additions and 7 deletions

View File

@ -91,6 +91,7 @@ our $Settings = {
color_toolpaths_by => 'role',
tabbed_preset_editors => 1,
show_host => 0,
nudge_val => 1
},
};

View File

@ -2727,7 +2727,10 @@ sub selection_changed {
my ($obj_idx, $object) = $self->selected_object;
my $have_sel = defined $obj_idx;
# Remove selection in 2d Plater.
$self->{canvas}->{selected_instance} = undef;
if (my $menu = $self->GetFrame->{plater_select_menu}) {
$_->Check(0) for $menu->GetMenuItems;
if ($have_sel) {
@ -2796,10 +2799,13 @@ sub selection_changed {
sub select_object {
my ($self, $obj_idx) = @_;
$_->selected(0) for @{ $self->{objects} };
$_->selected_instance(-1) for @{ $self->{objects} };
if (defined $obj_idx) {
$self->{objects}->[$obj_idx]->selected(1);
$self->{objects}->[$obj_idx]->selected_instance(0);
}
$self->selection_changed(1);
}
@ -3053,6 +3059,7 @@ has 'thumbnail' => (is => 'rw'); # ExPolygon::Collection in scaled m
has 'transformed_thumbnail' => (is => 'rw');
has 'instance_thumbnails' => (is => 'ro', default => sub { [] }); # array of ExPolygon::Collection objects, each one representing the actual placed thumbnail of each instance in pixel units
has 'selected' => (is => 'rw', default => sub { 0 });
has 'selected_instance' => (is => 'rw', default => sub { -1 });
sub make_thumbnail {
my ($self, $model, $obj_idx) = @_;

View File

@ -10,7 +10,7 @@ use List::Util qw(min max first);
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl);
use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE);
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_KEY_DOWN EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE);
use base 'Wx::Panel';
use constant CANVAS_TEXT => join('-', +(localtime)[3,4]) eq '13-8'
@ -34,7 +34,8 @@ sub new {
$self->{on_instances_moved} = sub {};
$self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID);
$self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID);
$self->{instance_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID);
$self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,166,128), wxSOLID);
$self->{dragged_brush} = Wx::Brush->new(Wx::Colour->new(128,128,255), wxSOLID);
$self->{transparent_brush} = Wx::Brush->new(Wx::Colour->new(0,0,0), wxTRANSPARENT);
$self->{grid_pen} = Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID);
@ -43,7 +44,9 @@ sub new {
$self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, wxSOLID);
$self->{user_drawn_background} = $^O ne 'darwin';
$self->{selected_instance} = undef;
EVT_PAINT($self, \&repaint);
EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background};
EVT_MOUSE_EVENTS($self, \&mouse_event);
@ -51,6 +54,22 @@ sub new {
$self->update_bed_size;
$self->Refresh;
});
EVT_KEY_DOWN($self, sub {
my ($s, $event) = @_;
my $key = $event->GetKeyCode;
if ($key == 65 || $key == 314) {
$self->nudge_instance('left');
} elsif ($key == 87 || $key == 315) {
$self->nudge_instance('up');
} elsif ($key == 68 || $key == 316) {
$self->nudge_instance('right');
} elsif ($key == 83 || $key == 317) {
$self->nudge_instance('down');
} else {
$event->Skip;
}
});
return $self;
}
@ -77,7 +96,10 @@ sub on_instances_moved {
sub repaint {
my ($self, $event) = @_;
# Focus is needed in order to catch keyboard events.
$self->SetFocus;
my $dc = Wx::AutoBufferedPaintDC->new($self);
my $size = $self->GetSize;
my @size = ($size->GetWidth, $size->GetHeight);
@ -149,6 +171,8 @@ sub repaint {
if (defined $self->{drag_object} && $self->{drag_object}[0] == $obj_idx && $self->{drag_object}[1] == $instance_idx) {
$dc->SetBrush($self->{dragged_brush});
} elsif ($object->selected && $object->selected_instance == $instance_idx) {
$dc->SetBrush($self->{instance_brush});
} elsif ($object->selected) {
$dc->SetBrush($self->{selected_brush});
} else {
@ -201,7 +225,11 @@ sub mouse_event {
my $pos = $event->GetPosition;
my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]]
if ($event->ButtonDown) {
# On Linux, Focus is needed in order to move selected instance using keyboard arrows.
$self->SetFocus;
$self->{on_select_object}->(undef);
$self->{selected_instance} = undef;
# traverse objects and instances in reverse order, so that if they're overlapping
# we get the one that gets drawn last, thus on top (as user expects that to move)
OBJECTS: for my $obj_idx (reverse 0 .. $#{$self->{objects}}) {
@ -220,6 +248,8 @@ sub mouse_event {
$point->y - $instance_origin->[Y], #-
];
$self->{drag_object} = [ $obj_idx, $instance_idx ];
$self->{objects}->[$obj_idx]->selected_instance($instance_idx);
$self->{selected_instance} = $self->{drag_object};
} elsif ($event->RightDown) {
$self->{on_right_click}->($pos);
}
@ -236,7 +266,7 @@ sub mouse_event {
$self->{drag_object} = undef;
$self->SetCursor(wxSTANDARD_CURSOR);
} elsif ($event->LeftDClick) {
$self->{on_double_click}->();
$self->{on_double_click}->();
} elsif ($event->Dragging) {
return if !$self->{drag_start_pos}; # concurrency problems
my ($obj_idx, $instance_idx) = @{ $self->{drag_object} };
@ -257,6 +287,55 @@ sub mouse_event {
}
}
sub nudge_instance{
my ($self, $direction) = @_;
# Get the selected instance of an object.
if (!defined $self->{selected_instance}) {
# Check if an object is selected.
for my $obj_idx (0 .. $#{$self->{objects}}) {
if ($self->{objects}->[$obj_idx]->selected) {
if ($self->{objects}->[$obj_idx]->selected_instance != -1) {
$self->{selected_instance} = [$obj_idx, $self->{objects}->[$obj_idx]->selected_instance];
}
}
}
}
return if not defined ($self->{selected_instance});
my ($obj_idx, $instance_idx) = @{ $self->{selected_instance} };
my $object = $self->{model}->objects->[$obj_idx];
my $instance = $object->instances->[$instance_idx];
# Get the nudge values.
my $x_nudge = 0;
my $y_nudge = 0;
$self->{nudge_value} = ($Slic3r::GUI::Settings->{_}{nudge_val} < 0.1 ? 0.1 : $Slic3r::GUI::Settings->{_}{nudge_val}) / &Slic3r::SCALING_FACTOR;
if ($direction eq 'right'){
$x_nudge = $self->{nudge_value};
} elsif ($direction eq 'left'){
$x_nudge = -1 * $self->{nudge_value};
} elsif ($direction eq 'up'){
$y_nudge = $self->{nudge_value};
} elsif ($direction eq 'down'){
$y_nudge = -$self->{nudge_value};
}
my $point = Slic3r::Pointf->new($x_nudge, $y_nudge);
my $instance_origin = [ map scale($_), @{$instance->offset} ];
$point = [ map scale($_), @{$point} ];
$instance->set_offset(
Slic3r::Pointf->new(
unscale( $instance_origin->[X] + $x_nudge),
unscale( $instance_origin->[Y] + $y_nudge),
));
$object->update_bounding_box;
$self->Refresh;
$self->{on_instances_moved}->();
}
sub update_bed_size {
my $self = shift;

View File

@ -85,6 +85,13 @@ sub new {
tooltip => 'Shows/Hides the Controller Tab. Requires a restart of Slic3r.',
default => $Slic3r::GUI::Settings->{_}{show_host},
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'nudge_val',
type => 's',
label => '2D plater nudge value',
tooltip => 'In 2D plater, Move objects using keyboard by nudge value of',
default => $Slic3r::GUI::Settings->{_}{nudge_val},
));
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
$sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);