1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <Qt5Graphics_Controls.hxx>
22 #include <QtGui/QPainter>
23 #include <QtWidgets/QApplication>
24 #include <QtWidgets/QStyle>
25 #include <QtWidgets/QStyleOption>
26 #include <QtWidgets/QFrame>
27 #include <QtWidgets/QLabel>
29 #include <qt5/Qt5Tools.hxx>
30 #include <vcl/decoview.hxx>
33 Conversion function between VCL ControlState together with
34 ImplControlValue and Qt state flags.
35 @param nControlState State of the widget (default, focused, ...) in Native Widget Framework.
36 @param aValue Value held by the widget (on, off, ...)
38 static QStyle::State
vclStateValue2StateFlag(ControlState nControlState
,
39 const ImplControlValue
& aValue
)
42 = ((nControlState
& ControlState::ENABLED
) ? QStyle::State_Enabled
: QStyle::State_None
)
43 | ((nControlState
& ControlState::FOCUSED
) ? QStyle::State_HasFocus
: QStyle::State_None
)
44 | ((nControlState
& ControlState::PRESSED
) ? QStyle::State_Sunken
: QStyle::State_None
)
45 | ((nControlState
& ControlState::SELECTED
) ? QStyle::State_Selected
: QStyle::State_None
)
46 | ((nControlState
& ControlState::ROLLOVER
) ? QStyle::State_MouseOver
47 : QStyle::State_None
);
49 switch (aValue
.getTristateVal())
52 nState
|= QStyle::State_On
;
54 case ButtonValue::Off
:
55 nState
|= QStyle::State_Off
;
57 case ButtonValue::Mixed
:
58 nState
|= QStyle::State_NoChange
;
67 Qt5Graphics_Controls::Qt5Graphics_Controls() {}
69 bool Qt5Graphics_Controls::isNativeControlSupported(ControlType type
, ControlPart part
)
73 case ControlType::Tooltip
:
74 case ControlType::Progress
:
75 case ControlType::ListNode
:
76 return (part
== ControlPart::Entire
);
78 case ControlType::Radiobutton
:
79 case ControlType::Checkbox
:
80 return (part
== ControlPart::Entire
) || (part
== ControlPart::Focus
);
81 case ControlType::Pushbutton
:
82 return (part
== ControlPart::Entire
);
84 case ControlType::ListHeader
:
85 return (part
== ControlPart::Button
);
87 case ControlType::Menubar
:
88 case ControlType::MenuPopup
:
89 case ControlType::Editbox
:
90 case ControlType::MultilineEditbox
:
91 case ControlType::Combobox
:
92 case ControlType::Toolbar
:
93 case ControlType::Frame
:
94 case ControlType::Scrollbar
:
95 case ControlType::WindowBackground
:
96 case ControlType::Fixedline
:
99 case ControlType::Listbox
:
100 return (part
== ControlPart::Entire
|| part
== ControlPart::HasBackgroundTexture
);
102 case ControlType::Spinbox
:
103 return (part
== ControlPart::Entire
|| part
== ControlPart::HasBackgroundTexture
);
105 case ControlType::Slider
:
106 return (part
== ControlPart::TrackHorzArea
|| part
== ControlPart::TrackVertArea
);
108 case ControlType::TabItem
:
109 case ControlType::TabPane
:
110 return ((part
== ControlPart::Entire
) || part
== ControlPart::TabPaneWithHeader
);
121 void draw(QStyle::ControlElement element
, QStyleOption
* option
, QImage
* image
,
122 QStyle::State
const state
= QStyle::State_None
, QRect rect
= QRect())
124 option
->state
|= state
;
125 option
->rect
= !rect
.isNull() ? rect
: image
->rect();
127 QPainter
painter(image
);
128 QApplication::style()->drawControl(element
, option
, &painter
);
131 void draw(QStyle::PrimitiveElement element
, QStyleOption
* option
, QImage
* image
,
132 QStyle::State
const state
= QStyle::State_None
, QRect rect
= QRect())
134 option
->state
|= state
;
135 option
->rect
= !rect
.isNull() ? rect
: image
->rect();
137 QPainter
painter(image
);
138 QApplication::style()->drawPrimitive(element
, option
, &painter
);
141 void draw(QStyle::ComplexControl element
, QStyleOptionComplex
* option
, QImage
* image
,
142 QStyle::State
const state
= QStyle::State_None
)
144 option
->state
|= state
;
145 option
->rect
= image
->rect();
147 QPainter
painter(image
);
148 QApplication::style()->drawComplexControl(element
, option
, &painter
);
151 void lcl_drawFrame(QStyle::PrimitiveElement element
, QImage
* image
, QStyle::State
const& state
,
153 QStyle::PixelMetric eLineMetric
= QStyle::PM_DefaultFrameWidth
)
155 const int fw
= QApplication::style()->pixelMetric(eLineMetric
);
156 QStyleOptionFrame option
;
157 option
.frameShape
= QFrame::StyledPanel
;
158 option
.state
= QStyle::State_Sunken
| state
;
159 option
.lineWidth
= fw
;
161 QRect
aRect(image
->rect());
164 QPainter
painter(image
);
166 painter
.setClipRegion(QRegion(aRect
).subtracted(aRect
.adjusted(fw
, fw
, -fw
, -fw
)));
167 QApplication::style()->drawPrimitive(element
, &option
, &painter
);
170 void lcl_fillQStyleOptionTab(const ImplControlValue
& value
, QStyleOptionTab
& sot
)
172 const TabitemValue
& rValue
= static_cast<const TabitemValue
&>(value
);
173 if (rValue
.isFirst())
174 sot
.position
= rValue
.isLast() ? QStyleOptionTab::OnlyOneTab
: QStyleOptionTab::Beginning
;
175 else if (rValue
.isLast())
176 sot
.position
= rValue
.isFirst() ? QStyleOptionTab::OnlyOneTab
: QStyleOptionTab::End
;
178 sot
.position
= QStyleOptionTab::Middle
;
181 void lcl_fullQStyleOptionTabWidgetFrame(QStyleOptionTabWidgetFrame
& option
)
183 option
.state
= QStyle::State_Enabled
;
184 option
.rightCornerWidgetSize
= QSize(0, 0);
185 option
.leftCornerWidgetSize
= QSize(0, 0);
186 option
.lineWidth
= QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth
);
187 option
.midLineWidth
= 0;
188 option
.shape
= QTabBar::RoundedNorth
;
192 bool Qt5Graphics_Controls::drawNativeControl(ControlType type
, ControlPart part
,
193 const tools::Rectangle
& rControlRegion
,
194 ControlState nControlState
,
195 const ImplControlValue
& value
, const OUString
&)
197 bool nativeSupport
= isNativeControlSupported(type
, part
);
200 assert(!nativeSupport
&& "drawNativeControl called without native support!");
204 if (m_lastPopupRect
.isValid()
205 && (type
!= ControlType::MenuPopup
|| part
!= ControlPart::MenuItem
))
206 m_lastPopupRect
= QRect();
208 bool returnVal
= true;
210 QRect widgetRect
= toQRect(rControlRegion
);
212 //if no image, or resized, make a new image
213 if (!m_image
|| m_image
->size() != widgetRect
.size())
215 m_image
.reset(new QImage(widgetRect
.width(), widgetRect
.height(),
216 QImage::Format_ARGB32_Premultiplied
));
219 // Default image color - just once
222 case ControlType::MenuPopup
:
223 if (part
== ControlPart::MenuItemCheckMark
|| part
== ControlPart::MenuItemRadioMark
)
225 // it is necessary to fill the background transparently first, as this
226 // is painted after menuitem highlight, otherwise there would be a grey area
227 m_image
->fill(Qt::transparent
);
230 [[fallthrough
]]; // QPalette::Window
231 case ControlType::Menubar
:
232 case ControlType::WindowBackground
:
233 m_image
->fill(QApplication::palette().color(QPalette::Window
).rgb());
235 case ControlType::Tooltip
:
236 m_image
->fill(QApplication::palette().color(QPalette::ToolTipBase
).rgb());
238 case ControlType::Scrollbar
:
239 if ((part
== ControlPart::DrawBackgroundVert
)
240 || (part
== ControlPart::DrawBackgroundHorz
))
242 m_image
->fill(QApplication::palette().color(QPalette::Window
).rgb());
245 [[fallthrough
]]; // Qt::transparent
247 m_image
->fill(Qt::transparent
);
251 if (type
== ControlType::Pushbutton
)
253 assert(part
== ControlPart::Entire
);
254 QStyleOptionButton option
;
255 draw(QStyle::CE_PushButton
, &option
, m_image
.get(),
256 vclStateValue2StateFlag(nControlState
, value
));
258 else if (type
== ControlType::Menubar
)
260 if (part
== ControlPart::MenuItem
)
262 QStyleOptionMenuItem option
;
263 option
.state
= vclStateValue2StateFlag(nControlState
, value
);
264 if ((nControlState
& ControlState::ROLLOVER
)
265 && QApplication::style()->styleHint(QStyle::SH_MenuBar_MouseTracking
))
266 option
.state
|= QStyle::State_Selected
;
269 & ControlState::SELECTED
) // Passing State_Sunken is currently not documented.
270 option
.state
|= QStyle::State_Sunken
; // But some kinds of QStyle interpret it.
272 draw(QStyle::CE_MenuBarItem
, &option
, m_image
.get());
274 else if (part
== ControlPart::Entire
)
276 QStyleOptionMenuItem option
;
277 draw(QStyle::CE_MenuBarEmptyArea
, &option
, m_image
.get(),
278 vclStateValue2StateFlag(nControlState
, value
));
285 else if (type
== ControlType::MenuPopup
)
287 assert(part
== ControlPart::MenuItem
? m_lastPopupRect
.isValid()
288 : !m_lastPopupRect
.isValid());
289 if (part
== ControlPart::MenuItem
)
291 QStyleOptionMenuItem option
;
292 draw(QStyle::CE_MenuItem
, &option
, m_image
.get(),
293 vclStateValue2StateFlag(nControlState
, value
));
294 // HACK: LO core first paints the entire popup and only then it paints menu items,
295 // but QMenu::paintEvent() paints popup frame after all items. That means highlighted
296 // items here would paint the highlight over the frame border. Since calls to ControlPart::MenuItem
297 // are always preceded by calls to ControlPart::Entire, just remember the size for the whole
298 // popup (otherwise not possible to get here) and draw the border afterwards.
299 QRect
framerect(m_lastPopupRect
.topLeft() - widgetRect
.topLeft(),
300 widgetRect
.size().expandedTo(m_lastPopupRect
.size()));
301 QStyleOptionFrame frame
;
302 draw(QStyle::PE_FrameMenu
, &frame
, m_image
.get(),
303 vclStateValue2StateFlag(nControlState
, value
), framerect
);
305 else if (part
== ControlPart::Separator
)
307 QStyleOptionMenuItem option
;
308 option
.menuItemType
= QStyleOptionMenuItem::Separator
;
309 // Painting the whole menu item area results in different background
310 // with at least Plastique style, so clip only to the separator itself
311 // (QSize( 2, 2 ) is hardcoded in Qt)
312 option
.rect
= m_image
->rect();
313 QSize size
= QApplication::style()->sizeFromContents(QStyle::CT_MenuItem
, &option
,
315 QRect rect
= m_image
->rect();
316 QPoint center
= rect
.center();
317 rect
.setHeight(size
.height());
318 rect
.moveCenter(center
);
319 option
.state
|= vclStateValue2StateFlag(nControlState
, value
);
322 QPainter
painter(m_image
.get());
323 // don't paint over popup frame border (like the hack above, but here it can be simpler)
324 const int fw
= QApplication::style()->pixelMetric(QStyle::PM_MenuPanelWidth
);
325 painter
.setClipRect(rect
.adjusted(fw
, 0, -fw
, 0));
326 QApplication::style()->drawControl(QStyle::CE_MenuItem
, &option
, &painter
);
328 else if (part
== ControlPart::MenuItemCheckMark
|| part
== ControlPart::MenuItemRadioMark
)
330 QStyleOptionMenuItem option
;
331 option
.checkType
= (part
== ControlPart::MenuItemCheckMark
)
332 ? QStyleOptionMenuItem::NonExclusive
333 : QStyleOptionMenuItem::Exclusive
;
334 option
.checked
= bool(nControlState
& ControlState::PRESSED
);
335 // widgetRect is now the rectangle for the checkbox/radiobutton itself, but Qt
336 // paints the whole menu item, so translate position (and it'll be clipped);
337 // it is also necessary to fill the background transparently first, as this
338 // is painted after menuitem highlight, otherwise there would be a grey area
339 assert(value
.getType() == ControlType::MenuPopup
);
340 const MenupopupValue
* menuVal
= static_cast<const MenupopupValue
*>(&value
);
341 QRect
menuItemRect(toQRect(menuVal
->maItemRect
));
342 QRect
rect(menuItemRect
.topLeft() - widgetRect
.topLeft(),
343 widgetRect
.size().expandedTo(menuItemRect
.size()));
344 // checkboxes are always displayed next to images in menus, so are never centered
345 const int focus_size
= QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin
);
346 rect
.moveTo(-focus_size
, rect
.y());
347 draw(QStyle::CE_MenuItem
, &option
, m_image
.get(),
348 vclStateValue2StateFlag(nControlState
& ~ControlState::PRESSED
, value
), rect
);
350 else if (part
== ControlPart::Entire
)
352 QStyleOptionMenuItem option
;
353 option
.state
= vclStateValue2StateFlag(nControlState
, value
);
354 draw(QStyle::PE_PanelMenu
, &option
, m_image
.get());
355 // Try hard to get any frame!
356 QStyleOptionFrame frame
;
357 draw(QStyle::PE_FrameMenu
, &frame
, m_image
.get());
358 draw(QStyle::PE_FrameWindow
, &frame
, m_image
.get());
359 m_lastPopupRect
= widgetRect
;
364 else if ((type
== ControlType::Toolbar
) && (part
== ControlPart::Button
))
366 QStyleOptionToolButton option
;
368 option
.arrowType
= Qt::NoArrow
;
369 option
.subControls
= QStyle::SC_ToolButton
;
370 option
.state
= vclStateValue2StateFlag(nControlState
, value
);
371 option
.state
|= QStyle::State_Raised
| QStyle::State_Enabled
| QStyle::State_AutoRaise
;
373 draw(QStyle::CC_ToolButton
, &option
, m_image
.get());
375 else if ((type
== ControlType::Toolbar
) && (part
== ControlPart::Entire
))
377 QStyleOptionToolBar option
;
378 draw(QStyle::CE_ToolBar
, &option
, m_image
.get(),
379 vclStateValue2StateFlag(nControlState
, value
));
381 else if ((type
== ControlType::Toolbar
)
382 && (part
== ControlPart::ThumbVert
|| part
== ControlPart::ThumbHorz
))
384 // reduce paint area only to the handle area
385 const int handleExtend
= QApplication::style()->pixelMetric(QStyle::PM_ToolBarHandleExtent
);
387 QRect aRect
= m_image
->rect();
388 if (part
== ControlPart::ThumbVert
)
390 aRect
.setWidth(handleExtend
);
391 option
.state
= QStyle::State_Horizontal
;
394 aRect
.setHeight(handleExtend
);
395 draw(QStyle::PE_IndicatorToolBarHandle
, &option
, m_image
.get(),
396 vclStateValue2StateFlag(nControlState
, value
), aRect
);
398 else if (type
== ControlType::Editbox
|| type
== ControlType::MultilineEditbox
)
400 lcl_drawFrame(QStyle::PE_FrameLineEdit
, m_image
.get(),
401 vclStateValue2StateFlag(nControlState
, value
), false);
403 else if (type
== ControlType::Combobox
)
405 QStyleOptionComboBox option
;
406 option
.editable
= true;
407 draw(QStyle::CC_ComboBox
, &option
, m_image
.get(),
408 vclStateValue2StateFlag(nControlState
, value
));
410 else if (type
== ControlType::Listbox
)
412 QStyleOptionComboBox option
;
413 option
.editable
= false;
416 case ControlPart::ListboxWindow
:
417 lcl_drawFrame(QStyle::PE_Frame
, m_image
.get(),
418 vclStateValue2StateFlag(nControlState
, value
), true,
419 QStyle::PM_ComboBoxFrameWidth
);
421 case ControlPart::SubEdit
:
422 draw(QStyle::CE_ComboBoxLabel
, &option
, m_image
.get(),
423 vclStateValue2StateFlag(nControlState
, value
));
425 case ControlPart::Entire
:
426 draw(QStyle::CC_ComboBox
, &option
, m_image
.get(),
427 vclStateValue2StateFlag(nControlState
, value
));
429 case ControlPart::ButtonDown
:
430 option
.subControls
= QStyle::SC_ComboBoxArrow
;
431 draw(QStyle::CC_ComboBox
, &option
, m_image
.get(),
432 vclStateValue2StateFlag(nControlState
, value
));
439 else if (type
== ControlType::ListNode
)
442 option
.state
= vclStateValue2StateFlag(nControlState
, value
);
443 option
.state
|= QStyle::State_Item
| QStyle::State_Children
;
445 if (value
.getTristateVal() == ButtonValue::On
)
446 option
.state
|= QStyle::State_Open
;
448 draw(QStyle::PE_IndicatorBranch
, &option
, m_image
.get());
450 else if (type
== ControlType::ListHeader
)
452 QStyleOptionHeader option
;
453 draw(QStyle::CE_HeaderSection
, &option
, m_image
.get(),
454 vclStateValue2StateFlag(nControlState
, value
));
456 else if (type
== ControlType::Checkbox
)
458 if (part
== ControlPart::Entire
)
460 QStyleOptionButton option
;
461 // clear FOCUSED bit, focus is drawn separately
462 nControlState
&= ~ControlState::FOCUSED
;
463 draw(QStyle::CE_CheckBox
, &option
, m_image
.get(),
464 vclStateValue2StateFlag(nControlState
, value
));
466 else if (part
== ControlPart::Focus
)
468 QStyleOptionFocusRect option
;
469 draw(QStyle::PE_FrameFocusRect
, &option
, m_image
.get(),
470 vclStateValue2StateFlag(nControlState
, value
));
473 else if (type
== ControlType::Scrollbar
)
475 if ((part
== ControlPart::DrawBackgroundVert
) || (part
== ControlPart::DrawBackgroundHorz
))
477 QStyleOptionSlider option
;
478 assert(value
.getType() == ControlType::Scrollbar
);
479 const ScrollbarValue
* sbVal
= static_cast<const ScrollbarValue
*>(&value
);
481 //if the scroll bar is active (aka not degenerate... allow for hover events)
482 if (sbVal
->mnVisibleSize
< sbVal
->mnMax
)
483 option
.state
= QStyle::State_MouseOver
;
485 bool horizontal
= (part
== ControlPart::DrawBackgroundHorz
); //horizontal or vertical
486 option
.orientation
= horizontal
? Qt::Horizontal
: Qt::Vertical
;
488 option
.state
|= QStyle::State_Horizontal
;
490 // If the scrollbar has a mnMin == 0 and mnMax == 0 then mnVisibleSize is set to -1?!
491 // I don't know if a negative mnVisibleSize makes any sense, so just handle this case
492 // without crashing LO with a SIGFPE in the Qt library.
493 const long nVisibleSize
= (sbVal
->mnMin
== sbVal
->mnMax
) ? 0 : sbVal
->mnVisibleSize
;
495 option
.minimum
= sbVal
->mnMin
;
496 option
.maximum
= sbVal
->mnMax
- nVisibleSize
;
497 option
.maximum
= qMax(option
.maximum
, option
.minimum
); // bnc#619772
498 option
.sliderValue
= sbVal
->mnCur
;
499 option
.sliderPosition
= sbVal
->mnCur
;
500 option
.pageStep
= nVisibleSize
;
501 if (part
== ControlPart::DrawBackgroundHorz
)
503 = (QGuiApplication::isRightToLeft()
504 && sbVal
->maButton1Rect
.Left() < sbVal
->maButton2Rect
.Left())
505 || (QGuiApplication::isLeftToRight()
506 && sbVal
->maButton1Rect
.Left() > sbVal
->maButton2Rect
.Left());
508 //setup the active control... always the slider
509 if (sbVal
->mnThumbState
& ControlState::ROLLOVER
)
510 option
.activeSubControls
= QStyle::SC_ScrollBarSlider
;
512 draw(QStyle::CC_ScrollBar
, &option
, m_image
.get(),
513 vclStateValue2StateFlag(nControlState
, value
));
520 else if (type
== ControlType::Spinbox
)
522 QStyleOptionSpinBox option
;
525 // determine active control
526 if (value
.getType() == ControlType::SpinButtons
)
528 const SpinbuttonValue
* pSpinVal
= static_cast<const SpinbuttonValue
*>(&value
);
529 if (pSpinVal
->mnUpperState
& ControlState::PRESSED
)
530 option
.activeSubControls
|= QStyle::SC_SpinBoxUp
;
531 if (pSpinVal
->mnLowerState
& ControlState::PRESSED
)
532 option
.activeSubControls
|= QStyle::SC_SpinBoxDown
;
533 if (pSpinVal
->mnUpperState
& ControlState::ENABLED
)
534 option
.stepEnabled
|= QAbstractSpinBox::StepUpEnabled
;
535 if (pSpinVal
->mnLowerState
& ControlState::ENABLED
)
536 option
.stepEnabled
|= QAbstractSpinBox::StepDownEnabled
;
537 if (pSpinVal
->mnUpperState
& ControlState::ROLLOVER
)
538 option
.state
= QStyle::State_MouseOver
;
539 if (pSpinVal
->mnLowerState
& ControlState::ROLLOVER
)
540 option
.state
= QStyle::State_MouseOver
;
543 draw(QStyle::CC_SpinBox
, &option
, m_image
.get(),
544 vclStateValue2StateFlag(nControlState
, value
));
546 else if (type
== ControlType::Radiobutton
)
548 if (part
== ControlPart::Entire
)
550 QStyleOptionButton option
;
551 // clear FOCUSED bit, focus is drawn separately
552 nControlState
&= ~ControlState::FOCUSED
;
553 draw(QStyle::CE_RadioButton
, &option
, m_image
.get(),
554 vclStateValue2StateFlag(nControlState
, value
));
556 else if (part
== ControlPart::Focus
)
558 QStyleOptionFocusRect option
;
559 draw(QStyle::PE_FrameFocusRect
, &option
, m_image
.get(),
560 vclStateValue2StateFlag(nControlState
, value
));
563 else if (type
== ControlType::Tooltip
)
566 draw(QStyle::PE_PanelTipLabel
, &option
, m_image
.get(),
567 vclStateValue2StateFlag(nControlState
, value
));
569 else if (type
== ControlType::Frame
)
571 lcl_drawFrame(QStyle::PE_Frame
, m_image
.get(),
572 vclStateValue2StateFlag(nControlState
, value
));
574 else if (type
== ControlType::WindowBackground
)
576 // Nothing to do - see "Default image color" switch ^^
578 else if (type
== ControlType::Fixedline
)
580 QStyleOptionMenuItem option
;
581 option
.menuItemType
= QStyleOptionMenuItem::Separator
;
582 option
.state
= vclStateValue2StateFlag(nControlState
, value
);
583 option
.state
|= QStyle::State_Item
;
585 draw(QStyle::CE_MenuItem
, &option
, m_image
.get());
587 else if (type
== ControlType::Slider
588 && (part
== ControlPart::TrackHorzArea
|| part
== ControlPart::TrackVertArea
))
590 assert(value
.getType() == ControlType::Slider
);
591 const SliderValue
* slVal
= static_cast<const SliderValue
*>(&value
);
592 QStyleOptionSlider option
;
594 option
.state
= vclStateValue2StateFlag(nControlState
, value
);
595 option
.maximum
= slVal
->mnMax
;
596 option
.minimum
= slVal
->mnMin
;
597 option
.sliderPosition
= option
.sliderValue
= slVal
->mnCur
;
598 bool horizontal
= (part
== ControlPart::TrackHorzArea
); //horizontal or vertical
599 option
.orientation
= horizontal
? Qt::Horizontal
: Qt::Vertical
;
601 option
.state
|= QStyle::State_Horizontal
;
603 draw(QStyle::CC_Slider
, &option
, m_image
.get());
605 else if (type
== ControlType::Progress
&& part
== ControlPart::Entire
)
607 SAL_WNODEPRECATED_DECLARATIONS_PUSH
608 QStyleOptionProgressBarV2 option
;
609 SAL_WNODEPRECATED_DECLARATIONS_POP
611 option
.maximum
= widgetRect
.width();
612 option
.progress
= value
.getNumericVal();
614 draw(QStyle::CE_ProgressBar
, &option
, m_image
.get(),
615 vclStateValue2StateFlag(nControlState
, value
));
617 else if (type
== ControlType::TabItem
&& part
== ControlPart::Entire
)
620 lcl_fillQStyleOptionTab(value
, sot
);
621 draw(QStyle::CE_TabBarTabShape
, &sot
, m_image
.get(),
622 vclStateValue2StateFlag(nControlState
, value
));
624 else if (type
== ControlType::TabPane
&& part
== ControlPart::Entire
)
626 const TabPaneValue
& rValue
= static_cast<const TabPaneValue
&>(value
);
628 // get the overlap size for the tabs, so they will overlap the frame
629 QStyleOptionTab tabOverlap
;
630 tabOverlap
.shape
= QTabBar::RoundedNorth
;
631 TabPaneValue::m_nOverlap
632 = QApplication::style()->pixelMetric(QStyle::PM_TabBarBaseOverlap
, &tabOverlap
);
634 QStyleOptionTabWidgetFrame option
;
635 lcl_fullQStyleOptionTabWidgetFrame(option
);
636 option
.tabBarRect
= toQRect(rValue
.m_aTabHeaderRect
);
637 option
.selectedTabRect
638 = rValue
.m_aSelectedTabRect
.IsEmpty() ? QRect() : toQRect(rValue
.m_aSelectedTabRect
);
639 option
.tabBarSize
= toQSize(rValue
.m_aTabHeaderRect
.GetSize());
640 option
.rect
= m_image
->rect();
641 QRect aRect
= QApplication::style()->subElementRect(QStyle::SE_TabWidgetTabPane
, &option
);
642 draw(QStyle::PE_FrameTabWidget
, &option
, m_image
.get(),
643 vclStateValue2StateFlag(nControlState
, value
), aRect
);
653 bool Qt5Graphics_Controls::getNativeControlRegion(ControlType type
, ControlPart part
,
654 const tools::Rectangle
& controlRegion
,
655 ControlState controlState
,
656 const ImplControlValue
& val
, const OUString
&,
657 tools::Rectangle
& nativeBoundingRegion
,
658 tools::Rectangle
& nativeContentRegion
)
662 QRect boundingRect
= toQRect(controlRegion
);
663 QRect contentRect
= boundingRect
;
664 QStyleOptionComplex styleOption
;
668 // Metrics of the push button
669 case ControlType::Pushbutton
:
670 if (part
== ControlPart::Entire
)
672 styleOption
.state
= vclStateValue2StateFlag(controlState
, val
);
674 if (controlState
& ControlState::DEFAULT
)
676 int size
= QApplication::style()->pixelMetric(QStyle::PM_ButtonDefaultIndicator
,
678 boundingRect
.adjust(-size
, -size
, size
, size
);
683 case ControlType::Editbox
:
684 case ControlType::MultilineEditbox
:
686 QStyleOptionFrame fo
;
687 fo
.frameShape
= QFrame::StyledPanel
;
688 fo
.state
= QStyle::State_Sunken
;
689 fo
.lineWidth
= QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth
);
690 QSize aMinSize
= QApplication::style()->sizeFromContents(QStyle::CT_LineEdit
, &fo
,
692 if (aMinSize
.height() > boundingRect
.height())
694 int nHeight
= (aMinSize
.height() - boundingRect
.height()) / 2;
695 assert(0 == (aMinSize
.height() - boundingRect
.height()) % 2);
696 boundingRect
.adjust(0, -nHeight
, 0, nHeight
);
698 if (aMinSize
.width() > boundingRect
.width())
700 int nWidth
= (aMinSize
.width() - boundingRect
.width()) / 2;
701 assert(0 == (aMinSize
.width() - boundingRect
.width()) % 2);
702 boundingRect
.adjust(-nWidth
, 0, nWidth
, 0);
707 case ControlType::Checkbox
:
708 if (part
== ControlPart::Entire
)
710 styleOption
.state
= vclStateValue2StateFlag(controlState
, val
);
712 contentRect
.setWidth(
713 QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth
, &styleOption
));
714 contentRect
.setHeight(
715 QApplication::style()->pixelMetric(QStyle::PM_IndicatorHeight
, &styleOption
));
717 contentRect
.adjust(0, 0,
719 * QApplication::style()->pixelMetric(
720 QStyle::PM_FocusFrameHMargin
, &styleOption
),
722 * QApplication::style()->pixelMetric(
723 QStyle::PM_FocusFrameVMargin
, &styleOption
));
725 boundingRect
= contentRect
;
730 case ControlType::Combobox
:
731 case ControlType::Listbox
:
733 QStyleOptionComboBox cbo
;
735 cbo
.rect
= QRect(0, 0, contentRect
.width(), contentRect
.height());
736 cbo
.state
= vclStateValue2StateFlag(controlState
, val
);
740 case ControlPart::Entire
:
742 // find out the minimum size that should be used
743 // assume contents is a text line
744 int nHeight
= QApplication::fontMetrics().height();
745 QSize
aContentSize(contentRect
.width(), nHeight
);
746 QSize aMinSize
= QApplication::style()->sizeFromContents(QStyle::CT_ComboBox
,
748 if (aMinSize
.height() > contentRect
.height())
749 contentRect
.adjust(0, 0, 0, aMinSize
.height() - contentRect
.height());
750 boundingRect
= contentRect
;
754 case ControlPart::ButtonDown
:
755 contentRect
= QApplication::style()->subControlRect(QStyle::CC_ComboBox
, &cbo
,
756 QStyle::SC_ComboBoxArrow
);
757 contentRect
.translate(boundingRect
.left(), boundingRect
.top());
760 case ControlPart::SubEdit
:
762 contentRect
= QApplication::style()->subControlRect(
763 QStyle::CC_ComboBox
, &cbo
, QStyle::SC_ComboBoxEditField
);
764 contentRect
.translate(boundingRect
.left(), boundingRect
.top());
773 case ControlType::Spinbox
:
775 QStyleOptionSpinBox sbo
;
778 sbo
.rect
= QRect(0, 0, contentRect
.width(), contentRect
.height());
779 sbo
.state
= vclStateValue2StateFlag(controlState
, val
);
783 case ControlPart::Entire
:
785 int nHeight
= QApplication::fontMetrics().height();
786 QSize
aContentSize(contentRect
.width(), nHeight
);
787 QSize aMinSize
= QApplication::style()->sizeFromContents(QStyle::CT_SpinBox
,
789 if (aMinSize
.height() > contentRect
.height())
790 contentRect
.adjust(0, 0, 0, aMinSize
.height() - contentRect
.height());
791 boundingRect
= contentRect
;
795 case ControlPart::ButtonUp
:
796 contentRect
= QApplication::style()->subControlRect(QStyle::CC_SpinBox
, &sbo
,
797 QStyle::SC_SpinBoxUp
);
798 contentRect
.translate(boundingRect
.left(), boundingRect
.top());
800 boundingRect
= QRect();
803 case ControlPart::ButtonDown
:
804 contentRect
= QApplication::style()->subControlRect(QStyle::CC_SpinBox
, &sbo
,
805 QStyle::SC_SpinBoxDown
);
807 contentRect
.translate(boundingRect
.left(), boundingRect
.top());
808 boundingRect
= QRect();
811 case ControlPart::SubEdit
:
812 contentRect
= QApplication::style()->subControlRect(
813 QStyle::CC_SpinBox
, &sbo
, QStyle::SC_SpinBoxEditField
);
815 contentRect
.translate(boundingRect
.left(), boundingRect
.top());
822 case ControlType::MenuPopup
:
827 case ControlPart::MenuItemCheckMark
:
828 h
= QApplication::style()->pixelMetric(QStyle::PM_IndicatorHeight
);
829 w
= QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth
);
832 case ControlPart::MenuItemRadioMark
:
833 h
= QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight
);
834 w
= QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth
);
842 contentRect
= QRect(0, 0, w
, h
);
843 boundingRect
= contentRect
;
847 case ControlType::Frame
:
849 if (part
== ControlPart::Border
)
851 auto nStyle
= static_cast<DrawFrameFlags
>(val
.getNumericVal() & 0xFFF0);
852 if (nStyle
& DrawFrameFlags::NoDraw
)
854 const int nFrameWidth
855 = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth
);
856 contentRect
.adjust(nFrameWidth
, nFrameWidth
, -nFrameWidth
, -nFrameWidth
);
862 case ControlType::Radiobutton
:
864 const int h
= QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight
);
865 const int w
= QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth
);
867 contentRect
= QRect(boundingRect
.left(), boundingRect
.top(), w
, h
);
870 2 * QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin
, &styleOption
),
871 2 * QApplication::style()->pixelMetric(QStyle::PM_FocusFrameVMargin
, &styleOption
));
872 boundingRect
= contentRect
;
877 case ControlType::Slider
:
879 const int w
= QApplication::style()->pixelMetric(QStyle::PM_SliderLength
);
880 if (part
== ControlPart::ThumbHorz
)
883 = QRect(boundingRect
.left(), boundingRect
.top(), w
, boundingRect
.height());
884 boundingRect
= contentRect
;
887 else if (part
== ControlPart::ThumbVert
)
890 = QRect(boundingRect
.left(), boundingRect
.top(), boundingRect
.width(), w
);
891 boundingRect
= contentRect
;
896 case ControlType::Toolbar
:
898 const int nWorH
= QApplication::style()->pixelMetric(QStyle::PM_ToolBarHandleExtent
);
899 if (part
== ControlPart::ThumbHorz
)
902 = QRect(boundingRect
.left(), boundingRect
.top(), boundingRect
.width(), nWorH
);
903 boundingRect
= contentRect
;
906 else if (part
== ControlPart::ThumbVert
)
909 = QRect(boundingRect
.left(), boundingRect
.top(), nWorH
, boundingRect
.height());
910 boundingRect
= contentRect
;
915 case ControlType::Scrollbar
:
917 // core can't handle 3-button scrollbars well, so we fix that in hitTestNativeControl(),
918 // for the rest also provide the track area (i.e. area not taken by buttons)
919 if (part
== ControlPart::TrackVertArea
|| part
== ControlPart::TrackHorzArea
)
921 QStyleOptionSlider option
;
922 bool horizontal
= (part
== ControlPart::TrackHorzArea
); //horizontal or vertical
923 option
.orientation
= horizontal
? Qt::Horizontal
: Qt::Vertical
;
925 option
.state
|= QStyle::State_Horizontal
;
926 // getNativeControlRegion usually gets ImplControlValue as 'val' (i.e. not the proper
927 // subclass), so use random sensible values (doesn't matter anyway, as the wanted
928 // geometry here depends only on button sizes)
931 option
.sliderPosition
= option
.sliderValue
= 4;
933 // Adjust coordinates to make the widget appear to be at (0,0), i.e. make
934 // widget and screen coordinates the same. QStyle functions should use screen
935 // coordinates but at least QPlastiqueStyle::subControlRect() is buggy
936 // and sometimes uses widget coordinates.
937 QRect rect
= contentRect
;
940 rect
= QApplication::style()->subControlRect(QStyle::CC_ScrollBar
, &option
,
941 QStyle::SC_ScrollBarGroove
);
942 rect
.translate(contentRect
.topLeft()); // reverse the workaround above
943 contentRect
= boundingRect
= rect
;
948 case ControlType::TabItem
:
951 lcl_fillQStyleOptionTab(val
, sot
);
952 QSize aMinSize
= QApplication::style()->sizeFromContents(QStyle::CT_TabBarTab
, &sot
,
954 contentRect
.setSize(aMinSize
);
955 boundingRect
= contentRect
;
959 case ControlType::TabPane
:
961 const TabPaneValue
& rValue
= static_cast<const TabPaneValue
&>(val
);
962 QStyleOptionTabWidgetFrame sotwf
;
963 lcl_fullQStyleOptionTabWidgetFrame(sotwf
);
964 QSize aMinSize
= QApplication::style()->sizeFromContents(
965 QStyle::CT_TabWidget
, &sotwf
,
966 QSize(std::max(rValue
.m_aTabHeaderRect
.GetWidth(), controlRegion
.GetWidth()),
967 rValue
.m_aTabHeaderRect
.GetHeight() + controlRegion
.GetHeight()));
968 contentRect
.setSize(aMinSize
);
969 boundingRect
= contentRect
;
978 nativeBoundingRegion
= toRectangle(boundingRect
);
979 nativeContentRegion
= toRectangle(contentRect
);
985 /** Test whether the position is in the native widget.
986 If the return value is true, bIsInside contains information whether
987 aPos was or was not inside the native widget specified by the
988 nType/nPart combination.
990 bool Qt5Graphics_Controls::hitTestNativeControl(ControlType nType
, ControlPart nPart
,
991 const tools::Rectangle
& rControlRegion
,
992 const Point
& rPos
, bool& rIsInside
)
994 if (nType
== ControlType::Scrollbar
)
996 if (nPart
!= ControlPart::ButtonUp
&& nPart
!= ControlPart::ButtonDown
997 && nPart
!= ControlPart::ButtonLeft
&& nPart
!= ControlPart::ButtonRight
)
998 { // we adjust only for buttons (because some scrollbars have 3 buttons,
999 // and LO core doesn't handle such scrollbars well)
1003 bool bHorizontal
= (nPart
== ControlPart::ButtonLeft
|| nPart
== ControlPart::ButtonRight
);
1004 QRect rect
= toQRect(rControlRegion
);
1005 QPoint
pos(rPos
.X(), rPos
.Y());
1006 // Adjust coordinates to make the widget appear to be at (0,0), i.e. make
1007 // widget and screen coordinates the same. QStyle functions should use screen
1008 // coordinates but at least QPlastiqueStyle::subControlRect() is buggy
1009 // and sometimes uses widget coordinates.
1010 pos
-= rect
.topLeft();
1012 QStyleOptionSlider options
;
1013 options
.orientation
= bHorizontal
? Qt::Horizontal
: Qt::Vertical
;
1015 options
.state
|= QStyle::State_Horizontal
;
1016 options
.rect
= rect
;
1017 // some random sensible values, since we call this code only for scrollbar buttons,
1018 // the slider position does not exactly matter
1019 options
.maximum
= 10;
1020 options
.minimum
= 0;
1021 options
.sliderPosition
= options
.sliderValue
= 4;
1022 options
.pageStep
= 2;
1023 QStyle::SubControl control
1024 = QApplication::style()->hitTestComplexControl(QStyle::CC_ScrollBar
, &options
, pos
);
1025 if (nPart
== ControlPart::ButtonUp
|| nPart
== ControlPart::ButtonLeft
)
1026 rIsInside
= (control
== QStyle::SC_ScrollBarSubLine
);
1028 rIsInside
= (control
== QStyle::SC_ScrollBarAddLine
);
1034 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */