bf4ec3b2be
Always run `QtGraphics_Controls::getNativeControlRegion` in the main thread, as it may create a dummy `QLineEdit` (see the `ControlType::MultilineEditbox` and `ControlType::Editbox` cases) and creating Qt widgets is only allowed in the main thread. Without the following scenario runs into an assert with a current Qt 6 dev debug build when using the qt6 VCL plugin: 1) open sample document attachment 193089 from tdf#160176 2) enable Form Design Toolbar 3) switch to Design mode 4) select label next to "Labelfield2" label 5) right-click, "Control Properties" 6) type anything for the help text and tab to the next UI element Backtrace: ASSERT failure in QWidget: "Widgets must be created in the GUI thread.", file /home/michi/development/git/qt5/qtbase/src/widgets/kernel/qwidget.cpp, line 956 Thread 45 "browserlistbox" received signal SIGABRT, Aborted. [Switching to Thread 0x7fff8d08d6c0 (LWP 203558)] __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44 44 ./nptl/pthread_kill.c: No such file or directory. (gdb) bt #0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44 #1 0x00007ffff78a81cf in __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78 #2 0x00007ffff785a472 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26 #3 0x00007ffff78444b2 in __GI_abort () at ./stdlib/abort.c:79 #4 0x00007fffe32f8540 in qAbort() () at /home/michi/development/git/qt5/qtbase/src/corelib/global/qassert.cpp:49 #5 0x00007fffe330c393 in qt_message_fatal<QString&>(QtMsgType, QMessageLogContext const&, QString&) (context=..., message=...) at /home/michi/development/git/qt5/qtbase/src/corelib/global/qlogging.cpp:2056 #6 0x00007fffe3305344 in qt_message(QtMsgType, const QMessageLogContext &, const char *, typedef __va_list_tag __va_list_tag *) (msgType=QtFatalMsg, context=..., msg=0x7fffe3981990 "ASSERT failure in %s: \"%s\", file %s, line %d", ap=0x7fff8d086178) at /home/michi/development/git/qt5/qtbase/src/corelib/global/qlogging.cpp:374 #7 0x00007fffe330740f in QMessageLogger::fatal(char const*, ...) const (this=0x7fff8d0863c0, msg=0x7fffe3981990 "ASSERT failure in %s: \"%s\", file %s, line %d") at /home/michi/development/git/qt5/qtbase/src/corelib/global/qlogging.cpp:874 #8 0x00007fffe32f8613 in qt_assert_x(char const*, char const*, char const*, int) (where=0x7fffe1becdf3 "QWidget", what=0x7fffe1becdc8 "Widgets must be created in the GUI thread.", file=0x7fffe1becca0 "/home/michi/development/git/qt5/qtbase/src/widgets/kernel/qwidget.cpp", line=956) at /home/michi/development/git/qt5/qtbase/src/corelib/global/qassert.cpp:114 #9 0x00007fffe144118c in QWidgetPrivate::init(QWidget*, QFlags<Qt::WindowType>) (this=0x7fff780054d0, parentWidget=0x0, f=...) at /home/michi/development/git/qt5/qtbase/src/widgets/kernel/qwidget.cpp:956 #10 0x00007fffe1440ca1 in QWidget::QWidget(QWidgetPrivate&, QWidget*, QFlags<Qt::WindowType>) (this=0x7fff8d086d00, dd=..., parent=0x0, f=...) at /home/michi/development/git/qt5/qtbase/src/widgets/kernel/qwidget.cpp:872 #11 0x00007fffe16a7d22 in QLineEdit::QLineEdit(QString const&, QWidget*) (this=0x7fff8d086d00, contents=..., parent=0x0) at /home/michi/development/git/qt5/qtbase/src/widgets/widgets/qlineedit.cpp:253 #12 0x00007fffe16a7c98 in QLineEdit::QLineEdit(QWidget*) (this=0x7fff8d086d00, parent=0x0) at /home/michi/development/git/qt5/qtbase/src/widgets/widgets/qlineedit.cpp:239 #13 0x00007fffe404aebc in QtGraphics_Controls::getNativeControlRegion(ControlType, ControlPart, tools::Rectangle const&, ControlState, ImplControlValue const&, rtl::OUString const&, tools::Rectangle&, tools::Rectangle&) (this=0x55555cbb13e0, type=ControlType::Editbox, part=ControlPart::Entire, controlRegion=..., controlState=(ControlState::ENABLED | ControlState::ROLLOVER), val=..., nativeBoundingRegion=..., nativeContentRegion=...) at vcl/qt6/../qt5/QtGraphics_Controls.cxx:784 #14 0x00007fffee72945e in SalGraphics::GetNativeControlRegion(ControlType, ControlPart, tools::Rectangle const&, ControlState, ImplControlValue const&, tools::Rectangle&, tools::Rectangle&, OutputDevice const&) (this=0x55555eb28920, nType=ControlType::Editbox, nPart=ControlPart::Entire, rControlRegion=..., nState=(ControlState::ENABLED | ControlState::ROLLOVER), aValue=..., rNativeBoundingRegion=..., rNativeContentRegion=..., rOutDev=...) at /home/michi/development/git/libreoffice/vcl/source/gdi/salgdilayout.cxx:834 #15 0x00007fffee3a7195 in OutputDevice::GetNativeControlRegion(ControlType, ControlPart, tools::Rectangle const&, ControlState, ImplControlValue const&, tools::Rectangle&, tools::Rectangle&) const (this=0x5555607cd870, nType=ControlType::Editbox, nPart=ControlPart::Entire, rControlRegion=..., nState=(ControlState::ENABLED | ControlState::ROLLOVER), aValue=..., rNativeBoundingRegion=..., rNativeContentRegion=...) at /home/michi/development/git/libreoffice/vcl/source/outdev/nativecontrols.cxx:313 #16 0x00007fffede63b9d in ImplSmallBorderWindowView::DrawWindow(OutputDevice&, Point const*) (this=0x5555607c7c10, rRenderContext=...) at /home/michi/development/git/libreoffice/vcl/source/window/brdwin.cxx:699 #17 0x00007fffede69900 in ImplBorderWindow::Paint(OutputDevice&, tools::Rectangle const&) (this=0x5555607c7ca0, rRenderContext=...) at /home/michi/development/git/libreoffice/vcl/source/window/brdwin.cxx:1628 #18 0x00007fffede3e241 in PaintHelper::DoPaint(vcl::Region const*) (this=0x7fff8d088188, pRegion=0x7fff78006480) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:313 #19 0x00007fffede3feff in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x5555607c7ca0, pRegion=0x7fff78006480, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:617 #20 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d0883c8) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #21 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55556002ead0, pRegion=0x7fff78005ec0, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #22 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d088608) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #23 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555edac0e0, pRegion=0x7fff78005dc0, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #24 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d088848) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #25 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555edab480, pRegion=0x7fff78005d80, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #26 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d088a88) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #27 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555edaa900, pRegion=0x7fff78004c40, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #28 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d088cc8) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #29 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555ed29400, pRegion=0x7fff78004c00, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #30 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d088f08) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #31 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555eccc910, pRegion=0x7fff78005d40, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #32 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d089148) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #33 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555eda7a70, pRegion=0x7fff78004ac0, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 --Type <RET> for more, q to quit, c to continue without paging-- #34 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d089388) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #35 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555eda2210, pRegion=0x7fff78001230, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #36 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d0895c8) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #37 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555ecd5d20, pRegion=0x7fff78002510, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #38 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d089808) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #39 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555ecdae90, pRegion=0x7fff78001ae0, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #40 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d089a48) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #41 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555bb5ddb0, pRegion=0x55555eb4e340, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #42 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d089c88) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #43 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555ed5c6e0, pRegion=0x7fff78001310, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #44 0x00007fffede3f791 in PaintHelper::~PaintHelper() (this=0x7fff8d089ec8) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:552 #45 0x00007fffede3ff86 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) (this=0x55555eb3aa10, pRegion=0x0, nPaintFlags=(ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren | ImplPaintFlags::Erase)) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:623 #46 0x00007fffede41a32 in vcl::Window::PaintImmediately() (this=0x5555607c7ca0) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:1340 #47 0x00007fffede415a0 in vcl::Window::PaintImmediately() (this=0x5555607c49b0) at /home/michi/development/git/libreoffice/vcl/source/window/paint.cxx:1280 #48 0x00007fffee17f7ac in Edit::ImplInvalidateOrRepaint() (this=0x5555607c49b0) at /home/michi/development/git/libreoffice/vcl/source/control/edit.cxx:451 #49 0x00007fffee182a52 in Edit::ImplAlignAndPaint() (this=0x5555607c49b0) at /home/michi/development/git/libreoffice/vcl/source/control/edit.cxx:1170 #50 0x00007fffee183ef0 in Edit::ImplInsertText(rtl::OUString const&, Selection const*, bool) (this=0x5555607c49b0, rStr="ee", pNewSel=0x7fff8d08a6b8, bIsUserInput=false) at /home/michi/development/git/libreoffice/vcl/source/control/edit.cxx:885 #51 0x00007fffee1841ca in Edit::ImplSetText(rtl::OUString const&, Selection const*) (this=0x5555607c49b0, rText="ee", pNewSelection=0x7fff8d08a6b8) at /home/michi/development/git/libreoffice/vcl/source/control/edit.cxx:918 #52 0x00007fffee18cfd4 in Edit::SetText(rtl::OUString const&) (this=0x5555607c49b0, rStr="ee") at /home/michi/development/git/libreoffice/vcl/source/control/edit.cxx:2560 #53 0x00007fffee8d974a in SalInstanceEntry::set_text(rtl::OUString const&) (this=0x5555607c4200, rText="ee") at /home/michi/development/git/libreoffice/vcl/source/app/salvtables.cxx:3383 #54 0x00007fff7ef6054a in pcr::OEditControl::setValue(com::sun:⭐:uno::Any const&) (this=0x5555607c7660, _rValue=uno::Any("string": "ee")) at /home/michi/development/git/libreoffice/extensions/source/propctrlr/standardcontrol.cxx:200 #55 0x00007fff7ed96819 in pcr::OBrowserListBox::impl_setControlAsPropertyValue(pcr::ListBoxLine const&, com::sun:⭐:uno::Any const&) (_rLine=..., _rPropertyValue=uno::Any("string": "ee")) at /home/michi/development/git/libreoffice/extensions/source/propctrlr/browserlistbox.cxx:551 #56 0x00007fff7ed96485 in pcr::OBrowserListBox::SetPropertyValue(rtl::OUString const&, com::sun:⭐:uno::Any const&, bool) (this=0x55555eda22b0, _rEntryName="HelpText", _rValue=uno::Any("string": "ee"), _bUnknownValue=false) at /home/michi/development/git/libreoffice/extensions/source/propctrlr/browserlistbox.cxx:403 #57 0x00007fff7ef432e8 in pcr::OPropertyEditor::SetPropertyValue(rtl::OUString const&, com::sun:⭐:uno::Any const&, bool) (this=0x55555ecdb3e0, rEntryName="HelpText", _rValue=uno::Any("string": "ee"), _bUnknownValue=false) at /home/michi/development/git/libreoffice/extensions/source/propctrlr/propertyeditor.cxx:277 #58 0x00007fff7ef01d4f in pcr::OPropertyBrowserController::Commit(rtl::OUString const&, com::sun:⭐:uno::Any const&) (this=0x55555c5f1b40, rName="HelpText", _rValue=uno::Any("string": "ee")) at /home/michi/development/git/libreoffice/extensions/source/propctrlr/propcontroller.cxx:1337 #59 0x00007fff7ed954ae in pcr::OBrowserListBox::valueChanged(com::sun:⭐:uno::Reference<com::sun:⭐:inspection::XPropertyControl> const&) (this=0x55555eda22b0, _rxControl=uno::Reference to (pcr::OEditControl *) 0x5555607c76b0) at /home/michi/development/git/libreoffice/extensions/source/propctrlr/browserlistbox.cxx:637 #60 0x00007fff7ed95268 in pcr::PropertyControlContext_Impl::impl_processEvent_throw(comphelper::AnyEvent const&) (this=0x55555ed29270, _rEvent=...) at /home/michi/development/git/libreoffice/extensions/source/propctrlr/browserlistbox.cxx:291 #61 0x00007fff7ed950b9 in pcr::PropertyControlContext_Impl::processEvent(comphelper::AnyEvent const&) (this=0x55555ed29270, _rEvent=...) at /home/michi/development/git/libreoffice/extensions/source/propctrlr/browserlistbox.cxx:272 #62 0x00007ffff6118a2e in comphelper::AsyncEventNotifierBase::execute() (this=0x5555568d6620) at /home/michi/development/git/libreoffice/comphelper/source/misc/asyncnotification.cxx:139 #63 0x00007ffff6119165 in comphelper::AsyncEventNotifier::execute() (this=0x5555568d6620) at /home/michi/development/git/libreoffice/comphelper/source/misc/asyncnotification.cxx:156 #64 0x00007ffff7d0f6ec in salhelper::Thread::run() (this=0x5555568d6630) at /home/michi/development/git/libreoffice/salhelper/source/thread.cxx:39 #65 0x00007ffff7d0ff2e in threadFunc(void*) (param=0x5555568d6640) at include/osl/thread.hxx:189 #66 0x00007ffff7fa4b2d in osl_thread_start_Impl(void*) (pData=0x55555ecc3e40) at /home/michi/development/git/libreoffice/sal/osl/unx/thread.cxx:237 #67 0x00007ffff78a645c in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:444 #68 0x00007ffff7926bbc in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 Change-Id: I9e0b0d973997e3f4f50a9a31e9692325664fd262 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165995 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
1179 lines
50 KiB
C++
1179 lines
50 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <QtGraphics_Controls.hxx>
|
|
|
|
#include <QtGui/QPainter>
|
|
#include <QtWidgets/QApplication>
|
|
#include <QtWidgets/QFrame>
|
|
#include <QtWidgets/QLabel>
|
|
#include <QtWidgets/QLineEdit>
|
|
|
|
#include <QtInstance.hxx>
|
|
#include <QtTools.hxx>
|
|
#include <QtGraphicsBase.hxx>
|
|
#include <vcl/decoview.hxx>
|
|
|
|
/**
|
|
Conversion function between VCL ControlState together with
|
|
ImplControlValue and Qt state flags.
|
|
@param nControlState State of the widget (default, focused, ...) in Native Widget Framework.
|
|
@param aValue Value held by the widget (on, off, ...)
|
|
*/
|
|
static QStyle::State vclStateValue2StateFlag(ControlState nControlState,
|
|
const ImplControlValue& aValue)
|
|
{
|
|
QStyle::State nState
|
|
= ((nControlState & ControlState::ENABLED) ? QStyle::State_Enabled : QStyle::State_None)
|
|
| ((nControlState & ControlState::FOCUSED)
|
|
? QStyle::State_HasFocus | QStyle::State_KeyboardFocusChange
|
|
: QStyle::State_None)
|
|
| ((nControlState & ControlState::PRESSED) ? QStyle::State_Sunken : QStyle::State_None)
|
|
| ((nControlState & ControlState::SELECTED) ? QStyle::State_Selected : QStyle::State_None)
|
|
| ((nControlState & ControlState::ROLLOVER) ? QStyle::State_MouseOver
|
|
: QStyle::State_None);
|
|
|
|
switch (aValue.getTristateVal())
|
|
{
|
|
case ButtonValue::On:
|
|
nState |= QStyle::State_On;
|
|
break;
|
|
case ButtonValue::Off:
|
|
nState |= QStyle::State_Off;
|
|
break;
|
|
case ButtonValue::Mixed:
|
|
nState |= QStyle::State_NoChange;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nState;
|
|
}
|
|
|
|
static void lcl_ApplyBackgroundColorToStyleOption(QStyleOption& rOption,
|
|
const Color& rBackgroundColor)
|
|
{
|
|
if (rBackgroundColor != COL_AUTO)
|
|
{
|
|
QColor aColor = toQColor(rBackgroundColor);
|
|
for (QPalette::ColorRole role : { QPalette::Window, QPalette::Button, QPalette::Base })
|
|
rOption.palette.setColor(role, aColor);
|
|
}
|
|
}
|
|
|
|
QtGraphics_Controls::QtGraphics_Controls(const QtGraphicsBase& rGraphics)
|
|
: m_rGraphics(rGraphics)
|
|
{
|
|
}
|
|
|
|
bool QtGraphics_Controls::isNativeControlSupported(ControlType type, ControlPart part)
|
|
{
|
|
switch (type)
|
|
{
|
|
case ControlType::Tooltip:
|
|
case ControlType::Progress:
|
|
case ControlType::ListNode:
|
|
return (part == ControlPart::Entire);
|
|
|
|
case ControlType::Pushbutton:
|
|
case ControlType::Radiobutton:
|
|
case ControlType::Checkbox:
|
|
return (part == ControlPart::Entire) || (part == ControlPart::Focus);
|
|
|
|
case ControlType::ListHeader:
|
|
return (part == ControlPart::Button);
|
|
|
|
case ControlType::Menubar:
|
|
case ControlType::MenuPopup:
|
|
case ControlType::Editbox:
|
|
case ControlType::MultilineEditbox:
|
|
case ControlType::Combobox:
|
|
case ControlType::Toolbar:
|
|
case ControlType::Frame:
|
|
case ControlType::Scrollbar:
|
|
case ControlType::WindowBackground:
|
|
case ControlType::Fixedline:
|
|
return true;
|
|
|
|
case ControlType::Listbox:
|
|
return (part == ControlPart::Entire || part == ControlPart::HasBackgroundTexture);
|
|
|
|
case ControlType::Spinbox:
|
|
return (part == ControlPart::Entire || part == ControlPart::HasBackgroundTexture);
|
|
|
|
case ControlType::Slider:
|
|
return (part == ControlPart::TrackHorzArea || part == ControlPart::TrackVertArea);
|
|
|
|
case ControlType::TabItem:
|
|
case ControlType::TabPane:
|
|
return ((part == ControlPart::Entire) || part == ControlPart::TabPaneWithHeader);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
inline int QtGraphics_Controls::pixelMetric(QStyle::PixelMetric metric, const QStyleOption* option,
|
|
const QWidget* pWidget)
|
|
{
|
|
return QApplication::style()->pixelMetric(metric, option, pWidget);
|
|
}
|
|
|
|
inline QSize QtGraphics_Controls::sizeFromContents(QStyle::ContentsType type,
|
|
const QStyleOption* option,
|
|
const QSize& contentsSize)
|
|
{
|
|
return QApplication::style()->sizeFromContents(type, option, contentsSize);
|
|
}
|
|
|
|
inline QRect QtGraphics_Controls::subControlRect(QStyle::ComplexControl control,
|
|
const QStyleOptionComplex* option,
|
|
QStyle::SubControl subControl)
|
|
{
|
|
return QApplication::style()->subControlRect(control, option, subControl);
|
|
}
|
|
|
|
inline QRect QtGraphics_Controls::subElementRect(QStyle::SubElement element,
|
|
const QStyleOption* option)
|
|
{
|
|
return QApplication::style()->subElementRect(element, option);
|
|
}
|
|
|
|
void QtGraphics_Controls::draw(QStyle::ControlElement element, QStyleOption& rOption, QImage* image,
|
|
const Color& rBackgroundColor, QStyle::State const state, QRect rect)
|
|
{
|
|
const QRect& targetRect = !rect.isNull() ? rect : image->rect();
|
|
|
|
rOption.state |= state;
|
|
rOption.rect = downscale(targetRect);
|
|
|
|
lcl_ApplyBackgroundColorToStyleOption(rOption, rBackgroundColor);
|
|
|
|
QPainter painter(image);
|
|
QApplication::style()->drawControl(element, &rOption, &painter);
|
|
}
|
|
|
|
void QtGraphics_Controls::draw(QStyle::PrimitiveElement element, QStyleOption& rOption,
|
|
QImage* image, const Color& rBackgroundColor,
|
|
QStyle::State const state, QRect rect)
|
|
{
|
|
const QRect& targetRect = !rect.isNull() ? rect : image->rect();
|
|
|
|
rOption.state |= state;
|
|
rOption.rect = downscale(targetRect);
|
|
|
|
lcl_ApplyBackgroundColorToStyleOption(rOption, rBackgroundColor);
|
|
|
|
QPainter painter(image);
|
|
QApplication::style()->drawPrimitive(element, &rOption, &painter);
|
|
}
|
|
|
|
void QtGraphics_Controls::draw(QStyle::ComplexControl element, QStyleOptionComplex& rOption,
|
|
QImage* image, const Color& rBackgroundColor,
|
|
QStyle::State const state)
|
|
{
|
|
const QRect& targetRect = image->rect();
|
|
|
|
rOption.state |= state;
|
|
rOption.rect = downscale(targetRect);
|
|
|
|
lcl_ApplyBackgroundColorToStyleOption(rOption, rBackgroundColor);
|
|
|
|
QPainter painter(image);
|
|
QApplication::style()->drawComplexControl(element, &rOption, &painter);
|
|
}
|
|
|
|
void QtGraphics_Controls::drawFrame(QStyle::PrimitiveElement element, QImage* image,
|
|
const Color& rBackgroundColor, QStyle::State const& state,
|
|
bool bClip, QStyle::PixelMetric eLineMetric)
|
|
{
|
|
const int fw = pixelMetric(eLineMetric);
|
|
QStyleOptionFrame option;
|
|
option.frameShape = QFrame::StyledPanel;
|
|
option.state = QStyle::State_Sunken | state;
|
|
option.lineWidth = fw;
|
|
|
|
QRect aRect = downscale(image->rect());
|
|
option.rect = aRect;
|
|
|
|
lcl_ApplyBackgroundColorToStyleOption(option, rBackgroundColor);
|
|
|
|
QPainter painter(image);
|
|
if (bClip)
|
|
painter.setClipRegion(QRegion(aRect).subtracted(aRect.adjusted(fw, fw, -fw, -fw)));
|
|
QApplication::style()->drawPrimitive(element, &option, &painter);
|
|
}
|
|
|
|
void QtGraphics_Controls::fillQStyleOptionTab(const ImplControlValue& value, QStyleOptionTab& sot)
|
|
{
|
|
const TabitemValue& rValue = static_cast<const TabitemValue&>(value);
|
|
if (rValue.isFirst())
|
|
sot.position = rValue.isLast() ? QStyleOptionTab::OnlyOneTab : QStyleOptionTab::Beginning;
|
|
else if (rValue.isLast())
|
|
sot.position = rValue.isFirst() ? QStyleOptionTab::OnlyOneTab : QStyleOptionTab::End;
|
|
else
|
|
sot.position = QStyleOptionTab::Middle;
|
|
}
|
|
|
|
void QtGraphics_Controls::fullQStyleOptionTabWidgetFrame(QStyleOptionTabWidgetFrame& option,
|
|
bool bDownscale)
|
|
{
|
|
option.state = QStyle::State_Enabled;
|
|
option.rightCornerWidgetSize = QSize(0, 0);
|
|
option.leftCornerWidgetSize = QSize(0, 0);
|
|
int nLineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth);
|
|
option.lineWidth = bDownscale ? std::max(1, downscale(nLineWidth, Round::Ceil)) : nLineWidth;
|
|
option.midLineWidth = 0;
|
|
option.shape = QTabBar::RoundedNorth;
|
|
}
|
|
|
|
bool QtGraphics_Controls::drawNativeControl(ControlType type, ControlPart part,
|
|
const tools::Rectangle& rControlRegion,
|
|
ControlState nControlState,
|
|
const ImplControlValue& value, const OUString&,
|
|
const Color& rBackgroundColor)
|
|
{
|
|
bool nativeSupport = isNativeControlSupported(type, part);
|
|
if (!nativeSupport)
|
|
{
|
|
assert(!nativeSupport && "drawNativeControl called without native support!");
|
|
return false;
|
|
}
|
|
|
|
if (m_lastPopupRect.isValid()
|
|
&& (type != ControlType::MenuPopup || part != ControlPart::MenuItem))
|
|
m_lastPopupRect = QRect();
|
|
|
|
bool returnVal = true;
|
|
|
|
QRect widgetRect = toQRect(rControlRegion);
|
|
|
|
//if no image, or resized, make a new image
|
|
if (!m_image || m_image->size() != widgetRect.size())
|
|
{
|
|
m_image.reset(new QImage(widgetRect.width(), widgetRect.height(),
|
|
QImage::Format_ARGB32_Premultiplied));
|
|
m_image->setDevicePixelRatio(m_rGraphics.devicePixelRatioF());
|
|
}
|
|
|
|
// Default image color - just once
|
|
switch (type)
|
|
{
|
|
case ControlType::MenuPopup:
|
|
if (part == ControlPart::MenuItemCheckMark || part == ControlPart::MenuItemRadioMark)
|
|
{
|
|
// it is necessary to fill the background transparently first, as this
|
|
// is painted after menuitem highlight, otherwise there would be a grey area
|
|
m_image->fill(Qt::transparent);
|
|
break;
|
|
}
|
|
[[fallthrough]]; // QPalette::Window
|
|
case ControlType::Menubar:
|
|
case ControlType::WindowBackground:
|
|
m_image->fill(QApplication::palette().color(QPalette::Window).rgb());
|
|
break;
|
|
case ControlType::Tooltip:
|
|
m_image->fill(QApplication::palette().color(QPalette::ToolTipBase).rgb());
|
|
break;
|
|
case ControlType::Scrollbar:
|
|
if ((part == ControlPart::DrawBackgroundVert)
|
|
|| (part == ControlPart::DrawBackgroundHorz))
|
|
{
|
|
m_image->fill(QApplication::palette().color(QPalette::Window).rgb());
|
|
break;
|
|
}
|
|
[[fallthrough]]; // Qt::transparent
|
|
default:
|
|
m_image->fill(Qt::transparent);
|
|
break;
|
|
}
|
|
|
|
if (type == ControlType::Pushbutton)
|
|
{
|
|
const PushButtonValue& rPBValue = static_cast<const PushButtonValue&>(value);
|
|
if (part == ControlPart::Focus)
|
|
// Nothing to do. Drawing focus separately is not needed because that's
|
|
// already handled by the ControlState::FOCUSED state being set when
|
|
// drawing the entire control
|
|
return true;
|
|
assert(part == ControlPart::Entire);
|
|
QStyleOptionButton option;
|
|
if (nControlState & ControlState::DEFAULT)
|
|
option.features |= QStyleOptionButton::DefaultButton;
|
|
if (rPBValue.m_bFlatButton)
|
|
option.features |= QStyleOptionButton::Flat;
|
|
draw(QStyle::CE_PushButton, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (type == ControlType::Menubar)
|
|
{
|
|
if (part == ControlPart::MenuItem)
|
|
{
|
|
QStyleOptionMenuItem option;
|
|
option.state = vclStateValue2StateFlag(nControlState, value);
|
|
if ((nControlState & ControlState::ROLLOVER)
|
|
&& QApplication::style()->styleHint(QStyle::SH_MenuBar_MouseTracking))
|
|
option.state |= QStyle::State_Selected;
|
|
|
|
if (nControlState
|
|
& ControlState::SELECTED) // Passing State_Sunken is currently not documented.
|
|
option.state |= QStyle::State_Sunken; // But some kinds of QStyle interpret it.
|
|
|
|
draw(QStyle::CE_MenuBarItem, option, m_image.get(), rBackgroundColor);
|
|
}
|
|
else if (part == ControlPart::Entire)
|
|
{
|
|
QStyleOptionMenuItem option;
|
|
draw(QStyle::CE_MenuBarEmptyArea, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else
|
|
{
|
|
returnVal = false;
|
|
}
|
|
}
|
|
else if (type == ControlType::MenuPopup)
|
|
{
|
|
assert(part == ControlPart::MenuItem ? m_lastPopupRect.isValid()
|
|
: !m_lastPopupRect.isValid());
|
|
if (part == ControlPart::MenuItem)
|
|
{
|
|
QStyleOptionMenuItem option;
|
|
draw(QStyle::CE_MenuItem, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
// HACK: LO core first paints the entire popup and only then it paints menu items,
|
|
// but QMenu::paintEvent() paints popup frame after all items. That means highlighted
|
|
// items here would paint the highlight over the frame border. Since calls to ControlPart::MenuItem
|
|
// are always preceded by calls to ControlPart::Entire, just remember the size for the whole
|
|
// popup (otherwise not possible to get here) and draw the border afterwards.
|
|
QRect framerect(m_lastPopupRect.topLeft() - widgetRect.topLeft(),
|
|
widgetRect.size().expandedTo(m_lastPopupRect.size()));
|
|
QStyleOptionFrame frame;
|
|
draw(QStyle::PE_FrameMenu, frame, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value), framerect);
|
|
}
|
|
else if (part == ControlPart::Separator)
|
|
{
|
|
QStyleOptionMenuItem option;
|
|
option.menuItemType = QStyleOptionMenuItem::Separator;
|
|
// Painting the whole menu item area results in different background
|
|
// with at least Plastique style, so clip only to the separator itself
|
|
// (QSize( 2, 2 ) is hardcoded in Qt)
|
|
option.rect = m_image->rect();
|
|
QSize size = sizeFromContents(QStyle::CT_MenuItem, &option, QSize(2, 2));
|
|
QRect rect = m_image->rect();
|
|
QPoint center = rect.center();
|
|
rect.setHeight(size.height());
|
|
rect.moveCenter(center);
|
|
option.state |= vclStateValue2StateFlag(nControlState, value);
|
|
option.rect = rect;
|
|
|
|
QPainter painter(m_image.get());
|
|
// don't paint over popup frame border (like the hack above, but here it can be simpler)
|
|
const int fw = pixelMetric(QStyle::PM_MenuPanelWidth);
|
|
painter.setClipRect(rect.adjusted(fw, 0, -fw, 0));
|
|
QApplication::style()->drawControl(QStyle::CE_MenuItem, &option, &painter);
|
|
}
|
|
else if (part == ControlPart::MenuItemCheckMark || part == ControlPart::MenuItemRadioMark)
|
|
{
|
|
QStyleOptionMenuItem option;
|
|
option.checkType = (part == ControlPart::MenuItemCheckMark)
|
|
? QStyleOptionMenuItem::NonExclusive
|
|
: QStyleOptionMenuItem::Exclusive;
|
|
option.checked = bool(nControlState & ControlState::PRESSED);
|
|
// widgetRect is now the rectangle for the checkbox/radiobutton itself, but Qt
|
|
// paints the whole menu item, so translate position (and it'll be clipped);
|
|
// it is also necessary to fill the background transparently first, as this
|
|
// is painted after menuitem highlight, otherwise there would be a grey area
|
|
assert(value.getType() == ControlType::MenuPopup);
|
|
const MenupopupValue* menuVal = static_cast<const MenupopupValue*>(&value);
|
|
QRect menuItemRect(toQRect(menuVal->maItemRect));
|
|
QRect rect(menuItemRect.topLeft() - widgetRect.topLeft(),
|
|
widgetRect.size().expandedTo(menuItemRect.size()));
|
|
// checkboxes are always displayed next to images in menus, so are never centered
|
|
const int focus_size = pixelMetric(QStyle::PM_FocusFrameHMargin);
|
|
rect.moveTo(-focus_size, rect.y());
|
|
draw(QStyle::CE_MenuItem, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState & ~ControlState::PRESSED, value), rect);
|
|
}
|
|
else if (part == ControlPart::Entire)
|
|
{
|
|
QStyleOptionMenuItem option;
|
|
option.state = vclStateValue2StateFlag(nControlState, value);
|
|
draw(QStyle::PE_PanelMenu, option, m_image.get(), rBackgroundColor);
|
|
// Try hard to get any frame!
|
|
QStyleOptionFrame frame;
|
|
draw(QStyle::PE_FrameMenu, frame, m_image.get(), rBackgroundColor);
|
|
draw(QStyle::PE_FrameWindow, frame, m_image.get(), rBackgroundColor);
|
|
m_lastPopupRect = widgetRect;
|
|
}
|
|
else
|
|
returnVal = false;
|
|
}
|
|
else if ((type == ControlType::Toolbar) && (part == ControlPart::Button))
|
|
{
|
|
QStyleOptionToolButton option;
|
|
|
|
option.arrowType = Qt::NoArrow;
|
|
option.subControls = QStyle::SC_ToolButton;
|
|
option.state = vclStateValue2StateFlag(nControlState, value);
|
|
option.state |= QStyle::State_Raised | QStyle::State_Enabled | QStyle::State_AutoRaise;
|
|
|
|
draw(QStyle::CC_ToolButton, option, m_image.get(), rBackgroundColor);
|
|
}
|
|
else if ((type == ControlType::Toolbar) && (part == ControlPart::Entire))
|
|
{
|
|
QStyleOptionToolBar option;
|
|
draw(QStyle::CE_ToolBar, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if ((type == ControlType::Toolbar)
|
|
&& (part == ControlPart::ThumbVert || part == ControlPart::ThumbHorz))
|
|
{
|
|
// reduce paint area only to the handle area
|
|
const int handleExtend = pixelMetric(QStyle::PM_ToolBarHandleExtent);
|
|
QStyleOption option;
|
|
QRect aRect = m_image->rect();
|
|
if (part == ControlPart::ThumbVert)
|
|
{
|
|
aRect.setWidth(handleExtend);
|
|
option.state = QStyle::State_Horizontal;
|
|
}
|
|
else
|
|
aRect.setHeight(handleExtend);
|
|
draw(QStyle::PE_IndicatorToolBarHandle, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value), aRect);
|
|
}
|
|
else if (type == ControlType::Editbox || type == ControlType::MultilineEditbox)
|
|
{
|
|
drawFrame(QStyle::PE_FrameLineEdit, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value), false);
|
|
}
|
|
else if (type == ControlType::Combobox)
|
|
{
|
|
QStyleOptionComboBox option;
|
|
option.editable = true;
|
|
draw(QStyle::CC_ComboBox, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (type == ControlType::Listbox)
|
|
{
|
|
QStyleOptionComboBox option;
|
|
option.editable = false;
|
|
switch (part)
|
|
{
|
|
case ControlPart::ListboxWindow:
|
|
drawFrame(QStyle::PE_Frame, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value), true,
|
|
QStyle::PM_ComboBoxFrameWidth);
|
|
break;
|
|
case ControlPart::SubEdit:
|
|
draw(QStyle::CE_ComboBoxLabel, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
break;
|
|
case ControlPart::Entire:
|
|
draw(QStyle::CC_ComboBox, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
break;
|
|
case ControlPart::ButtonDown:
|
|
option.subControls = QStyle::SC_ComboBoxArrow;
|
|
draw(QStyle::CC_ComboBox, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
break;
|
|
default:
|
|
returnVal = false;
|
|
break;
|
|
}
|
|
}
|
|
else if (type == ControlType::ListNode)
|
|
{
|
|
QStyleOption option;
|
|
option.state = vclStateValue2StateFlag(nControlState, value);
|
|
option.state |= QStyle::State_Item | QStyle::State_Children;
|
|
|
|
if (value.getTristateVal() == ButtonValue::On)
|
|
option.state |= QStyle::State_Open;
|
|
|
|
draw(QStyle::PE_IndicatorBranch, option, m_image.get(), rBackgroundColor);
|
|
}
|
|
else if (type == ControlType::ListHeader)
|
|
{
|
|
QStyleOptionHeader option;
|
|
draw(QStyle::CE_HeaderSection, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (type == ControlType::Checkbox)
|
|
{
|
|
if (part == ControlPart::Entire)
|
|
{
|
|
QStyleOptionButton option;
|
|
// clear FOCUSED bit, focus is drawn separately
|
|
nControlState &= ~ControlState::FOCUSED;
|
|
draw(QStyle::CE_CheckBox, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (part == ControlPart::Focus)
|
|
{
|
|
QStyleOptionFocusRect option;
|
|
draw(QStyle::PE_FrameFocusRect, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
}
|
|
else if (type == ControlType::Scrollbar)
|
|
{
|
|
if ((part == ControlPart::DrawBackgroundVert) || (part == ControlPart::DrawBackgroundHorz))
|
|
{
|
|
QStyleOptionSlider option;
|
|
assert(value.getType() == ControlType::Scrollbar);
|
|
const ScrollbarValue* sbVal = static_cast<const ScrollbarValue*>(&value);
|
|
|
|
//if the scroll bar is active (aka not degenerate... allow for hover events)
|
|
if (sbVal->mnVisibleSize < sbVal->mnMax)
|
|
option.state = QStyle::State_MouseOver;
|
|
|
|
bool horizontal = (part == ControlPart::DrawBackgroundHorz); //horizontal or vertical
|
|
option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
|
|
if (horizontal)
|
|
option.state |= QStyle::State_Horizontal;
|
|
|
|
// If the scrollbar has a mnMin == 0 and mnMax == 0 then mnVisibleSize is set to -1?!
|
|
// I don't know if a negative mnVisibleSize makes any sense, so just handle this case
|
|
// without crashing LO with a SIGFPE in the Qt library.
|
|
const tools::Long nVisibleSize
|
|
= (sbVal->mnMin == sbVal->mnMax) ? 0 : sbVal->mnVisibleSize;
|
|
|
|
option.minimum = sbVal->mnMin;
|
|
option.maximum = sbVal->mnMax - nVisibleSize;
|
|
option.maximum = qMax(option.maximum, option.minimum); // bnc#619772
|
|
option.sliderValue = sbVal->mnCur;
|
|
option.sliderPosition = sbVal->mnCur;
|
|
option.pageStep = nVisibleSize;
|
|
if (part == ControlPart::DrawBackgroundHorz)
|
|
option.upsideDown
|
|
= (QGuiApplication::isRightToLeft()
|
|
&& sbVal->maButton1Rect.Left() < sbVal->maButton2Rect.Left())
|
|
|| (QGuiApplication::isLeftToRight()
|
|
&& sbVal->maButton1Rect.Left() > sbVal->maButton2Rect.Left());
|
|
|
|
//setup the active control... always the slider
|
|
if (sbVal->mnThumbState & ControlState::ROLLOVER)
|
|
option.activeSubControls = QStyle::SC_ScrollBarSlider;
|
|
|
|
draw(QStyle::CC_ScrollBar, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else
|
|
{
|
|
returnVal = false;
|
|
}
|
|
}
|
|
else if (type == ControlType::Spinbox)
|
|
{
|
|
QStyleOptionSpinBox option;
|
|
option.frame = true;
|
|
|
|
// determine active control
|
|
if (value.getType() == ControlType::SpinButtons)
|
|
{
|
|
const SpinbuttonValue* pSpinVal = static_cast<const SpinbuttonValue*>(&value);
|
|
if (pSpinVal->mnUpperState & ControlState::PRESSED)
|
|
option.activeSubControls |= QStyle::SC_SpinBoxUp;
|
|
if (pSpinVal->mnLowerState & ControlState::PRESSED)
|
|
option.activeSubControls |= QStyle::SC_SpinBoxDown;
|
|
if (pSpinVal->mnUpperState & ControlState::ENABLED)
|
|
option.stepEnabled |= QAbstractSpinBox::StepUpEnabled;
|
|
if (pSpinVal->mnLowerState & ControlState::ENABLED)
|
|
option.stepEnabled |= QAbstractSpinBox::StepDownEnabled;
|
|
if (pSpinVal->mnUpperState & ControlState::ROLLOVER)
|
|
option.state = QStyle::State_MouseOver;
|
|
if (pSpinVal->mnLowerState & ControlState::ROLLOVER)
|
|
option.state = QStyle::State_MouseOver;
|
|
}
|
|
|
|
draw(QStyle::CC_SpinBox, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (type == ControlType::Radiobutton)
|
|
{
|
|
if (part == ControlPart::Entire)
|
|
{
|
|
QStyleOptionButton option;
|
|
// clear FOCUSED bit, focus is drawn separately
|
|
nControlState &= ~ControlState::FOCUSED;
|
|
draw(QStyle::CE_RadioButton, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (part == ControlPart::Focus)
|
|
{
|
|
QStyleOptionFocusRect option;
|
|
draw(QStyle::PE_FrameFocusRect, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
}
|
|
else if (type == ControlType::Tooltip)
|
|
{
|
|
QStyleOption option;
|
|
draw(QStyle::PE_PanelTipLabel, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (type == ControlType::Frame)
|
|
{
|
|
drawFrame(QStyle::PE_Frame, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (type == ControlType::WindowBackground)
|
|
{
|
|
// Nothing to do - see "Default image color" switch ^^
|
|
}
|
|
else if (type == ControlType::Fixedline)
|
|
{
|
|
QStyleOptionMenuItem option;
|
|
option.menuItemType = QStyleOptionMenuItem::Separator;
|
|
option.state = vclStateValue2StateFlag(nControlState, value);
|
|
option.state |= QStyle::State_Item;
|
|
|
|
draw(QStyle::CE_MenuItem, option, m_image.get(), rBackgroundColor);
|
|
}
|
|
else if (type == ControlType::Slider
|
|
&& (part == ControlPart::TrackHorzArea || part == ControlPart::TrackVertArea))
|
|
{
|
|
assert(value.getType() == ControlType::Slider);
|
|
const SliderValue* slVal = static_cast<const SliderValue*>(&value);
|
|
QStyleOptionSlider option;
|
|
|
|
option.state = vclStateValue2StateFlag(nControlState, value);
|
|
option.maximum = slVal->mnMax;
|
|
option.minimum = slVal->mnMin;
|
|
option.sliderPosition = option.sliderValue = slVal->mnCur;
|
|
bool horizontal = (part == ControlPart::TrackHorzArea); //horizontal or vertical
|
|
option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
|
|
if (horizontal)
|
|
option.state |= QStyle::State_Horizontal;
|
|
|
|
draw(QStyle::CC_Slider, option, m_image.get(), rBackgroundColor);
|
|
}
|
|
else if (type == ControlType::Progress && part == ControlPart::Entire)
|
|
{
|
|
QStyleOptionProgressBar option;
|
|
option.minimum = 0;
|
|
option.maximum = widgetRect.width();
|
|
option.progress = value.getNumericVal();
|
|
|
|
draw(QStyle::CE_ProgressBar, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (type == ControlType::TabItem && part == ControlPart::Entire)
|
|
{
|
|
QStyleOptionTab sot;
|
|
fillQStyleOptionTab(value, sot);
|
|
draw(QStyle::CE_TabBarTabShape, sot, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value));
|
|
}
|
|
else if (type == ControlType::TabPane && part == ControlPart::Entire)
|
|
{
|
|
const TabPaneValue& rValue = static_cast<const TabPaneValue&>(value);
|
|
|
|
// get the overlap size for the tabs, so they will overlap the frame
|
|
QStyleOptionTab tabOverlap;
|
|
tabOverlap.shape = QTabBar::RoundedNorth;
|
|
TabPaneValue::m_nOverlap = pixelMetric(QStyle::PM_TabBarBaseOverlap, &tabOverlap);
|
|
|
|
QStyleOptionTabWidgetFrame option;
|
|
fullQStyleOptionTabWidgetFrame(option, false);
|
|
option.tabBarRect = toQRect(rValue.m_aTabHeaderRect);
|
|
option.selectedTabRect
|
|
= rValue.m_aSelectedTabRect.IsEmpty() ? QRect() : toQRect(rValue.m_aSelectedTabRect);
|
|
option.tabBarSize = toQSize(rValue.m_aTabHeaderRect.GetSize());
|
|
option.rect = m_image->rect();
|
|
QRect aRect = subElementRect(QStyle::SE_TabWidgetTabPane, &option);
|
|
draw(QStyle::PE_FrameTabWidget, option, m_image.get(), rBackgroundColor,
|
|
vclStateValue2StateFlag(nControlState, value), aRect);
|
|
}
|
|
else
|
|
{
|
|
returnVal = false;
|
|
}
|
|
|
|
return returnVal;
|
|
}
|
|
|
|
bool QtGraphics_Controls::getNativeControlRegion(
|
|
ControlType type, ControlPart part, const tools::Rectangle& controlRegion,
|
|
ControlState controlState, const ImplControlValue& val, const OUString& rCaption,
|
|
tools::Rectangle& nativeBoundingRegion, tools::Rectangle& nativeContentRegion)
|
|
{
|
|
QtInstance* pQtInstance(GetQtInstance());
|
|
assert(pQtInstance);
|
|
if (!pQtInstance->IsMainThread())
|
|
{
|
|
bool bRet;
|
|
pQtInstance->RunInMainThread([&]() {
|
|
bRet = getNativeControlRegion(type, part, controlRegion, controlState, val, rCaption,
|
|
nativeBoundingRegion, nativeContentRegion);
|
|
});
|
|
return bRet;
|
|
}
|
|
|
|
bool retVal = false;
|
|
|
|
QRect boundingRect = toQRect(controlRegion);
|
|
QRect contentRect = boundingRect;
|
|
QStyleOptionComplex styleOption;
|
|
|
|
switch (type)
|
|
{
|
|
// Metrics of the push button
|
|
case ControlType::Pushbutton:
|
|
if (part == ControlPart::Entire)
|
|
{
|
|
styleOption.state = vclStateValue2StateFlag(controlState, val);
|
|
|
|
if (controlState & ControlState::DEFAULT)
|
|
{
|
|
int size = upscale(pixelMetric(QStyle::PM_ButtonDefaultIndicator, &styleOption),
|
|
Round::Ceil);
|
|
boundingRect.adjust(-size, -size, size, size);
|
|
retVal = true;
|
|
}
|
|
}
|
|
else if (part == ControlPart::Focus)
|
|
retVal = true;
|
|
break;
|
|
case ControlType::Editbox:
|
|
case ControlType::MultilineEditbox:
|
|
{
|
|
// we have to get stable borders, otherwise layout loops.
|
|
// so we simply only scale the detected borders.
|
|
QStyleOptionFrame fo;
|
|
fo.frameShape = QFrame::StyledPanel;
|
|
fo.state = QStyle::State_Sunken;
|
|
fo.lineWidth = pixelMetric(QStyle::PM_DefaultFrameWidth);
|
|
fo.rect = downscale(contentRect);
|
|
fo.rect.setSize(sizeFromContents(QStyle::CT_LineEdit, &fo, fo.rect.size()));
|
|
QRect aSubRect = subElementRect(QStyle::SE_LineEditContents, &fo);
|
|
|
|
// VCL tests borders with small defaults before layout, where Qt returns no sub-rect,
|
|
// so this gets us at least some frame.
|
|
int nLine = upscale(fo.lineWidth, Round::Ceil);
|
|
int nLeft = qMin(-nLine, upscale(fo.rect.left() - aSubRect.left(), Round::Floor));
|
|
int nTop = qMin(-nLine, upscale(fo.rect.top() - aSubRect.top(), Round::Floor));
|
|
int nRight = qMax(nLine, upscale(fo.rect.right() - aSubRect.right(), Round::Ceil));
|
|
int nBottom = qMax(nLine, upscale(fo.rect.bottom() - aSubRect.bottom(), Round::Ceil));
|
|
boundingRect.adjust(nLeft, nTop, nRight, nBottom);
|
|
|
|
// tdf#150451: ensure a minimum size that fits text content + frame at top and bottom.
|
|
// Themes may use the widget type for determining the actual frame width to use,
|
|
// so pass a dummy QLineEdit
|
|
//
|
|
// NOTE: This is currently only done here for the minimum size calculation and
|
|
// not above because the handling for edit boxes here and in the calling code
|
|
// currently does all kinds of "interesting" things like doing extra size adjustments
|
|
// or passing the content rect where the bounding rect would be expected,...
|
|
// Ideally this should be cleaned up in the callers and all platform integrations
|
|
// to adhere to what the doc in vcl/inc/WidgetDrawInterface.hxx says, but this
|
|
// here keeps it working with existing code for now.
|
|
// (s.a. discussion in https://gerrit.libreoffice.org/c/core/+/146516 for more details)
|
|
QLineEdit aDummyEdit;
|
|
const int nFrameWidth = pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, &aDummyEdit);
|
|
QFontMetrics aFontMetrics(QApplication::font());
|
|
const int minHeight = upscale(aFontMetrics.height() + 2 * nFrameWidth, Round::Floor);
|
|
if (boundingRect.height() < minHeight)
|
|
{
|
|
const int nDiff = minHeight - boundingRect.height();
|
|
boundingRect.setHeight(boundingRect.height() + nDiff);
|
|
contentRect.setHeight(contentRect.height() + nDiff);
|
|
}
|
|
|
|
retVal = true;
|
|
break;
|
|
}
|
|
case ControlType::Checkbox:
|
|
if (part == ControlPart::Entire)
|
|
{
|
|
styleOption.state = vclStateValue2StateFlag(controlState, val);
|
|
|
|
int nWidth = pixelMetric(QStyle::PM_IndicatorWidth, &styleOption);
|
|
int nHeight = pixelMetric(QStyle::PM_IndicatorHeight, &styleOption);
|
|
contentRect.setSize(upscale(QSize(nWidth, nHeight), Round::Ceil));
|
|
|
|
int nHMargin = pixelMetric(QStyle::PM_FocusFrameHMargin, &styleOption);
|
|
int nVMargin = pixelMetric(QStyle::PM_FocusFrameVMargin, &styleOption);
|
|
contentRect.adjust(0, 0, 2 * upscale(nHMargin, Round::Ceil),
|
|
2 * upscale(nVMargin, Round::Ceil));
|
|
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
}
|
|
break;
|
|
case ControlType::Combobox:
|
|
case ControlType::Listbox:
|
|
{
|
|
QStyleOptionComboBox cbo;
|
|
|
|
cbo.rect = downscale(QRect(0, 0, contentRect.width(), contentRect.height()));
|
|
cbo.state = vclStateValue2StateFlag(controlState, val);
|
|
|
|
switch (part)
|
|
{
|
|
case ControlPart::Entire:
|
|
{
|
|
// find out the minimum size that should be used
|
|
// assume contents is a text line
|
|
QSize aContentSize = downscale(contentRect.size(), Round::Ceil);
|
|
QFontMetrics aFontMetrics(QApplication::font());
|
|
aContentSize.setHeight(aFontMetrics.height());
|
|
QSize aMinSize = upscale(
|
|
sizeFromContents(QStyle::CT_ComboBox, &cbo, aContentSize), Round::Ceil);
|
|
if (aMinSize.height() > contentRect.height())
|
|
contentRect.setHeight(aMinSize.height());
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
break;
|
|
}
|
|
case ControlPart::ButtonDown:
|
|
{
|
|
contentRect = upscale(
|
|
subControlRect(QStyle::CC_ComboBox, &cbo, QStyle::SC_ComboBoxArrow));
|
|
contentRect.translate(boundingRect.left(), boundingRect.top());
|
|
retVal = true;
|
|
break;
|
|
}
|
|
case ControlPart::SubEdit:
|
|
{
|
|
contentRect = upscale(
|
|
subControlRect(QStyle::CC_ComboBox, &cbo, QStyle::SC_ComboBoxEditField));
|
|
contentRect.translate(boundingRect.left(), boundingRect.top());
|
|
retVal = true;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case ControlType::Spinbox:
|
|
{
|
|
QStyleOptionSpinBox sbo;
|
|
sbo.frame = true;
|
|
|
|
sbo.rect = downscale(QRect(0, 0, contentRect.width(), contentRect.height()));
|
|
sbo.state = vclStateValue2StateFlag(controlState, val);
|
|
|
|
switch (part)
|
|
{
|
|
case ControlPart::Entire:
|
|
{
|
|
QSize aContentSize = downscale(contentRect.size(), Round::Ceil);
|
|
QFontMetrics aFontMetrics(QApplication::font());
|
|
aContentSize.setHeight(aFontMetrics.height());
|
|
QSize aMinSize = upscale(
|
|
sizeFromContents(QStyle::CT_SpinBox, &sbo, aContentSize), Round::Ceil);
|
|
if (aMinSize.height() > contentRect.height())
|
|
contentRect.setHeight(aMinSize.height());
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
break;
|
|
}
|
|
case ControlPart::ButtonUp:
|
|
contentRect
|
|
= upscale(subControlRect(QStyle::CC_SpinBox, &sbo, QStyle::SC_SpinBoxUp));
|
|
contentRect.translate(boundingRect.left(), boundingRect.top());
|
|
retVal = true;
|
|
break;
|
|
case ControlPart::ButtonDown:
|
|
contentRect
|
|
= upscale(subControlRect(QStyle::CC_SpinBox, &sbo, QStyle::SC_SpinBoxDown));
|
|
contentRect.translate(boundingRect.left(), boundingRect.top());
|
|
retVal = true;
|
|
break;
|
|
case ControlPart::SubEdit:
|
|
contentRect = upscale(
|
|
subControlRect(QStyle::CC_SpinBox, &sbo, QStyle::SC_SpinBoxEditField));
|
|
contentRect.translate(boundingRect.left(), boundingRect.top());
|
|
retVal = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case ControlType::MenuPopup:
|
|
{
|
|
int h, w;
|
|
switch (part)
|
|
{
|
|
case ControlPart::MenuItemCheckMark:
|
|
h = upscale(pixelMetric(QStyle::PM_IndicatorHeight), Round::Floor);
|
|
w = upscale(pixelMetric(QStyle::PM_IndicatorWidth), Round::Floor);
|
|
retVal = true;
|
|
break;
|
|
case ControlPart::MenuItemRadioMark:
|
|
h = upscale(pixelMetric(QStyle::PM_ExclusiveIndicatorHeight), Round::Floor);
|
|
w = upscale(pixelMetric(QStyle::PM_ExclusiveIndicatorWidth), Round::Floor);
|
|
retVal = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (retVal)
|
|
{
|
|
contentRect = QRect(0, 0, w, h);
|
|
boundingRect = contentRect;
|
|
}
|
|
break;
|
|
}
|
|
case ControlType::Frame:
|
|
{
|
|
if (part == ControlPart::Border)
|
|
{
|
|
int nFrameWidth = upscale(pixelMetric(QStyle::PM_DefaultFrameWidth), Round::Ceil);
|
|
contentRect.adjust(nFrameWidth, nFrameWidth, -nFrameWidth, -nFrameWidth);
|
|
retVal = true;
|
|
}
|
|
break;
|
|
}
|
|
case ControlType::Radiobutton:
|
|
{
|
|
const int h = upscale(pixelMetric(QStyle::PM_ExclusiveIndicatorHeight), Round::Ceil);
|
|
const int w = upscale(pixelMetric(QStyle::PM_ExclusiveIndicatorWidth), Round::Ceil);
|
|
|
|
contentRect = QRect(boundingRect.left(), boundingRect.top(), w, h);
|
|
int nHMargin = pixelMetric(QStyle::PM_FocusFrameHMargin, &styleOption);
|
|
int nVMargin = pixelMetric(QStyle::PM_FocusFrameVMargin, &styleOption);
|
|
contentRect.adjust(0, 0, upscale(2 * nHMargin, Round::Ceil),
|
|
upscale(2 * nVMargin, Round::Ceil));
|
|
boundingRect = contentRect;
|
|
|
|
retVal = true;
|
|
break;
|
|
}
|
|
case ControlType::Slider:
|
|
{
|
|
const int w = upscale(pixelMetric(QStyle::PM_SliderLength), Round::Ceil);
|
|
if (part == ControlPart::ThumbHorz)
|
|
{
|
|
contentRect
|
|
= QRect(boundingRect.left(), boundingRect.top(), w, boundingRect.height());
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
}
|
|
else if (part == ControlPart::ThumbVert)
|
|
{
|
|
contentRect
|
|
= QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), w);
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
}
|
|
break;
|
|
}
|
|
case ControlType::Toolbar:
|
|
{
|
|
const int nWorH = upscale(pixelMetric(QStyle::PM_ToolBarHandleExtent), Round::Ceil);
|
|
if (part == ControlPart::ThumbHorz)
|
|
{
|
|
contentRect
|
|
= QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), nWorH);
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
}
|
|
else if (part == ControlPart::ThumbVert)
|
|
{
|
|
contentRect
|
|
= QRect(boundingRect.left(), boundingRect.top(), nWorH, boundingRect.height());
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
}
|
|
else if (part == ControlPart::Button)
|
|
{
|
|
QStyleOptionToolButton option;
|
|
option.arrowType = Qt::NoArrow;
|
|
option.features = QStyleOptionToolButton::None;
|
|
option.rect = downscale(QRect({ 0, 0 }, contentRect.size()));
|
|
contentRect = upscale(
|
|
subControlRect(QStyle::CC_ToolButton, &option, QStyle::SC_ToolButton));
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
}
|
|
break;
|
|
}
|
|
case ControlType::Scrollbar:
|
|
{
|
|
// core can't handle 3-button scrollbars well, so we fix that in hitTestNativeControl(),
|
|
// for the rest also provide the track area (i.e. area not taken by buttons)
|
|
if (part == ControlPart::TrackVertArea || part == ControlPart::TrackHorzArea)
|
|
{
|
|
QStyleOptionSlider option;
|
|
bool horizontal = (part == ControlPart::TrackHorzArea); //horizontal or vertical
|
|
option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
|
|
if (horizontal)
|
|
option.state |= QStyle::State_Horizontal;
|
|
// getNativeControlRegion usually gets ImplControlValue as 'val' (i.e. not the proper
|
|
// subclass), so use random sensible values (doesn't matter anyway, as the wanted
|
|
// geometry here depends only on button sizes)
|
|
option.maximum = 10;
|
|
option.minimum = 0;
|
|
option.sliderPosition = option.sliderValue = 4;
|
|
option.pageStep = 2;
|
|
// Adjust coordinates to make the widget appear to be at (0,0), i.e. make
|
|
// widget and screen coordinates the same. QStyle functions should use screen
|
|
// coordinates but at least QPlastiqueStyle::subControlRect() is buggy
|
|
// and sometimes uses widget coordinates.
|
|
option.rect = downscale(QRect({ 0, 0 }, contentRect.size()));
|
|
contentRect = upscale(
|
|
subControlRect(QStyle::CC_ScrollBar, &option, QStyle::SC_ScrollBarGroove));
|
|
contentRect.translate(boundingRect.left()
|
|
- (contentRect.width() - boundingRect.width()),
|
|
boundingRect.top());
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
}
|
|
break;
|
|
}
|
|
case ControlType::TabItem:
|
|
{
|
|
QStyleOptionTab sot;
|
|
fillQStyleOptionTab(val, sot);
|
|
QSize aMinSize = upscale(sizeFromContents(QStyle::CT_TabBarTab, &sot,
|
|
downscale(contentRect.size(), Round::Ceil)),
|
|
Round::Ceil);
|
|
contentRect.setSize(aMinSize);
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
break;
|
|
}
|
|
case ControlType::TabPane:
|
|
{
|
|
const TabPaneValue& rValue = static_cast<const TabPaneValue&>(val);
|
|
QStyleOptionTabWidgetFrame sotwf;
|
|
fullQStyleOptionTabWidgetFrame(sotwf, true);
|
|
QSize contentSize(
|
|
std::max(rValue.m_aTabHeaderRect.GetWidth(), controlRegion.GetWidth()),
|
|
rValue.m_aTabHeaderRect.GetHeight() + controlRegion.GetHeight());
|
|
QSize aMinSize = upscale(
|
|
sizeFromContents(QStyle::CT_TabWidget, &sotwf, downscale(contentSize, Round::Ceil)),
|
|
Round::Ceil);
|
|
contentRect.setSize(aMinSize);
|
|
boundingRect = contentRect;
|
|
retVal = true;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (retVal)
|
|
{
|
|
nativeBoundingRegion = toRectangle(boundingRect);
|
|
nativeContentRegion = toRectangle(contentRect);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/** Test whether the position is in the native widget.
|
|
If the return value is true, bIsInside contains information whether
|
|
aPos was or was not inside the native widget specified by the
|
|
nType/nPart combination.
|
|
*/
|
|
bool QtGraphics_Controls::hitTestNativeControl(ControlType nType, ControlPart nPart,
|
|
const tools::Rectangle& rControlRegion,
|
|
const Point& rPos, bool& rIsInside)
|
|
{
|
|
if (nType == ControlType::Scrollbar)
|
|
{
|
|
if (nPart != ControlPart::ButtonUp && nPart != ControlPart::ButtonDown
|
|
&& nPart != ControlPart::ButtonLeft && nPart != ControlPart::ButtonRight)
|
|
{ // we adjust only for buttons (because some scrollbars have 3 buttons,
|
|
// and LO core doesn't handle such scrollbars well)
|
|
return false;
|
|
}
|
|
rIsInside = false;
|
|
bool bHorizontal = (nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight);
|
|
QRect rect = toQRect(rControlRegion);
|
|
QPoint pos(rPos.X(), rPos.Y());
|
|
// Adjust coordinates to make the widget appear to be at (0,0), i.e. make
|
|
// widget and screen coordinates the same. QStyle functions should use screen
|
|
// coordinates but at least QPlastiqueStyle::subControlRect() is buggy
|
|
// and sometimes uses widget coordinates.
|
|
pos -= rect.topLeft();
|
|
rect.moveTo(0, 0);
|
|
QStyleOptionSlider options;
|
|
options.orientation = bHorizontal ? Qt::Horizontal : Qt::Vertical;
|
|
if (bHorizontal)
|
|
options.state |= QStyle::State_Horizontal;
|
|
options.rect = rect;
|
|
// some random sensible values, since we call this code only for scrollbar buttons,
|
|
// the slider position does not exactly matter
|
|
options.maximum = 10;
|
|
options.minimum = 0;
|
|
options.sliderPosition = options.sliderValue = 4;
|
|
options.pageStep = 2;
|
|
QStyle::SubControl control
|
|
= QApplication::style()->hitTestComplexControl(QStyle::CC_ScrollBar, &options, pos);
|
|
if (nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonLeft)
|
|
rIsInside = (control == QStyle::SC_ScrollBarSubLine);
|
|
else // DOWN, RIGHT
|
|
rIsInside = (control == QStyle::SC_ScrollBarAddLine);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline int QtGraphics_Controls::downscale(int size, Round eRound)
|
|
{
|
|
return static_cast<int>(eRound == Round::Ceil ? ceil(size / m_rGraphics.devicePixelRatioF())
|
|
: floor(size / m_rGraphics.devicePixelRatioF()));
|
|
}
|
|
|
|
inline int QtGraphics_Controls::upscale(int size, Round eRound)
|
|
{
|
|
return static_cast<int>(eRound == Round::Ceil ? ceil(size * m_rGraphics.devicePixelRatioF())
|
|
: floor(size * m_rGraphics.devicePixelRatioF()));
|
|
}
|
|
|
|
inline QRect QtGraphics_Controls::downscale(const QRect& rect)
|
|
{
|
|
return QRect(downscale(rect.x(), Round::Floor), downscale(rect.y(), Round::Floor),
|
|
downscale(rect.width(), Round::Ceil), downscale(rect.height(), Round::Ceil));
|
|
}
|
|
|
|
inline QRect QtGraphics_Controls::upscale(const QRect& rect)
|
|
{
|
|
return QRect(upscale(rect.x(), Round::Floor), upscale(rect.y(), Round::Floor),
|
|
upscale(rect.width(), Round::Ceil), upscale(rect.height(), Round::Ceil));
|
|
}
|
|
|
|
inline QSize QtGraphics_Controls::downscale(const QSize& size, Round eRound)
|
|
{
|
|
return QSize(downscale(size.width(), eRound), downscale(size.height(), eRound));
|
|
}
|
|
|
|
inline QSize QtGraphics_Controls::upscale(const QSize& size, Round eRound)
|
|
{
|
|
return QSize(upscale(size.width(), eRound), upscale(size.height(), eRound));
|
|
}
|
|
|
|
inline QPoint QtGraphics_Controls::upscale(const QPoint& point, Round eRound)
|
|
{
|
|
return QPoint(upscale(point.x(), eRound), upscale(point.y(), eRound));
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|