Export JPEG and Canvas Size take the app's colour picker; a panel opens over a modal sheet (13.4a)
This commit is contained in:
1 parent
0b7eb6cb6f
commit
bc49b722eb
23 files changed
+470
-134
No files matched your search
+16
-25
@@ -2,12 +2,11 @@
|
||||
#include "Document/DocumentLimits.h"
|
||||
#include "UI/KeyboardShortcuts.h"
|
||||
#include "UI/ByteCounts.h"
|
||||
#include "UI/ColorPaletteControls.h"
|
||||
#include "UI/ColorPickerSheet+Dialog.h"
|
||||
#include "UI/ColorPickerSheet.h"
|
||||
#include "UI/NumericScrub.h"
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
#include <QColorDialog>
|
||||
#include <QComboBox>
|
||||
#include <QFrame>
|
||||
#include <QGridLayout>
|
||||
@@ -109,13 +108,14 @@ protected:
|
||||
};
|
||||
}
|
||||
|
||||
CanvasSizeSheet::CanvasSizeSheet(const CanvasDocument &document, PaletteColor foreground, PaletteColor background,
|
||||
std::function<void(std::optional<CanvasSizeOptions>)> finish, QWidget *parent)
|
||||
: QWidget(parent), m_foreground(foreground), m_background(background), m_finish(std::move(finish)),
|
||||
CanvasSizeSheet::CanvasSizeSheet(const CanvasDocument &document, EditorSession &session, std::function<void(std::optional<CanvasSizeOptions>)> finish,
|
||||
QWidget *parent)
|
||||
: QWidget(parent), m_session(session), m_foreground(session.foregroundColor()), m_background(session.backgroundColor()), m_finish(std::move(finish)),
|
||||
m_draft(document.width, document.height, document.resolution), m_width(dimension(true)), m_height(dimension(false)),
|
||||
m_note(text(QString(), 12, QFont::Normal, QPalette::PlaceholderText, this)),
|
||||
m_anchors(new QButtonGroup(this)), m_anchorName(text(QString(), 12, QFont::Bold, QPalette::WindowText, this)), m_extension(new QComboBox(this)),
|
||||
m_customRow(new QWidget(this)), m_customSwatch(new SwatchButton([this] { return m_custom; }, 3, 0, 0.5, m_customRow)),
|
||||
m_customRow(new QWidget(this)), m_customSwatch(new DialogColorSwatch(
|
||||
QStringLiteral("Extension Color"), [this] { return m_custom; }, [this](const PaletteColor &custom) { m_custom = custom; }, session, m_customRow)),
|
||||
m_ok(new QPushButton(QStringLiteral("OK"), this))
|
||||
{
|
||||
setFixedWidth(450);
|
||||
@@ -224,14 +224,13 @@ CanvasSizeSheet::CanvasSizeSheet(const CanvasDocument &document, PaletteColor fo
|
||||
extension->addWidget(extensionLabel);
|
||||
extension->addWidget(m_extension, 1);
|
||||
column->addLayout(extension);
|
||||
// Swift's ColorPicker: its label and a well, no opacity.
|
||||
// Its label and the app's picker on a swatch.
|
||||
m_customRow->setObjectName(QStringLiteral("extensionColorRow"));
|
||||
m_customSwatch->setObjectName(QStringLiteral("extensionColor"));
|
||||
m_customSwatch->setFixedSize(36, 18);
|
||||
m_customSwatch->setAccessibleName(QStringLiteral("Extension color"));
|
||||
connect(m_customSwatch, &QAbstractButton::clicked, this, &CanvasSizeSheet::pickCustomColor);
|
||||
m_customSwatch->setToolTip(QStringLiteral("Color for the added canvas"));
|
||||
auto *customLayout = new QHBoxLayout(m_customRow);
|
||||
customLayout->setContentsMargins(0, 0, 0, 0);
|
||||
customLayout->setSpacing(8);
|
||||
customLayout->addWidget(new QLabel(QStringLiteral("Extension color"), m_customRow));
|
||||
customLayout->addWidget(m_customSwatch);
|
||||
customLayout->addStretch(1);
|
||||
@@ -240,13 +239,18 @@ CanvasSizeSheet::CanvasSizeSheet(const CanvasDocument &document, PaletteColor fo
|
||||
auto *cancel = new QPushButton(QStringLiteral("Cancel"), this);
|
||||
cancel->setObjectName(QStringLiteral("canvasCancel"));
|
||||
cancel->setAutoDefault(false);
|
||||
connect(cancel, &QPushButton::clicked, this, [this] { m_finish(std::nullopt); });
|
||||
connect(cancel, &QPushButton::clicked, this, [this] {
|
||||
DialogColorSwatch::closePicker(m_session);
|
||||
m_finish(std::nullopt);
|
||||
});
|
||||
m_ok->setObjectName(QStringLiteral("canvasOK"));
|
||||
m_ok->setDefault(true);
|
||||
// Swift's configuredNativeShortcut: Return and Escape, as remapped.
|
||||
NativeShortcut::bind(*this, m_ok, cancel);
|
||||
// OK rests while the draft is invalid: no guard needed.
|
||||
connect(m_ok, &QPushButton::clicked, this, [this] {
|
||||
// An open picker's colour counts: it closes first.
|
||||
DialogColorSwatch::closePicker(m_session);
|
||||
m_finish(CanvasSizeOptions{.width = qint64(std::round(m_draft.width)), .height = qint64(std::round(m_draft.height)), .anchor = m_anchor,
|
||||
.fill = fill()});
|
||||
});
|
||||
@@ -300,20 +304,6 @@ std::optional<CanvasExtensionColor> CanvasSizeSheet::fill() const
|
||||
}
|
||||
}
|
||||
|
||||
// The desktop's colour dialog, SwiftUI's colour panel, without alpha.
|
||||
void CanvasSizeSheet::pickCustomColor()
|
||||
{
|
||||
auto *dialog = new QColorDialog(m_custom.color(), this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->setWindowTitle(QStringLiteral("Extension color"));
|
||||
connect(dialog, &QColorDialog::colorSelected, this, [this](const QColor &picked) {
|
||||
const QColor srgb = picked.toRgb();
|
||||
m_custom = PaletteColor{srgb.redF(), srgb.greenF(), srgb.blueF()};
|
||||
m_customSwatch->update();
|
||||
});
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
// Units, Relative and Lock change only through their own controls.
|
||||
void CanvasSizeSheet::synchronize()
|
||||
{
|
||||
@@ -347,4 +337,5 @@ void CanvasSizeSheet::synchronize()
|
||||
CanvasSizeSheet::~CanvasSizeSheet()
|
||||
{
|
||||
releaseFocus(*this);
|
||||
DialogColorSwatch::closePicker(m_session);
|
||||
}
|
||||
@@ -12,14 +12,15 @@ class QButtonGroup;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class SwatchButton;
|
||||
class DialogColorSwatch;
|
||||
class EditorSession;
|
||||
|
||||
// Swift's CanvasSizeSheet: the new size, the point kept, the extension.
|
||||
class CanvasSizeSheet : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CanvasSizeSheet(const CanvasDocument &document, PaletteColor foreground, PaletteColor background,
|
||||
std::function<void(std::optional<CanvasSizeOptions>)> finish, QWidget *parent = nullptr);
|
||||
CanvasSizeSheet(const CanvasDocument &document, EditorSession &session, std::function<void(std::optional<CanvasSizeOptions>)> finish,
|
||||
QWidget *parent = nullptr);
|
||||
~CanvasSizeSheet() override;
|
||||
|
||||
private:
|
||||
@@ -27,9 +28,9 @@ private:
|
||||
// Swift's scrubbable Width or Height title.
|
||||
NumericScrub *scrub(QLabel *title, bool widthAxis);
|
||||
std::optional<CanvasExtensionColor> fill() const;
|
||||
void pickCustomColor();
|
||||
void synchronize();
|
||||
|
||||
EditorSession &m_session;
|
||||
const PaletteColor m_foreground;
|
||||
const PaletteColor m_background;
|
||||
const std::function<void(std::optional<CanvasSizeOptions>)> m_finish;
|
||||
@@ -45,6 +46,6 @@ private:
|
||||
QLabel *const m_anchorName;
|
||||
QComboBox *const m_extension;
|
||||
QWidget *const m_customRow;
|
||||
SwatchButton *const m_customSwatch;
|
||||
DialogColorSwatch *const m_customSwatch;
|
||||
QPushButton *const m_ok;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "UI/ColorPickerSheet+Dialog.h"
|
||||
#include <QHideEvent>
|
||||
|
||||
// The brush's swatch: 34 by 18, a white ring.
|
||||
DialogColorSwatch::DialogColorSwatch(const QString &title, std::function<PaletteColor()> colour, std::function<void(const PaletteColor &)> change,
|
||||
EditorSession &session, QWidget *parent)
|
||||
: SwatchButton(colour, 4, 1, 1, parent), m_session(session)
|
||||
{
|
||||
setFixedSize(34, 18);
|
||||
setAccessibleName(title);
|
||||
connect(this, &QAbstractButton::clicked, this, [this, title, colour, change, &session] {
|
||||
session.openDialogColorPicker(title, colour(), [this, change](const PaletteColor &picked) {
|
||||
change(picked);
|
||||
update();
|
||||
});
|
||||
});
|
||||
connect(&session, &EditorSession::changed, this, [this, &session] {
|
||||
const std::optional<PaletteColor> now = session.colorPicker() ? std::optional(session.colorPicker()->color()) : std::nullopt;
|
||||
if (now == m_seen)
|
||||
return;
|
||||
m_seen = now;
|
||||
session.previewDialogColor();
|
||||
});
|
||||
}
|
||||
|
||||
void DialogColorSwatch::hideEvent(QHideEvent *event)
|
||||
{
|
||||
if (!event->spontaneous())
|
||||
closePicker(m_session);
|
||||
SwatchButton::hideEvent(event);
|
||||
}
|
||||
|
||||
void DialogColorSwatch::closePicker(EditorSession &session)
|
||||
{
|
||||
if (session.pickingForDialog())
|
||||
session.closeColorPicker(true);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "Document/EditorSession.h"
|
||||
#include "UI/ColorPaletteControls.h"
|
||||
|
||||
// Swift's DialogColorSwatch: a dialog's colour in the app's picker.
|
||||
class DialogColorSwatch : public SwatchButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DialogColorSwatch(const QString &title, std::function<PaletteColor()> colour, std::function<void(const PaletteColor &)> change, EditorSession &session,
|
||||
QWidget *parent);
|
||||
// Puts the picker away with the dialog, keeping its colour.
|
||||
static void closePicker(EditorSession &session);
|
||||
|
||||
protected:
|
||||
// Swift's onDisappear: the picker goes with the swatch.
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
|
||||
private:
|
||||
EditorSession &m_session;
|
||||
// Swift's onChange: the picker's colour as last seen.
|
||||
std::optional<PaletteColor> m_seen;
|
||||
};
|
||||
@@ -235,19 +235,23 @@ ColorPickerSheet::ColorPickerSheet(EditorSession &session, std::function<void(bo
|
||||
m_hex->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
fields->addWidget(label(QStringLiteral("#"), this), 3, 0);
|
||||
fields->addWidget(m_hex, 3, 1, Qt::AlignLeft);
|
||||
auto *hint = new QLabel(QStringLiteral("Click the canvas to sample"), this);
|
||||
QFont small = hint->font();
|
||||
small.setPixelSize(10);
|
||||
hint->setFont(small);
|
||||
hint->setForegroundRole(QPalette::PlaceholderText);
|
||||
auto *column = new QVBoxLayout;
|
||||
column->setSpacing(0);
|
||||
column->addLayout(top);
|
||||
column->addStretch();
|
||||
column->addSpacing(12);
|
||||
column->addLayout(fields);
|
||||
column->addSpacing(8);
|
||||
column->addWidget(hint);
|
||||
// A dialog covers the canvas: nothing to sample.
|
||||
if (!m_session.pickingForDialog()) {
|
||||
auto *hint = new QLabel(QStringLiteral("Click the canvas to sample"), this);
|
||||
hint->setObjectName(QStringLiteral("pickerHint"));
|
||||
QFont small = hint->font();
|
||||
small.setPixelSize(10);
|
||||
hint->setFont(small);
|
||||
hint->setForegroundRole(QPalette::PlaceholderText);
|
||||
column->addSpacing(8);
|
||||
column->addWidget(hint);
|
||||
}
|
||||
auto *side = new QWidget(this);
|
||||
side->setFixedSize(180, int(fieldSize));
|
||||
side->setLayout(column);
|
||||
|
||||
@@ -65,8 +65,12 @@ void FloatingPanel::show(const QString &title, QWidget *content, QWidget *dock)
|
||||
showDocked(title, content, *dock);
|
||||
return;
|
||||
}
|
||||
// A modal sheet blocks its window's panels, not its own.
|
||||
QWidget *parent = QApplication::activeModalWidget() ? QApplication::activeModalWidget() : m_owner.window();
|
||||
if (m_panel && m_panel->parentWidget() != parent)
|
||||
m_panel->setParent(parent, m_panel->windowFlags());
|
||||
if (!m_panel) {
|
||||
m_panel = new PanelWindow(*this, m_owner.window());
|
||||
m_panel = new PanelWindow(*this, parent);
|
||||
m_panel->setObjectName(m_name);
|
||||
}
|
||||
const bool wasVisible = m_panel->isVisible();
|
||||
|
||||
+25
-29
@@ -1,8 +1,7 @@
|
||||
#include "UI/JPEGExportSheet.h"
|
||||
#include "UI/KeyboardShortcuts.h"
|
||||
#include "UI/ByteCounts.h"
|
||||
#include "UI/ColorPaletteControls.h"
|
||||
#include <QColorDialog>
|
||||
#include "UI/ColorPickerSheet+Dialog.h"
|
||||
#include <QFutureWatcher>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
@@ -83,10 +82,12 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
JPEGExportSheet::JPEGExportSheet(ExportRaster raster, std::function<void(std::optional<QByteArray>)> finish, QWidget *parent)
|
||||
: QWidget(parent), m_raster(std::move(raster)), m_finish(std::move(finish)), m_wait(new QTimer(this)), m_preview(new Preview(this)),
|
||||
JPEGExportSheet::JPEGExportSheet(ExportRaster raster, EditorSession &session, std::function<void(std::optional<QByteArray>)> finish, QWidget *parent)
|
||||
: QWidget(parent), m_raster(std::move(raster)), m_session(session), m_finish(std::move(finish)), m_wait(new QTimer(this)), m_preview(new Preview(this)),
|
||||
m_spinner(new QProgressBar(m_preview)), m_quality(new QSlider(Qt::Horizontal, this)),
|
||||
m_percent(text(QString(), 13, QFont::Normal, QPalette::WindowText, this)), m_matteSwatch(new SwatchButton([this] { return PaletteColor{m_options.red, m_options.green, m_options.blue}; }, 3, 0, 0.5, this)),
|
||||
m_percent(text(QString(), 13, QFont::Normal, QPalette::WindowText, this)), m_matteSwatch(new DialogColorSwatch(
|
||||
QStringLiteral("JPEG Background"), [this] { return PaletteColor{m_options.red, m_options.green, m_options.blue}; },
|
||||
[this](const PaletteColor &matte) { setMatte(matte); }, session, this)),
|
||||
m_failure(text(QString(), 13, QFont::Normal, QPalette::BrightText, this)), m_bytes(text(QString(), 13, QFont::Normal, QPalette::WindowText, this)),
|
||||
m_note(text(QString(), 13, QFont::Normal, QPalette::PlaceholderText, this)), m_export(new QPushButton(QStringLiteral("Export…"), this))
|
||||
{
|
||||
@@ -122,13 +123,12 @@ JPEGExportSheet::JPEGExportSheet(ExportRaster raster, std::function<void(std::op
|
||||
quality->addWidget(m_percent);
|
||||
column->addLayout(quality);
|
||||
|
||||
// Swift's ColorPicker: its label and a well, no opacity.
|
||||
// Its label and the app's picker on a swatch.
|
||||
auto *matte = new QHBoxLayout;
|
||||
matte->setSpacing(8);
|
||||
matte->addWidget(new QLabel(QStringLiteral("Background for transparency"), this));
|
||||
m_matteSwatch->setObjectName(QStringLiteral("jpegMatte"));
|
||||
m_matteSwatch->setFixedSize(36, 18);
|
||||
m_matteSwatch->setAccessibleName(QStringLiteral("Background for transparency"));
|
||||
connect(m_matteSwatch, &QAbstractButton::clicked, this, &JPEGExportSheet::pickMatte);
|
||||
m_matteSwatch->setToolTip(QStringLiteral("Color that fills transparent areas"));
|
||||
matte->addWidget(m_matteSwatch);
|
||||
matte->addStretch(1);
|
||||
column->addLayout(matte);
|
||||
@@ -146,13 +146,17 @@ JPEGExportSheet::JPEGExportSheet(ExportRaster raster, std::function<void(std::op
|
||||
auto *cancel = new QPushButton(QStringLiteral("Cancel"), this);
|
||||
cancel->setObjectName(QStringLiteral("jpegCancel"));
|
||||
cancel->setAutoDefault(false);
|
||||
connect(cancel, &QPushButton::clicked, this, [this] { m_finish(std::nullopt); });
|
||||
connect(cancel, &QPushButton::clicked, this, [this] {
|
||||
DialogColorSwatch::closePicker(m_session);
|
||||
m_finish(std::nullopt);
|
||||
});
|
||||
m_export->setObjectName(QStringLiteral("jpegExport"));
|
||||
m_export->setDefault(true);
|
||||
// Swift's configuredNativeShortcut: Return and Escape, as remapped.
|
||||
NativeShortcut::bind(*this, m_export, cancel);
|
||||
// Export rests until a result is ready.
|
||||
connect(m_export, &QPushButton::clicked, this, [this] {
|
||||
DialogColorSwatch::closePicker(m_session);
|
||||
QSettings().setValue(qualityKey, m_options.quality);
|
||||
m_finish(m_result.value().data);
|
||||
});
|
||||
@@ -172,6 +176,7 @@ JPEGExportSheet::JPEGExportSheet(ExportRaster raster, std::function<void(std::op
|
||||
JPEGExportSheet::~JPEGExportSheet()
|
||||
{
|
||||
m_cancelled->store(true);
|
||||
DialogColorSwatch::closePicker(m_session);
|
||||
}
|
||||
|
||||
// Swift's task(id:): the last request stops; this waits 200 ms.
|
||||
@@ -215,26 +220,17 @@ void JPEGExportSheet::encode()
|
||||
}));
|
||||
}
|
||||
|
||||
// The desktop's colour dialog, SwiftUI's colour panel, without alpha.
|
||||
void JPEGExportSheet::pickMatte()
|
||||
void JPEGExportSheet::setMatte(const PaletteColor &matte)
|
||||
{
|
||||
auto *dialog = new QColorDialog(QColor::fromRgbF(float(m_options.red), float(m_options.green), float(m_options.blue)), this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->setWindowTitle(QStringLiteral("Background for transparency"));
|
||||
connect(dialog, &QColorDialog::colorSelected, this, [this](const QColor &picked) {
|
||||
const QColor srgb = picked.toRgb();
|
||||
JPEGOptions next = m_options;
|
||||
next.red = srgb.redF();
|
||||
next.green = srgb.greenF();
|
||||
next.blue = srgb.blueF();
|
||||
// Swift's onChange: the same colour asks nothing.
|
||||
if (next == m_options)
|
||||
return;
|
||||
m_options = next;
|
||||
m_matteSwatch->update();
|
||||
request();
|
||||
});
|
||||
dialog->open();
|
||||
JPEGOptions next = m_options;
|
||||
next.red = matte.red;
|
||||
next.green = matte.green;
|
||||
next.blue = matte.blue;
|
||||
// Equal options ask nothing, as Swift's task id.
|
||||
if (next == m_options)
|
||||
return;
|
||||
m_options = next;
|
||||
request();
|
||||
}
|
||||
|
||||
void JPEGExportSheet::synchronize()
|
||||
|
||||
@@ -12,7 +12,8 @@ class QProgressBar;
|
||||
class QPushButton;
|
||||
class QSlider;
|
||||
class QTimer;
|
||||
class SwatchButton;
|
||||
class DialogColorSwatch;
|
||||
class EditorSession;
|
||||
|
||||
// Swift's JPEGExportSheet: quality, matte and the encoded preview.
|
||||
class JPEGExportSheet : public QWidget {
|
||||
@@ -21,17 +22,18 @@ public:
|
||||
// The last export's quality, where the next one starts.
|
||||
static const QString qualityKey;
|
||||
|
||||
JPEGExportSheet(ExportRaster raster, std::function<void(std::optional<QByteArray>)> finish, QWidget *parent = nullptr);
|
||||
JPEGExportSheet(ExportRaster raster, EditorSession &session, std::function<void(std::optional<QByteArray>)> finish, QWidget *parent = nullptr);
|
||||
~JPEGExportSheet() override;
|
||||
|
||||
private:
|
||||
class Preview;
|
||||
void request();
|
||||
void encode();
|
||||
void pickMatte();
|
||||
void setMatte(const PaletteColor &matte);
|
||||
void synchronize();
|
||||
|
||||
const ExportRaster m_raster;
|
||||
EditorSession &m_session;
|
||||
const std::function<void(std::optional<QByteArray>)> m_finish;
|
||||
JPEGOptions m_options;
|
||||
std::optional<JPEGResult> m_result;
|
||||
@@ -44,7 +46,7 @@ private:
|
||||
QProgressBar *const m_spinner;
|
||||
QSlider *const m_quality;
|
||||
QLabel *const m_percent;
|
||||
SwatchButton *const m_matteSwatch;
|
||||
DialogColorSwatch *const m_matteSwatch;
|
||||
QLabel *const m_failure;
|
||||
QLabel *const m_bytes;
|
||||
QLabel *const m_note;
|
||||
|
||||
Reference in new issue
Block a user