New Canvas presets in a More menu; every save writes QuickLook/Preview.jpg (13.7)
This commit is contained in:
1 parent
765e071c0c
commit
0616f9b006
12 files changed
+420
-7
No files matched your search
@@ -5,9 +5,13 @@
|
||||
#include <QBuffer>
|
||||
#include <QClipboard>
|
||||
#include <QGridLayout>
|
||||
#include <QActionGroup>
|
||||
#include <QImageReader>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QShortcut>
|
||||
#include <QVBoxLayout>
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
QLabel *text(const QString &words, int pixels, QFont::Weight weight, QPalette::ColorRole role, QWidget *parent)
|
||||
@@ -20,12 +24,58 @@ QLabel *text(const QString &words, int pixels, QFont::Weight weight, QPalette::C
|
||||
label->setForegroundRole(role);
|
||||
return label;
|
||||
}
|
||||
|
||||
// Swift's More button: three dots drawn exactly, flush right.
|
||||
class MoreButton : public QToolButton {
|
||||
public:
|
||||
using QToolButton::QToolButton;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(palette().color(QPalette::WindowText));
|
||||
const double dot = 2.5, top = (height() - 5 * dot) / 2;
|
||||
for (int index = 0; index < 3; ++index)
|
||||
painter.drawEllipse(QRectF(width() - dot, top + 2 * dot * index, dot, dot));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std::vector<std::vector<CanvasPreset>> CanvasPreset::groups = {
|
||||
{
|
||||
{QStringLiteral("4K"), 3840, 2160},
|
||||
{QStringLiteral("1440p"), 2560, 1440},
|
||||
{QStringLiteral("1080p"), 1920, 1080},
|
||||
},
|
||||
{
|
||||
{QStringLiteral("iPhone 18 Pro"), 1206, 2622},
|
||||
{QStringLiteral("iPhone 18 Pro Max"), 1320, 2868},
|
||||
{QStringLiteral("MacBook Pro 14\""), 3024, 1964},
|
||||
{QStringLiteral("MacBook Pro 16\""), 3456, 2234},
|
||||
{QStringLiteral("Studio Display"), 5120, 2880},
|
||||
},
|
||||
{
|
||||
{QStringLiteral("Instagram Square"), 1080, 1080},
|
||||
{QStringLiteral("Instagram Portrait"), 1080, 1350},
|
||||
{QStringLiteral("Instagram Story"), 1080, 1920},
|
||||
{QStringLiteral("YouTube Thumb"), 1080, 608},
|
||||
},
|
||||
};
|
||||
|
||||
const std::vector<CanvasPreset> CanvasPreset::all = [] {
|
||||
std::vector<CanvasPreset> presets;
|
||||
for (const std::vector<CanvasPreset> &group : groups)
|
||||
presets.insert(presets.end(), group.begin(), group.end());
|
||||
return presets;
|
||||
}();
|
||||
|
||||
NewCanvasSheet::NewCanvasSheet(EditorSession &session, std::function<void(int, int)> onCreate, std::function<void()> onOpen, QWidget *parent)
|
||||
: QWidget(parent), m_session(session), m_onCreate(std::move(onCreate)), m_width(new QLineEdit(QStringLiteral("1920"), this)),
|
||||
m_height(new QLineEdit(QStringLiteral("1080"), this)), m_note(text(QString(), 12, QFont::Normal, QPalette::PlaceholderText, this)),
|
||||
m_create(new QPushButton(QStringLiteral("Create canvas"), this))
|
||||
m_create(new QPushButton(QStringLiteral("Create canvas"), this)), m_presets(new MoreButton(this))
|
||||
{
|
||||
// Swift's fields stretch the sheet to its full 500 points.
|
||||
setFixedWidth(500);
|
||||
@@ -62,17 +112,46 @@ NewCanvasSheet::NewCanvasSheet(EditorSession &session, std::function<void(int, i
|
||||
auto *column = new QVBoxLayout(this);
|
||||
column->setContentsMargins(28, 28, 28, 28);
|
||||
column->setSpacing(24);
|
||||
auto *heading = new QVBoxLayout;
|
||||
heading->setSpacing(6);
|
||||
auto *heading = new QHBoxLayout;
|
||||
heading->addWidget(text(QStringLiteral("New canvas"), 17, QFont::DemiBold, QPalette::WindowText, this));
|
||||
heading->addWidget(text(QStringLiteral("A blank space for your next composition."), 13, QFont::Normal, QPalette::PlaceholderText, this));
|
||||
heading->addStretch(1);
|
||||
heading->addWidget(m_presets);
|
||||
column->addLayout(heading);
|
||||
column->addLayout(fields);
|
||||
column->addWidget(m_note);
|
||||
column->addLayout(buttons);
|
||||
|
||||
m_presets->setObjectName(QStringLiteral("presetSizes"));
|
||||
m_presets->setFixedSize(28, 28);
|
||||
m_presets->setPopupMode(QToolButton::InstantPopup);
|
||||
m_presets->setToolTip(QStringLiteral("Preset sizes for screens and common formats"));
|
||||
m_presets->setAccessibleName(QStringLiteral("Preset sizes"));
|
||||
auto *menu = new QMenu(m_presets);
|
||||
auto *choices = new QActionGroup(menu);
|
||||
// Custom changes nothing; the match stays checked.
|
||||
QAction *custom = choices->addAction(menu->addAction(QStringLiteral("Custom")));
|
||||
custom->setCheckable(true);
|
||||
custom->setData(-1);
|
||||
connect(custom, &QAction::triggered, this, &NewCanvasSheet::checkPreset);
|
||||
int index = 0;
|
||||
for (const std::vector<CanvasPreset> &group : CanvasPreset::groups) {
|
||||
menu->addSeparator();
|
||||
for (const CanvasPreset &preset : group) {
|
||||
QAction *action = choices->addAction(menu->addAction(preset.title));
|
||||
action->setCheckable(true);
|
||||
action->setData(index++);
|
||||
connect(action, &QAction::triggered, this, [this, preset] {
|
||||
m_width->setText(QString::number(preset.width));
|
||||
m_height->setText(QString::number(preset.height));
|
||||
});
|
||||
}
|
||||
}
|
||||
m_presets->setMenu(menu);
|
||||
|
||||
connect(m_width, &QLineEdit::textChanged, this, &NewCanvasSheet::validate);
|
||||
connect(m_height, &QLineEdit::textChanged, this, &NewCanvasSheet::validate);
|
||||
connect(m_width, &QLineEdit::textChanged, this, &NewCanvasSheet::checkPreset);
|
||||
connect(m_height, &QLineEdit::textChanged, this, &NewCanvasSheet::checkPreset);
|
||||
// Return anywhere on the sheet is the default action.
|
||||
std::vector<QShortcut *> returns;
|
||||
for (const Qt::Key key : {Qt::Key_Return, Qt::Key_Enter})
|
||||
@@ -90,6 +169,18 @@ NewCanvasSheet::NewCanvasSheet(EditorSession &session, std::function<void(int, i
|
||||
connect(&m_session, &EditorSession::changed, this, &NewCanvasSheet::synchronize);
|
||||
synchronize();
|
||||
validate();
|
||||
checkPreset();
|
||||
}
|
||||
|
||||
void NewCanvasSheet::checkPreset()
|
||||
{
|
||||
const auto matched = std::ranges::find_if(CanvasPreset::all, [this](const CanvasPreset &preset) {
|
||||
return QString::number(preset.width) == m_width->text() && QString::number(preset.height) == m_height->text();
|
||||
});
|
||||
const QVariant index(matched == CanvasPreset::all.end() ? -1 : int(matched - CanvasPreset::all.begin()));
|
||||
for (QAction *action : m_presets->menu()->actions())
|
||||
if (action->data() == index)
|
||||
action->setChecked(true);
|
||||
}
|
||||
|
||||
void NewCanvasSheet::synchronize()
|
||||
|
||||
@@ -4,8 +4,20 @@
|
||||
#include <QLineEdit>
|
||||
#include <QMimeData>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
#include <QWidget>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
// New Canvas sizes in pixels, upright as held.
|
||||
struct CanvasPreset {
|
||||
QString title;
|
||||
int width;
|
||||
int height;
|
||||
// Resolutions, Apple screens, then social formats; the menu divides them.
|
||||
static const std::vector<std::vector<CanvasPreset>> groups;
|
||||
static const std::vector<CanvasPreset> all;
|
||||
};
|
||||
|
||||
// The welcome: a new canvas, a project or an image.
|
||||
class NewCanvasSheet : public QWidget {
|
||||
@@ -23,6 +35,8 @@ private:
|
||||
void synchronize();
|
||||
void validate();
|
||||
void create();
|
||||
// Checks the preset the fields match, else Custom.
|
||||
void checkPreset();
|
||||
|
||||
EditorSession &m_session;
|
||||
const std::function<void(int, int)> m_onCreate;
|
||||
@@ -30,5 +44,6 @@ private:
|
||||
QLineEdit *const m_height;
|
||||
QLabel *const m_note;
|
||||
QPushButton *const m_create;
|
||||
QToolButton *const m_presets;
|
||||
bool m_suggestedClipboardSize = false;
|
||||
};
|
||||
Reference in new issue
Block a user