bump product version to 6.3.0.0.beta1
[LibreOffice.git] / vcl / qt5 / Qt5Graphics_Controls.cxx
blob4cc108f550438e7c8aa6a3e49bc2872751e36c97
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/QPushButton>
25 #include <QtWidgets/QStyle>
26 #include <QtWidgets/QStyleOption>
27 #include <QtWidgets/QFrame>
28 #include <QtWidgets/QLabel>
30 #include <qt5/Qt5Tools.hxx>
31 #include <vcl/decoview.hxx>
33 /**
34 Conversion function between VCL ControlState together with
35 ImplControlValue and Qt state flags.
36 @param nControlState State of the widget (default, focused, ...) in Native Widget Framework.
37 @param aValue Value held by the widget (on, off, ...)
39 static QStyle::State vclStateValue2StateFlag(ControlState nControlState,
40 const ImplControlValue& aValue)
42 QStyle::State nState
43 = ((nControlState & ControlState::ENABLED) ? QStyle::State_Enabled : QStyle::State_None)
44 | ((nControlState & ControlState::FOCUSED) ? QStyle::State_HasFocus : QStyle::State_None)
45 | ((nControlState & ControlState::PRESSED) ? QStyle::State_Sunken : QStyle::State_None)
46 | ((nControlState & ControlState::SELECTED) ? QStyle::State_Selected : QStyle::State_None)
47 | ((nControlState & ControlState::ROLLOVER) ? QStyle::State_MouseOver
48 : QStyle::State_None);
50 switch (aValue.getTristateVal())
52 case ButtonValue::On:
53 nState |= QStyle::State_On;
54 break;
55 case ButtonValue::Off:
56 nState |= QStyle::State_Off;
57 break;
58 case ButtonValue::Mixed:
59 nState |= QStyle::State_NoChange;
60 break;
61 default:
62 break;
65 return nState;
68 Qt5Graphics_Controls::Qt5Graphics_Controls() { initStyles(); }
70 bool Qt5Graphics_Controls::IsNativeControlSupported(ControlType type, ControlPart part)
72 switch (type)
74 case ControlType::Tooltip:
75 case ControlType::Progress:
76 case ControlType::ListNode:
77 return (part == ControlPart::Entire);
79 case ControlType::Radiobutton:
80 case ControlType::Checkbox:
81 case ControlType::Pushbutton:
82 return (part == ControlPart::Entire) || (part == ControlPart::Focus);
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 default:
109 break;
112 return false;
115 /// helper drawing methods
116 namespace
118 void draw(QStyle::ControlElement element, QStyleOption* option, QImage* image,
119 QStyle::State const& state, QRect rect = QRect())
121 option->state |= state;
122 option->rect = !rect.isNull() ? rect : image->rect();
124 QPainter painter(image);
125 QApplication::style()->drawControl(element, option, &painter);
128 void draw(QStyle::PrimitiveElement element, QStyleOption* option, QImage* image,
129 QStyle::State const& state, QRect rect = QRect())
131 option->state |= state;
132 option->rect = !rect.isNull() ? rect : image->rect();
134 QPainter painter(image);
135 QApplication::style()->drawPrimitive(element, option, &painter);
138 void draw(QStyle::ComplexControl element, QStyleOptionComplex* option, QImage* image,
139 QStyle::State const& state)
141 option->state |= state;
142 option->rect = image->rect();
144 QPainter painter(image);
145 QApplication::style()->drawComplexControl(element, option, &painter);
148 void lcl_drawFrame(QStyle::PrimitiveElement element, QImage* image, QStyle::State const& state,
149 bool bClip = true,
150 QStyle::PixelMetric eLineMetric = QStyle::PM_DefaultFrameWidth)
152 const int fw = QApplication::style()->pixelMetric(eLineMetric);
153 QStyleOptionFrame option;
154 option.frameShape = QFrame::StyledPanel;
155 option.state = QStyle::State_Sunken | state;
156 option.lineWidth = fw;
158 QRect aRect(image->rect());
159 option.rect = aRect;
161 QPainter painter(image);
162 if (bClip)
163 painter.setClipRegion(QRegion(aRect).subtracted(aRect.adjusted(fw, fw, -fw, -fw)));
164 QApplication::style()->drawPrimitive(element, &option, &painter);
168 bool Qt5Graphics_Controls::drawNativeControl(ControlType type, ControlPart part,
169 const tools::Rectangle& rControlRegion,
170 ControlState nControlState,
171 const ImplControlValue& value, const OUString&)
173 bool nativeSupport = IsNativeControlSupported(type, part);
174 if (!nativeSupport)
176 assert(!nativeSupport && "drawNativeControl called without native support!");
177 return false;
180 if (m_lastPopupRect.isValid()
181 && (type != ControlType::MenuPopup || part != ControlPart::MenuItem))
182 m_lastPopupRect = QRect();
184 bool returnVal = true;
186 QRect widgetRect = toQRect(rControlRegion);
188 //if no image, or resized, make a new image
189 if (!m_image || m_image->size() != widgetRect.size())
191 m_image.reset(new QImage(widgetRect.width(), widgetRect.height(),
192 QImage::Format_ARGB32_Premultiplied));
195 // Default image color - just once
196 switch (type)
198 case ControlType::MenuPopup:
199 if (part == ControlPart::MenuItemCheckMark || part == ControlPart::MenuItemRadioMark)
201 // it is necessary to fill the background transparently first, as this
202 // is painted after menuitem highlight, otherwise there would be a grey area
203 m_image->fill(Qt::transparent);
204 break;
206 [[fallthrough]]; // QPalette::Window
207 case ControlType::Menubar:
208 case ControlType::WindowBackground:
209 m_image->fill(QApplication::palette().color(QPalette::Window).rgb());
210 break;
211 case ControlType::Tooltip:
212 m_image->fill(QApplication::palette().color(QPalette::ToolTipBase).rgb());
213 break;
214 case ControlType::Scrollbar:
215 if ((part == ControlPart::DrawBackgroundVert)
216 || (part == ControlPart::DrawBackgroundHorz))
218 m_image->fill(QApplication::palette().color(QPalette::Window).rgb());
219 break;
221 [[fallthrough]]; // Qt::transparent
222 default:
223 m_image->fill(Qt::transparent);
224 break;
227 if (type == ControlType::Pushbutton)
229 if (part == ControlPart::Entire)
231 QStyleOptionButton option;
232 draw(QStyle::CE_PushButton, &option, m_image.get(),
233 vclStateValue2StateFlag(nControlState, value));
235 else if (part == ControlPart::Focus)
237 QStyleOptionButton option;
238 option.state = QStyle::State_HasFocus;
239 option.rect = m_image->rect();
240 QPainter painter(m_image.get());
241 m_focusedButton->style()->drawControl(QStyle::CE_PushButton, &option, &painter,
242 m_focusedButton.get());
245 else if (type == ControlType::Menubar)
247 if (part == ControlPart::MenuItem)
249 QStyleOptionMenuItem option;
250 if ((nControlState & ControlState::ROLLOVER)
251 && QApplication::style()->styleHint(QStyle::SH_MenuBar_MouseTracking))
252 option.state |= QStyle::State_Selected;
254 if (nControlState
255 & ControlState::SELECTED) // Passing State_Sunken is currently not documented.
256 option.state |= QStyle::State_Sunken; // But some kinds of QStyle interpret it.
258 draw(QStyle::CE_MenuBarItem, &option, m_image.get(),
259 vclStateValue2StateFlag(nControlState, value));
261 else if (part == ControlPart::Entire)
263 QStyleOptionMenuItem option;
264 draw(QStyle::CE_MenuBarEmptyArea, &option, m_image.get(),
265 vclStateValue2StateFlag(nControlState, value));
267 else
269 returnVal = false;
272 else if (type == ControlType::MenuPopup)
274 assert(part == ControlPart::MenuItem ? m_lastPopupRect.isValid()
275 : !m_lastPopupRect.isValid());
276 if (part == ControlPart::MenuItem)
278 QStyleOptionMenuItem option;
279 draw(QStyle::CE_MenuItem, &option, m_image.get(),
280 vclStateValue2StateFlag(nControlState, value));
281 // HACK: LO core first paints the entire popup and only then it paints menu items,
282 // but QMenu::paintEvent() paints popup frame after all items. That means highlighted
283 // items here would paint the highlight over the frame border. Since calls to ControlPart::MenuItem
284 // are always preceded by calls to ControlPart::Entire, just remember the size for the whole
285 // popup (otherwise not possible to get here) and draw the border afterwards.
286 QRect framerect(m_lastPopupRect.topLeft() - widgetRect.topLeft(),
287 widgetRect.size().expandedTo(m_lastPopupRect.size()));
288 QStyleOptionFrame frame;
289 draw(QStyle::PE_FrameMenu, &frame, m_image.get(),
290 vclStateValue2StateFlag(nControlState, value), framerect);
292 else if (part == ControlPart::Separator)
294 QStyleOptionMenuItem option;
295 option.menuItemType = QStyleOptionMenuItem::Separator;
296 // Painting the whole menu item area results in different background
297 // with at least Plastique style, so clip only to the separator itself
298 // (QSize( 2, 2 ) is hardcoded in Qt)
299 option.rect = m_image->rect();
300 QSize size = QApplication::style()->sizeFromContents(QStyle::CT_MenuItem, &option,
301 QSize(2, 2));
302 QRect rect = m_image->rect();
303 QPoint center = rect.center();
304 rect.setHeight(size.height());
305 rect.moveCenter(center);
306 option.state |= vclStateValue2StateFlag(nControlState, value);
307 option.rect = rect;
309 QPainter painter(m_image.get());
310 // don't paint over popup frame border (like the hack above, but here it can be simpler)
311 const int fw = QApplication::style()->pixelMetric(QStyle::PM_MenuPanelWidth);
312 painter.setClipRect(rect.adjusted(fw, 0, -fw, 0));
313 QApplication::style()->drawControl(QStyle::CE_MenuItem, &option, &painter);
315 else if (part == ControlPart::MenuItemCheckMark || part == ControlPart::MenuItemRadioMark)
317 QStyleOptionMenuItem option;
318 option.checkType = (part == ControlPart::MenuItemCheckMark)
319 ? QStyleOptionMenuItem::NonExclusive
320 : QStyleOptionMenuItem::Exclusive;
321 option.checked = bool(nControlState & ControlState::PRESSED);
322 // widgetRect is now the rectangle for the checkbox/radiobutton itself, but Qt
323 // paints the whole menu item, so translate position (and it'll be clipped);
324 // it is also necessary to fill the background transparently first, as this
325 // is painted after menuitem highlight, otherwise there would be a grey area
326 assert(value.getType() == ControlType::MenuPopup);
327 const MenupopupValue* menuVal = static_cast<const MenupopupValue*>(&value);
328 QRect menuItemRect(toQRect(menuVal->maItemRect));
329 QRect rect(menuItemRect.topLeft() - widgetRect.topLeft(),
330 widgetRect.size().expandedTo(menuItemRect.size()));
331 // checkboxes are always displayed next to images in menus, so are never centered
332 const int focus_size = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
333 rect.moveTo(-focus_size, rect.y());
334 draw(QStyle::CE_MenuItem, &option, m_image.get(),
335 vclStateValue2StateFlag(nControlState & ~ControlState::PRESSED, value), rect);
337 else if (part == ControlPart::Entire)
339 QStyleOptionMenuItem option;
340 draw(QStyle::PE_PanelMenu, &option, m_image.get(),
341 vclStateValue2StateFlag(nControlState, value));
342 // Try hard to get any frame!
343 QStyleOptionFrame frame;
344 draw(QStyle::PE_FrameMenu, &frame, m_image.get(),
345 vclStateValue2StateFlag(nControlState, value));
346 draw(QStyle::PE_FrameWindow, &frame, m_image.get(),
347 vclStateValue2StateFlag(nControlState, value));
348 m_lastPopupRect = widgetRect;
350 else
351 returnVal = false;
353 else if ((type == ControlType::Toolbar) && (part == ControlPart::Button))
355 QStyleOptionToolButton option;
357 option.arrowType = Qt::NoArrow;
358 option.subControls = QStyle::SC_ToolButton;
360 option.state = vclStateValue2StateFlag(nControlState, value);
361 option.state |= QStyle::State_Raised | QStyle::State_Enabled | QStyle::State_AutoRaise;
363 draw(QStyle::CC_ToolButton, &option, m_image.get(),
364 vclStateValue2StateFlag(nControlState, value));
366 else if ((type == ControlType::Toolbar) && (part == ControlPart::Entire))
368 QStyleOptionToolBar option;
370 option.rect = QRect(0, 0, widgetRect.width(), widgetRect.height());
371 option.state = vclStateValue2StateFlag(nControlState, value);
373 draw(QStyle::CE_ToolBar, &option, m_image.get(),
374 vclStateValue2StateFlag(nControlState, value));
376 else if ((type == ControlType::Toolbar)
377 && (part == ControlPart::ThumbVert || part == ControlPart::ThumbHorz))
378 { // reduce paint area only to the handle area
379 const int handleExtend = QApplication::style()->pixelMetric(QStyle::PM_ToolBarHandleExtent);
380 QStyleOption option;
381 option.state = vclStateValue2StateFlag(nControlState, value);
383 QPainter painter(m_image.get());
384 if (part == ControlPart::ThumbVert)
386 option.rect = QRect(0, 0, handleExtend, widgetRect.height());
387 painter.setClipRect(widgetRect.x(), widgetRect.y(), handleExtend, widgetRect.height());
388 option.state |= QStyle::State_Horizontal;
390 else
392 option.rect = QRect(0, 0, widgetRect.width(), handleExtend);
393 painter.setClipRect(widgetRect.x(), widgetRect.y(), widgetRect.width(), handleExtend);
395 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &option, &painter);
397 else if (type == ControlType::Editbox || type == ControlType::MultilineEditbox)
399 lcl_drawFrame(QStyle::PE_FrameLineEdit, m_image.get(),
400 vclStateValue2StateFlag(nControlState, value), false);
402 else if (type == ControlType::Combobox)
404 QStyleOptionComboBox option;
405 option.editable = true;
406 draw(QStyle::CC_ComboBox, &option, m_image.get(),
407 vclStateValue2StateFlag(nControlState, value));
409 else if (type == ControlType::Listbox)
411 QStyleOptionComboBox option;
412 option.editable = false;
413 switch (part)
415 case ControlPart::ListboxWindow:
416 lcl_drawFrame(QStyle::PE_Frame, m_image.get(),
417 vclStateValue2StateFlag(nControlState, value), true,
418 QStyle::PM_ComboBoxFrameWidth);
419 break;
420 case ControlPart::SubEdit:
421 draw(QStyle::CE_ComboBoxLabel, &option, m_image.get(),
422 vclStateValue2StateFlag(nControlState, value));
423 break;
424 case ControlPart::Entire:
425 draw(QStyle::CC_ComboBox, &option, m_image.get(),
426 vclStateValue2StateFlag(nControlState, value));
427 break;
428 case ControlPart::ButtonDown:
429 option.subControls = QStyle::SC_ComboBoxArrow;
430 draw(QStyle::CC_ComboBox, &option, m_image.get(),
431 vclStateValue2StateFlag(nControlState, value));
432 break;
433 default:
434 returnVal = false;
435 break;
438 else if (type == ControlType::ListNode)
440 QStyleOption option;
441 option.state = QStyle::State_Item | QStyle::State_Children;
443 if (value.getTristateVal() == ButtonValue::On)
444 option.state |= QStyle::State_Open;
446 draw(QStyle::PE_IndicatorBranch, &option, m_image.get(),
447 vclStateValue2StateFlag(nControlState, value));
449 else if (type == ControlType::ListHeader)
451 QStyleOptionHeader option;
452 draw(QStyle::CE_HeaderSection, &option, m_image.get(),
453 vclStateValue2StateFlag(nControlState, value));
455 else if (type == ControlType::Checkbox)
457 if (part == ControlPart::Entire)
459 QStyleOptionButton option;
460 // clear FOCUSED bit, focus is drawn separately
461 nControlState &= ~ControlState::FOCUSED;
462 draw(QStyle::CE_CheckBox, &option, m_image.get(),
463 vclStateValue2StateFlag(nControlState, value));
465 else if (part == ControlPart::Focus)
467 QStyleOptionFocusRect option;
468 draw(QStyle::PE_FrameFocusRect, &option, m_image.get(),
469 vclStateValue2StateFlag(nControlState, value));
472 else if (type == ControlType::Scrollbar)
474 if ((part == ControlPart::DrawBackgroundVert) || (part == ControlPart::DrawBackgroundHorz))
476 QStyleOptionSlider option;
477 assert(value.getType() == ControlType::Scrollbar);
478 const ScrollbarValue* sbVal = static_cast<const ScrollbarValue*>(&value);
480 //if the scroll bar is active (aka not degenerate... allow for hover events)
481 if (sbVal->mnVisibleSize < sbVal->mnMax)
482 option.state = QStyle::State_MouseOver;
484 bool horizontal = (part == ControlPart::DrawBackgroundHorz); //horizontal or vertical
485 option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
486 if (horizontal)
487 option.state |= QStyle::State_Horizontal;
489 //setup parameters from the OO values
490 option.minimum = sbVal->mnMin;
491 option.maximum = sbVal->mnMax - sbVal->mnVisibleSize;
492 option.maximum = qMax(option.maximum, option.minimum); // bnc#619772
493 option.sliderValue = sbVal->mnCur;
494 option.sliderPosition = sbVal->mnCur;
495 option.pageStep = sbVal->mnVisibleSize;
496 if (part == ControlPart::DrawBackgroundHorz)
497 option.upsideDown = sbVal->maButton1Rect.Left() > sbVal->maButton2Rect.Left();
499 //setup the active control... always the slider
500 if (sbVal->mnThumbState & ControlState::ROLLOVER)
501 option.activeSubControls = QStyle::SC_ScrollBarSlider;
503 draw(QStyle::CC_ScrollBar, &option, m_image.get(),
504 vclStateValue2StateFlag(nControlState, value));
506 else
508 returnVal = false;
511 else if (type == ControlType::Spinbox)
513 QStyleOptionSpinBox option;
514 option.frame = true;
516 // determine active control
517 if (value.getType() == ControlType::SpinButtons)
519 const SpinbuttonValue* pSpinVal = static_cast<const SpinbuttonValue*>(&value);
520 if (pSpinVal->mnUpperState & ControlState::PRESSED)
521 option.activeSubControls |= QStyle::SC_SpinBoxUp;
522 if (pSpinVal->mnLowerState & ControlState::PRESSED)
523 option.activeSubControls |= QStyle::SC_SpinBoxDown;
524 if (pSpinVal->mnUpperState & ControlState::ENABLED)
525 option.stepEnabled |= QAbstractSpinBox::StepUpEnabled;
526 if (pSpinVal->mnLowerState & ControlState::ENABLED)
527 option.stepEnabled |= QAbstractSpinBox::StepDownEnabled;
528 if (pSpinVal->mnUpperState & ControlState::ROLLOVER)
529 option.state = QStyle::State_MouseOver;
530 if (pSpinVal->mnLowerState & ControlState::ROLLOVER)
531 option.state = QStyle::State_MouseOver;
534 draw(QStyle::CC_SpinBox, &option, m_image.get(),
535 vclStateValue2StateFlag(nControlState, value));
537 else if (type == ControlType::Radiobutton)
539 if (part == ControlPart::Entire)
541 QStyleOptionButton option;
542 // clear FOCUSED bit, focus is drawn separately
543 nControlState &= ~ControlState::FOCUSED;
544 draw(QStyle::CE_RadioButton, &option, m_image.get(),
545 vclStateValue2StateFlag(nControlState, value));
547 else if (part == ControlPart::Focus)
549 QStyleOptionFocusRect option;
550 draw(QStyle::PE_FrameFocusRect, &option, m_image.get(),
551 vclStateValue2StateFlag(nControlState, value));
554 else if (type == ControlType::Tooltip)
556 QStyleOption option;
557 draw(QStyle::PE_PanelTipLabel, &option, m_image.get(),
558 vclStateValue2StateFlag(nControlState, value));
560 else if (type == ControlType::Frame)
562 lcl_drawFrame(QStyle::PE_Frame, m_image.get(),
563 vclStateValue2StateFlag(nControlState, value));
565 else if (type == ControlType::WindowBackground)
567 // Nothing to do - see "Default image color" switch ^^
569 else if (type == ControlType::Fixedline)
571 QStyleOptionMenuItem option;
572 option.menuItemType = QStyleOptionMenuItem::Separator;
573 option.state |= QStyle::State_Item;
575 draw(QStyle::CE_MenuItem, &option, m_image.get(),
576 vclStateValue2StateFlag(nControlState, value));
578 else if (type == ControlType::Slider
579 && (part == ControlPart::TrackHorzArea || part == ControlPart::TrackVertArea))
581 assert(value.getType() == ControlType::Slider);
582 const SliderValue* slVal = static_cast<const SliderValue*>(&value);
583 QStyleOptionSlider option;
585 option.rect = QRect(0, 0, widgetRect.width(), widgetRect.height());
586 option.state = vclStateValue2StateFlag(nControlState, value);
587 option.maximum = slVal->mnMax;
588 option.minimum = slVal->mnMin;
589 option.sliderPosition = option.sliderValue = slVal->mnCur;
590 bool horizontal = (part == ControlPart::TrackHorzArea); //horizontal or vertical
591 option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
592 if (horizontal)
593 option.state |= QStyle::State_Horizontal;
595 draw(QStyle::CC_Slider, &option, m_image.get(),
596 vclStateValue2StateFlag(nControlState, value));
598 else if (type == ControlType::Progress && part == ControlPart::Entire)
600 SAL_WNODEPRECATED_DECLARATIONS_PUSH
601 QStyleOptionProgressBarV2 option;
602 SAL_WNODEPRECATED_DECLARATIONS_POP
603 option.minimum = 0;
604 option.maximum = widgetRect.width();
605 option.progress = value.getNumericVal();
606 option.rect = QRect(0, 0, widgetRect.width(), widgetRect.height());
607 option.state = vclStateValue2StateFlag(nControlState, value);
609 draw(QStyle::CE_ProgressBar, &option, m_image.get(),
610 vclStateValue2StateFlag(nControlState, value));
612 else
614 returnVal = false;
617 return returnVal;
620 bool Qt5Graphics_Controls::getNativeControlRegion(ControlType type, ControlPart part,
621 const tools::Rectangle& controlRegion,
622 ControlState controlState,
623 const ImplControlValue& val, const OUString&,
624 tools::Rectangle& nativeBoundingRegion,
625 tools::Rectangle& nativeContentRegion)
627 bool retVal = false;
629 QRect boundingRect = toQRect(controlRegion);
630 QRect contentRect = boundingRect;
631 QStyleOptionComplex styleOption;
633 switch (type)
635 // Metrics of the push button
636 case ControlType::Pushbutton:
637 if (part == ControlPart::Entire)
639 styleOption.state = vclStateValue2StateFlag(controlState, val);
641 if (controlState & ControlState::DEFAULT)
643 int size = QApplication::style()->pixelMetric(QStyle::PM_ButtonDefaultIndicator,
644 &styleOption);
645 boundingRect.adjust(-size, -size, size, size);
646 retVal = true;
649 break;
650 case ControlType::Editbox:
651 case ControlType::MultilineEditbox:
653 QStyleOptionFrame fo;
654 fo.frameShape = QFrame::StyledPanel;
655 fo.state = QStyle::State_Sunken;
656 fo.lineWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
657 QSize aMinSize = QApplication::style()->sizeFromContents(QStyle::CT_LineEdit, &fo,
658 contentRect.size());
659 if (aMinSize.height() > boundingRect.height())
661 int nHeight = (aMinSize.height() - boundingRect.height()) / 2;
662 assert(0 == (aMinSize.height() - boundingRect.height()) % 2);
663 boundingRect.adjust(0, -nHeight, 0, nHeight);
665 if (aMinSize.width() > boundingRect.width())
667 int nWidth = (aMinSize.width() - boundingRect.width()) / 2;
668 assert(0 == (aMinSize.width() - boundingRect.width()) % 2);
669 boundingRect.adjust(-nWidth, 0, nWidth, 0);
671 retVal = true;
672 break;
674 case ControlType::Checkbox:
675 if (part == ControlPart::Entire)
677 styleOption.state = vclStateValue2StateFlag(controlState, val);
679 contentRect.setWidth(
680 QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth, &styleOption));
681 contentRect.setHeight(
682 QApplication::style()->pixelMetric(QStyle::PM_IndicatorHeight, &styleOption));
684 contentRect.adjust(0, 0,
686 * QApplication::style()->pixelMetric(
687 QStyle::PM_FocusFrameHMargin, &styleOption),
689 * QApplication::style()->pixelMetric(
690 QStyle::PM_FocusFrameVMargin, &styleOption));
692 boundingRect = contentRect;
694 retVal = true;
696 break;
697 case ControlType::Combobox:
698 case ControlType::Listbox:
700 QStyleOptionComboBox cbo;
702 cbo.rect = QRect(0, 0, contentRect.width(), contentRect.height());
703 cbo.state = vclStateValue2StateFlag(controlState, val);
705 switch (part)
707 case ControlPart::Entire:
709 // find out the minimum size that should be used
710 // assume contents is a text line
711 int nHeight = QApplication::fontMetrics().height();
712 QSize aContentSize(contentRect.width(), nHeight);
713 QSize aMinSize = QApplication::style()->sizeFromContents(QStyle::CT_ComboBox,
714 &cbo, aContentSize);
715 if (aMinSize.height() > contentRect.height())
716 contentRect.adjust(0, 0, 0, aMinSize.height() - contentRect.height());
717 boundingRect = contentRect;
718 retVal = true;
719 break;
721 case ControlPart::ButtonDown:
722 contentRect = QApplication::style()->subControlRect(QStyle::CC_ComboBox, &cbo,
723 QStyle::SC_ComboBoxArrow);
724 contentRect.translate(boundingRect.left(), boundingRect.top());
725 retVal = true;
726 break;
727 case ControlPart::SubEdit:
729 contentRect = QApplication::style()->subControlRect(
730 QStyle::CC_ComboBox, &cbo, QStyle::SC_ComboBoxEditField);
731 contentRect.translate(boundingRect.left(), boundingRect.top());
732 retVal = true;
733 break;
735 default:
736 break;
738 break;
740 case ControlType::Spinbox:
742 QStyleOptionSpinBox sbo;
743 sbo.frame = true;
745 sbo.rect = QRect(0, 0, contentRect.width(), contentRect.height());
746 sbo.state = vclStateValue2StateFlag(controlState, val);
748 switch (part)
750 case ControlPart::Entire:
752 int nHeight = QApplication::fontMetrics().height();
753 QSize aContentSize(contentRect.width(), nHeight);
754 QSize aMinSize = QApplication::style()->sizeFromContents(QStyle::CT_SpinBox,
755 &sbo, aContentSize);
756 if (aMinSize.height() > contentRect.height())
757 contentRect.adjust(0, 0, 0, aMinSize.height() - contentRect.height());
758 boundingRect = contentRect;
759 retVal = true;
760 break;
762 case ControlPart::ButtonUp:
763 contentRect = QApplication::style()->subControlRect(QStyle::CC_SpinBox, &sbo,
764 QStyle::SC_SpinBoxUp);
765 contentRect.translate(boundingRect.left(), boundingRect.top());
766 retVal = true;
767 boundingRect = QRect();
768 break;
770 case ControlPart::ButtonDown:
771 contentRect = QApplication::style()->subControlRect(QStyle::CC_SpinBox, &sbo,
772 QStyle::SC_SpinBoxDown);
773 retVal = true;
774 contentRect.translate(boundingRect.left(), boundingRect.top());
775 boundingRect = QRect();
776 break;
778 case ControlPart::SubEdit:
779 contentRect = QApplication::style()->subControlRect(
780 QStyle::CC_SpinBox, &sbo, QStyle::SC_SpinBoxEditField);
781 retVal = true;
782 contentRect.translate(boundingRect.left(), boundingRect.top());
783 break;
784 default:
785 break;
787 break;
789 case ControlType::MenuPopup:
791 int h, w;
792 switch (part)
794 case ControlPart::MenuItemCheckMark:
795 h = QApplication::style()->pixelMetric(QStyle::PM_IndicatorHeight);
796 w = QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth);
797 retVal = true;
798 break;
799 case ControlPart::MenuItemRadioMark:
800 h = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight);
801 w = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth);
802 retVal = true;
803 break;
804 default:
805 break;
807 if (retVal)
809 contentRect = QRect(0, 0, w, h);
810 boundingRect = contentRect;
812 break;
814 case ControlType::Frame:
816 if (part == ControlPart::Border)
818 auto nStyle = static_cast<DrawFrameFlags>(val.getNumericVal() & 0xFFF0);
819 if (nStyle & DrawFrameFlags::NoDraw)
821 int nFrameWidth
822 = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
823 contentRect.adjust(nFrameWidth, nFrameWidth, -nFrameWidth, -nFrameWidth);
825 retVal = true;
827 break;
829 case ControlType::Radiobutton:
831 const int h = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight);
832 const int w = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth);
834 contentRect = QRect(boundingRect.left(), boundingRect.top(), w, h);
835 contentRect.adjust(
836 0, 0,
837 2 * QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &styleOption),
838 2 * QApplication::style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &styleOption));
839 boundingRect = contentRect;
841 retVal = true;
842 break;
844 case ControlType::Slider:
846 const int w = QApplication::style()->pixelMetric(QStyle::PM_SliderLength);
847 if (part == ControlPart::ThumbHorz)
849 contentRect
850 = QRect(boundingRect.left(), boundingRect.top(), w, boundingRect.height());
851 boundingRect = contentRect;
852 retVal = true;
854 else if (part == ControlPart::ThumbVert)
856 contentRect
857 = QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), w);
858 boundingRect = contentRect;
859 retVal = true;
861 break;
863 case ControlType::Toolbar:
865 const int nWorH = QApplication::style()->pixelMetric(QStyle::PM_ToolBarHandleExtent);
866 if (part == ControlPart::ThumbHorz)
868 contentRect
869 = QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), nWorH);
870 boundingRect = contentRect;
871 retVal = true;
873 else if (part == ControlPart::ThumbVert)
875 contentRect
876 = QRect(boundingRect.left(), boundingRect.top(), nWorH, boundingRect.height());
877 boundingRect = contentRect;
878 retVal = true;
880 break;
882 case ControlType::Scrollbar:
884 // core can't handle 3-button scrollbars well, so we fix that in hitTestNativeControl(),
885 // for the rest also provide the track area (i.e. area not taken by buttons)
886 if (part == ControlPart::TrackVertArea || part == ControlPart::TrackHorzArea)
888 QStyleOptionSlider option;
889 bool horizontal = (part == ControlPart::TrackHorzArea); //horizontal or vertical
890 option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
891 if (horizontal)
892 option.state |= QStyle::State_Horizontal;
893 // getNativeControlRegion usually gets ImplControlValue as 'val' (i.e. not the proper
894 // subclass), so use random sensible values (doesn't matter anyway, as the wanted
895 // geometry here depends only on button sizes)
896 option.maximum = 10;
897 option.minimum = 0;
898 option.sliderPosition = option.sliderValue = 4;
899 option.pageStep = 2;
900 // Adjust coordinates to make the widget appear to be at (0,0), i.e. make
901 // widget and screen coordinates the same. QStyle functions should use screen
902 // coordinates but at least QPlastiqueStyle::subControlRect() is buggy
903 // and sometimes uses widget coordinates.
904 QRect rect = contentRect;
905 rect.moveTo(0, 0);
906 option.rect = rect;
907 rect = QApplication::style()->subControlRect(QStyle::CC_ScrollBar, &option,
908 QStyle::SC_ScrollBarGroove);
909 rect.translate(contentRect.topLeft()); // reverse the workaround above
910 contentRect = boundingRect = rect;
911 retVal = true;
913 break;
915 default:
916 break;
918 if (retVal)
920 nativeBoundingRegion = toRectangle(boundingRect);
921 nativeContentRegion = toRectangle(contentRect);
924 return retVal;
927 /** Test whether the position is in the native widget.
928 If the return value is true, bIsInside contains information whether
929 aPos was or was not inside the native widget specified by the
930 nType/nPart combination.
932 bool Qt5Graphics_Controls::hitTestNativeControl(ControlType nType, ControlPart nPart,
933 const tools::Rectangle& rControlRegion,
934 const Point& rPos, bool& rIsInside)
936 if (nType == ControlType::Scrollbar)
938 if (nPart != ControlPart::ButtonUp && nPart != ControlPart::ButtonDown
939 && nPart != ControlPart::ButtonLeft && nPart != ControlPart::ButtonRight)
940 { // we adjust only for buttons (because some scrollbars have 3 buttons,
941 // and LO core doesn't handle such scrollbars well)
942 return false;
944 rIsInside = false;
945 bool bHorizontal = (nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight);
946 QRect rect = toQRect(rControlRegion);
947 QPoint pos(rPos.X(), rPos.Y());
948 // Adjust coordinates to make the widget appear to be at (0,0), i.e. make
949 // widget and screen coordinates the same. QStyle functions should use screen
950 // coordinates but at least QPlastiqueStyle::subControlRect() is buggy
951 // and sometimes uses widget coordinates.
952 pos -= rect.topLeft();
953 rect.moveTo(0, 0);
954 QStyleOptionSlider options;
955 options.orientation = bHorizontal ? Qt::Horizontal : Qt::Vertical;
956 if (bHorizontal)
957 options.state |= QStyle::State_Horizontal;
958 options.rect = rect;
959 // some random sensible values, since we call this code only for scrollbar buttons,
960 // the slider position does not exactly matter
961 options.maximum = 10;
962 options.minimum = 0;
963 options.sliderPosition = options.sliderValue = 4;
964 options.pageStep = 2;
965 QStyle::SubControl control
966 = QApplication::style()->hitTestComplexControl(QStyle::CC_ScrollBar, &options, pos);
967 if (nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonLeft)
968 rIsInside = (control == QStyle::SC_ScrollBarSubLine);
969 else // DOWN, RIGHT
970 rIsInside = (control == QStyle::SC_ScrollBarAddLine);
971 return true;
973 return false;
976 void Qt5Graphics_Controls::initStyles()
978 // button focus
979 m_focusedButton.reset(new QPushButton());
980 QString aHighlightColor = QApplication::palette().color(QPalette::Highlight).name();
981 QString focusStyleSheet("background-color: rgb(0,0,0,0%); border: 1px; border-radius: 2px; "
982 "border-color: %1; border-style:solid;");
983 focusStyleSheet.replace("%1", aHighlightColor);
984 m_focusedButton->setStyleSheet(focusStyleSheet);
987 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */