12.3b: rulers, the grid and guides on the canvas, guide drags (Compositor 1.1.7)
This commit is contained in:
1 parent
338451fc35
commit
78bfa2445d
19 files changed
+718
-10
No files matched your search
+15
-1
@@ -1,4 +1,5 @@
|
||||
#include "ContentView.h"
|
||||
#include "UI/CanvasRulers.h"
|
||||
#include "UI/ColorPaletteControls.h"
|
||||
#include "Logging.h"
|
||||
#include "Document/ProjectWorkspace.h"
|
||||
@@ -245,7 +246,18 @@ ContentView::ContentView(EditorSession &session, ProjectController *projects, QW
|
||||
middle->setSpacing(0);
|
||||
middle->addWidget(rail);
|
||||
middle->addWidget(divider(QFrame::VLine, this));
|
||||
middle->addWidget(canvas, 1);
|
||||
// The rulers frame the canvas above and left, Swift's stacks.
|
||||
auto *framed = new QWidget(this);
|
||||
auto *rulers = new QGridLayout(framed);
|
||||
rulers->setContentsMargins(0, 0, 0, 0);
|
||||
rulers->setSpacing(0);
|
||||
m_rulers = {new CanvasRulerCorner(framed), new CanvasRulerView(session, CanvasGuide::Axis::horizontal, *m_canvas, framed),
|
||||
new CanvasRulerView(session, CanvasGuide::Axis::vertical, *m_canvas, framed)};
|
||||
rulers->addWidget(m_rulers[0], 0, 0);
|
||||
rulers->addWidget(m_rulers[1], 0, 1);
|
||||
rulers->addWidget(m_rulers[2], 1, 0);
|
||||
rulers->addWidget(canvas, 1, 1);
|
||||
middle->addWidget(framed, 1);
|
||||
m_layersPanel->setFixedWidth(int(layersPanelWidth()));
|
||||
middle->addWidget(new PanelResizeEdge(*m_layersPanel, this));
|
||||
middle->addWidget(m_layersPanel);
|
||||
@@ -372,6 +384,8 @@ void ContentView::synchronize()
|
||||
}
|
||||
const std::optional<CanvasDocument> &document = m_session.document();
|
||||
showWelcome(!document);
|
||||
for (QWidget *ruler : m_rulers)
|
||||
ruler->setVisible(m_session.showsRulers() && document);
|
||||
m_zoom->setVisible(document.has_value());
|
||||
m_dimensions->setVisible(document.has_value());
|
||||
if (document) {
|
||||
|
||||
@@ -69,6 +69,8 @@ private:
|
||||
LayersPanel *const m_layersPanel;
|
||||
// An accent ring while a drop may land.
|
||||
QWidget *const m_dropRing;
|
||||
// Swift 1.1.7's rulers: the corner, then the two strips.
|
||||
std::vector<QWidget *> m_rulers;
|
||||
ProjectWorkspace *m_workspace = nullptr;
|
||||
// A document that appears hands the canvas the keys.
|
||||
bool m_hadDocument = false;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Swift's Guides extension, and the View menu's guide state.
|
||||
public:
|
||||
// Cyan, Photoshop's default guide colour.
|
||||
static inline const QColor guideColor = QColor::fromRgbF(0, 1, 1, 0.9f);
|
||||
static constexpr double guideHitDistance = 5;
|
||||
bool showsGrid() const { return m_showsGrid; }
|
||||
void setShowsGrid(bool shows);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "Rendering/EditorCanvas.h"
|
||||
|
||||
// Swift's isOverRuler: rulers sit above and left.
|
||||
bool CanvasView::isOverRuler(QPointF point) const
|
||||
{
|
||||
return m_session.showsRulers() && (point.x() < 0 || point.y() < 0);
|
||||
}
|
||||
|
||||
std::optional<double> CanvasView::documentPosition(CanvasGuide::Axis axis, QPointF point) const
|
||||
{
|
||||
const std::optional<CanvasDocument> &document = m_session.document();
|
||||
if (!document)
|
||||
return std::nullopt;
|
||||
const QPointF pixel = m_session.viewport.documentPoint(point, document->size());
|
||||
return axis == CanvasGuide::Axis::vertical ? pixel.x() : pixel.y();
|
||||
}
|
||||
|
||||
// A press on a guide moves it, before any layer.
|
||||
bool CanvasView::beginGuideDrag(QPointF point)
|
||||
{
|
||||
const std::optional<CanvasGuide> guide = m_session.canEditGuides() ? m_session.hitGuide(point) : std::nullopt;
|
||||
if (!guide)
|
||||
return false;
|
||||
m_session.beginGuideMove(*guide);
|
||||
m_guideDragging = true;
|
||||
m_dragCursor = QCursor(guide->axis == CanvasGuide::Axis::vertical ? Qt::SizeHorCursor : Qt::SizeVerCursor);
|
||||
setCursor(*m_dragCursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
// A move without the left button: Qt lost the release.
|
||||
bool CanvasView::guideMove(QPointF point, Qt::MouseButtons buttons)
|
||||
{
|
||||
if (!m_guideDragging)
|
||||
return false;
|
||||
if (!buttons.testFlag(Qt::LeftButton)) {
|
||||
endGuideDrag(point);
|
||||
return true;
|
||||
}
|
||||
const std::optional<GuideDrag> &drag = m_session.guideDrag();
|
||||
if (const std::optional<double> position = drag ? documentPosition(drag->axis, point) : std::nullopt)
|
||||
m_session.moveGuideDrag(*position);
|
||||
synchronizeDisplay();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Released over a ruler, the guide goes.
|
||||
void CanvasView::endGuideDrag(QPointF point)
|
||||
{
|
||||
if (!m_guideDragging)
|
||||
return;
|
||||
m_guideDragging = false;
|
||||
m_dragCursor.reset();
|
||||
m_session.finishGuideDrag(isOverRuler(point));
|
||||
updateCursor();
|
||||
}
|
||||
@@ -111,6 +111,11 @@ void CanvasView::pressKey(QKeyEvent *event, int physical)
|
||||
m_session.cancelGradient();
|
||||
} else if (cropKey(*event)) {
|
||||
return;
|
||||
} else if (event->key() == Qt::Key_Escape && m_session.guideDrag()) {
|
||||
m_session.cancelGuideDrag();
|
||||
m_guideDragging = false;
|
||||
m_dragCursor.reset();
|
||||
updateCursor();
|
||||
} else if ((event->key() == Qt::Key_Escape || enter) && m_session.transformEdit()) {
|
||||
// The keys end a drag with its edit, cursor included.
|
||||
m_transformDrag.reset();
|
||||
|
||||
@@ -29,7 +29,8 @@ bool CanvasView::event(QEvent *event)
|
||||
if (event->type() == QEvent::NativeGesture) {
|
||||
const auto *gesture = static_cast<QNativeGestureEvent *>(event);
|
||||
// A pinch, as Swift's magnify; not mid-drag or mid-stroke.
|
||||
if (gesture->gestureType() == Qt::ZoomNativeGesture && !m_transformDrag && !m_cropDrag && !m_session.brushStroke() && !m_session.warpStroke()) {
|
||||
if (gesture->gestureType() == Qt::ZoomNativeGesture && !m_transformDrag && !m_cropDrag && !m_guideDragging && !m_session.brushStroke()
|
||||
&& !m_session.warpStroke()) {
|
||||
m_session.zoom(m_session.viewport.zoom() * (1 + gesture->value()), gesture->position());
|
||||
event->accept();
|
||||
return true;
|
||||
@@ -97,7 +98,7 @@ void CanvasView::consumeFocusRequest(int request)
|
||||
|
||||
void CanvasView::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (!m_session.document() || m_transformDrag || m_cropDrag || m_session.brushStroke() || m_session.warpStroke())
|
||||
if (!m_session.document() || m_transformDrag || m_cropDrag || m_guideDragging || m_session.brushStroke() || m_session.warpStroke())
|
||||
return;
|
||||
// Trackpads report pixels; a wheel reports notches, lines each.
|
||||
const bool precise = !event->pixelDelta().isNull();
|
||||
@@ -146,7 +147,8 @@ void CanvasView::mousePressEvent(QMouseEvent *event)
|
||||
m_lastDragPoint = point;
|
||||
updateCursor();
|
||||
} else if (m_session.tool() == NavigationTool::move) {
|
||||
beginTransformDrag(point, event->modifiers());
|
||||
if (!beginGuideDrag(point))
|
||||
beginTransformDrag(point, event->modifiers());
|
||||
} else if (isSelectionTool(m_session.tool())) {
|
||||
lassoMouseDown(point, event->modifiers());
|
||||
updateCursor();
|
||||
@@ -211,7 +213,7 @@ void CanvasView::mouseMoveEvent(QMouseEvent *event)
|
||||
if (m_inlineTextEditor && !held)
|
||||
m_inlineTextEditor->release();
|
||||
if (pickingMove(point, event->buttons()) || targetingMove(point, event->buttons(), event->modifiers())
|
||||
|| cropMove(point, event->buttons(), event->modifiers()))
|
||||
|| cropMove(point, event->buttons(), event->modifiers()) || guideMove(point, event->buttons()))
|
||||
return;
|
||||
if (m_transformDrag) {
|
||||
dragTransform(point, event->modifiers());
|
||||
@@ -280,6 +282,7 @@ void CanvasView::mouseReleaseEvent(QMouseEvent *event)
|
||||
return;
|
||||
}
|
||||
m_session.snapGuides = {};
|
||||
endGuideDrag(event->position());
|
||||
pickingRelease();
|
||||
targetingRelease();
|
||||
brushMouseUp(event->position());
|
||||
@@ -323,6 +326,8 @@ void CanvasView::focusOutEvent(QFocusEvent *event)
|
||||
updateBrushCursor();
|
||||
cancelTransformDrag();
|
||||
endCropDrag();
|
||||
// A lost release: the guide stays where it was dragged.
|
||||
endGuideDrag(QPointF(0, 0));
|
||||
m_gradientDrag.reset();
|
||||
m_session.cancelShape();
|
||||
endSelectionGestures();
|
||||
|
||||
@@ -178,6 +178,9 @@ QCursor CanvasView::transformCursor(QPointF point, Qt::KeyboardModifiers modifie
|
||||
const std::optional<TransformOverlayGeometry> geometry = m_overlay.geometry();
|
||||
const std::optional<TransformDrag::Mode> hit = geometry ? geometry->hit(point) : std::nullopt;
|
||||
if (!hit) {
|
||||
// Guides lie under the handles, over a layer's drag.
|
||||
if (const std::optional<CanvasGuide> guide = m_session.hitGuide(point))
|
||||
return QCursor(guide->axis == CanvasGuide::Axis::vertical ? Qt::SizeHorCursor : Qt::SizeVerCursor);
|
||||
if (!pressMovesLayer(point, modifiers))
|
||||
return QCursor(Qt::ArrowCursor);
|
||||
return duplicate ? duplicateCursor(ratio) : moveCursor(ratio);
|
||||
|
||||
@@ -161,6 +161,11 @@ bool CanvasView::synchronizeDisplay()
|
||||
} else if (m_displayedTool != m_session.tool() || m_displayedBlocked != blocked || m_displayedSelectionCursor != cursorState) {
|
||||
updateCursor();
|
||||
}
|
||||
// The grid and guides redraw the view, as Swift's observation.
|
||||
if (const GuidesShown lines{m_session.showsGrid(), m_session.showsGuides(), m_session.displayedGuides()}; m_displayedGuideLines != lines) {
|
||||
update();
|
||||
m_displayedGuideLines = lines;
|
||||
}
|
||||
// A shape draft redraws the view, as Swift's observation does.
|
||||
if (const std::optional<ShapeShown> shape = shownShape(); m_displayedShape != shape) {
|
||||
update();
|
||||
|
||||
@@ -58,6 +58,9 @@ public:
|
||||
const SampleRingOverlay &sampleRing() const { return m_sampleRing; }
|
||||
// The text typed on the canvas, while one is open.
|
||||
InlineTextEditor *inlineTextEditor() const { return m_inlineTextEditor.get(); }
|
||||
// Released on the rulers, just outside the canvas.
|
||||
bool isOverRuler(QPointF point) const;
|
||||
std::optional<double> documentPosition(CanvasGuide::Axis axis, QPointF point) const;
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event) override;
|
||||
@@ -234,6 +237,10 @@ private:
|
||||
void dragCrop(QPointF point, Qt::KeyboardModifiers modifiers);
|
||||
void endCropDrag();
|
||||
bool cropKey(const QKeyEvent &key);
|
||||
// The Move tool's guide drags (EditorCanvas+Guides.cpp).
|
||||
bool beginGuideDrag(QPointF point);
|
||||
bool guideMove(QPointF point, Qt::MouseButtons buttons);
|
||||
void endGuideDrag(QPointF point);
|
||||
// Swift's targeted drag: right raises; Ctrl drags the hue.
|
||||
bool targetingMove(QPointF point, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers);
|
||||
void targetingRelease();
|
||||
@@ -348,4 +355,13 @@ private:
|
||||
std::optional<CropSnap> m_cropSnap;
|
||||
// The crop frame as last shown, in view points.
|
||||
std::optional<QRectF> m_displayedCrop;
|
||||
bool m_guideDragging = false;
|
||||
// The grid and guides as drawn: a change redraws.
|
||||
struct GuidesShown {
|
||||
bool grid;
|
||||
bool guides;
|
||||
std::vector<CanvasGuide> lines;
|
||||
friend bool operator==(const GuidesShown &, const GuidesShown &) = default;
|
||||
};
|
||||
std::optional<GuidesShown> m_displayedGuideLines;
|
||||
};
|
||||
@@ -181,6 +181,8 @@ std::optional<std::pair<QPointF, QPointF>> TransformOverlay::gradientLine() cons
|
||||
|
||||
void TransformOverlay::draw(QPainter &context, const QPalette &palette) const
|
||||
{
|
||||
drawLayoutGrid(context);
|
||||
drawGuides(context);
|
||||
if (m_session.tool() == NavigationTool::crop)
|
||||
drawCrop(context);
|
||||
else if (const std::optional<std::pair<QPointF, QPointF>> line = gradientLine())
|
||||
@@ -359,6 +361,70 @@ void TransformOverlay::drawTransformHandles(QPainter &context, const QPalette &p
|
||||
context.restore();
|
||||
}
|
||||
|
||||
// The layout grid: solid majors, dotted subdivisions when roomy.
|
||||
void TransformOverlay::drawLayoutGrid(QPainter &context) const
|
||||
{
|
||||
const std::optional<CanvasDocument> &document = m_session.document();
|
||||
if (!m_session.showsGrid() || !document)
|
||||
return;
|
||||
const QSizeF size = document->size();
|
||||
const QTransform map = documentToView();
|
||||
const double hairline = 1 / std::max(m_session.viewport.backingScale, 1.0);
|
||||
const auto lines = [&](bool majors) {
|
||||
QPainterPath path;
|
||||
for (const double x : LayoutGrid::lines(size.width())) {
|
||||
if (LayoutGrid::isMajor(x) == majors) {
|
||||
path.moveTo(map.map(QPointF(x, 0)));
|
||||
path.lineTo(map.map(QPointF(x, size.height())));
|
||||
}
|
||||
}
|
||||
for (const double y : LayoutGrid::lines(size.height())) {
|
||||
if (LayoutGrid::isMajor(y) == majors) {
|
||||
path.moveTo(map.map(QPointF(0, y)));
|
||||
path.lineTo(map.map(QPointF(size.width(), y)));
|
||||
}
|
||||
}
|
||||
return path;
|
||||
};
|
||||
context.save();
|
||||
context.setRenderHint(QPainter::Antialiasing, true);
|
||||
context.setBrush(Qt::NoBrush);
|
||||
if (LayoutGrid::step * m_session.viewport.pointsPerPixel() >= 4) {
|
||||
QPen dotted(QColor::fromRgbF(0.55, 0.55, 0.55, 0.28), hairline, Qt::CustomDashLine, Qt::FlatCap);
|
||||
// One point on, two off: Qt counts in pen widths.
|
||||
dotted.setDashPattern({1 / hairline, 2 / hairline});
|
||||
context.setPen(dotted);
|
||||
context.drawPath(lines(false));
|
||||
}
|
||||
context.setPen(QPen(QColor::fromRgbF(0.7, 0.7, 0.7, 0.45), hairline, Qt::SolidLine, Qt::FlatCap));
|
||||
context.drawPath(lines(true));
|
||||
context.restore();
|
||||
}
|
||||
|
||||
// Guides span the whole view, past the document too.
|
||||
void TransformOverlay::drawGuides(QPainter &context) const
|
||||
{
|
||||
const std::optional<CanvasDocument> &document = m_session.document();
|
||||
if (!m_session.showsGuides() || !document)
|
||||
return;
|
||||
const std::vector<CanvasGuide> guides = m_session.displayedGuides();
|
||||
if (guides.empty())
|
||||
return;
|
||||
const CanvasViewport &viewport = m_session.viewport;
|
||||
const QSizeF view = viewport.viewSize;
|
||||
context.save();
|
||||
context.setRenderHint(QPainter::Antialiasing, true);
|
||||
context.setPen(QPen(EditorSession::guideColor, 1 / std::max(viewport.backingScale, 1.0), Qt::SolidLine, Qt::FlatCap));
|
||||
for (const CanvasGuide &guide : guides) {
|
||||
const QPointF at = viewport.viewPoint(QPointF(guide.position, guide.position), document->size());
|
||||
if (guide.axis == CanvasGuide::Axis::vertical)
|
||||
context.drawLine(QPointF(at.x(), 0), QPointF(at.x(), view.height()));
|
||||
else
|
||||
context.drawLine(QPointF(0, at.y()), QPointF(view.width(), at.y()));
|
||||
}
|
||||
context.restore();
|
||||
}
|
||||
|
||||
// While a move snaps, a line along what it met.
|
||||
void TransformOverlay::drawSnapGuides(QPainter &context, const QPalette &palette) const
|
||||
{
|
||||
|
||||
@@ -51,6 +51,8 @@ public:
|
||||
|
||||
private:
|
||||
QTransform documentToView() const;
|
||||
void drawLayoutGrid(QPainter &context) const;
|
||||
void drawGuides(QPainter &context) const;
|
||||
void drawTransformHandles(QPainter &context, const QPalette &palette) const;
|
||||
void drawGradientLine(QPainter &context, QPointF start, QPointF end) const;
|
||||
void drawCrop(QPainter &context) const;
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
#include "UI/CanvasRulers.h"
|
||||
#include "Rendering/EditorCanvas.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
CanvasRulerCorner::CanvasRulerCorner(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setFixedSize(CanvasRuler::thickness, CanvasRuler::thickness);
|
||||
}
|
||||
|
||||
void CanvasRulerCorner::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.fillRect(rect(), QColor::fromRgbF(0.2, 0.2, 0.2));
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
painter.setPen(QPen(QColor::fromRgbF(1, 1, 1, 0.28), 1));
|
||||
const double side = CanvasRuler::thickness;
|
||||
painter.drawLine(QPointF(5, side - 4), QPointF(side - 4, 5));
|
||||
}
|
||||
|
||||
CanvasRulerView::CanvasRulerView(EditorSession &session, CanvasGuide::Axis axis, CanvasView &canvas, QWidget *parent)
|
||||
: QWidget(parent), m_session(session), m_axis(axis), m_canvas(canvas)
|
||||
{
|
||||
const bool vertical = axis == CanvasGuide::Axis::vertical;
|
||||
setAccessibleName(vertical ? QStringLiteral("Vertical ruler") : QStringLiteral("Horizontal ruler"));
|
||||
setCursor(vertical ? Qt::SizeHorCursor : Qt::SizeVerCursor);
|
||||
if (vertical)
|
||||
setFixedWidth(CanvasRuler::thickness);
|
||||
else
|
||||
setFixedHeight(CanvasRuler::thickness);
|
||||
connect(&session, &EditorSession::changed, this, qOverload<>(&QWidget::update));
|
||||
}
|
||||
|
||||
double CanvasRulerView::majorStep(double pointsPerPixel)
|
||||
{
|
||||
const double target = 70 / std::max(pointsPerPixel, 0.0001);
|
||||
static constexpr std::array<double, 18> nice{1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1'000, 2'000, 2'500, 5'000, 10'000, 20'000, 25'000};
|
||||
for (const double step : nice) {
|
||||
if (step >= target)
|
||||
return step;
|
||||
}
|
||||
return 50'000;
|
||||
}
|
||||
|
||||
QString CanvasRulerView::label(double value)
|
||||
{
|
||||
return QString::number(qint64(std::round(value)));
|
||||
}
|
||||
|
||||
void CanvasRulerView::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.fillRect(rect(), QColor::fromRgbF(0.2, 0.2, 0.2));
|
||||
const std::optional<CanvasDocument> &document = m_session.document();
|
||||
if (!document)
|
||||
return;
|
||||
const bool horizontal = m_axis == CanvasGuide::Axis::horizontal;
|
||||
const QSizeF size = document->size();
|
||||
const CanvasViewport &viewport = m_session.viewport;
|
||||
const double step = majorStep(viewport.pointsPerPixel());
|
||||
const double minor = step / 10;
|
||||
const double hairline = 1 / std::max(devicePixelRatioF(), 1.0);
|
||||
// The canvas's own points, wherever the ruler sits beside it.
|
||||
const QPointF offset = inCanvas(QPointF(0, 0));
|
||||
const QPointF from = viewport.documentPoint(offset, size), to = viewport.documentPoint(offset + QPointF(width(), height()), size);
|
||||
const double start = horizontal ? from.x() : from.y(), end = horizontal ? to.x() : to.y();
|
||||
const double first = std::floor(std::min(start, end) / minor) * minor, last = std::ceil(std::max(start, end) / minor) * minor;
|
||||
if (!(minor > 0) || !std::isfinite(first) || !std::isfinite(last))
|
||||
return;
|
||||
QFont font = painter.font();
|
||||
font.setPixelSize(8);
|
||||
painter.setFont(font);
|
||||
const QColor tick = QColor::fromRgbF(0.62, 0.62, 0.62), words = QColor::fromRgbF(0.78, 0.78, 0.78);
|
||||
for (double value = first; value <= last + 0.001; value += minor) {
|
||||
const QPointF at = viewport.viewPoint(QPointF(value, value), size) - offset;
|
||||
const double view = horizontal ? at.x() : at.y();
|
||||
const double remainder = std::abs(std::remainder(value, step));
|
||||
const bool major = remainder < 0.001 || std::abs(remainder - step) < 0.001;
|
||||
const bool middle = !major && std::abs(std::remainder(value, step / 2)) < 0.001;
|
||||
const double length = major ? 8 : middle ? 5 : 3;
|
||||
painter.fillRect(horizontal ? QRectF(view - hairline / 2, height() - length, hairline, length)
|
||||
: QRectF(width() - length, view - hairline / 2, length, hairline),
|
||||
tick);
|
||||
if (!major)
|
||||
continue;
|
||||
const QString text = label(value);
|
||||
const QSizeF drawn = painter.fontMetrics().size(Qt::TextSingleLine, text);
|
||||
painter.setPen(words);
|
||||
if (horizontal) {
|
||||
painter.drawText(QRectF(QPointF(view + 2, 0), drawn), Qt::AlignLeft | Qt::AlignTop, text);
|
||||
} else {
|
||||
// Turned along the tick, as Swift draws it.
|
||||
painter.save();
|
||||
painter.translate(1, view + 2);
|
||||
painter.rotate(-90);
|
||||
painter.drawText(QRectF(QPointF(-drawn.width(), 0), drawn), Qt::AlignLeft | Qt::AlignTop, text);
|
||||
painter.restore();
|
||||
}
|
||||
}
|
||||
const QColor edge = QColor::fromRgbF(0.08, 0.08, 0.08);
|
||||
painter.fillRect(horizontal ? QRectF(0, height() - hairline, width(), hairline) : QRectF(width() - hairline, 0, hairline, height()), edge);
|
||||
}
|
||||
|
||||
// The canvas is a sibling: points go by the screen.
|
||||
QPointF CanvasRulerView::inCanvas(QPointF point) const
|
||||
{
|
||||
return m_canvas.mapFromGlobal(mapToGlobal(point));
|
||||
}
|
||||
|
||||
std::optional<double> CanvasRulerView::documentPosition(QPointF point) const
|
||||
{
|
||||
return m_canvas.documentPosition(m_axis, inCanvas(point));
|
||||
}
|
||||
|
||||
void CanvasRulerView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() != Qt::LeftButton)
|
||||
return;
|
||||
m_canvas.setFocus(Qt::MouseFocusReason);
|
||||
const std::optional<double> position = documentPosition(event->position());
|
||||
if (!m_session.canEditGuides() || !position)
|
||||
return;
|
||||
m_session.beginGuideCreation(m_axis, *position);
|
||||
}
|
||||
|
||||
void CanvasRulerView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!m_session.guideDrag())
|
||||
return;
|
||||
if (const std::optional<double> position = documentPosition(event->position()))
|
||||
m_session.moveGuideDrag(*position);
|
||||
}
|
||||
|
||||
void CanvasRulerView::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() != Qt::LeftButton || !m_session.guideDrag())
|
||||
return;
|
||||
m_session.finishGuideDrag(m_canvas.isOverRuler(inCanvas(event->position())));
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "Document/EditorSession.h"
|
||||
#include <QWidget>
|
||||
|
||||
class CanvasView;
|
||||
|
||||
namespace CanvasRuler {
|
||||
inline constexpr int thickness = 18;
|
||||
}
|
||||
|
||||
// The square where the two rulers meet.
|
||||
class CanvasRulerCorner : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CanvasRulerCorner(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
// Swift's CanvasRulerNSView: ticks in document pixels; drag out a guide.
|
||||
class CanvasRulerView : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CanvasRulerView(EditorSession &session, CanvasGuide::Axis axis, CanvasView &canvas, QWidget *parent = nullptr);
|
||||
// Numbered ticks about 70 points apart, 1-2-5 steps.
|
||||
static double majorStep(double pointsPerPixel);
|
||||
static QString label(double value);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
QPointF inCanvas(QPointF point) const;
|
||||
std::optional<double> documentPosition(QPointF point) const;
|
||||
|
||||
EditorSession &m_session;
|
||||
const CanvasGuide::Axis m_axis;
|
||||
CanvasView &m_canvas;
|
||||
};
|
||||
Reference in new issue
Block a user