bump product version to 6.4.0.3
[LibreOffice.git] / vcl / qt5 / Qt5Graphics_Controls.cxx
blobff8537764bd9410dc88a0e733d200096cf883171
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
32 /**
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)
41 QStyle::State nState
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())
51 case ButtonValue::On:
52 nState |= QStyle::State_On;
53 break;
54 case ButtonValue::Off:
55 nState |= QStyle::State_Off;
56 break;
57 case ButtonValue::Mixed:
58 nState |= QStyle::State_NoChange;
59 break;
60 default:
61 break;
64 return nState;
67 Qt5Graphics_Controls::Qt5Graphics_Controls() {}
69 bool Qt5Graphics_Controls::isNativeControlSupported(ControlType type, ControlPart part)
71 switch (type)
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:
97 return true;
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);
112 default:
113 break;
116 return false;
119 namespace
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,
152 bool bClip = true,
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());
162 option.rect = aRect;
164 QPainter painter(image);
165 if (bClip)
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;
177 else
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);
198 if (!nativeSupport)
200 assert(!nativeSupport && "drawNativeControl called without native support!");
201 return false;
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
220 switch (type)
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);
228 break;
230 [[fallthrough]]; // QPalette::Window
231 case ControlType::Menubar:
232 case ControlType::WindowBackground:
233 m_image->fill(QApplication::palette().color(QPalette::Window).rgb());
234 break;
235 case ControlType::Tooltip:
236 m_image->fill(QApplication::palette().color(QPalette::ToolTipBase).rgb());
237 break;
238 case ControlType::Scrollbar:
239 if ((part == ControlPart::DrawBackgroundVert)
240 || (part == ControlPart::DrawBackgroundHorz))
242 m_image->fill(QApplication::palette().color(QPalette::Window).rgb());
243 break;
245 [[fallthrough]]; // Qt::transparent
246 default:
247 m_image->fill(Qt::transparent);
248 break;
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;
268 if (nControlState
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));
280 else
282 returnVal = false;
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,
314 QSize(2, 2));
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);
320 option.rect = rect;
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;
361 else
362 returnVal = false;
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);
386 QStyleOption option;
387 QRect aRect = m_image->rect();
388 if (part == ControlPart::ThumbVert)
390 aRect.setWidth(handleExtend);
391 option.state = QStyle::State_Horizontal;
393 else
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;
414 switch (part)
416 case ControlPart::ListboxWindow:
417 lcl_drawFrame(QStyle::PE_Frame, m_image.get(),
418 vclStateValue2StateFlag(nControlState, value), true,
419 QStyle::PM_ComboBoxFrameWidth);
420 break;
421 case ControlPart::SubEdit:
422 draw(QStyle::CE_ComboBoxLabel, &option, m_image.get(),
423 vclStateValue2StateFlag(nControlState, value));
424 break;
425 case ControlPart::Entire:
426 draw(QStyle::CC_ComboBox, &option, m_image.get(),
427 vclStateValue2StateFlag(nControlState, value));
428 break;
429 case ControlPart::ButtonDown:
430 option.subControls = QStyle::SC_ComboBoxArrow;
431 draw(QStyle::CC_ComboBox, &option, m_image.get(),
432 vclStateValue2StateFlag(nControlState, value));
433 break;
434 default:
435 returnVal = false;
436 break;
439 else if (type == ControlType::ListNode)
441 QStyleOption option;
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;
487 if (horizontal)
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)
502 option.upsideDown
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));
515 else
517 returnVal = false;
520 else if (type == ControlType::Spinbox)
522 QStyleOptionSpinBox option;
523 option.frame = true;
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)
565 QStyleOption option;
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;
600 if (horizontal)
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
610 option.minimum = 0;
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)
619 QStyleOptionTab sot;
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);
645 else
647 returnVal = false;
650 return returnVal;
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)
660 bool retVal = false;
662 QRect boundingRect = toQRect(controlRegion);
663 QRect contentRect = boundingRect;
664 QStyleOptionComplex styleOption;
666 switch (type)
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,
677 &styleOption);
678 boundingRect.adjust(-size, -size, size, size);
679 retVal = true;
682 break;
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,
691 contentRect.size());
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);
704 retVal = true;
705 break;
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;
727 retVal = true;
729 break;
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);
738 switch (part)
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,
747 &cbo, aContentSize);
748 if (aMinSize.height() > contentRect.height())
749 contentRect.adjust(0, 0, 0, aMinSize.height() - contentRect.height());
750 boundingRect = contentRect;
751 retVal = true;
752 break;
754 case ControlPart::ButtonDown:
755 contentRect = QApplication::style()->subControlRect(QStyle::CC_ComboBox, &cbo,
756 QStyle::SC_ComboBoxArrow);
757 contentRect.translate(boundingRect.left(), boundingRect.top());
758 retVal = true;
759 break;
760 case ControlPart::SubEdit:
762 contentRect = QApplication::style()->subControlRect(
763 QStyle::CC_ComboBox, &cbo, QStyle::SC_ComboBoxEditField);
764 contentRect.translate(boundingRect.left(), boundingRect.top());
765 retVal = true;
766 break;
768 default:
769 break;
771 break;
773 case ControlType::Spinbox:
775 QStyleOptionSpinBox sbo;
776 sbo.frame = true;
778 sbo.rect = QRect(0, 0, contentRect.width(), contentRect.height());
779 sbo.state = vclStateValue2StateFlag(controlState, val);
781 switch (part)
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,
788 &sbo, aContentSize);
789 if (aMinSize.height() > contentRect.height())
790 contentRect.adjust(0, 0, 0, aMinSize.height() - contentRect.height());
791 boundingRect = contentRect;
792 retVal = true;
793 break;
795 case ControlPart::ButtonUp:
796 contentRect = QApplication::style()->subControlRect(QStyle::CC_SpinBox, &sbo,
797 QStyle::SC_SpinBoxUp);
798 contentRect.translate(boundingRect.left(), boundingRect.top());
799 retVal = true;
800 boundingRect = QRect();
801 break;
803 case ControlPart::ButtonDown:
804 contentRect = QApplication::style()->subControlRect(QStyle::CC_SpinBox, &sbo,
805 QStyle::SC_SpinBoxDown);
806 retVal = true;
807 contentRect.translate(boundingRect.left(), boundingRect.top());
808 boundingRect = QRect();
809 break;
811 case ControlPart::SubEdit:
812 contentRect = QApplication::style()->subControlRect(
813 QStyle::CC_SpinBox, &sbo, QStyle::SC_SpinBoxEditField);
814 retVal = true;
815 contentRect.translate(boundingRect.left(), boundingRect.top());
816 break;
817 default:
818 break;
820 break;
822 case ControlType::MenuPopup:
824 int h, w;
825 switch (part)
827 case ControlPart::MenuItemCheckMark:
828 h = QApplication::style()->pixelMetric(QStyle::PM_IndicatorHeight);
829 w = QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth);
830 retVal = true;
831 break;
832 case ControlPart::MenuItemRadioMark:
833 h = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight);
834 w = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth);
835 retVal = true;
836 break;
837 default:
838 break;
840 if (retVal)
842 contentRect = QRect(0, 0, w, h);
843 boundingRect = contentRect;
845 break;
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);
858 retVal = true;
860 break;
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);
868 contentRect.adjust(
869 0, 0,
870 2 * QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &styleOption),
871 2 * QApplication::style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &styleOption));
872 boundingRect = contentRect;
874 retVal = true;
875 break;
877 case ControlType::Slider:
879 const int w = QApplication::style()->pixelMetric(QStyle::PM_SliderLength);
880 if (part == ControlPart::ThumbHorz)
882 contentRect
883 = QRect(boundingRect.left(), boundingRect.top(), w, boundingRect.height());
884 boundingRect = contentRect;
885 retVal = true;
887 else if (part == ControlPart::ThumbVert)
889 contentRect
890 = QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), w);
891 boundingRect = contentRect;
892 retVal = true;
894 break;
896 case ControlType::Toolbar:
898 const int nWorH = QApplication::style()->pixelMetric(QStyle::PM_ToolBarHandleExtent);
899 if (part == ControlPart::ThumbHorz)
901 contentRect
902 = QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), nWorH);
903 boundingRect = contentRect;
904 retVal = true;
906 else if (part == ControlPart::ThumbVert)
908 contentRect
909 = QRect(boundingRect.left(), boundingRect.top(), nWorH, boundingRect.height());
910 boundingRect = contentRect;
911 retVal = true;
913 break;
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;
924 if (horizontal)
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)
929 option.maximum = 10;
930 option.minimum = 0;
931 option.sliderPosition = option.sliderValue = 4;
932 option.pageStep = 2;
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;
938 rect.moveTo(0, 0);
939 option.rect = rect;
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;
944 retVal = true;
946 break;
948 case ControlType::TabItem:
950 QStyleOptionTab sot;
951 lcl_fillQStyleOptionTab(val, sot);
952 QSize aMinSize = QApplication::style()->sizeFromContents(QStyle::CT_TabBarTab, &sot,
953 contentRect.size());
954 contentRect.setSize(aMinSize);
955 boundingRect = contentRect;
956 retVal = true;
957 break;
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;
970 retVal = true;
971 break;
973 default:
974 break;
976 if (retVal)
978 nativeBoundingRegion = toRectangle(boundingRect);
979 nativeContentRegion = toRectangle(contentRect);
982 return retVal;
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)
1000 return false;
1002 rIsInside = false;
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();
1011 rect.moveTo(0, 0);
1012 QStyleOptionSlider options;
1013 options.orientation = bHorizontal ? Qt::Horizontal : Qt::Vertical;
1014 if (bHorizontal)
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);
1027 else // DOWN, RIGHT
1028 rIsInside = (control == QStyle::SC_ScrollBarAddLine);
1029 return true;
1031 return false;
1034 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */