Skip to content

Commit 0b9ccf9

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 1f5c3ba commit 0b9ccf9

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

@@ -112,6 +125,7 @@ void OptionsWidget::preUpdateUI(const PageInfo& pageInfo, const Margins& margins
112125

113126
marginsGroup->setEnabled(false);
114127
alignmentGroup->setEnabled(false);
128+
keepSourceDpiFieldsEnabled();
115129

116130
onUnitsChanged(UnitsProvider::getInstance().getUnits());
117131
} // OptionsWidget::preUpdateUI
@@ -124,6 +138,12 @@ void OptionsWidget::postUpdateUI() {
124138

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

129149
void OptionsWidget::marginsSetExternally(const Margins& marginsMm) {
@@ -447,6 +467,9 @@ void OptionsWidget::setupUiConnections() {
447467
CONNECT(leftRightLink, SIGNAL(clicked()), this, SLOT(leftRightLinkClicked()));
448468
CONNECT(applyMarginsBtn, SIGNAL(clicked()), this, SLOT(showApplyMarginsDialog()));
449469
CONNECT(fixDpiBtn, SIGNAL(clicked()), this, SLOT(onFixDpiClicked()));
470+
CONNECT(sourceDpiCombo, SIGNAL(activated(int)), this, SLOT(sourceDpiComboActivated(int)));
471+
CONNECT(sourceXDpi, SIGNAL(editingFinished()), this, SLOT(sourceDpiEditingFinished()));
472+
CONNECT(sourceYDpi, SIGNAL(editingFinished()), this, SLOT(sourceDpiEditingFinished()));
450473
CONNECT(alignWithOthersCB, SIGNAL(toggled(bool)), this, SLOT(alignWithOthersToggled()));
451474
CONNECT(applyAlignmentBtn, SIGNAL(clicked()), this, SLOT(showApplyAlignmentDialog()));
452475
CONNECT(matchSizeToAllBtn, SIGNAL(clicked()), this, SLOT(matchSizeToAllPages()));
@@ -539,6 +562,160 @@ void OptionsWidget::setupIcons() {
539562
}
540563

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

@@ -93,6 +99,10 @@ class OptionsWidget : public FilterOptionsWidget, public UnitsListener, private
9399

94100
void onFixDpiClicked();
95101

102+
void sourceDpiComboActivated(int index);
103+
104+
void sourceDpiEditingFinished();
105+
96106
void applyMargins(const std::set<PageId>& pages,
97107
bool applyLeft = true,
98108
bool applyRight = true,
@@ -122,13 +132,32 @@ class OptionsWidget : public FilterOptionsWidget, public UnitsListener, private
122132

123133
void setupIcons();
124134

135+
void setupSourceDpiControls();
136+
137+
void updateSourceDpiDisplay();
138+
139+
void commitSourceDpiIfValid();
140+
141+
void decorateSourceDpiField(QLineEdit* field, ImageMetadata::DpiStatus dpiStatus);
142+
143+
void updateSourceDpiComboFromFields();
144+
145+
void keepSourceDpiFieldsEnabled();
146+
147+
bool isSourceDpiFieldFocused() const;
148+
125149
std::shared_ptr<Settings> m_settings;
150+
std::shared_ptr<ProjectPages> m_pages;
126151
PageSelectionAccessor m_pageSelectionAccessor;
127152
QIcon m_chainIcon;
128153
QIcon m_brokenChainIcon;
129154
AlignmentByButton m_alignmentByButton;
130155
PageId m_pageId;
131156
Dpi m_dpi;
157+
QSize m_sourceImagePixelSize;
158+
QPalette m_sourceDpiNormalPalette;
159+
QPalette m_sourceDpiErrorPalette;
160+
QWidget* m_sourceDpiFocusWidget;
132161
Margins m_marginsMM;
133162
Alignment m_alignment;
134163
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)