tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / vcl / qt5 / QtFrame.cxx
blobd993c63e77626059a4ff2fb91827c7829fc728a8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <IconThemeSelector.hxx>
21 #include <QtCustomStyle.hxx>
22 #include <QtFrame.hxx>
23 #include <QtFrame.moc>
25 #include <QtData.hxx>
26 #include <QtDragAndDrop.hxx>
27 #include <QtFontFace.hxx>
28 #include <QtGraphics.hxx>
29 #include <QtInstance.hxx>
30 #include <QtMainWindow.hxx>
31 #include <QtMenu.hxx>
32 #include <QtSvpGraphics.hxx>
33 #include <QtSystem.hxx>
34 #include <QtTransferable.hxx>
35 #if CHECK_ANY_QT_USING_X11
36 #include <QtX11Support.hxx>
37 #endif
39 #include <QtCore/QLibraryInfo>
40 #include <QtCore/QMimeData>
41 #include <QtCore/QPoint>
42 #include <QtCore/QSize>
43 #include <QtCore/QThread>
44 #include <QtGui/QDragMoveEvent>
45 #include <QtGui/QDropEvent>
46 #include <QtGui/QIcon>
47 #include <QtGui/QWindow>
48 #include <QtGui/QScreen>
49 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
50 #include <QtGui/QStyleHints>
51 #endif
52 #include <QtWidgets/QStyle>
53 #include <QtWidgets/QToolTip>
54 #include <QtWidgets/QApplication>
55 #include <QtWidgets/QMenuBar>
56 #include <QtWidgets/QMainWindow>
57 #if CHECK_QT5_USING_X11
58 #include <QtX11Extras/QX11Info>
59 #endif
61 #include <window.h>
62 #include <vcl/qt/QtUtils.hxx>
63 #include <vcl/syswin.hxx>
65 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
67 #include <cairo.h>
68 #include <headless/svpgdi.hxx>
70 #include <unx/fontmanager.hxx>
72 static void SvpDamageHandler(void* handle, sal_Int32 nExtentsX, sal_Int32 nExtentsY,
73 sal_Int32 nExtentsWidth, sal_Int32 nExtentsHeight)
75 QtFrame* pThis = static_cast<QtFrame*>(handle);
76 pThis->Damage(nExtentsX, nExtentsY, nExtentsWidth, nExtentsHeight);
79 QtFrame::QtFrame(QtFrame* pParent, SalFrameStyleFlags nStyle, bool bUseCairo)
80 : m_pTopLevel(nullptr)
81 , m_bUseCairo(bUseCairo)
82 , m_bNullRegion(true)
83 , m_bGraphicsInUse(false)
84 , m_ePointerStyle(PointerStyle::Arrow)
85 , m_pDragSource(nullptr)
86 , m_pDropTarget(nullptr)
87 , m_bInDrag(false)
88 , m_bDefaultSize(true)
89 , m_bDefaultPos(true)
90 , m_bFullScreen(false)
91 , m_bFullScreenSpanAll(false)
92 #if CHECK_ANY_QT_USING_X11
93 , m_nKeyModifiers(ModKeyFlags::NONE)
94 #endif
95 , m_nInputLanguage(LANGUAGE_DONTKNOW)
97 GetQtInstance().insertFrame(this);
99 m_aDamageHandler.handle = this;
100 m_aDamageHandler.damaged = ::SvpDamageHandler;
102 if (nStyle & SalFrameStyleFlags::DEFAULT) // ensure default style
104 nStyle |= SalFrameStyleFlags::MOVEABLE | SalFrameStyleFlags::SIZEABLE
105 | SalFrameStyleFlags::CLOSEABLE;
106 nStyle &= ~SalFrameStyleFlags::FLOAT;
109 m_nStyle = nStyle;
110 m_pParent = pParent;
112 Qt::WindowFlags aWinFlags(Qt::Widget);
113 if (!(nStyle & SalFrameStyleFlags::SYSTEMCHILD))
115 if (nStyle & SalFrameStyleFlags::INTRO)
116 aWinFlags = Qt::SplashScreen;
117 // floating toolbars are frameless tool windows
118 // + they must be able to receive keyboard focus
119 else if ((nStyle & SalFrameStyleFlags::FLOAT)
120 && (nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION))
121 aWinFlags = Qt::Tool | Qt::FramelessWindowHint;
122 else if (nStyle & SalFrameStyleFlags::TOOLTIP)
123 aWinFlags = Qt::ToolTip;
124 // Can't use Qt::Popup, because it grabs the input focus and generates a focus-out event,
125 // instantly auto-closing the LO's editable ComboBox popup.
126 // On X11, the alternative Qt::Window | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint
127 // seems to work well enough, but at least on Wayland and WASM, this results in problems.
128 // So while using Qt::ToolTip, the popups are wrongly advertised via accessibility, at least
129 // the GUI seems to work on all platforms... what a mess.
130 else if (isPopup())
131 aWinFlags = Qt::ToolTip | Qt::FramelessWindowHint;
132 else if (nStyle & SalFrameStyleFlags::TOOLWINDOW)
133 aWinFlags = Qt::Tool;
134 // top level windows can't be transient in Qt, so make them dialogs, if they have a parent. At least
135 // the plasma shell relies on this setting to skip dialogs in the window list. And Qt Xcb will just
136 // set transient for the types Dialog, Sheet, Tool, SplashScreen, ToolTip, Drawer and Popup.
137 else if (nStyle & SalFrameStyleFlags::DIALOG || m_pParent)
138 aWinFlags = Qt::Dialog;
139 else
140 aWinFlags = Qt::Window;
143 if (aWinFlags == Qt::Window)
145 m_pTopLevel = new QtMainWindow(*this, aWinFlags);
146 m_pQWidget = new QtWidget(*this);
147 m_pTopLevel->setCentralWidget(m_pQWidget);
148 m_pTopLevel->setFocusProxy(m_pQWidget);
150 else
152 m_pQWidget = new QtWidget(*this, aWinFlags);
153 // from Qt's POV the popup window doesn't have the input focus, so we must force tooltips...
154 if (isPopup())
155 m_pQWidget->setAttribute(Qt::WA_AlwaysShowToolTips);
158 FillSystemEnvData(m_aSystemData, reinterpret_cast<sal_IntPtr>(this), m_pQWidget);
160 SetIcon(SV_ICON_ID_OFFICE);
163 void QtFrame::screenChanged(QScreen*) { m_pQWidget->fakeResize(); }
165 void QtFrame::FillSystemEnvData(SystemEnvData& rData, sal_IntPtr pWindow, QWidget* pWidget)
167 assert(rData.platform == SystemEnvData::Platform::Invalid);
168 assert(rData.toolkit == SystemEnvData::Toolkit::Invalid);
169 if (QGuiApplication::platformName() == "wayland")
170 rData.platform = SystemEnvData::Platform::Wayland;
171 else if (QGuiApplication::platformName() == "xcb")
172 rData.platform = SystemEnvData::Platform::Xcb;
173 else if (QGuiApplication::platformName() == "wasm")
174 rData.platform = SystemEnvData::Platform::WASM;
175 else
177 // maybe add a SystemEnvData::Platform::Unsupported to avoid special cases and not abort?
178 SAL_WARN("vcl.qt",
179 "Unsupported qt VCL platform: " << toOUString(QGuiApplication::platformName()));
180 std::abort();
183 rData.toolkit = SystemEnvData::Toolkit::Qt;
184 rData.aShellWindow = pWindow;
185 rData.pWidget = pWidget;
188 QtFrame::~QtFrame()
190 GetQtInstance().eraseFrame(this);
191 delete asChild();
192 m_aSystemData.aShellWindow = 0;
195 void QtFrame::Damage(sal_Int32 nExtentsX, sal_Int32 nExtentsY, sal_Int32 nExtentsWidth,
196 sal_Int32 nExtentsHeight) const
198 m_pQWidget->update(scaledQRect(QRect(nExtentsX, nExtentsY, nExtentsWidth, nExtentsHeight),
199 1 / devicePixelRatioF()));
202 SalGraphics* QtFrame::AcquireGraphics()
204 if (m_bGraphicsInUse)
205 return nullptr;
207 m_bGraphicsInUse = true;
209 if (m_bUseCairo)
211 if (!m_pSvpGraphics)
213 QSize aSize = m_pQWidget->size() * devicePixelRatioF();
214 m_pSvpGraphics.reset(new QtSvpGraphics(this));
215 m_pSurface.reset(
216 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, aSize.width(), aSize.height()));
217 m_pSvpGraphics->setSurface(m_pSurface.get(),
218 basegfx::B2IVector(aSize.width(), aSize.height()));
219 cairo_surface_set_user_data(m_pSurface.get(), QtSvpGraphics::getDamageKey(),
220 &m_aDamageHandler, nullptr);
222 return m_pSvpGraphics.get();
224 else
226 if (!m_pQtGraphics)
228 m_pQtGraphics.reset(new QtGraphics(this));
229 m_pQImage.reset(
230 new QImage(m_pQWidget->size() * devicePixelRatioF(), Qt_DefaultFormat32));
231 m_pQImage->fill(Qt::transparent);
232 m_pQtGraphics->ChangeQImage(m_pQImage.get());
234 return m_pQtGraphics.get();
238 void QtFrame::ReleaseGraphics(SalGraphics* pSalGraph)
240 (void)pSalGraph;
241 if (m_bUseCairo)
242 assert(pSalGraph == m_pSvpGraphics.get());
243 else
244 assert(pSalGraph == m_pQtGraphics.get());
245 m_bGraphicsInUse = false;
248 bool QtFrame::PostEvent(std::unique_ptr<ImplSVEvent> pData)
250 GetQtInstance().PostEvent(this, pData.release(), SalEvent::UserEvent);
251 return true;
254 QWidget* QtFrame::asChild() const
256 if (m_pTopLevel)
257 return m_pTopLevel;
258 return m_pQWidget;
261 qreal QtFrame::devicePixelRatioF() const { return asChild()->devicePixelRatioF(); }
263 bool QtFrame::isWindow() const { return asChild()->isWindow(); }
265 QWindow* QtFrame::windowHandle() const
267 // set attribute 'Qt::WA_NativeWindow' first to make sure a window handle actually exists
268 QWidget* pChild = asChild();
269 assert(pChild->window() == pChild);
270 switch (m_aSystemData.platform)
272 case SystemEnvData::Platform::Wayland:
273 case SystemEnvData::Platform::Xcb:
274 pChild->setAttribute(Qt::WA_NativeWindow);
275 break;
276 case SystemEnvData::Platform::WASM:
277 // no idea, why Qt::WA_NativeWindow breaks the menubar for EMSCRIPTEN
278 break;
279 case SystemEnvData::Platform::Invalid:
280 std::abort();
281 break;
283 return pChild->windowHandle();
286 QScreen* QtFrame::screen() const { return asChild()->screen(); }
288 sal_Int32 QtFrame::screenNumber() const
290 QScreen* pScreen = screen();
291 const QList<QScreen*> screens = QApplication::screens();
292 return screens.indexOf(pScreen);
295 bool QtFrame::GetUseDarkMode() const
297 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
298 const Qt::ColorScheme eColorScheme = QApplication::styleHints()->colorScheme();
299 if (eColorScheme == Qt::ColorScheme::Dark)
300 return true;
301 if (eColorScheme == Qt::ColorScheme::Light)
302 return false;
303 #endif
305 // use same mechanism for determining dark mode preference as xdg-desktop-portal-kde, s.
306 // https://invent.kde.org/plasma/xdg-desktop-portal-kde/-/blob/0a4237549debf9518f8cfbaf531456850c0729bd/src/settings.cpp#L213-227
307 const QPalette aPalette = QApplication::palette();
308 const int nWindowBackGroundGray = qGray(aPalette.window().color().rgb());
309 return nWindowBackGroundGray < 192;
312 bool QtFrame::isMinimized() const { return asChild()->isMinimized(); }
314 bool QtFrame::isMaximized() const { return asChild()->isMaximized(); }
316 void QtFrame::SetWindowStateImpl(Qt::WindowStates eState)
318 return asChild()->setWindowState(eState);
321 void QtFrame::SetTitle(const OUString& rTitle)
323 GetQtInstance().RunInMainThread(
324 [this, rTitle]() { m_pQWidget->window()->setWindowTitle(toQString(rTitle)); });
327 void QtFrame::SetIcon(sal_uInt16 nIcon)
329 QtInstance& rQtInstance = GetQtInstance();
330 if (!rQtInstance.IsMainThread())
332 rQtInstance.RunInMainThread([this, nIcon]() { SetIcon(nIcon); });
333 return;
336 if (m_nStyle
337 & (SalFrameStyleFlags::PLUG | SalFrameStyleFlags::SYSTEMCHILD
338 | SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::INTRO
339 | SalFrameStyleFlags::OWNERDRAWDECORATION)
340 || !isWindow())
341 return;
343 QString appicon;
345 if (nIcon == SV_ICON_ID_TEXT)
346 appicon = "libreoffice-writer";
347 else if (nIcon == SV_ICON_ID_SPREADSHEET)
348 appicon = "libreoffice-calc";
349 else if (nIcon == SV_ICON_ID_DRAWING)
350 appicon = "libreoffice-draw";
351 else if (nIcon == SV_ICON_ID_PRESENTATION)
352 appicon = "libreoffice-impress";
353 else if (nIcon == SV_ICON_ID_DATABASE)
354 appicon = "libreoffice-base";
355 else if (nIcon == SV_ICON_ID_FORMULA)
356 appicon = "libreoffice-math";
357 else
358 appicon = "libreoffice-startcenter";
360 QIcon aIcon = QIcon::fromTheme(appicon);
361 m_pQWidget->window()->setWindowIcon(aIcon);
363 if (QGuiApplication::platformName() == "wayland" && m_pQWidget->window()->isVisible())
365 // Qt currently doesn't provide API to directly set the app_id for a single
366 // window/toplevel on Wayland, but the one set for the application is picked up
367 // on hide/show, so do that.
368 // An alternative would be to use private Qt API and low-level wayland API to set the
369 // app_id directly, s. discussion in QTBUG-77182.
370 const QString sOrigDesktopFileName = QGuiApplication::desktopFileName();
371 QGuiApplication::setDesktopFileName(appicon);
372 m_pQWidget->window()->hide();
373 m_pQWidget->window()->show();
374 QGuiApplication::setDesktopFileName(sOrigDesktopFileName);
378 void QtFrame::SetMenu(SalMenu*) {}
380 void QtFrame::SetExtendedFrameStyle(SalExtStyle /*nExtStyle*/) { /* not needed */}
382 void QtFrame::Show(bool bVisible, bool bNoActivate)
384 SolarMutexGuard g;
385 QtInstance& rQtInstance = GetQtInstance();
386 if (!rQtInstance.IsMainThread())
388 rQtInstance.RunInMainThread([&] { Show(bVisible, bNoActivate); });
389 return;
392 assert(m_pQWidget);
393 if (bVisible == asChild()->isVisible())
394 return;
396 if (!bVisible) // hide
398 asChild()->setVisible(false);
399 return;
402 QWindow* pChildWindow = windowHandle();
403 connect(pChildWindow, &QWindow::screenChanged, this, &QtFrame::screenChanged,
404 Qt::UniqueConnection);
406 if (m_pParent && !(m_pParent->m_nStyle & SalFrameStyleFlags::PLUG))
408 QWindow* pParentWindow = m_pParent->windowHandle();
409 if (pParentWindow && pChildWindow && (pParentWindow != pChildWindow))
410 pChildWindow->setTransientParent(pParentWindow);
413 // show
414 SetDefaultSize();
416 QWidget* const pChild = asChild();
417 pChild->setVisible(true);
418 pChild->raise();
419 if (!bNoActivate)
421 pChild->activateWindow();
422 pChild->setFocus();
426 void QtFrame::SetMinClientSize(tools::Long nWidth, tools::Long nHeight)
428 if (!isChild())
430 const qreal fRatio = devicePixelRatioF();
431 asChild()->setMinimumSize(round(nWidth / fRatio), round(nHeight / fRatio));
435 void QtFrame::SetMaxClientSize(tools::Long nWidth, tools::Long nHeight)
437 if (!isChild())
439 const qreal fRatio = devicePixelRatioF();
440 asChild()->setMaximumSize(round(nWidth / fRatio), round(nHeight / fRatio));
444 void QtFrame::SetDefaultPos()
446 if (!m_bDefaultPos)
447 return;
449 // center on parent
450 if (m_pParent)
452 const qreal fRatio = devicePixelRatioF();
453 QWidget* const pParentWin = m_pParent->asChild()->window();
454 QWidget* const pChildWin = asChild()->window();
455 QPoint aPos = (pParentWin->rect().center() - pChildWin->rect().center()) * fRatio;
456 SetPosSize(aPos.x(), aPos.y(), 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y);
457 assert(!m_bDefaultPos);
459 else
460 m_bDefaultPos = false;
463 Size QtFrame::CalcDefaultSize()
465 assert(isWindow());
467 Size aSize;
468 if (!m_bFullScreen)
470 const QScreen* pScreen = screen();
471 if (!pScreen)
472 pScreen = QGuiApplication::screens().at(0);
473 aSize = bestmaxFrameSizeForScreenSize(toSize(pScreen->size()));
475 else
477 if (!m_bFullScreenSpanAll)
479 aSize = toSize(screen()->size());
481 else
483 QScreen* pScreen = QGuiApplication::screenAt(QPoint(0, 0));
484 aSize = toSize(pScreen->availableVirtualGeometry().size());
488 return aSize;
491 void QtFrame::SetDefaultSize()
493 if (!m_bDefaultSize)
494 return;
496 Size aDefSize = CalcDefaultSize();
497 SetPosSize(0, 0, aDefSize.Width(), aDefSize.Height(),
498 SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT);
499 assert(!m_bDefaultSize);
502 void QtFrame::SetPosSize(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
503 sal_uInt16 nFlags)
505 SolarMutexGuard g;
506 QtInstance& rQtInstance = GetQtInstance();
507 if (!rQtInstance.IsMainThread())
509 rQtInstance.RunInMainThread([&] { SetPosSize(nX, nY, nWidth, nHeight, nFlags); });
512 if (!isWindow() || isChild(true, false))
513 return;
515 if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT))
517 if (isChild(false) || !m_pQWidget->isMaximized())
519 if (!(nFlags & SAL_FRAME_POSSIZE_WIDTH))
520 nWidth = GetWidth();
521 else if (!(nFlags & SAL_FRAME_POSSIZE_HEIGHT))
522 nHeight = GetHeight();
524 if (nWidth > 0 && nHeight > 0)
526 m_bDefaultSize = false;
527 const int nNewWidth = round(nWidth / devicePixelRatioF());
528 const int nNewHeight = round(nHeight / devicePixelRatioF());
529 if (m_nStyle & SalFrameStyleFlags::SIZEABLE)
530 asChild()->resize(nNewWidth, nNewHeight);
531 else
532 asChild()->setFixedSize(nNewWidth, nNewHeight);
537 if (!(nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y)))
539 if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT))
540 SetDefaultPos();
541 return;
544 if (m_pParent)
546 const SalFrameGeometry aParentGeometry = m_pParent->GetUnmirroredGeometry();
547 if (QGuiApplication::isRightToLeft())
548 nX = aParentGeometry.x() + aParentGeometry.width() - nX - GetWidth() - 1;
549 else
550 nX += aParentGeometry.x();
551 nY += aParentGeometry.y();
554 if (!(nFlags & SAL_FRAME_POSSIZE_X))
555 nX = GetUnmirroredGeometry().x();
556 else if (!(nFlags & SAL_FRAME_POSSIZE_Y))
557 nY = GetUnmirroredGeometry().y();
559 m_bDefaultPos = false;
560 asChild()->move(round(nX / devicePixelRatioF()), round(nY / devicePixelRatioF()));
563 void QtFrame::GetClientSize(tools::Long& rWidth, tools::Long& rHeight)
565 rWidth = round(m_pQWidget->width() * devicePixelRatioF());
566 rHeight = round(m_pQWidget->height() * devicePixelRatioF());
569 SalFrameGeometry QtFrame::GetUnmirroredGeometry() const
571 SalFrameGeometry aGeometry;
573 const qreal fRatio = devicePixelRatioF();
574 const QPoint aScreenPos = m_pQWidget->mapToGlobal(QPoint(0, 0));
575 aGeometry.setX(aScreenPos.x() * fRatio);
576 aGeometry.setY(aScreenPos.y() * fRatio);
577 aGeometry.setWidth(m_pQWidget->width() * fRatio);
578 aGeometry.setHeight(m_pQWidget->height() * fRatio);
580 aGeometry.setScreen(std::max(sal_Int32(0), screenNumber()));
582 return aGeometry;
585 void QtFrame::GetWorkArea(AbsoluteScreenPixelRectangle& rRect)
587 if (!isWindow())
588 return;
589 QScreen* pScreen = screen();
590 if (!pScreen)
591 return;
593 QSize aSize = pScreen->availableVirtualSize() * devicePixelRatioF();
594 rRect = AbsoluteScreenPixelRectangle(0, 0, aSize.width(), aSize.height());
597 SalFrame* QtFrame::GetParent() const { return m_pParent; }
599 void QtFrame::SetModal(bool bModal)
601 if (!isWindow() || asChild()->isModal() == bModal)
602 return;
604 GetQtInstance().RunInMainThread([this, bModal]() {
606 QWidget* const pChild = asChild();
607 const bool bWasVisible = pChild->isVisible();
609 // modality change is only effective if the window is hidden
610 if (bWasVisible)
612 pChild->hide();
613 if (QGuiApplication::platformName() == "xcb")
615 SAL_WARN("vcl.qt", "SetModal called after Show - apply delay");
616 // tdf#152979 give QXcbConnection some time to avoid
617 // "qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window"
618 QThread::msleep(100);
622 pChild->setWindowModality(bModal ? Qt::WindowModal : Qt::NonModal);
624 if (bWasVisible)
625 pChild->show();
629 void QtFrame::SetWindowState(const vcl::WindowData* pState)
631 QtInstance& rQtInstance = GetQtInstance();
632 if (!rQtInstance.IsMainThread())
634 rQtInstance.RunInMainThread([this, pState]() { SetWindowState(pState); });
635 return;
638 if (!isWindow() || !pState || isChild(true, false))
639 return;
641 const vcl::WindowDataMask nMaxGeometryMask
642 = vcl::WindowDataMask::PosSize | vcl::WindowDataMask::MaximizedX
643 | vcl::WindowDataMask::MaximizedY | vcl::WindowDataMask::MaximizedWidth
644 | vcl::WindowDataMask::MaximizedHeight;
646 if ((pState->mask() & vcl::WindowDataMask::State)
647 && (pState->state() & vcl::WindowState::Maximized) && !isMaximized()
648 && (pState->mask() & nMaxGeometryMask) == nMaxGeometryMask)
650 const qreal fRatio = devicePixelRatioF();
651 QWidget* const pChild = asChild();
652 pChild->resize(ceil(pState->width() / fRatio), ceil(pState->height() / fRatio));
653 pChild->move(ceil(pState->x() / fRatio), ceil(pState->y() / fRatio));
654 SetWindowStateImpl(Qt::WindowMaximized);
656 else if (pState->mask() & vcl::WindowDataMask::PosSize)
658 sal_uInt16 nPosSizeFlags = 0;
659 if (pState->mask() & vcl::WindowDataMask::X)
660 nPosSizeFlags |= SAL_FRAME_POSSIZE_X;
661 if (pState->mask() & vcl::WindowDataMask::Y)
662 nPosSizeFlags |= SAL_FRAME_POSSIZE_Y;
663 if (pState->mask() & vcl::WindowDataMask::Width)
664 nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH;
665 if (pState->mask() & vcl::WindowDataMask::Height)
666 nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT;
667 SetPosSize(pState->x(), pState->y(), pState->width(), pState->height(), nPosSizeFlags);
669 else if (pState->mask() & vcl::WindowDataMask::State && !isChild())
671 if (pState->state() & vcl::WindowState::Maximized)
672 SetWindowStateImpl(Qt::WindowMaximized);
673 else if (pState->state() & vcl::WindowState::Minimized)
674 SetWindowStateImpl(Qt::WindowMinimized);
675 else
676 SetWindowStateImpl(Qt::WindowNoState);
680 bool QtFrame::GetWindowState(vcl::WindowData* pState)
682 pState->setState(vcl::WindowState::Normal);
683 pState->setMask(vcl::WindowDataMask::State);
684 if (isMinimized())
685 pState->rState() |= vcl::WindowState::Minimized;
686 else if (isMaximized())
687 pState->rState() |= vcl::WindowState::Maximized;
688 else
690 // we want the frame position and the client area size
691 QRect rect = scaledQRect({ asChild()->pos(), asChild()->size() }, devicePixelRatioF());
692 pState->setPosSize(toRectangle(rect));
693 pState->rMask() |= vcl::WindowDataMask::PosSize;
696 return true;
699 void QtFrame::ShowFullScreen(bool bFullScreen, sal_Int32 nScreen)
701 // only top-level windows can go fullscreen
702 assert(m_pTopLevel);
704 if (m_bFullScreen == bFullScreen)
705 return;
707 m_bFullScreen = bFullScreen;
708 m_bFullScreenSpanAll = m_bFullScreen && (nScreen < 0);
710 // show it if it isn't shown yet
711 if (!isWindow())
712 m_pTopLevel->show();
714 if (m_bFullScreen)
716 m_aRestoreGeometry = m_pTopLevel->geometry();
717 m_nRestoreScreen = std::max(sal_Int32(0), screenNumber());
718 SetScreenNumber(m_bFullScreenSpanAll ? m_nRestoreScreen : nScreen);
719 if (!m_bFullScreenSpanAll)
720 windowHandle()->showFullScreen();
721 else
722 windowHandle()->showNormal();
724 else
726 SetScreenNumber(m_nRestoreScreen);
727 windowHandle()->showNormal();
728 m_pTopLevel->setGeometry(m_aRestoreGeometry);
732 void QtFrame::StartPresentation(bool bStart)
734 #if !defined EMSCRIPTEN
735 assert(m_aSystemData.platform != SystemEnvData::Platform::Invalid);
737 #if CHECK_QT5_USING_X11
738 unsigned int nRootWindow(0);
739 std::optional<Display*> aDisplay;
740 if (QX11Info::isPlatformX11())
742 nRootWindow = QX11Info::appRootWindow();
743 aDisplay = QX11Info::display();
745 m_SessionManagerInhibitor.inhibit(bStart, u"presentation", APPLICATION_INHIBIT_IDLE,
746 nRootWindow, aDisplay);
747 #else
748 m_SessionManagerInhibitor.inhibit(bStart, u"presentation", APPLICATION_INHIBIT_IDLE);
749 #endif
750 #else
751 Q_UNUSED(bStart)
752 #endif
755 void QtFrame::SetAlwaysOnTop(bool bOnTop)
757 QWidget* const pWidget = asChild();
758 const Qt::WindowFlags flags = pWidget->windowFlags();
759 if (bOnTop)
760 pWidget->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
761 else
762 pWidget->setWindowFlags(flags & ~(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint));
765 void QtFrame::ToTop(SalFrameToTop nFlags)
767 GetQtInstance().RunInMainThread([this, nFlags]() {
768 QWidget* const pWidget = asChild();
769 if (isWindow() && !(nFlags & SalFrameToTop::GrabFocusOnly))
770 pWidget->raise();
771 if ((nFlags & SalFrameToTop::RestoreWhenMin) || (nFlags & SalFrameToTop::ForegroundTask))
773 if (nFlags & SalFrameToTop::RestoreWhenMin)
774 pWidget->setWindowState(pWidget->windowState() & ~Qt::WindowMinimized);
775 pWidget->activateWindow();
777 else if ((nFlags & SalFrameToTop::GrabFocus) || (nFlags & SalFrameToTop::GrabFocusOnly))
779 if (!(nFlags & SalFrameToTop::GrabFocusOnly))
780 pWidget->activateWindow();
781 pWidget->setFocus(Qt::OtherFocusReason);
786 void QtFrame::SetPointer(PointerStyle ePointerStyle)
788 if (ePointerStyle == m_ePointerStyle)
789 return;
790 m_ePointerStyle = ePointerStyle;
792 m_pQWidget->setCursor(GetQtData()->getCursor(ePointerStyle));
795 void QtFrame::CaptureMouse(bool bMouse)
797 static const char* pEnv = getenv("SAL_NO_MOUSEGRABS");
798 if (pEnv && *pEnv)
799 return;
801 if (bMouse)
802 m_pQWidget->grabMouse();
803 else
804 m_pQWidget->releaseMouse();
807 void QtFrame::SetPointerPos(tools::Long nX, tools::Long nY)
809 // some cursor already exists (and it has m_ePointerStyle shape)
810 // so here we just reposition it
811 QCursor::setPos(m_pQWidget->mapToGlobal(QPoint(nX, nY) / devicePixelRatioF()));
814 void QtFrame::Flush()
816 // was: QGuiApplication::sync();
817 // but FIXME it causes too many issues, figure out sth better
819 // unclear if we need to also flush cairo surface - gtk3 backend
820 // does not do it. QPainter in QtWidget::paintEvent() is
821 // destroyed, so that state should be safely flushed.
824 bool QtFrame::ShowTooltip(const OUString& rText, const tools::Rectangle& rHelpArea)
826 QRect aHelpArea(toQRect(rHelpArea));
827 if (QGuiApplication::isRightToLeft())
828 aHelpArea.moveLeft(GetWidth() - aHelpArea.width() - aHelpArea.left() - 1);
829 m_aTooltipText = rText;
830 m_aTooltipArea = aHelpArea;
831 return true;
834 void QtFrame::SetInputContext(SalInputContext* pContext)
836 if (!pContext)
837 return;
839 if (!(pContext->mnOptions & InputContextFlags::Text))
840 return;
842 m_pQWidget->setAttribute(Qt::WA_InputMethodEnabled);
845 void QtFrame::EndExtTextInput(EndExtTextInputFlags /*nFlags*/)
847 if (m_pQWidget)
848 m_pQWidget->endExtTextInput();
851 OUString QtFrame::GetKeyName(sal_uInt16 nKeyCode)
853 vcl::KeyCode vclKeyCode(nKeyCode);
854 int nCode = vclKeyCode.GetCode();
855 int nRetCode = 0;
857 if (nCode >= KEY_0 && nCode <= KEY_9)
858 nRetCode = (nCode - KEY_0) + Qt::Key_0;
859 else if (nCode >= KEY_A && nCode <= KEY_Z)
860 nRetCode = (nCode - KEY_A) + Qt::Key_A;
861 else if (nCode >= KEY_F1 && nCode <= KEY_F26)
862 nRetCode = (nCode - KEY_F1) + Qt::Key_F1;
863 else
865 switch (nCode)
867 case KEY_DOWN:
868 nRetCode = Qt::Key_Down;
869 break;
870 case KEY_UP:
871 nRetCode = Qt::Key_Up;
872 break;
873 case KEY_LEFT:
874 nRetCode = Qt::Key_Left;
875 break;
876 case KEY_RIGHT:
877 nRetCode = Qt::Key_Right;
878 break;
879 case KEY_HOME:
880 nRetCode = Qt::Key_Home;
881 break;
882 case KEY_END:
883 nRetCode = Qt::Key_End;
884 break;
885 case KEY_PAGEUP:
886 nRetCode = Qt::Key_PageUp;
887 break;
888 case KEY_PAGEDOWN:
889 nRetCode = Qt::Key_PageDown;
890 break;
891 case KEY_RETURN:
892 nRetCode = Qt::Key_Return;
893 break;
894 case KEY_ESCAPE:
895 nRetCode = Qt::Key_Escape;
896 break;
897 case KEY_TAB:
898 nRetCode = Qt::Key_Tab;
899 break;
900 case KEY_BACKSPACE:
901 nRetCode = Qt::Key_Backspace;
902 break;
903 case KEY_SPACE:
904 nRetCode = Qt::Key_Space;
905 break;
906 case KEY_INSERT:
907 nRetCode = Qt::Key_Insert;
908 break;
909 case KEY_DELETE:
910 nRetCode = Qt::Key_Delete;
911 break;
912 case KEY_ADD:
913 nRetCode = Qt::Key_Plus;
914 break;
915 case KEY_SUBTRACT:
916 nRetCode = Qt::Key_Minus;
917 break;
918 case KEY_MULTIPLY:
919 nRetCode = Qt::Key_Asterisk;
920 break;
921 case KEY_DIVIDE:
922 nRetCode = Qt::Key_Slash;
923 break;
924 case KEY_POINT:
925 nRetCode = Qt::Key_Period;
926 break;
927 case KEY_COMMA:
928 nRetCode = Qt::Key_Comma;
929 break;
930 case KEY_LESS:
931 nRetCode = Qt::Key_Less;
932 break;
933 case KEY_GREATER:
934 nRetCode = Qt::Key_Greater;
935 break;
936 case KEY_EQUAL:
937 nRetCode = Qt::Key_Equal;
938 break;
939 case KEY_FIND:
940 nRetCode = Qt::Key_Find;
941 break;
942 case KEY_CONTEXTMENU:
943 nRetCode = Qt::Key_Menu;
944 break;
945 case KEY_HELP:
946 nRetCode = Qt::Key_Help;
947 break;
948 case KEY_UNDO:
949 nRetCode = Qt::Key_Undo;
950 break;
951 case KEY_REPEAT:
952 nRetCode = Qt::Key_Redo;
953 break;
954 case KEY_TILDE:
955 nRetCode = Qt::Key_AsciiTilde;
956 break;
957 case KEY_QUOTELEFT:
958 nRetCode = Qt::Key_QuoteLeft;
959 break;
960 case KEY_BRACKETLEFT:
961 nRetCode = Qt::Key_BracketLeft;
962 break;
963 case KEY_BRACKETRIGHT:
964 nRetCode = Qt::Key_BracketRight;
965 break;
966 case KEY_NUMBERSIGN:
967 nRetCode = Qt::Key_NumberSign;
968 break;
969 case KEY_XF86FORWARD:
970 nRetCode = Qt::Key_Forward;
971 break;
972 case KEY_XF86BACK:
973 nRetCode = Qt::Key_Back;
974 break;
975 case KEY_COLON:
976 nRetCode = Qt::Key_Colon;
977 break;
978 case KEY_SEMICOLON:
979 nRetCode = Qt::Key_Semicolon;
980 break;
982 // Special cases
983 case KEY_COPY:
984 nRetCode = Qt::Key_Copy;
985 break;
986 case KEY_CUT:
987 nRetCode = Qt::Key_Cut;
988 break;
989 case KEY_PASTE:
990 nRetCode = Qt::Key_Paste;
991 break;
992 case KEY_OPEN:
993 nRetCode = Qt::Key_Open;
994 break;
998 if (vclKeyCode.IsShift())
999 nRetCode += Qt::SHIFT;
1000 if (vclKeyCode.IsMod1())
1001 nRetCode += Qt::CTRL;
1002 if (vclKeyCode.IsMod2())
1003 nRetCode += Qt::ALT;
1005 QKeySequence keySeq(nRetCode);
1006 OUString sKeyName = toOUString(keySeq.toString());
1008 return sKeyName;
1011 bool QtFrame::MapUnicodeToKeyCode(sal_Unicode /*aUnicode*/, LanguageType /*aLangType*/,
1012 vcl::KeyCode& /*rKeyCode*/)
1014 // not supported yet
1015 return false;
1018 LanguageType QtFrame::GetInputLanguage() { return m_nInputLanguage; }
1020 void QtFrame::setInputLanguage(LanguageType nInputLanguage)
1022 if (nInputLanguage == m_nInputLanguage)
1023 return;
1024 m_nInputLanguage = nInputLanguage;
1025 CallCallback(SalEvent::InputLanguageChange, nullptr);
1028 static bool toVclFont(const QFont& rQFont, const css::lang::Locale& rLocale, vcl::Font& rVclFont)
1030 FontAttributes aFA;
1031 QtFontFace::fillAttributesFromQFont(rQFont, aFA);
1033 bool bFound = psp::PrintFontManager::get().matchFont(aFA, rLocale);
1034 SAL_INFO("vcl.qt",
1035 "font match result for '"
1036 << rQFont.family() << "': "
1037 << (bFound ? OUString::Concat("'") + aFA.GetFamilyName() + "'" : u"failed"_ustr));
1039 if (!bFound)
1040 return false;
1042 QFontInfo qFontInfo(rQFont);
1043 int nPointHeight = qFontInfo.pointSize();
1044 if (nPointHeight <= 0)
1045 nPointHeight = rQFont.pointSize();
1047 vcl::Font aFont(aFA.GetFamilyName(), Size(0, nPointHeight));
1048 if (aFA.GetWeight() != WEIGHT_DONTKNOW)
1049 aFont.SetWeight(aFA.GetWeight());
1050 if (aFA.GetWidthType() != WIDTH_DONTKNOW)
1051 aFont.SetWidthType(aFA.GetWidthType());
1052 if (aFA.GetItalic() != ITALIC_DONTKNOW)
1053 aFont.SetItalic(aFA.GetItalic());
1054 if (aFA.GetPitch() != PITCH_DONTKNOW)
1055 aFont.SetPitch(aFA.GetPitch());
1057 rVclFont = aFont;
1058 return true;
1061 void QtFrame::UpdateSettings(AllSettings& rSettings)
1063 if (QtData::noNativeControls())
1064 return;
1065 QtCustomStyle::LoadCustomStyle(GetUseDarkMode());
1067 StyleSettings style(rSettings.GetStyleSettings());
1068 const css::lang::Locale aLocale = rSettings.GetUILanguageTag().getLocale();
1070 // General settings
1071 QPalette pal = QApplication::palette();
1073 style.SetToolbarIconSize(ToolbarIconSize::Large);
1075 Color aFore = toColor(pal.color(QPalette::Active, QPalette::WindowText));
1076 Color aBack = toColor(pal.color(QPalette::Active, QPalette::Window));
1077 Color aText = toColor(pal.color(QPalette::Active, QPalette::Text));
1078 Color aBase = toColor(pal.color(QPalette::Active, QPalette::Base));
1079 Color aButn = toColor(pal.color(QPalette::Active, QPalette::ButtonText));
1080 Color aMid = toColor(pal.color(QPalette::Active, QPalette::Mid));
1081 Color aHigh = toColor(pal.color(QPalette::Active, QPalette::Highlight));
1082 Color aHighText = toColor(pal.color(QPalette::Active, QPalette::HighlightedText));
1083 Color aLink = toColor(pal.color(QPalette::Active, QPalette::Link));
1084 Color aVisitedLink = toColor(pal.color(QPalette::Active, QPalette::LinkVisited));
1086 style.SetSkipDisabledInMenus(true);
1088 // Foreground
1089 style.SetRadioCheckTextColor(aFore);
1090 style.SetLabelTextColor(aFore);
1091 style.SetDialogTextColor(aFore);
1092 style.SetGroupTextColor(aFore);
1094 // Text
1095 style.SetFieldTextColor(aText);
1096 style.SetFieldRolloverTextColor(aText);
1097 style.SetListBoxWindowTextColor(aText);
1098 style.SetWindowTextColor(aText);
1099 style.SetToolTextColor(aText);
1101 // Base
1102 style.SetFieldColor(aBase);
1103 style.SetActiveTabColor(aBase);
1104 style.SetListBoxWindowBackgroundColor(aBase);
1105 style.SetAlternatingRowColor(toColor(pal.color(QPalette::Active, QPalette::AlternateBase)));
1107 // Buttons
1108 style.SetDefaultButtonTextColor(aButn);
1109 style.SetButtonTextColor(aButn);
1110 style.SetDefaultActionButtonTextColor(aButn);
1111 style.SetActionButtonTextColor(aButn);
1112 style.SetFlatButtonTextColor(aButn);
1113 style.SetDefaultButtonRolloverTextColor(aButn);
1114 style.SetButtonRolloverTextColor(aButn);
1115 style.SetDefaultActionButtonRolloverTextColor(aButn);
1116 style.SetActionButtonRolloverTextColor(aButn);
1117 style.SetFlatButtonRolloverTextColor(aButn);
1118 style.SetDefaultButtonPressedRolloverTextColor(aButn);
1119 style.SetButtonPressedRolloverTextColor(aButn);
1120 style.SetDefaultActionButtonPressedRolloverTextColor(aButn);
1121 style.SetActionButtonPressedRolloverTextColor(aButn);
1122 style.SetFlatButtonPressedRolloverTextColor(aButn);
1124 // Tabs
1125 style.SetTabTextColor(aButn);
1126 style.SetTabRolloverTextColor(aButn);
1127 style.SetTabHighlightTextColor(aButn);
1129 // Disable color
1130 style.SetDisableColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
1132 // Background
1133 style.BatchSetBackgrounds(aBack);
1134 style.SetInactiveTabColor(aBack);
1135 style.SetWindowColor(aBack);
1137 // Workspace
1138 style.SetWorkspaceColor(aMid);
1140 // Selection
1141 // https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/305
1142 style.SetAccentColor(aHigh);
1143 style.SetHighlightColor(aHigh);
1144 style.SetHighlightTextColor(aHighText);
1145 style.SetListBoxWindowHighlightColor(aHigh);
1146 style.SetListBoxWindowHighlightTextColor(aHighText);
1147 style.SetActiveColor(aHigh);
1148 style.SetActiveTextColor(aHighText);
1150 // Links
1151 style.SetLinkColor(aLink);
1152 style.SetVisitedLinkColor(aVisitedLink);
1154 // Tooltip
1155 style.SetHelpColor(toColor(QToolTip::palette().color(QPalette::Active, QPalette::ToolTipBase)));
1156 style.SetHelpTextColor(
1157 toColor(QToolTip::palette().color(QPalette::Active, QPalette::ToolTipText)));
1159 // Menu
1160 std::unique_ptr<QMenuBar> pMenuBar = std::make_unique<QMenuBar>();
1161 QPalette qMenuCG = pMenuBar->palette();
1163 // Menu text and background color, theme specific
1164 Color aMenuFore = toColor(qMenuCG.color(QPalette::WindowText));
1165 Color aMenuBack = toColor(qMenuCG.color(QPalette::Window));
1167 style.SetMenuTextColor(aMenuFore);
1168 style.SetMenuBarTextColor(style.GetPersonaMenuBarTextColor().value_or(aMenuFore));
1169 style.SetMenuColor(aMenuBack);
1170 style.SetMenuBarColor(aMenuBack);
1171 style.SetMenuHighlightColor(toColor(qMenuCG.color(QPalette::Highlight)));
1172 style.SetMenuHighlightTextColor(toColor(qMenuCG.color(QPalette::HighlightedText)));
1174 // set special menubar highlight text color
1175 if (QApplication::style()->inherits("HighContrastStyle"))
1176 ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor
1177 = toColor(qMenuCG.color(QPalette::HighlightedText));
1178 else
1179 ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor = aMenuFore;
1181 // set menubar rollover color
1182 if (pMenuBar->style()->styleHint(QStyle::SH_MenuBar_MouseTracking))
1184 style.SetMenuBarRolloverColor(toColor(qMenuCG.color(QPalette::Highlight)));
1185 style.SetMenuBarRolloverTextColor(ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor);
1187 else
1189 style.SetMenuBarRolloverColor(aMenuBack);
1190 style.SetMenuBarRolloverTextColor(aMenuFore);
1192 style.SetMenuBarHighlightTextColor(style.GetMenuHighlightTextColor());
1194 // Default fonts
1195 vcl::Font aFont;
1196 if (toVclFont(QApplication::font(), aLocale, aFont))
1198 style.BatchSetFonts(aFont, aFont);
1199 aFont.SetWeight(WEIGHT_BOLD);
1200 style.SetTitleFont(aFont);
1201 style.SetFloatTitleFont(aFont);
1204 // Tooltip font
1205 if (toVclFont(QToolTip::font(), aLocale, aFont))
1206 style.SetHelpFont(aFont);
1208 // Menu bar font
1209 if (toVclFont(pMenuBar->font(), aLocale, aFont))
1210 style.SetMenuFont(aFont);
1212 // Icon theme
1213 const bool bPreferDarkTheme = GetUseDarkMode();
1214 style.SetPreferredIconTheme(toOUString(QIcon::themeName()), bPreferDarkTheme);
1216 // Scroll bar size
1217 style.SetScrollBarSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent));
1218 style.SetMinThumbSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarSliderMin));
1220 // These colors are used for the ruler text and marks
1221 style.SetShadowColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
1222 style.SetDarkShadowColor(toColor(pal.color(QPalette::Inactive, QPalette::WindowText)));
1224 // match native QComboBox behavior of putting text cursor to end of text
1225 // without text selection when combobox entry is selected
1226 style.SetComboBoxTextSelectionMode(ComboBoxTextSelectionMode::CursorToEnd);
1228 // Cursor blink interval
1229 int nFlashTime = QApplication::cursorFlashTime();
1230 style.SetCursorBlinkTime(nFlashTime != 0 ? nFlashTime / 2 : STYLE_CURSOR_NOBLINKTIME);
1232 rSettings.SetStyleSettings(style);
1235 void QtFrame::Beep() { QApplication::beep(); }
1237 SalFrame::SalPointerState QtFrame::GetPointerState()
1239 SalPointerState aState;
1240 aState.maPos = toPoint(QCursor::pos() * devicePixelRatioF());
1241 SalFrameGeometry aGeometry = GetUnmirroredGeometry();
1242 aState.maPos.Move(-aGeometry.x(), -aGeometry.y());
1243 aState.mnState = GetMouseModCode(QGuiApplication::mouseButtons())
1244 | GetKeyModCode(QGuiApplication::keyboardModifiers());
1245 return aState;
1248 KeyIndicatorState QtFrame::GetIndicatorState() { return KeyIndicatorState(); }
1250 void QtFrame::SimulateKeyPress(sal_uInt16 nKeyCode)
1252 SAL_WARN("vcl.qt", "missing simulate keypress " << nKeyCode);
1255 // don't set QWidget parents; this breaks popups on Wayland, like the LO ComboBox or ColorPicker!
1256 void QtFrame::SetParent(SalFrame* pNewParent) { m_pParent = static_cast<QtFrame*>(pNewParent); }
1258 void QtFrame::SetPluginParent(SystemParentData* /*pNewParent*/)
1260 //FIXME: no SetPluginParent impl. for qt5
1263 void QtFrame::ResetClipRegion() { m_bNullRegion = true; }
1265 void QtFrame::BeginSetClipRegion(sal_uInt32)
1267 m_aRegion = QRegion(QRect(QPoint(0, 0), m_pQWidget->size()));
1270 void QtFrame::UnionClipRegion(tools::Long nX, tools::Long nY, tools::Long nWidth,
1271 tools::Long nHeight)
1273 m_aRegion
1274 = m_aRegion.united(scaledQRect(QRect(nX, nY, nWidth, nHeight), 1 / devicePixelRatioF()));
1277 void QtFrame::EndSetClipRegion() { m_bNullRegion = false; }
1279 void QtFrame::SetScreenNumber(unsigned int nScreen)
1281 if (!isWindow())
1282 return;
1284 QWindow* const pWindow = windowHandle();
1285 if (!pWindow)
1286 return;
1288 QList<QScreen*> screens = QApplication::screens();
1289 if (static_cast<int>(nScreen) >= screens.size() && !m_bFullScreenSpanAll)
1291 SAL_WARN("vcl.qt", "Ignoring request to set invalid screen number");
1292 return;
1295 QRect screenGeo;
1297 if (!m_bFullScreenSpanAll)
1299 screenGeo = QGuiApplication::screens().at(nScreen)->geometry();
1300 pWindow->setScreen(QApplication::screens().at(nScreen));
1302 else // special case: fullscreen over all available screens
1304 assert(m_bFullScreen);
1305 // left-most screen
1306 QScreen* pScreen = QGuiApplication::screenAt(QPoint(0, 0));
1307 // entire virtual desktop
1308 screenGeo = pScreen->availableVirtualGeometry();
1309 pWindow->setScreen(pScreen);
1310 pWindow->setGeometry(screenGeo);
1313 // setScreen by itself has no effect, explicitly move the widget to
1314 // the new screen
1315 asChild()->move(screenGeo.topLeft());
1318 void QtFrame::SetApplicationID(const OUString& rWMClass)
1320 #if CHECK_QT5_USING_X11
1321 assert(m_aSystemData.platform != SystemEnvData::Platform::Invalid);
1322 if (m_aSystemData.platform != SystemEnvData::Platform::Xcb || !m_pTopLevel)
1323 return;
1325 QtX11Support::setApplicationID(m_pTopLevel->winId(), rWMClass);
1326 #else
1327 Q_UNUSED(rWMClass);
1328 #endif
1331 void QtFrame::ResolveWindowHandle(SystemEnvData& rData) const
1333 if (!rData.pWidget)
1334 return;
1335 assert(rData.platform != SystemEnvData::Platform::Invalid);
1336 // Calling QWidget::winId() implicitly enables native windows to be used instead
1337 // of "alien widgets" that don't have a native widget associated with them,
1338 // s. https://doc.qt.io/qt-6/qwidget.html#native-widgets-vs-alien-widgets
1339 // Avoid native widgets with Qt 5 on Wayland and with Qt 6 altogether as they
1340 // cause unresponsive UI, s. tdf#122293/QTBUG-75766 and tdf#160565
1341 // (for qt5 xcb, they're needed for video playback)
1342 if (rData.platform != SystemEnvData::Platform::Wayland
1343 && QLibraryInfo::version().majorVersion() < 6)
1345 rData.SetWindowHandle(static_cast<QWidget*>(rData.pWidget)->winId());
1349 bool QtFrame::GetUseReducedAnimation() const { return GetQtInstance().GetUseReducedAnimation(); }
1351 // Drag'n'drop foo
1353 void QtFrame::registerDragSource(QtDragSource* pDragSource)
1355 assert(!m_pDragSource);
1356 m_pDragSource = pDragSource;
1359 void QtFrame::deregisterDragSource(QtDragSource const* pDragSource)
1361 assert(m_pDragSource == pDragSource);
1362 (void)pDragSource;
1363 m_pDragSource = nullptr;
1366 void QtFrame::registerDropTarget(QtDropTarget* pDropTarget)
1368 assert(!m_pDropTarget);
1369 m_pDropTarget = pDropTarget;
1371 GetQtInstance().RunInMainThread([this]() { m_pQWidget->setAcceptDrops(true); });
1374 void QtFrame::deregisterDropTarget(QtDropTarget const* pDropTarget)
1376 assert(m_pDropTarget == pDropTarget);
1377 (void)pDropTarget;
1378 m_pDropTarget = nullptr;
1381 static css::uno::Reference<css::datatransfer::XTransferable>
1382 lcl_getXTransferable(const QMimeData* pMimeData)
1384 css::uno::Reference<css::datatransfer::XTransferable> xTransferable;
1385 const QtMimeData* pQtMimeData = qobject_cast<const QtMimeData*>(pMimeData);
1386 if (!pQtMimeData)
1387 xTransferable = new QtDnDTransferable(pMimeData);
1388 else
1389 xTransferable = pQtMimeData->xTransferable();
1390 return xTransferable;
1393 static sal_Int8 lcl_getUserDropAction(const QDropEvent* pEvent, const sal_Int8 nSourceActions,
1394 const QMimeData* pMimeData)
1396 // we completely ignore all proposals by the Qt event, as they don't
1397 // match at all with the preferred LO DnD actions.
1398 // check the key modifiers to detect a user-overridden DnD action
1399 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1400 const Qt::KeyboardModifiers eKeyMod = pEvent->modifiers();
1401 #else
1402 const Qt::KeyboardModifiers eKeyMod = pEvent->keyboardModifiers();
1403 #endif
1404 sal_Int8 nUserDropAction = 0;
1405 if ((eKeyMod & Qt::ShiftModifier) && !(eKeyMod & Qt::ControlModifier))
1406 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_MOVE;
1407 else if ((eKeyMod & Qt::ControlModifier) && !(eKeyMod & Qt::ShiftModifier))
1408 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1409 else if ((eKeyMod & Qt::ShiftModifier) && (eKeyMod & Qt::ControlModifier))
1410 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_LINK;
1411 nUserDropAction &= nSourceActions;
1413 // select the default DnD action, if there isn't a user preference
1414 if (0 == nUserDropAction)
1416 // default LO internal action is move, but default external action is copy
1417 nUserDropAction = qobject_cast<const QtMimeData*>(pMimeData)
1418 ? css::datatransfer::dnd::DNDConstants::ACTION_MOVE
1419 : css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1420 nUserDropAction &= nSourceActions;
1422 // if the default doesn't match any allowed source action, fall back to the
1423 // preferred of all allowed source actions
1424 if (0 == nUserDropAction)
1425 nUserDropAction = toVclDropAction(getPreferredDropAction(nSourceActions));
1427 // this is "our" preference, but actually we would even prefer any default,
1428 // if there is any
1429 nUserDropAction |= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT;
1431 return nUserDropAction;
1434 void QtFrame::handleDragMove(QDragMoveEvent* pEvent)
1436 assert(m_pDropTarget);
1438 // prepare our suggested drop action for the drop target
1439 const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions());
1440 const QMimeData* pMimeData = pEvent->mimeData();
1441 const sal_Int8 nUserDropAction = lcl_getUserDropAction(pEvent, nSourceActions, pMimeData);
1443 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1444 const Point aPos = toPoint(pEvent->position().toPoint() * devicePixelRatioF());
1445 #else
1446 const Point aPos = toPoint(pEvent->pos() * devicePixelRatioF());
1447 #endif
1449 css::datatransfer::dnd::DropTargetDragEnterEvent aEvent;
1450 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1451 aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDragContext*>(m_pDropTarget);
1452 aEvent.LocationX = aPos.X();
1453 aEvent.LocationY = aPos.Y();
1454 aEvent.DropAction = nUserDropAction;
1455 aEvent.SourceActions = nSourceActions;
1457 // ask the drop target to accept our drop action
1458 if (!m_bInDrag)
1460 aEvent.SupportedDataFlavors = lcl_getXTransferable(pMimeData)->getTransferDataFlavors();
1461 m_pDropTarget->fire_dragEnter(aEvent);
1462 m_bInDrag = true;
1464 else
1465 m_pDropTarget->fire_dragOver(aEvent);
1467 // the drop target accepted our drop action => inform Qt
1468 if (m_pDropTarget->proposedDropAction() != 0)
1470 pEvent->setDropAction(getPreferredDropAction(m_pDropTarget->proposedDropAction()));
1471 pEvent->accept();
1473 else // or maybe someone else likes it?
1474 pEvent->ignore();
1477 void QtFrame::handleDrop(QDropEvent* pEvent)
1479 assert(m_pDropTarget);
1481 // prepare our suggested drop action for the drop target
1482 const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions());
1483 const sal_Int8 nUserDropAction
1484 = lcl_getUserDropAction(pEvent, nSourceActions, pEvent->mimeData());
1486 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1487 const Point aPos = toPoint(pEvent->position().toPoint() * devicePixelRatioF());
1488 #else
1489 const Point aPos = toPoint(pEvent->pos() * devicePixelRatioF());
1490 #endif
1492 css::datatransfer::dnd::DropTargetDropEvent aEvent;
1493 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1494 aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDropContext*>(m_pDropTarget);
1495 aEvent.LocationX = aPos.X();
1496 aEvent.LocationY = aPos.Y();
1497 aEvent.SourceActions = nSourceActions;
1498 aEvent.DropAction = nUserDropAction;
1499 aEvent.Transferable = lcl_getXTransferable(pEvent->mimeData());
1501 // ask the drop target to accept our drop action
1502 m_pDropTarget->fire_drop(aEvent);
1503 m_bInDrag = false;
1505 const bool bDropSuccessful = m_pDropTarget->dropSuccessful();
1506 const sal_Int8 nDropAction = m_pDropTarget->proposedDropAction();
1508 // inform the drag source of the drag-origin frame of the drop result
1509 if (pEvent->source())
1511 QtWidget* pWidget = qobject_cast<QtWidget*>(pEvent->source());
1512 assert(pWidget); // AFAIK there shouldn't be any non-Qt5Widget as source in LO itself
1513 if (pWidget)
1514 pWidget->frame().m_pDragSource->fire_dragEnd(nDropAction, bDropSuccessful);
1517 // the drop target accepted our drop action => inform Qt
1518 if (bDropSuccessful)
1520 pEvent->setDropAction(getPreferredDropAction(nDropAction));
1521 pEvent->accept();
1523 else // or maybe someone else likes it?
1524 pEvent->ignore();
1527 void QtFrame::handleDragLeave()
1529 css::datatransfer::dnd::DropTargetEvent aEvent;
1530 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1531 m_pDropTarget->fire_dragExit(aEvent);
1532 m_bInDrag = false;
1535 void QtFrame::handleMoveEvent(QMoveEvent*) { CallCallback(SalEvent::Move, nullptr); }
1537 void QtFrame::handlePaintEvent(QPaintEvent* pEvent, QWidget* pWidget)
1539 QPainter p(pWidget);
1540 if (!m_bNullRegion)
1541 p.setClipRegion(m_aRegion);
1543 QImage aImage;
1544 if (m_bUseCairo)
1546 cairo_surface_t* pSurface = m_pSurface.get();
1547 cairo_surface_flush(pSurface);
1549 aImage = QImage(cairo_image_surface_get_data(pSurface),
1550 cairo_image_surface_get_width(pSurface),
1551 cairo_image_surface_get_height(pSurface), Qt_DefaultFormat32);
1553 else
1554 aImage = *m_pQImage;
1556 const qreal fRatio = devicePixelRatioF();
1557 aImage.setDevicePixelRatio(fRatio);
1558 QRectF source(pEvent->rect().topLeft() * fRatio, pEvent->rect().size() * fRatio);
1559 p.drawImage(pEvent->rect(), aImage, source);
1562 void QtFrame::handleResizeEvent(QResizeEvent* pEvent)
1564 const qreal fRatio = devicePixelRatioF();
1565 const int nWidth = ceil(pEvent->size().width() * fRatio);
1566 const int nHeight = ceil(pEvent->size().height() * fRatio);
1568 if (m_bUseCairo)
1570 if (m_pSurface)
1572 const int nOldWidth = cairo_image_surface_get_width(m_pSurface.get());
1573 const int nOldHeight = cairo_image_surface_get_height(m_pSurface.get());
1574 if (nOldWidth != nWidth || nOldHeight != nHeight)
1576 cairo_surface_t* pSurface
1577 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, nWidth, nHeight);
1578 cairo_surface_set_user_data(pSurface, SvpSalGraphics::getDamageKey(),
1579 &m_aDamageHandler, nullptr);
1580 m_pSvpGraphics->setSurface(pSurface, basegfx::B2IVector(nWidth, nHeight));
1581 UniqueCairoSurface old_surface(m_pSurface.release());
1582 m_pSurface.reset(pSurface);
1584 const int nMinWidth = qMin(nOldWidth, nWidth);
1585 const int nMinHeight = qMin(nOldHeight, nHeight);
1586 SalTwoRect rect(0, 0, nMinWidth, nMinHeight, 0, 0, nMinWidth, nMinHeight);
1587 m_pSvpGraphics->copySource(rect, old_surface.get());
1591 else
1593 if (m_pQImage && m_pQImage->size() != QSize(nWidth, nHeight))
1595 QImage* pImage = new QImage(m_pQImage->copy(0, 0, nWidth, nHeight));
1596 m_pQtGraphics->ChangeQImage(pImage);
1597 m_pQImage.reset(pImage);
1601 CallCallback(SalEvent::Resize, nullptr);
1604 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */