Files
OmaPhoto/src/UI/NewCanvasSheet.cpp
T

146 lines
6.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "UI/NewCanvasSheet.h"
#include "UI/KeyboardShortcuts.h"
#include <QApplication>
#include <QBuffer>
#include <QClipboard>
#include <QGridLayout>
#include <QImageReader>
#include <QShortcut>
#include <QVBoxLayout>
namespace {
QLabel *text(const QString &words, int pixels, QFont::Weight weight, QPalette::ColorRole role, QWidget *parent)
{
auto *label = new QLabel(words, parent);
QFont font = label->font();
font.setPixelSize(pixels);
font.setWeight(weight);
label->setFont(font);
label->setForegroundRole(role);
return label;
}
}
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))
{
// Swift's fields stretch the sheet to its full 500 points.
setFixedWidth(500);
m_width->setObjectName(QStringLiteral("widthInput"));
m_height->setObjectName(QStringLiteral("heightInput"));
m_note->setObjectName(QStringLiteral("canvasNote"));
m_create->setObjectName(QStringLiteral("createCanvas"));
m_create->setDefault(true);
// Swift's configuredNativeShortcut: Return and Escape, as remapped.
NativeShortcut::bind(*this, m_create, nullptr);
auto *open = new QPushButton(QStringLiteral("Open project"), this);
open->setObjectName(QStringLiteral("openProject"));
auto *import = new QPushButton(QStringLiteral("Import image"), this);
import->setObjectName(QStringLiteral("importImage"));
auto *fields = new QGridLayout;
fields->setHorizontalSpacing(16);
fields->setVerticalSpacing(8);
fields->addWidget(text(QStringLiteral("Width"), 12, QFont::Medium, QPalette::WindowText, this), 0, 0);
fields->addWidget(text(QStringLiteral("Height"), 12, QFont::Medium, QPalette::WindowText, this), 0, 3);
fields->addWidget(m_width, 1, 0);
fields->addWidget(text(QStringLiteral("px"), 12, QFont::Normal, QPalette::PlaceholderText, this), 1, 1);
fields->addWidget(text(QStringLiteral("×"), 14, QFont::Normal, QPalette::PlaceholderText, this), 1, 2);
fields->addWidget(m_height, 1, 3);
fields->addWidget(text(QStringLiteral("px"), 12, QFont::Normal, QPalette::PlaceholderText, this), 1, 4);
auto *buttons = new QHBoxLayout;
buttons->setSpacing(10);
buttons->addWidget(open);
buttons->addWidget(import);
buttons->addStretch(1);
buttons->addWidget(m_create);
auto *column = new QVBoxLayout(this);
column->setContentsMargins(28, 28, 28, 28);
column->setSpacing(24);
auto *heading = new QVBoxLayout;
heading->setSpacing(6);
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));
column->addLayout(heading);
column->addLayout(fields);
column->addWidget(m_note);
column->addLayout(buttons);
connect(m_width, &QLineEdit::textChanged, this, &NewCanvasSheet::validate);
connect(m_height, &QLineEdit::textChanged, this, &NewCanvasSheet::validate);
// Return anywhere on the sheet is the default action.
std::vector<QShortcut *> returns;
for (const Qt::Key key : {Qt::Key_Return, Qt::Key_Enter})
returns.push_back(new QShortcut(QKeySequence(key), this, this, &NewCanvasSheet::create, Qt::WidgetWithChildrenShortcut));
// Until Apply is moved elsewhere, as Swift's native shortcut.
const auto follow = [returns] {
for (QShortcut *shortcut : returns)
shortcut->setEnabled(ShortcutSettings::shared().native(ShortcutChord(QStringLiteral("\r"))) == ShortcutChord(QStringLiteral("\r")));
};
connect(&ShortcutSettings::shared(), &ShortcutSettings::changed, this, follow);
follow();
connect(m_create, &QPushButton::clicked, this, &NewCanvasSheet::create);
connect(open, &QPushButton::clicked, this, [onOpen = std::move(onOpen)] { onOpen(); });
connect(import, &QPushButton::clicked, this, [this] { m_session.setShowsImporter(true); });
connect(&m_session, &EditorSession::changed, this, &NewCanvasSheet::synchronize);
synchronize();
validate();
}
void NewCanvasSheet::synchronize()
{
setEnabled(!m_session.isImporting() && !m_session.showsBusy());
}
void NewCanvasSheet::validate()
{
const bool valid = CanvasDocument::validDimension(m_width->text()) && CanvasDocument::validDimension(m_height->text());
m_note->setText(valid ? QStringLiteral("Transparent canvas · sRGB") : QStringLiteral("Enter whole numbers from 1 to 30,000 pixels."));
// The palette's bright text is the theme's warning colour.
m_note->setForegroundRole(valid ? QPalette::PlaceholderText : QPalette::BrightText);
m_create->setEnabled(valid);
}
void NewCanvasSheet::create()
{
const std::optional<int> width = CanvasDocument::validDimension(m_width->text()), height = CanvasDocument::validDimension(m_height->text());
if (width && height)
m_onCreate(*width, *height);
}
void NewCanvasSheet::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
if (!m_suggestedClipboardSize) {
m_suggestedClipboardSize = true;
if (m_session.skipsInitialClipboardCanvasSize) {
m_session.skipsInitialClipboardCanvasSize = false;
} else if (const std::optional<QSize> size = clipboardDimensions(QApplication::clipboard()->mimeData())) {
m_width->setText(QString::number(size->width()));
m_height->setText(QString::number(size->height()));
}
}
m_width->setFocus();
}
std::optional<QSize> NewCanvasSheet::clipboardDimensions(const QMimeData *clipboard)
{
for (const QString &type : {QStringLiteral("image/png"), QStringLiteral("image/tiff")}) {
QByteArray data = clipboard ? clipboard->data(type) : QByteArray();
QBuffer buffer(&data);
QImageReader reader(&buffer);
QSize size = reader.size();
// Stored on its side, a picture swaps its sides.
if (reader.transformation() & QImageIOHandler::TransformationRotate90)
size.transpose();
// In range on one side alone is no canvas.
if (CanvasDocument::validDimension(QString::number(size.width())) && CanvasDocument::validDimension(QString::number(size.height())))
return size;
}
return std::nullopt;
}