Skip to content

Commit 875f8fa

Browse files
committed
feat(page_layout): inline source DPI fields in Margins panel (#93)
Add Source DPI combo and X/Y fields to the Margins panel so users can fix per-image DPI without opening the bulk Fix DPI dialog. Values commit on editingFinished; keyboard focus is preserved when switching pages while a DPI field is active. The existing button opens Fix all… for bulk edits.
1 parent dc40f86 commit 875f8fa

4 files changed

Lines changed: 254 additions & 7 deletions

File tree

src/core/filters/page_layout/Filter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
namespace page_layout {
2727
Filter::Filter(std::shared_ptr<ProjectPages> pages, const PageSelectionAccessor& pageSelectionAccessor)
2828
: m_pages(std::move(pages)), m_settings(std::make_shared<Settings>()), m_selectedPageOrder(0) {
29-
m_optionsWidget.reset(new OptionsWidget(m_settings, pageSelectionAccessor));
29+
m_optionsWidget.reset(new OptionsWidget(m_settings, m_pages, pageSelectionAccessor));
3030

3131
const PageOrderOption::ProviderPtr defaultOrder;
3232
const auto orderByWidth = std::make_shared<OrderByWidthProvider>(m_settings);

src/core/filters/page_layout/OptionsWidget.cpp

Lines changed: 180 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,34 @@
33

44
#include "OptionsWidget.h"
55

6+
#include <ColorSchemeManager.h>
67
#include <UnitsProvider.h>
78
#include <core/IconProvider.h>
89

10+
#include <QIntValidator>
11+
#include <QLineEdit>
912
#include <QSettings>
1013
#include <utility>
1114

1215
#include "../../Utils.h"
1316
#include "ApplyDialog.h"
1417
#include "ApplyMarginsDialog.h"
18+
#include "ProjectPages.h"
1519
#include "Settings.h"
1620

1721
using namespace core;
1822

1923
namespace page_layout {
20-
OptionsWidget::OptionsWidget(std::shared_ptr<Settings> settings, const PageSelectionAccessor& pageSelectionAccessor)
24+
OptionsWidget::OptionsWidget(std::shared_ptr<Settings> settings,
25+
std::shared_ptr<ProjectPages> pages,
26+
const PageSelectionAccessor& pageSelectionAccessor)
2127
: m_settings(std::move(settings)),
28+
m_pages(std::move(pages)),
2229
m_pageSelectionAccessor(pageSelectionAccessor),
2330
m_leftRightLinked(true),
2431
m_topBottomLinked(true),
25-
m_connectionManager(std::bind(&OptionsWidget::setupUiConnections, this)) {
32+
m_connectionManager(std::bind(&OptionsWidget::setupUiConnections, this)),
33+
m_sourceDpiFocusWidget(nullptr) {
2634
{
2735
QSettings appSettings;
2836
m_leftRightLinked = appSettings.value("margins/leftRightLinked", true).toBool();
@@ -31,6 +39,7 @@ OptionsWidget::OptionsWidget(std::shared_ptr<Settings> settings, const PageSelec
3139

3240
setupUi(this);
3341
setupIcons();
42+
setupSourceDpiControls();
3443

3544
updateLinkDisplay(topBottomLink, m_topBottomLinked);
3645
updateLinkDisplay(leftRightLink, m_leftRightLinked);
@@ -57,10 +66,14 @@ OptionsWidget::OptionsWidget(std::shared_ptr<Settings> settings, const PageSelec
5766
OptionsWidget::~OptionsWidget() = default;
5867

5968
void OptionsWidget::preUpdateUI(const PageInfo& pageInfo, const Margins& marginsMm, const Alignment& alignment) {
69+
commitSourceDpiIfValid();
70+
m_sourceDpiFocusWidget = isSourceDpiFieldFocused() ? focusWidget() : nullptr;
71+
6072
auto block = m_connectionManager.getScopedBlock();
6173

6274
m_pageId = pageInfo.id();
6375
m_dpi = pageInfo.metadata().dpi();
76+
m_sourceImagePixelSize = pageInfo.metadata().size();
6477
m_marginsMM = marginsMm;
6578
m_alignment = alignment;
6679

@@ -111,6 +124,7 @@ void OptionsWidget::preUpdateUI(const PageInfo& pageInfo, const Margins& margins
111124

112125
marginsGroup->setEnabled(false);
113126
alignmentGroup->setEnabled(false);
127+
keepSourceDpiFieldsEnabled();
114128

115129
onUnitsChanged(UnitsProvider::getInstance().getUnits());
116130
} // OptionsWidget::preUpdateUI
@@ -123,6 +137,12 @@ void OptionsWidget::postUpdateUI() {
123137

124138
m_marginsMM = m_settings->getHardMarginsMM(m_pageId);
125139
updateMarginsDisplay();
140+
updateSourceDpiDisplay();
141+
142+
if (m_sourceDpiFocusWidget) {
143+
m_sourceDpiFocusWidget->setFocus(Qt::OtherFocusReason);
144+
m_sourceDpiFocusWidget = nullptr;
145+
}
126146
}
127147

128148
void OptionsWidget::marginsSetExternally(const Margins& marginsMm) {
@@ -441,6 +461,9 @@ void OptionsWidget::setupUiConnections() {
441461
CONNECT(leftRightLink, SIGNAL(clicked()), this, SLOT(leftRightLinkClicked()));
442462
CONNECT(applyMarginsBtn, SIGNAL(clicked()), this, SLOT(showApplyMarginsDialog()));
443463
CONNECT(fixDpiBtn, SIGNAL(clicked()), this, SLOT(onFixDpiClicked()));
464+
CONNECT(sourceDpiCombo, SIGNAL(activated(int)), this, SLOT(sourceDpiComboActivated(int)));
465+
CONNECT(sourceXDpi, SIGNAL(editingFinished()), this, SLOT(sourceDpiEditingFinished()));
466+
CONNECT(sourceYDpi, SIGNAL(editingFinished()), this, SLOT(sourceDpiEditingFinished()));
444467
CONNECT(alignWithOthersCB, SIGNAL(toggled(bool)), this, SLOT(alignWithOthersToggled()));
445468
CONNECT(applyAlignmentBtn, SIGNAL(clicked()), this, SLOT(showApplyAlignmentDialog()));
446469
CONNECT(matchSizeToAllBtn, SIGNAL(clicked()), this, SLOT(matchSizeToAllPages()));
@@ -532,6 +555,160 @@ void OptionsWidget::setupIcons() {
532555
}
533556

534557
void OptionsWidget::onFixDpiClicked() {
535-
emit fixDpiRequested(); // MainWindow opens FixDpiDialog (issue #93).
558+
emit fixDpiRequested();
559+
}
560+
561+
void OptionsWidget::setupSourceDpiControls() {
562+
sourceDpiCombo->addItem(tr("Custom"), QVariant());
563+
sourceDpiCombo->addItem(QStringLiteral("300 x 300"), QSize(300, 300));
564+
sourceDpiCombo->addItem(QStringLiteral("400 x 400"), QSize(400, 400));
565+
sourceDpiCombo->addItem(QStringLiteral("600 x 600"), QSize(600, 600));
566+
sourceDpiCombo->addItem(QStringLiteral("1200 x 1200"), QSize(1200, 1200));
567+
568+
sourceXDpi->setMaxLength(4);
569+
sourceYDpi->setMaxLength(4);
570+
sourceXDpi->setValidator(new QIntValidator(sourceXDpi));
571+
sourceYDpi->setValidator(new QIntValidator(sourceYDpi));
572+
573+
m_sourceDpiNormalPalette = sourceXDpi->palette();
574+
m_sourceDpiErrorPalette = m_sourceDpiNormalPalette;
575+
const QColor errorColor(ColorSchemeManager::instance().getColorParam("FixDpiDialogErrorText", QColor(Qt::red)));
576+
m_sourceDpiErrorPalette.setColor(QPalette::Text, errorColor);
577+
}
578+
579+
void OptionsWidget::updateSourceDpiDisplay() {
580+
auto block = m_connectionManager.getScopedBlock();
581+
582+
if (m_dpi.isNull()) {
583+
sourceXDpi->clear();
584+
sourceYDpi->clear();
585+
} else {
586+
sourceXDpi->setText(QString::number(m_dpi.horizontal()));
587+
sourceYDpi->setText(QString::number(m_dpi.vertical()));
588+
}
589+
590+
updateSourceDpiComboFromFields();
591+
592+
const ImageMetadata metadata(m_sourceImagePixelSize, m_dpi);
593+
decorateSourceDpiField(sourceXDpi, metadata.horizontalDpiStatus());
594+
decorateSourceDpiField(sourceYDpi, metadata.verticalDpiStatus());
595+
}
596+
597+
void OptionsWidget::commitSourceDpiIfValid() {
598+
if (m_pageId.isNull() || !m_pages) {
599+
return;
600+
}
601+
602+
bool xOk = false;
603+
bool yOk = false;
604+
const int horizontalDpi = sourceXDpi->text().toInt(&xOk);
605+
const int verticalDpi = sourceYDpi->text().toInt(&yOk);
606+
if (!xOk || !yOk) {
607+
return;
608+
}
609+
610+
const Dpi dpi(horizontalDpi, verticalDpi);
611+
if (dpi == m_dpi) {
612+
return;
613+
}
614+
615+
const ImageMetadata updated(m_sourceImagePixelSize, dpi);
616+
if (!updated.isDpiOK()) {
617+
return;
618+
}
619+
620+
m_pages->updateImageMetadata(m_pageId.imageId(), updated);
621+
m_dpi = dpi;
622+
emit invalidateAllThumbnails();
623+
emit reloadRequested();
624+
updateMarginsDisplay();
625+
}
626+
627+
void OptionsWidget::decorateSourceDpiField(QLineEdit* field, const ImageMetadata::DpiStatus dpiStatus) {
628+
if (dpiStatus == ImageMetadata::DPI_OK) {
629+
field->setPalette(m_sourceDpiNormalPalette);
630+
field->setToolTip(QString());
631+
return;
632+
}
633+
634+
field->setPalette(m_sourceDpiErrorPalette);
635+
switch (dpiStatus) {
636+
case ImageMetadata::DPI_TOO_LARGE:
637+
field->setToolTip(tr("DPI is too large and most likely wrong."));
638+
break;
639+
case ImageMetadata::DPI_TOO_SMALL:
640+
field->setToolTip(
641+
tr("DPI is too small. Even if it's correct, you are not going to get acceptable results with it."));
642+
break;
643+
case ImageMetadata::DPI_TOO_SMALL_FOR_THIS_PIXEL_SIZE:
644+
field->setToolTip(
645+
tr("An extremely low DPI value. That might correspond to a very large paper size for the pixel size in "
646+
"question."));
647+
break;
648+
default:
649+
field->setToolTip(QString());
650+
break;
651+
}
652+
}
653+
654+
void OptionsWidget::updateSourceDpiComboFromFields() {
655+
bool xOk = false;
656+
bool yOk = false;
657+
const QSize dpi(sourceXDpi->text().toInt(&xOk), sourceYDpi->text().toInt(&yOk));
658+
659+
if (xOk && yOk) {
660+
const int count = sourceDpiCombo->count();
661+
for (int i = 0; i < count; ++i) {
662+
const QVariant data(sourceDpiCombo->itemData(i));
663+
if (data.isValid() && (dpi == data.toSize())) {
664+
sourceDpiCombo->setCurrentIndex(i);
665+
return;
666+
}
667+
}
668+
}
669+
670+
sourceDpiCombo->setCurrentIndex(0);
671+
}
672+
673+
void OptionsWidget::keepSourceDpiFieldsEnabled() {
674+
sourceDpiLabel->setEnabled(true);
675+
sourceDpiCombo->setEnabled(true);
676+
sourceDpiTimesLabel->setEnabled(true);
677+
sourceXDpi->setEnabled(true);
678+
sourceYDpi->setEnabled(true);
679+
fixDpiBtn->setEnabled(true);
680+
}
681+
682+
bool OptionsWidget::isSourceDpiFieldFocused() const {
683+
const QWidget* const focused = focusWidget();
684+
return (focused == sourceXDpi) || (focused == sourceYDpi) || (focused == sourceDpiCombo);
685+
}
686+
687+
void OptionsWidget::sourceDpiComboActivated(const int index) {
688+
const QVariant data(sourceDpiCombo->itemData(index));
689+
if (!data.isValid()) {
690+
return;
691+
}
692+
693+
const QSize dpi(data.toSize());
694+
sourceXDpi->setText(QString::number(dpi.width()));
695+
sourceYDpi->setText(QString::number(dpi.height()));
696+
sourceDpiEditingFinished();
697+
}
698+
699+
void OptionsWidget::sourceDpiEditingFinished() {
700+
updateSourceDpiComboFromFields();
701+
702+
bool xOk = false;
703+
bool yOk = false;
704+
const int horizontalDpi = sourceXDpi->text().toInt(&xOk);
705+
const int verticalDpi = sourceYDpi->text().toInt(&yOk);
706+
if (xOk && yOk) {
707+
const ImageMetadata metadata(m_sourceImagePixelSize, Dpi(horizontalDpi, verticalDpi));
708+
decorateSourceDpiField(sourceXDpi, metadata.horizontalDpiStatus());
709+
decorateSourceDpiField(sourceYDpi, metadata.verticalDpiStatus());
710+
}
711+
712+
commitSourceDpiIfValid();
536713
}
537714
} // namespace page_layout

src/core/filters/page_layout/OptionsWidget.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,34 @@
88
#include <core/ConnectionManager.h>
99

1010
#include <QIcon>
11-
#include <list>
11+
#include <QPalette>
12+
#include <QSize>
13+
#include <QWidget>
1214
#include <memory>
1315
#include <set>
1416
#include <unordered_map>
1517

1618
#include "Alignment.h"
1719
#include "FilterOptionsWidget.h"
20+
#include "ImageMetadata.h"
1821
#include "Margins.h"
1922
#include "PageId.h"
2023
#include "PageSelectionAccessor.h"
2124
#include "ui_OptionsWidget.h"
2225

2326
class QToolButton;
2427
class ProjectPages;
28+
class QLineEdit;
2529

2630
namespace page_layout {
2731
class Settings;
2832

2933
class OptionsWidget : public FilterOptionsWidget, public UnitsListener, private Ui::OptionsWidget {
3034
Q_OBJECT
3135
public:
32-
OptionsWidget(std::shared_ptr<Settings> settings, const PageSelectionAccessor& pageSelectionAccessor);
36+
OptionsWidget(std::shared_ptr<Settings> settings,
37+
std::shared_ptr<ProjectPages> pages,
38+
const PageSelectionAccessor& pageSelectionAccessor);
3339

3440
~OptionsWidget() override;
3541

@@ -91,6 +97,10 @@ class OptionsWidget : public FilterOptionsWidget, public UnitsListener, private
9197

9298
void onFixDpiClicked();
9399

100+
void sourceDpiComboActivated(int index);
101+
102+
void sourceDpiEditingFinished();
103+
94104
void applyMargins(const std::set<PageId>& pages,
95105
bool applyLeft = true,
96106
bool applyRight = true,
@@ -120,13 +130,32 @@ class OptionsWidget : public FilterOptionsWidget, public UnitsListener, private
120130

121131
void setupIcons();
122132

133+
void setupSourceDpiControls();
134+
135+
void updateSourceDpiDisplay();
136+
137+
void commitSourceDpiIfValid();
138+
139+
void decorateSourceDpiField(QLineEdit* field, ImageMetadata::DpiStatus dpiStatus);
140+
141+
void updateSourceDpiComboFromFields();
142+
143+
void keepSourceDpiFieldsEnabled();
144+
145+
bool isSourceDpiFieldFocused() const;
146+
123147
std::shared_ptr<Settings> m_settings;
148+
std::shared_ptr<ProjectPages> m_pages;
124149
PageSelectionAccessor m_pageSelectionAccessor;
125150
QIcon m_chainIcon;
126151
QIcon m_brokenChainIcon;
127152
AlignmentByButton m_alignmentByButton;
128153
PageId m_pageId;
129154
Dpi m_dpi;
155+
QSize m_sourceImagePixelSize;
156+
QPalette m_sourceDpiNormalPalette;
157+
QPalette m_sourceDpiErrorPalette;
158+
QWidget* m_sourceDpiFocusWidget;
130159
Margins m_marginsMM;
131160
Alignment m_alignment;
132161
bool m_leftRightLinked;

src/core/filters/page_layout/OptionsWidget.ui

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,47 @@ QToolButton:pressed {
276276
</item>
277277
</layout>
278278
</item>
279+
<item>
280+
<layout class="QHBoxLayout" name="sourceDpiLayout">
281+
<item>
282+
<widget class="QLabel" name="sourceDpiLabel">
283+
<property name="text">
284+
<string>Source DPI</string>
285+
</property>
286+
</widget>
287+
</item>
288+
<item>
289+
<widget class="QComboBox" name="sourceDpiCombo"/>
290+
</item>
291+
<item>
292+
<widget class="QLineEdit" name="sourceXDpi">
293+
<property name="maximumSize">
294+
<size>
295+
<width>40</width>
296+
<height>16777215</height>
297+
</size>
298+
</property>
299+
</widget>
300+
</item>
301+
<item>
302+
<widget class="QLabel" name="sourceDpiTimesLabel">
303+
<property name="text">
304+
<string notr="true">×</string>
305+
</property>
306+
</widget>
307+
</item>
308+
<item>
309+
<widget class="QLineEdit" name="sourceYDpi">
310+
<property name="maximumSize">
311+
<size>
312+
<width>40</width>
313+
<height>16777215</height>
314+
</size>
315+
</property>
316+
</widget>
317+
</item>
318+
</layout>
319+
</item>
279320
<item>
280321
<layout class="QHBoxLayout" name="horizontalLayout_5">
281322
<item>
@@ -301,7 +342,7 @@ QToolButton:pressed {
301342
<item>
302343
<widget class="QPushButton" name="fixDpiBtn">
303344
<property name="text">
304-
<string>Fix DPI...</string>
345+
<string>Fix all...</string>
305346
</property>
306347
</widget>
307348
</item>

0 commit comments

Comments
 (0)