Files
qt5-legatus-legacy/Lts/Common/LtsWidgets/lts_tab_widget.cpp
T
2026-08-13 18:33:10 +02:00

907 lines
24 KiB
C++
Executable File

/**
* @RDE_HEADER@
* TabWidget for new EXCON
* $Id$
* $HeadURL$
* $Author$
*/
#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); // also sets m_tabBar
QFont currentFont = tabBar->font();
QFont::Capitalization c = QFont::SmallCaps; // Small Capitals make it look more Rheinmetall
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);
// Adding maximize button aside the tabbar
QSplitter *splitter = static_cast<QSplitter*>(parent);
if (splitter &&
splitter->objectName() == QStringLiteral("mainSplitter") &&
splitter->widget(1) == this &&
!isSecondary)
{
int tabBarHeight = m_tabbar->height() * 1.33; // Luckily found this factor, no clue where that comes from
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()
{
/* no need to delete all widgets as they are parented to this TabWidget, so they get deleted anyways
BUT all tabs have to be removed, otherwise onRemoveTab (from the tabs) is going to be called on a dead 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;
}
// the underlying tab widget might have a dialog open, which is automatically focused also
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); // this is a sub tab widget?
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; // is an LtsDialog, but is not closable, so cancel here
}
}
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; //The double delete might be suspended due to the delete later but safety first
default:
return;
}
}
else
{
//tab->deleteLater();
closeRequestTabWidget(tab);
}
}
w->deleteLater(); // closing the widget would destroy the widget, which emits the signal 'destroyed' which removes the tab that widget was on - does not work here
//onRemoveTab(w); <= TODO: reactive if deleteLater active again
// NOTE: calling close on an LtsDialog removes it automatically from the tab widget
}
void LtsTabWidget::onCheckTabBarVisibility() // if this is called directly, the call comes from THIS TabWidget
{
// The CALL is on the Main Tab when CALLER exists (Sub Tab calls) so, no need to check for hasSubTabs
if (!m_isSecondary) // iam NOT the secondary, iam the FIRST
{
// i dont have any children anymore, hide me!
if (count() < 1)
setHidden(true);
}
else // IAM THE SECONDARY TAB WIDGET
{
if (count() < 1)
deleteLater(); // this will delete the secondary tab widget (me), but after this. my DESTROYED will call removeTab. after calling removeTab, this method is called on the main tab widget, which hides itself if empty
}
/*/
* also re-show close-buttons after they were hidden (only on main tab),
* but must now be visible again (such as there is no closable dialog anymore)
* but this case is very unlikely, because a non-closable tab can't be closed, but this CAN happen... so: impl!
/*/
// TODO: impl
// if (m_hasSubTabs)
// {
// LtsTabBar *tabbar = m_tabbar;
// bool showAgain = true;
// for (int i = 0; i < tabbar->count(); i++)
// {
// if (tabbar->tabButton(i, QTabBar::RightSide)->isHidden()) // if something is still hidden, do not show again!
// {
// showAgain = false;
// break;
// }
// }
// if (showAgain)
// m_tabbar->tabButton(currentIndex(), QTabBar::RightSide)->show();
// }
}
void LtsTabWidget::setSubTabsVisible(bool visible)
{
if (count())
{
// TODO: warn the dev that this is not allowed after adding tabs!
return;
}
m_hasSubTabs = visible;
}
void LtsTabWidget::addTab(const QString &menuGroup, const QString &identifier, QWidget *widget,
bool setClosable, bool focusNewTab)
{
//qWarning() << "LtsTabWidget::addTab";
setFocus();
LtsTabWidget *tab = this; // keep this when m_hasSubTabs == false
QString fullName;
// TODO: FIXME: rm! TESTING:
//widget->setParent(this); << after tests: interferes with deleted LtsTabWidget and to-be-deleted (and not delete-latered' by qt) - so onRemoveTab gets called on a dead LtsTabWidget
// END TESTING
if (m_hasSubTabs)
{
fullName = identifier; // for the widget, not for us!
tab = qobject_cast<LtsTabWidget *>(m_tabMap.value(menuGroup, nullptr)); // get SUB tab, when existing.. wenn ein sub tab nicht existiert, existiert im main tab auch kein tab dafür.... also neuesn tab anlegen
if (!tab)
{
//qWarning() << "LtsTabWidget::addTab: there is no sub tab - creating it!";
tab = new LtsTabWidget(nullptr, true); // create new SUB tab..!
tab->setMovable(true);
addWidget(menuGroup, tab, true /* will be checked later! */, focusNewTab);
}
else
{
//qWarning() << "LtsTabWidget::addTab: there is already a tab!";
tab->setHidden(false); // tab regests content, reshow (even when it's visible, that does not matter)
}
if (focusNewTab)
setCurrentIndex(indexOf(tab));
}
else // do not show & use sub tabs
{
fullName = menuGroup + QLatin1Char('.') + identifier;
// addWidget does that: m_tabMap.insert(fullName, widget); // by group + ident name
}
setHidden(false); // as there will be content, show again!
tab->addWidget(fullName, widget, setClosable, focusNewTab); // the 'tab' ptr is either THIS tab or ONE OF this tab's subtabs
/*/
* check if there are sub tabs that are not closable,
* but only if this tab is the main tab! (as sub tabs have no sub tabs, only the dialogs)
/*/
if (m_hasSubTabs)
{
LtsTabBar *tabbar = tab->tabBar(); // sub tab
for (int i = 0; i < tabbar->count(); i++)
{
// hide close btn of main tab if even ONE tab is not closable - otherwise the main tab could close a sub-tab which is not closable
if (tabbar->tabButton(i, QTabBar::RightSide)->isHidden())
{
/* main tab */ m_tabbar->tabButton(currentIndex(), QTabBar::RightSide)->hide();
break; // one positive condition is enough to hide the main close-button
}
}
}
}
void LtsTabWidget::addWidget(const QString &fullName, QWidget *widget, bool setClosable, bool focusNewTab)
{
QString theName = fullName;
if (fullName.isEmpty())
{
// create random name.......
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); // must match the string in the tabMap, so it can be deleted by the object's name
LtsDialogHelper *helper = qobject_cast<LtsDialogHelper *>(widget);
if (helper)
helper->retranslate(); // properly set the window title
QString title = widget->windowTitle();
if (title.isEmpty())
{
if (helper)
title = QStringLiteral("retranslate() fehlt!"); // Da es sich heir um ein LtsDialog handelt, muss in der retranslate()-Methode des LtsDialog auch eine Uebersetzung des Titels stattfinden!
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)); // hide and delete tab
//qWarning() << "addWidget: was a helper widget, added hide signal";
}
connect(widget, &QWidget::destroyed, this, static_cast<void (LtsTabWidget::*)(QObject*)>(&LtsTabWidget::onRemoveTab), Qt::DirectConnection); // as it was deleted, there was DESTROYED called, so only a remove is needed
connect(widget, &QWidget::windowTitleChanged, this, &LtsTabWidget::slotWindowTitleChanged);
if (helper)
{
m_tabMap.insert(theName, helper);
//qWarning() << "LtsTabWidget::addWidget: added" << theName << helper << "to tab map";
}
else
{
m_tabMap.insert(theName, widget); // as this is a sub tab, save the pure widgets by their identifier
//qWarning() << "LtsTabWidget::addWidget: added" << theName << widget << "to tab map";
}
int newTabIndex = QTabWidget::addTab(widget, title); // finally add widget to a new tab on the sub tab widget
#ifdef LTS_DEBUG
setTabToolTip(newTabIndex, tr("DEBUG: WindowName: %1").arg(theName));
#endif
if (focusNewTab)
setCurrentIndex(newTabIndex); // .. and focus it also (if desired)!
LtsTabBar *tabbar = qobject_cast<LtsTabBar *>(tabBar());
if (tabbar)
{
// create close btn
CloseButton *closeBtn = new CloseButton();
connect(closeBtn, &QPushButton::clicked, tabbar, &LtsTabBar::_internal_closeTab); // because the button does not know its index ...
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();
// format: remove "(",")", prepend "Locked by: "
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))); // svg -> icon -> pixmap -> scale
tabBar->setTabButton(index, QTabBar::ButtonPosition::LeftSide, readOnly);
} else {
QWidget *readOnly = tabBar->tabButton(index, QTabBar::ButtonPosition::LeftSide);
if (readOnly)
{
// Qt docu:
// The tab bar will take ownership of the widget and so all widgets set
// here will be deleted by the tab bar when it is destroyed unless you
// separately reparent the widget *after* setting some other widget (or nullptr).
tabBar->setTabButton(index, QTabBar::ButtonPosition::LeftSide, nullptr);
readOnly->setParent(this);
delete readOnly;
}
}
}
QWidget *LtsTabWidget::getWidget(const QString &menuGroup, const QString &identifier)
{
LtsTabWidget *tab = this; // keep this when m_hasSubTabs == false
QString fullName;
if (m_hasSubTabs)
{
fullName = identifier;
tab = qobject_cast<LtsTabWidget *>(m_tabMap.value(menuGroup, nullptr));
if (tab == nullptr)
return nullptr; // not found!
}
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; // keep this when m_hasSubTabs == false
QString fullName;
if (m_hasSubTabs)
{
fullName = identifier;
tab = qobject_cast<LtsTabWidget *>(m_tabMap.value(menuGroup, nullptr));
if (!tab)
{
//qWarning() << "LtsTabWidget::focusTab: tab for" << menuGroup << "not found! Cannot focus.";
return false;
}
setCurrentIndex(indexOf(tab)); // set focus on main tab (e.g. detail tab widget)
}
else // no sub tabs
fullName = menuGroup + QLatin1Char('.') + identifier;
if (!tab)
return false;
// start animation if splitter is collapsed
LtsSplitter *splitter = static_cast<LtsSplitter*>(parent());
// Norbert Thoden, 06.11.2024
// lambda are sometimes ugly, like here:
// opApp crashes if dialog is closed/destroyed WHILE the 500msec.
// fix: use QPointer for tab
QTimer::singleShot(500, this, [splitter, tab] (){
// Norbert Thoden, 06.11.2024
// What does following if mean? really hard to maintain:
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)); // set focus on sub tab (e.g. Exm => Edit)
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;
} // LtsTabWidget::removeWidget(QWidget * widget)
void LtsTabWidget::onRemoveTab(QObject *object)
{
if (object == nullptr)
{
qWarning() << "onRemoveTab: obj == nullptr";
return;
}
QWidget *w = static_cast<QWidget *>(object); // destroyed widgets cannot be dynamically casted, but this slot is only called by widgets !
bool rmd = false;
for (auto i = m_tabMap.begin(); i != m_tabMap.end(); ++i) // try to remove the tab that belongs to the widget
{
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); // NOTE: calling indexOf with a dead widget - I might look into the source if it tries to afect the widget
if (index < 0)
{
// ignoring this message, as clearAll() is evil
qWarning() << "LtsTabWidget::onRemoveTab: cannot remove widget, as it was not found on the tab widget" << (void *)w;
}
else
{
removeTab(index);
}
onCheckTabBarVisibility(); // check if this was the last tab and hide / delete me
}
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();
// Small Capitals make it look more Rheinmetall
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()
{
// FIXME: there is no other way to get all widgets in a tab widget (?), so this code must be used. But removing a tab, then deleting it, causes the widget to call "onRemoveTab" on signal DESTROYED, which then says that the widget was not found (as it was removed here before..)
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)); // otherwise call to already deleted LtsTabWidget
w->deleteLater(); // queue deletion
}
clear(); // remove (but don't delete) all tabs now. If not, destroyed signal is queued for a dead LtsTabWidget
}
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)
{
//qWarning() << "CHECKING DLG '"<<dlg->windowTitle()<<"' / '"<<dlg->objectName()<<"'";
if (dlg->hasUnsavedChanges())
{
int dlgIndex = tabWidget->indexOf(helper);
if (dlgIndex != -1)
tabWidget->setCurrentIndex(dlgIndex);
//dlg->closeRequest(); // tell the operator ..
return true; // .. and stop here!
}
}
}
LtsTabWidget *tab = qobject_cast<LtsTabWidget *>(w); // this is a sub tab widget?
if (tab)
{
//qWarning() << "CHECKING NEXT TAB...";
bool _hasUnsavedChanges = hasUnsavedChanges(tab); // recursive
if (_hasUnsavedChanges)
{
int tabIndex = tabWidget->indexOf(tab);
if (tabIndex != -1)
tabWidget->setCurrentIndex(tabIndex);
return true; // only abort if all tested and one of them has changes
}
}
/*if (!dlg && !tab)
{
qWarning() << "This thing was a "<<w->metaObject()->className()<<".. '"<<w->objectName()<<"' / '"<<w->windowTitle()<<"'";
}*/
}
return false; // has no changes if it comes until here
}
// Close Button:
void CloseButton::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.save();
p.setPen(QPen(QColor(m_fontColor), 1.5)); // line
p.setRenderHint(QPainter::Antialiasing);
// V-Line
p.drawLine(line());
// X / cross
p.setPen(QPen(QColor(m_fontColor), 2.5)); // bold cross
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; // starts at the first half of the margin (see sizeHint)
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; // add a quarter more space, for looks!
#ifdef TABLET_MODE
finalSize = finalSize*1.5;
#endif
return QSize(finalSize/* + fontHeightQuarter*/, finalSize);
}
void CloseButton::mousePressEvent(QMouseEvent *e)
{
e->ignore();
m_isPressed = true; // dunno if qt does this automatically
}
void CloseButton::mouseReleaseEvent(QMouseEvent *e) // only accept a click directly on the X / cross
{
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 /* hack */))); // be more tolerant
if (!adjusted.contains(e->localPos().toPoint()))
{
//qWarning() << "NO! release - event:" << e->localPos() << "cross:" << cross();
e->ignore();
return;
}
//qWarning() << "YES! release - event:" << e->localPos() << "cross:" << cross();
e->accept();
emit clicked();
}
} // namespace LtsWidgets