1 /*
2 SPDX-FileCopyrightText: 2017 Jean-Baptiste Mardelle
3 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4 */
5
6 #include "geometrywidget.h"
7 #include "core.h"
8 #include "doublewidget.h"
9 #include "dragvalue.h"
10 #include "monitor/monitor.h"
11
12 #include <KLocalizedString>
13 #include <QGridLayout>
14
GeometryWidget(Monitor * monitor,QPair<int,int> range,const QRect & rect,double opacity,const QSize frameSize,bool useRatioLock,bool useOpacity,QWidget * parent)15 GeometryWidget::GeometryWidget(Monitor *monitor, QPair<int, int> range, const QRect &rect, double opacity, const QSize frameSize, bool useRatioLock,
16 bool useOpacity, QWidget *parent)
17 : QWidget(parent)
18 , m_min(range.first)
19 , m_max(range.second)
20 , m_active(false)
21 , m_monitor(monitor)
22 , m_opacity(nullptr)
23 , m_opacityFactor(100.)
24 {
25 Q_UNUSED(useRatioLock)
26 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
27 auto *layout = new QVBoxLayout(this);
28 layout->setContentsMargins(0, 0, 0, 0);
29 layout->setSpacing(0);
30 m_defaultSize = pCore->getCurrentFrameSize();
31 m_sourceSize = (frameSize.isValid() && !frameSize.isNull()) ? frameSize : m_defaultSize;
32 /*QString paramName = i18n(paramTag.toUtf8().data());
33 QString comment = m_model->data(ix, AssetParameterModel::CommentRole).toString();
34 if (!comment.isEmpty()) {
35 comment = i18n(comment.toUtf8().data());
36 }*/
37
38 auto *horLayout = new QHBoxLayout;
39 horLayout->setSpacing(2);
40 m_spinX = new DragValue(i18nc("x axis position", "X"), 0, 0, -99000, 99000, -1, QString(), false, false, this);
41 connect(m_spinX, &DragValue::valueChanged, this, &GeometryWidget::slotAdjustRectKeyframeValue);
42 horLayout->addWidget(m_spinX);
43 m_spinX->setObjectName("spinX");
44
45 m_spinY = new DragValue(i18nc("y axis position", "Y"), 0, 0, -99000, 99000, -1, QString(), false, false, this);
46 connect(m_spinY, &DragValue::valueChanged, this, &GeometryWidget::slotAdjustRectKeyframeValue);
47 horLayout->addWidget(m_spinY);
48 m_spinY->setObjectName("spinY");
49
50 m_spinWidth = new DragValue(i18nc("Frame width", "W"), m_defaultSize.width(), 0, 1, 99000, -1, QString(), false, false, this);
51 connect(m_spinWidth, &DragValue::valueChanged, this, &GeometryWidget::slotAdjustRectWidth);
52 horLayout->addWidget(m_spinWidth);
53 m_spinWidth->setObjectName("spinW");
54
55 // Lock ratio stuff
56 m_lockRatio = new QAction(QIcon::fromTheme(QStringLiteral("link")), i18n("Lock aspect ratio"), this);
57 m_lockRatio->setCheckable(true);
58 connect(m_lockRatio, &QAction::triggered, this, &GeometryWidget::slotLockRatio);
59 auto *ratioButton = new QToolButton;
60 ratioButton->setDefaultAction(m_lockRatio);
61 horLayout->addWidget(ratioButton);
62
63 m_spinHeight = new DragValue(i18nc("Frame height", "H"), m_defaultSize.height(), 0, 1, 99000, -1, QString(), false, false, this);
64 connect(m_spinHeight, &DragValue::valueChanged, this, &GeometryWidget::slotAdjustRectHeight);
65 m_spinHeight->setObjectName("spinH");
66 horLayout->addWidget(m_spinHeight);
67 horLayout->addStretch(10);
68
69 auto *horLayout2 = new QHBoxLayout;
70 horLayout2->setSpacing(2);
71 m_spinSize = new DragValue(i18n("Size"), 100, 2, 1, 99000, -1, i18n("%"), false, false, this);
72 m_spinSize->setStep(5);
73 m_spinSize->setObjectName("spinS");
74 connect(m_spinSize, &DragValue::valueChanged, this, &GeometryWidget::slotResize);
75 horLayout2->addWidget(m_spinSize);
76
77 if (useOpacity) {
78 m_opacity = new DragValue(i18n("Opacity"), 100, 0, 0, 100, -1, i18n("%"), true, false, this);
79 m_opacity->setValue((int)(opacity * m_opacityFactor));
80 connect(m_opacity, &DragValue::valueChanged, this, [&]() { emit valueChanged(getValue()); });
81 m_opacity->setObjectName("spinO");
82 horLayout2->addWidget(m_opacity);
83 }
84 horLayout2->addStretch(10);
85
86 // Build buttons
87 m_originalSize = new QAction(QIcon::fromTheme(QStringLiteral("zoom-original")), i18n("Adjust to original size"), this);
88 connect(m_originalSize, &QAction::triggered, this, &GeometryWidget::slotAdjustToSource);
89 m_originalSize->setCheckable(true);
90 QAction *adjustSize = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-best")), i18n("Adjust and center in frame"), this);
91 connect(adjustSize, &QAction::triggered, this, &GeometryWidget::slotAdjustToFrameSize);
92 QAction *fitToWidth = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), i18n("Fit to width"), this);
93 connect(fitToWidth, &QAction::triggered, this, &GeometryWidget::slotFitToWidth);
94 QAction *fitToHeight = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-height")), i18n("Fit to height"), this);
95 connect(fitToHeight, &QAction::triggered, this, &GeometryWidget::slotFitToHeight);
96
97 QAction *alignleft = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-left")), i18n("Align left"), this);
98 connect(alignleft, &QAction::triggered, this, &GeometryWidget::slotMoveLeft);
99 QAction *alignhcenter = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-hor")), i18n("Center horizontally"), this);
100 connect(alignhcenter, &QAction::triggered, this, &GeometryWidget::slotCenterH);
101 QAction *alignright = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-right")), i18n("Align right"), this);
102 connect(alignright, &QAction::triggered, this, &GeometryWidget::slotMoveRight);
103 QAction *aligntop = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-top")), i18n("Align top"), this);
104 connect(aligntop, &QAction::triggered, this, &GeometryWidget::slotMoveTop);
105 QAction *alignvcenter = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-vert")), i18n("Center vertically"), this);
106 connect(alignvcenter, &QAction::triggered, this, &GeometryWidget::slotCenterV);
107 QAction *alignbottom = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-align-bottom")), i18n("Align bottom"), this);
108 connect(alignbottom, &QAction::triggered, this, &GeometryWidget::slotMoveBottom);
109
110 auto *alignLayout = new QHBoxLayout;
111 alignLayout->setSpacing(0);
112 auto *alignButton = new QToolButton;
113 alignButton->setDefaultAction(alignleft);
114 alignButton->setAutoRaise(true);
115 alignLayout->addWidget(alignButton);
116
117 alignButton = new QToolButton;
118 alignButton->setDefaultAction(alignhcenter);
119 alignButton->setAutoRaise(true);
120 alignLayout->addWidget(alignButton);
121
122 alignButton = new QToolButton;
123 alignButton->setDefaultAction(alignright);
124 alignButton->setAutoRaise(true);
125 alignLayout->addWidget(alignButton);
126
127 alignButton = new QToolButton;
128 alignButton->setDefaultAction(aligntop);
129 alignButton->setAutoRaise(true);
130 alignLayout->addWidget(alignButton);
131
132 alignButton = new QToolButton;
133 alignButton->setDefaultAction(alignvcenter);
134 alignButton->setAutoRaise(true);
135 alignLayout->addWidget(alignButton);
136
137 alignButton = new QToolButton;
138 alignButton->setDefaultAction(alignbottom);
139 alignButton->setAutoRaise(true);
140 alignLayout->addWidget(alignButton);
141
142 alignButton = new QToolButton;
143 alignButton->setDefaultAction(m_originalSize);
144 alignButton->setAutoRaise(true);
145 alignLayout->addWidget(alignButton);
146
147 alignButton = new QToolButton;
148 alignButton->setDefaultAction(adjustSize);
149 alignButton->setAutoRaise(true);
150 alignLayout->addWidget(alignButton);
151
152 alignButton = new QToolButton;
153 alignButton->setDefaultAction(fitToWidth);
154 alignButton->setAutoRaise(true);
155 alignLayout->addWidget(alignButton);
156
157 alignButton = new QToolButton;
158 alignButton->setDefaultAction(fitToHeight);
159 alignButton->setAutoRaise(true);
160 alignLayout->addWidget(alignButton);
161 alignLayout->addStretch(10);
162
163 layout->addLayout(horLayout);
164 layout->addLayout(alignLayout);
165 layout->addLayout(horLayout2);
166 slotUpdateGeometryRect(rect);
167 slotAdjustRectKeyframeValue();
168 setMinimumHeight(horLayout->sizeHint().height() + horLayout2->sizeHint().height() + alignLayout->sizeHint().height());
169 }
170
slotAdjustToSource()171 void GeometryWidget::slotAdjustToSource()
172 {
173 m_spinWidth->blockSignals(true);
174 m_spinHeight->blockSignals(true);
175 m_spinWidth->setValue((int)(m_sourceSize.width() / pCore->getCurrentSar() + 0.5), false);
176 m_spinHeight->setValue(m_sourceSize.height(), false);
177 m_spinWidth->blockSignals(false);
178 m_spinHeight->blockSignals(false);
179 slotAdjustRectKeyframeValue();
180 if (m_lockRatio->isChecked()) {
181 m_monitor->setEffectSceneProperty(QStringLiteral("lockratio"), m_originalSize->isChecked() ? (double)m_sourceSize.width() / m_sourceSize.height()
182 : (double)m_defaultSize.width() / m_defaultSize.height());
183 }
184 }
slotAdjustToFrameSize()185 void GeometryWidget::slotAdjustToFrameSize()
186 {
187 double monitorDar = pCore->getCurrentDar();
188 double sourceDar = m_sourceSize.width() / m_sourceSize.height();
189 QSignalBlocker bk1(m_spinWidth);
190 QSignalBlocker bk2(m_spinHeight);
191 if (sourceDar > monitorDar) {
192 // Fit to width
193 double factor = (double)m_defaultSize.width() / m_sourceSize.width() * pCore->getCurrentSar();
194 m_spinHeight->setValue((int)(m_sourceSize.height() * factor + 0.5));
195 m_spinWidth->setValue(m_defaultSize.width());
196 } else {
197 // Fit to height
198 double factor = (double)m_defaultSize.height() / m_sourceSize.height();
199 m_spinHeight->setValue(m_defaultSize.height());
200 m_spinWidth->setValue((int)(m_sourceSize.width() / pCore->getCurrentSar() * factor + 0.5));
201 }
202 // Center
203 QSignalBlocker bk3(m_spinX);
204 QSignalBlocker bk4(m_spinY);
205 m_spinX->setValue((m_defaultSize.width() - m_spinWidth->value()) / 2);
206 m_spinY->setValue((m_defaultSize.height() - m_spinHeight->value()) / 2);
207 slotAdjustRectKeyframeValue();
208 }
209
slotFitToWidth()210 void GeometryWidget::slotFitToWidth()
211 {
212 double factor = (double)m_defaultSize.width() / m_sourceSize.width() * pCore->getCurrentSar();
213 m_spinWidth->blockSignals(true);
214 m_spinHeight->blockSignals(true);
215 m_spinHeight->setValue((int)(m_sourceSize.height() * factor + 0.5));
216 m_spinWidth->setValue(m_defaultSize.width());
217 m_spinWidth->blockSignals(false);
218 m_spinHeight->blockSignals(false);
219 slotAdjustRectKeyframeValue();
220 }
slotFitToHeight()221 void GeometryWidget::slotFitToHeight()
222 {
223 double factor = (double)m_defaultSize.height() / m_sourceSize.height();
224 m_spinWidth->blockSignals(true);
225 m_spinHeight->blockSignals(true);
226 m_spinHeight->setValue(m_defaultSize.height());
227 m_spinWidth->setValue((int)(m_sourceSize.width() / pCore->getCurrentSar() * factor + 0.5));
228 m_spinWidth->blockSignals(false);
229 m_spinHeight->blockSignals(false);
230 slotAdjustRectKeyframeValue();
231 }
slotResize(double value)232 void GeometryWidget::slotResize(double value)
233 {
234 QSignalBlocker bkh(m_spinHeight);
235 QSignalBlocker bkw(m_spinWidth);
236 QSignalBlocker bkx(m_spinX);
237 QSignalBlocker bky(m_spinY);
238 int w = (m_originalSize->isChecked() ? m_sourceSize.width() : m_defaultSize.width()) * value / 100.0;
239 int h = (m_originalSize->isChecked() ? m_sourceSize.height() : m_defaultSize.height()) * value / 100.0;
240 int delta_x = (m_spinWidth->value() - w) / 2;
241 int delta_y = (m_spinHeight->value() - h) / 2;
242 m_spinWidth->setValue(w);
243 m_spinHeight->setValue(h);
244 m_spinX->setValue(m_spinX->value() + delta_x);
245 m_spinY->setValue(m_spinY->value() + delta_y);
246 slotAdjustRectKeyframeValue();
247 }
248
249 /** @brief Moves the rect to the left frame border (x position = 0). */
slotMoveLeft()250 void GeometryWidget::slotMoveLeft()
251 {
252 m_spinX->setValue(0);
253 }
254 /** @brief Centers the rect horizontally. */
slotCenterH()255 void GeometryWidget::slotCenterH()
256 {
257 m_spinX->setValue((m_defaultSize.width() - m_spinWidth->value()) / 2);
258 }
259 /** @brief Moves the rect to the right frame border (x position = frame width - rect width). */
slotMoveRight()260 void GeometryWidget::slotMoveRight()
261 {
262 m_spinX->setValue(m_defaultSize.width() - m_spinWidth->value());
263 }
264
265 /** @brief Moves the rect to the top frame border (y position = 0). */
slotMoveTop()266 void GeometryWidget::slotMoveTop()
267 {
268 m_spinY->setValue(0);
269 }
270
271 /** @brief Centers the rect vertically. */
slotCenterV()272 void GeometryWidget::slotCenterV()
273 {
274 m_spinY->setValue((m_defaultSize.height() - m_spinHeight->value()) / 2);
275 }
276
277 /** @brief Moves the rect to the bottom frame border (y position = frame height - rect height). */
slotMoveBottom()278 void GeometryWidget::slotMoveBottom()
279 {
280 m_spinY->setValue(m_defaultSize.height() - m_spinHeight->value());
281 }
282
283 /** @brief Un/Lock aspect ratio for size in effect parameter. */
slotLockRatio()284 void GeometryWidget::slotLockRatio()
285 {
286 auto *lockRatio = qobject_cast<QAction *>(QObject::sender());
287 if (lockRatio->isChecked()) {
288 m_monitor->setEffectSceneProperty(QStringLiteral("lockratio"), m_originalSize->isChecked() ? (double)m_sourceSize.width() / m_sourceSize.height()
289 : (double)m_defaultSize.width() / m_defaultSize.height());
290 } else {
291 m_monitor->setEffectSceneProperty(QStringLiteral("lockratio"), -1);
292 }
293 }
slotAdjustRectHeight()294 void GeometryWidget::slotAdjustRectHeight()
295 {
296 if (m_lockRatio->isChecked()) {
297 m_spinWidth->blockSignals(true);
298 if (m_originalSize->isChecked()) {
299 m_spinWidth->setValue((int)(m_spinHeight->value() * m_sourceSize.width() / m_sourceSize.height() + 0.5));
300 } else {
301 m_spinWidth->setValue((int)(m_spinHeight->value() * m_defaultSize.width() / m_defaultSize.height() + 0.5));
302 }
303 m_spinWidth->blockSignals(false);
304 }
305 adjustSizeValue();
306 slotAdjustRectKeyframeValue();
307 }
308
slotAdjustRectWidth()309 void GeometryWidget::slotAdjustRectWidth()
310 {
311 if (m_lockRatio->isChecked()) {
312 m_spinHeight->blockSignals(true);
313 if (m_originalSize->isChecked()) {
314 m_spinHeight->setValue((int)(m_spinWidth->value() * m_sourceSize.height() / m_sourceSize.width() + 0.5));
315 } else {
316 m_spinHeight->setValue((int)(m_spinWidth->value() * m_defaultSize.height() / m_defaultSize.width() + 0.5));
317 }
318 m_spinHeight->blockSignals(false);
319 }
320 adjustSizeValue();
321 slotAdjustRectKeyframeValue();
322 }
323
adjustSizeValue()324 void GeometryWidget::adjustSizeValue()
325 {
326 double size;
327 if ((double)m_spinWidth->value() / m_spinHeight->value() < pCore->getCurrentDar()) {
328 if (m_originalSize->isChecked()) {
329 size = m_spinWidth->value() * 100.0 / m_sourceSize.width();
330 } else {
331 size = m_spinWidth->value() * 100.0 / m_defaultSize.width();
332 }
333 } else {
334 if (m_originalSize->isChecked()) {
335 size = m_spinHeight->value() * 100.0 / m_sourceSize.height();
336 } else {
337 size = m_spinHeight->value() * 100.0 / m_defaultSize.height();
338 }
339 }
340 m_spinSize->blockSignals(true);
341 m_spinSize->setValue(size);
342 m_spinSize->blockSignals(false);
343 }
344
slotAdjustRectKeyframeValue()345 void GeometryWidget::slotAdjustRectKeyframeValue()
346 {
347 QRect rect(m_spinX->value(), m_spinY->value(), m_spinWidth->value(), m_spinHeight->value());
348 emit updateMonitorGeometry(rect);
349 emit valueChanged(getValue());
350 }
351
slotUpdateGeometryRect(const QRect r)352 void GeometryWidget::slotUpdateGeometryRect(const QRect r)
353 {
354 if (!r.isValid()) {
355 return;
356 }
357 m_spinX->blockSignals(true);
358 m_spinY->blockSignals(true);
359 m_spinWidth->blockSignals(true);
360 m_spinHeight->blockSignals(true);
361 m_spinX->setValue(r.x());
362 m_spinY->setValue(r.y());
363 m_spinWidth->setValue(r.width());
364 m_spinHeight->setValue(r.height());
365 m_spinX->blockSignals(false);
366 m_spinY->blockSignals(false);
367 m_spinWidth->blockSignals(false);
368 m_spinHeight->blockSignals(false);
369 //emit updateMonitorGeometry(r);
370 adjustSizeValue();
371 emit valueChanged(getValue());
372 }
373
setValue(const QRect r,double opacity)374 void GeometryWidget::setValue(const QRect r, double opacity)
375 {
376 if (!r.isValid()) {
377 return;
378 }
379 m_spinX->blockSignals(true);
380 m_spinY->blockSignals(true);
381 m_spinWidth->blockSignals(true);
382 m_spinHeight->blockSignals(true);
383 m_spinX->setValue(r.x());
384 m_spinY->setValue(r.y());
385 m_spinWidth->setValue(r.width());
386 m_spinHeight->setValue(r.height());
387 if (m_opacity) {
388 m_opacity->blockSignals(true);
389 if (opacity < 0) {
390 opacity = 100 / m_opacityFactor;
391 }
392 m_opacity->setValue((int)(opacity * m_opacityFactor + 0.5));
393 m_opacity->blockSignals(false);
394 }
395 m_spinX->blockSignals(false);
396 m_spinY->blockSignals(false);
397 m_spinWidth->blockSignals(false);
398 m_spinHeight->blockSignals(false);
399 adjustSizeValue();
400 emit updateMonitorGeometry(r);
401 }
402
getValue() const403 const QString GeometryWidget::getValue() const
404 {
405 if (m_opacity) {
406 return QStringLiteral("%1 %2 %3 %4 %5")
407 .arg(m_spinX->value())
408 .arg(m_spinY->value())
409 .arg(m_spinWidth->value())
410 .arg(m_spinHeight->value())
411 .arg(QString::number(m_opacity->value() / m_opacityFactor, 'f'));
412 }
413 return QStringLiteral("%1 %2 %3 %4").arg(m_spinX->value()).arg(m_spinY->value()).arg(m_spinWidth->value()).arg(m_spinHeight->value());
414 }
415
connectMonitor(bool activate)416 void GeometryWidget::connectMonitor(bool activate)
417 {
418 if (m_active == activate) {
419 return;
420 }
421 m_active = activate;
422 if (activate) {
423 connect(m_monitor, &Monitor::effectChanged, this, &GeometryWidget::slotUpdateGeometryRect, Qt::UniqueConnection);
424 QRect rect(m_spinX->value(), m_spinY->value(), m_spinWidth->value(), m_spinHeight->value());
425 emit updateMonitorGeometry(rect);
426 } else {
427 m_monitor->setEffectKeyframe(false);
428 disconnect(m_monitor, &Monitor::effectChanged, this, &GeometryWidget::slotUpdateGeometryRect);
429 }
430 }
431
slotSetRange(QPair<int,int> range)432 void GeometryWidget::slotSetRange(QPair<int, int> range)
433 {
434 m_min = range.first;
435 m_max = range.second;
436 }
437