uno grid grid a11y: Return existing cell accessible
[LibreOffice.git] / vcl / qt5 / QtFrame.cxx
bloba8debeff5a8e0cca05285df966bf376b1b6df788
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::WASM:
273 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
274 // no idea, why Qt::WA_NativeWindow breaks the menubar for EMSCRIPTEN
275 pChild->setAttribute(Qt::WA_NativeWindow);
276 break;
277 #endif
278 case SystemEnvData::Platform::Wayland:
279 case SystemEnvData::Platform::Xcb:
280 pChild->setAttribute(Qt::WA_NativeWindow);
281 break;
282 case SystemEnvData::Platform::Invalid:
283 std::abort();
284 break;
286 return pChild->windowHandle();
289 QScreen* QtFrame::screen() const { return asChild()->screen(); }
291 sal_Int32 QtFrame::screenNumber() const
293 QScreen* pScreen = screen();
294 const QList<QScreen*> screens = QApplication::screens();
295 return screens.indexOf(pScreen);
298 bool QtFrame::GetUseDarkMode() const
300 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
301 const Qt::ColorScheme eColorScheme = QApplication::styleHints()->colorScheme();
302 if (eColorScheme == Qt::ColorScheme::Dark)
303 return true;
304 if (eColorScheme == Qt::ColorScheme::Light)
305 return false;
306 #endif
308 // use same mechanism for determining dark mode preference as xdg-desktop-portal-kde, s.
309 // https://invent.kde.org/plasma/xdg-desktop-portal-kde/-/blob/0a4237549debf9518f8cfbaf531456850c0729bd/src/settings.cpp#L213-227
310 const QPalette aPalette = QApplication::palette();
311 const int nWindowBackGroundGray = qGray(aPalette.window().color().rgb());
312 return nWindowBackGroundGray < 192;
315 bool QtFrame::isMinimized() const { return asChild()->isMinimized(); }
317 bool QtFrame::isMaximized() const { return asChild()->isMaximized(); }
319 void QtFrame::SetWindowStateImpl(Qt::WindowStates eState)
321 return asChild()->setWindowState(eState);
324 void QtFrame::SetTitle(const OUString& rTitle)
326 GetQtInstance().RunInMainThread(
327 [this, rTitle]() { m_pQWidget->window()->setWindowTitle(toQString(rTitle)); });
330 void QtFrame::SetIcon(sal_uInt16 nIcon)
332 QtInstance& rQtInstance = GetQtInstance();
333 if (!rQtInstance.IsMainThread())
335 rQtInstance.RunInMainThread([this, nIcon]() { SetIcon(nIcon); });
336 return;
339 if (m_nStyle
340 & (SalFrameStyleFlags::PLUG | SalFrameStyleFlags::SYSTEMCHILD
341 | SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::INTRO
342 | SalFrameStyleFlags::OWNERDRAWDECORATION)
343 || !isWindow())
344 return;
346 QString appicon;
348 if (nIcon == SV_ICON_ID_TEXT)
349 appicon = "libreoffice-writer";
350 else if (nIcon == SV_ICON_ID_SPREADSHEET)
351 appicon = "libreoffice-calc";
352 else if (nIcon == SV_ICON_ID_DRAWING)
353 appicon = "libreoffice-draw";
354 else if (nIcon == SV_ICON_ID_PRESENTATION)
355 appicon = "libreoffice-impress";
356 else if (nIcon == SV_ICON_ID_DATABASE)
357 appicon = "libreoffice-base";
358 else if (nIcon == SV_ICON_ID_FORMULA)
359 appicon = "libreoffice-math";
360 else
361 appicon = "libreoffice-startcenter";
363 QIcon aIcon = QIcon::fromTheme(appicon);
364 m_pQWidget->window()->setWindowIcon(aIcon);
366 if (QGuiApplication::platformName() == "wayland" && m_pQWidget->window()->isVisible())
368 // Qt currently doesn't provide API to directly set the app_id for a single
369 // window/toplevel on Wayland, but the one set for the application is picked up
370 // on hide/show, so do that.
371 // An alternative would be to use private Qt API and low-level wayland API to set the
372 // app_id directly, s. discussion in QTBUG-77182.
373 const QString sOrigDesktopFileName = QGuiApplication::desktopFileName();
374 QGuiApplication::setDesktopFileName(appicon);
375 m_pQWidget->window()->hide();
376 m_pQWidget->window()->show();
377 QGuiApplication::setDesktopFileName(sOrigDesktopFileName);
381 void QtFrame::SetMenu(SalMenu*) {}
383 void QtFrame::SetExtendedFrameStyle(SalExtStyle /*nExtStyle*/) { /* not needed */}
385 void QtFrame::Show(bool bVisible, bool bNoActivate)
387 SolarMutexGuard g;
388 QtInstance& rQtInstance = GetQtInstance();
389 if (!rQtInstance.IsMainThread())
391 rQtInstance.RunInMainThread([&] { Show(bVisible, bNoActivate); });
392 return;
395 assert(m_pQWidget);
396 if (bVisible == asChild()->isVisible())
397 return;
399 if (!bVisible) // hide
401 asChild()->setVisible(false);
402 return;
405 QWindow* pChildWindow = windowHandle();
406 connect(pChildWindow, &QWindow::screenChanged, this, &QtFrame::screenChanged,
407 Qt::UniqueConnection);
409 if (m_pParent && !(m_pParent->m_nStyle & SalFrameStyleFlags::PLUG))
411 QWindow* pParentWindow = m_pParent->windowHandle();
412 if (pParentWindow && pChildWindow && (pParentWindow != pChildWindow))
413 pChildWindow->setTransientParent(pParentWindow);
416 // show
417 SetDefaultSize();
419 QWidget* const pChild = asChild();
420 pChild->setVisible(true);
421 pChild->raise();
422 if (!bNoActivate)
424 pChild->activateWindow();
425 pChild->setFocus();
429 void QtFrame::SetMinClientSize(tools::Long nWidth, tools::Long nHeight)
431 if (!isChild())
433 const qreal fRatio = devicePixelRatioF();
434 asChild()->setMinimumSize(round(nWidth / fRatio), round(nHeight / fRatio));
438 void QtFrame::SetMaxClientSize(tools::Long nWidth, tools::Long nHeight)
440 if (!isChild())
442 const qreal fRatio = devicePixelRatioF();
443 asChild()->setMaximumSize(round(nWidth / fRatio), round(nHeight / fRatio));
447 void QtFrame::SetDefaultPos()
449 if (!m_bDefaultPos)
450 return;
452 // center on parent
453 if (m_pParent)
455 const qreal fRatio = devicePixelRatioF();
456 QWidget* const pParentWin = m_pParent->asChild()->window();
457 QWidget* const pChildWin = asChild()->window();
458 QPoint aPos = (pParentWin->rect().center() - pChildWin->rect().center()) * fRatio;
459 SetPosSize(aPos.x(), aPos.y(), 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y);
460 assert(!m_bDefaultPos);
462 else
463 m_bDefaultPos = false;
466 Size QtFrame::CalcDefaultSize()
468 assert(isWindow());
470 Size aSize;
471 if (!m_bFullScreen)
473 const QScreen* pScreen = screen();
474 if (!pScreen)
475 pScreen = QGuiApplication::screens().at(0);
476 aSize = bestmaxFrameSizeForScreenSize(toSize(pScreen->size()));
478 else
480 if (!m_bFullScreenSpanAll)
482 aSize = toSize(screen()->size());
484 else
486 QScreen* pScreen = QGuiApplication::screenAt(QPoint(0, 0));
487 aSize = toSize(pScreen->availableVirtualGeometry().size());
491 return aSize;
494 void QtFrame::SetDefaultSize()
496 if (!m_bDefaultSize)
497 return;
499 Size aDefSize = CalcDefaultSize();
500 SetPosSize(0, 0, aDefSize.Width(), aDefSize.Height(),
501 SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT);
502 assert(!m_bDefaultSize);
505 void QtFrame::SetPosSize(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
506 sal_uInt16 nFlags)
508 SolarMutexGuard g;
509 QtInstance& rQtInstance = GetQtInstance();
510 if (!rQtInstance.IsMainThread())
512 rQtInstance.RunInMainThread([&] { SetPosSize(nX, nY, nWidth, nHeight, nFlags); });
515 if (!isWindow() || isChild(true, false))
516 return;
518 if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT))
520 if (isChild(false) || !m_pQWidget->isMaximized())
522 if (!(nFlags & SAL_FRAME_POSSIZE_WIDTH))
523 nWidth = GetWidth();
524 else if (!(nFlags & SAL_FRAME_POSSIZE_HEIGHT))
525 nHeight = GetHeight();
527 if (nWidth > 0 && nHeight > 0)
529 m_bDefaultSize = false;
530 const int nNewWidth = round(nWidth / devicePixelRatioF());
531 const int nNewHeight = round(nHeight / devicePixelRatioF());
532 if (m_nStyle & SalFrameStyleFlags::SIZEABLE)
533 asChild()->resize(nNewWidth, nNewHeight);
534 else
535 asChild()->setFixedSize(nNewWidth, nNewHeight);
540 if (!(nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y)))
542 if (nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT))
543 SetDefaultPos();
544 return;
547 if (m_pParent)
549 const SalFrameGeometry aParentGeometry = m_pParent->GetUnmirroredGeometry();
550 if (QGuiApplication::isRightToLeft())
551 nX = aParentGeometry.x() + aParentGeometry.width() - nX - GetWidth() - 1;
552 else
553 nX += aParentGeometry.x();
554 nY += aParentGeometry.y();
557 if (!(nFlags & SAL_FRAME_POSSIZE_X))
558 nX = GetUnmirroredGeometry().x();
559 else if (!(nFlags & SAL_FRAME_POSSIZE_Y))
560 nY = GetUnmirroredGeometry().y();
562 m_bDefaultPos = false;
563 asChild()->move(round(nX / devicePixelRatioF()), round(nY / devicePixelRatioF()));
566 void QtFrame::GetClientSize(tools::Long& rWidth, tools::Long& rHeight)
568 rWidth = round(m_pQWidget->width() * devicePixelRatioF());
569 rHeight = round(m_pQWidget->height() * devicePixelRatioF());
572 SalFrameGeometry QtFrame::GetUnmirroredGeometry() const
574 SalFrameGeometry aGeometry;
576 const qreal fRatio = devicePixelRatioF();
577 const QPoint aScreenPos = m_pQWidget->mapToGlobal(QPoint(0, 0));
578 aGeometry.setX(aScreenPos.x() * fRatio);
579 aGeometry.setY(aScreenPos.y() * fRatio);
580 aGeometry.setWidth(m_pQWidget->width() * fRatio);
581 aGeometry.setHeight(m_pQWidget->height() * fRatio);
583 aGeometry.setScreen(std::max(sal_Int32(0), screenNumber()));
585 return aGeometry;
588 void QtFrame::GetWorkArea(AbsoluteScreenPixelRectangle& rRect)
590 if (!isWindow())
591 return;
592 QScreen* pScreen = screen();
593 if (!pScreen)
594 return;
596 QSize aSize = pScreen->availableVirtualSize() * devicePixelRatioF();
597 rRect = AbsoluteScreenPixelRectangle(0, 0, aSize.width(), aSize.height());
600 SalFrame* QtFrame::GetParent() const { return m_pParent; }
602 void QtFrame::SetModal(bool bModal)
604 if (!isWindow() || asChild()->isModal() == bModal)
605 return;
607 GetQtInstance().RunInMainThread([this, bModal]() {
609 QWidget* const pChild = asChild();
610 const bool bWasVisible = pChild->isVisible();
612 // modality change is only effective if the window is hidden
613 if (bWasVisible)
615 pChild->hide();
616 if (QGuiApplication::platformName() == "xcb")
618 SAL_WARN("vcl.qt", "SetModal called after Show - apply delay");
619 // tdf#152979 give QXcbConnection some time to avoid
620 // "qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window"
621 QThread::msleep(100);
625 pChild->setWindowModality(bModal ? Qt::WindowModal : Qt::NonModal);
627 if (bWasVisible)
628 pChild->show();
632 void QtFrame::SetWindowState(const vcl::WindowData* pState)
634 QtInstance& rQtInstance = GetQtInstance();
635 if (!rQtInstance.IsMainThread())
637 rQtInstance.RunInMainThread([this, pState]() { SetWindowState(pState); });
638 return;
641 if (!isWindow() || !pState || isChild(true, false))
642 return;
644 const vcl::WindowDataMask nMaxGeometryMask
645 = vcl::WindowDataMask::PosSize | vcl::WindowDataMask::MaximizedX
646 | vcl::WindowDataMask::MaximizedY | vcl::WindowDataMask::MaximizedWidth
647 | vcl::WindowDataMask::MaximizedHeight;
649 if ((pState->mask() & vcl::WindowDataMask::State)
650 && (pState->state() & vcl::WindowState::Maximized) && !isMaximized()
651 && (pState->mask() & nMaxGeometryMask) == nMaxGeometryMask)
653 const qreal fRatio = devicePixelRatioF();
654 QWidget* const pChild = asChild();
655 pChild->resize(ceil(pState->width() / fRatio), ceil(pState->height() / fRatio));
656 pChild->move(ceil(pState->x() / fRatio), ceil(pState->y() / fRatio));
657 SetWindowStateImpl(Qt::WindowMaximized);
659 else if (pState->mask() & vcl::WindowDataMask::PosSize)
661 sal_uInt16 nPosSizeFlags = 0;
662 if (pState->mask() & vcl::WindowDataMask::X)
663 nPosSizeFlags |= SAL_FRAME_POSSIZE_X;
664 if (pState->mask() & vcl::WindowDataMask::Y)
665 nPosSizeFlags |= SAL_FRAME_POSSIZE_Y;
666 if (pState->mask() & vcl::WindowDataMask::Width)
667 nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH;
668 if (pState->mask() & vcl::WindowDataMask::Height)
669 nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT;
670 SetPosSize(pState->x(), pState->y(), pState->width(), pState->height(), nPosSizeFlags);
672 else if (pState->mask() & vcl::WindowDataMask::State && !isChild())
674 if (pState->state() & vcl::WindowState::Maximized)
675 SetWindowStateImpl(Qt::WindowMaximized);
676 else if (pState->state() & vcl::WindowState::Minimized)
677 SetWindowStateImpl(Qt::WindowMinimized);
678 else
679 SetWindowStateImpl(Qt::WindowNoState);
683 bool QtFrame::GetWindowState(vcl::WindowData* pState)
685 pState->setState(vcl::WindowState::Normal);
686 pState->setMask(vcl::WindowDataMask::State);
687 if (isMinimized())
688 pState->rState() |= vcl::WindowState::Minimized;
689 else if (isMaximized())
690 pState->rState() |= vcl::WindowState::Maximized;
691 else
693 // we want the frame position and the client area size
694 QRect rect = scaledQRect({ asChild()->pos(), asChild()->size() }, devicePixelRatioF());
695 pState->setPosSize(toRectangle(rect));
696 pState->rMask() |= vcl::WindowDataMask::PosSize;
699 return true;
702 void QtFrame::ShowFullScreen(bool bFullScreen, sal_Int32 nScreen)
704 // only top-level windows can go fullscreen
705 assert(m_pTopLevel);
707 if (m_bFullScreen == bFullScreen)
708 return;
710 m_bFullScreen = bFullScreen;
711 m_bFullScreenSpanAll = m_bFullScreen && (nScreen < 0);
713 // show it if it isn't shown yet
714 if (!isWindow())
715 m_pTopLevel->show();
717 if (m_bFullScreen)
719 m_aRestoreGeometry = m_pTopLevel->geometry();
720 m_nRestoreScreen = std::max(sal_Int32(0), screenNumber());
721 SetScreenNumber(m_bFullScreenSpanAll ? m_nRestoreScreen : nScreen);
722 if (!m_bFullScreenSpanAll)
723 m_pTopLevel->showFullScreen();
724 else
725 m_pTopLevel->showNormal();
727 else
729 SetScreenNumber(m_nRestoreScreen);
730 m_pTopLevel->showNormal();
731 m_pTopLevel->setGeometry(m_aRestoreGeometry);
735 void QtFrame::StartPresentation(bool bStart)
737 #if !defined EMSCRIPTEN
738 assert(m_aSystemData.platform != SystemEnvData::Platform::Invalid);
740 #if CHECK_QT5_USING_X11
741 unsigned int nRootWindow(0);
742 std::optional<Display*> aDisplay;
743 if (QX11Info::isPlatformX11())
745 nRootWindow = QX11Info::appRootWindow();
746 aDisplay = QX11Info::display();
748 m_SessionManagerInhibitor.inhibit(bStart, u"presentation", APPLICATION_INHIBIT_IDLE,
749 nRootWindow, aDisplay);
750 #else
751 m_SessionManagerInhibitor.inhibit(bStart, u"presentation", APPLICATION_INHIBIT_IDLE);
752 #endif
753 #else
754 Q_UNUSED(bStart)
755 #endif
758 void QtFrame::SetAlwaysOnTop(bool bOnTop)
760 QWidget* const pWidget = asChild();
761 const Qt::WindowFlags flags = pWidget->windowFlags();
762 if (bOnTop)
763 pWidget->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
764 else
765 pWidget->setWindowFlags(flags & ~(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint));
768 void QtFrame::ToTop(SalFrameToTop nFlags)
770 GetQtInstance().RunInMainThread([this, nFlags]() {
771 QWidget* const pWidget = asChild();
772 if (isWindow() && !(nFlags & SalFrameToTop::GrabFocusOnly))
773 pWidget->raise();
774 if ((nFlags & SalFrameToTop::RestoreWhenMin) || (nFlags & SalFrameToTop::ForegroundTask))
776 if (nFlags & SalFrameToTop::RestoreWhenMin)
777 pWidget->setWindowState(pWidget->windowState() & ~Qt::WindowMinimized);
778 pWidget->activateWindow();
780 else if ((nFlags & SalFrameToTop::GrabFocus) || (nFlags & SalFrameToTop::GrabFocusOnly))
782 if (!(nFlags & SalFrameToTop::GrabFocusOnly))
783 pWidget->activateWindow();
784 pWidget->setFocus(Qt::OtherFocusReason);
789 void QtFrame::SetPointer(PointerStyle ePointerStyle)
791 if (ePointerStyle == m_ePointerStyle)
792 return;
793 m_ePointerStyle = ePointerStyle;
795 m_pQWidget->setCursor(GetQtData()->getCursor(ePointerStyle));
798 void QtFrame::CaptureMouse(bool bMouse)
800 static const char* pEnv = getenv("SAL_NO_MOUSEGRABS");
801 if (pEnv && *pEnv)
802 return;
804 if (bMouse)
805 m_pQWidget->grabMouse();
806 else
807 m_pQWidget->releaseMouse();
810 void QtFrame::SetPointerPos(tools::Long nX, tools::Long nY)
812 // some cursor already exists (and it has m_ePointerStyle shape)
813 // so here we just reposition it
814 QCursor::setPos(m_pQWidget->mapToGlobal(QPoint(nX, nY) / devicePixelRatioF()));
817 void QtFrame::Flush()
819 // was: QGuiApplication::sync();
820 // but FIXME it causes too many issues, figure out sth better
822 // unclear if we need to also flush cairo surface - gtk3 backend
823 // does not do it. QPainter in QtWidget::paintEvent() is
824 // destroyed, so that state should be safely flushed.
827 bool QtFrame::ShowTooltip(const OUString& rText, const tools::Rectangle& rHelpArea)
829 QRect aHelpArea(toQRect(rHelpArea));
830 if (QGuiApplication::isRightToLeft())
831 aHelpArea.moveLeft(GetWidth() - aHelpArea.width() - aHelpArea.left() - 1);
832 m_aTooltipText = rText;
833 m_aTooltipArea = aHelpArea;
834 return true;
837 void QtFrame::SetInputContext(SalInputContext* pContext)
839 if (!pContext)
840 return;
842 if (!(pContext->mnOptions & InputContextFlags::Text))
843 return;
845 m_pQWidget->setAttribute(Qt::WA_InputMethodEnabled);
848 void QtFrame::EndExtTextInput(EndExtTextInputFlags /*nFlags*/)
850 if (m_pQWidget)
851 m_pQWidget->endExtTextInput();
854 OUString QtFrame::GetKeyName(sal_uInt16 nKeyCode)
856 vcl::KeyCode vclKeyCode(nKeyCode);
857 int nCode = vclKeyCode.GetCode();
858 int nRetCode = 0;
860 if (nCode >= KEY_0 && nCode <= KEY_9)
861 nRetCode = (nCode - KEY_0) + Qt::Key_0;
862 else if (nCode >= KEY_A && nCode <= KEY_Z)
863 nRetCode = (nCode - KEY_A) + Qt::Key_A;
864 else if (nCode >= KEY_F1 && nCode <= KEY_F26)
865 nRetCode = (nCode - KEY_F1) + Qt::Key_F1;
866 else
868 switch (nCode)
870 case KEY_DOWN:
871 nRetCode = Qt::Key_Down;
872 break;
873 case KEY_UP:
874 nRetCode = Qt::Key_Up;
875 break;
876 case KEY_LEFT:
877 nRetCode = Qt::Key_Left;
878 break;
879 case KEY_RIGHT:
880 nRetCode = Qt::Key_Right;
881 break;
882 case KEY_HOME:
883 nRetCode = Qt::Key_Home;
884 break;
885 case KEY_END:
886 nRetCode = Qt::Key_End;
887 break;
888 case KEY_PAGEUP:
889 nRetCode = Qt::Key_PageUp;
890 break;
891 case KEY_PAGEDOWN:
892 nRetCode = Qt::Key_PageDown;
893 break;
894 case KEY_RETURN:
895 nRetCode = Qt::Key_Return;
896 break;
897 case KEY_ESCAPE:
898 nRetCode = Qt::Key_Escape;
899 break;
900 case KEY_TAB:
901 nRetCode = Qt::Key_Tab;
902 break;
903 case KEY_BACKSPACE:
904 nRetCode = Qt::Key_Backspace;
905 break;
906 case KEY_SPACE:
907 nRetCode = Qt::Key_Space;
908 break;
909 case KEY_INSERT:
910 nRetCode = Qt::Key_Insert;
911 break;
912 case KEY_DELETE:
913 nRetCode = Qt::Key_Delete;
914 break;
915 case KEY_ADD:
916 nRetCode = Qt::Key_Plus;
917 break;
918 case KEY_SUBTRACT:
919 nRetCode = Qt::Key_Minus;
920 break;
921 case KEY_MULTIPLY:
922 nRetCode = Qt::Key_Asterisk;
923 break;
924 case KEY_DIVIDE:
925 nRetCode = Qt::Key_Slash;
926 break;
927 case KEY_POINT:
928 nRetCode = Qt::Key_Period;
929 break;
930 case KEY_COMMA:
931 nRetCode = Qt::Key_Comma;
932 break;
933 case KEY_LESS:
934 nRetCode = Qt::Key_Less;
935 break;
936 case KEY_GREATER:
937 nRetCode = Qt::Key_Greater;
938 break;
939 case KEY_EQUAL:
940 nRetCode = Qt::Key_Equal;
941 break;
942 case KEY_FIND:
943 nRetCode = Qt::Key_Find;
944 break;
945 case KEY_CONTEXTMENU:
946 nRetCode = Qt::Key_Menu;
947 break;
948 case KEY_HELP:
949 nRetCode = Qt::Key_Help;
950 break;
951 case KEY_UNDO:
952 nRetCode = Qt::Key_Undo;
953 break;
954 case KEY_REPEAT:
955 nRetCode = Qt::Key_Redo;
956 break;
957 case KEY_TILDE:
958 nRetCode = Qt::Key_AsciiTilde;
959 break;
960 case KEY_QUOTELEFT:
961 nRetCode = Qt::Key_QuoteLeft;
962 break;
963 case KEY_BRACKETLEFT:
964 nRetCode = Qt::Key_BracketLeft;
965 break;
966 case KEY_BRACKETRIGHT:
967 nRetCode = Qt::Key_BracketRight;
968 break;
969 case KEY_NUMBERSIGN:
970 nRetCode = Qt::Key_NumberSign;
971 break;
972 case KEY_XF86FORWARD:
973 nRetCode = Qt::Key_Forward;
974 break;
975 case KEY_XF86BACK:
976 nRetCode = Qt::Key_Back;
977 break;
978 case KEY_COLON:
979 nRetCode = Qt::Key_Colon;
980 break;
981 case KEY_SEMICOLON:
982 nRetCode = Qt::Key_Semicolon;
983 break;
985 // Special cases
986 case KEY_COPY:
987 nRetCode = Qt::Key_Copy;
988 break;
989 case KEY_CUT:
990 nRetCode = Qt::Key_Cut;
991 break;
992 case KEY_PASTE:
993 nRetCode = Qt::Key_Paste;
994 break;
995 case KEY_OPEN:
996 nRetCode = Qt::Key_Open;
997 break;
1001 if (vclKeyCode.IsShift())
1002 nRetCode += Qt::SHIFT;
1003 if (vclKeyCode.IsMod1())
1004 nRetCode += Qt::CTRL;
1005 if (vclKeyCode.IsMod2())
1006 nRetCode += Qt::ALT;
1008 QKeySequence keySeq(nRetCode);
1009 OUString sKeyName = toOUString(keySeq.toString());
1011 return sKeyName;
1014 bool QtFrame::MapUnicodeToKeyCode(sal_Unicode /*aUnicode*/, LanguageType /*aLangType*/,
1015 vcl::KeyCode& /*rKeyCode*/)
1017 // not supported yet
1018 return false;
1021 LanguageType QtFrame::GetInputLanguage() { return m_nInputLanguage; }
1023 void QtFrame::setInputLanguage(LanguageType nInputLanguage)
1025 if (nInputLanguage == m_nInputLanguage)
1026 return;
1027 m_nInputLanguage = nInputLanguage;
1028 CallCallback(SalEvent::InputLanguageChange, nullptr);
1031 static bool toVclFont(const QFont& rQFont, const css::lang::Locale& rLocale, vcl::Font& rVclFont)
1033 FontAttributes aFA;
1034 QtFontFace::fillAttributesFromQFont(rQFont, aFA);
1036 bool bFound = psp::PrintFontManager::get().matchFont(aFA, rLocale);
1037 SAL_INFO("vcl.qt",
1038 "font match result for '"
1039 << rQFont.family() << "': "
1040 << (bFound ? OUString::Concat("'") + aFA.GetFamilyName() + "'" : u"failed"_ustr));
1042 if (!bFound)
1043 return false;
1045 QFontInfo qFontInfo(rQFont);
1046 int nPointHeight = qFontInfo.pointSize();
1047 if (nPointHeight <= 0)
1048 nPointHeight = rQFont.pointSize();
1050 vcl::Font aFont(aFA.GetFamilyName(), Size(0, nPointHeight));
1051 if (aFA.GetWeight() != WEIGHT_DONTKNOW)
1052 aFont.SetWeight(aFA.GetWeight());
1053 if (aFA.GetWidthType() != WIDTH_DONTKNOW)
1054 aFont.SetWidthType(aFA.GetWidthType());
1055 if (aFA.GetItalic() != ITALIC_DONTKNOW)
1056 aFont.SetItalic(aFA.GetItalic());
1057 if (aFA.GetPitch() != PITCH_DONTKNOW)
1058 aFont.SetPitch(aFA.GetPitch());
1060 rVclFont = aFont;
1061 return true;
1064 void QtFrame::UpdateSettings(AllSettings& rSettings)
1066 if (QtData::noNativeControls())
1067 return;
1068 QtCustomStyle::LoadCustomStyle(GetUseDarkMode());
1070 StyleSettings style(rSettings.GetStyleSettings());
1071 const css::lang::Locale aLocale = rSettings.GetUILanguageTag().getLocale();
1073 // General settings
1074 QPalette pal = QApplication::palette();
1076 style.SetToolbarIconSize(ToolbarIconSize::Large);
1078 Color aFore = toColor(pal.color(QPalette::Active, QPalette::WindowText));
1079 Color aBack = toColor(pal.color(QPalette::Active, QPalette::Window));
1080 Color aText = toColor(pal.color(QPalette::Active, QPalette::Text));
1081 Color aBase = toColor(pal.color(QPalette::Active, QPalette::Base));
1082 Color aButn = toColor(pal.color(QPalette::Active, QPalette::ButtonText));
1083 Color aMid = toColor(pal.color(QPalette::Active, QPalette::Mid));
1084 Color aHigh = toColor(pal.color(QPalette::Active, QPalette::Highlight));
1085 Color aHighText = toColor(pal.color(QPalette::Active, QPalette::HighlightedText));
1086 Color aLink = toColor(pal.color(QPalette::Active, QPalette::Link));
1087 Color aVisitedLink = toColor(pal.color(QPalette::Active, QPalette::LinkVisited));
1089 style.SetSkipDisabledInMenus(true);
1091 // Foreground
1092 style.SetRadioCheckTextColor(aFore);
1093 style.SetLabelTextColor(aFore);
1094 style.SetDialogTextColor(aFore);
1095 style.SetGroupTextColor(aFore);
1097 // Text
1098 style.SetFieldTextColor(aText);
1099 style.SetFieldRolloverTextColor(aText);
1100 style.SetListBoxWindowTextColor(aText);
1101 style.SetWindowTextColor(aText);
1102 style.SetToolTextColor(aText);
1104 // Base
1105 style.SetFieldColor(aBase);
1106 style.SetActiveTabColor(aBase);
1107 style.SetListBoxWindowBackgroundColor(aBase);
1108 style.SetAlternatingRowColor(toColor(pal.color(QPalette::Active, QPalette::AlternateBase)));
1110 // Buttons
1111 style.SetDefaultButtonTextColor(aButn);
1112 style.SetButtonTextColor(aButn);
1113 style.SetDefaultActionButtonTextColor(aButn);
1114 style.SetActionButtonTextColor(aButn);
1115 style.SetFlatButtonTextColor(aButn);
1116 style.SetDefaultButtonRolloverTextColor(aButn);
1117 style.SetButtonRolloverTextColor(aButn);
1118 style.SetDefaultActionButtonRolloverTextColor(aButn);
1119 style.SetActionButtonRolloverTextColor(aButn);
1120 style.SetFlatButtonRolloverTextColor(aButn);
1121 style.SetDefaultButtonPressedRolloverTextColor(aButn);
1122 style.SetButtonPressedRolloverTextColor(aButn);
1123 style.SetDefaultActionButtonPressedRolloverTextColor(aButn);
1124 style.SetActionButtonPressedRolloverTextColor(aButn);
1125 style.SetFlatButtonPressedRolloverTextColor(aButn);
1127 // Tabs
1128 style.SetTabTextColor(aButn);
1129 style.SetTabRolloverTextColor(aButn);
1130 style.SetTabHighlightTextColor(aButn);
1132 // Disable color
1133 style.SetDisableColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
1135 // Background
1136 style.BatchSetBackgrounds(aBack);
1137 style.SetInactiveTabColor(aBack);
1138 style.SetWindowColor(aBack);
1140 // Workspace
1141 style.SetWorkspaceColor(aMid);
1143 // Selection
1144 // https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/305
1145 style.SetAccentColor(aHigh);
1146 style.SetHighlightColor(aHigh);
1147 style.SetHighlightTextColor(aHighText);
1148 style.SetListBoxWindowHighlightColor(aHigh);
1149 style.SetListBoxWindowHighlightTextColor(aHighText);
1150 style.SetActiveColor(aHigh);
1151 style.SetActiveTextColor(aHighText);
1153 // Links
1154 style.SetLinkColor(aLink);
1155 style.SetVisitedLinkColor(aVisitedLink);
1157 // Tooltip
1158 style.SetHelpColor(toColor(QToolTip::palette().color(QPalette::Active, QPalette::ToolTipBase)));
1159 style.SetHelpTextColor(
1160 toColor(QToolTip::palette().color(QPalette::Active, QPalette::ToolTipText)));
1162 // Menu
1163 std::unique_ptr<QMenuBar> pMenuBar = std::make_unique<QMenuBar>();
1164 QPalette qMenuCG = pMenuBar->palette();
1166 // Menu text and background color, theme specific
1167 Color aMenuFore = toColor(qMenuCG.color(QPalette::WindowText));
1168 Color aMenuBack = toColor(qMenuCG.color(QPalette::Window));
1170 style.SetMenuTextColor(aMenuFore);
1171 style.SetMenuBarTextColor(aMenuFore);
1172 style.SetMenuColor(aMenuBack);
1173 style.SetMenuBarColor(aMenuBack);
1174 style.SetMenuHighlightColor(toColor(qMenuCG.color(QPalette::Highlight)));
1175 style.SetMenuHighlightTextColor(toColor(qMenuCG.color(QPalette::HighlightedText)));
1177 // set special menubar highlight text color
1178 if (QApplication::style()->inherits("HighContrastStyle"))
1179 ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor
1180 = toColor(qMenuCG.color(QPalette::HighlightedText));
1181 else
1182 ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor = aMenuFore;
1184 // set menubar rollover color
1185 if (pMenuBar->style()->styleHint(QStyle::SH_MenuBar_MouseTracking))
1187 style.SetMenuBarRolloverColor(toColor(qMenuCG.color(QPalette::Highlight)));
1188 style.SetMenuBarRolloverTextColor(ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor);
1190 else
1192 style.SetMenuBarRolloverColor(aMenuBack);
1193 style.SetMenuBarRolloverTextColor(aMenuFore);
1195 style.SetMenuBarHighlightTextColor(style.GetMenuHighlightTextColor());
1197 // Default fonts
1198 vcl::Font aFont;
1199 if (toVclFont(QApplication::font(), aLocale, aFont))
1201 style.BatchSetFonts(aFont, aFont);
1202 aFont.SetWeight(WEIGHT_BOLD);
1203 style.SetTitleFont(aFont);
1204 style.SetFloatTitleFont(aFont);
1207 // Tooltip font
1208 if (toVclFont(QToolTip::font(), aLocale, aFont))
1209 style.SetHelpFont(aFont);
1211 // Menu bar font
1212 if (toVclFont(pMenuBar->font(), aLocale, aFont))
1213 style.SetMenuFont(aFont);
1215 // Icon theme
1216 const bool bPreferDarkTheme = GetUseDarkMode();
1217 style.SetPreferredIconTheme(toOUString(QIcon::themeName()), bPreferDarkTheme);
1219 // Scroll bar size
1220 style.SetScrollBarSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent));
1221 style.SetMinThumbSize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarSliderMin));
1223 // These colors are used for the ruler text and marks
1224 style.SetShadowColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
1225 style.SetDarkShadowColor(toColor(pal.color(QPalette::Inactive, QPalette::WindowText)));
1227 // match native QComboBox behavior of putting text cursor to end of text
1228 // without text selection when combobox entry is selected
1229 style.SetComboBoxTextSelectionMode(ComboBoxTextSelectionMode::CursorToEnd);
1231 // Cursor blink interval
1232 int nFlashTime = QApplication::cursorFlashTime();
1233 style.SetCursorBlinkTime(nFlashTime != 0 ? nFlashTime / 2 : STYLE_CURSOR_NOBLINKTIME);
1235 rSettings.SetStyleSettings(style);
1238 void QtFrame::Beep() { QApplication::beep(); }
1240 SalFrame::SalPointerState QtFrame::GetPointerState()
1242 SalPointerState aState;
1243 aState.maPos = toPoint(QCursor::pos() * devicePixelRatioF());
1244 SalFrameGeometry aGeometry = GetUnmirroredGeometry();
1245 aState.maPos.Move(-aGeometry.x(), -aGeometry.y());
1246 aState.mnState = GetMouseModCode(QGuiApplication::mouseButtons())
1247 | GetKeyModCode(QGuiApplication::keyboardModifiers());
1248 return aState;
1251 KeyIndicatorState QtFrame::GetIndicatorState() { return KeyIndicatorState(); }
1253 void QtFrame::SimulateKeyPress(sal_uInt16 nKeyCode)
1255 SAL_WARN("vcl.qt", "missing simulate keypress " << nKeyCode);
1258 // don't set QWidget parents; this breaks popups on Wayland, like the LO ComboBox or ColorPicker!
1259 void QtFrame::SetParent(SalFrame* pNewParent) { m_pParent = static_cast<QtFrame*>(pNewParent); }
1261 void QtFrame::SetPluginParent(SystemParentData* /*pNewParent*/)
1263 //FIXME: no SetPluginParent impl. for qt5
1266 void QtFrame::ResetClipRegion() { m_bNullRegion = true; }
1268 void QtFrame::BeginSetClipRegion(sal_uInt32)
1270 m_aRegion = QRegion(QRect(QPoint(0, 0), m_pQWidget->size()));
1273 void QtFrame::UnionClipRegion(tools::Long nX, tools::Long nY, tools::Long nWidth,
1274 tools::Long nHeight)
1276 m_aRegion
1277 = m_aRegion.united(scaledQRect(QRect(nX, nY, nWidth, nHeight), 1 / devicePixelRatioF()));
1280 void QtFrame::EndSetClipRegion() { m_bNullRegion = false; }
1282 void QtFrame::SetScreenNumber(unsigned int nScreen)
1284 if (!isWindow())
1285 return;
1287 QWindow* const pWindow = windowHandle();
1288 if (!pWindow)
1289 return;
1291 QList<QScreen*> screens = QApplication::screens();
1292 if (static_cast<int>(nScreen) >= screens.size() && !m_bFullScreenSpanAll)
1294 SAL_WARN("vcl.qt", "Ignoring request to set invalid screen number");
1295 return;
1298 QRect screenGeo;
1300 if (!m_bFullScreenSpanAll)
1302 screenGeo = QGuiApplication::screens().at(nScreen)->geometry();
1303 pWindow->setScreen(QApplication::screens().at(nScreen));
1305 else // special case: fullscreen over all available screens
1307 assert(m_bFullScreen);
1308 // left-most screen
1309 QScreen* pScreen = QGuiApplication::screenAt(QPoint(0, 0));
1310 // entire virtual desktop
1311 screenGeo = pScreen->availableVirtualGeometry();
1312 pWindow->setScreen(pScreen);
1313 pWindow->setGeometry(screenGeo);
1316 // setScreen by itself has no effect, explicitly move the widget to
1317 // the new screen
1318 asChild()->move(screenGeo.topLeft());
1321 void QtFrame::SetApplicationID(const OUString& rWMClass)
1323 #if CHECK_QT5_USING_X11
1324 assert(m_aSystemData.platform != SystemEnvData::Platform::Invalid);
1325 if (m_aSystemData.platform != SystemEnvData::Platform::Xcb || !m_pTopLevel)
1326 return;
1328 QtX11Support::setApplicationID(m_pTopLevel->winId(), rWMClass);
1329 #else
1330 Q_UNUSED(rWMClass);
1331 #endif
1334 void QtFrame::ResolveWindowHandle(SystemEnvData& rData) const
1336 if (!rData.pWidget)
1337 return;
1338 assert(rData.platform != SystemEnvData::Platform::Invalid);
1339 // Calling QWidget::winId() implicitly enables native windows to be used instead
1340 // of "alien widgets" that don't have a native widget associated with them,
1341 // s. https://doc.qt.io/qt-6/qwidget.html#native-widgets-vs-alien-widgets
1342 // Avoid native widgets with Qt 5 on Wayland and with Qt 6 altogether as they
1343 // cause unresponsive UI, s. tdf#122293/QTBUG-75766 and tdf#160565
1344 // (for qt5 xcb, they're needed for video playback)
1345 if (rData.platform != SystemEnvData::Platform::Wayland
1346 && QLibraryInfo::version().majorVersion() < 6)
1348 rData.SetWindowHandle(static_cast<QWidget*>(rData.pWidget)->winId());
1352 bool QtFrame::GetUseReducedAnimation() const { return GetQtInstance().GetUseReducedAnimation(); }
1354 // Drag'n'drop foo
1356 void QtFrame::registerDragSource(QtDragSource* pDragSource)
1358 assert(!m_pDragSource);
1359 m_pDragSource = pDragSource;
1362 void QtFrame::deregisterDragSource(QtDragSource const* pDragSource)
1364 assert(m_pDragSource == pDragSource);
1365 (void)pDragSource;
1366 m_pDragSource = nullptr;
1369 void QtFrame::registerDropTarget(QtDropTarget* pDropTarget)
1371 assert(!m_pDropTarget);
1372 m_pDropTarget = pDropTarget;
1374 GetQtInstance().RunInMainThread([this]() { m_pQWidget->setAcceptDrops(true); });
1377 void QtFrame::deregisterDropTarget(QtDropTarget const* pDropTarget)
1379 assert(m_pDropTarget == pDropTarget);
1380 (void)pDropTarget;
1381 m_pDropTarget = nullptr;
1384 static css::uno::Reference<css::datatransfer::XTransferable>
1385 lcl_getXTransferable(const QMimeData* pMimeData)
1387 css::uno::Reference<css::datatransfer::XTransferable> xTransferable;
1388 const QtMimeData* pQtMimeData = qobject_cast<const QtMimeData*>(pMimeData);
1389 if (!pQtMimeData)
1390 xTransferable = new QtDnDTransferable(pMimeData);
1391 else
1392 xTransferable = pQtMimeData->xTransferable();
1393 return xTransferable;
1396 static sal_Int8 lcl_getUserDropAction(const QDropEvent* pEvent, const sal_Int8 nSourceActions,
1397 const QMimeData* pMimeData)
1399 // we completely ignore all proposals by the Qt event, as they don't
1400 // match at all with the preferred LO DnD actions.
1401 // check the key modifiers to detect a user-overridden DnD action
1402 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1403 const Qt::KeyboardModifiers eKeyMod = pEvent->modifiers();
1404 #else
1405 const Qt::KeyboardModifiers eKeyMod = pEvent->keyboardModifiers();
1406 #endif
1407 sal_Int8 nUserDropAction = 0;
1408 if ((eKeyMod & Qt::ShiftModifier) && !(eKeyMod & Qt::ControlModifier))
1409 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_MOVE;
1410 else if ((eKeyMod & Qt::ControlModifier) && !(eKeyMod & Qt::ShiftModifier))
1411 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1412 else if ((eKeyMod & Qt::ShiftModifier) && (eKeyMod & Qt::ControlModifier))
1413 nUserDropAction = css::datatransfer::dnd::DNDConstants::ACTION_LINK;
1414 nUserDropAction &= nSourceActions;
1416 // select the default DnD action, if there isn't a user preference
1417 if (0 == nUserDropAction)
1419 // default LO internal action is move, but default external action is copy
1420 nUserDropAction = qobject_cast<const QtMimeData*>(pMimeData)
1421 ? css::datatransfer::dnd::DNDConstants::ACTION_MOVE
1422 : css::datatransfer::dnd::DNDConstants::ACTION_COPY;
1423 nUserDropAction &= nSourceActions;
1425 // if the default doesn't match any allowed source action, fall back to the
1426 // preferred of all allowed source actions
1427 if (0 == nUserDropAction)
1428 nUserDropAction = toVclDropAction(getPreferredDropAction(nSourceActions));
1430 // this is "our" preference, but actually we would even prefer any default,
1431 // if there is any
1432 nUserDropAction |= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT;
1434 return nUserDropAction;
1437 void QtFrame::handleDragMove(QDragMoveEvent* pEvent)
1439 assert(m_pDropTarget);
1441 // prepare our suggested drop action for the drop target
1442 const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions());
1443 const QMimeData* pMimeData = pEvent->mimeData();
1444 const sal_Int8 nUserDropAction = lcl_getUserDropAction(pEvent, nSourceActions, pMimeData);
1446 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1447 const Point aPos = toPoint(pEvent->position().toPoint() * devicePixelRatioF());
1448 #else
1449 const Point aPos = toPoint(pEvent->pos() * devicePixelRatioF());
1450 #endif
1452 css::datatransfer::dnd::DropTargetDragEnterEvent aEvent;
1453 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1454 aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDragContext*>(m_pDropTarget);
1455 aEvent.LocationX = aPos.X();
1456 aEvent.LocationY = aPos.Y();
1457 aEvent.DropAction = nUserDropAction;
1458 aEvent.SourceActions = nSourceActions;
1460 // ask the drop target to accept our drop action
1461 if (!m_bInDrag)
1463 aEvent.SupportedDataFlavors = lcl_getXTransferable(pMimeData)->getTransferDataFlavors();
1464 m_pDropTarget->fire_dragEnter(aEvent);
1465 m_bInDrag = true;
1467 else
1468 m_pDropTarget->fire_dragOver(aEvent);
1470 // the drop target accepted our drop action => inform Qt
1471 if (m_pDropTarget->proposedDropAction() != 0)
1473 pEvent->setDropAction(getPreferredDropAction(m_pDropTarget->proposedDropAction()));
1474 pEvent->accept();
1476 else // or maybe someone else likes it?
1477 pEvent->ignore();
1480 void QtFrame::handleDrop(QDropEvent* pEvent)
1482 assert(m_pDropTarget);
1484 // prepare our suggested drop action for the drop target
1485 const sal_Int8 nSourceActions = toVclDropActions(pEvent->possibleActions());
1486 const sal_Int8 nUserDropAction
1487 = lcl_getUserDropAction(pEvent, nSourceActions, pEvent->mimeData());
1489 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1490 const Point aPos = toPoint(pEvent->position().toPoint() * devicePixelRatioF());
1491 #else
1492 const Point aPos = toPoint(pEvent->pos() * devicePixelRatioF());
1493 #endif
1495 css::datatransfer::dnd::DropTargetDropEvent aEvent;
1496 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1497 aEvent.Context = static_cast<css::datatransfer::dnd::XDropTargetDropContext*>(m_pDropTarget);
1498 aEvent.LocationX = aPos.X();
1499 aEvent.LocationY = aPos.Y();
1500 aEvent.SourceActions = nSourceActions;
1501 aEvent.DropAction = nUserDropAction;
1502 aEvent.Transferable = lcl_getXTransferable(pEvent->mimeData());
1504 // ask the drop target to accept our drop action
1505 m_pDropTarget->fire_drop(aEvent);
1506 m_bInDrag = false;
1508 const bool bDropSuccessful = m_pDropTarget->dropSuccessful();
1509 const sal_Int8 nDropAction = m_pDropTarget->proposedDropAction();
1511 // inform the drag source of the drag-origin frame of the drop result
1512 if (pEvent->source())
1514 QtWidget* pWidget = qobject_cast<QtWidget*>(pEvent->source());
1515 assert(pWidget); // AFAIK there shouldn't be any non-Qt5Widget as source in LO itself
1516 if (pWidget)
1517 pWidget->frame().m_pDragSource->fire_dragEnd(nDropAction, bDropSuccessful);
1520 // the drop target accepted our drop action => inform Qt
1521 if (bDropSuccessful)
1523 pEvent->setDropAction(getPreferredDropAction(nDropAction));
1524 pEvent->accept();
1526 else // or maybe someone else likes it?
1527 pEvent->ignore();
1530 void QtFrame::handleDragLeave()
1532 css::datatransfer::dnd::DropTargetEvent aEvent;
1533 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(m_pDropTarget);
1534 m_pDropTarget->fire_dragExit(aEvent);
1535 m_bInDrag = false;
1538 void QtFrame::handleMoveEvent(QMoveEvent*) { CallCallback(SalEvent::Move, nullptr); }
1540 void QtFrame::handlePaintEvent(QPaintEvent* pEvent, QWidget* pWidget)
1542 QPainter p(pWidget);
1543 if (!m_bNullRegion)
1544 p.setClipRegion(m_aRegion);
1546 QImage aImage;
1547 if (m_bUseCairo)
1549 cairo_surface_t* pSurface = m_pSurface.get();
1550 cairo_surface_flush(pSurface);
1552 aImage = QImage(cairo_image_surface_get_data(pSurface),
1553 cairo_image_surface_get_width(pSurface),
1554 cairo_image_surface_get_height(pSurface), Qt_DefaultFormat32);
1556 else
1557 aImage = *m_pQImage;
1559 const qreal fRatio = devicePixelRatioF();
1560 aImage.setDevicePixelRatio(fRatio);
1561 QRectF source(pEvent->rect().topLeft() * fRatio, pEvent->rect().size() * fRatio);
1562 p.drawImage(pEvent->rect(), aImage, source);
1565 void QtFrame::handleResizeEvent(QResizeEvent* pEvent)
1567 const qreal fRatio = devicePixelRatioF();
1568 const int nWidth = ceil(pEvent->size().width() * fRatio);
1569 const int nHeight = ceil(pEvent->size().height() * fRatio);
1571 if (m_bUseCairo)
1573 if (m_pSurface)
1575 const int nOldWidth = cairo_image_surface_get_width(m_pSurface.get());
1576 const int nOldHeight = cairo_image_surface_get_height(m_pSurface.get());
1577 if (nOldWidth != nWidth || nOldHeight != nHeight)
1579 cairo_surface_t* pSurface
1580 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, nWidth, nHeight);
1581 cairo_surface_set_user_data(pSurface, SvpSalGraphics::getDamageKey(),
1582 &m_aDamageHandler, nullptr);
1583 m_pSvpGraphics->setSurface(pSurface, basegfx::B2IVector(nWidth, nHeight));
1584 UniqueCairoSurface old_surface(m_pSurface.release());
1585 m_pSurface.reset(pSurface);
1587 const int nMinWidth = qMin(nOldWidth, nWidth);
1588 const int nMinHeight = qMin(nOldHeight, nHeight);
1589 SalTwoRect rect(0, 0, nMinWidth, nMinHeight, 0, 0, nMinWidth, nMinHeight);
1590 m_pSvpGraphics->copySource(rect, old_surface.get());
1594 else
1596 if (m_pQImage && m_pQImage->size() != QSize(nWidth, nHeight))
1598 QImage* pImage = new QImage(m_pQImage->copy(0, 0, nWidth, nHeight));
1599 m_pQtGraphics->ChangeQImage(pImage);
1600 m_pQImage.reset(pImage);
1604 CallCallback(SalEvent::Resize, nullptr);
1607 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */