From c4e21438a1da2e880f6e041669b993225a3f7e2f Mon Sep 17 00:00:00 2001 From: rfranke Date: Fri, 3 Jan 2020 10:37:22 +0100 Subject: [PATCH 1/7] Open inspector fullscreen and exploit monitor resolution, see #1474, #5591 - filecatalog.cc: add shortcuts 'f' for image on full screen 'Shift' 'f' for 100% crop on full screen (synchronization with mouse move as before with dual monitors) - filepanel.cc: don't create inspector tab - inspector.cc, inspector.h: create separate window for inspector scale image if requested exploit device scaling for full monitor resolution under macOS --- rtgui/filecatalog.cc | 10 +++++++ rtgui/filepanel.cc | 8 +++--- rtgui/inspector.cc | 68 ++++++++++++++++++++++++++++++++++++++------ rtgui/inspector.h | 10 ++++++- 4 files changed, 83 insertions(+), 13 deletions(-) diff --git a/rtgui/filecatalog.cc b/rtgui/filecatalog.cc index 109a1bb57..a283398ee 100644 --- a/rtgui/filecatalog.cc +++ b/rtgui/filecatalog.cc @@ -42,6 +42,7 @@ #include "pathutils.h" #include "thumbnail.h" #include "toolbar.h" +#include "inspector.h" using namespace std; @@ -2503,6 +2504,15 @@ bool FileCatalog::handleShortcutKey (GdkEventKey* event) } } + if (!ctrl && !alt) { + switch (event->keyval) { + case GDK_KEY_f: + case GDK_KEY_F: + fileBrowser->getInspector()->showWindow(!shift); + return true; + } + } + return fileBrowser->keyPressed(event); } diff --git a/rtgui/filepanel.cc b/rtgui/filepanel.cc index 1a66aed7c..983a0840c 100644 --- a/rtgui/filepanel.cc +++ b/rtgui/filepanel.cc @@ -115,9 +115,9 @@ FilePanel::FilePanel () : parent(nullptr), error(0) Gtk::Label* devLab = Gtk::manage ( new Gtk::Label (M("MAIN_TAB_DEVELOP")) ); devLab->set_name ("LabelRightNotebook"); devLab->set_angle (90); - Gtk::Label* inspectLab = Gtk::manage ( new Gtk::Label (M("MAIN_TAB_INSPECT")) ); - inspectLab->set_name ("LabelRightNotebook"); - inspectLab->set_angle (90); + //Gtk::Label* inspectLab = Gtk::manage ( new Gtk::Label (M("MAIN_TAB_INSPECT")) ); + //inspectLab->set_name ("LabelRightNotebook"); + //inspectLab->set_angle (90); Gtk::Label* filtLab = Gtk::manage ( new Gtk::Label (M("MAIN_TAB_FILTER")) ); filtLab->set_name ("LabelRightNotebook"); filtLab->set_angle (90); @@ -132,7 +132,7 @@ FilePanel::FilePanel () : parent(nullptr), error(0) tpcPaned->pack2 (*history, true, false); rightNotebook->append_page (*sFilterPanel, *filtLab); - rightNotebook->append_page (*inspectorPanel, *inspectLab); + //rightNotebook->append_page (*inspectorPanel, *inspectLab); rightNotebook->append_page (*tpcPaned, *devLab); //rightNotebook->append_page (*taggingBox, *tagLab); commented out: currently the tab is empty ... rightNotebook->append_page (*sExportPanel, *exportLab); diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc index 9002cc389..d27dd8d08 100644 --- a/rtgui/inspector.cc +++ b/rtgui/inspector.cc @@ -82,9 +82,16 @@ InspectorBuffer::~InspectorBuffer() { // return deg; //} -Inspector::Inspector () : currImage(nullptr), zoom(0.0), active(false) +Inspector::Inspector () : currImage(nullptr), scaled(false), active(false) { set_name("Inspector"); + window.add_events(Gdk::KEY_PRESS_MASK); + window.signal_key_release_event().connect(sigc::mem_fun(*this, &Inspector::on_key_release)); + window.set_title("RawTherapee Inspector"); + window.add(*this); + window.show_all(); + window.set_visible(false); + active = true; // always track inspected thumbnails } Inspector::~Inspector() @@ -92,6 +99,19 @@ Inspector::~Inspector() deleteBuffers(); } +void Inspector::showWindow(bool scaled) +{ + this->scaled = scaled; + window.fullscreen(); + window.set_visible(true); +} + +bool Inspector::on_key_release(GdkEventKey *event) +{ + window.set_visible(false); + return true; +} + bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) { @@ -116,10 +136,18 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) rtengine::Coord availableSize; rtengine::Coord topLeft; rtengine::Coord dest(0, 0); - availableSize.x = win->get_width(); - availableSize.y = win->get_height(); + int deviceScale = get_scale_factor(); + availableSize.x = win->get_width() * deviceScale; + availableSize.y = win->get_height() * deviceScale; int imW = currImage->imgBuffer.getWidth(); int imH = currImage->imgBuffer.getHeight(); + double scale = 1.0; + if (scaled) { + // reduce size of image to fit into window + scale = rtengine::min(1.0, rtengine::min((double)availableSize.x/imW, (double)availableSize.y/imH)); + availableSize.x /= scale; + availableSize.y /= scale; + } if (imW < availableSize.x) { // center the image in the available space along X @@ -163,24 +191,48 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) Glib::RefPtr style = get_style_context(); // draw the background - style->render_background(cr, 0, 0, get_width(), get_height()); + //style->render_background(cr, 0, 0, get_width(), get_height()); - /* --- old method + ///* --- old method (the new method does not seem to work) c = style->get_background_color (Gtk::STATE_FLAG_NORMAL); cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue()); cr->set_line_width (0); cr->rectangle (0, 0, availableSize.x, availableSize.y); cr->fill (); - */ + //*/ - currImage->imgBuffer.copySurface(win); + bool scaledImage = scaled && (imW > win->get_width() || imH > win->get_height()); + if (deviceScale == 1 && !scaledImage) { + // standard drawing + currImage->imgBuffer.copySurface(win); + } + else { + // consider device scale and image scale + if (deviceScale > 1) { + // use full device resolution and let it scale the image (macOS) + cairo_surface_set_device_scale(cr->get_target()->cobj(), scale, scale); + scaledImage = false; + } + Glib::RefPtr crop = Gdk::Pixbuf::create(currImage->imgBuffer.getSurface(), topLeft.x, topLeft.y, rtengine::min(imW, availableSize.x), rtengine::min(imH, availableSize.y)); + if (!scaledImage) { + Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x, dest.y); + } + else { + // assume that the device does not support scaling (Linux) + crop = crop->scale_simple(imW*scale, imH*scale, Gdk::INTERP_BILINEAR); + Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x*scale, dest.y*scale); + } + cr->paint(); + } + /* --- not for separate window // draw the frame c = style->get_border_color (Gtk::STATE_FLAG_NORMAL); cr->set_source_rgb (c.get_red(), c.get_green(), c.get_blue()); cr->set_line_width (1); cr->rectangle (0.5, 0.5, availableSize.x - 1, availableSize.y - 1); cr->stroke (); + */ } return true; @@ -309,7 +361,7 @@ void Inspector::setActive(bool state) flushBuffers(); } - active = state; + //active = state; } diff --git a/rtgui/inspector.h b/rtgui/inspector.h index 8338259bf..2844b55ac 100644 --- a/rtgui/inspector.h +++ b/rtgui/inspector.h @@ -47,12 +47,15 @@ private: rtengine::Coord center; std::vector images; InspectorBuffer* currImage; - double zoom; + bool scaled; bool active; sigc::connection delayconn; Glib::ustring next_image_path; + Gtk::Window window; + bool on_key_release(GdkEventKey *event); + bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) override; void deleteBuffers(); @@ -62,6 +65,11 @@ public: Inspector(); ~Inspector() override; + /** @brief Show or hide window + * @param scaled fit image into window + */ + void showWindow(bool scaled); + /** @brief Mouse movement to a new position * @param pos Location of the mouse, in percentage (i.e. [0;1] range) relative to the full size image ; -1,-1 == out of the image * @param transform H/V flip and coarse rotation transformation From 3deff849efd09f4d8fa0c9fbb2eec0b192443d63 Mon Sep 17 00:00:00 2001 From: rfranke Date: Fri, 3 Jan 2020 16:46:00 +0100 Subject: [PATCH 2/7] Hide inspector window during initialization, thanks to @heckflosse --- rtgui/inspector.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc index d27dd8d08..aa0a9c4ad 100644 --- a/rtgui/inspector.cc +++ b/rtgui/inspector.cc @@ -85,6 +85,7 @@ InspectorBuffer::~InspectorBuffer() { Inspector::Inspector () : currImage(nullptr), scaled(false), active(false) { set_name("Inspector"); + window.set_visible(false); window.add_events(Gdk::KEY_PRESS_MASK); window.signal_key_release_event().connect(sigc::mem_fun(*this, &Inspector::on_key_release)); window.set_title("RawTherapee Inspector"); From 6604ab1b7c6f72782d7f9827b710ef4889981d3e Mon Sep 17 00:00:00 2001 From: rfranke Date: Sat, 4 Jan 2020 18:35:15 +0100 Subject: [PATCH 3/7] Enhance inspector window with scrolling and pinning - 2D scrolling during 1:1 view (tested with mac trackpad) - click to pin inspector window - another click or ESC to hide inspector window - 'z' or 'F' to switch to 1:1 view, 'f' to switch to scaled view --- rtgui/inspector.cc | 73 ++++++++++++++++++++++++++++++++++++++++++++-- rtgui/inspector.h | 4 +++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc index aa0a9c4ad..9cda065fc 100644 --- a/rtgui/inspector.cc +++ b/rtgui/inspector.cc @@ -82,12 +82,19 @@ InspectorBuffer::~InspectorBuffer() { // return deg; //} -Inspector::Inspector () : currImage(nullptr), scaled(false), active(false) +Inspector::Inspector () : currImage(nullptr), scaled(false), active(false), pinned(false) { set_name("Inspector"); window.set_visible(false); + window.add_events(Gdk::KEY_PRESS_MASK); + window.add_events(Gdk::BUTTON_PRESS_MASK); + window.add_events(Gdk::SCROLL_MASK); window.signal_key_release_event().connect(sigc::mem_fun(*this, &Inspector::on_key_release)); + window.signal_button_press_event().connect(sigc::mem_fun(*this, &Inspector::on_button_press)); + window.signal_key_press_event().connect(sigc::mem_fun(*this, &Inspector::on_key_press)); + window.signal_scroll_event().connect(sigc::mem_fun(*this, &Inspector::on_scroll)); + window.set_title("RawTherapee Inspector"); window.add(*this); window.show_all(); @@ -105,11 +112,73 @@ void Inspector::showWindow(bool scaled) this->scaled = scaled; window.fullscreen(); window.set_visible(true); + pinned = false; } bool Inspector::on_key_release(GdkEventKey *event) { - window.set_visible(false); + if (!pinned) { + switch (event->keyval) { + case GDK_KEY_f: + case GDK_KEY_F: + window.set_visible(false); + return true; + } + } + return false; +} + +bool Inspector::on_button_press(GdkEventButton *event) +{ + if (event->type == GDK_BUTTON_PRESS) { + if (!pinned) + // pin window with mouse click + pinned = true; + else + // release window with another mouse click + window.set_visible(false); + return true; + } + return false; +} + +bool Inspector::on_key_press(GdkEventKey *event) +{ + switch (event->keyval) { + case GDK_KEY_z: + case GDK_KEY_F: + scaled = false; + queue_draw(); + return true; + case GDK_KEY_f: + scaled = true; + queue_draw(); + return true; + case GDK_KEY_Escape: + window.set_visible(false); + return true; + } + + return false; +} + +bool Inspector::on_scroll(GdkEventScroll *event) +{ + if (!currImage) + return false; + + rtengine::Coord margin; // limit for scroll area + int deviceScale = get_scale_factor(); + int imW = currImage->imgBuffer.getWidth(); + int imH = currImage->imgBuffer.getHeight(); + margin.x = (window.get_width() * deviceScale) / 2; + margin.y = (window.get_height() * deviceScale) / 2; + int new_x = rtengine::min(center.x + event->delta_x * deviceScale, imW - margin.x); + int new_y = rtengine::min(center.y + event->delta_y * deviceScale, imH - margin.y); + + center.set(rtengine::max(margin.x, new_x), rtengine::max(margin.y, new_y)); + queue_draw(); + return true; } diff --git a/rtgui/inspector.h b/rtgui/inspector.h index 2844b55ac..5f916ae81 100644 --- a/rtgui/inspector.h +++ b/rtgui/inspector.h @@ -49,12 +49,16 @@ private: InspectorBuffer* currImage; bool scaled; bool active; + bool pinned; sigc::connection delayconn; Glib::ustring next_image_path; Gtk::Window window; bool on_key_release(GdkEventKey *event); + bool on_button_press(GdkEventButton *event); + bool on_key_press(GdkEventKey *event); + bool on_scroll(GdkEventScroll *event); bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) override; void deleteBuffers(); From a064a225e0fd0726fa3faaf70a993efec7d8d48b Mon Sep 17 00:00:00 2001 From: rfranke Date: Sun, 5 Jan 2020 17:48:24 +0100 Subject: [PATCH 4/7] Support more scroll devices for inspector window - introduce step increment for non-mac - don't close pinned window on button press anymore --- rtgui/inspector.cc | 85 +++++++++++++++++++++++++++++++--------------- rtgui/inspector.h | 5 +-- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc index 9cda065fc..4624bab50 100644 --- a/rtgui/inspector.cc +++ b/rtgui/inspector.cc @@ -86,16 +86,14 @@ Inspector::Inspector () : currImage(nullptr), scaled(false), active(false), pinn { set_name("Inspector"); window.set_visible(false); + window.set_title("RawTherapee Inspector"); window.add_events(Gdk::KEY_PRESS_MASK); - window.add_events(Gdk::BUTTON_PRESS_MASK); - window.add_events(Gdk::SCROLL_MASK); window.signal_key_release_event().connect(sigc::mem_fun(*this, &Inspector::on_key_release)); - window.signal_button_press_event().connect(sigc::mem_fun(*this, &Inspector::on_button_press)); window.signal_key_press_event().connect(sigc::mem_fun(*this, &Inspector::on_key_press)); - window.signal_scroll_event().connect(sigc::mem_fun(*this, &Inspector::on_scroll)); - window.set_title("RawTherapee Inspector"); + add_events(Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK); + window.add(*this); window.show_all(); window.set_visible(false); @@ -128,20 +126,6 @@ bool Inspector::on_key_release(GdkEventKey *event) return false; } -bool Inspector::on_button_press(GdkEventButton *event) -{ - if (event->type == GDK_BUTTON_PRESS) { - if (!pinned) - // pin window with mouse click - pinned = true; - else - // release window with another mouse click - window.set_visible(false); - return true; - } - return false; -} - bool Inspector::on_key_press(GdkEventKey *event) { switch (event->keyval) { @@ -162,7 +146,18 @@ bool Inspector::on_key_press(GdkEventKey *event) return false; } -bool Inspector::on_scroll(GdkEventScroll *event) +bool Inspector::on_button_press_event(GdkEventButton *event) +{ + if (event->type == GDK_BUTTON_PRESS) { + if (!pinned) + // pin window with mouse click + pinned = true; + return true; + } + return false; +} + +bool Inspector::on_scroll_event(GdkEventScroll *event) { if (!currImage) return false; @@ -173,10 +168,46 @@ bool Inspector::on_scroll(GdkEventScroll *event) int imH = currImage->imgBuffer.getHeight(); margin.x = (window.get_width() * deviceScale) / 2; margin.y = (window.get_height() * deviceScale) / 2; - int new_x = rtengine::min(center.x + event->delta_x * deviceScale, imW - margin.x); - int new_y = rtengine::min(center.y + event->delta_y * deviceScale, imH - margin.y); - center.set(rtengine::max(margin.x, new_x), rtengine::max(margin.y, new_y)); +#ifdef GDK_WINDOWING_QUARTZ + // event reports speed of scroll wheel + double step_x = -event->delta_x; + double step_y = event->delta_y; +#else + // assume fixed step of 5% + double step_x = 5; + double step_y = 5; +#endif + int delta_x = 0; + int delta_y = 0; + switch (event->direction) { + case GDK_SCROLL_SMOOTH: +#ifdef GDK_WINDOWING_QUARTZ + // no additional step for smooth scrolling + delta_x = event->delta_x * deviceScale; + delta_y = event->delta_y * deviceScale; +#else + // apply step to smooth scrolling as well + delta_x = event->delta_x * deviceScale * step_x * imW / 100; + delta_y = event->delta_y * deviceScale * step_y * imH / 100; +#endif + break; + case GDK_SCROLL_DOWN: + delta_y = step_y * deviceScale * imH / 100; + break; + case GDK_SCROLL_UP: + delta_y = -step_y * deviceScale * imH / 100; + break; + case GDK_SCROLL_LEFT: + delta_x = step_x * deviceScale * imW / 100; + break; + case GDK_SCROLL_RIGHT: + delta_x = -step_x * deviceScale * imW / 100; + break; + } + + center.set(rtengine::LIM(center.x + delta_x, margin.x, imW - margin.x), + rtengine::LIM(center.y + delta_y, margin.y, imH - margin.y)); queue_draw(); return true; @@ -272,11 +303,11 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) //*/ bool scaledImage = scaled && (imW > win->get_width() || imH > win->get_height()); - if (deviceScale == 1 && !scaledImage) { + if (deviceScale == 1 && !scaledImage) { // standard drawing currImage->imgBuffer.copySurface(win); - } - else { + } + else { // consider device scale and image scale if (deviceScale > 1) { // use full device resolution and let it scale the image (macOS) @@ -293,7 +324,7 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x*scale, dest.y*scale); } cr->paint(); - } + } /* --- not for separate window // draw the frame diff --git a/rtgui/inspector.h b/rtgui/inspector.h index 5f916ae81..5262ccc8f 100644 --- a/rtgui/inspector.h +++ b/rtgui/inspector.h @@ -56,9 +56,10 @@ private: Gtk::Window window; bool on_key_release(GdkEventKey *event); - bool on_button_press(GdkEventButton *event); bool on_key_press(GdkEventKey *event); - bool on_scroll(GdkEventScroll *event); + + bool on_button_press_event(GdkEventButton *event) override; + bool on_scroll_event(GdkEventScroll *event) override; bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) override; void deleteBuffers(); From 134aa407c079fe18c664600bdf3516fc053d876c Mon Sep 17 00:00:00 2001 From: rfranke Date: Sun, 19 Jan 2020 16:01:29 +0100 Subject: [PATCH 5/7] Enhance inspector window with zooming - support zoom gesture and Alt scroll --- rtgui/inspector.cc | 99 ++++++++++++++++++++++++++++++++++++++-------- rtgui/inspector.h | 11 +++++- 2 files changed, 92 insertions(+), 18 deletions(-) diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc index 4624bab50..471f08e95 100644 --- a/rtgui/inspector.cc +++ b/rtgui/inspector.cc @@ -82,7 +82,7 @@ InspectorBuffer::~InspectorBuffer() { // 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"); 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)); 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.show_all(); @@ -119,6 +122,7 @@ bool Inspector::on_key_release(GdkEventKey *event) switch (event->keyval) { case GDK_KEY_f: case GDK_KEY_F: + zoomScale = 1.0; window.set_visible(false); return true; } @@ -131,14 +135,19 @@ bool Inspector::on_key_press(GdkEventKey *event) switch (event->keyval) { case GDK_KEY_z: case GDK_KEY_F: + if (pinned || scaled) + zoomScale = 1.0; // reset if not key hold scaled = false; queue_draw(); return true; case GDK_KEY_f: + if (pinned || !scaled) + zoomScale = 1.0; // reset if not key hold scaled = true; queue_draw(); return true; case GDK_KEY_Escape: + zoomScale = 1.0; window.set_visible(false); return true; } @@ -162,12 +171,10 @@ bool Inspector::on_scroll_event(GdkEventScroll *event) if (!currImage) return false; - rtengine::Coord margin; // limit for scroll area + bool alt = event->state & GDK_MOD1_MASK; int deviceScale = get_scale_factor(); int imW = currImage->imgBuffer.getWidth(); int imH = currImage->imgBuffer.getHeight(); - margin.x = (window.get_width() * deviceScale) / 2; - margin.y = (window.get_height() * deviceScale) / 2; #ifdef GDK_WINDOWING_QUARTZ // event reports speed of scroll wheel @@ -206,15 +213,66 @@ bool Inspector::on_scroll_event(GdkEventScroll *event) 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(window.get_width() * deviceScale / scale, imW) / 2; + margin.y = rtengine::min(window.get_height() * deviceScale / scale, imH) / 2; center.set(rtengine::LIM(center.x + delta_x, margin.x, imW - margin.x), rtengine::LIM(center.y + delta_y, margin.y, imH - margin.y)); - queue_draw(); + + if (!dirty) { + dirty = true; + queue_draw(); + } 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(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) { + dirty = false; Glib::RefPtr win = get_window(); @@ -240,15 +298,21 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) int deviceScale = get_scale_factor(); availableSize.x = win->get_width() * deviceScale; availableSize.y = win->get_height() * deviceScale; - int imW = currImage->imgBuffer.getWidth(); - int imH = currImage->imgBuffer.getHeight(); - double scale = 1.0; + int imW = rtengine::max(currImage->imgBuffer.getWidth(), 1); + int imH = rtengine::max(currImage->imgBuffer.getHeight(), 1); + scale = rtengine::min((double)availableSize.x / imW, (double)availableSize.y / imH); if (scaled) { - // reduce size of image to fit into window - scale = rtengine::min(1.0, rtengine::min((double)availableSize.x/imW, (double)availableSize.y/imH)); - availableSize.x /= scale; - availableSize.y /= scale; + // reduce size of image to fit into window, no further zoom down + zoomScale = rtengine::max(zoomScale, 1.0); + scale *= zoomScale; } + else { + // limit zoom to fill at least complete window or 1:1 + zoomScale = rtengine::max(zoomScale, rtengine::min(1.0, scale)); + scale = zoomScale; + } + availableSize.x /= scale; + availableSize.y /= scale; if (imW < availableSize.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 = rtengine::max(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); // define the destination area @@ -302,7 +365,7 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) cr->fill (); //*/ - bool scaledImage = scaled && (imW > win->get_width() || imH > win->get_height()); + bool scaledImage = scale != 1.0; if (deviceScale == 1 && !scaledImage) { // standard drawing 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); scaledImage = false; } - Glib::RefPtr crop = Gdk::Pixbuf::create(currImage->imgBuffer.getSurface(), topLeft.x, topLeft.y, rtengine::min(imW, availableSize.x), rtengine::min(imH, availableSize.y)); + int viewW = rtengine::min(imW, availableSize.x); + int viewH = rtengine::min(imH, availableSize.y); + Glib::RefPtr crop = Gdk::Pixbuf::create(currImage->imgBuffer.getSurface(), topLeft.x, topLeft.y, viewW, viewH); if (!scaledImage) { Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x, dest.y); } else { - // assume that the device does not support scaling (Linux) - crop = crop->scale_simple(imW*scale, imH*scale, Gdk::INTERP_BILINEAR); + // scale crop as the device does not seem to support it (Linux) + crop = crop->scale_simple(viewW*scale, viewH*scale, Gdk::INTERP_BILINEAR); Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x*scale, dest.y*scale); } cr->paint(); diff --git a/rtgui/inspector.h b/rtgui/inspector.h index 5262ccc8f..18e285b39 100644 --- a/rtgui/inspector.h +++ b/rtgui/inspector.h @@ -47,9 +47,13 @@ private: rtengine::Coord center; std::vector images; 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 pinned; + bool dirty; sigc::connection delayconn; Glib::ustring next_image_path; @@ -61,6 +65,11 @@ private: bool on_button_press_event(GdkEventButton *event) override; bool on_scroll_event(GdkEventScroll *event) override; + Glib::RefPtr 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; void deleteBuffers(); From 38c898884d0afe5968a5e1875978dd97ddebff15 Mon Sep 17 00:00:00 2001 From: rfranke Date: Thu, 23 Jan 2020 07:15:29 +0100 Subject: [PATCH 6/7] Change direction and center when zooming inspector with scroll wheel --- rtgui/inspector.cc | 27 ++++++++++++++++++++------- rtgui/inspector.h | 1 + 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc index 471f08e95..7d5d44e44 100644 --- a/rtgui/inspector.cc +++ b/rtgui/inspector.cc @@ -217,18 +217,14 @@ bool Inspector::on_scroll_event(GdkEventScroll *event) // 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); + on_zoom_scale_changed(1.0 - (double)delta_y / imH / deviceScale); else - on_zoom_scale_changed(1.0 + (double)delta_x / imW / deviceScale); + 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(window.get_width() * deviceScale / scale, imW) / 2; - margin.y = rtengine::min(window.get_height() * deviceScale / scale, imH) / 2; - center.set(rtengine::LIM(center.x + delta_x, margin.x, imW - margin.x), - rtengine::LIM(center.y + delta_y, margin.y, imH - margin.y)); + moveCenter(delta_x, delta_y, imW, imH, deviceScale); if (!dirty) { dirty = true; @@ -238,13 +234,30 @@ bool Inspector::on_scroll_event(GdkEventScroll *event) return true; } +void Inspector::moveCenter(int delta_x, int delta_y, int imW, int imH, int deviceScale) +{ + rtengine::Coord margin; // limit to image size + margin.x = rtengine::min(window.get_width() * deviceScale / scale, imW) / 2; + margin.y = rtengine::min(window.get_height() * deviceScale / scale, imH) / 2; + center.set(rtengine::LIM(center.x + delta_x, margin.x, imW - margin.x), + rtengine::LIM(center.y + delta_y, margin.y, imH - margin.y)); +} + void Inspector::beginZoom(double x, double y) { int deviceScale = get_scale_factor(); + int imW = currImage->imgBuffer.getWidth(); + int imH = currImage->imgBuffer.getHeight(); + + // limit center to image size + moveCenter(0, 0, imW, imH, deviceScale); + + // store center and current position for zooming 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) diff --git a/rtgui/inspector.h b/rtgui/inspector.h index 18e285b39..4cb0cb8df 100644 --- a/rtgui/inspector.h +++ b/rtgui/inspector.h @@ -64,6 +64,7 @@ private: bool on_button_press_event(GdkEventButton *event) override; bool on_scroll_event(GdkEventScroll *event) override; + void moveCenter(int delta_x, int delta_y, int imW, int imH, int deviceScale); Glib::RefPtr gestureZoom; void beginZoom(double x, double y); From bb88f4ad96878186476a05454ccd6cfe8fe965ed Mon Sep 17 00:00:00 2001 From: rfranke Date: Tue, 28 Jul 2020 18:11:36 +0200 Subject: [PATCH 7/7] Update AUTHORS.txt --- AUTHORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 227390faa..ba31af23a 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -14,6 +14,7 @@ Development contributors, in last name alphabetical order: Maciek Dworak Michael Ezra Flössie + Rüdiger Franke Jean-Christophe Frisch Ilias Giarimis Alberto Griggio