Masks as Photoshop's: the button reveals the selection, Alt-click shows a mask alone (13.12)
This commit is contained in:
1 parent
cc9bf95722
commit
0b0d36a7df
28 files changed
+554
-72
No files matched your search
@@ -313,6 +313,12 @@ void LayerIcons::paint(QPainter &painter, LayerIcon icon, QPointF origin, double
|
||||
case LayerIcon::dottedCircle: dottedCircle(painter); break;
|
||||
case LayerIcon::plusMagnifyingGlass: magnifyingGlass(painter, true); break;
|
||||
case LayerIcon::minusMagnifyingGlass: magnifyingGlass(painter, false); break;
|
||||
// Swift's xmark at bold weight.
|
||||
case LayerIcon::xmark:
|
||||
painter.setPen(QPen(colour, 2.6, Qt::SolidLine, Qt::RoundCap));
|
||||
painter.drawLine(QPointF(4, 4), QPointF(14, 14));
|
||||
painter.drawLine(QPointF(14, 4), QPointF(4, 14));
|
||||
break;
|
||||
}
|
||||
painter.restore();
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
enum class LayerIcon {
|
||||
eye, eyeSlash, chevronRight, chevronDown, folder, link, newLayer, newFolder, addMask, trash, layers, text,
|
||||
halfFilledCircle, sliders, curvePath, plusMinusCircle, paintPalette, circleGrid, sparkles, rightHalfCircle, hatchedCircle, axes,
|
||||
scope, lineDiagonal, drop, wind, dottedCircle, plusMagnifyingGlass, minusMagnifyingGlass
|
||||
scope, lineDiagonal, drop, wind, dottedCircle, plusMagnifyingGlass, minusMagnifyingGlass, xmark
|
||||
};
|
||||
|
||||
namespace LayerIcons {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "UI/LayerMaskMenu.h"
|
||||
#include "UI/LayerIcons.h"
|
||||
#include <QEvent>
|
||||
#include <QGuiApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPainter>
|
||||
|
||||
LayerMaskMenu::LayerMaskMenu(EditorSession &session, QWidget *parent) : QToolButton(parent), m_session(session)
|
||||
{
|
||||
@@ -8,7 +11,8 @@ LayerMaskMenu::LayerMaskMenu(EditorSession &session, QWidget *parent) : QToolBut
|
||||
setAccessibleName(QStringLiteral("Add layer mask"));
|
||||
setAutoRaise(true);
|
||||
setFixedSize(32, 40);
|
||||
connect(this, &QToolButton::clicked, this, [this] { m_session.addMask(); });
|
||||
// Swift reads Option from the event that clicked.
|
||||
connect(this, &QToolButton::clicked, this, [this] { m_session.addMask(!QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)); });
|
||||
connect(&m_session, &EditorSession::changed, this, &LayerMaskMenu::synchronize);
|
||||
synchronize();
|
||||
}
|
||||
@@ -17,7 +21,8 @@ void LayerMaskMenu::synchronize()
|
||||
{
|
||||
const std::optional<ImageLayer> active = m_session.activeLayer();
|
||||
setEnabled(m_session.canEditMask() && !(active && active->mask));
|
||||
setToolTip(m_session.selection() ? QStringLiteral("Add layer mask (the selection becomes black)") : QStringLiteral("Add layer mask"));
|
||||
setToolTip(m_session.selection() ? QStringLiteral("Add layer mask revealing the selection (Alt-click to hide it)")
|
||||
: QStringLiteral("Add layer mask (Alt-click for a black mask)"));
|
||||
setIcon(LayerIcons::pixmap(LayerIcon::addMask, 16, palette().color(QPalette::PlaceholderText), devicePixelRatio()));
|
||||
}
|
||||
|
||||
@@ -27,3 +32,54 @@ void LayerMaskMenu::changeEvent(QEvent *event)
|
||||
if (event->type() == QEvent::PaletteChange)
|
||||
synchronize();
|
||||
}
|
||||
|
||||
MaskAloneBadge::MaskAloneBadge(EditorSession &session, QWidget *parent) : QWidget(parent), m_name(new QLabel(this))
|
||||
{
|
||||
setObjectName(QStringLiteral("maskAloneBadge"));
|
||||
setFixedHeight(26);
|
||||
const auto font = [](QLabel *label, bool semibold, double alpha) {
|
||||
QFont face = label->font();
|
||||
face.setPixelSize(12);
|
||||
face.setWeight(semibold ? QFont::DemiBold : QFont::Normal);
|
||||
label->setFont(face);
|
||||
QPalette colours = label->palette();
|
||||
colours.setColor(QPalette::WindowText, QColor::fromRgbF(1, 1, 1, alpha));
|
||||
label->setPalette(colours);
|
||||
};
|
||||
auto *icon = new QLabel(this);
|
||||
icon->setPixmap(LayerIcons::pixmap(LayerIcon::addMask, 11, Qt::white, devicePixelRatioF()));
|
||||
auto *title = new QLabel(QStringLiteral("Layer Mask"), this);
|
||||
title->setObjectName(QStringLiteral("maskAloneTitle"));
|
||||
font(title, true, 1);
|
||||
m_name->setObjectName(QStringLiteral("maskAloneName"));
|
||||
m_name->setTextFormat(Qt::PlainText);
|
||||
m_name->setMaximumWidth(220);
|
||||
font(m_name, false, 0.6);
|
||||
auto *close = new QToolButton(this);
|
||||
close->setObjectName(QStringLiteral("maskAloneClose"));
|
||||
close->setAutoRaise(true);
|
||||
close->setIcon(LayerIcons::pixmap(LayerIcon::xmark, 9, Qt::white, devicePixelRatioF()));
|
||||
close->setToolTip(QStringLiteral("Show the image again (or Alt-click the mask thumbnail)"));
|
||||
close->setAccessibleName(QStringLiteral("Stop viewing the mask"));
|
||||
connect(close, &QToolButton::clicked, this, [&session] { session.setViewsMaskAlone(false); });
|
||||
auto *row = new QHBoxLayout(this);
|
||||
row->setContentsMargins(11, 0, 8, 0);
|
||||
row->setSpacing(7);
|
||||
for (QWidget *widget : std::initializer_list<QWidget *>{icon, title, m_name, close})
|
||||
row->addWidget(widget);
|
||||
}
|
||||
|
||||
// Swift's byTruncatingTail within 220 points.
|
||||
void MaskAloneBadge::setLayerName(const QString &name)
|
||||
{
|
||||
m_name->setText(m_name->fontMetrics().elidedText(name, Qt::ElideRight, 220));
|
||||
}
|
||||
|
||||
void MaskAloneBadge::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setPen(QPen(QColor::fromRgbF(1, 1, 1, 0.14), 1));
|
||||
painter.setBrush(QColor::fromRgbF(0, 0, 0, 0.75));
|
||||
painter.drawRoundedRect(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5), 13, 13);
|
||||
}
|
||||
+16
-1
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
#include "Document/EditorSession.h"
|
||||
#include <QLabel>
|
||||
#include <QToolButton>
|
||||
|
||||
// One click adds a mask: white, or the selection black.
|
||||
// One click adds a mask revealing the selection; Alt, hiding.
|
||||
class LayerMaskMenu : public QToolButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -16,3 +17,17 @@ private:
|
||||
|
||||
EditorSession &m_session;
|
||||
};
|
||||
|
||||
// While a mask shows alone: whose, and a way back.
|
||||
class MaskAloneBadge : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MaskAloneBadge(EditorSession &session, QWidget *parent = nullptr);
|
||||
void setLayerName(const QString &name);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QLabel *const m_name;
|
||||
};
|
||||
@@ -70,9 +70,10 @@ LayerThumbnailButton::LayerThumbnailButton(NativeLayerList &list, bool maskTarge
|
||||
setAutoRaise(true);
|
||||
}
|
||||
|
||||
void LayerThumbnailButton::setTargeted(bool targeted)
|
||||
void LayerThumbnailButton::setTargeted(bool targeted, bool alone)
|
||||
{
|
||||
m_targeted = targeted;
|
||||
m_alone = alone;
|
||||
QWidget::update();
|
||||
}
|
||||
|
||||
@@ -155,12 +156,9 @@ void LayerThumbnailButton::mouseReleaseEvent(QMouseEvent *event)
|
||||
if (!m_altMaskPress || event->button() != Qt::LeftButton)
|
||||
return;
|
||||
endAltMaskPress();
|
||||
// Released where pressed: a click, not a drag; Shift toggles.
|
||||
if (!rect().contains(event->position().toPoint()))
|
||||
return;
|
||||
m_list.session().selectLayerTarget(layerID, true);
|
||||
if (event->modifiers().testFlag(Qt::ShiftModifier))
|
||||
m_list.session().toggleLayerMask();
|
||||
// Released where pressed: the mask alone, or the image again.
|
||||
if (rect().contains(event->position().toPoint()))
|
||||
m_list.session().toggleMaskAlone(layerID);
|
||||
}
|
||||
|
||||
// With a modifier the second press is the button's again.
|
||||
@@ -179,7 +177,7 @@ void LayerThumbnailButton::paintEvent(QPaintEvent *)
|
||||
if (!m_targeted)
|
||||
return;
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setPen(QPen(palette().color(QPalette::Highlight), 2));
|
||||
painter.setPen(QPen(m_alone ? QColor(Qt::white) : palette().color(QPalette::Highlight), 2));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.drawRoundedRect(QRectF(rect()).adjusted(1, 1, -1, -1), 3, 3);
|
||||
}
|
||||
@@ -31,7 +31,7 @@ LayerCell::LayerCell(NativeLayerList &list)
|
||||
connect(m_disclosure, &QToolButton::clicked, this, [this] { m_list.session().toggleGroupExpansion(m_layerID); });
|
||||
m_thumbnail->setObjectName(QStringLiteral("layerThumbnail"));
|
||||
m_maskThumbnail->setObjectName(QStringLiteral("maskThumbnail"));
|
||||
m_maskThumbnail->setToolTip(QStringLiteral("Select layer mask; Shift-click to enable/disable; Ctrl-click to select its black areas (Ctrl-Shift adds, Ctrl-Alt subtracts)"));
|
||||
m_maskThumbnail->setToolTip(QStringLiteral("Select layer mask; Alt-click to view it alone; Shift-click to enable/disable; Ctrl-click to select its black areas (Ctrl-Shift adds, Ctrl-Alt subtracts)"));
|
||||
m_link->setObjectName(QStringLiteral("maskLink"));
|
||||
m_link->setAutoRaise(true);
|
||||
m_link->setFixedSize(9, 20);
|
||||
@@ -159,7 +159,7 @@ void LayerCell::updateTarget()
|
||||
const bool active = session.activeLayerID() == m_layerID && session.selectedLayerIDs().size() == 1;
|
||||
const bool mask = session.isMaskSelected();
|
||||
m_thumbnail->setTargeted(active && !mask);
|
||||
m_maskThumbnail->setTargeted(active && mask);
|
||||
m_maskThumbnail->setTargeted(active && mask, active && session.maskAloneLayer());
|
||||
QWidget::update();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ public:
|
||||
LayerThumbnailButton(NativeLayerList &list, bool maskTarget, QWidget *parent);
|
||||
QUuid layerID;
|
||||
const bool isMaskTarget;
|
||||
// An accent ring while the target is selected.
|
||||
void setTargeted(bool targeted);
|
||||
// An accent ring while targeted; white while shown alone.
|
||||
void setTargeted(bool targeted, bool alone = false);
|
||||
bool isTargeted() const { return m_targeted; }
|
||||
|
||||
~LayerThumbnailButton() override;
|
||||
@@ -63,6 +63,7 @@ private:
|
||||
|
||||
NativeLayerList &m_list;
|
||||
bool m_targeted = false;
|
||||
bool m_alone = false;
|
||||
// Alt on a mask: chosen on release, or dragged.
|
||||
std::optional<QPoint> m_altMaskPress;
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user