Files
qt6-migration-pipeline/Lts/Common/LtsWidgets/lts_tab_widget.cpp
T

895 lines
17 KiB
C++
Executable File

#include "lts_tab_widget.h"
#include "lts_tab_bar.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsWidgets/lts_dialog_helper.h>
#include <LtsWidgets/lts_splitter.h>
#include <QAbstractButton>
#include <QDialog>
#include <QDebug>
#include <QSize>
#include <QPaintEvent>
#include <QPainter>
#include <QBrush>
#include <QApplication>
#include <QColor>
#include <QPen>
#include <QFontMetrics>
#include <QRectF>
#include <QLineF>
#include <QPointF>
#include <math.h>
#include <QTimer>
#include <QLabel>
namespace LtsWidgets
{
LtsTabWidget::LtsTabWidget(QWidget *parent, bool isSecondary)
: QTabWidget(parent)
, m_tabbar(nullptr)
, m_hasSubTabs(false)
, m_isSecondary(isSecondary)
, m_mainSplitterState(QByteArray())
{
setDocumentMode(true);
setMinimumSize(200, 100);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
LtsTabBar *tabBar = new LtsTabBar(isSecondary, this);
setTabBar(tabBar);
QFont currentFont = tabBar->font();
QFont::Capitalization c = QFont::SmallCaps;
bool bold = true;
if (m_isSecondary)
{
c = QFont::MixedCase;
bold = false;
}
currentFont.setCapitalization(c);
currentFont.setBold(bold);
tabBar->setFont(currentFont);
connect(this, &LtsTabWidget::currentChanged,
this, &LtsTabWidget::slotCurrentChanged);
QSplitter *splitter = static_cast<QSplitter*>(parent);
if (splitter &&
splitter->objectName() == QStringLiteral("mainSplitter") &&
splitter->widget(1) == this &&
!isSecondary)
{
int tabBarHeight = m_tabbar->height() * 1.33;
int tabBarWidth = 20;
setStyleSheet(styleSheet() + QStringLiteral("QTabWidget::tab-bar {"
"left: %1px; "
"} ").arg(tabBarWidth));
QPushButton *maxBtn = new QPushButton(this);
maxBtn->setObjectName(QStringLiteral("MinMaxTabButton"));
maxBtn->setText(QStringLiteral("▲\n▼"));
maxBtn->setFixedSize(tabBarWidth, tabBarHeight * 2);
maxBtn->setStyleSheet(QStringLiteral("QPushButton { "
"border-radius: 0px; "
"border: 1px solid gray; "
"} "));
connect(maxBtn, &QPushButton::clicked, this, &LtsTabWidget::slotMaximizeButtonClicked);
}
}
LtsTabWidget::~LtsTabWidget()
{
clearAll();
}
void LtsTabWidget::slotCurrentChanged(int newIdx)
{
if (newIdx < 0)
return;
QWidget *w = widget(newIdx);
if (w == nullptr)
return;
LtsDialogHelper *dialog1 = qobject_cast<LtsDialogHelper *>(w);
if (dialog1 != nullptr)
{
LtsDialog *mainDialog = dialog1->getDialog();
if (mainDialog != nullptr)
emit mainDialog->signalFocused();
return;
}
LtsTabWidget *tab = qobject_cast<LtsTabWidget *>(w);
if (tab != nullptr)
{
LtsDialogHelper *dialog2 = qobject_cast<LtsDialogHelper *>(tab->widget(tab->currentIndex()));
if (dialog2 != nullptr)
{
LtsDialog *mainDialog = dialog2->getDialog();
if (mainDialog != nullptr)
emit mainDialog->signalFocused();
return;
}
}
}
void LtsTabWidget::slotMaximizeButtonClicked()
{
LtsSplitter *splitter = static_cast<LtsSplitter*>(parent());
if (splitter)
{
if (splitter->widget(0)->height() != splitter->widget(0)->minimumHeight())
{
m_mainSplitterState = splitter->saveState();
splitter->collapseChild(0);
}
else
{
splitter->restoreState(m_mainSplitterState);
}
}
}
void LtsTabWidget::closeRequestTabWidget(LtsTabWidget *tabWidget)
{
for (int i = 0; i < tabWidget->count(); i++)
{
QWidget *w = tabWidget->widget(i);
LtsDialogHelper *helper = qobject_cast<LtsDialogHelper *>(w);
if (helper)
{
LtsDialog *dlg = qobject_cast<LtsDialog *>(helper->getDialog());
if (dlg)
dlg->closeRequest();
}
LtsTabWidget *tab = qobject_cast<LtsTabWidget *>(w);
if (tab)
closeRequestTabWidget(tab);
}
}
void LtsTabWidget::slotTabCloseRequested(int index)
{
QWidget *w = widget(index);
if (w == nullptr)
return;
LtsDialogHelper *helper = qobject_cast<LtsDialogHelper *>(w);
if (helper != nullptr)
{
LtsDialog *dialog = helper->getDialog();
if (dialog != nullptr)
{
if (dialog->isClosable())
{
setCurrentIndex(index);
dialog->closeRequest();
return;
}
return;
}
}
LtsTabWidget *tab = qobject_cast<LtsTabWidget *>(w);
if (tab != nullptr)
{
if (LtsTabWidget::hasUnsavedChanges(tab))
{
LtsWidgets::Buttons buttons;
buttons.setFlag(Button::Yes, true);
buttons.setFlag(Button::Cancel, true);
QHash<Button, QString> buttonTexts;
buttonTexts.insert(Button::Yes, tr("Discard All"));
const auto ret = LtsMessageDialog::showMessage(this, tr("There are dialogs with unsaved changes.<br>Please save or discard their changes before closing."), tr("Unsaved changes"), Behaviour::Modal, buttons, buttonTexts);
switch (ret)
{
case Button::Yes:
tab->deleteLater();
return;
default:
return;
}
}
else
{
closeRequestTabWidget(tab);
}
}
w->deleteLater();
}
void LtsTabWidget::onCheckTabBarVisibility()
{
if (!m_isSecondary)
{
if (count() < 1)
setHidden(true);
}
else
{
if (count() < 1)
deleteLater();
}
}
void LtsTabWidget::setSubTabsVisible(bool visible)
{
if (count())
{
return;
}
m_hasSubTabs = visible;
}
void LtsTabWidget::addTab(const QString &menuGroup, const QString &identifier, QWidget *widget,
bool setClosable, bool focusNewTab)
{
setFocus();
LtsTabWidget *tab = this;
QString fullName;
if (m_hasSubTabs)
{
fullName = identifier;
tab = qobject_cast<LtsTabWidget *>(m_tabMap.value(menuGroup, nullptr));
if (!tab)
{
tab = new LtsTabWidget(nullptr, true);
tab->setMovable(true);
addWidget(menuGroup, tab, true , focusNewTab);
}
else
{
tab->setHidden(false);
}
if (focusNewTab)
setCurrentIndex(indexOf(tab));
}
else
{
fullName = menuGroup + QLatin1Char('.') + identifier;
}
setHidden(false);
tab->addWidget(fullName, widget, setClosable, focusNewTab);
if (m_hasSubTabs)
{
LtsTabBar *tabbar = tab->tabBar();
for (int i = 0; i < tabbar->count(); i++)
{
if (tabbar->tabButton(i, QTabBar::RightSide)->isHidden())
{
m_tabbar->tabButton(currentIndex(), QTabBar::RightSide)->hide();
break;
}
}
}
}
void LtsTabWidget::addWidget(const QString &fullName, QWidget *widget, bool setClosable, bool focusNewTab)
{
QString theName = fullName;
if (fullName.isEmpty())
{
const QString possibleCharacters(QStringLiteral("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
const int randomStringLength = 24;
QString randomString;
for (int i = 0; i < randomStringLength; i++)
{
int index = qrand() % possibleCharacters.length();
QChar nextChar = possibleCharacters.at(index);
randomString.append(nextChar);
}
theName = randomString;
}
widget->setObjectName(theName);
LtsDialogHelper *helper = qobject_cast<LtsDialogHelper *>(widget);
if (helper)
helper->retranslate();
QString title = widget->windowTitle();
if (title.isEmpty())
{
if (helper)
title = QStringLiteral("retranslate() fehlt!");
else
title = widget->objectName();
}
if (helper)
{
connect(helper->getDialog(), &LtsDialog::signalHide, this, static_cast<void (LtsTabWidget::*)(QObject*)>(&LtsTabWidget::onRemoveTab),
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
}
connect(widget, &QWidget::destroyed, this, static_cast<void (LtsTabWidget::*)(QObject*)>(&LtsTabWidget::onRemoveTab), Qt::DirectConnection);
connect(widget, &QWidget::windowTitleChanged, this, &LtsTabWidget::slotWindowTitleChanged);
if (helper)
{
m_tabMap.insert(theName, helper);
}
else
{
m_tabMap.insert(theName, widget);
}
int newTabIndex = QTabWidget::addTab(widget, title);
#ifdef LTS_DEBUG
setTabToolTip(newTabIndex, tr("DEBUG: WindowName: %1").arg(theName));
#endif
if (focusNewTab)
setCurrentIndex(newTabIndex);
LtsTabBar *tabbar = qobject_cast<LtsTabBar *>(tabBar());
if (tabbar)
{
CloseButton *closeBtn = new CloseButton();
connect(closeBtn, &QPushButton::clicked, tabbar, &LtsTabBar::_internal_closeTab);
tabbar->setTabButton(newTabIndex, QTabBar::RightSide, closeBtn);
}
if (!setClosable)
m_tabbar->tabButton(newTabIndex, QTabBar::RightSide)->hide();
if (tabbar)
tabbar->updateCloseButtonColors();
}
void LtsTabWidget::slotWindowTitleChanged(const QString &title)
{
QWidget *w = qobject_cast<QWidget *>(sender());
if (!w)
{
qWarning() << "LtsTabWidget: slotWindowTitleChanged failed! (not a QWidget) " << sender();
return;
}
int index = indexOf(w);
if (index < 0)
{
qWarning() << "LtsTabWidget: slotWindowTitleChanged failed! (widget not found on TabBar)";
return;
}
QString realTitle = title;
QString readOnlyHint;
QStringList sl = title.split(QStringLiteral("- READ-ONLY"));
if (sl.size() == 2)
{
realTitle = sl.first().trimmed();
readOnlyHint = sl.back().trimmed();
if (readOnlyHint.startsWith(QStringLiteral("(")))
readOnlyHint.remove(0,1);
if (readOnlyHint.endsWith(QStringLiteral(")")))
readOnlyHint.remove(readOnlyHint.size()-1,1);
readOnlyHint = tr("Locked by: %1").arg(readOnlyHint);
}
setTabText(index, realTitle);
QTabBar *tabBar = this->tabBar();
if (!readOnlyHint.isEmpty())
{
QLabel *readOnly = new QLabel(QStringLiteral("<b>READ-ONLY</b>"), this);
readOnly->setToolTip(readOnlyHint);
readOnly->setPixmap(QIcon(QStringLiteral("icon:warning.svg")).pixmap(QSize(25, 25)));
tabBar->setTabButton(index, QTabBar::ButtonPosition::LeftSide, readOnly);
} else {
QWidget *readOnly = tabBar->tabButton(index, QTabBar::ButtonPosition::LeftSide);
if (readOnly)
{
tabBar->setTabButton(index, QTabBar::ButtonPosition::LeftSide, nullptr);
readOnly->setParent(this);
delete readOnly;
}
}
}
QWidget *LtsTabWidget::getWidget(const QString &menuGroup, const QString &identifier)
{
LtsTabWidget *tab = this;
QString fullName;
if (m_hasSubTabs)
{
fullName = identifier;
tab = qobject_cast<LtsTabWidget *>(m_tabMap.value(menuGroup, nullptr));
if (tab == nullptr)
return nullptr;
}
else
{
fullName = menuGroup + QLatin1Char('.') + identifier;
}
return tab->getWidget(fullName);
}
QWidget *LtsTabWidget::getWidget(const QString &fullName)
{
return m_tabMap.value(fullName, nullptr);
}
bool LtsTabWidget::focusTab(const QString &menuGroup, const QString &identifier)
{
QPointer<LtsTabWidget> tab = this;
QString fullName;
if (m_hasSubTabs)
{
fullName = identifier;
tab = qobject_cast<LtsTabWidget *>(m_tabMap.value(menuGroup, nullptr));
if (!tab)
{
return false;
}
setCurrentIndex(indexOf(tab));
}
else
fullName = menuGroup + QLatin1Char('.') + identifier;
if (!tab)
return false;
LtsSplitter *splitter = static_cast<LtsSplitter*>(parent());
QTimer::singleShot(500, this, [splitter, tab] (){
if (splitter && tab && (tab->count() >= 1) && splitter->sizes().count() && !(splitter->sizes().at(1)))
{
splitter->animate(1);
}
});
return tab->focusWidget(fullName);
}
bool LtsTabWidget::focusWidget(const QString &fullName)
{
QWidget *widget = m_tabMap.value(fullName, nullptr);
if (widget == nullptr)
return false;
setCurrentIndex(indexOf(widget));
widget->setFocus();
return true;
}
bool LtsTabWidget::removeWidget(QWidget * widget)
{
if (nullptr == widget)
return false;
for (auto i = m_tabMap.begin(); i != m_tabMap.end(); ++i)
{
QWidget *w = i.value();
if (w == nullptr)
continue;
if (w == widget)
{
m_tabMap.erase(i);
disconnect(widget, &QWidget::destroyed, this, static_cast<void (LtsTabWidget::*)(QObject*)>(&LtsTabWidget::onRemoveTab));
return true;
}
if (m_hasSubTabs)
{
LtsTabWidget *tab = qobject_cast<LtsTabWidget*>(w);
if (tab != nullptr && tab->removeWidget(widget))
{
return true;
}
}
}
return false;
}
void LtsTabWidget::onRemoveTab(QObject *object)
{
if (object == nullptr)
{
qWarning() << "onRemoveTab: obj == nullptr";
return;
}
QWidget *w = static_cast<QWidget *>(object);
bool rmd = false;
for (auto i = m_tabMap.begin(); i != m_tabMap.end(); ++i)
{
if (i.value() == w)
{
m_tabMap.erase(i);
rmd = true;
break;
}
}
if (!rmd)
{
qWarning() << "LtsTabWidget::onRemoveTab: widget to remove was not on tab map!";
}
int index = indexOf(w);
if (index < 0)
{
qWarning() << "LtsTabWidget::onRemoveTab: cannot remove widget, as it was not found on the tab widget" << (void *)w;
}
else
{
removeTab(index);
}
onCheckTabBarVisibility();
}
void LtsTabWidget::setActuatorTabStyle(bool enable)
{
if (enable)
setTabBar(new LtsTabBar(false, this));
}
LtsTabBar *LtsTabWidget::tabBar()
{
return m_tabbar;
}
void LtsTabWidget::setTabBar(LtsTabBar *tabbar)
{
if (m_tabbar)
m_tabbar->deleteLater();
QFont currentFont = font();
currentFont.setCapitalization(QFont::SmallCaps);
currentFont.setBold(true);
tabbar->setFont(currentFont);
connect(tabbar, &LtsTabBar::tabCloseRequested, this, &LtsTabWidget::slotTabCloseRequested);
m_tabbar = tabbar;
QTabWidget::setTabBar(m_tabbar);
}
void LtsTabWidget::clearAll()
{
for (int i = 0; i < count(); i++)
{
QWidget *w = widget(i);
if (w == nullptr)
continue;
disconnect(w, &QWidget::destroyed, this, static_cast<void (LtsTabWidget::*)(QObject*)>(&LtsTabWidget::onRemoveTab));
w->deleteLater();
}
clear();
}
void LtsTabWidget::aboutToClose()
{
for (int i = 0; i < count(); i++)
{
if (m_hasSubTabs)
{
LtsTabWidget *tab = qobject_cast<LtsTabWidget *>(widget(i));
tab->aboutToClose();
}
else
{
LtsDialogHelper *helper = qobject_cast<LtsDialogHelper *>(widget(i));
if (helper == nullptr)
continue;
LtsDialog *dialog = qobject_cast<LtsDialog *>(helper->getDialog());
if (dialog == nullptr)
continue;
dialog->aboutToClose();
}
}
}
bool LtsTabWidget::hasUnsavedChanges(LtsTabWidget *tabWidget)
{
for (int i = 0; i < tabWidget->count(); i++)
{
QWidget *w = tabWidget->widget(i);
LtsDialogHelper *helper = qobject_cast<LtsDialogHelper *>(w);
if (helper)
{
LtsDialog *dlg = qobject_cast<LtsDialog *>(helper->getDialog());
if (dlg != nullptr)
{
if (dlg->hasUnsavedChanges())
{
int dlgIndex = tabWidget->indexOf(helper);
if (dlgIndex != -1)
tabWidget->setCurrentIndex(dlgIndex);
return true;
}
}
}
LtsTabWidget *tab = qobject_cast<LtsTabWidget *>(w);
if (tab)
{
bool _hasUnsavedChanges = hasUnsavedChanges(tab);
if (_hasUnsavedChanges)
{
int tabIndex = tabWidget->indexOf(tab);
if (tabIndex != -1)
tabWidget->setCurrentIndex(tabIndex);
return true;
}
}
}
return false;
}
void CloseButton::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.save();
p.setPen(QPen(QColor(m_fontColor), 1.5));
p.setRenderHint(QPainter::Antialiasing);
p.drawLine(line());
p.setPen(QPen(QColor(m_fontColor), 2.5));
QRectF c = cross();
p.drawLine(c.topLeft(), c.bottomRight());
p.drawLine(c.bottomLeft(), c.topRight());
p.restore();
}
QLineF CloseButton::line() const
{
qreal forceBlur = 0.001;
qreal lineStart = height() * (0.25 / 2) + forceBlur;
qreal lineSize = height() - lineStart;
return QLineF(QPointF(lineStart, lineStart), QPointF(lineStart, lineSize));
}
QRectF CloseButton::cross() const
{
QLineF l = line();
qreal lineLeft = l.x1();
qreal forceBlur = 0.001;
qreal crossStartX = lineLeft * 3;
qreal crossStartY = lineLeft * 2;
qreal crossEndX = width(); crossEndX -= crossEndX * 0.15 + forceBlur;
qreal crossEndY = height() - crossStartY;
return QRectF(QPointF(crossStartX, crossStartY), QPointF(crossEndX, crossEndY));
}
QSize CloseButton::sizeHint() const
{
QFontMetrics fm (QApplication::font());
int fontHeight = fm.height();
int fontHeightQuarter = fontHeight * 0.25;
int finalSize = fontHeight + fontHeightQuarter;
#ifdef TABLET_MODE
finalSize = finalSize*1.5;
#endif
return QSize(finalSize, finalSize);
}
void CloseButton::mousePressEvent(QMouseEvent *e)
{
e->ignore();
m_isPressed = true;
}
void CloseButton::mouseReleaseEvent(QMouseEvent *e)
{
bool isPressed = m_isPressed;
m_isPressed = false;
if (!isPressed)
{
e->ignore();
return;
}
QRectF c = cross();
QRect adjusted = QRect(QPoint(floor(c.x()), floor(c.y())), QPoint(ceil(c.right()), ceil(c.bottom() +1 )));
if (!adjusted.contains(e->localPos().toPoint()))
{
e->ignore();
return;
}
e->accept();
emit clicked();
}
}