summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Marek Glogowski <glogow@fbihome.de>2022-05-26 13:48:38 +0200
committerJan-Marek Glogowski <glogow@fbihome.de>2022-05-28 15:37:31 +0200
commit9ea767cb568cef1b142190d2adb0e301baa382e2 (patch)
tree9e15df5510929616990cd4e410ecf58a10cd0724
parent553750d5763b2e605cc8990d81c5588db4319b62 (diff)
tdf#132350 Qt implement SalMenu button interface
This turned out much more complicated then expected: 1. The QMenuBar needs explicit adjustSize() calls to position a changed corner widget (or a resize...). 2. The adjustSize() results in a temporary, minimal QMenuBar layout, so GetMenuBarButtonRectPixel needs to account for the extra space itself, instead of just a mapTo call. 3. I didn't know, that you can't add shown widgets to a layout, but must call show() after add / insert, otherwise they are ignored in the layout (but show up as the layout items) and are painted in strange positions. This also includes the transparency flags for our QtWidget, so the updater "bubble window" is properly alpha-masked / shaped. And this maybe has too many asserts... Change-Id: I86a708175e9f9be786f5dc1810ae0197a0d3fc39 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135021 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.weghorn@posteo.de> Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
-rw-r--r--vcl/inc/qt5/QtMenu.hxx15
-rw-r--r--vcl/qt5/QtMenu.cxx190
-rw-r--r--vcl/qt5/QtWidget.cxx3
3 files changed, 191 insertions, 17 deletions
diff --git a/vcl/inc/qt5/QtMenu.hxx b/vcl/inc/qt5/QtMenu.hxx
index 11f3f00c5aa6..a1b77687ce86 100644
--- a/vcl/inc/qt5/QtMenu.hxx
+++ b/vcl/inc/qt5/QtMenu.hxx
@@ -16,11 +16,13 @@
#include <memory>
class MenuItemList;
+class QAbstractButton;
class QAction;
class QActionGroup;
-class QPushButton;
+class QButtonGroup;
class QMenu;
class QMenuBar;
+class QPushButton;
class QtMenuItem;
class QtFrame;
@@ -49,6 +51,7 @@ private:
std::unique_ptr<QMenu> mpOwnedQMenu;
// pointer to QMenu owned by the corresponding QtMenuItem or self (-> mpOwnedQMenu)
QMenu* mpQMenu;
+ QButtonGroup* m_pButtonGroup;
void DoFullMenuUpdate(Menu* pMenuBar);
static void NativeItemText(OUString& rItemText);
@@ -58,7 +61,10 @@ private:
void ReinitializeActionGroup(unsigned nPos);
void ResetAllActionGroups();
void UpdateActionGroupItem(const QtMenuItem* pSalMenuItem);
- bool validateQMenuBar();
+ bool validateQMenuBar() const;
+ QPushButton* ImplAddMenuBarButton(const QIcon& rIcon, const QString& rToolTip, int nId);
+ void ImplRemoveMenuBarButton(int nId);
+ void adjustButtonSizes();
public:
QtMenu(bool bMenuBar);
@@ -86,6 +92,10 @@ public:
const vcl::KeyCode& rKeyCode, const OUString& rKeyName) override;
virtual void GetSystemMenuData(SystemMenuData* pData) override;
virtual void ShowCloseButton(bool bShow) override;
+ virtual bool AddMenuBarButton(const SalMenuButtonItem&) override;
+ virtual void RemoveMenuBarButton(sal_uInt16 nId) override;
+ virtual tools::Rectangle GetMenuBarButtonRectPixel(sal_uInt16 nId, SalFrame*) override;
+ virtual int GetMenuBarHeight() const override;
void SetMenu(Menu* pMenu) { mpVCLMenu = pMenu; }
Menu* GetMenu() { return mpVCLMenu; }
@@ -97,6 +107,7 @@ private slots:
static void slotMenuAboutToShow(QtMenuItem* pQItem);
static void slotMenuAboutToHide(QtMenuItem* pQItem);
void slotCloseDocument();
+ void slotMenuBarButtonClicked(QAbstractButton*);
};
class QtMenuItem : public SalMenuItem
diff --git a/vcl/qt5/QtMenu.cxx b/vcl/qt5/QtMenu.cxx
index fd38a0380000..ccd2eb431f7e 100644
--- a/vcl/qt5/QtMenu.cxx
+++ b/vcl/qt5/QtMenu.cxx
@@ -20,8 +20,11 @@
#include <QtGui/QActionGroup>
#endif
+#include <QtWidgets/QButtonGroup>
+#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
+#include <QtWidgets/QStyle>
#include <o3tl/safeint.hxx>
#include <vcl/svapp.hxx>
@@ -33,6 +36,18 @@
#include <vcl/toolkit/floatwin.hxx>
#include <window.h>
+// LO SalMenuButtonItem::mnId is sal_uInt16, so we go with -2, as -1 has a special meaning as automatic id
+constexpr int CLOSE_BUTTON_ID = -2;
+const QString gButtonGroupKey("QtMenu::ButtonGroup");
+
+static inline void lcl_force_menubar_layout_update(QMenuBar& rMenuBar)
+{
+ // just exists as a function to not comment it everywhere: forces reposition of the
+ // corner widget after its layout changes, which will otherwise just happen on resize.
+ // it unfortunatly has additional side effects; see QtMenu::GetMenuBarButtonRectPixel.
+ rMenuBar.adjustSize();
+}
+
QtMenu::QtMenu(bool bMenuBar)
: mpVCLMenu(nullptr)
, mpParentSalMenu(nullptr)
@@ -40,6 +55,7 @@ QtMenu::QtMenu(bool bMenuBar)
, mbMenuBar(bMenuBar)
, mpQMenuBar(nullptr)
, mpQMenu(nullptr)
+ , m_pButtonGroup(nullptr)
{
}
@@ -430,9 +446,19 @@ void QtMenu::SetFrame(const SalFrame* pFrame)
mpQMenuBar = new QMenuBar();
pMainWindow->setMenuBar(mpQMenuBar);
- QPushButton* pButton = static_cast<QPushButton*>(mpQMenuBar->cornerWidget(Qt::TopRightCorner));
- if (pButton)
- connect(pButton, &QPushButton::clicked, this, &QtMenu::slotCloseDocument);
+ QWidget* pWidget = mpQMenuBar->cornerWidget(Qt::TopRightCorner);
+ if (pWidget)
+ {
+ m_pButtonGroup = pWidget->findChild<QButtonGroup*>(gButtonGroupKey);
+ assert(m_pButtonGroup);
+ connect(m_pButtonGroup, QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked), this,
+ &QtMenu::slotMenuBarButtonClicked);
+ QPushButton* pButton = static_cast<QPushButton*>(m_pButtonGroup->button(CLOSE_BUTTON_ID));
+ if (pButton)
+ connect(pButton, &QPushButton::clicked, this, &QtMenu::slotCloseDocument);
+ }
+ else
+ m_pButtonGroup = nullptr;
mpQMenu = nullptr;
DoFullMenuUpdate(mpVCLMenu);
@@ -556,7 +582,7 @@ QtMenu* QtMenu::GetTopLevel()
return pMenu;
}
-bool QtMenu::validateQMenuBar()
+bool QtMenu::validateQMenuBar() const
{
if (!mpQMenuBar)
return false;
@@ -565,14 +591,21 @@ bool QtMenu::validateQMenuBar()
assert(pMainWindow);
const bool bValid = mpQMenuBar == pMainWindow->menuBar();
if (!bValid)
- mpQMenuBar = nullptr;
+ {
+ QtMenu* thisPtr = const_cast<QtMenu*>(this);
+ thisPtr->mpQMenuBar = nullptr;
+ }
return bValid;
}
void QtMenu::ShowMenuBar(bool bVisible)
{
- if (validateQMenuBar())
- mpQMenuBar->setVisible(bVisible);
+ if (!validateQMenuBar())
+ return;
+
+ mpQMenuBar->setVisible(bVisible);
+ if (bVisible)
+ lcl_force_menubar_layout_update(*mpQMenuBar);
}
const QtFrame* QtMenu::GetFrame() const
@@ -645,14 +678,135 @@ void QtMenu::slotCloseDocument()
Application::PostUserEvent(pVclMenuBar->GetCloseButtonClickHdl());
}
+void QtMenu::slotMenuBarButtonClicked(QAbstractButton* pButton)
+{
+ MenuBar* pVclMenuBar = static_cast<MenuBar*>(mpVCLMenu.get());
+ if (pVclMenuBar)
+ {
+ SolarMutexGuard aGuard;
+ pVclMenuBar->HandleMenuButtonEvent(m_pButtonGroup->id(pButton));
+ }
+}
+
+QPushButton* QtMenu::ImplAddMenuBarButton(const QIcon& rIcon, const QString& rToolTip, int nId)
+{
+ if (!validateQMenuBar())
+ return nullptr;
+
+ QWidget* pWidget = mpQMenuBar->cornerWidget(Qt::TopRightCorner);
+ QHBoxLayout* pLayout;
+ if (!pWidget)
+ {
+ assert(!m_pButtonGroup);
+ pWidget = new QWidget(mpQMenuBar);
+ assert(!pWidget->layout());
+ pLayout = new QHBoxLayout();
+ pLayout->setContentsMargins(QMargins());
+ pLayout->setSpacing(0);
+ pWidget->setLayout(pLayout);
+ m_pButtonGroup = new QButtonGroup(pLayout);
+ m_pButtonGroup->setObjectName(gButtonGroupKey);
+ m_pButtonGroup->setExclusive(false);
+ connect(m_pButtonGroup, QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked), this,
+ &QtMenu::slotMenuBarButtonClicked);
+ pWidget->show();
+ mpQMenuBar->setCornerWidget(pWidget, Qt::TopRightCorner);
+ }
+ else
+ pLayout = static_cast<QHBoxLayout*>(pWidget->layout());
+ assert(m_pButtonGroup);
+ assert(pLayout);
+
+ QPushButton* pButton = static_cast<QPushButton*>(m_pButtonGroup->button(nId));
+ if (pButton)
+ ImplRemoveMenuBarButton(nId);
+
+ pButton = new QPushButton();
+ // we don't want the button to increase the QMenuBar height, so a fixed size square it is
+ const int nFixedLength
+ = mpQMenuBar->height() - 2 * mpQMenuBar->style()->pixelMetric(QStyle::PM_MenuBarVMargin);
+ pButton->setFixedSize(nFixedLength, nFixedLength);
+ pButton->setIcon(rIcon);
+ pButton->setFlat(true);
+ pButton->setFocusPolicy(Qt::NoFocus);
+ pButton->setToolTip(rToolTip);
+
+ m_pButtonGroup->addButton(pButton, nId);
+ int nPos = pLayout->count();
+ if (m_pButtonGroup->button(CLOSE_BUTTON_ID))
+ nPos--;
+ pLayout->insertWidget(nPos, pButton, 0, Qt::AlignCenter);
+ // show must happen after adding the button to the layout, otherwise the button is
+ // shown, but not correct in the layout, if at all! Some times the layout ignores it.
+ pButton->show();
+
+ lcl_force_menubar_layout_update(*mpQMenuBar);
+
+ return pButton;
+}
+
+bool QtMenu::AddMenuBarButton(const SalMenuButtonItem& rItem)
+{
+ if (!validateQMenuBar())
+ return false;
+ return !!ImplAddMenuBarButton(QIcon(QPixmap::fromImage(toQImage(rItem.maImage))),
+ toQString(rItem.maToolTipText), rItem.mnId);
+}
+
+void QtMenu::ImplRemoveMenuBarButton(int nId)
+{
+ if (!validateQMenuBar())
+ return;
+
+ assert(m_pButtonGroup);
+ auto* pButton = m_pButtonGroup->button(nId);
+ assert(pButton);
+ QWidget* pWidget = mpQMenuBar->cornerWidget(Qt::TopRightCorner);
+ assert(pWidget);
+ QLayout* pLayout = pWidget->layout();
+ m_pButtonGroup->removeButton(pButton);
+ pLayout->removeWidget(pButton);
+ delete pButton;
+
+ lcl_force_menubar_layout_update(*mpQMenuBar);
+}
+
+void QtMenu::RemoveMenuBarButton(sal_uInt16 nId) { ImplRemoveMenuBarButton(nId); }
+
+tools::Rectangle QtMenu::GetMenuBarButtonRectPixel(sal_uInt16 nId, SalFrame* pFrame)
+{
+#ifdef NDEBUG
+ Q_UNUSED(pFrame);
+#endif
+ if (!validateQMenuBar())
+ return tools::Rectangle();
+
+ assert(mpFrame == static_cast<QtFrame*>(pFrame));
+ assert(m_pButtonGroup);
+ auto* pButton = static_cast<QPushButton*>(m_pButtonGroup->button(nId));
+ assert(pButton);
+
+ // unfortunatly, calling lcl_force_menubar_layout_update results in a temporary wrong menubar size,
+ // but it's the correct minimal size AFAIK and the layout seems correct, so just adjust the width.
+ QPoint aPos = pButton->mapTo(mpFrame->asChild(), QPoint());
+ aPos.rx() += (mpFrame->asChild()->width() - mpQMenuBar->width());
+ return tools::Rectangle(toPoint(aPos), toSize(pButton->size()));
+}
+
void QtMenu::ShowCloseButton(bool bShow)
{
if (!validateQMenuBar())
return;
- QPushButton* pButton = static_cast<QPushButton*>(mpQMenuBar->cornerWidget(Qt::TopRightCorner));
- if (!pButton && !bShow)
+ if (!bShow && !m_pButtonGroup)
+ return;
+
+ QPushButton* pButton = nullptr;
+ if (m_pButtonGroup)
+ pButton = static_cast<QPushButton*>(m_pButtonGroup->button(CLOSE_BUTTON_ID));
+ if (!bShow && !pButton)
return;
+
if (!pButton)
{
QIcon aIcon;
@@ -661,12 +815,8 @@ void QtMenu::ShowCloseButton(bool bShow)
else
aIcon = QIcon(
QPixmap::fromImage(toQImage(Image(StockImage::Yes, SV_RESID_BITMAP_CLOSEDOC))));
- pButton = new QPushButton(mpQMenuBar);
- pButton->setIcon(aIcon);
- pButton->setFlat(true);
- pButton->setFocusPolicy(Qt::NoFocus);
- pButton->setToolTip(toQString(VclResId(SV_HELPTEXT_CLOSEDOCUMENT)));
- mpQMenuBar->setCornerWidget(pButton, Qt::TopRightCorner);
+ pButton = ImplAddMenuBarButton(aIcon, toQString(VclResId(SV_HELPTEXT_CLOSEDOCUMENT)),
+ CLOSE_BUTTON_ID);
connect(pButton, &QPushButton::clicked, this, &QtMenu::slotCloseDocument);
}
@@ -674,6 +824,8 @@ void QtMenu::ShowCloseButton(bool bShow)
pButton->show();
else
pButton->hide();
+
+ lcl_force_menubar_layout_update(*mpQMenuBar);
}
bool QtMenu::ShowNativePopupMenu(FloatingWindow*, const tools::Rectangle&,
@@ -689,6 +841,14 @@ bool QtMenu::ShowNativePopupMenu(FloatingWindow*, const tools::Rectangle&,
return true;
}
+int QtMenu::GetMenuBarHeight() const
+{
+ if (!validateQMenuBar() || !mpQMenuBar->isVisible())
+ return 0;
+
+ return mpQMenuBar->height();
+}
+
QtMenuItem::QtMenuItem(const SalItemParams* pItemData)
: mpParentMenu(nullptr)
, mpSubMenu(nullptr)
diff --git a/vcl/qt5/QtWidget.cxx b/vcl/qt5/QtWidget.cxx
index 04a3af5dac23..a9484f0b2531 100644
--- a/vcl/qt5/QtWidget.cxx
+++ b/vcl/qt5/QtWidget.cxx
@@ -694,6 +694,9 @@ QtWidget::QtWidget(QtFrame& rFrame, Qt::WindowFlags f)
, m_nDeltaX(0)
, m_nDeltaY(0)
{
+ setAttribute(Qt::WA_TranslucentBackground);
+ setAttribute(Qt::WA_OpaquePaintEvent);
+ setAttribute(Qt::WA_NoSystemBackground);
setMouseTracking(true);
if (!rFrame.isPopup())
setFocusPolicy(Qt::StrongFocus);