merge with dev
This commit is contained in:
commit
e07d142749
@ -15,6 +15,7 @@ Development contributors, in last name alphabetical order:
|
||||
Maciek Dworak
|
||||
Michael Ezra
|
||||
Flössie
|
||||
Rüdiger Franke
|
||||
Jean-Christophe Frisch
|
||||
Ilias Giarimis
|
||||
Alberto Griggio
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "pathutils.h"
|
||||
#include "thumbnail.h"
|
||||
#include "toolbar.h"
|
||||
#include "inspector.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -2508,6 +2509,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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -82,9 +82,25 @@ InspectorBuffer::~InspectorBuffer() {
|
||||
// return deg;
|
||||
//}
|
||||
|
||||
Inspector::Inspector () : currImage(nullptr), zoom(0.0), active(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);
|
||||
window.set_title("RawTherapee Inspector");
|
||||
|
||||
window.add_events(Gdk::KEY_PRESS_MASK);
|
||||
window.signal_key_release_event().connect(sigc::mem_fun(*this, &Inspector::on_key_release));
|
||||
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();
|
||||
window.set_visible(false);
|
||||
active = true; // always track inspected thumbnails
|
||||
}
|
||||
|
||||
Inspector::~Inspector()
|
||||
@ -92,8 +108,184 @@ Inspector::~Inspector()
|
||||
deleteBuffers();
|
||||
}
|
||||
|
||||
void Inspector::showWindow(bool scaled)
|
||||
{
|
||||
this->scaled = scaled;
|
||||
window.fullscreen();
|
||||
window.set_visible(true);
|
||||
pinned = false;
|
||||
}
|
||||
|
||||
bool Inspector::on_key_release(GdkEventKey *event)
|
||||
{
|
||||
if (!pinned) {
|
||||
switch (event->keyval) {
|
||||
case GDK_KEY_f:
|
||||
case GDK_KEY_F:
|
||||
zoomScale = 1.0;
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
bool alt = event->state & GDK_MOD1_MASK;
|
||||
int deviceScale = get_scale_factor();
|
||||
int imW = currImage->imgBuffer.getWidth();
|
||||
int imH = currImage->imgBuffer.getHeight();
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
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
|
||||
moveCenter(delta_x, delta_y, imW, imH, deviceScale);
|
||||
|
||||
if (!dirty) {
|
||||
dirty = true;
|
||||
queue_draw();
|
||||
}
|
||||
|
||||
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<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),
|
||||
rtengine::LIM<int>(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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
dirty = false;
|
||||
|
||||
Glib::RefPtr<Gdk::Window> win = get_window();
|
||||
|
||||
@ -116,10 +308,24 @@ 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 imW = currImage->imgBuffer.getWidth();
|
||||
int imH = currImage->imgBuffer.getHeight();
|
||||
int deviceScale = get_scale_factor();
|
||||
availableSize.x = win->get_width() * deviceScale;
|
||||
availableSize.y = win->get_height() * deviceScale;
|
||||
int imW = rtengine::max<int>(currImage->imgBuffer.getWidth(), 1);
|
||||
int imH = rtengine::max<int>(currImage->imgBuffer.getHeight(), 1);
|
||||
scale = rtengine::min<double>((double)availableSize.x / imW, (double)availableSize.y / imH);
|
||||
if (scaled) {
|
||||
// reduce size of image to fit into window, no further zoom down
|
||||
zoomScale = rtengine::max<double>(zoomScale, 1.0);
|
||||
scale *= zoomScale;
|
||||
}
|
||||
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) {
|
||||
// center the image in the available space along X
|
||||
@ -146,7 +352,6 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
||||
topLeft.y -= availableSize.y;
|
||||
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);
|
||||
|
||||
// define the destination area
|
||||
@ -163,24 +368,50 @@ bool Inspector::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
|
||||
Glib::RefPtr<Gtk::StyleContext> 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 = scale != 1.0;
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
Gdk::Cairo::set_source_pixbuf(cr, crop, dest.x, dest.y);
|
||||
}
|
||||
else {
|
||||
// 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();
|
||||
}
|
||||
|
||||
/* --- 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 +540,7 @@ void Inspector::setActive(bool state)
|
||||
flushBuffers();
|
||||
}
|
||||
|
||||
active = state;
|
||||
//active = state;
|
||||
}
|
||||
|
||||
|
||||
|
@ -47,12 +47,30 @@ private:
|
||||
rtengine::Coord center;
|
||||
std::vector<InspectorBuffer*> images;
|
||||
InspectorBuffer* currImage;
|
||||
double zoom;
|
||||
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;
|
||||
|
||||
Gtk::Window window;
|
||||
bool on_key_release(GdkEventKey *event);
|
||||
bool on_key_press(GdkEventKey *event);
|
||||
|
||||
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<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;
|
||||
void deleteBuffers();
|
||||
|
||||
@ -62,6 +80,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
|
||||
|
Loading…
x
Reference in New Issue
Block a user