/** * @RDE_HEADER@ * Base Layout Widget with all Areas (Info, Main, Detail, Menu) * $Id$ * $HeadURL$ * $Author$ */ #include "lts_base_layout_widget.h" #include "ui_lts_base_layout.h" #include "lts_dialog.h" #include "lts_dialog_helper.h" #include "lts_drag_icon.h" #include "lts_grid_layout.h" #include "lts_message_dialog.h" #include "lts_operator_menu.h" #include "lts_tool_bar_area.h" #include "lts_splash.h" #include "lts_events.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace LtsWidgets { static QVariant splitterInterpolator(const QList &start, const QList &end, qreal progress) { QList ret; ret = start; for (int i = 0; i < start.size(); i++) { ret[i] = start[i] + qRound((end[i] - start[i]) * progress); if (ret[i] <= 0) ret[i] = 1; } return QVariant::fromValue(ret); } LtsBaseLayoutWidget::LtsBaseLayoutWidget(QWidget *parent, bool splitMenu) : QWidget(parent) , m_currentAnimation(nullptr) , m_ui(new Ui::LtsBaseLayoutWidget) , m_partitionType(IDL::PartitionType::Global) /* First ever Session must be the Administrative Global Partition */ , m_domainId(-1) , m_partitionId(-1) , m_firstShow(true) , m_showTerminalButton(false) , m_exerciseTime(0) , m_setCurrentTimeTimer(new QTimer(this)) , m_irreplaceableOnce(false) , m_bg(nullptr) , m_screenCount(1) // at least 1 screen, always , m_menuCollapsed(true) , m_notiBox(nullptr) { m_ui->setupUi(this); #ifndef LTS_DEBUG m_ui->pbDebug->hide(); #endif addDebugMenu(); { // Current Time label's context menu m_ui->lbCurrentTime->setContextMenuPolicy(Qt::CustomContextMenu); QMenu *currentTimeMenu = new QMenu(m_ui->lbCurrentTime); currentTimeMenu->addAction(tr("Copy"), this, [this]() { QGuiApplication::clipboard()->setText(m_ui->lbCurrentTime->text()); }); currentTimeMenu->addAction(tr("Copy in milliseconds"), this, []() { QGuiApplication::clipboard()->setText(QString::number(QDateTime::currentMSecsSinceEpoch())); }); connect(m_ui->lbCurrentTime, &QLabel::customContextMenuRequested, this, [this, currentTimeMenu](const QPoint &pos) { currentTimeMenu->popup(m_ui->lbCurrentTime->mapToGlobal(pos)); }); } { // Exercise Time label's context menu m_ui->lbExerciseTime->setContextMenuPolicy(Qt::CustomContextMenu); QMenu *exerciseTimeMenu = new QMenu(m_ui->lbExerciseTime); exerciseTimeMenu->addAction(tr("Copy"), this, [this]() { QGuiApplication::clipboard()->setText(m_ui->lbExerciseTime->text()); }); exerciseTimeMenu->addAction(tr("Copy in milliseconds"), this, [this]() { QGuiApplication::clipboard()->setText(QString::number(m_exerciseTime)); }); connect(m_ui->lbExerciseTime, &QLabel::customContextMenuRequested, this, [this, exerciseTimeMenu](const QPoint &pos) { exerciseTimeMenu->popup(m_ui->lbExerciseTime->mapToGlobal(pos)); }); } setMinimumSize(1280, 900); m_ui->detailWidget->setSubTabsVisible(true); // only the detail tab widget has sub tabs !! //Register qRegisterAnimationInterpolator >(splitterInterpolator); qRegisterMetaType("LayoutArea"); m_ui->mainWidget->setTabBarAutoHide(true); m_ui->infoWidget->setActuatorTabStyle(true); m_ui->infoWidget->setTabBarAutoHide(false); m_ui->infoWidget->setTabPosition(QTabWidget::West); if (m_ui->infoWidget->count() == 0) m_ui->infoWidget->hide(); if (m_ui->detailWidget->count() == 0) m_ui->detailWidget->hide(); if (m_ui->specialWidget->count() == 0) m_ui->specialWidget->hide(); // master splitter stretch m_ui->masterSplitter->setStretchFactor(m_ui->masterSplitter->indexOf(m_ui->infoWidget), 0); m_ui->masterSplitter->setStretchFactor(m_ui->masterSplitter->indexOf(m_ui->mainSplitter), 20); // main splitter stretch m_ui->mainSplitter->setStretchFactor(m_ui->mainSplitter->indexOf(m_ui->mainWidget), 20); m_ui->mainSplitter->setStretchFactor(m_ui->mainSplitter->indexOf(m_ui->detailWidget), 0); // right splitter stretch m_ui->rightSplitter->setStretchFactor(m_ui->rightSplitter->indexOf(m_ui->stackedWidget), 20); m_ui->rightSplitter->setStretchFactor(m_ui->rightSplitter->indexOf(m_ui->specialWidget), 0); m_isHidden = false; connect(m_ui->profileButton, &LtsProfileButton::signalClicked, this, &LtsBaseLayoutWidget::slotOperatorMenuClicked); //m_ui->lbExerciseTime->hide(); //m_ui->lbExercisePhase->hide(); //m_ui->lbExerciseStatus->hide(); //m_ui->lbExerciseName->hide(); m_ui->lbCustomTextBullet->hide(); m_ui->lbCustomText->hide(); m_ui->lbExerciseTime->setText(QStringLiteral(" # ")); m_ui->lbCurrentTime->setText(QStringLiteral(" # ")); connect(m_setCurrentTimeTimer, &QTimer::timeout, this, &LtsBaseLayoutWidget::setCurrentTime); m_setCurrentTimeTimer->start(200); setExerciseData(m_exerciseData); // initially set labels to invalid values if (splitMenu) { m_bg = new QButtonGroup(m_ui->naviWidget); m_bg->addButton(m_ui->pbPlanning, (int) ExercisePart::ePlanning); m_bg->addButton(m_ui->pbExecution, (int) ExercisePart::eExecution); m_bg->addButton(m_ui->pbAfterAction, (int) ExercisePart::eAfterAction); m_bg->addButton(m_ui->pbSpecial, (int) ExercisePart::eSpecial); connect(m_bg, QOverload::of(&QButtonGroup::buttonClicked), m_ui->stackedWidget, [this](int id) { m_ui->stackedWidget->setCurrentWidget(getToolBox((ExercisePart) id)); }); m_ui->pbSpecial->click(); } else { m_ui->stackedWidget->setCurrentWidget(getToolBox(ExercisePart::eSpecial)); } checkExercisePartButtonVisibility(); } LtsBaseLayoutWidget::~LtsBaseLayoutWidget() { // memleak .. x) // qCritical() << "~LtsBaseLayoutWidget"; if (m_notiBox != nullptr) m_notiBox->setParent(nullptr); delete m_notiBox; delete m_currentAnimation; m_currentAnimation = nullptr; clearMenuActions(); delete m_ui; m_ui = nullptr; // do not delete m_progressDialogHelper because it is parented with this object (??!!) } void LtsBaseLayoutWidget::setCurrentTime() { m_ui->lbCurrentTime->setText(DTG::toString(QDateTime::currentDateTime())); if (m_exerciseTime > 0) m_ui->lbExerciseTime->setText(DTG::toStringMSec(m_exerciseTime)); else m_ui->lbExerciseTime->setText(QStringLiteral(" # ")); } void LtsBaseLayoutWidget::slotScreenCountChanged(int count) { m_screenCount = count; } const QString LtsBaseLayoutWidget::customText() const { return m_ui->lbCustomText->text(); } void LtsBaseLayoutWidget::setCustomText(const QString &text) { if (text.isEmpty()) { m_ui->lbCustomText->setText(QString()); m_ui->lbCustomText->hide(); m_ui->lbCustomTextBullet->hide(); return; } m_ui->lbCustomText->show(); m_ui->lbCustomTextBullet->show(); m_ui->lbCustomText->setText(text); } void LtsBaseLayoutWidget::addDebugMenu() { #ifdef LTS_DEBUG QMenu *menu = new QMenu(this); menu->addAction(QStringLiteral("NOTE: all actions are delayed by 1s"))->setEnabled(false); menu->addSeparator(); menu->addAction(QStringLiteral("logout"), this, [this]() { QTimer::singleShot(1000, this, &LtsBaseLayoutWidget::slotLogout); }); menu->addAction(QStringLiteral("forced logout"), this, [this]() { QTimer::singleShot(1000, this, &LtsBaseLayoutWidget::signalLogout); }); menu->addAction(QStringLiteral("app exit"), this, [this]() { QTimer::singleShot(1000, this, &LtsBaseLayoutWidget::signalClose); }); menu->addSeparator(); menu->addAction(QStringLiteral("show test message, unparented"), this, [this]() { QTimer::singleShot(1000, this, []() { LtsWidgets::LtsMessageDialog::showMessage(nullptr /* INTENDED! */, QStringLiteral("test"), QStringLiteral("test")); } ); }); menu->addAction(QStringLiteral("show test message"), this, [this]() { QTimer::singleShot(1000, this, [this]() { LtsWidgets::LtsMessageDialog::showMessage(this, QStringLiteral("test"), QStringLiteral("test")); } ); }); menu->addSeparator(); menu->addAction(QStringLiteral("hide details tab"), this, [this]() { QTimer::singleShot(1000, this, [this]() { this->hideWidgets(); } ); }); menu->addAction(QStringLiteral("show details tab"), this, [this]() { QTimer::singleShot(1000, this, [this]() { this->showWidgets(); } ); }); menu->addAction(QStringLiteral("hide details tab, animated"), this, [this]() { QTimer::singleShot(1000, this, [this]() { this->onHideAnimated(); } ); }); menu->addAction(QStringLiteral("show details tab, animated"), this, [this]() { QTimer::singleShot(1000, this, [this]() { this->onReShowAnimated(); } ); }); menu->addSeparator(); menu->addAction(QStringLiteral("hide top bar"), this, [this]() { QTimer::singleShot(1000, this, [this]() { this->setTopBarVisible(false); } ); }); menu->addSeparator(); menu->addAction(QStringLiteral("test NotificationBox with error"), this, [this]() { QTimer::singleShot(1000, this, [this]() { showMessage(QStringLiteral("Test Title"), QStringLiteral("A long test text that notifies the user about something
that spreads over two lines to fit even more text."), LtsWidgets::LtsNotificationBox::MessageType::Error, [this](const QString &) { new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("You clicked the link!")); }, true, QInputDialog::getText(this, QStringLiteral("Identifier"), QStringLiteral("Identifier (keep empty to not set)"))); }); }); menu->addAction(QStringLiteral("test NotificationBox with success"), this, [this]() { QTimer::singleShot(1000, this, [this]() { showMessage(QStringLiteral("Test Title"), QStringLiteral("A long test text that notifies the user about something
that spreads over two lines to fit even more text."), LtsWidgets::LtsNotificationBox::MessageType::Success, [this](const QString &) { new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("You clicked the link!")); }, true, QInputDialog::getText(this, QStringLiteral("Identifier"), QStringLiteral("Identifier (keep empty to not set)"))); }); }); menu->addAction(QStringLiteral("test NotificationBox with info"), this, [this]() { QTimer::singleShot(1000, this, [this]() { showMessage(QStringLiteral("Test Title"), QStringLiteral("A long test text that notifies the user about something
that spreads over two lines to fit even more text."), LtsWidgets::LtsNotificationBox::MessageType::Info, [this](const QString &) { new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("You clicked the link!")); }, true, QInputDialog::getText(this, QStringLiteral("Identifier"), QStringLiteral("Identifier (keep empty to not set)"))); }); }); menu->addAction(QStringLiteral("test NotificationBox with warning"), this, [this]() { QTimer::singleShot(1000, this, [this]() { showMessage(QStringLiteral("Test Title"), QStringLiteral("A long test text that notifies the user about something
that spreads over two lines to fit even more text."), LtsWidgets::LtsNotificationBox::MessageType::Warning, [this](const QString &) { new LtsWidgets::LtsMessageDialogNew(this, QStringLiteral("You clicked the link!")); }, true, QInputDialog::getText(this, QStringLiteral("Identifier"), QStringLiteral("Identifier (keep empty to not set)"))); }); }); m_ui->pbDebug->setMenu(menu); #endif } void LtsBaseLayoutWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); if (!m_firstShow) return; m_firstShow = false; QSize rendererSizeHint = m_ui->topLayoutWidget->sizeHint(); rendererSizeHint.scale(rendererSizeHint.width() * 0.65, rendererSizeHint.height() * 0.65, Qt::IgnoreAspectRatio); const auto dpr = devicePixelRatioF(); { const QSize s(QSize(rendererSizeHint.height(), rendererSizeHint.height()) * dpr); // RDE SPLITTER LEFT QSvgRenderer svgRenderer; QPixmap pixmap(s); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); svgRenderer.load(QLatin1String(":rde_splitter_left.svg")); svgRenderer.render(&painter); painter.end(); pixmap.setDevicePixelRatio(dpr); m_ui->lbRdeSplitterLeft->setPixmap(pixmap); } { // PROJECT LOGO QString projectLogoFilename = QLatin1String(":project/logo.png"); QPixmap pixmap(projectLogoFilename); pixmap.setDevicePixelRatio(dpr); if (pixmap.isNull()) { qWarning() << "LoginWindow::c_tor: (PROJECT LOGO) file missing or is not an valid image '" << projectLogoFilename << "'"; } else { pixmap = pixmap.scaled(rendererSizeHint * dpr, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_ui->lbProjectLogo->setPixmap(pixmap); } } #ifdef TABLET_MODE QList scrollWidgets = findChildren(); for (auto i = scrollWidgets.begin(); i != scrollWidgets.end(); ++i) { QWidget *widget = *i; QVariant isConst = widget->property("const"); if (isConst.isValid() && isConst.toBool()) continue; addTouchScroll(widget); } #endif Splash::getInstance()->done(); } void LtsBaseLayoutWidget::addTouchScroll(QWidget *widget) { if (!widget) return; widget->setAttribute(Qt::WA_AcceptTouchEvents); QScrollArea *sca = qobject_cast(widget); if (sca) { QScroller::grabGesture(sca->viewport(), QScroller::LeftMouseButtonGesture); return; } QAbstractItemView *aiv = qobject_cast(widget); if (aiv) { QScroller::grabGesture(aiv->viewport(), QScroller::LeftMouseButtonGesture); aiv->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); aiv->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); return; } } void LtsBaseLayoutWidget::setAppName(const QString &appName) { m_ui->lbAppName->setText(appName); } void LtsBaseLayoutWidget::setExerciseData(const ExerciseData &data) { m_exerciseData = data; // copy m_ui->lbExerciseName->setText(data.exName.isEmpty() ? tr("no exercise") : data.exName); QString exerciseNameToolTip(tr("Exercise name")); if (!data.exName.isEmpty() && m_partitionId > 0) { exerciseNameToolTip += tr("\nPartition Id:"); exerciseNameToolTip += QStringLiteral(" %1").arg(m_partitionId); } if (!data.exDescription.isEmpty()) { exerciseNameToolTip += tr("\nDescription:"); exerciseNameToolTip += QStringLiteral(" %1").arg(data.exDescription); } m_ui->lbExerciseName->setToolTip(exerciseNameToolTip); LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); const bool enableEditPhases = ps->value(QStringLiteral("exm"), QStringLiteral("enableEditPhases")).toBool(); if (!enableEditPhases) { m_ui->lbExercisePhase->hide(); m_ui->bullet1->hide(); } m_ui->lbExercisePhase->setText(data.phName.isEmpty() ? tr("no phase") : data.phName); QString phaseNameToolTip(tr("Exercise phase")); if (!data.phDescription.isEmpty()) { phaseNameToolTip += tr("\nDescription:"); phaseNameToolTip += QStringLiteral(" %1").arg(data.phDescription); } m_ui->lbExercisePhase->setToolTip(phaseNameToolTip); m_ui->lbExerciseStatus->setText(data.exStateString.isEmpty() ? tr("no status") : data.exStateString); if (data.exStateCol.isValid() && data.exStateCol != QColor()) { QPalette p = m_ui->lbExerciseStatus->palette(); p.setColor(m_ui->lbExerciseStatus->backgroundRole(), data.exStateCol); p.setColor(m_ui->lbExerciseStatus->foregroundRole(), data.exStateCol); m_ui->lbExerciseStatus->setPalette(p); } m_ui->lbPartitionType->setText(data.partitionType); applyExerciseStateRestricition(); } void LtsBaseLayoutWidget::applyExerciseStateRestricition() { for (auto it = m_mapButtons.cbegin(); it != m_mapButtons.cend(); ++it) { bool visible = it.value().exerciseStates.testFlag(m_exerciseData.exState); it.key()->setButtonVisible(visible); } } void LtsBaseLayoutWidget::initilizationFinished() { // nothing... // QMap fKeys; // for(auto it = variantMap.cbegin(); it != variantMap.cend(); ++it) // fKeys[it.key().toInt()] = it.value().toString(); // setToolBarActions(settings); // setFunctionKeys(fKeys); } void LtsBaseLayoutWidget::setToolBarActions(const QStringList &actions) { QVector desiredActions; m_notFoundYet = actions; for (const QString &action : actions) { for (auto innerIt = m_mapButtons.cbegin(); innerIt != m_mapButtons.cend(); ++innerIt) { if (innerIt.value().identifier.compare(action) == 0) { desiredActions << innerIt.key(); m_notFoundYet.removeAll(action); } } } m_ui->toolBar->setMenuEntries(desiredActions); } QStringList LtsBaseLayoutWidget::toolBarActions() const { if (m_ui == nullptr || m_ui->toolBar == nullptr) return QStringList(); return m_ui->toolBar->menuEntries(); } void LtsBaseLayoutWidget::setFunctionKeys(const QMap &actions) { for (auto it = actions.cbegin(); it != actions.cend(); ++it) m_ui->toolBar->setFunctionKey(it.key(), it.value()); } QMap LtsBaseLayoutWidget::functionKeys() const { if (m_ui == nullptr) return QMap(); return m_ui->toolBar->functionKeys(); } void LtsBaseLayoutWidget::setSessionInfo(const int domainId, const IDL::PartitionType &partitionType, const int partitionId, const QString &workspaceId) { m_domainId = domainId; m_partitionType = partitionType; m_partitionId = partitionId; m_workspaceId = workspaceId; } // public void LtsBaseLayoutWidget::setOperatorName(const QString &operatorName, bool showTerminalButton) { m_operatorName = operatorName; if (m_operatorName.isEmpty()) m_operatorName = tr("Admin"); m_ui->profileButton->setToolTip(tr("Operator Name: %1").arg(m_operatorName)); m_showTerminalButton = showTerminalButton; } void LtsBaseLayoutWidget::setProfilePicture(const QPixmap &profilePicture) { m_ui->profileButton->setPixmap(profilePicture); } void LtsBaseLayoutWidget::slotOperatorMenuClicked() { bool debug = false; #ifndef LTS_DEBUG if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("DeveloperTools.Actions")) == QVariant(true)) #endif { debug = true; } LtsOperatorMenu *operatorMenu = new LtsOperatorMenu(m_operatorName, m_workspaceId, debug, m_screenCount, this); operatorMenu->setTerminalButton(m_showTerminalButton); bool hasOperatorSettings = false; for (const PluginData &pd : qAsConst(m_mapButtons)) { if (pd.identifier == QStringLiteral("_OpmShowOperatorSettings")) { hasOperatorSettings = true; break; } } operatorMenu->setOperatorSettingsButton(hasOperatorSettings); // available for Replay connect(operatorMenu, &LtsOperatorMenu::signalLogout, this, &LtsBaseLayoutWidget::slotLogout); connect(operatorMenu, &LtsOperatorMenu::signalExit, this, &LtsBaseLayoutWidget::slotClose); connect(operatorMenu, &LtsOperatorMenu::signalShowOperatorSettings, this, &LtsBaseLayoutWidget::signalShowOperatorSettings); connect(operatorMenu, &LtsOperatorMenu::signalSwap, this, &LtsBaseLayoutWidget::signalSwap); operatorMenu->show(); } bool LtsBaseLayoutWidget::haveUnsavedChanges() { if (LtsTabWidget::hasUnsavedChanges(getTabFromLayoutArea(LayoutArea::eDialogTab))) return true; if (LtsTabWidget::hasUnsavedChanges(getTabFromLayoutArea(LayoutArea::eInfoTab))) return true; if (LtsTabWidget::hasUnsavedChanges(getTabFromLayoutArea(LayoutArea::eMainTab))) return true; if (LtsTabWidget::hasUnsavedChanges(getTabFromLayoutArea(LayoutArea::eSpecialTab))) return true; return false; // no unsaved changes } //LtsUtils::Promise *notifyUnsavedChangesAsync() //{ // if (haveUnsavedChanges()) // { // LtsMessageDialog::showMessage(this, tr("There are dialogs with unsaved changes.
Please save or discard their changes before closing."), tr("Unsaved changes")); // return false; // } //} bool LtsBaseLayoutWidget::notifyUnsavedChanges() { if (isSignalConnected(QMetaMethod::fromSignal(&LtsBaseLayoutWidget::signalNotifyUnsavedChanges))) { bool reject = false; emit signalNotifyUnsavedChanges(reject); return reject; } else { if (haveUnsavedChanges()) { LtsWidgets::Buttons buttons; buttons.setFlag(Button::Yes, true); buttons.setFlag(Button::Cancel, true); QHash buttonTexts; buttonTexts.insert(Button::Yes, tr("Discard All")); const auto ret = LtsMessageDialog::showMessage(this, tr("There are dialogs with unsaved changes.
Please save or discard their changes before closing"), tr("Unsaved changes"),Behaviour::Modal, buttons, buttonTexts); return ret == Button::Yes; } } return true; } void LtsBaseLayoutWidget::slotLogout() // TODO: rm, then signal on signal { emit signalLogout(); } bool LtsBaseLayoutWidget::slotClose() { if (!notifyUnsavedChanges()) return false; // // no unsaved changes? delete everything // getTabFromLayoutArea(LayoutArea::eDialogTab)->clearAll(); // getTabFromLayoutArea(LayoutArea::eInfoTab)->clearAll(); // getTabFromLayoutArea(LayoutArea::eMainTab)->clearAll(); // getTabFromLayoutArea(LayoutArea::eSpecialTab)->clearAll(); // // save toolbar entries // auto toolBarItems = m_ui->toolBar->getMenuEntries(); // Q_FOREACH(const QString &entry, toolBarItems) // qCritical() << entry; // clearMenuActions(); // delete all menu entries // //delete m_ui; // now delete the ui, incl. widgets from any plugin, first ! // //m_ui = nullptr; // TODO: remove this !! // if (!m_progressDialogHelper) // m_progressDialogHelper = new LtsProgressDialogHelper(this); // m_progressDialogHelper->slotShowWaitForReplyWindow(); // this will run, until the LtsSession closes the BaseLayout - this will also delete the pdh, as it's parented hide(); emit signalClose(); //CloseRequest(m_domainId, m_partitionType, m_partitionId); return true; } //void LtsBaseLayoutWidget::removeNotification(LtsNotificationIcon *noti) //{ // ... //} void LtsBaseLayoutWidget::closeEvent(QCloseEvent *event) { event->ignore(); // do not close this BaseLayout, the Session will do this on signalCloseRequest ! slotClose(); } bool LtsBaseLayoutWidget::focusTab(const QString &menuGroup, const QString &identifier, LayoutArea area) { LtsTabWidget *tab = getTabFromLayoutArea(area); if (!tab) { qWarning() << "LtsBaseLayoutWidget::focusTab: tab, by enum type, not found!"; return false; } return tab->focusTab(menuGroup, identifier); } bool LtsBaseLayoutWidget::isDialogFocused(const LtsDialog *dialog, const LtsTabWidget *tab) const { QWidget *w = tab->widget(tab->currentIndex()); if (w == nullptr) return false; const LtsTabWidget *secondTab = qobject_cast(w); if (secondTab != nullptr) return isDialogFocused(dialog, secondTab); const LtsDialogHelper *helper = qobject_cast(w); if (helper != nullptr) return helper->compare(dialog); qWarning() << "LtsBaseLayoutWidget::isDialogFocused(LtsDialog *dialog, LtsTabWidget *tab): i found something weird.." << w->metaObject()->className(); return false; } bool LtsBaseLayoutWidget::isDialogFocused(const LtsDialog *dialog, const LayoutArea &layoutArea) const { const LtsTabWidget *tab = getTabFromLayoutArea(layoutArea); if (tab == nullptr) { qWarning() << "LtsBaseLayoutWidget::addWidget: tab, by enum type, not found!"; return false; } return isDialogFocused(dialog, tab); } bool LtsBaseLayoutWidget::isDialogFocused(const LtsDialog *dialog) const { return (isDialogFocused(dialog, LayoutArea::eDialogTab) || isDialogFocused(dialog, LayoutArea::eInfoTab) || isDialogFocused(dialog, LayoutArea::eSpecialTab) || isDialogFocused(dialog, LayoutArea::eMainTab)); } bool LtsBaseLayoutWidget::addOrReplaceWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area, bool setClosable, bool focusNewTab) { LtsTabWidget *tab = getTabFromLayoutArea(area); if (tab && dialog && (!identifier.isEmpty())) { QWidget *existingWidget = tab->getWidget(menuGroup, identifier); if (nullptr != existingWidget) { if (tab->removeWidget(existingWidget)) { existingWidget->deleteLater(); // bye bye existingWidget, may be deleteLater already has been called before // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling deleteLater multiple times is discuraged.. } else { qWarning() << __FUNCTION__ << " : Remove existing widget from TabWidget failed."; } } } return addWidget(dialog, menuGroup, identifier, area, setClosable, focusNewTab); } // bool LtsBaseLayoutWidget::addOrReplaceWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area, bool setClosable, bool focusNewTab) bool LtsBaseLayoutWidget::addWidget(LtsDialog *dialog, const QString &menuGroup, const QString &identifier, LayoutArea area, bool setClosable, bool focusNewTab) { if (!dialog) { qWarning() << "LtsBaseLayoutWidget::addWidget: cannot add null-widget!"; return false; } LtsTabWidget *tab = getTabFromLayoutArea(area); if (!tab) { qWarning() << "LtsBaseLayoutWidget::addWidget: tab, by enum type, not found!"; return false; } dialog->setMenuGroup(menuGroup); dialog->setIdentifier(identifier); QWidget /*LtsDialogHelper*/ *helper = dialog->getHelper(); if (!helper) // LtsDialog is in compatibility mode ! bcuz it's old { helper = dialog; // pretend that the helper exists dialog->setAttribute(Qt::WA_DeleteOnClose); // dialog must delete itself, as there is no Dialog Helper } if (!identifier.isEmpty()) // do not check for existing tabs, if ident is empty { QWidget *existingWidget = tab->getWidget(menuGroup, identifier); if (existingWidget != nullptr) // check here also if widget is already there (as widgets can call addWidget when e.g. editing something) { helper->deleteLater(); // delete existing widget's helper! if (focusNewTab) // "foxus existing tab" tab->focusTab(menuGroup, identifier); // animation tab is hidding? int indexOfSplitter = getIndexOfTab(tab); // animate if splitter is collapsed if (area == LayoutArea::eDialogTab && !(m_ui->mainSplitter->sizes().at(indexOfSplitter))) m_ui->mainSplitter->animate(1); else if (area == LayoutArea::eInfoTab && !(m_ui->masterSplitter->sizes().at(indexOfSplitter))) m_ui->masterSplitter->animate(1); return false; // already exists, do nothing, as focus is done } } dialog->setBaseLayoutWidget(this); connect(dialog, &LtsDialog::startMainSelect, this, &LtsBaseLayoutWidget::onHideAnimated); connect(dialog, &LtsDialog::endMainSelect, this, &LtsBaseLayoutWidget::onReShowAnimated); // Lock : emit signalStapleTopicLockHandler(dialog); // must be called before addTab, as addTab calls retranslate, which also might try a lock so the connection must be already made - this slot is called sync/directly - sequential dialog->triggerTopicLock(); // : Lock tab->addTab(menuGroup, identifier, helper, setClosable, focusNewTab); // tell the MAIN tab that he should add a new MAIN tab, when there is none, and to add a SUB tab on the MAIN tab, with the given widget // animate if splitter is collapsed if (tab->count() > 1) { int indexOfSplitter = getIndexOfTab(tab); if (area == LayoutArea::eDialogTab && !(m_ui->mainSplitter->sizes().at(indexOfSplitter))) { qDebug()<<"animate 1a: "; m_ui->mainSplitter->animate(1); } else if (area == LayoutArea::eInfoTab && !(m_ui->masterSplitter->sizes().at(indexOfSplitter))) { qDebug()<<"animate 1b: "; m_ui->masterSplitter->animate(1); } } //#define ADJUST_SPLITTER_SIZES_DIALOGTAB #ifdef ADJUST_SPLITTER_SIZES_DIALOGTAB /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * In layout area DialogTab dialogs are created and destroyed after use. The requested size of the tab widget changes every time. * But the superordinate splitter doesn't adjust its geometry automatically. Do it here manually to get the PlanEditor working now. * * JT 2017/02/10 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ if (LayoutArea::eDialogTab == area) { const QList & sizes = m_ui->mainSplitter->sizes(); int requestedTabHeight = tab->sizeHint().height(); int mainHeight = sizes[0]+sizes[1]-requestedTabHeight; m_ui->mainSplitter->setSizes( QList() << mainHeight << requestedTabHeight ); } #endif // ADJUST_SPLITTER_SIZES_DIALOGTAB return true; } LtsDialog *LtsBaseLayoutWidget::getWidget(const QString &menuGroup, const QString &identifier, LayoutArea area) { LtsTabWidget *tab = getTabFromLayoutArea(area); if (tab == nullptr) return nullptr; QWidget *w = tab->getWidget(menuGroup, identifier); if (w == nullptr) return nullptr; LtsDialogHelper *helper = qobject_cast(w); if (helper == nullptr) return nullptr; LtsDialog *dialog = qobject_cast(helper->getDialog()); return dialog; } void LtsBaseLayoutWidget::clearMenuActions() { // menu while (m_ui->naviActionsPlanning->count()) // remove all items m_ui->naviActionsPlanning->removeItem(0); while (m_ui->naviActionsExecution->count()) // remove all items m_ui->naviActionsExecution->removeItem(0); while (m_ui->naviActionsAfterAction->count()) // remove all items m_ui->naviActionsAfterAction->removeItem(0); while (m_ui->naviActionsSpecial->count()) // remove all items m_ui->naviActionsSpecial->removeItem(0); checkExercisePartButtonVisibility(); // map buttons for(auto it = m_mapButtons.begin(); it != m_mapButtons.end(); ++it) delete it.key(); m_mapButtons.clear(); } void LtsBaseLayoutWidget::executeCreateInstantFunctions(const PluginData &plData) { if (plData.createWidgetFunc) { LtsDialog *w = plData.createWidgetFunc(this); if (w) addWidget(w, plData.menuGroup, plData.identifier, plData.layoutArea, plData.closable); } if (plData.directFunc) { plData.directFunc(plData); } } void LtsBaseLayoutWidget::setExerciseTime(qint64 mSecsSinceEpoch) { m_exerciseTime = mSecsSinceEpoch; } LtsDragIcon *LtsBaseLayoutWidget::addMenuAction(const PluginData &plData, bool isFirstRun) { if (!LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(plData.operatorRight)) { qDebug() << " > Action" << plData.menuGroup << ":" << plData.menuText << "was not added due to missing operator right 0x" << QString::number(plData.operatorRight, 16); return nullptr; } if (isFirstRun) { switch (plData.createInstant) // init and show every action, that goes must be created instantly { case CreateInstantType::CreateInstantAndAddAction: { executeCreateInstantFunctions(plData); break; } case CreateInstantType::CreateInstantAndDiscardAction: { executeCreateInstantFunctions(plData); return nullptr; } default: // case CreateInstantType::DontCreateInstant: { break; } } } else { if (plData.createInstant == CreateInstantType::CreateInstantAndDiscardAction) // discarded actions are not shown, ignore them return nullptr; } bool err = false; if (plData.identifier.isEmpty()) { #ifdef LTS_DEBUG LtsWidgets::LtsMessageDialog::showMessage(this, QStringLiteral("STOP!\n" "Hier wurde ein Menu Entry hinzugefuegt, welcher ueber keinen Identifier verfuegt!\n" "Bitte aendern!\n" "Jetzt Debugger attachen und schauen woher der aufruf kommt.\n" "Da der Identifier fehlt kann ich Dir hier nicht sagen um welchen Menu Entry es sich handelt =)")); #endif err = true; } if (plData.menuGroup.isEmpty()) { #ifdef LTS_DEBUG LtsWidgets::LtsMessageDialog::showMessage(this, QStringLiteral("STOP!\n" "Hier wurde ein Menu Entry hinzugefuegt, welcher ueber keine Menu Group verfuegt!\n" "Bitte aendern!\n" "Jetzt Debugger attachen und schauen woher der aufruf kommt.\n" "Da der Identifier fehlt kann ich Dir hier nicht sagen um welchen Menu Entry es sich handelt =)")); #endif err = true; } if (err) return nullptr; bool found = false; if (!isFirstRun) // refetch { for (auto it = m_mapButtons.begin(); it != m_mapButtons.end(); ++it) { LtsDragIcon *thatIcon = it.key(); if (thatIcon->objectName() == plData.identifier) { thatIcon->setText(plData.menuText); thatIcon->setContext(plData.menuGroup); bool groupFound = false; // rename group for (int i = 0; i < m_ui->stackedWidget->count(); ++i) { LtsWidgets::LtsToolBox *tb = qobject_cast(m_ui->stackedWidget->widget(i)); int groupWidgetIndex = tb->indexOf(thatIcon->parentWidget() /* the group entry */); if (groupWidgetIndex >= 0) { tb->setItemText(groupWidgetIndex, plData.menuGroup); groupFound = true; break; } } if (!groupFound) { qWarning() << "group widget not found"; } found = true; break; } } if (!found) { //qWarning() << "Could not find and replace the menu item" << plData.identifier << " - creating it!"; } else { return nullptr; // only replace texts on re-adding actions } } LtsWidgets::LtsToolBox *tb = getToolBox(m_bg ? plData.exercisePart : ExercisePart::eSpecial); // otherwise create an action and add it to the menu QWidget *tmpW = tb->findChild(plData.menuGroup); if (!tmpW) { // no separator on first place if (plData.createWidgetFunc == nullptr && plData.directFunc == nullptr) return nullptr; // no tab for this menu group available -> create new one tmpW = new QWidget; tmpW->setObjectName(plData.menuGroup); LtsGridLayout *ltsGridLayout = new LtsGridLayout(LtsWidgets::ButtonSize::eDefault, tmpW); tmpW->setLayout(ltsGridLayout); tb->addItem(tmpW, plData.menuGroup); connect(tb, &LtsToolBox::resized, ltsGridLayout, &LtsGridLayout::onParentResize); } // layout should now already be here ... QLayout *widgetLayout = tmpW->layout(); LtsGridLayout *ltsGridLayout = qobject_cast(widgetLayout); if (!ltsGridLayout) { //ERROR_LOG(m_logger) << "temp widget has no layout! cannot add action to menu!"; qWarning() << "temp widget has no layout! cannot add action" << plData.menuText << "to menu! (the bad widget was of type" << tmpW->metaObject()->className() << ") [did you forget to add the menuGroup to that action?]"; return nullptr; } // remove old spacer (a new one is created at the end) if (widgetLayout->count() > 0) { QSpacerItem *spacer = dynamic_cast(widgetLayout->itemAt(widgetLayout->count() - 1)); if (spacer) { ltsGridLayout->removeItem(spacer); delete spacer; } } // create entry LtsDragIcon *dragIcon = nullptr; if (plData.createWidgetFunc == nullptr && plData.directFunc == nullptr) { QFrame *line = new QFrame(tmpW); line->setLineWidth(2); line->setFrameStyle(QFrame::HLine); // add to layout ltsGridLayout->add_widget(line); } else { if (isFirstRun || !found) { dragIcon = createButton(plData); // add to layout ltsGridLayout->add_widget(dragIcon); m_mapButtons.insert(dragIcon, plData); // FIXME: rm destroyed buttons !! auto it = std::find_if(m_notFoundYet.begin(), m_notFoundYet.end(), [plData](const QString &s) { return s == plData.identifier; }); if (it != m_notFoundYet.end()) // found { m_ui->toolBar->addMenuEntry(dragIcon); m_notFoundYet.erase(it); } } } checkExercisePartButtonVisibility(); // add spacer at end QSpacerItem *spacer = new QSpacerItem(10, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); ltsGridLayout->addItem(spacer, ltsGridLayout->rowCount(), 0); return dragIcon; } void LtsBaseLayoutWidget::addNotification(QToolButton *noti) { m_ui->notificationLayoutWidget->layout()->addWidget(noti); } bool LtsBaseLayoutWidget::notificationExists(const QString &identifier) const { return (m_ui->notificationLayoutWidget->findChild(identifier, Qt::FindDirectChildrenOnly) != nullptr); } bool LtsBaseLayoutWidget::removeMenuAction(const QString &byIdentifier) { for (auto it = m_mapButtons.begin(); it != m_mapButtons.end(); ++it) { if (it.value().identifier == byIdentifier) return removeMenuAction(it.key()); } return false; } bool LtsBaseLayoutWidget::removeMenuAction(const PluginData &plData) { for (auto it = m_mapButtons.begin(); it != m_mapButtons.end(); ++it) { if (it.value().identifier == plData.identifier) return removeMenuAction(it.key()); } return false; } bool LtsBaseLayoutWidget::removeMenuAction(LtsDragIcon *icon) { auto it = m_mapButtons.find(icon); if (it == m_mapButtons.end()) return false; const QString menuGroup = it->menuGroup; const ExercisePart ep = it->exercisePart; m_mapButtons.erase(it); icon->setParent(nullptr); icon->deleteLater(); checkMenuGroup(menuGroup, ep); return true; } void LtsBaseLayoutWidget::executeAction(const QString &actionIdentifier) { const QList plDatas(m_mapButtons.values()); auto it = std::find_if(plDatas.constBegin(), plDatas.constEnd(), [actionIdentifier](const PluginData &plData) { return (plData.identifier == actionIdentifier); }); if (it == plDatas.constEnd()) return; executePluginData(*it); } void LtsBaseLayoutWidget::executePluginData(const PluginData &plData) { const QString &identifier = plData.identifier; const QString &menuGroup = plData.menuGroup; LtsTabWidget *tab = getTabFromLayoutArea(plData.layoutArea); if (!tab->focusTab(menuGroup, identifier)) // focus widget - is there a widget to focus? no? add a new one! yes? focus and do nothing.. { //qWarning() << "LtsBaseLayoutWidget::onMenuEntryClicked: cannot focus - creating new widget!"; if (plData.createWidgetFunc) { LtsDialog *w = plData.createWidgetFunc(this); if (w) { //qWarning() << "LtsBaseLayoutWidget::onMenuEntryClicked: adding widget!"; addWidget(w, menuGroup, identifier, plData.layoutArea, plData.closable); } else { //qWarning() << "LtsBaseLayoutWidget::onMenuEntryClicked: no widget to add.."; } } else { if (plData.directFunc) plData.directFunc(plData); } } else { //qWarning() << "LtsBaseLayoutWidget::onMenuEntryClicked: focused!"; } } //SLOTS void LtsBaseLayoutWidget::onMenuEntryClicked() { LtsDragIcon *btn = qobject_cast(sender()); if (!btn) return; if (!m_mapButtons.contains(btn)) return; executePluginData(m_mapButtons.value(btn)); } void LtsBaseLayoutWidget::resetLayoutVisibility() { for (auto i = m_oldLayoutVisibilityStates.cbegin(); i != m_oldLayoutVisibilityStates.cend(); ++i) { const auto &tabPtr = i.key(); if (tabPtr) tabPtr->setVisible(i.value()); } m_oldLayoutVisibilityStates.clear(); } void LtsBaseLayoutWidget::hideVisibleLayouts() { if (!m_oldLayoutVisibilityStates.isEmpty()) { qWarning() << "LtsBaseLayoutWidget::hideVisibleLayouts: old states available - ignoring"; return; } LtsWidgets::LtsTabWidget *infoTab = getTabFromLayoutArea(LtsWidgets::LayoutArea::eInfoTab); LtsWidgets::LtsTabWidget *specialTab = getTabFromLayoutArea(LtsWidgets::LayoutArea::eSpecialTab); LtsWidgets::LtsTabWidget *dialogTab = getTabFromLayoutArea(LtsWidgets::LayoutArea::eDialogTab); m_oldLayoutVisibilityStates.insert(infoTab, infoTab->isVisible()); m_oldLayoutVisibilityStates.insert(specialTab, specialTab->isVisible()); m_oldLayoutVisibilityStates.insert(dialogTab, dialogTab->isVisible()); // hiding an invisible tab makes the splitter reduce the tab's size to 0 - only hide if visible if (infoTab->isVisible()) infoTab->setVisible(false); if (specialTab->isVisible()) specialTab->setVisible(false); if (dialogTab->isVisible()) dialogTab->setVisible(false); } void LtsBaseLayoutWidget::setTopBarVisible(bool visible) { m_ui->topLayoutWidget->setVisible(visible); } bool LtsBaseLayoutWidget::isTopBarVisible() const { return m_ui->topLayoutWidget->isVisible(); } bool LtsBaseLayoutWidget::isMenuCollapsed() const { return m_menuCollapsed; } void LtsBaseLayoutWidget::setMenuVisible(bool visible) { m_ui->rightSplitter->setVisible(visible); } bool LtsBaseLayoutWidget::isMenuVisible() const { return m_ui->rightSplitter->isVisible(); } void LtsBaseLayoutWidget::collapseMenu(bool collapse) { m_menuCollapsed = collapse; if (collapse) { m_ui->masterSplitter->setSizes(m_menuSplitterSizes); } else { m_menuSplitterSizes = m_ui->masterSplitter->sizes(); m_ui->masterSplitter->setSizes(QList() << 1 /* main, info, etc. */ << 0 /* menu */); } } void LtsBaseLayoutWidget::onReShowAnimated() { QPropertyAnimation *animation = new QPropertyAnimation(m_ui->mainSplitter, "sizes", this); animation->setDuration(500); //Save Old Sizes QList start = m_mainSplitterSizes; start[getIndexOfTab(getTabFromLayoutArea(LayoutArea::eDialogTab))] = 0; QList end = m_mainSplitterSizes; animation->setStartValue(QVariant::fromValue(start)); animation->setEndValue(QVariant::fromValue(end)); connect(animation, &QPropertyAnimation::finished, this, [this]() { m_currentAnimation = nullptr; }); m_ui->detailWidget->show(); animation->start(QAbstractAnimation::DeleteWhenStopped); m_isHidden = false; delete m_currentAnimation; m_currentAnimation = animation; } void LtsBaseLayoutWidget::onHideAnimated() { if (m_currentAnimation) { delete m_currentAnimation; m_currentAnimation = nullptr; } QPropertyAnimation *animation = new QPropertyAnimation(m_ui->mainSplitter, "sizes", this); animation->setDuration(500); //Save Old Sizes m_mainSplitterSizes = m_ui->mainSplitter->sizes(); QList start = m_ui->mainSplitter->sizes(); QList end = start; end[getIndexOfTab(getTabFromLayoutArea(LayoutArea::eDialogTab))] = 1; animation->setStartValue(QVariant::fromValue(start)); animation->setEndValue(QVariant::fromValue(end)); connect(animation, &QPropertyAnimation::finished, this, &LtsBaseLayoutWidget::hideWidgets); connect(animation, &QPropertyAnimation::finished, this, [this]() { m_currentAnimation = nullptr; }); animation->start(QAbstractAnimation::DeleteWhenStopped); m_isHidden = true; m_currentAnimation = animation; } void LtsBaseLayoutWidget::retranslate() { emit signalRetranslateActions(); setExerciseData(m_exerciseData); m_ui->retranslateUi(this); // etc. } void LtsBaseLayoutWidget::changeEvent(QEvent *e) { QWidget::changeEvent(e); if (e->type() == QEvent::LanguageChange) retranslate(); } int LtsBaseLayoutWidget::getIndexOfTab(LtsTabWidget *tab) { int result = 0; // emergency value result = m_ui->masterSplitter->indexOf(tab); if (result > -1) // found? return result; // not found? result = m_ui->mainSplitter->indexOf(tab); if (result > -1) // now found? return result; // still not found? result = m_ui->rightSplitter->indexOf(tab); if (result > -1) // and now? return result; qWarning() << "LtsBaseLayoutWidget::getIndexOfTab: tab not found in any splitter.."; return result; } LtsToolBox* LtsBaseLayoutWidget::getToolBox(const ExercisePart &exercisePart) const { switch (exercisePart) { case ExercisePart::eAfterAction: return m_ui->naviActionsAfterAction; case ExercisePart::eExecution: return m_ui->naviActionsExecution; case ExercisePart::ePlanning: return m_ui->naviActionsPlanning; case ExercisePart::eSpecial: return m_ui->naviActionsSpecial; } Q_ASSERT(false); return nullptr; } QPushButton* LtsBaseLayoutWidget::getExercisePartButton(const ExercisePart &exercisePart) const { switch (exercisePart) { case ExercisePart::eAfterAction: return m_ui->pbAfterAction; case ExercisePart::eExecution: return m_ui->pbExecution; case ExercisePart::ePlanning: return m_ui->pbPlanning; case ExercisePart::eSpecial: return m_ui->pbSpecial; } Q_ASSERT(false); return nullptr; } void LtsBaseLayoutWidget::checkMenuGroup(const QString &menuGroup, const ExercisePart &exercisePart) { QWidget *tmpW = getToolBox(exercisePart)->findChild(menuGroup); if (!tmpW) return; // should not happen #ifdef NEVER_PRINT_DEBUG for (int i = 0; i < tmpW->children().size(); i++) { QObject * whatEver = tmpW->children().at( i ); const QString & name = whatEver->objectName(); qCritical() << "MenuGroup child " << i << " : " << name; } #endif // Something is wrong here! The printing shows an additional object besides the // LtsDragIcons of the menuGroup. // As workaround chnage condition // if (tmpW->children().size() == 0) to if (tmpW->children().size() <= 1) // JT 2016/08/03 delete tmpW; checkExercisePartButtonVisibility(); } void LtsBaseLayoutWidget::checkExercisePartButtonVisibility() { if (nullptr == m_bg) { for (int ep = (int)ExercisePart::ePlanning; ep <= (int)ExercisePart::eSpecial; ++ep) { getExercisePartButton(static_cast(ep))->setVisible(false); } return; } int visibleButtons = 0; ExercisePart lastVisibleButton = ExercisePart::ePlanning; for (int ep = (int) ExercisePart::ePlanning; ep <= (int) ExercisePart::eSpecial; ++ep) { bool visible = getToolBox((ExercisePart) ep)->children().count() > 1; getExercisePartButton((ExercisePart) ep)->setVisible(visible); if (visible) { ++visibleButtons; lastVisibleButton = (ExercisePart) ep; } } if (visibleButtons == 0) lastVisibleButton = ExercisePart::eSpecial; getExercisePartButton(lastVisibleButton)->setChecked(true); getExercisePartButton(lastVisibleButton)->setVisible(visibleButtons > 1); } bool LtsBaseLayoutWidget::isWidgetsHidden() const { return m_ui->detailWidget->isVisible(); } void LtsBaseLayoutWidget::showWidgets() { m_ui->detailWidget->show(); } void LtsBaseLayoutWidget::hideWidgets() { m_ui->detailWidget->hide(); } LtsDragIcon *LtsBaseLayoutWidget::createButton(const PluginData &plData) { // TODO: really nullptr? LtsDragIcon *tmpBtn = new LtsDragIcon(nullptr); tmpBtn->setObjectName(plData.identifier); tmpBtn->setButtonStyle(LtsWidgets::ButtonStyle::eTextNextToIcon); tmpBtn->setIcon(plData.menuIcon); tmpBtn->setContext(plData.menuGroup); tmpBtn->setText(plData.menuText); if (!plData.tooltip.isEmpty()) tmpBtn->setToolTip(plData.tooltip); tmpBtn->setDropHandlerKey(plData.dropHandlerKey); tmpBtn->setIdentifier(plData.identifier); connect(tmpBtn, &QWidget::destroyed, this, []() { qDebug() << "LtsDragIcon was destroyed"; }); connect(tmpBtn, &LtsDragIcon::clicked, this, &LtsBaseLayoutWidget::onMenuEntryClicked); return tmpBtn; } LtsToolBarArea * LtsBaseLayoutWidget::getToolbar() const { return m_ui->toolBar; } QWidget *LtsBaseLayoutWidget::getMenu() const { return m_ui->naviWidget; } LtsTabWidget *LtsBaseLayoutWidget::getTabFromLayoutArea(LayoutArea area) const { switch (area) { case LayoutArea::eDialogTab: return m_ui->detailWidget; case LayoutArea::eInfoTab: return m_ui->infoWidget; case LayoutArea::eSpecialTab: return m_ui->specialWidget; case LayoutArea::eMainTab: return m_ui->mainWidget; case LayoutArea::eUnknown: // better than returning a 0ptr return m_ui->detailWidget; } return nullptr; } void LtsBaseLayoutWidget::showNextQueuedMessage() { if (m_messageQueue.isEmpty()) { return; } const Message m = m_messageQueue.takeLast(); showMessage(m.title, m.text, m.type, m.clickedLinkCallback, m.closeOnLinkClicked, m.identifier); } void LtsBaseLayoutWidget::removeQueuedMessage(const QString &messageIdentifier) { if (messageIdentifier.isEmpty()) // empty / unset identifiers can't be removed { return; } m_messageQueue.erase(std::remove_if(m_messageQueue.begin(), m_messageQueue.end(), [messageIdentifier](const Message &m) // remove all queued messages with the same identifier { return (m.identifier == messageIdentifier); }), m_messageQueue.end()); if (!m_currentMessage.isEmpty()) { if (m_currentMessage.first().identifier == messageIdentifier) { // this is the current active message - hide it and call show next queued message (if any) m_currentMessage.clear(); m_notiBox->hide(); showNextQueuedMessage(); } } } void LtsBaseLayoutWidget::showMessage(const QString &title, const QString &text, LtsNotificationBox::MessageType type, const std::function &clickedLinkCallback, bool closeOnLinkClicked, const QString &identifier) { if (m_notiBox == nullptr) { m_notiBox = new LtsNotificationBox; connect(m_notiBox, &LtsNotificationBox::signalLinkClicked, this, [this, closeOnLinkClicked, clickedLinkCallback](const QString &link) { // qWarning() << "link" << link; auto *event = new LtsLinkEvent(QUrl(link), this); event->setAccepted(false); QApplication::postEvent(qApp, event); // receive link events by overriding eventFilter of your *parented* QObject class if (clickedLinkCallback) clickedLinkCallback(link); if (closeOnLinkClicked) { m_notiBox->hide(); m_currentMessage.clear(); showNextQueuedMessage(); // show next message (if any) after closing current message } }); connect(m_notiBox, &QObject::destroyed, this, [this]() { if (qobject_cast(sender()) == m_notiBox) m_notiBox = nullptr; }); connect(m_notiBox, &LtsNotificationBox::signalClose, this, [this]() { m_notiBox->hide(); m_currentMessage.clear(); showNextQueuedMessage(); // show next message (if any) after closing current message }); connect(m_notiBox, &LtsNotificationBox::signalAck, this, [this]() { auto *event = new LtsLinkEvent(QUrl(QStringLiteral("lts://NotificationAck/%1").arg(m_currentMessage.first().identifier)), this); event->setAccepted(false); QApplication::postEvent(qApp, event); // send link event to tell NotificationManagement to ack this notification m_notiBox->hide(); m_currentMessage.clear(); showNextQueuedMessage(); // show next message (if any) after closing current message }); m_notiBox->setParent(this); m_notiBox->setGeometry(0, 0, m_notiBox->width(), m_notiBox->height()); } m_messageQueue.erase(std::remove_if(m_messageQueue.begin(), m_messageQueue.end(), [identifier](const Message &m) // remove all queued messages with the same identifier (if not empty) { return (!identifier.isEmpty() && m.identifier == identifier); }), m_messageQueue.end()); if (!m_currentMessage.isEmpty()) { const auto m = m_currentMessage.takeFirst(); if (identifier.isEmpty() || m.identifier != identifier) // replace current message with new message if identifier is same (and not empty) { m_messageQueue += m; } } m_currentMessage += Message({ title, text, type, clickedLinkCallback, closeOnLinkClicked, identifier }); m_notiBox->message(title, text, type); // TODO: ahtc set new x on resize auto *animation = new QPropertyAnimation(m_notiBox, "geometry"); connect(animation, &QPropertyAnimation::finished, animation, &QObject::deleteLater); animation->setDuration(700); animation->setStartValue(QRect(width() + 10, height() + 10, m_notiBox->width(), m_notiBox->height())); animation->setEndValue(QRect(width() - m_notiBox->width() - 15, height() - m_notiBox->height() - 15, m_notiBox->width(), m_notiBox->height())); animation->setEasingCurve(QEasingCurve::OutElastic); animation->start(QAbstractAnimation::DeleteWhenStopped); } bool LtsBaseLayoutWidget::messageIdentifierExists(const QString &identifier) const { if (identifier.isEmpty()) // empty / unset identifiers never exist { return false; } if (!m_currentMessage.isEmpty() && m_currentMessage.first().identifier == identifier) { return true; } for (const Message &m : qAsConst(m_messageQueue)) { if (m.identifier == identifier) { return true; } } return false; } } // namespace LtsWidgets //