1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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>
26 #include <QtDragAndDrop.hxx>
27 #include <QtFontFace.hxx>
28 #include <QtGraphics.hxx>
29 #include <QtInstance.hxx>
30 #include <QtMainWindow.hxx>
32 #include <QtSvpGraphics.hxx>
33 #include <QtSystem.hxx>
34 #include <QtTransferable.hxx>
35 #if CHECK_ANY_QT_USING_X11
36 #include <QtX11Support.hxx>
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>
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>
62 #include <vcl/qt/QtUtils.hxx>
63 #include <vcl/syswin.hxx>
65 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
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
)
83 , m_bGraphicsInUse(false)
84 , m_ePointerStyle(PointerStyle::Arrow
)
85 , m_pDragSource(nullptr)
86 , m_pDropTarget(nullptr)
88 , m_bDefaultSize(true)
90 , m_bFullScreen(false)
91 , m_bFullScreenSpanAll(false)
92 #if CHECK_ANY_QT_USING_X11
93 , m_nKeyModifiers(ModKeyFlags::NONE
)
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
;
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.
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
;
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
);
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...
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
;
177 // maybe add a SystemEnvData::Platform::Unsupported to avoid special cases and not abort?
179 "Unsupported qt VCL platform: " << toOUString(QGuiApplication::platformName()));
183 rData
.toolkit
= SystemEnvData::Toolkit::Qt
;
184 rData
.aShellWindow
= pWindow
;
185 rData
.pWidget
= pWidget
;
190 GetQtInstance().eraseFrame(this);
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
)
207 m_bGraphicsInUse
= true;
213 QSize aSize
= m_pQWidget
->size() * devicePixelRatioF();
214 m_pSvpGraphics
.reset(new QtSvpGraphics(this));
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();
228 m_pQtGraphics
.reset(new QtGraphics(this));
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
)
242 assert(pSalGraph
== m_pSvpGraphics
.get());
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
);
254 QWidget
* QtFrame::asChild() const
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
);
278 case SystemEnvData::Platform::Wayland
:
279 case SystemEnvData::Platform::Xcb
:
280 pChild
->setAttribute(Qt::WA_NativeWindow
);
282 case SystemEnvData::Platform::Invalid
:
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
)
304 if (eColorScheme
== Qt::ColorScheme::Light
)
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
); });
340 & (SalFrameStyleFlags::PLUG
| SalFrameStyleFlags::SYSTEMCHILD
341 | SalFrameStyleFlags::FLOAT
| SalFrameStyleFlags::INTRO
342 | SalFrameStyleFlags::OWNERDRAWDECORATION
)
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";
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
)
388 QtInstance
& rQtInstance
= GetQtInstance();
389 if (!rQtInstance
.IsMainThread())
391 rQtInstance
.RunInMainThread([&] { Show(bVisible
, bNoActivate
); });
396 if (bVisible
== asChild()->isVisible())
399 if (!bVisible
) // hide
401 asChild()->setVisible(false);
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
);
419 QWidget
* const pChild
= asChild();
420 pChild
->setVisible(true);
424 pChild
->activateWindow();
429 void QtFrame::SetMinClientSize(tools::Long nWidth
, tools::Long nHeight
)
433 const qreal fRatio
= devicePixelRatioF();
434 asChild()->setMinimumSize(round(nWidth
/ fRatio
), round(nHeight
/ fRatio
));
438 void QtFrame::SetMaxClientSize(tools::Long nWidth
, tools::Long nHeight
)
442 const qreal fRatio
= devicePixelRatioF();
443 asChild()->setMaximumSize(round(nWidth
/ fRatio
), round(nHeight
/ fRatio
));
447 void QtFrame::SetDefaultPos()
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
);
463 m_bDefaultPos
= false;
466 Size
QtFrame::CalcDefaultSize()
473 const QScreen
* pScreen
= screen();
475 pScreen
= QGuiApplication::screens().at(0);
476 aSize
= bestmaxFrameSizeForScreenSize(toSize(pScreen
->size()));
480 if (!m_bFullScreenSpanAll
)
482 aSize
= toSize(screen()->size());
486 QScreen
* pScreen
= QGuiApplication::screenAt(QPoint(0, 0));
487 aSize
= toSize(pScreen
->availableVirtualGeometry().size());
494 void QtFrame::SetDefaultSize()
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
,
509 QtInstance
& rQtInstance
= GetQtInstance();
510 if (!rQtInstance
.IsMainThread())
512 rQtInstance
.RunInMainThread([&] { SetPosSize(nX
, nY
, nWidth
, nHeight
, nFlags
); });
515 if (!isWindow() || isChild(true, false))
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
))
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
);
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
))
549 const SalFrameGeometry aParentGeometry
= m_pParent
->GetUnmirroredGeometry();
550 if (QGuiApplication::isRightToLeft())
551 nX
= aParentGeometry
.x() + aParentGeometry
.width() - nX
- GetWidth() - 1;
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()));
588 void QtFrame::GetWorkArea(AbsoluteScreenPixelRectangle
& rRect
)
592 QScreen
* pScreen
= screen();
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
)
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
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
);
632 void QtFrame::SetWindowState(const vcl::WindowData
* pState
)
634 QtInstance
& rQtInstance
= GetQtInstance();
635 if (!rQtInstance
.IsMainThread())
637 rQtInstance
.RunInMainThread([this, pState
]() { SetWindowState(pState
); });
641 if (!isWindow() || !pState
|| isChild(true, false))
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
);
679 SetWindowStateImpl(Qt::WindowNoState
);
683 bool QtFrame::GetWindowState(vcl::WindowData
* pState
)
685 pState
->setState(vcl::WindowState::Normal
);
686 pState
->setMask(vcl::WindowDataMask::State
);
688 pState
->rState() |= vcl::WindowState::Minimized
;
689 else if (isMaximized())
690 pState
->rState() |= vcl::WindowState::Maximized
;
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
;
702 void QtFrame::ShowFullScreen(bool bFullScreen
, sal_Int32 nScreen
)
704 // only top-level windows can go fullscreen
707 if (m_bFullScreen
== bFullScreen
)
710 m_bFullScreen
= bFullScreen
;
711 m_bFullScreenSpanAll
= m_bFullScreen
&& (nScreen
< 0);
713 // show it if it isn't shown yet
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();
725 m_pTopLevel
->showNormal();
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
);
751 m_SessionManagerInhibitor
.inhibit(bStart
, u
"presentation", APPLICATION_INHIBIT_IDLE
);
758 void QtFrame::SetAlwaysOnTop(bool bOnTop
)
760 QWidget
* const pWidget
= asChild();
761 const Qt::WindowFlags flags
= pWidget
->windowFlags();
763 pWidget
->setWindowFlags(flags
| Qt::CustomizeWindowHint
| Qt::WindowStaysOnTopHint
);
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
))
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
)
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");
805 m_pQWidget
->grabMouse();
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
;
837 void QtFrame::SetInputContext(SalInputContext
* pContext
)
842 if (!(pContext
->mnOptions
& InputContextFlags::Text
))
845 m_pQWidget
->setAttribute(Qt::WA_InputMethodEnabled
);
848 void QtFrame::EndExtTextInput(EndExtTextInputFlags
/*nFlags*/)
851 m_pQWidget
->endExtTextInput();
854 OUString
QtFrame::GetKeyName(sal_uInt16 nKeyCode
)
856 vcl::KeyCode
vclKeyCode(nKeyCode
);
857 int nCode
= vclKeyCode
.GetCode();
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
;
871 nRetCode
= Qt::Key_Down
;
874 nRetCode
= Qt::Key_Up
;
877 nRetCode
= Qt::Key_Left
;
880 nRetCode
= Qt::Key_Right
;
883 nRetCode
= Qt::Key_Home
;
886 nRetCode
= Qt::Key_End
;
889 nRetCode
= Qt::Key_PageUp
;
892 nRetCode
= Qt::Key_PageDown
;
895 nRetCode
= Qt::Key_Return
;
898 nRetCode
= Qt::Key_Escape
;
901 nRetCode
= Qt::Key_Tab
;
904 nRetCode
= Qt::Key_Backspace
;
907 nRetCode
= Qt::Key_Space
;
910 nRetCode
= Qt::Key_Insert
;
913 nRetCode
= Qt::Key_Delete
;
916 nRetCode
= Qt::Key_Plus
;
919 nRetCode
= Qt::Key_Minus
;
922 nRetCode
= Qt::Key_Asterisk
;
925 nRetCode
= Qt::Key_Slash
;
928 nRetCode
= Qt::Key_Period
;
931 nRetCode
= Qt::Key_Comma
;
934 nRetCode
= Qt::Key_Less
;
937 nRetCode
= Qt::Key_Greater
;
940 nRetCode
= Qt::Key_Equal
;
943 nRetCode
= Qt::Key_Find
;
945 case KEY_CONTEXTMENU
:
946 nRetCode
= Qt::Key_Menu
;
949 nRetCode
= Qt::Key_Help
;
952 nRetCode
= Qt::Key_Undo
;
955 nRetCode
= Qt::Key_Redo
;
958 nRetCode
= Qt::Key_AsciiTilde
;
961 nRetCode
= Qt::Key_QuoteLeft
;
963 case KEY_BRACKETLEFT
:
964 nRetCode
= Qt::Key_BracketLeft
;
966 case KEY_BRACKETRIGHT
:
967 nRetCode
= Qt::Key_BracketRight
;
970 nRetCode
= Qt::Key_NumberSign
;
972 case KEY_XF86FORWARD
:
973 nRetCode
= Qt::Key_Forward
;
976 nRetCode
= Qt::Key_Back
;
979 nRetCode
= Qt::Key_Colon
;
982 nRetCode
= Qt::Key_Semicolon
;
987 nRetCode
= Qt::Key_Copy
;
990 nRetCode
= Qt::Key_Cut
;
993 nRetCode
= Qt::Key_Paste
;
996 nRetCode
= Qt::Key_Open
;
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());
1014 bool QtFrame::MapUnicodeToKeyCode(sal_Unicode
/*aUnicode*/, LanguageType
/*aLangType*/,
1015 vcl::KeyCode
& /*rKeyCode*/)
1017 // not supported yet
1021 LanguageType
QtFrame::GetInputLanguage() { return m_nInputLanguage
; }
1023 void QtFrame::setInputLanguage(LanguageType nInputLanguage
)
1025 if (nInputLanguage
== m_nInputLanguage
)
1027 m_nInputLanguage
= nInputLanguage
;
1028 CallCallback(SalEvent::InputLanguageChange
, nullptr);
1031 static bool toVclFont(const QFont
& rQFont
, const css::lang::Locale
& rLocale
, vcl::Font
& rVclFont
)
1034 QtFontFace::fillAttributesFromQFont(rQFont
, aFA
);
1036 bool bFound
= psp::PrintFontManager::get().matchFont(aFA
, rLocale
);
1038 "font match result for '"
1039 << rQFont
.family() << "': "
1040 << (bFound
? OUString::Concat("'") + aFA
.GetFamilyName() + "'" : u
"failed"_ustr
));
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());
1064 void QtFrame::UpdateSettings(AllSettings
& rSettings
)
1066 if (QtData::noNativeControls())
1068 QtCustomStyle::LoadCustomStyle(GetUseDarkMode());
1070 StyleSettings
style(rSettings
.GetStyleSettings());
1071 const css::lang::Locale aLocale
= rSettings
.GetUILanguageTag().getLocale();
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);
1092 style
.SetRadioCheckTextColor(aFore
);
1093 style
.SetLabelTextColor(aFore
);
1094 style
.SetDialogTextColor(aFore
);
1095 style
.SetGroupTextColor(aFore
);
1098 style
.SetFieldTextColor(aText
);
1099 style
.SetFieldRolloverTextColor(aText
);
1100 style
.SetListBoxWindowTextColor(aText
);
1101 style
.SetWindowTextColor(aText
);
1102 style
.SetToolTextColor(aText
);
1105 style
.SetFieldColor(aBase
);
1106 style
.SetActiveTabColor(aBase
);
1107 style
.SetListBoxWindowBackgroundColor(aBase
);
1108 style
.SetAlternatingRowColor(toColor(pal
.color(QPalette::Active
, QPalette::AlternateBase
)));
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
);
1128 style
.SetTabTextColor(aButn
);
1129 style
.SetTabRolloverTextColor(aButn
);
1130 style
.SetTabHighlightTextColor(aButn
);
1133 style
.SetDisableColor(toColor(pal
.color(QPalette::Disabled
, QPalette::WindowText
)));
1136 style
.BatchSetBackgrounds(aBack
);
1137 style
.SetInactiveTabColor(aBack
);
1138 style
.SetWindowColor(aBack
);
1141 style
.SetWorkspaceColor(aMid
);
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
);
1154 style
.SetLinkColor(aLink
);
1155 style
.SetVisitedLinkColor(aVisitedLink
);
1158 style
.SetHelpColor(toColor(QToolTip::palette().color(QPalette::Active
, QPalette::ToolTipBase
)));
1159 style
.SetHelpTextColor(
1160 toColor(QToolTip::palette().color(QPalette::Active
, QPalette::ToolTipText
)));
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
));
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
);
1192 style
.SetMenuBarRolloverColor(aMenuBack
);
1193 style
.SetMenuBarRolloverTextColor(aMenuFore
);
1195 style
.SetMenuBarHighlightTextColor(style
.GetMenuHighlightTextColor());
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
);
1208 if (toVclFont(QToolTip::font(), aLocale
, aFont
))
1209 style
.SetHelpFont(aFont
);
1212 if (toVclFont(pMenuBar
->font(), aLocale
, aFont
))
1213 style
.SetMenuFont(aFont
);
1216 const bool bPreferDarkTheme
= GetUseDarkMode();
1217 style
.SetPreferredIconTheme(toOUString(QIcon::themeName()), bPreferDarkTheme
);
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());
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
)
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
)
1287 QWindow
* const pWindow
= windowHandle();
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");
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
);
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
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
)
1328 QtX11Support::setApplicationID(m_pTopLevel
->winId(), rWMClass
);
1334 void QtFrame::ResolveWindowHandle(SystemEnvData
& rData
) const
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(); }
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
);
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
);
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
);
1390 xTransferable
= new QtDnDTransferable(pMimeData
);
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();
1405 const Qt::KeyboardModifiers eKeyMod
= pEvent
->keyboardModifiers();
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,
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());
1449 const Point aPos
= toPoint(pEvent
->pos() * devicePixelRatioF());
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
1463 aEvent
.SupportedDataFlavors
= lcl_getXTransferable(pMimeData
)->getTransferDataFlavors();
1464 m_pDropTarget
->fire_dragEnter(aEvent
);
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()));
1476 else // or maybe someone else likes it?
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());
1492 const Point aPos
= toPoint(pEvent
->pos() * devicePixelRatioF());
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
);
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
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
));
1526 else // or maybe someone else likes it?
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
);
1538 void QtFrame::handleMoveEvent(QMoveEvent
*) { CallCallback(SalEvent::Move
, nullptr); }
1540 void QtFrame::handlePaintEvent(QPaintEvent
* pEvent
, QWidget
* pWidget
)
1542 QPainter
p(pWidget
);
1544 p
.setClipRegion(m_aRegion
);
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
);
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
);
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());
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: */