File indexing completed on 2025-01-19 03:57:57
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-09-12 0007 * Description : Widget for assignment and confirmation of names for faces 0008 * 0009 * SPDX-FileCopyrightText: 2010 by Aditya Bhatt <adityabhatt1991 at gmail dot com> 0010 * SPDX-FileCopyrightText: 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0011 * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "assignnamewidget_p.h" 0018 0019 namespace Digikam 0020 { 0021 0022 AssignNameWidget::Private::Private(AssignNameWidget* const q) 0023 : mode (InvalidMode), 0024 layoutMode (InvalidLayout), 0025 visualStyle (InvalidVisualStyle), 0026 widgetMode (InvalidTagEntryWidgetMode), 0027 comboBox (nullptr), 0028 lineEdit (nullptr), 0029 confirmButton (nullptr), 0030 rejectButton (nullptr), 0031 ignoreButton (nullptr), 0032 clickLabel (nullptr), 0033 modelsGiven (false), 0034 tagModel (nullptr), 0035 tagFilterModel (nullptr), 0036 tagFilteredModel(nullptr), 0037 layout (nullptr), 0038 q (q) 0039 { 0040 } 0041 0042 QWidget* AssignNameWidget::Private::addTagsWidget() const 0043 { 0044 if (comboBox) 0045 { 0046 return comboBox; 0047 } 0048 else 0049 { 0050 return lineEdit; 0051 } 0052 } 0053 0054 bool AssignNameWidget::Private::isValid() const 0055 { 0056 return ( 0057 (mode != InvalidMode) && 0058 (layoutMode != InvalidLayout) && 0059 (visualStyle != InvalidVisualStyle) && 0060 (widgetMode != InvalidTagEntryWidgetMode) 0061 ); 0062 } 0063 0064 void AssignNameWidget::Private::clearWidgets() 0065 { 0066 delete comboBox; 0067 comboBox = nullptr; 0068 0069 delete lineEdit; 0070 lineEdit = nullptr; 0071 0072 delete confirmButton; 0073 confirmButton = nullptr; 0074 0075 delete rejectButton; 0076 rejectButton = nullptr; 0077 0078 delete ignoreButton; 0079 ignoreButton = nullptr; 0080 0081 delete clickLabel; 0082 clickLabel = nullptr; 0083 } 0084 0085 QToolButton* AssignNameWidget::Private::createToolButton(const QIcon& icon, 0086 const QString& text, 0087 const QString& tip) const 0088 { 0089 QToolButton* const b = new QToolButton; 0090 b->setIcon(icon); 0091 b->setText(text); 0092 b->setToolTip(tip); 0093 b->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); 0094 0095 return b; 0096 } 0097 0098 void AssignNameWidget::Private::updateModes() 0099 { 0100 if (isValid()) 0101 { 0102 clearWidgets(); 0103 checkWidgets(); 0104 updateIgnoreButton(); 0105 updateLayout(); 0106 updateVisualStyle(); 0107 } 0108 } 0109 0110 template <class T> 0111 void AssignNameWidget::Private::setupAddTagsWidget(T* const widget) 0112 { 0113 if (modelsGiven) 0114 { 0115 widget->setAlbumModels(tagModel, tagFilteredModel, tagFilterModel); 0116 } 0117 0118 if (parentTag) 0119 { 0120 widget->setParentTag(parentTag); 0121 } 0122 0123 q->connect(widget, SIGNAL(taggingActionActivated(TaggingAction)), 0124 q, SLOT(slotActionActivated(TaggingAction))); 0125 0126 q->connect(widget, SIGNAL(taggingActionSelected(TaggingAction)), 0127 q, SLOT(slotActionSelected(TaggingAction))); 0128 } 0129 0130 void AssignNameWidget::Private::checkWidgets() 0131 { 0132 if (!isValid()) 0133 { 0134 return; 0135 } 0136 0137 switch (mode) 0138 { 0139 case InvalidMode: 0140 { 0141 break; 0142 } 0143 0144 case UnconfirmedEditMode: 0145 case ConfirmedEditMode: 0146 { 0147 switch (widgetMode) 0148 { 0149 case InvalidTagEntryWidgetMode: 0150 { 0151 break; 0152 } 0153 0154 case AddTagsComboBoxMode: 0155 { 0156 if (!comboBox) 0157 { 0158 comboBox = new AddTagsComboBox(q); 0159 setupAddTagsWidget(comboBox); 0160 } 0161 0162 break; 0163 } 0164 0165 case AddTagsLineEditMode: 0166 { 0167 if (!lineEdit) 0168 { 0169 lineEdit = new AddTagsLineEdit(q); 0170 setupAddTagsWidget(lineEdit); 0171 } 0172 0173 break; 0174 } 0175 } 0176 0177 if (!confirmButton) 0178 { 0179 confirmButton = createToolButton(QIcon::fromTheme(QLatin1String("dialog-ok-apply")), i18nc("@action", "OK")); 0180 confirmButton->setToolTip(i18nc("@info:tooltip", "Confirm that the selected person is shown here")); 0181 0182 if (mode == UnconfirmedEditMode) 0183 { 0184 confirmButton->setText(i18nc("@action", "Confirm")); 0185 } 0186 0187 q->connect(confirmButton, SIGNAL(clicked()), 0188 q, SLOT(slotConfirm())); 0189 } 0190 0191 if (!rejectButton) 0192 { 0193 rejectButton = createToolButton(QIcon::fromTheme(QLatin1String("list-remove")), i18nc("@action", "Remove")); 0194 rejectButton->setToolTip(i18nc("@info:tooltip", "Remove this face region")); 0195 0196 q->connect(rejectButton, SIGNAL(clicked()), 0197 q, SLOT(slotReject())); 0198 } 0199 0200 break; 0201 } 0202 0203 case IgnoredMode: 0204 { 0205 if (layoutMode == Compact) 0206 { 0207 if (!confirmButton) 0208 { 0209 confirmButton = createToolButton(QIcon::fromTheme(QLatin1String("edit-undo")), i18nc("@action", "OK")); 0210 confirmButton->setToolTip(i18nc("@info:tooltip", "Unmark this face as Ignored")); 0211 0212 q->connect(confirmButton, SIGNAL(clicked()), 0213 q, SLOT(slotIgnoredClicked())); 0214 } 0215 0216 if (!rejectButton) 0217 { 0218 rejectButton = createToolButton(QIcon::fromTheme(QLatin1String("list-remove")), i18nc("@action", "Remove")); 0219 rejectButton->setToolTip(i18nc("@info:tooltip", "Remove this face region")); 0220 0221 q->connect(rejectButton, SIGNAL(clicked()), 0222 q, SLOT(slotReject())); 0223 } 0224 } 0225 else 0226 { 0227 clickLabel = new DClickLabel; 0228 clickLabel->setAlignment(Qt::AlignCenter); 0229 0230 connect(clickLabel, SIGNAL(activated()), 0231 q, SLOT(slotIgnoredClicked())); 0232 } 0233 0234 break; 0235 } 0236 0237 case ConfirmedMode: 0238 { 0239 clickLabel = new DClickLabel; 0240 clickLabel->setAlignment(Qt::AlignCenter); 0241 0242 connect(clickLabel, SIGNAL(activated()), 0243 q, SLOT(slotLabelClicked())); 0244 0245 break; 0246 } 0247 } 0248 } 0249 0250 void AssignNameWidget::Private::layoutAddTagsWidget(bool exceedBounds, int minimumContentsLength) 0251 { 0252 if (comboBox) 0253 { 0254 comboBox->setMinimumContentsLength(minimumContentsLength); 0255 comboBox->lineEdit()->setAllowExceedBound(exceedBounds); 0256 } 0257 else if (lineEdit) 0258 { 0259 lineEdit->setAllowExceedBound(exceedBounds); 0260 } 0261 } 0262 0263 void AssignNameWidget::Private::setSizePolicies(QSizePolicy::Policy h, QSizePolicy::Policy v) 0264 { 0265 confirmButton->setSizePolicy(h, v); 0266 rejectButton->setSizePolicy(h, v); 0267 0268 if (ignoreButton) 0269 { 0270 ignoreButton->setSizePolicy(h, v); 0271 } 0272 0273 addTagsWidget()->setSizePolicy(h, v); 0274 } 0275 0276 void AssignNameWidget::Private::setToolButtonStyles(Qt::ToolButtonStyle style) 0277 { 0278 confirmButton->setToolButtonStyle(style); 0279 rejectButton->setToolButtonStyle(style); 0280 0281 if (ignoreButton) 0282 { 0283 ignoreButton->setToolButtonStyle(style); 0284 } 0285 } 0286 0287 void AssignNameWidget::Private::updateLayout() 0288 { 0289 if (!isValid()) 0290 { 0291 return; 0292 } 0293 0294 delete layout; 0295 layout = new QGridLayout; 0296 0297 switch (mode) 0298 { 0299 case InvalidMode: 0300 { 0301 break; 0302 } 0303 0304 case UnconfirmedEditMode: 0305 case ConfirmedEditMode: 0306 { 0307 switch (layoutMode) 0308 { 0309 case InvalidLayout: 0310 { 0311 break; 0312 } 0313 0314 case FullLine: 0315 { 0316 layout->addWidget(addTagsWidget(), 0, 0); 0317 layout->addWidget(confirmButton, 0, 1); 0318 layout->addWidget(rejectButton, 0, 2); 0319 layout->addWidget(ignoreButton, 0, 3); 0320 layout->setColumnStretch(0, 1); 0321 0322 setSizePolicies(QSizePolicy::Expanding, QSizePolicy::Preferred); 0323 setToolButtonStyles(Qt::ToolButtonTextBesideIcon); 0324 layoutAddTagsWidget(false, 15); 0325 0326 break; 0327 } 0328 0329 case TwoLines: 0330 case Compact: 0331 { 0332 layout->addWidget(addTagsWidget(), 0, 0, 1, 3); 0333 layout->addWidget(confirmButton, 1, 0); 0334 layout->addWidget(rejectButton, 1, 1); 0335 layout->addWidget(ignoreButton, 1, 2); 0336 0337 setSizePolicies(QSizePolicy::Expanding, QSizePolicy::Minimum); 0338 0339 if (layoutMode == AssignNameWidget::TwoLines) 0340 { 0341 setToolButtonStyles(Qt::ToolButtonTextBesideIcon); 0342 layoutAddTagsWidget(true, 10); 0343 } 0344 else 0345 { 0346 setToolButtonStyles(Qt::ToolButtonIconOnly); 0347 layoutAddTagsWidget(true, 0); 0348 } 0349 0350 break; 0351 } 0352 } 0353 0354 break; 0355 } 0356 0357 case IgnoredMode: 0358 { 0359 if (layoutMode == Compact) 0360 { 0361 layout->addWidget(confirmButton, 0, 0); 0362 layout->addWidget(rejectButton, 0, 1); 0363 0364 confirmButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); 0365 rejectButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); 0366 setToolButtonStyles(Qt::ToolButtonIconOnly); 0367 layoutAddTagsWidget(true, 0); 0368 } 0369 else 0370 { 0371 layout->addWidget(clickLabel, 0, 0); 0372 } 0373 0374 break; 0375 } 0376 0377 case ConfirmedMode: 0378 { 0379 layout->addWidget(clickLabel, 0, 0); 0380 0381 break; 0382 } 0383 } 0384 0385 layout->setContentsMargins(1, 1, 1, 1); 0386 layout->setSpacing(1); 0387 q->setLayout(layout); 0388 } 0389 0390 QString AssignNameWidget::Private::styleSheetFontDescriptor(const QFont& font) const 0391 { 0392 QString s; 0393 0394 s += (font.pointSize() == -1) ? QString::fromUtf8("font-size: %1px; ").arg(font.pixelSize()) 0395 : QString::fromUtf8("font-size: %1pt; ").arg(font.pointSize()); 0396 s += QString::fromUtf8("font-family: \"%1\"; ").arg(font.family()); 0397 0398 return s; 0399 } 0400 0401 void AssignNameWidget::Private::updateVisualStyle() 0402 { 0403 if (!isValid()) 0404 { 0405 return; 0406 } 0407 0408 QFont appFont = ApplicationSettings::instance()->getApplicationFont(); 0409 0410 if (appFont.pointSize() == -1) 0411 { 0412 appFont.setPixelSize(appFont.pixelSize() - 1); 0413 } 0414 else 0415 { 0416 appFont.setPointSize(appFont.pointSize() - 1); 0417 } 0418 0419 switch (visualStyle) 0420 { 0421 case InvalidVisualStyle: 0422 { 0423 break; 0424 } 0425 0426 case TranslucentDarkRound: 0427 { 0428 q->setStyleSheet( 0429 QString::fromUtf8( 0430 "QWidget { " 0431 " %1 " 0432 "} " 0433 0434 "QFrame {" 0435 " background-color: rgba(0, 0, 0, 66%); " 0436 " border: 1px solid rgba(100, 100, 100, 66%); " 0437 " border-radius: %2px; " 0438 "} " 0439 0440 "QToolButton { " 0441 " color: rgba(255, 255, 255, 220); " 0442 " padding: 1px; " 0443 " background-color: " 0444 " qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 100), " 0445 " stop:1 rgba(255, 255, 255, 0)); " 0446 " border: 1px solid rgba(255, 255, 255, 127); " 0447 " border-radius: 4px; " 0448 "} " 0449 0450 "QToolButton:hover { " 0451 " border-color: white; " 0452 "} " 0453 0454 "QToolButton:pressed { " 0455 " background-color: " 0456 " qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 0), " 0457 " stop:1 rgba(255, 255, 255, 100)); " 0458 " border-color: white; " 0459 "} " 0460 0461 "QToolButton:disabled { " 0462 " color: rgba(255, 255, 255, 120); " 0463 " background-color: " 0464 " qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 50), " 0465 " stop:1 rgba(255, 255, 255, 0)); " 0466 "} " 0467 0468 "QComboBox { " 0469 " color: white; " 0470 " background-color: transparent; " 0471 "} " 0472 0473 "QComboBox QAbstractItemView, QListView::item:!selected { " 0474 " color: white; " 0475 " background-color: rgba(0, 0, 0, 80%); " 0476 "} " 0477 0478 "QLabel { " 0479 " color: white; background-color: transparent; border: none; " 0480 "}" 0481 ).arg(styleSheetFontDescriptor(appFont)) 0482 .arg(((mode == ConfirmedMode) || (mode == IgnoredMode)) ? QLatin1String("8") 0483 : QLatin1String("4")) 0484 ); 0485 0486 break; 0487 } 0488 0489 case TranslucentThemedFrameless: 0490 { 0491 QColor bg = qApp->palette().color(QPalette::Base); 0492 q->setStyleSheet( 0493 QString::fromUtf8( 0494 "QWidget { " 0495 " %1 " 0496 "} " 0497 0498 "QFrame#assignNameWidget {" 0499 " background-color: " 0500 " qradialgradient(cx:0.5 cy:0.5, fx:0, fy:0, radius: 1, stop:0 rgba(%2, %3, %4, 255), " 0501 " stop:0.8 rgba(%2, %3, %4, 200), stop:1 rgba(%2, %3, %4, 0));" 0502 " border: none; " 0503 " border-radius: 8px; " 0504 "}" 0505 ).arg(styleSheetFontDescriptor(appFont)) 0506 .arg(bg.red()) 0507 .arg(bg.green()) 0508 .arg(bg.blue()) 0509 ); 0510 0511 break; 0512 } 0513 0514 case StyledFrame: 0515 { 0516 q->setStyleSheet(QString()); 0517 q->setFrameStyle(Raised | StyledPanel); 0518 0519 break; 0520 } 0521 } 0522 } 0523 0524 template <class T> 0525 void AssignNameWidget::Private::setAddTagsWidgetContents(T* const widget) 0526 { 0527 if (widget) 0528 { 0529 widget->setCurrentTag(currentTag); 0530 widget->setPlaceholderText((mode == UnconfirmedEditMode) ? i18nc("@label", "Who is this?") 0531 : QString()); 0532 0533 if (confirmButton) 0534 { 0535 confirmButton->setEnabled(widget->currentTaggingAction().isValid()); 0536 } 0537 } 0538 } 0539 0540 void AssignNameWidget::Private::updateContents() 0541 { 0542 if (!isValid()) 0543 { 0544 return; 0545 } 0546 0547 if (comboBox) 0548 { 0549 setAddTagsWidgetContents(comboBox); 0550 } 0551 else if (lineEdit) 0552 { 0553 setAddTagsWidgetContents(lineEdit); 0554 } 0555 0556 if (clickLabel) 0557 { 0558 if (mode != IgnoredMode) 0559 { 0560 clickLabel->setText(currentTag ? currentTag->title() 0561 : QString()); 0562 } 0563 else 0564 { 0565 int tagId = FaceTags::ignoredPersonTagId(); 0566 clickLabel->setText(FaceTags::faceNameForTag(tagId)); 0567 } 0568 } 0569 } 0570 0571 void AssignNameWidget::Private::updateIgnoreButton() 0572 { 0573 if (!ignoreButton) 0574 { 0575 ignoreButton = createToolButton(QIcon::fromTheme(QLatin1String("dialog-cancel")), 0576 i18nc("@action", "Ignore")); 0577 ignoreButton->setToolTip(i18nc("@info:tooltip", "Ignore this face")); 0578 0579 q->connect(ignoreButton, SIGNAL(clicked()), 0580 q, SLOT(slotIgnore())); 0581 } 0582 else 0583 { 0584 FaceTagsIface face = FaceTagsIface::fromVariant(faceIdentifier); 0585 0586 if ((face.type() == FaceTagsIface::IgnoredName) || 0587 (face.type() == FaceTagsIface::UnknownName)) 0588 { 0589 ignoreButton->hide(); 0590 } 0591 else 0592 { 0593 ignoreButton->show(); 0594 } 0595 } 0596 } 0597 0598 void AssignNameWidget::Private::updateRejectButton() 0599 { 0600 if (!rejectButton) 0601 { 0602 return; 0603 } 0604 0605 FaceTagsIface face = FaceTagsIface::fromVariant(faceIdentifier); 0606 0607 if (face.type() == FaceTagsIface::UnknownName) 0608 { 0609 rejectButton->setToolTip(i18nc("@info:tooltip", "Mark this face as Ignored")); 0610 rejectButton->setIcon(QIcon::fromTheme(QLatin1String("dialog-cancel"))); 0611 } 0612 else if (face.type() == FaceTagsIface::UnconfirmedName) 0613 { 0614 rejectButton->setToolTip(i18nc("@info:tooltip", "Reject this suggestion")); 0615 rejectButton->setIcon(QIcon::fromTheme(QLatin1String("list-remove"))); 0616 } 0617 0618 updateIgnoreButton(); 0619 } 0620 0621 } // namespace Digikam