Enhance inspector window with zooming
- support zoom gesture and Alt scroll
This commit is contained in:
parent
a064a225e0
commit
134aa407c0
@ -82,7 +82,7 @@ InspectorBuffer::~InspectorBuffer() {
|
|||||||
// return deg;
|
// return deg;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
Inspector::Inspector () : currImage(nullptr), scaled(false), active(false), pinned(false)
|
Inspector::Inspector () : currImage(nullptr), scaled(false), scale(1.0), zoomScale(1.0), zoomScaleBegin(1.0), active(false), pinned(false), dirty(false)
|
||||||
{
|
{
|
||||||
set_name("Inspector");
|
set_name("Inspector");
|
||||||
window.set_visible(false);
|
window.set_visible(false);
|
||||||
@ -93,6 +93,9 @@ Inspector::Inspector () : currImage(nullptr), scaled(false), active(false), pinn
|
|||||||
window.signal_key_press_event().connect(sigc::mem_fun(*this, &Inspector::on_key_press));
|
window.signal_key_press_event().connect(sigc::mem_fun(*this, &Inspector::on_key_press));
|
||||||
|
|
||||||
add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
|
||||||
|
gestureZoom = Gtk::GestureZoom::create(*this);
|
||||||
|
gestureZoom->signal_begin().connect(sigc::mem_fun(*this, &Inspector::on_zoom_begin));
|
||||||
|
gestureZoom->signal_scale_changed().connect(sigc::mem_fun(*this, &Inspector::on_zoom_scale_changed));
|
||||||
|
|
||||||
window.add(*this);
|
window.add(*this);
|
||||||
window.show_all();
|
window.show_all();
|
||||||
@ -119,6 +122,7 @@ bool Inspector::on_key_release(GdkEventKey *event)
|
|||||||
switch (event->keyval) {
|
switch (event->keyval) {
|
||||||
case GDK_KEY_f:
|
case GDK_KEY_f:
|
||||||
case GDK_KEY_F:
|
case GDK_KEY_F:
|
||||||
|
zoomScale = 1.0;
|
||||||
window.set_visible(false);
|
window.set_visible(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -131,14 +135,19 @@ bool Inspector::on_key_press(GdkEventKey *event)
|
|||||||
switch (event->keyval) {
|
switch (event->keyval) {
|
||||||
case GDK_KEY_z:
|
case GDK_KEY_z:
|
||||||
case GDK_KEY_F:
|
case GDK_KEY_F:
|
||||||
|
if (pinned || scaled)
|
||||||
|
zoomScale = 1.0; // reset if not key hold
|
||||||
scaled = false;
|
scaled = false;
|
||||||
queue_draw();
|
queue_draw();
|
||||||
return true;
|
return true;
|
||||||
case GDK_KEY_f:
|
case GDK_KEY_f:
|
||||||
|
if (pinned || !scaled)
|
||||||
|
zoomScale = 1.0; // reset if not key hold
|
||||||
scaled = true;
|
scaled = true;
|
||||||
queue_draw();
|
queue_draw();
|
||||||
return true;
|
return true;
|
||||||
case GDK_KEY_Escape:
|
case GDK_KEY_Escape:
|
||||||
|
zoomScale = 1.0;
|
||||||
window.set_visible(false);
|
window.set_visible(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -162,12 +171,10 @@ bool Inspector::on_scroll_event(GdkEventScroll *event)
|
|||||||
if (!currImage)
|
if (!currImage)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
rtengine::Coord margin; // limit for scroll area
|
bool alt = event->state & GDK_MOD1_MASK;
|
||||||
int deviceScale = get_scale_factor();
|
int deviceScale = get_scale_factor();
|
||||||
int imW = currImage->imgBuffer.getWidth();
|
int imW = currImage->imgBuffer.getWidth();
|
||||||
int imH = currImage->imgBuffer.getHeight();
|
int imH = currImage->imgBuffer.getHeight();
|
||||||
margin.x = (window.get_width() * deviceScale) / 2;
|
|
||||||
margin.y = (window.get_height() * deviceScale) / 2;
|
|
||||||
|
|
||||||
#ifdef GDK_WINDOWING_QUARTZ
|
#ifdef GDK_WINDOWING_QUARTZ
|
||||||
// event reports speed of scroll wheel
|
// event reports speed of scroll wheel
|
||||||
@ -206,15 +213,66 @@ bool Inspector::on_scroll_event(GdkEventScroll *event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (alt) {
|
||||||
|
// zoom
|
||||||
|
beginZoom(event->x, event->y);
|
||||||
|
if (std::fabs(delta_y) > std::fabs(delta_x))
|
||||||
|
on_zoom_scale_changed(1.0 + (double)delta_y / imH / deviceScale);
|
||||||
|
else
|
||||||
|
on_zoom_scale_changed(1.0 + (double)delta_x / imW / deviceScale);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll
|
||||||
|
rtengine::Coord margin; // limit for scroll area
|
||||||
|
margin.x = rtengine::min<int>(window.get_width() * deviceScale / scale, imW) / 2;
|
||||||
|
margin.y = rtengine::min<int>(window.get_height() * deviceScale / scale, imH) / 2;
|
||||||
center.set(rtengine::LIM<int>(center.x + delta_x, margin.x, imW - margin.x),
|
center.set(rtengine::LIM<int>(center.x + delta_x, margin.x, imW - margin.x),
|
||||||
rtengine::LIM<int>(center.y + delta_y, margin.y, imH - margin.y));
|
rtengine::LIM<int>(center.y + delta_y, margin.y, imH - margin.y));
|
||||||
queue_draw();
|
|
||||||
|
if (!dirty) {
|
||||||
|
dirty = true;
|
||||||
|
queue_draw();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Inspector::beginZoom(double x, double y)
|
||||||
|
{
|
||||||
|
int deviceScale = get_scale_factor();
|
||||||
|
dcenterBegin.x = (x - window.get_width()/2) / scale * deviceScale;
|
||||||
|
dcenterBegin.y = (y - window.get_height()/2) / scale * deviceScale;
|
||||||
|
centerBegin = center;
|
||||||
|
zoomScaleBegin = zoomScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inspector::on_zoom_begin(GdkEventSequence *s)
|
||||||
|
{
|
||||||
|
double x, y;
|
||||||
|
if (gestureZoom->get_point(s, x, y))
|
||||||
|
beginZoom(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inspector::on_zoom_scale_changed(double zscale)
|
||||||
|
{
|
||||||
|
if (!currImage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
zoomScale = rtengine::LIM<double>(zoomScaleBegin * zscale, 0.01, 16.0);
|
||||||
|
double dcenterRatio = 1.0 - zoomScaleBegin / zoomScale;
|
||||||
|
center.x = centerBegin.x + dcenterBegin.x * dcenterRatio;
|
||||||
|
center.y = centerBegin.y + dcenterBegin.y * dcenterRatio;
|
||||||
|
|
||||||
|
if (!dirty) {
|
||||||
|
dirty = true;
|
||||||
|
queue_draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
||||||
{
|
{
|
||||||
|
dirty = false;
|
||||||
|
|
||||||
Glib::RefPtr<Gdk::Window> win = get_window();
|
Glib::RefPtr<Gdk::Window> win = get_window();
|
||||||
|
|
||||||
@ -240,15 +298,21 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
|||||||
int deviceScale = get_scale_factor();
|
int deviceScale = get_scale_factor();
|
||||||
availableSize.x = win->get_width() * deviceScale;
|
availableSize.x = win->get_width() * deviceScale;
|
||||||
availableSize.y = win->get_height() * deviceScale;
|
availableSize.y = win->get_height() * deviceScale;
|
||||||
int imW = currImage->imgBuffer.getWidth();
|
int imW = rtengine::max<int>(currImage->imgBuffer.getWidth(), 1);
|
||||||
int imH = currImage->imgBuffer.getHeight();
|
int imH = rtengine::max<int>(currImage->imgBuffer.getHeight(), 1);
|
||||||
double scale = 1.0;
|
scale = rtengine::min<double>((double)availableSize.x / imW, (double)availableSize.y / imH);
|
||||||
if (scaled) {
|
if (scaled) {
|
||||||
// reduce size of image to fit into window
|
// reduce size of image to fit into window, no further zoom down
|
||||||
scale = rtengine::min<double>(1.0, rtengine::min<double>((double)availableSize.x/imW, (double)availableSize.y/imH));
|
zoomScale = rtengine::max<double>(zoomScale, 1.0);
|
||||||
availableSize.x /= scale;
|
scale *= zoomScale;
|
||||||
availableSize.y /= scale;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// limit zoom to fill at least complete window or 1:1
|
||||||
|
zoomScale = rtengine::max<double>(zoomScale, rtengine::min<double>(1.0, scale));
|
||||||
|
scale = zoomScale;
|
||||||
|
}
|
||||||
|
availableSize.x /= scale;
|
||||||
|
availableSize.y /= scale;
|
||||||
|
|
||||||
if (imW < availableSize.x) {
|
if (imW < availableSize.x) {
|
||||||
// center the image in the available space along X
|
// center the image in the available space along X
|
||||||
@ -275,7 +339,6 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
|||||||
topLeft.y -= availableSize.y;
|
topLeft.y -= availableSize.y;
|
||||||
topLeft.y = rtengine::max<int>(topLeft.y, 0);
|
topLeft.y = rtengine::max<int>(topLeft.y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//printf("center: %d, %d (img: %d, %d) (availableSize: %d, %d) (topLeft: %d, %d)\n", center.x, center.y, imW, imH, availableSize.x, availableSize.y, topLeft.x, topLeft.y);
|
//printf("center: %d, %d (img: %d, %d) (availableSize: %d, %d) (topLeft: %d, %d)\n", center.x, center.y, imW, imH, availableSize.x, availableSize.y, topLeft.x, topLeft.y);
|
||||||
|
|
||||||
// define the destination area
|
// define the destination area
|
||||||
@ -302,7 +365,7 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
|||||||
cr->fill ();
|
cr->fill ();
|
||||||
//*/
|
//*/
|
||||||
|
|
||||||
bool scaledImage = scaled && (imW > win->get_width() || imH > win->get_height());
|
bool scaledImage = scale != 1.0;
|
||||||
if (deviceScale == 1 && !scaledImage) {
|
if (deviceScale == 1 && !scaledImage) {
|
||||||
// standard drawing
|
// standard drawing
|
||||||
currImage->imgBuffer.copySurface(win);
|
currImage->imgBuffer.copySurface(win);
|
||||||
@ -314,13 +377,15 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
|||||||
cairo_surface_set_device_scale(cr->get_target()->cobj(), scale, scale);
|
cairo_surface_set_device_scale(cr->get_target()->cobj(), scale, scale);
|
||||||
scaledImage = false;
|
scaledImage = false;
|
||||||
}
|
}
|
||||||
Glib::RefPtr<Gdk::Pixbuf> crop = Gdk::Pixbuf::create(currImage->imgBuffer.getSurface(), topLeft.x, topLeft.y, rtengine::min<int>(imW, availableSize.x), rtengine::min<int>(imH, availableSize.y));
|
int viewW = rtengine::min<int>(imW, availableSize.x);
|
||||||
|
int viewH = rtengine::min<int>(imH, availableSize.y);
|
||||||
|
Glib::RefPtr<Gdk::Pixbuf> crop = Gdk::Pixbuf::create(currImage->imgBuffer.getSurface(), topLeft.x, topLeft.y, viewW, viewH);
|
||||||
if (!scaledImage) {
|
if (!scaledImage) {
|
||||||
Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x, dest.y);
|
Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x, dest.y);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// assume that the device does not support scaling (Linux)
|
// scale crop as the device does not seem to support it (Linux)
|
||||||
crop = crop->scale_simple(imW*scale, imH*scale, Gdk::INTERP_BILINEAR);
|
crop = crop->scale_simple(viewW*scale, viewH*scale, Gdk::INTERP_BILINEAR);
|
||||||
Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x*scale, dest.y*scale);
|
Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x*scale, dest.y*scale);
|
||||||
}
|
}
|
||||||
cr->paint();
|
cr->paint();
|
||||||
|
@ -47,9 +47,13 @@ private:
|
|||||||
rtengine::Coord center;
|
rtengine::Coord center;
|
||||||
std::vector<InspectorBuffer*> images;
|
std::vector<InspectorBuffer*> images;
|
||||||
InspectorBuffer* currImage;
|
InspectorBuffer* currImage;
|
||||||
bool scaled;
|
bool scaled; // fit image into window
|
||||||
|
double scale; // current scale
|
||||||
|
double zoomScale, zoomScaleBegin; // scale during zoom
|
||||||
|
rtengine::Coord centerBegin, dcenterBegin; // center during zoom
|
||||||
bool active;
|
bool active;
|
||||||
bool pinned;
|
bool pinned;
|
||||||
|
bool dirty;
|
||||||
|
|
||||||
sigc::connection delayconn;
|
sigc::connection delayconn;
|
||||||
Glib::ustring next_image_path;
|
Glib::ustring next_image_path;
|
||||||
@ -61,6 +65,11 @@ private:
|
|||||||
bool on_button_press_event(GdkEventButton *event) override;
|
bool on_button_press_event(GdkEventButton *event) override;
|
||||||
bool on_scroll_event(GdkEventScroll *event) override;
|
bool on_scroll_event(GdkEventScroll *event) override;
|
||||||
|
|
||||||
|
Glib::RefPtr<Gtk::GestureZoom> gestureZoom;
|
||||||
|
void beginZoom(double x, double y);
|
||||||
|
void on_zoom_begin(GdkEventSequence *);
|
||||||
|
void on_zoom_scale_changed(double zscale);
|
||||||
|
|
||||||
bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) override;
|
bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) override;
|
||||||
void deleteBuffers();
|
void deleteBuffers();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user