Files
qelectrotech-source-mirror/sources/qetgraphicsitem/cropgeometry.h
T
Beat Hangartner d86f2d5461 Crop a picture directly on the folio
Double-clicking a picture, or "Crop on the folio" in its context
menu, crops it in place: the whole original is shown, dimmed outside
the region kept, and eight handles move the crop's edges; a drag inside
the frame moves the crop window over the original. Enter, a double-click
or a click elsewhere applies the crop as one undo step, keeping the
kept region where it is on the folio; Escape drops it. Rotated and
scaled pictures are cropped along their own sides.

While cropping, the picture is the folio's keyboardItem (see the
Escape commit before this one), so the view hands it Enter and Escape
before its own shortcuts: Enter does not repeat the last placing
command, and Escape does not clear the selection, which would apply
the crop.

The context menu gains "Reset crop", which shows the
whole original again, and "Properties...", since double-click no
longer opens them. The crop dialog stays available.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:46:46 +02:00

98 lines
3.1 KiB
C++

/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CROPGEOMETRY_H
#define CROPGEOMETRY_H
#include <QPointF>
#include <QRect>
#include <QRectF>
#include <algorithm>
#include <cmath>
/**
The geometry of cropping a picture directly on the folio, in the
picture's own pixels. Free of any item so that it can be tested on
its own.
*/
namespace CropGeometry
{
/// Which edges of the crop frame a handle moves.
struct Edges
{
bool left = false;
bool top = false;
bool right = false;
bool bottom = false;
};
/**
Edges moved by the handle at @a handle on a frame of @a size, from
where that handle sits: on the left side it moves the left edge,
in the middle of a side only that side's edge.
*/
inline Edges edgesForHandle(const QPointF &handle, const QSizeF &size)
{
Edges e;
e.left = qFuzzyIsNull(handle.x());
e.right = qFuzzyCompare(handle.x(), size.width());
e.top = qFuzzyIsNull(handle.y());
e.bottom = qFuzzyCompare(handle.y(), size.height());
return e;
}
/**
@a crop with the @a edges moved to @a point, kept inside @a bounds
and never smaller than @a minimum on either side: an edge stops
at the opposite one instead of crossing it.
*/
inline QRectF dragEdges(const QRectF &crop, Edges edges, const QPointF &point,
const QRectF &bounds, qreal minimum)
{
const qreal x = std::clamp(point.x(), bounds.left(), bounds.right());
const qreal y = std::clamp(point.y(), bounds.top(), bounds.bottom());
QRectF r = crop;
if (edges.left) r.setLeft(std::min(x, crop.right() - minimum));
if (edges.right) r.setRight(std::max(x, crop.left() + minimum));
if (edges.top) r.setTop(std::min(y, crop.bottom() - minimum));
if (edges.bottom) r.setBottom(std::max(y, crop.top() + minimum));
return r;
}
/// @a crop moved by @a delta, but no further than @a bounds allow.
inline QRectF moveWithin(const QRectF &crop, const QPointF &delta, const QRectF &bounds)
{
QRectF r = crop.translated(delta);
r.moveLeft(std::clamp(r.left(), bounds.left(), bounds.right() - r.width()));
r.moveTop(std::clamp(r.top(), bounds.top(), bounds.bottom() - r.height()));
return r;
}
/// The whole-pixel crop rectangle closest to @a crop.
inline QRect toPixels(const QRectF &crop)
{
const int left = int(std::lround(crop.left()));
const int top = int(std::lround(crop.top()));
return QRect(left, top,
int(std::lround(crop.right())) - left,
int(std::lround(crop.bottom())) - top);
}
}
#endif // CROPGEOMETRY_H